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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告1 P6 P" ^/ z0 d6 J+ f
漏洞作者:skysheep
1 {1 I$ z7 u: V# I# {6 w+ w分析作者:Seay
/ }' U' X2 n) }( Z. X博客:http://www.cnseay.com/# Z1 v* ]4 j6 j/ H) p
漏洞分析:
1 J6 I; Z; `& F3 U  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
& y) v( {, m$ A3 Z6 a
# t4 v2 g6 b+ M7 r9 u1 @
, K* m# ^* U3 l+ T; A& D- z. x' [
) Z: w& {; ]# i! ^$ d2 ppublic function account_manage_info() {  7 T" v3 d: k5 f( \# k  o
       if(isset($_POST['dosubmit'])) {  2 k8 l- ]. w+ D8 I
           //更新用户昵称  
  r* p; ]' v' `! u3 n6 B           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
9 }2 |2 g, n% H$ y           if($nickname) {  7 f6 }5 W3 J' P+ U7 Y
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
: g, j5 T# Z$ K1 w              if(!isset($cookietime)) {  & Q3 R# E' h" ~/ o9 d& F
                  $get_cookietime = param::get_cookie('cookietime');  / x6 Q4 s0 u1 K/ o3 v; P
              }  3 e2 f% O. T/ y8 L: y* |
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  + n$ m, Q  n! F! d
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
) s; l- H* V% Z* J0 N              param::set_cookie('_nickname', $nickname, $cookietime);  
, Q1 n9 ~0 X5 T- T# Y" y6 `; u% C6 u           }  : C1 q5 Q5 f  {/ F7 t
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
+ W8 V: R/ E0 d1 V           require_once CACHE_MODEL_PATH.'member_update.class.php';  4 |; f" _2 ]$ k) h( ^( M1 D+ l
           $member_input = new member_input($this->memberinfo['modelid']);  
8 E/ C0 g$ v- ~# g: G2 L% i: C           $modelinfo = $member_input->get($_POST['info']);  
# r( |8 c4 w# \+ y4 Y* X+ x1 T: F           $this->db->set_model($this->memberinfo['modelid']);  0 w: G) L' w% o4 ^, G# r+ J$ S
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  5 O% G  D# C/ {7 d
           if(!empty($membermodelinfo)) {  
! Q: @9 H7 o. z9 k              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  2 r5 c. Y7 J6 G+ |9 u
           } else {  & a1 P$ p; u. W+ h  q/ G% V
              $modelinfo['userid'] = $this->memberinfo['userid'];  
  ^/ S- Q  I/ N              $this->db->insert($modelinfo);  
  P4 K, ~. ]5 K: Z           } % m6 O/ K# j; T; }( J3 S
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,( y8 q8 r$ R/ X
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
! N% Y1 e) Z' c. s7 [# P0 m# ~
7 h, y4 @- ]7 G0 u/ n$ Q3 c  _. P( A9 H

9 s2 x+ {" x0 V' V/ G1 X& T# rfunction get($data) {  4 y, H! V& c, u; E( d! [
       $this->data = $data = trim_script($data);  8 k  h; C2 m: r  _+ h1 Y
       $model_cache = getcache('member_model', 'commons');  
+ n4 m" T7 z2 r/ Q9 o4 p1 d# f       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  ( ]  l) I9 T8 F8 J. _" ~% v( U
       $info = array();  4 A$ ^. @7 P! N3 i
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
+ R, c3 h$ I, w1 V8 J       if(is_array($data)) {  
0 _9 g& i9 ~( x           foreach($data as $field=>$value) {    z6 Z8 t& `' K7 D
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
( d6 M$ R4 H( i' _/ ]- d              $name = $this->fields[$field]['name'];  
4 ^% n% j. B3 {( z$ N              $minlength = $this->fields[$field]['minlength'];  
* M1 d/ x# m3 p; u8 _7 P# m              $maxlength = $this->fields[$field]['maxlength'];  5 S+ p: h. @+ m+ ~+ L; C' u; I
              $pattern = $this->fields[$field]['pattern'];  ; A) I' v4 W. u% e6 M7 ^
              $errortips = $this->fields[$field]['errortips'];  
  a& n8 v# {5 B' t" o              if(empty($errortips)) $errortips = "$name 不符合要求!";  
& B6 v3 ^" e6 H; Z! o- J  ]              $length = empty($value) ? 0 : strlen($value);  & i& J2 U. k, ?8 u
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
& Q9 X! I: \# P# H3 C$ \$ z              if($maxlength && $length > $maxlength && !$isimport) {  8 Y$ d; k) z+ ~( i) F" I
                  showmessage("$name 不得超过 $maxlength 个字符!");  ( I# O# X1 W  W) S- A/ n
              } else {  1 X/ y2 U, Z5 G0 P
                  str_cut($value, $maxlength);    u; B3 D( N7 u; [
              }  
9 W# G6 X" t2 k  U5 I+ f              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  4 O8 m' L9 A- O; h. |+ \! l. Y4 Q
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
0 |- `7 ?% _& G6 u  B              $func = $this->fields[$field]['formtype'];  : ]: `5 S1 r# O! P9 \2 s
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
/ j! h* A" u% y, r! t6 ?: X              $info[$field] = $value;  
  C, U: U0 c  q/ c  U4 T) \; g7 p           }  
2 i* R/ k1 U/ x! `       }  
+ I/ q: a+ L5 W6 h% S7 [       return $info;  2 p9 c3 r' W3 J3 s+ U# W3 G  L
    }
2 s* e1 X6 l+ Y' ~/ e- N, g6 etrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,+ F2 m. `  T7 K5 q1 E! o; X
( ]0 @. X2 ?2 @3 q+ Z& h: f! I
再到phpcms\modules\member\index.php 文件account_manage_info函数
" R: M; }" @" ]1 Q4 S$ x过了get()函数之后。! ~8 C) J- @' t- b* I4 I2 E
' u& I, `2 N  D# [( g9 i

) j6 X- B& \/ [( T- a1 s$modelinfo = $member_input->get($_POST['info']);  
$ q$ o* P9 c0 [: [  O8 \           $this->db->set_model($this->memberinfo['modelid']);  5 F: r3 `% ^: h
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
4 Z/ V) j, o: H$ r( Y. b+ I           if(!empty($membermodelinfo)) {  4 x, [1 q+ s0 g6 I# H+ t& I( ~* B
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ( k  r6 v) V5 X5 t; C  L4 F
           } else {
  w- k* o9 t5 b3 ^( W  v6 H) u& a% K直接带入数据库,update函数我们跟进看看
. V! W+ W6 o4 }0 r- Q
1 C$ H9 W0 ?( e
: [6 n" R1 j1 \7 P0 r/ c" e! [public function update($data, $table, $where = '') {  ; r3 y3 e. Q  y( F
       if($table == '' or $where == '') {  ( f! y) o5 g3 V) E: K( u) _
           return false;  
$ b" L9 P! l1 x       }  
# z: L' l9 }6 q% K' R$ U4 t       $where = ' WHERE '.$where;  
, \4 l& o9 J0 w; J8 }2 |0 l7 x       $field = '';  
$ v3 _' A2 |/ I' |3 V4 h       if(is_string($data) && $data != '') {  9 |3 U1 ?& Z) x
           $field = $data;  
& f, K/ Q, [" |% a. z; x- ]       } elseif (is_array($data) && count($data) > 0) {  
$ U6 D' Y! N8 _# R  Y2 L3 g           $fields = array();  
( M! o( x' H3 [4 U, E' u" s+ Y           foreach($data as $k=>$v) {  
" Y; R! A) v6 z0 }5 Z              switch (substr($v, 0, 2)) {  
( d  J! J+ j& w& J) G! u4 |                  case '+=':  7 ?( H/ t( ^0 O3 [! K9 H
                     $v = substr($v,2);  ; f( @" ?" p# _! P9 m
                     if (is_numeric($v)) {  : T! u. I, ?. J; Y) I# [
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
/ b1 m( F  y! ?5 W: N- o! o                     } else {  
" F  s: f' \0 z2 E                         continue;  
/ R2 a; D& U- S" c0 t                     }  * p3 A& [$ }1 q* J2 g- h' `; K9 m
                     break;  
7 h& n3 _# J( V                  case '-=':  
( [+ k9 m5 _4 h# P1 c( X                     $v = substr($v,2);  ) ?0 U7 U0 ]! n; O- J5 J$ q
                     if (is_numeric($v)) {  
9 ~6 `, Z. a7 u2 u' A                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  ' q: J5 ~/ q) [& U' n% ]  `
                     } else {  
& A, j2 X0 G2 T: |* p                         continue;  . U7 i. E" v7 I$ b/ y5 a. W
                     }  
& d/ I- s7 v: P0 |7 a+ j' h! ^7 a                     break;  
" T1 t9 W$ O$ C9 f0 d                  default:  
* t  t" S0 z( Q! Q                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  0 e4 C* a; G6 T. G. ^! K; f
              }  & A9 W  g# ]3 Q; _+ k
           }  / W7 K1 }3 Y: f' j) T6 Q* d9 _
           $field = implode(',', $fields);  
( Y$ D: O' \; W6 Q* k$ m3 h       } else {  ; g$ v$ U# C8 [. Q$ \
           return false;  
( @7 e1 `: Z! N' c( d( T       }  
4 Y4 |2 v+ x( X7 q. E4 s       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
9 ~$ Q9 g$ o5 X! Q$ ~! n       print_r($sql);  * u+ t' H  |$ d  c
       return $this->execute($sql);  5 f& h" s& x+ O; F" ^8 I
    } : K. w7 E" [; ]5 [8 M6 v
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。5 Y% c& R. m# h, v& d( ?
' Q' |: Q* A; y' h
攻击测试:( D+ b# T! c/ h" K! L0 r
测试地址http://localhost
+ p, K# u1 R; ~$ a) z0 i# B! Z  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句' m" X, C% x4 k- M$ y
0 j# a) G5 h7 G  X
0 d2 S( J6 l. Y- q1 ?

) J; i! v# b4 ]* ~
) q6 i4 m9 L9 N+ ~& T9 }# V
2 ^, I7 X; R4 k) E# O% r, Y5 _

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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