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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
. X( \- |7 ^- `7 }漏洞作者:skysheep# _9 v- s+ T  h7 l
分析作者:Seay" t# y2 d1 {' ^. }" V
博客:http://www.cnseay.com/
9 ~9 e( b, D' a漏洞分析:
+ \5 {: w4 J/ |$ ~  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
* ^5 I# H/ b" l1 z3 n5 H
& R% w8 c+ \7 \' J' h* p( i# D, m  T4 T! M. A

0 h4 l7 ?9 U  ~+ o( Zpublic function account_manage_info() {  
  U% {( C& B7 s8 N" I, L       if(isset($_POST['dosubmit'])) {  
  j, K4 ~2 D& G' |9 f/ n           //更新用户昵称  / ]0 o9 a4 H) ]1 M, l& y1 v& ]
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  / h4 m. r8 g) |  C
           if($nickname) {  
4 F* v6 t$ ]4 H, D" n5 h              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
- {$ |) x% W" E              if(!isset($cookietime)) {  9 ]  x7 t9 v; d/ l$ s0 K) }
                  $get_cookietime = param::get_cookie('cookietime');  , Q# \- ^: Y  M9 B8 `5 ^0 l
              }  
7 k1 L" c$ H# [- c. t6 k              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
  Y) ?+ a! x: t9 ~* Z9 V0 M2 c              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
# G7 k0 A3 X2 D& w6 r              param::set_cookie('_nickname', $nickname, $cookietime);  % \- s" v$ A% [/ o
           }  7 d9 a) @7 L0 r$ Q5 K. n  y! a
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
7 J. i3 l2 `4 S: ]  M6 g$ b  ?0 k           require_once CACHE_MODEL_PATH.'member_update.class.php';  
/ W" q% W5 x' L) c           $member_input = new member_input($this->memberinfo['modelid']);  : }* P. C! j5 d! m
           $modelinfo = $member_input->get($_POST['info']);  3 q- t/ Q2 b  ?+ I) a5 L
           $this->db->set_model($this->memberinfo['modelid']);  
2 h; b1 x6 i# }6 [           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
2 r7 Y5 h6 p- d8 [! T3 ^2 @           if(!empty($membermodelinfo)) {  4 U# Z( ~) l" p& k
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
( P: Y; W( S& o. M$ C. C           } else {  
/ ?4 ], }! w& e" Q$ E0 J              $modelinfo['userid'] = $this->memberinfo['userid'];  * V0 z* C1 Q/ [
              $this->db->insert($modelinfo);  ! W5 N: {7 p( R$ Y$ {
           } 6 ]( Y1 z0 X: U' J
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
$ }; ^8 U# b7 \9 n/ H" z在\caches\caches_model\caches_data\ member_input.class.php 文件中:
( a% C3 K/ a. s, T9 ]# L  ^$ z8 r  O& K- M
  B4 I( f4 K$ T. w: p3 Q8 l

0 s" L9 T7 K$ n* ^function get($data) {  
2 V, W- B: i1 p$ B* b6 ~       $this->data = $data = trim_script($data);  
( |) ?0 D. _* B# a! V$ N       $model_cache = getcache('member_model', 'commons');  ' [* w  x; g+ \+ p/ Q
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
5 ]$ h( c$ i1 ?1 J. B; p       $info = array();  % w. u" }7 b9 Z
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  4 @8 M8 b, ?& b! q: V7 x
       if(is_array($data)) {  ! C0 L6 W% m% d- i% D
           foreach($data as $field=>$value) {  . ~: t) p4 P; x* }
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  1 y, ^9 q/ Y7 V8 Z: s
              $name = $this->fields[$field]['name'];  
" @/ w. i5 m' [" a0 t              $minlength = $this->fields[$field]['minlength'];  ( l6 s( r9 Y" e7 f  S% x
              $maxlength = $this->fields[$field]['maxlength'];  
  h# z- y; A, ~8 U8 ]              $pattern = $this->fields[$field]['pattern'];  , E  E& |  y; r# s
              $errortips = $this->fields[$field]['errortips'];  
6 s/ i- ~- ?# d4 Y" B5 J  M              if(empty($errortips)) $errortips = "$name 不符合要求!";  
9 Y4 G( @/ \+ Y8 ]              $length = empty($value) ? 0 : strlen($value);  
3 y, }0 O/ ?9 k              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
5 v/ ?% U( ^$ C9 M% }. v. r              if($maxlength && $length > $maxlength && !$isimport) {  
, B! \. S5 Q  |6 b                  showmessage("$name 不得超过 $maxlength 个字符!");  : [9 M- L4 C1 B" ^' c& [" H
              } else {  1 D6 Z% h' C7 Q" B! P
                  str_cut($value, $maxlength);  . r6 [! h% r( X' L
              }  
% `7 {2 c- {2 U6 k% ~3 e: n2 I              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
0 o% [' z% H, g& u7 T                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
) E' Y! z4 q) J* n7 k7 _& P/ ~              $func = $this->fields[$field]['formtype'];  ) A: L- C+ h0 q* Y4 z6 j
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
% @# H; x! l% n1 `              $info[$field] = $value;  
3 A5 Z  Z. c8 [# L2 H2 A! l( I           }  $ E% t5 O2 c2 z& ]! U  z
       }  
# |6 z/ b. B& h4 B" [' U7 L8 v       return $info;  
' E! }* C/ O5 s7 W    }
: n( D0 T5 w, t4 e! Q2 btrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
1 ^, H- ~9 a+ K! D4 Y2 y3 Z
3 n/ |" w: _; U+ @再到phpcms\modules\member\index.php 文件account_manage_info函数, c7 T& v4 ?7 q" C& P% a
过了get()函数之后。0 F: d( X5 J+ P) k: J9 Z% X( k8 R
# s8 s* k: v+ i
" k1 e. t' |. C# |! S# @# L
$modelinfo = $member_input->get($_POST['info']);  
, B: `, Y& j0 _3 S) A' b- z           $this->db->set_model($this->memberinfo['modelid']);  ; _) }9 c  O! `  L4 ]6 F
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  6 N1 J+ o0 L3 }  V2 Z. e" S4 g
           if(!empty($membermodelinfo)) {  2 R+ g; [  q& R7 g0 S) o* u8 M7 u
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
. S1 [! K, K- d( v  d1 u, b           } else {
5 _( X6 f4 v. _$ e" H直接带入数据库,update函数我们跟进看看
5 K" V0 M' ^+ G$ e& s" O% [
7 y& G* x( w! T4 X( | ( v& A" S8 }$ v4 m7 Y+ n- x
public function update($data, $table, $where = '') {  6 P' D8 Y. k( h6 H7 ]! _" S3 A
       if($table == '' or $where == '') {  ! q& K! I0 ~. j0 ~
           return false;  
' d" E8 n3 |( a# {, z       }  2 B5 F: ~5 ]7 W# M' w% w) u$ V7 n
       $where = ' WHERE '.$where;  
3 v" L5 }3 o' f4 t* X( K% z       $field = '';  : i/ A# m+ v% i, s
       if(is_string($data) && $data != '') {  8 R2 @4 ?  N2 [8 J. z
           $field = $data;  
1 l: I$ z& b# @, U: Y2 n" k1 Q) R- T       } elseif (is_array($data) && count($data) > 0) {  # ~, _% B1 b( e
           $fields = array();  
% J) K1 f) Q; S4 \* H/ e           foreach($data as $k=>$v) {  
8 c, V, Q* Q/ b4 k0 x1 A0 M              switch (substr($v, 0, 2)) {  
) r& z/ @* u# i5 d9 \/ c7 C. e                  case '+=':  
0 y% N1 J3 D0 P) S" u9 {1 q                     $v = substr($v,2);  + C0 \2 F& C- J) D! G) T
                     if (is_numeric($v)) {  
- |# Q+ H. S6 x* j7 ?9 _                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  6 n* C3 q+ ]7 X  c, c
                     } else {  
" C2 D2 Q$ v. t/ s8 O6 j8 B: \0 X$ F                         continue;  
% l+ J: k% ^' U6 s                     }  4 u- Y7 Z) d7 N$ s6 @  `' H5 J
                     break;  
0 F  `; s$ d+ Q% H& w4 B( [                  case '-=':  ) r8 Z+ e& Z5 R% d
                     $v = substr($v,2);  , I; a& O  C5 u% n" i& d% ^
                     if (is_numeric($v)) {  ) ?2 |$ e$ d0 ~& z4 S1 V# u
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  + m( {' Z6 D2 S& `/ ]' S
                     } else {  ) ^2 l  z5 v8 ~
                         continue;  
, h( c( j8 V' L! ^& X                     }  
' j4 T$ f% S5 K* b, ?                     break;  & e5 T) s- u- ]9 D  c% b
                  default:  - |$ d- ~' `9 M- @7 G2 B3 t
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
+ Z( w- U+ Q1 s+ g6 q              }  % C* u9 C. Y6 e# Z# E8 N5 P3 {) r
           }  7 r: z% Z( h3 \" O4 \4 x  \
           $field = implode(',', $fields);  6 k" _  S( d6 K9 b* P
       } else {  # O6 O6 Q3 l6 v) B3 k$ q
           return false;  
6 d# a/ h$ p; A- n, m- R       }  6 C% ?' V$ R  c4 v
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
/ I! {0 V( C( J% {; C0 H7 Q/ \       print_r($sql);  / I6 u6 z2 L( g# |4 Z" @* y
       return $this->execute($sql);  ) m  n6 s) Z' v
    }
: m# h: ]% t' {9 o' ]从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。9 J6 d; Z" @" \1 V" b- O2 N
% C+ w' k) x; j% C/ P
攻击测试:, ?* g9 s9 O: O- Q! \! P
测试地址http://localhost' t4 F' |# x& K; Y
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句9 L0 K! C# J9 ^* q0 d' [

# d$ \$ P& ]+ Q* K 6 P8 P+ v# g7 v& ]0 k

& V3 Q% c* ?% `3 L: \  |/ y* @( K1 _& l$ d( @6 i: W0 b6 }* I

! f) J1 X, R- ~0 s' Y& z& G

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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