一、前言
想象一下,假如某天我们发现了一个未受限制的文件上传漏洞,已经将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
选项禁用system
、exec
、shell_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
配置选项可以用来激活预认证模式中的rsh
及ssh
协议。
-oProxyCommand
参数
SSH
命令中用到了许多命令,其中我们可以使用-o
参数来设置连接期间可用的各种选项。在建立SSH连接之前,我们可以设置ProxyCommand
参数,如下所示:
ssh -oProxyCommand="touch tmp.txt" localhost
当我们执行这条命令时,可以发现即便我们没有建立与localhost
的SSH连接,也会创建tmp.txt
文件。
根据前面的分析,即使当前系统禁用了system
、passthru
等命令,如果存在RFI或者LFI漏洞,目标系统还是有命令执行风险。
三、缓解措施
我们可以采取两种措施来缓解imap
PHP扩展所带来的风险。第一种方法是检查传递给imap_open
的用户输入参数中是否存在任何特殊字符(如斜杠),这样就能避免存在远程代码执行执行(RCE)漏洞。前面提到过,我们可以在mailbox
参数中使用某些标志,其中/norsh
标志可以用来禁用IMAP预身份认证模式。
此外,为了避免攻击者绕过disable_functions
选项,我们可以将php.ini
文件中imap.enable_insecure_rsh
选项的值设置为0
。然而在PHP 5中并不能使用这个选项,因此我们应该慎重考虑,判断是否需要使用imap
扩展,是否需要将imap_open
添加到禁用函数列表中。