为了练习源代码审查,我一直在深入研究开源 LMS 代码库的结构,以便找到未被发现的漏洞。最初,我主要关注 Chamilo LMS。之后,我查看了 Moodle LMS。
在每个LMS中,我们将使用不同的方法至少执行两次扫描:源-汇(Source-to-Sink) 和汇-源 (Sink to Source)。为了搜索 Source to Sink,跟踪用户控制的输入(源)以查看它们是否被发送到敏感的 PHP 函数,例如 exec()、system()、……。至于 Sink to Source,我们只是颠倒步骤,从敏感的PHP函数开始,尝试查找用户控制的输入。
在搜索 Sink to Source 时,我们可以开始在代码库中搜索常见的PHP Sink。至于从 Source 到 Sink 的搜索,我们首先要看看每个代码库是如何接收用户输入的。
Chamilo
由于代码库是用 PHP 编写的,没有很多自定义函数包装,我们可以使用以下正则表达式进行搜索,以查看分配给 PHP 变量的每个 HTTP 请求参数值是如何使用的:
Moodle
尽管代码库也是用PHP编写的,但要稍微复杂一些,因为它们抽象出了许多默认的PHP特性,并编写了自己的包装器。例如,当读取HTTP请求参数时,他们会调用他们的自定义函数optional_param()或required_param()。然后,这些包装器函数将调用典型的$_GET[]和$_POST[]来获取参数值。
读取参数值后,它被传递给另一个自定义函数clean_param(),在这个函数中,根据不同的$type对输入进行清理:
尽管定义了许多清理方法,但第一种示例PARAM_RAW 引起了我们的注意,因为它不会对指定的 HTTP 请求参数执行任何清理。因此,我们可以使用以下正则表达式在代码库中搜索用户输入直接分配给变量的区域:
发现的漏洞
通过搜索这些过滤后的文件,我能够在 Chamilo 和 Moodle 上找到相当多的漏洞。让我们来看看一些有趣的发现。
Chamilo:不安全的反序列化和不安全的文件上传导致远程代码执行。
这是一个有趣的发现,它将两个不同的漏洞组合到一个链中,导致远程代码执行。
不安全的文件上传
在 Chamilo,学生和教师可以将文件上传到他们管理的任何课程,但是,该应用程序会将某些文件扩展名列入黑名单。例如,它不允许上传 .php或其任何变体,如 .php3、.php4、.phar 等,这样做会导致应用程序将文件扩展名重命名为 .phps。
当应用程序无法确保上传的图像文件就是图像时,就会出现该漏洞,因为它只检查文件扩展名。这意味着只要扩展名不是列入黑名单的扩展名之一,用户就可以上传任意文件。
我发现只有教师才能上传到课程的文档部分。这很重要,因为上传到文档里的文件具有可确定的本地文件路径:
学生只能上传到课程的 Dropbox 部分,该部分会随机化存储在服务器上的实际文件中。
然后,我使用 PHPGGC 生成了一个带有 RCE 负载的 phar 压缩文件。在选择要使用的 PHP 小工具时,我检查了 Chamilo 拥有的 PHP 依赖项,发现有一些小工具可以使用。在这个例子中,我将使用 Monolog/RCE1。以下命令将生成一个 phar 文件 (rce.jpg),该文件将在反序列化时对我的攻击机执行 curl 命令。
验证此 phar 文件是否可以上传:
现在可以在 /path/to/chamilo/app/courses/< COURSE_CODE >/document/目录中找到:
安装phar负载后,我们需要找到一种通过反序列化来触发它的方法。
不安全的反序列化
我在 /main/document/save_pixlr.php 发现了一个终端,它有以下代码:
当用户控制的输入 $urlcontents 直接传入 file_get_contents() 函数时,就会出现该漏洞。这意味着用户可以指定任意协议,例如 phar://,这将反序列化本地文件。我们现在可以放心地忽略 remove_XSS() 函数,因为它只从输入中去除 < > 字符。
能够控制进入 file_get_contents() 的内容也可以被视为 SSRF 漏洞,因为应用程序不限制允许的 URL。
从上面的代码中,我们看到如果执行没有结束,就必须设置一些GET参数。我们还看到一个会话变量paint_dir必须存在。这可以通过访问http://CHAMILO_WEBSITE/main/document/create_paint.php来满足,它用以下代码为我们设置会话变量:
将漏洞联系在一起
我们之前已经将phar上传到服务器上,并知道它的本地路径。现在,在/main/document/save_pixlr.php反序列化终端,我们通过图像GET参数发送URL字符串phar:// var/www/chamilo/app/courses/C001/document/rce.jpg:
这将导致 phar 压缩文件的反序列化,并且将触发命令执行有效负载。
攻击者主机如下所示:
这确认了远程代码执行。这是一个带有反向 shell 有效负载的完整链的演示:
Chamilo:导致远程代码执行的跨站请求伪造 (CSRF)
安全管理页面上缺乏反 CSRF 措施,这允许攻击者制作 CSRF 有效负载,以便当经过身份验证的管理员触发它时,更改站点安全设置。一个可以更改的有趣功能是站点范围的黑名单和白名单,这将允许危险的文件被上传。
我们之前已经发现应用程序会自己清理上传的文件名,如函数 /main/inc/lib/fileUpload.lib.php:htaccess2txt() 中所示。这意味着当上传的文件名是.htaccess时,生成的文件名将是htaccess.txt。
下面的 PoC 会将扩展名 txt 附加到黑名单中,并将黑名单中的扩展名替换为 /../.htaccess
由于 .txt 现在是列入黑名单的扩展名,它将被替换为 /../.htaccess。因此最终的文件名是 htaccess/../.htaccess 并且因为只使用了文件名,所以返回的结果是 .htaccess。
然后,教师用户可以上传 .htaccess 文件,以便在当前目录中执行带有自定义扩展名 (.1337) 的 PHP 代码:
然后,上传扩展名为 .1337 的 PHP 文件:
这将允许任意代码执行。
或者,我们可以将 php添加到黑名单中,也可以用 php.ini 替换黑名单中的扩展。这是因为还有一个清理函数,它可以清理 /main/inc/lib/fileUpload.lib.php:php2phps() 中存在的 php 文件扩展名:
每当上传的文件包含扩展名 .php 时,生成的文件名将是 .phps。由于 .phps 现在是列入黑名单的扩展名,它将被替换。因此,最终的文件名将是 .php。
然后,将成功上传文件名为 php-backdoor.php 的 PHP webshell:
但是,由于根目录中存在.htaccess,我们将无法直接执行上传的PHP文件:
检查 web 根目录下的 .htaccess 文件,似乎可以通过在要执行的 PHP 文件的末尾附加一个 / 来绕过正则表达式:
这可以再次为我们提供远程代码执行功能。
这意味着,拥有多个清理代码可能会导致意想不到的效果。我们看到了文件上传清理如何被站点的安全功能阻止,因为它们相互抵消。
Chamilo :经过身份验证的SQL盲注
SQL Injection(Blind),即SQL盲注,与一般注入的区别在于,一般的注入攻击者可以直接从页面上看到注入语句的执行结果,而盲注时攻击者通常是无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知,因此盲注的难度要比一般注入高。目前网络上现存的SQL注入漏洞大多是SQL盲注。
代码中一共发现了 4 个经过身份验证的 SQL 盲注。所有这些都是通过以 Source to Sink 的方式对代码进行 grepping 发现的。
其中一个示例是来自 /main/blog/blog.php 的以下代码,其中经过身份验证的学生能够触发此漏洞:
由于原始查询仅选择 Integer 类型的单个列,我们可以执行基于布尔值的盲 SQL 注入攻击,以便从数据库中提取信息。因此,让我们列出TRUE 和 FALSE 查询:
真实示例:
假设示例:
由于 TRUE 和 FALSE 查询显示不同的响应,因此我们可以使用自动脚本逐个字符泄漏任意子查询的输出。当泄漏 SQL 查询 SELECT USER(); 的输出时,有效负载可能如下所示:
这是一个 TRUE 输出,意味着 SQL 查询 SELECT USER() 的第一个字符是“c”。
在/main/forum/download.php (Student), /main/inc/ajax/ practice .ajax.php (Teacher)和/main/session/session_category_list.php (session Admin)中发现了另外3个类似的经过身份验证的SQL盲注。
Chamilo:反射型 XSS
还记得之前我提到应用程序的 remove_XSS() 只删除 < > 标签吗?在研究这个函数如何清理输入时,我发现这个函数给了开发人员一种错误的安全感,因为即使输入通过该函数传递,仍然有可能实现 XSS。
在file /index.php 中,我们看到HTTP 参数firstpage 的值作为唯一变量直接传递给remove_XSS()。然后将返回值直接插入到 HTML 页面中的
并且它会触发 XSS,因为输入直接反映在 HTML 响应中。
由于我们有办法执行 XSS,所以可以假设受害者是经过身份验证的管理员。然后,使用以下有效负载(压缩为单行),我们可以将站点管理员更改为具有凭据 (xss:xss) 的自定义用户。
有效载荷将获取用户的 sesskey,这是一种反 csrf 措施,并提交更改站点管理员所需的 POST 请求。
在触发 XSS 负载之前显示用户列表的管理面板:
在触发有效负载后,请注意站点管理员已经被“Created by XSS”用户替换:
这样就拥有了Moodle 示例的所有权。
总结
进入大型代码库查找漏洞非常有趣,即使代码库可能已经成熟,但如果没有适当且强制执行的编码标准,仍然会出现漏洞。 从开发人员的角度来看,在处理大型代码库时,确保代码的每个功能部分都没有错误并同时不断添加新功能并非易事。 这就是为什么从头开始构建安全应用程序很重要的原因。
本文翻译自:https://starlabs.sg/blog/2021/11/diving-into-open-source-lms-codebases/如若转载,请注明原文地址