找回密码
 立即注册
查看: 2772|回复: 0
打印 上一主题 下一主题

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告( S' j7 w3 D  O6 D9 ?
漏洞作者:skysheep
, {) |) W* ^9 [1 K' T4 D5 C分析作者:Seay
; x0 ^; \4 y7 c6 q% |, }博客:http://www.cnseay.com/, d" g' A4 I: F) E. A
漏洞分析:( O' z, @- p) p/ T
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。* p# u* R) r& v# r9 j" {. y

. x1 f9 J6 l) P8 u7 ~# Q6 r* y/ t) Z" Z  }
7 b. h/ o  _% H/ }; d" k  c" V$ V
public function account_manage_info() {  ( z% E" h  n/ D" l) m+ {2 ~
       if(isset($_POST['dosubmit'])) {  
) T4 |8 i2 l, |$ `           //更新用户昵称  
/ H4 y, a: z' q           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  : `: j  ?* D2 M/ T1 Y+ n
           if($nickname) {  / n" j' h$ {! r/ f, \" g
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  7 K2 k' s8 j+ j& \: K+ H# n3 Q
              if(!isset($cookietime)) {  
3 L8 {/ G/ ?/ H; I                  $get_cookietime = param::get_cookie('cookietime');  1 n! c4 y6 g4 }+ @; L
              }  ! F- u9 o3 y5 m) W' H0 M, H
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
1 Y& W# L" }& \" ?              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  : i' n8 a% f4 e% B- c3 Z. w( j' _! [
              param::set_cookie('_nickname', $nickname, $cookietime);  ! v8 j, W4 c* ~* l$ t
           }  , ^; m; o* l' d" |
           require_once CACHE_MODEL_PATH.'member_input.class.php';  * O$ C4 m4 |$ |7 c$ b
           require_once CACHE_MODEL_PATH.'member_update.class.php';  ; u( p, I( {' P
           $member_input = new member_input($this->memberinfo['modelid']);  
" p# i6 ~6 h3 `" S& k' F; b           $modelinfo = $member_input->get($_POST['info']);  
: Y3 _9 d" M8 O( y7 v           $this->db->set_model($this->memberinfo['modelid']);  
0 o" [) o- p, ^1 t           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
% ?) A1 d, k: l- U. T( l& F           if(!empty($membermodelinfo)) {  & `6 p' c+ ]+ b$ G' E4 x1 H9 o
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  / J2 M; V6 E$ W5 H% i
           } else {  
( ^* r( Q- r; F! n2 N6 [              $modelinfo['userid'] = $this->memberinfo['userid'];  
" Z* Q) c% ?; T, E! {% w8 [              $this->db->insert($modelinfo);  / I# l5 n% M1 i+ t+ j' H; x3 k
           }
- t( G% b8 z6 |. A% Y* X代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
' p) c4 J5 p8 P在\caches\caches_model\caches_data\ member_input.class.php 文件中:* N; {" g  a( R# w5 m+ ^
2 E  H( m# e" w9 l

( k6 u; E" J" X; `0 n: Z ) [9 P0 V; l5 m  P) m
function get($data) {  $ v5 ?+ t6 v5 J% r9 U
       $this->data = $data = trim_script($data);  3 U  P6 r/ {7 I, U; C7 x
       $model_cache = getcache('member_model', 'commons');  
. m% [' V3 t! {- m       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  9 V# x6 v, |5 ?' }8 b% t6 K" K6 R
       $info = array();  . m4 L' o2 n: f, y4 O. C
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  6 U: C# P1 s3 u) i$ j% C2 K9 O
       if(is_array($data)) {  
; `+ P, z% U0 i) s4 p* |           foreach($data as $field=>$value) {  % O5 I; T  o% X. A
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  1 I  Z" w0 |  D  E3 `% [
              $name = $this->fields[$field]['name'];  
7 C% s; I6 R9 N- d& |# k1 c              $minlength = $this->fields[$field]['minlength'];  3 p! X  u$ `/ W% r
              $maxlength = $this->fields[$field]['maxlength'];  
/ m- R) `; g7 `' F7 L) {) T              $pattern = $this->fields[$field]['pattern'];  6 V( \& Z5 m2 e: J  V' A' [% t+ W, V
              $errortips = $this->fields[$field]['errortips'];  ( ~( u7 Z: t& q. ?
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
0 T! V! l5 {$ S5 J              $length = empty($value) ? 0 : strlen($value);  , q# d1 K  y; A6 \; h  b. s
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  & I0 N9 l8 l2 ~% C( R
              if($maxlength && $length > $maxlength && !$isimport) {  8 n: L# c. [, Q& v4 o
                  showmessage("$name 不得超过 $maxlength 个字符!");  
$ D: F6 S! R3 p5 Y* c. K0 V              } else {  
# Y9 E8 f: c1 c3 C/ y                  str_cut($value, $maxlength);  6 i& K: N) \( G* _
              }  
  |0 ]6 R# ]8 G- Q0 P              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
1 T: |/ M- H! X; [2 E" k% @- k' Z                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  & R" `+ n, i/ p8 f( R1 I) I6 q
              $func = $this->fields[$field]['formtype'];  
7 s4 E, C5 L; `2 w" x# c$ P              if(method_exists($this, $func)) $value = $this->$func($field, $value);  : q3 O9 t5 u* ~$ S& `, P
              $info[$field] = $value;  . O4 J4 g* @2 \0 \* s1 o: W5 e
           }  - H7 `# q; f" _2 u
       }  , T- D2 A& M7 F1 @  ?
       return $info;  
3 y- k9 Z8 G& r9 R    }
1 L5 }) L9 H1 h. U+ I$ Ftrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
! a. B! b. {' H/ e
0 {. t0 h5 `0 w) {5 v0 @再到phpcms\modules\member\index.php 文件account_manage_info函数
* h" h9 }: s& M3 V; k过了get()函数之后。
% k, D( d3 ?6 c( S$ |# e* x" G6 b0 D" V: ^( l5 ]  k5 P

3 ?( c4 t. x* h3 l6 a$modelinfo = $member_input->get($_POST['info']);  % a! N# |6 g8 I1 ^
           $this->db->set_model($this->memberinfo['modelid']);  ( k! X; w2 X# t* w1 T; L, P3 w6 Z
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));    U/ ]# U' w. M/ L/ T' j+ f
           if(!empty($membermodelinfo)) {  8 y9 r7 Y9 }, f4 K
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  * D+ T1 B, n" Q6 A
           } else { 1 Q, v$ j7 ^. l/ V. E1 S6 ~/ b- t: Z
直接带入数据库,update函数我们跟进看看" q" D" x1 I. v9 K+ g3 }% Y  y* q
+ k+ H4 v/ s5 a6 f& @  u

/ U& i  ~7 S' m- Dpublic function update($data, $table, $where = '') {  
+ h2 Z, N1 |8 k! E7 d4 q* D       if($table == '' or $where == '') {  
& s# _. Y! W) @9 p+ {7 `4 |. R           return false;  4 Z: O/ t6 F# y6 a! A
       }  ' {: n, K7 r9 p( _3 t
       $where = ' WHERE '.$where;  ) N' ?+ `9 G0 g$ a  C5 c  _8 @
       $field = '';  
! J1 n1 Q7 u  N+ i       if(is_string($data) && $data != '') {  & ]' g$ O# s: u' i5 q1 O1 u* e& h- _+ Q
           $field = $data;  
# |- `9 M6 @. x       } elseif (is_array($data) && count($data) > 0) {  & I* W( N2 i$ f, h/ v2 r. C. o( N
           $fields = array();  
8 m9 ^9 T4 V3 W* z* \, x           foreach($data as $k=>$v) {  
; Q" D/ Y- r) q. H5 F8 o# o              switch (substr($v, 0, 2)) {  . M5 e, m+ ]) B+ y
                  case '+=':  $ }, b$ u5 I8 L7 M( M
                     $v = substr($v,2);  + l; K" g: \  m+ j9 o- o( L$ N
                     if (is_numeric($v)) {  + ]% R( L  W1 j! f
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  & B' G) ~9 u5 G' \; e
                     } else {  3 b2 E+ d3 P6 q4 c
                         continue;  
* }9 [& A2 I/ P: M4 a* v                     }  
: b- a1 Z  V; U: Q* m+ P/ V                     break;  
6 g, ], V+ X+ E8 s2 z3 g2 g+ }                  case '-=':  ) k, ?( o; T. D) n2 [# Q
                     $v = substr($v,2);  & k/ _! a+ b. k& M
                     if (is_numeric($v)) {  3 ]2 F; b* N) Y1 O4 L7 [: _
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
' [# ?2 O- j- h* }, b% }                     } else {  " w( Z+ [0 {3 F" Q0 O$ I' P: C8 H5 T
                         continue;  ' A$ H( g. U8 n0 r5 }; X& z9 J
                     }  
  G6 H% K  h% H0 N" |                     break;  
) D: M2 h# h% ^* ^0 Y9 B                  default:  ; `3 s# C  B! \  M' g. ~* [
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
& T  A+ A9 r" N0 ~$ R' s, x4 E              }    S: W3 d1 z, ?% {5 {' f; P* N5 t) U
           }  7 u  f) u  y) ~% h  ~$ K1 _
           $field = implode(',', $fields);  2 f6 _" K) v* C3 T0 D
       } else {  
# S3 m' P% b! {           return false;  
  O6 P0 P  l3 T; w! R- K$ N       }  
: h( _. J8 E" P) T( B# ^/ L# o       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
2 B, ?2 \# E' X2 g  M+ l       print_r($sql);  
* U6 e% Y7 a& v$ n       return $this->execute($sql);  
5 l1 [% K! m' a    }
  r5 F& [4 V; K! t: x, b# d# M* R从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
& O: C# G; l4 h
& i8 D' h4 U' M攻击测试:
' O. J3 @: s2 G测试地址http://localhost- @- ]4 T2 N3 X( E  B1 V" ]* N7 q+ j
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句5 @2 ^/ P; i9 t# w

/ p2 T" I9 ]9 r. T4 a& V
# V5 t2 L. K+ l0 }
! g4 s+ D, e" n$ X9 E  y2 O3 J: U! U5 C6 q0 M: f+ E9 L

! e8 ^& p) a0 _6 V! g* k1 o: |

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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