We’re going to look at two of the tasks of the PHDays finals, namely breadcrumbs which we solved and homepage, which unfortunately we were not able to solve during the CTF, though the solution seems to be correct. If you have any additional information on this particular task, I’d love to have your comments : @plopz0r.
Breadcrumbs
This was a forensics task where a full virtual machine was provided to us. We were simply told that the task started with the user “user”. So after extracting the VM’s disk, we started by searching in that user’s home directory. Easily enough, we discover a file named simple_bsl.png.
In a comment section of the image, we can find :
Next breadcrumb is located at /usr/share/doc/ranger/ But first find password...
And in this folder, there is a file named “hacking.gpg” which obviously needs a password. The name of the first image points in the direction of information being hidden in the least-significant bit of each pixel (bsl -> lsb). The answer is not quite as straight forward though, as actually only the first 8 out of every 9 bits must be considered to find the password.
The file then decrypts this message:
Password is the name of the folder, where file is located. Let the force be with you, good luck, etc :)We can easily find this file with the following command:
find . | xargs md5sum | grep 30055312e086ea49424c759f3deb751a
The file is /etc/magic, it is also a GPG encrypted file. We can decrypt it with “etc” as a password. This now reveals:
Valid flag's parts can contain ONLY alphabet symbols, underscore ("_") and exclamation ("!") mark. First part of the flag is "Not_". Good luck!We quickly see that Not_ is prepended by Key_part: and so we grepped for all “Key_part: ” followed by alphabet, underscore and exclamation mark characters. This however reveals way too many results to find the flag immediately… So we tried ordering the parts by file modification date, or line number within the file, but this didn’t really give any results, so we proceeded with a bit of guessing.
The flag starts with “Not_” and there is a key part “_nor_” and another one “tel!”. So we decided it was likely that the flag would be of the format “Not_XXXXX_nor_YYYYYtel!”
One other key part is “gre”, which can be added before “tel!” to give “gretel!”. Since the name of the task is “breadcrumbs”, We guessed the flag was “Not_hansel_nor_gretel!”, which was correct!
Homepage
This was a web task and when you connect to the application, there is not much you can do. Essentially, create a user, login with that user, view a blog page in english or russian, and send a message to the admin. The blog page itself talks about XSS, CSRF, Response Splitting and Clickjacking. There is an admin page, but we cannot access the contents.
We first started looking for XSS flaws in the site, but to no avail, everything is correctly encoded. We tried sending messages to the admin and noticed that he would click on links that are sent to him (though this did not seem to work at the start of the CTF), therefore allowing us to do CSRF attacks on the site. How could we exploit this?
We noticed that there is a session fixation flaw in the login mechanism, as the JSESSIONID is not regenerated when a user logs in.And the only remaining action that can be done on the website is to change the language that is set for the blog page. This is done through a parameter named “locale” which then has the server set a cookie for the user:
Set-cookie: locale="XXX"; path=/HomePage/blog/; HttpOnly
Could we maybe directly inject new HTTP headers and set the admin’s JSESSIONID cookie into the response by inserting CR/LF characters? The answer is no… They are converted to “space” characters in the response, so no new lines allowed 🙁
Pretty much any other characters are allowed though. So we tried to set multiple cookies in a single set-cookie header, but again this failed. However, it turns out that Tomcat will accept “,” as a cookie separator when a request is received. So it is actually possible to smuggle several cookies into a single one! Pretty cool! For example, if you decide to set the locale like this:
?locale=en", JSESSIONID=XXXXXXXXXXXXXXXXXXXXXX, toto="
When your browser sends this cookie to the server, it will look like this:
Cookie: locale="en", JSESSIONID=XXXXXXXXXXXXX, toto=""; JSESSIONID=origsessionid
The server will then see three different cookies, and only take the first JSESSIONID which is the one set by us! We’re definitely on the right track.
The only problem is the cookie that we set for the locale is only set for /HomePage/blog/ and none of the other pages of the site. No login, no access to the admin interface, nothing. So even if we can force the admin to use a specific sessionid, it will only be used on a page that doesn’t interest us…
At this point, we’re pretty sure we’re on the right track and that we just have to find a way to set the cookie for the whole domain instead of the blog page. Once that is done, we can get the admin to click our link, set the cookie and just wait for him to login again and we can reuse the sessionid to access the page as an admin (because of the session fixation flaw).
Now this took ages and a little hint from the organisers to actually get it done. We tried adding various cookie parameters such as an additional path, or domain, secure and so on, but apparently the browser will only use the last path that is specified if there are more than one 🙁
But (and this is where the orgas gave us a little hint) if you add the path parameter multiple times, Chrome will actually ignore the last one and select one of the ones that was specified before hand. Wtf?
I’m not quite sure what the exact number is, but if you set the following locale, you will actually end up with a JSESSIONID that is valid for the whole web site (at least in the latest version of Chrome).
?locale=en", JSESSIONID=XXXXXXXXXXXXXXXXXXXXXXXXXXX, toto="; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/; path=/HomePage/;
This was discovered 10 minutes before the end of the CTF, so I quickly sent the link to an admin with my own JSESSIONID in there and waited for him to click and hope that he then logged in to the site again. Unfortunately nothing happened, I never got the admin privs and couldn’t access the admin page to get the flag. So I’m wondering if I’m still missing something or if the challenge was broken?
Interesting challenge though!