找回密码
 立即注册
欢迎中测联盟老会员回家,1997年注册的域名
查看: 2117|回复: 0
打印 上一主题 下一主题

phpcms v9 2013-02-01 会员中心注入漏洞分析报告

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称: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
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表