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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告2 k  q' d% @) V7 g6 t
漏洞作者:skysheep" G, K9 k# e4 r: U; X+ u
分析作者:Seay/ x' z9 A: x, I: `/ s. m& y
博客:http://www.cnseay.com/
$ r% U8 Z- e8 \& s9 h8 \漏洞分析:' J6 A$ }" ]# U4 M9 w+ G7 J  g
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。* ~4 b+ W2 {5 j6 G0 l5 E
+ D$ }& N+ W. j# `2 w

/ W' B$ I; H* u3 ]6 B! v
. c0 K, e6 a8 O6 T. d  qpublic function account_manage_info() {  
4 `, m5 y  Z# _% E. q2 [0 u% v       if(isset($_POST['dosubmit'])) {  4 I( J" \% u/ B6 H# _" f. M6 E+ C
           //更新用户昵称  4 h% X6 D! v' p/ F4 _
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  2 b2 j& x2 d. B; I
           if($nickname) {  $ C9 n: Q6 E/ e/ `3 X( `
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
$ d. ]" ~4 D) d1 }3 b1 F' N              if(!isset($cookietime)) {  
+ p& q7 I/ F& P                  $get_cookietime = param::get_cookie('cookietime');  ) P! D0 Q  L" c0 N+ [  [8 I( r2 f
              }  . f0 t; j- \" [2 I6 N, x' Q; ]/ n
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
: c: }. o/ k9 n' t3 Z0 `              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  , l# ^  O7 M2 k  d8 b4 C7 y& A
              param::set_cookie('_nickname', $nickname, $cookietime);  ! ?8 q% S7 i3 F% ~; L
           }  4 ?( Y! n% l% E1 `# g$ ~$ ]
           require_once CACHE_MODEL_PATH.'member_input.class.php';  6 W9 f; D1 j0 s2 a; [* L
           require_once CACHE_MODEL_PATH.'member_update.class.php';  8 q, e* W, C5 L0 c6 s( j1 `
           $member_input = new member_input($this->memberinfo['modelid']);  
7 ~# U1 B* b2 L5 D/ k( t9 z           $modelinfo = $member_input->get($_POST['info']);  
4 _  H+ E& V) d           $this->db->set_model($this->memberinfo['modelid']);  
7 @0 ?: P# k8 K, L6 y% Z           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
4 f" q* B% ^8 [           if(!empty($membermodelinfo)) {  3 j" r/ \) A$ s/ V) @
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
" P$ B& J9 k, I           } else {  
6 T, h$ Z- Z0 H# D% q/ J              $modelinfo['userid'] = $this->memberinfo['userid'];  8 \) ^3 }3 L) ?% ^, k, [! f& s
              $this->db->insert($modelinfo);  0 G5 C1 s2 }. ]) o+ a. Z. f
           }
( C2 y* d# k5 ~- S& k9 g代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
* a0 N6 T0 C  Q# W4 n/ O; I& [6 s0 S在\caches\caches_model\caches_data\ member_input.class.php 文件中:
8 J  c! o3 p5 J3 E6 v. _1 |
& S! Q# g3 {( L/ K& P" S; X4 r6 U: w+ }

3 R5 N7 t  @! I4 M$ |* efunction get($data) {  ; d6 b& ]2 N, D1 |4 z6 }; e  \! P; W- w
       $this->data = $data = trim_script($data);  
4 ^- k0 p  X' _       $model_cache = getcache('member_model', 'commons');  
8 ^( h/ D/ I- M0 z" w0 E; A$ Q       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  ( U( Q. }  z# x3 `& x1 B
       $info = array();  5 D$ H) x/ i: ?+ B/ F# O
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  6 y! x9 B) c' @: y9 m% l
       if(is_array($data)) {  
7 k4 l3 I# r" b' V6 M- D- R           foreach($data as $field=>$value) {  
( T& Y" b5 O. h4 f' t9 V) a7 F              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
* R/ q- k* _( i              $name = $this->fields[$field]['name'];  
# ^" x" ^, L2 W$ }6 K9 S1 B! Q              $minlength = $this->fields[$field]['minlength'];  
3 Z4 r7 B9 N$ v6 T4 j              $maxlength = $this->fields[$field]['maxlength'];  8 n% _' r; H4 \4 @& n6 T) w1 X
              $pattern = $this->fields[$field]['pattern'];  9 k& A1 I" m4 l! e( A
              $errortips = $this->fields[$field]['errortips'];  
% z: Y6 v" W& N8 s5 }! r6 K              if(empty($errortips)) $errortips = "$name 不符合要求!";  
4 R& m" _1 R2 g! b              $length = empty($value) ? 0 : strlen($value);  
. Z9 H+ ~& N" k& ]              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  ' b' q" a8 B8 r  H9 P
              if($maxlength && $length > $maxlength && !$isimport) {  
) s4 X# N+ B4 L. l2 h$ G                  showmessage("$name 不得超过 $maxlength 个字符!");  ( \/ j1 [% ?4 J) ~+ `! c  G! X
              } else {  
  \5 _! h/ t  G# P  V! [                  str_cut($value, $maxlength);  
$ @  T5 P4 t# D" V              }  6 @7 k$ I# e8 G' I) }
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
* U5 x9 t! V  z' R                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  ' F3 Y5 c, e) x7 I
              $func = $this->fields[$field]['formtype'];  
4 w  k5 X" v0 F# b1 Z              if(method_exists($this, $func)) $value = $this->$func($field, $value);  ' `. S4 O1 g, u8 u
              $info[$field] = $value;  
# |/ E/ Q3 V+ ~! w! C           }  
9 t! P, {; S, L/ w2 F       }  
& T2 ^4 Q' i' R" Q. b8 G% R       return $info;  
) X! P8 U: C" _4 `: h/ x    } $ y" S9 m0 f3 ~
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,& b6 p7 p9 W" x9 e/ Z
9 T2 l2 T- E: M! ]
再到phpcms\modules\member\index.php 文件account_manage_info函数' x, N( {& h3 J3 y) f* s' D
过了get()函数之后。
, _- d1 _" \3 l* W$ E5 Q1 t# f5 t' `9 A2 N) m5 V
9 J) |' B. M# b2 i. g5 k- B# i6 V
$modelinfo = $member_input->get($_POST['info']);  
& Y0 Q" G  v2 A" X, `           $this->db->set_model($this->memberinfo['modelid']);  
6 Y* t' j& ]. K  B0 p5 ?8 j# _           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
9 E9 W8 ^' ~" T: J7 l# q           if(!empty($membermodelinfo)) {  % R$ m! p+ z4 C9 L
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
- g; }0 }$ E4 l# C- |$ {           } else { % m) P# |, I7 S  p8 n5 _+ _
直接带入数据库,update函数我们跟进看看7 I6 @# h1 d; {% t$ S

& H: I; i) q5 g* l4 K* t; l
) n) h. ~! ~$ |) g& Apublic function update($data, $table, $where = '') {  + K7 L" X# S, |& Z1 w& Z) V/ B
       if($table == '' or $where == '') {  
  K& B" w* i. U  N# I# p           return false;  5 s8 E2 B; S: `+ s& x# e
       }  ( Y' |% a1 q  U7 I' j! T) Z
       $where = ' WHERE '.$where;  7 S8 D3 |! O: R0 S/ N- B* d
       $field = '';  
3 c4 Y' t# U/ k* f8 @       if(is_string($data) && $data != '') {  ( Q. z3 E' |  u6 f5 ^8 }
           $field = $data;  
! k- p6 y8 r/ W5 N- N       } elseif (is_array($data) && count($data) > 0) {  
, i8 i4 m8 J- R1 n& o           $fields = array();  
2 i" s/ ?) f$ ?: n! n) O           foreach($data as $k=>$v) {  $ p& |9 \6 l8 {1 d. e
              switch (substr($v, 0, 2)) {  ' k3 o. c/ x9 c! Y( E
                  case '+=':  
' S/ c6 ?0 x+ M6 w: H" W                     $v = substr($v,2);  
3 Q3 \7 I# C4 u( k                     if (is_numeric($v)) {  
! G( S  I0 Q2 {, q                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
) |6 ?' }; B* h2 y                     } else {  + `2 ^8 ?; z# q1 Q  e- h* ~7 L% Y
                         continue;  : [$ p) B/ `- \) D* R; l
                     }  
4 m$ l. i' y4 M) ?5 r, E9 U" x                     break;  3 E0 P/ Q2 b! y) F% ^4 C: `
                  case '-=':  
% t% ~/ N) ^- \! f$ _& m' ^" q                     $v = substr($v,2);  
8 @/ I8 H. W( ~. Z- K                     if (is_numeric($v)) {  
; Y# s4 I4 M  l! f3 o& W                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
$ m1 f( r9 L$ J. M                     } else {  # x7 _/ U- F' @, s2 M& u
                         continue;  / g" m% q7 L+ w4 I& c3 d5 P
                     }  
$ J: B1 E0 B. Y  m                     break;  
) R3 c5 k( M0 W& e' h" C. q0 @                  default:  
3 n/ K1 W5 h: Q  s. ~1 P                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  . d* ~0 Q& q. H2 I- [  j
              }  
! P$ D  b0 W; s' q3 A- Q           }  
5 p# i2 Q# C& p# ~4 W           $field = implode(',', $fields);  
$ V; {2 V/ U. o! J$ S9 G7 k       } else {  
/ p& A4 f. Z7 T8 J  p) S0 e8 N2 ?0 W2 H           return false;  
& x, Q6 ]' t/ D: [0 r) {. I       }  
& e" H8 ]6 V$ [( ?& M( ~+ q       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  , ?- |0 P+ `- q$ h6 ^" |) x# H+ J
       print_r($sql);  
7 \9 h0 K) E3 Y       return $this->execute($sql);  
" n- u( g# d' Y' O' r& P. G    } 2 }. o  k  F6 Q& M' ?0 _4 c8 R, {
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
* q- i: J2 n% ^0 t7 J! \
# ~+ M  H1 T9 o5 a! O攻击测试:4 Y8 C+ }" d6 u/ @4 Y$ U, s
测试地址http://localhost6 V1 l& F1 V0 ?* V2 k; m4 ~
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
% \* f6 {% s$ v5 _5 j# [# H6 L1 G, e- ~: ?" l: `: Q8 c

7 r0 V3 t* |+ F7 k6 R2 p2 _
9 T; i. o2 H8 O$ R$ M& V& @/ o
0 ~& }! F& P! N3 b# r  m% e6 q; m2 \5 M

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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