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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
/ E% C" _1 I3 v! w- p; W/ G漏洞作者:skysheep
) K0 D. f( f  ?分析作者:Seay! c% o( R% z9 g
博客:http://www.cnseay.com/" P" C4 k. U9 B5 e/ l$ r
漏洞分析:
' `( I2 g9 ?3 ^$ Z6 i( y  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
- W. e  X3 z: k1 F. M% Z& U( l$ u0 T$ y

3 c  F6 a, J- c; M. M; M
1 J( e+ J( J( U2 K; E# rpublic function account_manage_info() {  
+ Q$ V7 B- p2 I       if(isset($_POST['dosubmit'])) {  
! ]( I- H8 A2 V! c! z# F           //更新用户昵称  
( e1 ~' [9 s& d8 a           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
) K" m: @- Q0 r* X           if($nickname) {  
6 D1 L7 ~+ \) g6 r) X) [              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  5 g( H" z; E# I1 a6 |8 I
              if(!isset($cookietime)) {  3 V- m+ W0 G1 m# b! G9 l
                  $get_cookietime = param::get_cookie('cookietime');  
) L. c4 z0 v( S1 B              }  
/ G6 @+ s1 v6 z7 ?9 R" j+ G              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  / n( @0 d/ B: ~7 ?- L
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  % c8 _$ c2 S! ~' Q. p& I
              param::set_cookie('_nickname', $nickname, $cookietime);  3 q( p. u. u. `2 v7 a# U# \
           }  
& u$ B& L5 H+ J& f) c  [: U           require_once CACHE_MODEL_PATH.'member_input.class.php';  6 z1 q& g( j/ X# v
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
% s! E2 U7 ~# ?+ I           $member_input = new member_input($this->memberinfo['modelid']);  " Z( j2 K$ r- W
           $modelinfo = $member_input->get($_POST['info']);  1 @/ M0 Y4 I$ X' t
           $this->db->set_model($this->memberinfo['modelid']);  # a$ D5 j" h8 C. n
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
5 o% n) }0 G" F! j0 I6 U7 q3 X  ?           if(!empty($membermodelinfo)) {  
0 O! _0 P  {# ^7 m+ S. q% O! C% A/ o              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
$ _1 e- m9 I+ d/ G$ }           } else {  9 K8 ?7 f7 e7 }- d2 z
              $modelinfo['userid'] = $this->memberinfo['userid'];  ; e; D! S; s) B4 ^' W3 C9 p
              $this->db->insert($modelinfo);  
6 n, C$ H# g. E5 g7 g           }
, b5 p$ z& V' r1 P) J3 T# W代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
! a3 Z2 r/ g  ^4 M" a在\caches\caches_model\caches_data\ member_input.class.php 文件中:
1 x& d7 B: x: p0 i3 ^
4 e" l3 j4 O. h; L# S/ Y& ~- s) T  F( K, M# e5 D
0 s* t0 i% ^7 g  K
function get($data) {  3 w' `2 P2 Y1 I: {5 F
       $this->data = $data = trim_script($data);  & B. ?  {* Y0 K6 C6 S! x' `4 V, v% n
       $model_cache = getcache('member_model', 'commons');  2 k9 W8 i9 f: z* q
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
% U& ], ]# u0 Y1 {% y       $info = array();  . W7 [4 r8 y. q$ e9 n
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
" A$ J4 V* w2 B; h  {) X; h! d       if(is_array($data)) {  & z. v1 z7 c) v% y5 U
           foreach($data as $field=>$value) {  
: F: ?8 y7 z2 ~  W8 V, ?              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
" l5 W0 J7 H6 @7 o- ^5 W6 G/ O              $name = $this->fields[$field]['name'];  
8 g) S& i' u7 [; d              $minlength = $this->fields[$field]['minlength'];  
9 o) n* a8 `& w1 Q$ C3 B* X              $maxlength = $this->fields[$field]['maxlength'];  , H3 Y$ f  L% h6 z: D
              $pattern = $this->fields[$field]['pattern'];  ! M7 M# q3 c- I9 y+ T
              $errortips = $this->fields[$field]['errortips'];  
; M8 \: @! N3 Q. G4 g              if(empty($errortips)) $errortips = "$name 不符合要求!";  ! F4 [$ w' S" K) Q
              $length = empty($value) ? 0 : strlen($value);  
3 q& C. E7 w* ?% K              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  ) N+ C0 I8 P" b/ v
              if($maxlength && $length > $maxlength && !$isimport) {  ( j2 m) ?$ a! c; E/ \! u+ I# H( F+ v
                  showmessage("$name 不得超过 $maxlength 个字符!");  
+ F* z! |! I1 J; Y( d/ D              } else {  
( g' O8 Q9 r; n4 f                  str_cut($value, $maxlength);  ) n; i0 {  m1 U2 R
              }  # K$ O: z! z! m2 }9 l' ?
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
" I8 v( `* i. i                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
2 U% A1 b( t) k/ ^              $func = $this->fields[$field]['formtype'];  1 p* v8 W8 v. X, ^( u
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
' ]7 T& b; I# \) f: D4 U              $info[$field] = $value;  3 L, ?: u; x' y
           }  
2 C5 u+ B$ h/ N/ P1 Z' f; N+ @+ Z       }  % M7 N, w8 O& Q" p" ~
       return $info;  
/ n, x* h" b9 c    } , E6 L! }; w/ s6 F( Q
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
$ J. h0 ~; |/ s( m  K  \& j7 r$ O) z; ~& C, l. S2 H
再到phpcms\modules\member\index.php 文件account_manage_info函数$ }- m; w! n' v9 f  D5 x
过了get()函数之后。6 p+ ?! V" o7 H; m
, }) a& W2 j% c# N* m5 V! L

& p3 f( |* S4 E9 v  x4 I& _$modelinfo = $member_input->get($_POST['info']);  $ p, D2 l  E) k$ X+ b
           $this->db->set_model($this->memberinfo['modelid']);  3 g/ m! {5 H0 \: ], f; q
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  - s- H) `7 Z3 A% e" Z& r- ]
           if(!empty($membermodelinfo)) {  
* x- w9 z+ o2 [              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
" W7 g, H; w4 `, J/ w) \           } else { 4 _5 I) G2 J6 A8 r: G
直接带入数据库,update函数我们跟进看看8 |" y' S3 `5 E$ @
  A0 t2 m+ E5 T$ l  S* r
7 p' s7 G+ q8 d
public function update($data, $table, $where = '') {    Q0 {7 o% j  q8 E3 _/ ?
       if($table == '' or $where == '') {  
6 J9 J, n( v/ H* x% _9 h' J" w           return false;  2 l7 r2 _7 I2 M% Z1 r
       }  
" M# `9 j5 _: E6 V1 r" M       $where = ' WHERE '.$where;  " k8 P8 E7 F! O% k! i
       $field = '';  ) T& Z# F2 j* e$ P7 _
       if(is_string($data) && $data != '') {  2 o6 W+ Z5 o7 _
           $field = $data;  
% P: i/ F/ z& u! K* ]- c. x       } elseif (is_array($data) && count($data) > 0) {  ; Z6 l# V! \2 C. r' O8 Q) m
           $fields = array();  6 C' D$ U# r; [4 ]& H2 ?" C* U
           foreach($data as $k=>$v) {  2 ~$ Z" U2 ^4 ?4 n; U1 L
              switch (substr($v, 0, 2)) {  
! i* J2 Q: B* X6 q                  case '+=':  
: o3 x: P7 }$ U" B! @* ^2 q, G                     $v = substr($v,2);  5 u) Z" a& _- e5 ^2 w, k
                     if (is_numeric($v)) {  
2 A9 H! G9 z4 K* Q/ S                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  8 t" e6 d) ^9 v" f3 d  }( ~
                     } else {  6 }2 v& X+ a9 Y- P( ?/ M
                         continue;  
2 p! |' K9 n3 K; J5 A                     }  
, ~. k( t+ \( b, g0 l4 }5 W( a                     break;  # f$ O% J  }; g2 l) A$ D
                  case '-=':  
; p" S+ j  x/ b# \                     $v = substr($v,2);  
" z: Y% k3 \/ x; F6 }% [- Z! F                     if (is_numeric($v)) {  - |4 ?+ u2 F% @, e" M) Q9 U
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
0 o5 \. Y' g) `3 y# [0 u6 h                     } else {  & j' _9 k6 r, S6 N
                         continue;  1 h  e' e/ d3 H: N: N1 l' o
                     }  5 F$ m9 V" Q; P0 m$ b" ^
                     break;  
: B* K, p/ n, ~  r- k                  default:  6 ?8 d/ G1 r+ [0 W3 u$ q- A
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
+ F4 ?2 }  a  U, g  u' x              }  
; Y% t  o1 @, q           }  
" e% r/ k5 v2 u& p3 P. p8 p           $field = implode(',', $fields);  5 _7 T/ `5 J* P# S3 N* c
       } else {  
4 A7 _+ }* J  _, l           return false;  ; x7 y1 S/ ^; {7 Q8 U' s
       }  
" S* b) V  D5 U; L: J) ?! ?       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
, P" f/ L; S  Q) @$ u3 v       print_r($sql);  
6 a5 R' C. |! f! D; l. {& C       return $this->execute($sql);  5 O7 [. W% T2 C; f
    }
6 r. g, r5 B" h# D7 W从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
3 X) Q8 s1 W) R# \5 x, a' _2 Y2 l. d5 I7 Z6 z
攻击测试:% x' W! Y$ @) r8 K
测试地址http://localhost
# _: N. E' I7 D+ W) e  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
7 O! @$ S8 O. o+ G
, i+ L# O8 a, ^# a  Z
9 l  J$ J. E) ~$ s! v: ^3 S3 {8 T8 j

# I& _& X* t% H" A% R8 `8 i$ j+ Y" v! c7 p! v, A$ O4 D+ f: u+ {

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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