1. ๋ฌธ์
https://dreamhack.io/wargame/challenges/552
2. ํด๊ฒฐ ๊ณผ์
/ -> /view๋ก ๋ฆฌ๋ค์ด๋ ํธ ๋๋ค.
/view์์๋ mini_database ๋ฆฌ์คํธ์ ํฌํจ๋์ด ์๋ ์ด๋ฏธ์ง๋ฅผ ๋ณด์ฌ์ค๋ค.
@app.route('/')
def index():
return redirect(url_for('view'))
@app.route('/view')
def view():
return render_template('view.html', img_list=mini_database)
/request ํ์ด์ง์์๋ url์ ์ ๋ ฅ๋ฐ์ GET์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ๋ฐ๋๋ค.
์ด๋ ์ด๋ฏธ์ง์ url์ ๊ณต๋ฐฑ์ด ์๊ฑฐ๋ (''), "file://"๋ก ์์ํ๊ฑฐ๋ flag๋ผ๋ ๋ฌธ์์ด์ด ํฌํจ๋ ๊ฒฝ์ฐ ํด๋น ์์ฒญ์ ์ฒ๋ฆฌํ์ง ์๋๋ค. ์ฆ, ์๋ฒ์ ํ์ผ ์์คํ ์ ์ ๊ทผํ๋ ค๋ ์๋๋ฅผ ํํฐ๋งํ๋ค.
file:// — Accessing local filesystem
https://mnzy.tistory.com/128
์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ url์ ์ฝ์ด์ ์ด๋ฏธ์ง๋ฅผ data์ ์ ์ฅํ ๋ค base64๋ก ์ธ์ฝ๋ฉํ์ฌ mini_database๋ผ๋ ๋ฆฌ์คํธ์ title์ ํค๋ก, ์ธ์ฝ๋ฉ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ผ๋ก ์ ์ฅํ๋ค. (title: ์ธ์ฝ๋ฉ ๋ฐ์ดํฐ)
๋ฌธ์ ์์ '์ธ๋ถ๋ก ์์ฒญํ๋ ๊ธฐ๋ฅ์ด ์์ ํ ๊ฑด์ง ๋ชจ๋ฅด๊ฒ ๋ค๊ณ ํ๋ค์...' ๋ผ๊ณ ํ์๋ค.
์ฆ, ํํฐ๋ง์ ์ฐํํ์ฌ LFI ๊ณต๊ฒฉ์ ์ํํ ์ ์์ ๊ฒ์ด๋ผ๊ณ ์์ํ ์ ์๋ค.
@app.route('/request')
def url_request():
url = request.args.get('url', '').lower()
title = request.args.get('title', '')
if url == '' or url.startswith("file://") or "flag" in url or title == '':
return render_template('request.html')
try:
data = urlopen(url).read()
mini_database.append({title: base64.b64encode(data).decode('utf-8')})
return redirect(url_for('view'))
except:
return render_template("request.html")
/upload ํ์ด์ง์์๋ ํ์ผ์ POST ์์ฒญ์ผ๋ก ์ ๋ ฅํ์ฌ ์ ๋ก๋ํ๋ ํจ์๋ฅผ ์คํํ๋ค.
ํผ ๋ฐ์ดํฐ์์ file ํ๋๋ฅผ ๊ฐ์ ธ์ ๋ณ์ f์ ์ ์ฅํ๊ณ title ํ๋๋ฅผ ๊ฐ์ ธ์ ๋ณ์ title์ ์ ์ฅํ๋ค.
๋ง์ฝ title์ด ๊ณต๋ฐฑ์ด๊ฑฐ๋ file์ด ์กด์ฌํ์ง ์์ผ๋ฉด ์์ฒญ์ ์ฒ๋ฆฌํ์ง ์๋๋ค.
์ ์์ ์ธ ์ ๋ก๋ ์ผ ๊ฒฝ์ฐ, ํ์ผ ๋ด์ฉ์ ์ฝ์ด base64๋ก ์ธ์ฝ๋ฉํ๊ณ , ์ด๋ฅผ ๋ฌธ์์ด๋ก ๋ณํํ์ฌ ์ ๋ชฉ์ ํค๋ก, ์ธ์ฝ๋ฉ๋ ํ์ผ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ผ๋ก ํ๋ ์ฌ์ ์ mini_database ๋ฆฌ์คํธ์ ์ถ๊ฐํ๋ค.
@app.route('/upload', methods=['GET', 'POST'])
def upload():
if request.method == 'POST':
f = request.files['file']
title = request.form.get('title', '')
if not f or title == '':
return render_template('upload.html')
en_data = base64.b64encode(f.read()).decode('utf-8')
mini_database.append({title: en_data})
return redirect(url_for('view'))
else:
return render_template('upload.html')
๋จผ์ url๋ก get ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต ๊ฐ์ผ๋ก ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ๋ request ํ์ด์ง๋ฅผ ์ต์คํ๋ก์ํด๋ณผ ๊ฒ์ด๋ค.
๋ฐ๋ผ์ ์๋์ ๊ฐ์ ์์ฒญ์ ๋ณด๋ผ ์ ์๋ค.
file:// ์ด ํต์งธ๋ก ํํฐ๋ง์ ํ๊ณ ์์ผ๋ฏ๋ก, /์ ํ๋๋ง ์ ๋ ฅํ๊ณ , flag ๋ฌธ์์ด์ ๋ํด์๋ url ์ธ์ฝ๋ฉ์ ํตํด (f = %66) ํํฐ๋ง์ ์ฐํํ๋ค.
์ดํ hi๋ผ๋ ์ด๋ฆ์ ์ด๋ฏธ์ง๊ฐ ์ฝ์ ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๊ฐ๋ฐ์ ๋๊ตฌ์์ html ์ฝ๋๋ฅผ ๋ณด๋ฉด base64๋ก ์ธ์ฝ๋ฉ๋ ๊ฐ์ ํ์ธํ ์ ์๋ค.
๋์ฝ๋ฉํ๋ฉด ํ๋๊ทธ ํ๋ ์ฑ๊ณต