|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告& _( T/ M: a) @: J: M1 v; v
漏洞作者:skysheep+ B4 B8 p( j+ Y9 L" O% C; S; i
分析作者:Seay
/ S. S$ ` ~0 k' j1 y博客:http://www.cnseay.com/
3 x1 ]& ]0 i' n1 t( P7 ~. ]漏洞分析:9 m- y0 Q7 ~; @8 r. V, z
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。5 v8 z- N3 X# W3 _7 n, c
B; }8 f; k5 ^! \/ ~
C( I& |- s5 R0 B! w6 X
5 e0 q" I i3 e) L/ Upublic function account_manage_info() { ' B% f. x$ t1 D; p7 t. L1 o8 `, l
if(isset($_POST['dosubmit'])) { . | S" ]0 `3 I. C
//更新用户昵称
5 i: @; c" z% u S& p/ s $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
8 L: M$ p; W$ i if($nickname) { 6 k9 Q3 [# s9 m: C. O3 M2 D
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ( _' R* C! X) x7 T. E4 L: }
if(!isset($cookietime)) { ) j; m3 r6 J6 N5 r
$get_cookietime = param::get_cookie('cookietime'); 7 p' u6 F- i. j6 n, G3 d+ [9 @. p: j
} / p1 y2 l4 Y+ V4 E* C# m( q2 D/ l
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
" r4 R! v% a$ L9 W# X$ A/ X% u4 W, C$ v $cookietime = $_cookietime ? TIME + $_cookietime : 0;
9 ~0 A+ h- ^# U, f5 ] param::set_cookie('_nickname', $nickname, $cookietime);
9 N- m6 e9 W: o8 T }
) Q7 W$ Y/ u2 s% D! p( ? require_once CACHE_MODEL_PATH.'member_input.class.php';
- H/ q! N o0 G require_once CACHE_MODEL_PATH.'member_update.class.php'; $ B- j+ M, z0 k7 g
$member_input = new member_input($this->memberinfo['modelid']); ( h0 G- j5 d! @4 v$ l' H4 v
$modelinfo = $member_input->get($_POST['info']);
1 d! O# k9 `. f2 s6 z $this->db->set_model($this->memberinfo['modelid']);
' G3 D' q/ h4 | $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
8 @$ S- L' M1 c- P0 H4 V& ] if(!empty($membermodelinfo)) { ) m' m% d) L+ ~* I# y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ; a7 |, o5 K0 ]) O+ b
} else {
# H0 @( w2 O& k% ^0 G2 |) Y $modelinfo['userid'] = $this->memberinfo['userid'];
) |) m2 ?5 H2 s4 d% n4 O $this->db->insert($modelinfo); 0 v" m, T+ E) h) b/ j; n- C
} / K! T; [( r% G" @2 G' y
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,! C1 E% W* D) m6 A7 y9 f# E6 Y) |
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
. L8 [8 A' n* |2 | @: g0 m# R7 g+ p! `$ W; B3 X$ h# o0 E
. I) ]: E# t* P0 M, t+ V5 H
2 o9 D8 \4 R. q+ f9 p! _function get($data) {
/ \8 p$ v- o% P8 m, O $this->data = $data = trim_script($data); 0 V/ {# e7 [4 H8 a# s
$model_cache = getcache('member_model', 'commons'); , C4 C, D: ^2 k! \! i) h1 U
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; , E% b4 ~$ Z$ Z' N
$info = array(); . ?' A$ T4 Q4 P; }& h. B' ^3 J
$debar_filed = array('catid','title','style','thumb','status','islink','description');
% \8 \: o: p9 d; C if(is_array($data)) { _. ]9 Y7 N6 Q# j. ^2 T# m' r
foreach($data as $field=>$value) { e7 {( B+ X& h! e4 i* |) S
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
" {" n, U3 @0 T; Y" J: N $name = $this->fields[$field]['name']; 5 C$ i: d/ D, r
$minlength = $this->fields[$field]['minlength']; # w$ ?& J4 d- [* S
$maxlength = $this->fields[$field]['maxlength'];
% R7 R0 G9 F8 Y1 K7 j1 ]: k, X/ E $pattern = $this->fields[$field]['pattern'];
" h: {8 j! _# r, m+ q $errortips = $this->fields[$field]['errortips']; ) M* X$ s4 T4 E. h4 J
if(empty($errortips)) $errortips = "$name 不符合要求!"; % \6 `: W7 M, n8 o! o8 m+ e
$length = empty($value) ? 0 : strlen($value); - H, {! u) ]5 e( G" g4 I3 b% ?
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ; o0 F$ d6 X- J* ^% q* l# n, ]
if($maxlength && $length > $maxlength && !$isimport) { ' y5 g9 R5 w- I6 T# p+ ]6 G% O
showmessage("$name 不得超过 $maxlength 个字符!"); 6 h6 r: \1 I8 w. ^
} else { / _, t; L# ^& i: K' ^( ]
str_cut($value, $maxlength);
! s4 U j* R) k2 [. G }
; x2 z) X/ }& L! _8 g if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); / b5 I: ~! P& _& h5 |+ t
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 6 P9 s. m. U; A7 U7 j
$func = $this->fields[$field]['formtype']; 4 q/ ?! a) \- s, U/ `
if(method_exists($this, $func)) $value = $this->$func($field, $value);
5 d( Q" B5 q0 D1 H $info[$field] = $value; 0 o0 P+ F. Z9 f
} ; K5 Q2 v! _1 j
}
' l0 }* b- d8 l: i/ Y# E return $info; * X0 g* t( `9 Z+ S
} 6 Z" p7 m* a+ x6 M( K" _, i
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
) A- p0 Q3 ^; ?8 H7 K9 N
; m- U/ ~! B; |9 k1 F& \, n& @再到phpcms\modules\member\index.php 文件account_manage_info函数
/ [. ]( i9 w9 Y过了get()函数之后。6 [2 o8 _. E3 a1 t
' k8 r* q' \: z( _ ' q8 k5 N: O9 z4 G5 b, t* C8 R
$modelinfo = $member_input->get($_POST['info']); $ V: w2 ^1 L# ^" C1 P \9 g
$this->db->set_model($this->memberinfo['modelid']);
% }1 W: i( w9 J# e5 O $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
0 `7 l5 z5 \- B if(!empty($membermodelinfo)) {
; L6 \3 b+ P' ]4 f% ` $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 0 Q2 ?! g( U) E" u/ ^, V( K2 k
} else { - ^; Z7 K1 l* m3 |: E) R% P
直接带入数据库,update函数我们跟进看看
" H t6 }( J9 \2 ^6 B6 ?3 X: i
a; z/ ^- }) z3 b% C- ^
/ m# O- w" H' Q) L1 v t0 u1 o( tpublic function update($data, $table, $where = '') { ) o2 F9 Q) @- X2 D; m0 p6 @
if($table == '' or $where == '') {
7 P# P2 s4 C/ P$ m return false; ' a' C9 S2 o! B! d" e3 s8 |
}
^% o& d+ n) \* I $where = ' WHERE '.$where;
0 _, n0 f0 H! e; a7 |3 w# f $field = ''; ; T8 Q, l# ~9 M1 F/ Y' x
if(is_string($data) && $data != '') { ! n4 M. ^9 U f5 L% q$ Z
$field = $data;
: o3 H" ^( j9 N* _, m( M } elseif (is_array($data) && count($data) > 0) {
) V# ]! D$ }) p, P, p% H $fields = array();
+ B! H5 S) Y e; p# u l+ V2 } foreach($data as $k=>$v) {
: {# I: b5 o) S7 |' S$ R) x switch (substr($v, 0, 2)) { 3 l7 j8 m5 |6 U% \; H, R- S. N' {
case '+=': " I4 u) R; {6 ?* n
$v = substr($v,2);
' ?3 i" k6 i. |% F3 \ if (is_numeric($v)) { & `' M. d) P# g, R
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); % m5 Q8 S6 \, V( Q, | T5 F& l
} else {
$ x) `6 C) d2 F: H8 ?1 |" G* ^ continue; 3 F% ?, { y# Q4 c5 h8 [ C
}
, S' I$ j3 l7 ]" l- @% s break;
' C, w* s8 h+ M case '-=': ' C) [+ `! |: u/ I
$v = substr($v,2); ! V0 c. {4 r" ^9 |1 M
if (is_numeric($v)) {
W% a8 x. j, r $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); : S3 u7 R# ]' h' N: {( u
} else { ! O i. n/ g% E0 _3 i
continue; / I' d* L) g& K& h1 X
} 5 Z @( i4 H2 B" @2 r: i- H* y
break; # _! `) Y+ Y* E9 V, B( ?# h
default:
6 |% m) d- J6 x% W# h4 ^ $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); , ` Z' c* b- c
}
' I. }4 \' V8 X7 g+ b+ J, u }
% b1 m! P8 [" O, B6 U' L $field = implode(',', $fields); 0 T3 G: A" t+ }; P+ C0 F
} else { , d9 @& n1 h9 V
return false;
: h' c( w& B4 v7 P3 Q } + T3 u5 \" a* x
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; / E( h% v |7 v
print_r($sql);
# Q4 [7 m2 k2 l% ^& }2 h3 i return $this->execute($sql); ( r# [$ V' |8 g; g j
} 7 } ?& j% n$ Z" ?, l
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。& L1 U9 e+ [$ k/ p
4 M: i6 m& s; h# W* h( k
攻击测试:
( I5 }: {, j7 |6 k) W测试地址http://localhost
2 h# z e; Z3 n7 D. b 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
& j! Q3 K5 L/ A( h' _- v9 B5 r0 }5 ]4 F0 m7 m
. } b( U( a% X
# S f/ h, C; ]0 P9 c3 C4 \! b/ I
) ^5 v" _& {/ i1 L
) B( N2 I# [4 h7 G* L' p
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|