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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
9 s9 L' Q# ?, \: i- [- y; O' a$ h# Z2 O漏洞作者:skysheep
( f+ G5 p: _. L- P- L分析作者:Seay5 ~' b6 C  [9 R8 C7 S" j; C" v
博客:http://www.cnseay.com/
/ c0 M: a) t) l' B漏洞分析:
1 X- k4 }' b+ u9 F5 F: u  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。5 L! t9 c% s7 I7 X5 @  k
5 G+ \6 r- p1 ~" e
8 t! h  P, s6 t

3 T7 S8 u7 e( S$ w. n5 bpublic function account_manage_info() {  
5 Z2 D1 a" m2 J& Z       if(isset($_POST['dosubmit'])) {  
* b' d3 p* }& n3 D           //更新用户昵称  
3 P) z; \: c3 N: M: W& _: U           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';    W# R6 I: ^- ~% v( H6 O2 ]7 g
           if($nickname) {  8 f& b* V2 p( Y' _
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  ( x6 m6 h2 ]1 m. q7 t: c% `3 w+ }. i
              if(!isset($cookietime)) {  
5 [' H. z) D4 ^                  $get_cookietime = param::get_cookie('cookietime');  ' l; \" q0 C9 ~. J6 f; i% `# c
              }  
: P  i6 f3 a" S9 B              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  ) b3 y) I8 r3 k4 S1 d& [
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
1 q% x" y  O' }; p2 C+ U, u              param::set_cookie('_nickname', $nickname, $cookietime);  
# L6 P( \' G9 T: q2 ]: j0 v           }  
' x5 _# m" ]; e; b1 K& L" H# d           require_once CACHE_MODEL_PATH.'member_input.class.php';  
3 z, w# ?/ y) m1 K, _% |           require_once CACHE_MODEL_PATH.'member_update.class.php';  
5 y6 l; v' G% W- o8 V: L" ~           $member_input = new member_input($this->memberinfo['modelid']);  7 v$ D" ~+ M( o/ D' s8 S8 U- Q
           $modelinfo = $member_input->get($_POST['info']);  - L5 Y" D: N: k. U8 W  e; a. y! _
           $this->db->set_model($this->memberinfo['modelid']);  - D  c" }" F) G6 p& V
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
# B5 m/ c* }( G( O$ S0 ^2 v           if(!empty($membermodelinfo)) {  
# ]  q+ T. M& D              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
# H  Y( [9 ?: I) Z# V           } else {  ) V- B) ~& l; L6 q+ [. o$ p6 D
              $modelinfo['userid'] = $this->memberinfo['userid'];  
' E3 W- \8 U' S, ^$ n              $this->db->insert($modelinfo);  
, d/ K5 ^' C% [; T: q' h           }
; N) r2 h* ?. c/ z! B( j代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
* C8 u% R0 z0 Q$ h( s: K) h在\caches\caches_model\caches_data\ member_input.class.php 文件中:  X3 }- ~3 Z1 f- t! i1 X( i

) x- l$ y! N6 A3 U$ E2 A2 {& w
' b- |' e% A6 I) V 6 x3 ?0 |( X' X
function get($data) {  * r+ f' x( O# p, P+ A
       $this->data = $data = trim_script($data);  : C# w3 {8 O+ ]; \% B  Z
       $model_cache = getcache('member_model', 'commons');  2 f9 r. |8 y5 c: y# [2 X
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  ) q3 h4 t4 ~+ \1 f: N% e* ]2 J/ P
       $info = array();  7 o" l4 N7 T4 e5 M+ n9 d
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  % o1 U% O' ?6 Q+ O; ]2 \& F
       if(is_array($data)) {  7 Z- Z" b0 i, }& Q) L
           foreach($data as $field=>$value) {  
8 x# ?) T( }0 t8 K! Z: l% I- _0 |- c              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
! y3 X) ?6 E( G' A& E              $name = $this->fields[$field]['name'];  5 ~1 C" ?* x) O' [2 N! B! ^
              $minlength = $this->fields[$field]['minlength'];  % B- Q& c8 G6 F; W7 |
              $maxlength = $this->fields[$field]['maxlength'];  
, o7 i9 M$ [; X) U2 Z              $pattern = $this->fields[$field]['pattern'];  
$ F; S1 `9 [. B9 `! @1 R& G, l              $errortips = $this->fields[$field]['errortips'];  
9 g, E6 y- {7 w" n9 q: c. _2 s              if(empty($errortips)) $errortips = "$name 不符合要求!";  1 k  H- v# V6 r) [
              $length = empty($value) ? 0 : strlen($value);  
0 A4 n" w, V, U9 H( ^              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  8 `. N( H5 V# L- j/ c
              if($maxlength && $length > $maxlength && !$isimport) {  
1 ]' a6 p# D- |8 [                  showmessage("$name 不得超过 $maxlength 个字符!");  
+ ?5 {7 N0 @. L. X) C, n$ B  k/ _              } else {  
8 I  X6 n7 M- i! j$ O8 M+ j' y6 Q# O                  str_cut($value, $maxlength);  : W& s+ o" M# b6 L7 @; S
              }  
# n9 p# O: i  j              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
5 ^+ u# z' b0 h: n$ t                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
" {, F. r  x' g# g: f              $func = $this->fields[$field]['formtype'];  
: }% F6 I+ ?  M6 ~: ^              if(method_exists($this, $func)) $value = $this->$func($field, $value);  7 O) @3 G- H$ u
              $info[$field] = $value;  ! u' ?$ L) L: V8 f6 ^# f
           }  
0 F% ]6 o9 `( L8 ~3 r: W* {( l  E       }  0 G  E5 A5 C4 T9 M
       return $info;  & }" t& {9 G9 Q) a& X4 o$ v
    }
, K! V" U6 @( D9 @* G/ B: }trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
& ^7 C6 v4 ]: h$ U$ I0 A& h2 p6 S8 a
再到phpcms\modules\member\index.php 文件account_manage_info函数  s! f' _+ c/ y* i* I; [1 q$ v
过了get()函数之后。
4 ~" o/ @* J- t) v- m- B
; c' w3 n" G- d, t ! H" g# S! T- E# g) Q) Y- D6 n
$modelinfo = $member_input->get($_POST['info']);  
' M( I$ S3 ~, v  F) G           $this->db->set_model($this->memberinfo['modelid']);  - z/ V! r. ~. V( s9 r
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  ! |, b5 J5 [4 |' M8 b: a
           if(!empty($membermodelinfo)) {  
4 ]  y6 K- L  X7 q7 k$ D7 ]              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
- @, ^! w$ L) m3 B2 W* `; d' O           } else {
$ v# t5 l/ w* i( K! p$ e5 |$ v直接带入数据库,update函数我们跟进看看
. w& \7 a/ R: T8 A3 r* c, h) [' q) t& t0 H. @: M
2 R4 C1 Y- B+ E; S' v/ Y; @
public function update($data, $table, $where = '') {  1 z4 H, _5 ^) a- n" Q+ j$ m
       if($table == '' or $where == '') {  5 k& J- L5 J, R: l% W
           return false;  " r" t5 z3 z1 ]* x0 }7 J
       }  4 z/ b6 W( Z6 a4 z. R2 r! i
       $where = ' WHERE '.$where;  ; ]/ {  d2 j3 A, ]. N8 ]; U
       $field = '';  
# g3 E+ x/ U1 C+ y       if(is_string($data) && $data != '') {  7 k  X% x( W0 X4 W
           $field = $data;  
+ u- J5 ]6 `! D/ k       } elseif (is_array($data) && count($data) > 0) {  3 o: r# |2 N. J; m" ~* R, S' k- e& W
           $fields = array();  ; k5 S9 r" D# S! v3 e
           foreach($data as $k=>$v) {  
& Y" m. z/ u1 p, L% o& P              switch (substr($v, 0, 2)) {  : W6 j+ g8 s# J' ~; @
                  case '+=':  
* n8 N: }3 D3 M9 m' ?/ _                     $v = substr($v,2);  
* j. u' V0 L8 a2 o' n1 {6 W6 \6 q                     if (is_numeric($v)) {  
5 {3 k- ]# r0 Y" |4 r                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);    J* N/ `; t1 v; G8 `9 f
                     } else {  
* u+ A& E+ I5 ?# p  W+ e. `                         continue;  
4 G( p; [9 y( {1 j( _/ ?                     }  
, r6 q1 L; }1 W) ^7 o- o  k4 X$ g                     break;  
, E1 E3 T3 a  _                  case '-=':  0 M8 u" ~  F# s* K9 W
                     $v = substr($v,2);  
, F4 I4 Z: X" l                     if (is_numeric($v)) {  
. l8 ?8 I0 N3 j$ d4 L2 S( X                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
, `/ s$ q& Y# N! q9 v7 x                     } else {  0 F3 ?4 \7 A* u6 s5 V
                         continue;  
6 A" ^& F! m: s3 Z8 d( S/ i# b5 p                     }  2 n' J7 ?. t" u& x, W
                     break;  2 o$ X) y2 F- I0 _% z
                  default:  + l5 g/ i' |* {: l5 {
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  2 Z) W0 |' M& A  S# R* ^
              }  , ?- @( p5 Q4 R4 S
           }  
2 b* S2 H+ o* P( Q, ]) Q7 d           $field = implode(',', $fields);  
  X- z" U' C$ J# n       } else {  
8 ^' |2 ?3 O7 p4 f           return false;  # I* `( N6 q! X
       }  
4 G/ k0 A; b  @       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
& D3 Z2 |0 C, O, ]) [( ]6 G       print_r($sql);  
2 E* a; G, G1 J/ x/ @       return $this->execute($sql);  7 n6 @6 s; A/ ]
    } 6 d0 v' K; E+ B
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
& q7 J0 u9 E$ Q5 I( f) e2 P
4 N: n( K- }* E+ X, b) R9 z攻击测试:7 _7 g. I2 T# a7 S$ H  z& T4 x7 O3 }
测试地址http://localhost! Q( f2 |6 Y$ v8 A* k7 Z6 j
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句, g/ A& B* J/ \& F: D3 A' q& V* [
0 \2 H- C& y4 z: @2 B* A* Z0 r; @

8 _9 E* G- n" [" ^& |1 q8 L1 z8 @" U
! P" T& j$ o6 o, W% \

: S  l. f3 H5 Z8 v' ~$ |

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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