MoeCTF2024 WriteUP

CTFerName:fifker
题目链接:https://ctf.xidian.edu.cn/training/10

开发与运维基础

开发与运维基础入门指北

要求主要有这几个:

在你的家目录中有一个文件夹:

1
2
3
1.将形如 xxxxxxx.bak 的文件清理掉
2.将所有带 xml 后缀的文件重命名为 html 后缀
3.按照前缀将所有文件分类到对应的文件夹,例如 f572d396fae9206628714fb2ce00f72e94f2258f.txt 需要放在 f5/72/f572d396fae9206628714fb2ce00f72e94f2258f.txt 下。

全部整理完毕后,使用 root 用户将其移至 /var/www/html 文件夹下,然后稍等片刻,从 /var/log/nginx/access.log 里找 flag 就可以了!

使用FinalShell连接,不过操作均使用shell命令。

我们来一个一个完成:

任务一

首先,将形如 xxxxxxx.bak 的文件清理掉。那么我们就用正则表达式匹配相关文件,然后删除(使用rm)。命令如下:

1
rm *.bak

任务二

第二步,看到改名字,首先能想到的就是rename命令了,可惜该系统没有该命令:

alt text

不然可以使用

1
rename 's/\.xml$/.html/' *.xml

或者

1
rename .xml .html *.xml

那么就使用for循环吧:

1
2
3
for file in *.xml; do
mv "$file" "${file%.xml}.html"
done

来解析一下这几行shell:

for file in *.xml,就是一个简单的for循环,在当前目录下选取所有xml后缀的文件。

然后mv "$file" "${file%.xml}.html",${file%.xml}的意思是从变量file的值中,从末尾删除.xml这个后缀。最后利用.html直接拼接上去。

这样就可以用满足全部条件。

任务三

最后一步,这一步比较复杂,使用shell脚本(附上注释):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for file in *.txt *.html; do
# 获取文件名(不含后缀)
basename="${file%.txt}"

# 取前2个字符作为第一级目录
dir1="${basename:0:2}"

# 取第3-4个字符作为第二级目录
dir2="${basename:2:2}"

# 创建目录(-p 参数会自动创建父目录)
mkdir -p "$dir1/$dir2"

# 移动文件
mv "$file" "$dir1/$dir2/"
done

完成任务后,登录root用户,使用su命令:

alt text

移动文件。

1
mv /home/user/* /var/www/html

查看flag。

1
cat /var/log/nginx/access.log

alt text

哦不!我的libc!

这是一题抢救glibc的题,有点吃手速(只有一次连接机会且断的很快)

题目连接方式:

1
2
$ ssh root@127.0.0.1 -p <port>
root 的密码: toor

由于为了降低难度,只需要恢复ls和cat即可完成题目目标,无须真的恢复服务器的glibc,所以出现了一堆非预期。

题目说到:flag 文件在 /flag.txt 。因此最简单的办法就是:

1
echo "$(< /flag.txt)"

alt text

哦不!我的nginx!

本题模拟 glibc 被删除后的服务器救援。动态链接程序都需要动态加载器 ld-linux-x86-64.so.2 和 libc.so.6,因此 ls、cat、cp、nginx 等命令都会无法启动。

但当前 SSH 中已经运行的 Bash 不会立刻退出。它启动时所需的库已经映射到内存,因此 Bash 内建的 printf、read、变量、重定向和 /dev/tcp 仍能使用。

本题的关键,就是先利用 Bash 内建能力写入一个静态 BusyBox,再恢复 libc 和动态加载器。

注意:坏机只能使用第一次登录得到的 Bash。不要退出 SSH,也不要建立新的 root SSH 连接,否则新的 /bin/bash 无法启动。

1. 准备静态 BusyBox

BusyBox 是静态链接程序,不依赖目标机的 glibc。先在本地下载 x86_64 musl 版本:

1
wget https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox

将其上传到 neighbor:

1
scp -P <neighbor端口> ./busybox user@127.0.0.1:~/busybox

BusyBox 是多调用程序,执行时会根据自己的文件名决定模拟哪个命令。例如文件名为 /bin/cp 时,它会自动作为 cp 工作。

2. 为什么写入 /bin/cp

坏机没有 chmod,直接新建 /tmp/busybox 后无法赋予可执行权限。

但 /bin/cp 原本已经是可执行文件。使用重定向覆盖它的内容时,文件权限不会改变:

1
printf '内容' > /bin/cp

因此把静态 BusyBox 写入 /bin/cp 后,/bin/cp 仍然可以执行,并且会成为 BusyBox 的 cp applet。

3. 用 neighbor 传输 BusyBox

二进制文件不能直接可靠地存入 Bash 变量,所以先将每个字节变成 \xNN 形式的可打印文本。

在 neighbor 中执行:

1
2
3
od -v -An -tx1 ~/busybox |
awk '{for (i=1;i<=NF;i++) printf "\\x%s", $i}' |
nc -lvp 8000

来看看这有啥用:

首先,od -An -tx1:将 BusyBox 转为十六进制字节,不输出地址。
其次,awk:把 7f 45 4c 46 转成 \x7f\x45\x4c\x46。
最后,nc -lvp 8000:监听 8000 端口,等待坏机连接。

假设 BusyBox 大小为 S 字节,转义后长度为 4*S。本题实际 BusyBox 为 1131168 字节,因此需要读取:

1
1131168 * 4 = 4524672

在坏机第一次 root SSH 会话中执行:

1
2
3
4
exec 3<>/dev/tcp/127.0.0.1/8000
IFS= read -r -u 3 -N 4524672 payload
printf '%b' "$payload" > /bin/cp
unset payload

其中:

  • exec 3<>...:将 TCP 连接绑定到文件描述符 3。
  • read -u 3:从描述符 3 读取数据。
  • -N 4524672:精确读取这么多字符。
  • -r:保留反斜杠,不把 \x 提前处理。
  • printf '%b':将 \xNN 重新解释为真实二进制字节。

至此,/bin/cp 已经是可执行的静态 BusyBox。

4. 恢复基础命令

1
2
3
4
5
6
/bin/cp /bin/cp /bin/chmod
/bin/cp /bin/cp /bin/cat
/bin/cp /bin/cp /bin/nc
/bin/cp /bin/cp /bin/sha256sum

/bin/chmod 755 /bin/cp /bin/chmod /bin/cat /bin/nc /bin/sha256sum

这样就重新获得了文件复制、修改权限、查看文件和网络传输能力。

如果只做前面那一题,到这里将 BusyBox 复制为 /bin/ls、/bin/cat 即可读取 /flag.txt:

1
2
3
/bin/cp /bin/cp /bin/ls
/bin/cp /bin/cp /bin/cat
/bin/cat /flag.txt

5. 恢复 libc 和动态加载器

由于本道题要求启动 Nginx,因此还需要恢复完整动态运行环境。

neighbor 发送 libc:

1
nc -lvp 8001 < /usr/lib/x86_64-linux-gnu/libc.so.6

坏机接收:

1
/bin/nc 127.0.0.1 8001 > /usr/lib/x86_64-linux-gnu/libc.so.6

neighbor 发送动态加载器:

1
nc -lvp 8002 < /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2

坏机接收:

1
/bin/nc 127.0.0.1 8002 > /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2

最后恢复执行权限:

1
2
/bin/chmod 755 /usr/lib/x86_64-linux-gnu/libc.so.6
/bin/chmod 755 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2

两者缺一不可:ld-linux 负责装载动态程序,libc 提供程序运行所需的基础函数。

6. 删除事故触发器并启动 Nginx

题目通过 /root/.bashrc 模拟删除 glibc 的事故,因此恢复后必须删除它:

1
rm -f /root/.bashrc

验证动态程序已经能运行:

1
2
ls -l /usr/lib/x86_64-linux-gnu/libc.so.6
nginx -t

启动 Nginx:

1
2
nginx -g 'daemon off;' &
sleep 5

客户端会持续请求服务器,并将 flag 放在请求参数中。查看访问日志:

1
cat /var/log/nginx/access.log

可以得到flag:

1
moectf{th@ts-NoT+vERY_H@Rd-T0_R3scUe,riGhT?1e3e4b}