|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
/ E% C" _1 I3 v! w- p; W/ G漏洞作者:skysheep
) K0 D. f( f ?分析作者:Seay! c% o( R% z9 g
博客:http://www.cnseay.com/" P" C4 k. U9 B5 e/ l$ r
漏洞分析:
' `( I2 g9 ?3 ^$ Z6 i( y 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
- W. e X3 z: k1 F. M% Z& U( l$ u0 T$ y
3 c F6 a, J- c; M. M; M
1 J( e+ J( J( U2 K; E# rpublic function account_manage_info() {
+ Q$ V7 B- p2 I if(isset($_POST['dosubmit'])) {
! ]( I- H8 A2 V! c! z# F //更新用户昵称
( e1 ~' [9 s& d8 a $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
) K" m: @- Q0 r* X if($nickname) {
6 D1 L7 ~+ \) g6 r) X) [ $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 5 g( H" z; E# I1 a6 |8 I
if(!isset($cookietime)) { 3 V- m+ W0 G1 m# b! G9 l
$get_cookietime = param::get_cookie('cookietime');
) L. c4 z0 v( S1 B }
/ G6 @+ s1 v6 z7 ?9 R" j+ G $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); / n( @0 d/ B: ~7 ?- L
$cookietime = $_cookietime ? TIME + $_cookietime : 0; % c8 _$ c2 S! ~' Q. p& I
param::set_cookie('_nickname', $nickname, $cookietime); 3 q( p. u. u. `2 v7 a# U# \
}
& u$ B& L5 H+ J& f) c [: U require_once CACHE_MODEL_PATH.'member_input.class.php'; 6 z1 q& g( j/ X# v
require_once CACHE_MODEL_PATH.'member_update.class.php';
% s! E2 U7 ~# ?+ I $member_input = new member_input($this->memberinfo['modelid']); " Z( j2 K$ r- W
$modelinfo = $member_input->get($_POST['info']); 1 @/ M0 Y4 I$ X' t
$this->db->set_model($this->memberinfo['modelid']); # a$ D5 j" h8 C. n
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
5 o% n) }0 G" F! j0 I6 U7 q3 X ? if(!empty($membermodelinfo)) {
0 O! _0 P {# ^7 m+ S. q% O! C% A/ o $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
$ _1 e- m9 I+ d/ G$ } } else { 9 K8 ?7 f7 e7 }- d2 z
$modelinfo['userid'] = $this->memberinfo['userid']; ; e; D! S; s) B4 ^' W3 C9 p
$this->db->insert($modelinfo);
6 n, C$ H# g. E5 g7 g }
, b5 p$ z& V' r1 P) J3 T# W代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
! a3 Z2 r/ g ^4 M" a在\caches\caches_model\caches_data\ member_input.class.php 文件中:
1 x& d7 B: x: p0 i3 ^
4 e" l3 j4 O. h; L# S/ Y& ~- s) T F( K, M# e5 D
0 s* t0 i% ^7 g K
function get($data) { 3 w' `2 P2 Y1 I: {5 F
$this->data = $data = trim_script($data); & B. ? {* Y0 K6 C6 S! x' `4 V, v% n
$model_cache = getcache('member_model', 'commons'); 2 k9 W8 i9 f: z* q
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
% U& ], ]# u0 Y1 {% y $info = array(); . W7 [4 r8 y. q$ e9 n
$debar_filed = array('catid','title','style','thumb','status','islink','description');
" A$ J4 V* w2 B; h {) X; h! d if(is_array($data)) { & z. v1 z7 c) v% y5 U
foreach($data as $field=>$value) {
: F: ?8 y7 z2 ~ W8 V, ? if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
" l5 W0 J7 H6 @7 o- ^5 W6 G/ O $name = $this->fields[$field]['name'];
8 g) S& i' u7 [; d $minlength = $this->fields[$field]['minlength'];
9 o) n* a8 `& w1 Q$ C3 B* X $maxlength = $this->fields[$field]['maxlength']; , H3 Y$ f L% h6 z: D
$pattern = $this->fields[$field]['pattern']; ! M7 M# q3 c- I9 y+ T
$errortips = $this->fields[$field]['errortips'];
; M8 \: @! N3 Q. G4 g if(empty($errortips)) $errortips = "$name 不符合要求!"; ! F4 [$ w' S" K) Q
$length = empty($value) ? 0 : strlen($value);
3 q& C. E7 w* ?% K if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ) N+ C0 I8 P" b/ v
if($maxlength && $length > $maxlength && !$isimport) { ( j2 m) ?$ a! c; E/ \! u+ I# H( F+ v
showmessage("$name 不得超过 $maxlength 个字符!");
+ F* z! |! I1 J; Y( d/ D } else {
( g' O8 Q9 r; n4 f str_cut($value, $maxlength); ) n; i0 { m1 U2 R
} # K$ O: z! z! m2 }9 l' ?
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
" I8 v( `* i. i if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
2 U% A1 b( t) k/ ^ $func = $this->fields[$field]['formtype']; 1 p* v8 W8 v. X, ^( u
if(method_exists($this, $func)) $value = $this->$func($field, $value);
' ]7 T& b; I# \) f: D4 U $info[$field] = $value; 3 L, ?: u; x' y
}
2 C5 u+ B$ h/ N/ P1 Z' f; N+ @+ Z } % M7 N, w8 O& Q" p" ~
return $info;
/ n, x* h" b9 c } , E6 L! }; w/ s6 F( Q
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
$ J. h0 ~; |/ s( m K \& j7 r$ O) z; ~& C, l. S2 H
再到phpcms\modules\member\index.php 文件account_manage_info函数$ }- m; w! n' v9 f D5 x
过了get()函数之后。6 p+ ?! V" o7 H; m
, }) a& W2 j% c# N* m5 V! L
& p3 f( |* S4 E9 v x4 I& _$modelinfo = $member_input->get($_POST['info']); $ p, D2 l E) k$ X+ b
$this->db->set_model($this->memberinfo['modelid']); 3 g/ m! {5 H0 \: ], f; q
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); - s- H) `7 Z3 A% e" Z& r- ]
if(!empty($membermodelinfo)) {
* x- w9 z+ o2 [ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
" W7 g, H; w4 `, J/ w) \ } else { 4 _5 I) G2 J6 A8 r: G
直接带入数据库,update函数我们跟进看看8 |" y' S3 `5 E$ @
A0 t2 m+ E5 T$ l S* r
7 p' s7 G+ q8 d
public function update($data, $table, $where = '') { Q0 {7 o% j q8 E3 _/ ?
if($table == '' or $where == '') {
6 J9 J, n( v/ H* x% _9 h' J" w return false; 2 l7 r2 _7 I2 M% Z1 r
}
" M# `9 j5 _: E6 V1 r" M $where = ' WHERE '.$where; " k8 P8 E7 F! O% k! i
$field = ''; ) T& Z# F2 j* e$ P7 _
if(is_string($data) && $data != '') { 2 o6 W+ Z5 o7 _
$field = $data;
% P: i/ F/ z& u! K* ]- c. x } elseif (is_array($data) && count($data) > 0) { ; Z6 l# V! \2 C. r' O8 Q) m
$fields = array(); 6 C' D$ U# r; [4 ]& H2 ?" C* U
foreach($data as $k=>$v) { 2 ~$ Z" U2 ^4 ?4 n; U1 L
switch (substr($v, 0, 2)) {
! i* J2 Q: B* X6 q case '+=':
: o3 x: P7 }$ U" B! @* ^2 q, G $v = substr($v,2); 5 u) Z" a& _- e5 ^2 w, k
if (is_numeric($v)) {
2 A9 H! G9 z4 K* Q/ S $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 8 t" e6 d) ^9 v" f3 d }( ~
} else { 6 }2 v& X+ a9 Y- P( ?/ M
continue;
2 p! |' K9 n3 K; J5 A }
, ~. k( t+ \( b, g0 l4 }5 W( a break; # f$ O% J }; g2 l) A$ D
case '-=':
; p" S+ j x/ b# \ $v = substr($v,2);
" z: Y% k3 \/ x; F6 }% [- Z! F if (is_numeric($v)) { - |4 ?+ u2 F% @, e" M) Q9 U
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
0 o5 \. Y' g) `3 y# [0 u6 h } else { & j' _9 k6 r, S6 N
continue; 1 h e' e/ d3 H: N: N1 l' o
} 5 F$ m9 V" Q; P0 m$ b" ^
break;
: B* K, p/ n, ~ r- k default: 6 ?8 d/ G1 r+ [0 W3 u$ q- A
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
+ F4 ?2 } a U, g u' x }
; Y% t o1 @, q }
" e% r/ k5 v2 u& p3 P. p8 p $field = implode(',', $fields); 5 _7 T/ `5 J* P# S3 N* c
} else {
4 A7 _+ }* J _, l return false; ; x7 y1 S/ ^; {7 Q8 U' s
}
" S* b) V D5 U; L: J) ?! ? $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
, P" f/ L; S Q) @$ u3 v print_r($sql);
6 a5 R' C. |! f! D; l. {& C return $this->execute($sql); 5 O7 [. W% T2 C; f
}
6 r. g, r5 B" h# D7 W从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
3 X) Q8 s1 W) R# \5 x, a' _2 Y2 l. d5 I7 Z6 z
攻击测试:% x' W! Y$ @) r8 K
测试地址http://localhost
# _: N. E' I7 D+ W) e 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
7 O! @$ S8 O. o+ G
, i+ L# O8 a, ^# a Z
9 l J$ J. E) ~$ s! v: ^3 S3 {8 T8 j
# I& _& X* t% H" A% R8 `8 i$ j+ Y" v! c7 p! v, A$ O4 D+ f: u+ {
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|