如何绕过PHP中被禁用的函数

一、前言

想象一下,假如某天我们发现了一个未受限制的文件上传漏洞,已经将web shell上传到目标服务器上;或者假如我们可以通过LFI(Local File Inclusion,本地文件包含)或RFI(Remote File Inclusion,远程文件包含)漏洞在目标系统上执行命令,正准备大干一场。

当我们执行某些命令后,希望能够在服务端调用系统函数时,却看到突如其来的一个警告,声称由于该函数已被禁用,因此不能调用:

www.example.com/shell.php?cmd=whoami

Warning: system() has been disabled for security reasons in /var/www/html/shell.php on line 6

我们可以在php.ini配置文件中配置disable_functions选项,禁用某些函数。为了加固系统安全,通常的建议是通过disable_functions选项禁用systemexecshell_exec以及passthru等函数。然而,最近Twoster在俄罗斯Antichat论坛上公布了一个新的方法,能够绕过这种安全机制。在本文中我们会与大家分享这方面技术细节。

 

二、绕过方法

在Antichat论坛上公布后该方法后,Anton Lopanitsyn上周也在Github上分享了利用代码。 在利用代码中,我们可以发现这种绕过方法依赖的是imap_open()函数,在PHP上安装imap扩展后就会激活该函数。

<?php
# CRLF (c)
# echo '1234567890'>/tmp/test0001

$server = "x -oProxyCommand=echotZWNobyAnMTIzNDU2Nzg5MCc+L3RtcC90ZXN0MDAwMQo=|base64t-d|sh}";

imap_open('{'.$server.':143/imap}INBOX', '', '') or die("nnError: ".imap_last_error());

PHP函数库(core)中并不包含imap_open()函数,该函数是imapd的一个封装函数,由华盛顿大学的研究人员开发。前文提到过,只有当我们安装IMAP PHP扩展后,PHP才会包含imap_open()函数。接下来让我们逐步分析利用代码中的每个组件。

imap_open函数参数

我们先来观察一下mailbox参数,理解利用代码中imap_open函数的作用。该函数的语法如下:

resource imap_open ( string $mailbox , string $username , string $password [, int $options = 0 [, int $n_retries = 0 [, array $params = NULL ]]] )

mailbox参数的值由服务器名和服务器上的mailbox文件路径所组成,INBOX代表的是当前用户的个人邮箱。比如,我们可以通过如下方式来设置mailbox参数:

$mbox = imap_open ("{localhost:993/PROTOCOL/FLAG}INBOX", "user_id", "password");

在括号内的字符串中,我们可以看到服务器名称(或者IP地址)、端口号以及协议名称。用户可以在协议名后设置标志(第3个参数)。

在PHP官方文档中,关于imap_open参数的设置有如下一段警告内容:

根据警告信息,除非我们禁用了enable_insecure_rsh选项,否则不要将用户数据直接传输到mailbox参数中。现在让我们看看IMAP扩展的工作过程,理解enable_insecure_rsh选项所发挥的作用,以及官方文档为什么建议用户禁用该选项。

IMAP服务器类型及SSH连接

现在有两种基于Unix的IMAP服务器被人们广泛使用,一种为华盛顿大学开发的imapd,另一种为Cyrus开发的IMAP服务器。

Cyrus会将用户邮件存储到内置数据库中,只有通过IMAP协议才能访问Cyrus。因此,当使用Cyrus时,已安装IMAP的Unix系统上的用户账户与IMAP账户之间并没有任何关联。

另一方面,imapd会将邮箱存储到文件中,而这些文件由Unix系统中邮件用户所有,如/var/spool/mail,因此imapd对应的用户账户以及访问权限与Unix服务器直接相关。如果邮件存放在我们具备访问权限的spool文件中,我们可以通过SSH方式登录系统,验证我们对这些文件的访问权限。

当能够使用SSH时,整个过程并不需要建立IMAP连接。imap_open函数首先会建立SSH连接,如果认证通过,则会在没有IMAP连接的情况下继续执行,这就是所谓的IMAP预认证模式。基于这一点,我们才会看到前面提到的关于mailbox参数的警告信息。在设置SSH连接时,mailbox参数的值会以参数形式传递给SSH命令。

在安全SSH协议被广泛使用之前,还有一个名为rsh的协议。然而默认情况下这种协议非常不安全,没有使用加密技术,因此不应当在本地网络环境外使用(甚至也不要在本地网络环境中使用)。imap.enable_insecure_rsh配置选项可以用来激活预认证模式中的rshssh协议。

-oProxyCommand参数

SSH命令中用到了许多命令,其中我们可以使用-o参数来设置连接期间可用的各种选项。在建立SSH连接之前,我们可以设置ProxyCommand参数,如下所示:

ssh -oProxyCommand="touch tmp.txt" localhost

当我们执行这条命令时,可以发现即便我们没有建立与localhost的SSH连接,也会创建tmp.txt文件。

根据前面的分析,即使当前系统禁用了systempassthru等命令,如果存在RFI或者LFI漏洞,目标系统还是有命令执行风险。

 

三、缓解措施

我们可以采取两种措施来缓解imap PHP扩展所带来的风险。第一种方法是检查传递给imap_open的用户输入参数中是否存在任何特殊字符(如斜杠),这样就能避免存在远程代码执行执行(RCE)漏洞。前面提到过,我们可以在mailbox参数中使用某些标志,其中/norsh标志可以用来禁用IMAP预身份认证模式。

此外,为了避免攻击者绕过disable_functions选项,我们可以将php.ini文件中imap.enable_insecure_rsh选项的值设置为0。然而在PHP 5中并不能使用这个选项,因此我们应该慎重考虑,判断是否需要使用imap扩展,是否需要将imap_open添加到禁用函数列表中。

(完)