|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告! [/ N/ Z& d% D' h2 z0 i# S9 j5 y
漏洞作者:skysheep% r8 v# [$ u" d1 c$ Y
分析作者:Seay4 D) H! k# r" A
博客:http://www.cnseay.com/* S' I8 G/ D6 D* r* [+ ?
漏洞分析:& h- L7 ~. {8 d6 J7 R
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。9 g7 I; \; F9 P$ ~1 z( j5 h, p
9 U2 ?9 N( W, P! }3 O+ L1 Z6 V- e
! f: u+ U0 @3 p( S/ @5 j
# m! s( ~% |& s$ d
public function account_manage_info() { ! g9 g; a4 b# j5 ?/ c1 [
if(isset($_POST['dosubmit'])) { + Y2 N3 ]$ l8 h2 E
//更新用户昵称 1 Q: Q* b2 `0 ~/ ]( g4 H7 }+ l
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
5 B) F% a* @- W2 C( \+ C if($nickname) { & r0 e, s$ _$ }6 U9 ~+ _* Y
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); & z, [2 }7 |; l! g7 I& b
if(!isset($cookietime)) {
2 V& G" n6 J1 I: `& O; Y $get_cookietime = param::get_cookie('cookietime');
/ B1 r4 k% J- t0 D }
. K8 X0 s6 C; M1 s. m2 l $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); ! X6 `- E2 w/ T
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
/ p" Z" [1 K8 ] param::set_cookie('_nickname', $nickname, $cookietime);
- k& N9 F N' D. ]6 q! t$ w }
4 _; M( D+ o0 F5 V( \1 c1 [ require_once CACHE_MODEL_PATH.'member_input.class.php'; 5 z2 V' y4 Y' H
require_once CACHE_MODEL_PATH.'member_update.class.php'; 1 r. H0 e% u6 W# I
$member_input = new member_input($this->memberinfo['modelid']); % m6 k5 g, X6 e N
$modelinfo = $member_input->get($_POST['info']); 4 D3 [3 L- Q$ [& z# i
$this->db->set_model($this->memberinfo['modelid']); - U7 E: G6 z$ f. e$ n5 f5 E
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
: [% F) E1 z! W. ^- z a8 }* m; R if(!empty($membermodelinfo)) {
! f( d! o; j* r! ^: Z& | $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); + Q' P0 d! a1 F: Q8 r# v6 y4 O
} else {
6 M/ G# H5 A0 [$ Z$ ^ $modelinfo['userid'] = $this->memberinfo['userid']; 1 Z1 B2 y/ N9 ^. S
$this->db->insert($modelinfo);
( s9 w6 E4 ~! v9 W } 3 I$ V {" n, \* f1 ?: Y7 o% E
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
, R/ O2 U6 Z" Y2 V$ M8 b) E在\caches\caches_model\caches_data\ member_input.class.php 文件中:
3 S5 g& q0 {4 i
7 @ ^3 C- u! q$ X. i
# M1 V- c% }. h8 i- j " ^9 C8 t" s$ L
function get($data) {
9 G+ k& ]* c/ w2 F $this->data = $data = trim_script($data);
/ m9 K" ]) F8 E. C. s $model_cache = getcache('member_model', 'commons');
8 Z2 i3 N* S3 u5 p' u- a2 k $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
- w4 Q! Z/ u2 x! e7 ~9 f' K! |' y $info = array(); 7 Q3 m& h0 V& z1 `& q
$debar_filed = array('catid','title','style','thumb','status','islink','description');
U% g3 x1 E. U if(is_array($data)) {
0 w! K! n" p _ G+ K; q/ X3 X& r foreach($data as $field=>$value) { # V+ [) W. K) n* E6 H: c
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; ) S% A2 [: T0 |2 K5 A
$name = $this->fields[$field]['name'];
. m! \) {- H6 K+ q+ U: [- X $minlength = $this->fields[$field]['minlength']; 7 o8 M* s8 Z3 h
$maxlength = $this->fields[$field]['maxlength'];
* W' D1 S; `% T $pattern = $this->fields[$field]['pattern']; 5 p+ u; L# r/ [- C
$errortips = $this->fields[$field]['errortips']; , R, e9 X, z8 v8 L
if(empty($errortips)) $errortips = "$name 不符合要求!"; & @1 i% m/ B* b6 p. `( S# |
$length = empty($value) ? 0 : strlen($value); 7 w7 X6 f9 p9 P# D5 y* b# w
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); - k! M+ x k6 i
if($maxlength && $length > $maxlength && !$isimport) { ; `7 v+ r- O5 `& p7 v" ^
showmessage("$name 不得超过 $maxlength 个字符!");
' w2 x7 e2 x7 R, R1 M* m } else {
) t; F' L3 ]5 q; V1 ^: w4 b6 k str_cut($value, $maxlength);
B9 X, ]1 n( L, J Z2 j }
1 k9 ^( {# k0 Y& z- e; P if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ' V* D% J# F N6 \+ b, _
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); ; g: [! H! H% b
$func = $this->fields[$field]['formtype']; * k/ t7 d8 G+ S6 i; w
if(method_exists($this, $func)) $value = $this->$func($field, $value);
2 z2 ?. l1 l7 @0 A+ i$ {. w8 S $info[$field] = $value;
9 m( x# \% |' O6 n& K }
9 j& }; ^/ `: q. ~+ R0 K } : z+ @9 G8 Y4 P3 F" T/ u) p: A
return $info;
# n& d" D% o& I1 ^7 M& T }
3 d# i% f. \5 m1 z2 g/ y# L; ?4 Ftrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
& l$ G3 M& b4 s; V7 |) b0 [ K) c, Q V
再到phpcms\modules\member\index.php 文件account_manage_info函数
9 k7 X* R; @0 @过了get()函数之后。
2 z- K) Y7 {3 @5 u% F8 r. }$ @$ `3 t* O ~- x
- ]; a8 v8 D9 F3 c, g* r& W$modelinfo = $member_input->get($_POST['info']); 5 p) z5 z$ V) O
$this->db->set_model($this->memberinfo['modelid']);
, T) W/ F7 L6 |4 g $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
9 z( ?9 V4 j* B if(!empty($membermodelinfo)) {
7 u& l0 H9 Q8 S& T $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
6 K3 N4 m) | E( l S" {7 j } else { , Q* [- O0 R3 t6 ~" j& |; Q
直接带入数据库,update函数我们跟进看看7 {8 a/ u" A6 E: Y
: K. G9 R0 D6 A8 ~$ _7 g; h" |- C0 G8 P9 X & o j6 x$ x" S; ~# N$ o
public function update($data, $table, $where = '') { * t0 s0 I- F: y" F
if($table == '' or $where == '') {
' L: @0 A9 W& ]; u! h3 n$ f return false;
8 d6 Q; x9 x# _1 I% i$ q: u3 E }
2 P& r( k5 z; C3 _ $where = ' WHERE '.$where; 5 J0 Y& a5 }" l- G6 Q& Y
$field = ''; 1 Z* {+ {1 b8 g" P. T# \5 Q
if(is_string($data) && $data != '') {
) ^+ `7 o1 N( d# J $field = $data; * R* z/ b2 S/ n/ Z. z
} elseif (is_array($data) && count($data) > 0) { $ M+ K: f! @/ V4 D. n* h4 ^- Q9 g
$fields = array(); * \2 S: w; O1 S! m Q
foreach($data as $k=>$v) { . p1 |4 F9 ]+ U) ]7 z d6 x; i
switch (substr($v, 0, 2)) { : v6 K: A, ?1 x+ q8 L' H5 ?
case '+=':
5 \1 F9 e" @1 A $v = substr($v,2);
K( |1 g) Q4 w, N; b0 c1 X4 X if (is_numeric($v)) { 9 g2 J/ |% {- Q% D
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 6 t s6 W+ }/ A' B/ c; K7 O$ V' P
} else { 3 p* q& k3 k9 M0 ^8 e
continue; : { [- `7 l3 U9 d c' C5 }
} 6 A$ V5 R# L7 D' W& p
break; . A7 C. c8 C! t, P# w2 |6 s
case '-=':
( e+ z" _2 `5 x $v = substr($v,2); y' ] K( p& O8 C' @
if (is_numeric($v)) {
2 @5 |. h5 F8 E $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 3 d+ T8 \5 I! G# x J( `# r
} else { ( I) f0 }3 D: f/ @2 y, E
continue;
8 |( z- g0 l% w' ?8 `* ~ } 6 m8 D3 R$ }' Z7 \' e! }
break; 4 L" ?7 f& _0 a6 [
default: 8 N7 f* H" M: o' i) \0 d& }
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
2 S' W" ~/ p; N% u" c }
7 B$ E3 c9 l! m+ z1 K* w& B: X } ) r+ l E" _1 l% } M+ Z6 \
$field = implode(',', $fields); . J) L% F0 w! C) A% Y
} else { & j0 L- r* C1 p8 t3 @" h, n7 u
return false;
* D$ T! M e- ^9 c }
- u* ~3 Q. O1 K0 N% A $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
: G" \6 r% ?" G. ?) D print_r($sql); , w# l8 A4 Q, g: L: T
return $this->execute($sql);
0 @. n1 h1 ^# [: z% J# u; o+ R, i }
+ w) ?0 f* {6 h: \从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。+ o. {/ J7 Q) X; K4 i7 U
/ P# i2 b+ w9 e攻击测试:9 ]" q) K2 {0 Y2 f3 p) i
测试地址http://localhost2 @: T0 S6 i9 H0 E% u
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
* y8 |# c6 O, r9 t( c5 `5 H! ?9 G# c- \1 V
; Y8 \- z5 P6 W [9 N! ?2 @5 `
C0 {& L. `9 ]- O
( E3 X7 S" C: L i$ i
& X4 k3 x$ h; Y |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|