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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告' J8 J1 T  T) x: ~
漏洞作者:skysheep$ Y1 \/ n" o% W
分析作者:Seay
* x" X& D# I* d; e博客:http://www.cnseay.com/
: l; y& i" y8 a& q# l* B; n漏洞分析:
. J5 i% x% S. w& d. n& ]$ g0 P  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。+ I" ?( m8 P, N
2 B8 M( p, i& d+ `3 l9 L
- V5 J# N: [& m3 G) P

5 E+ x4 R8 s) gpublic function account_manage_info() {  $ q% Q0 X9 ?# V6 A
       if(isset($_POST['dosubmit'])) {  : t0 ^1 e7 a# {" n3 F4 t
           //更新用户昵称  7 @+ g9 Q9 S- A) @0 T+ s' }
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  7 b& f! j) A) p, y/ d! h
           if($nickname) {  7 t6 j- X& A; ~, v
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
0 ~6 G* z/ O5 M. N8 u2 ?              if(!isset($cookietime)) {  * s" l* w8 U9 M7 V! ^* {5 V
                  $get_cookietime = param::get_cookie('cookietime');  & t, y% z  {% f; m
              }  ! k+ F6 s5 Y$ y7 D  N% W' \
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
3 V4 G3 T3 P+ C- X, x, k' d* \( x              $cookietime = $_cookietime ? TIME + $_cookietime : 0;    @6 @8 k3 G3 b5 S& M% r4 U* a+ }
              param::set_cookie('_nickname', $nickname, $cookietime);  / p$ c' D3 r; Q/ o% h
           }  # _( i& v+ g- W9 d
           require_once CACHE_MODEL_PATH.'member_input.class.php';  : l2 _8 s( q' \) a8 m' _6 W2 I
           require_once CACHE_MODEL_PATH.'member_update.class.php';  7 A4 B. r4 u$ C- Q* h9 ]% Y! a
           $member_input = new member_input($this->memberinfo['modelid']);  
: _/ c; E4 p7 L; o8 O% l: o           $modelinfo = $member_input->get($_POST['info']);  3 e& @, d$ _: ?8 u! e
           $this->db->set_model($this->memberinfo['modelid']);  " F3 T6 |$ L- q% O, S6 F8 [7 b
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  4 w* ^" n% s4 D7 o4 O1 Z8 s
           if(!empty($membermodelinfo)) {  
  Y) `" Q3 b! J: |6 G              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
7 R0 r2 t5 h5 h2 Y; ~$ K1 _/ j           } else {  
6 X3 F' g" K# b7 \1 q+ r2 c              $modelinfo['userid'] = $this->memberinfo['userid'];  - n& p8 w7 \% W9 v* u
              $this->db->insert($modelinfo);  7 K* ]1 s2 g$ z, _0 L( G+ s! C
           }   F: y3 [2 V9 S: C
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,# \6 M& U! n: o8 u# R9 R
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
  O; }/ a7 Z! O3 E2 f$ U+ G% u* B1 h  ]2 W1 L, \( Q

* Q% Y$ V+ J  i* s' y
+ r; M( ]+ E3 Q$ D  _function get($data) {    x% b1 e$ b3 x/ m6 d
       $this->data = $data = trim_script($data);  4 q0 s) |* m0 N& n  p
       $model_cache = getcache('member_model', 'commons');  4 H: b) G' R7 ]" R2 ^
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  - Y! V* G( p+ O/ r" g; _
       $info = array();  9 Q* H! f8 `, v% j; F+ W+ I# e
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  5 ~- O$ s7 v# R5 Q
       if(is_array($data)) {  " s7 s& S4 f, d3 s, ?0 Z1 G1 b
           foreach($data as $field=>$value) {  
9 M( q0 T2 p  R. Y$ j" K% Y              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  8 [! q# Q% l! R2 m0 q6 T/ b
              $name = $this->fields[$field]['name'];  
2 X+ N+ e- Z6 B! U              $minlength = $this->fields[$field]['minlength'];  7 e' _8 M- e" N: j5 R: ^
              $maxlength = $this->fields[$field]['maxlength'];  * I, A- j5 b# w+ a6 c. q: R+ P
              $pattern = $this->fields[$field]['pattern'];  8 X: Y4 X$ R, }7 L
              $errortips = $this->fields[$field]['errortips'];  + o  z9 B. y& R
              if(empty($errortips)) $errortips = "$name 不符合要求!";  ; U: }2 q6 g- |, N2 e
              $length = empty($value) ? 0 : strlen($value);  
5 o# p1 ~6 O9 }1 Z' S( |" n              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  5 F- Z! ~0 S, S  i2 w$ N5 J
              if($maxlength && $length > $maxlength && !$isimport) {  . [- I0 `* y/ \6 P
                  showmessage("$name 不得超过 $maxlength 个字符!");  1 |5 Z4 f  f) c% K
              } else {  % S1 ]: @( y0 C3 u2 `1 A5 i9 ^; ]
                  str_cut($value, $maxlength);  
- `9 u, ?! q5 ^- Z# S              }  $ x& K& x3 g; u0 X6 U* O0 S/ R- K* |3 H
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  ; Y+ U( Q7 Z" a7 K
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  3 I+ F- u9 p. [8 T; a9 o
              $func = $this->fields[$field]['formtype'];  , ?  \) r, e# w5 i$ c+ @; K
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  4 Q* ]4 g  p+ H  B4 E1 d
              $info[$field] = $value;  
, \1 e" c/ z8 w9 w2 E$ |) W/ E           }  
) i. g% s- l7 a$ J0 N  o' A       }  
, m" ?5 `2 w5 s2 D# U1 G       return $info;  5 U5 I- _1 J; J/ h* m  C) |
    }
7 e1 r. `5 I& d, ktrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
3 g; B' |3 T1 B0 @
: T8 O5 e4 }5 M+ D再到phpcms\modules\member\index.php 文件account_manage_info函数2 s& c  u5 p* W) H+ g# E, ^& ^2 i( }% m
过了get()函数之后。: h  g# q& g0 _- k1 x1 v& Y

4 T3 G# C9 G4 Q4 w
" a$ r- I4 q0 i2 z$modelinfo = $member_input->get($_POST['info']);  
( Q: G1 V$ r' Z$ W7 K- M           $this->db->set_model($this->memberinfo['modelid']);  $ T% V' w% a" V  [( j( @
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
" u2 w2 x# u- ~2 B5 x           if(!empty($membermodelinfo)) {  
) |( B3 W. Q. g+ B              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));    z( r6 t  a8 A6 k: g2 e! a9 u0 `
           } else {
# \' `" n: N+ A$ Y9 P% R2 [9 G5 t直接带入数据库,update函数我们跟进看看
% I5 Z5 D+ H0 B' g* u- r3 z. l/ i; {0 a0 R1 E

: ~+ L# [. y+ l& p# U3 upublic function update($data, $table, $where = '') {  3 X: E5 w/ R2 T3 X3 j
       if($table == '' or $where == '') {  6 A7 P+ v6 Y- s. o1 Q
           return false;  # a6 i" R9 S& A7 C8 Y
       }  
1 ?; f- J: J. n2 o( p8 j! w       $where = ' WHERE '.$where;  
) e8 m$ V' u6 i( x, f( M4 r; Z       $field = '';  : G* p, Q1 c$ N% Z# `, O
       if(is_string($data) && $data != '') {  # d  x- I( Y$ K: I0 G% _, }! ?
           $field = $data;  
6 }- d* {; j! V9 U; P/ u5 U       } elseif (is_array($data) && count($data) > 0) {  
5 o' a; T) x  G8 V& E( _           $fields = array();  9 E" X8 z$ h3 t; R* u0 \( ^) W6 a* A
           foreach($data as $k=>$v) {  4 ^" p/ H" y$ M, [5 X' r
              switch (substr($v, 0, 2)) {  & k9 w2 A- k  _& W* x: ^
                  case '+=':  % ^" q! H5 f" S6 F9 T
                     $v = substr($v,2);  , ?3 _* [1 x6 s( d6 @) S' v+ j
                     if (is_numeric($v)) {  / i1 v+ Y3 M; Z+ c7 f
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
% L% i  J$ H5 H2 {* }                     } else {  
5 a6 z% V* a& P0 c+ ^                         continue;  ! H* O& P7 v7 w4 \7 O
                     }  
+ p9 F( \! }4 e# i. y( y8 m& D2 `. M                     break;  
6 Q9 H% Z  c1 f) g, v5 B                  case '-=':  
8 U0 G/ `4 R8 W0 P                     $v = substr($v,2);  
7 x4 h# x1 Q; V& B/ O" q2 s                     if (is_numeric($v)) {  : X% e1 ^" d) L% }/ b  O
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  9 P! M: C/ F1 o  e8 D, s
                     } else {  3 M1 @8 Z* D8 H- I) X  h
                         continue;  
4 F% s, B" ]  a/ n9 E                     }  
9 X7 t0 g8 ?* q& i                     break;  2 u/ l" Y3 P9 v8 J% {4 `# p
                  default:  
; z' a; u& y, v% c                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
/ s: P/ T' u$ O; q( ?6 C& y, J              }  
7 F) R& e: t8 l: l( a8 a           }  
; u: t( L; E+ j' K$ p           $field = implode(',', $fields);  
; x/ F7 W4 _  J5 G( Q       } else {  " h4 L9 S$ l5 X, b. [. s
           return false;  
, H! j. J1 `  J# U1 d# R       }  : G4 C9 f6 n; w- g% R+ X5 t1 D0 u
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
5 w; j$ l8 D7 p- U! \1 G. D$ w       print_r($sql);  
0 b1 p" V# U. F       return $this->execute($sql);  # `/ Y6 B+ y8 r7 A* C9 ^" f9 n
    } 3 \7 B7 b  t: H' G# {) f3 H" J9 g
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
$ c4 @+ i- r4 z, Q* d( q- {/ L/ Q; j- C$ V
攻击测试:6 \* M$ K6 z3 ]8 A  }0 E
测试地址http://localhost3 k$ |* H3 O( G: s2 s
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
: j: s6 s( ?7 w& x# X' V% j: |8 A
9 c* s0 U* b1 `; ? , E: Q) @6 x4 k2 e/ i' p

' f0 x6 q0 @0 u: f8 Q' i9 S$ x" U4 H5 [0 i, Q& v% t! n

4 D0 z3 q- K2 Y* f

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

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

本版积分规则

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