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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
8 \4 l8 J7 h2 }6 c  g漏洞作者:skysheep
$ R. m+ c8 x# f2 a& o4 d分析作者:Seay  D2 @6 Z9 \# M7 x% p8 ^% R
博客:http://www.cnseay.com/
1 `2 _+ W* g  O7 `漏洞分析:# y8 y/ Q8 f- _
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
# j! X; E. Z, U9 `' j" G' g0 y( i6 T2 Z+ g2 u
; Z9 v! M! ]! G' u$ J

: P0 Q* I( V6 I) Tpublic function account_manage_info() {  : T8 ]4 m0 n6 [- \7 u* d6 S* W& J
       if(isset($_POST['dosubmit'])) {  " I4 F- ?3 [& U! K+ Q2 n) j4 P
           //更新用户昵称  6 O- U% A" z' A2 y
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  7 |7 S; \/ M+ |* o) s' `
           if($nickname) {  % S* e1 |6 b5 |3 R: N
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  ; D1 O( \$ D9 d! K& ~1 m
              if(!isset($cookietime)) {  4 J4 z4 i0 h( @, {# ~& j
                  $get_cookietime = param::get_cookie('cookietime');  3 ~% W3 o! d# D( M  a; T/ b
              }  
  D6 [) q1 g0 H6 c& W3 b              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
7 G5 P+ U  ^! |4 K2 S% h4 U              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
6 w7 q3 n( ~& }              param::set_cookie('_nickname', $nickname, $cookietime);  7 n5 s; f; F% r+ ]
           }  5 ^5 d& g# ^# o/ {8 N- n7 S" B
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
- \- W$ A; W0 [5 A# c! F           require_once CACHE_MODEL_PATH.'member_update.class.php';  4 Q  |9 R- s4 g4 P9 A
           $member_input = new member_input($this->memberinfo['modelid']);  # r0 N6 C; W: A* \( A
           $modelinfo = $member_input->get($_POST['info']);  8 E# r2 Z7 H  J0 b
           $this->db->set_model($this->memberinfo['modelid']);  $ [, [+ c% C9 v/ \6 Q* d' |4 N
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
+ L. L9 |+ Z) i           if(!empty($membermodelinfo)) {  % L* }' K5 f$ U8 q
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ' L' a% |1 y% M7 }3 G( T! C- O
           } else {  * {" D  _) V! {' l" ]% v6 ?: X) h
              $modelinfo['userid'] = $this->memberinfo['userid'];  
& O0 o, ]. S# j& p1 O* |# K, @: i8 Y              $this->db->insert($modelinfo);  + m# y- ^, L2 G& i- C' L$ x; Q
           }
! i! M& k- ^% h4 T8 U代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,( y7 o7 S* e; u0 W
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
# @2 {( q0 i' U$ Y
# `( h" T" A; B4 H" ?
; l5 E" z, ?  D
; e" o1 e3 w0 S3 cfunction get($data) {  
' d# {1 y" [% ~       $this->data = $data = trim_script($data);  . z+ H8 @. k5 E
       $model_cache = getcache('member_model', 'commons');  
% q3 `5 H& K- Z- o' g* m       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  5 }4 u/ p$ `# e0 j- W( ?. W
       $info = array();  6 C8 x. k) O, u9 y3 P; l* @3 B
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  4 j' L8 c: X3 Z" r& ~! p
       if(is_array($data)) {  
, ]' Y- s; f" f           foreach($data as $field=>$value) {  
1 N6 d3 S; W* a+ S2 R              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  ; L! ~) p2 T/ @3 [& g3 H0 E. ]
              $name = $this->fields[$field]['name'];  
( ~7 ~% X# [  f" A1 J              $minlength = $this->fields[$field]['minlength'];  
6 V% v. x8 j2 I* s% J; X& F4 p              $maxlength = $this->fields[$field]['maxlength'];  
5 @6 W5 |5 t7 w9 a% `              $pattern = $this->fields[$field]['pattern'];  + Y/ T. j- t: M# Z7 ~+ D9 d+ K
              $errortips = $this->fields[$field]['errortips'];  
- ~" B' O; z& ~( ^; [) z              if(empty($errortips)) $errortips = "$name 不符合要求!";  
8 D' Z. J" z! b" s- [              $length = empty($value) ? 0 : strlen($value);  ( T  {6 `7 }/ i* u
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  # H3 C2 l' Q# o+ d( Z
              if($maxlength && $length > $maxlength && !$isimport) {  - E  F* e$ m/ T) N: [' @
                  showmessage("$name 不得超过 $maxlength 个字符!");  
$ c8 y% E: }8 p# A) D1 `              } else {  
" I, D7 l8 o5 ^) f                  str_cut($value, $maxlength);  2 y5 b8 g! e8 D2 |5 E
              }  2 e1 p" x7 f( `7 e' I( C& k5 m
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
( a9 }# q4 G5 Y% K; ^                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
( y4 }& d( S+ t# O              $func = $this->fields[$field]['formtype'];  8 c' v( p: @/ x' h  x' i% X) q
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
- `9 O0 n% [, r$ c              $info[$field] = $value;  / w/ G( u8 n7 B) U8 q0 w0 \
           }  
! [& h. u3 G9 T9 h3 R       }  , {) E. u5 c- _6 g( i
       return $info;  ( o" M- K1 q* A( B" @( C
    } " O6 I( m" B- V2 v/ W7 E
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,- Q# K+ v' I& ]% P& s% X: A
3 {! f0 f1 W" c. N% \  S& M
再到phpcms\modules\member\index.php 文件account_manage_info函数' ?9 d& Z3 l5 J2 F4 n% H
过了get()函数之后。
/ t7 m! e( J/ o: H/ G7 ?: `
6 g; W7 F7 z. v: h# _
+ M1 M; `, |# ^$ n$modelinfo = $member_input->get($_POST['info']);  
& Q# G6 w: N% D) D0 @           $this->db->set_model($this->memberinfo['modelid']);  
. F9 G4 p' C  s           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
1 G% Y1 y, _: r           if(!empty($membermodelinfo)) {  , t' P: M4 f, C
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  8 [7 R) m! r5 I* i( J" k
           } else { - c! n  |: l' E, K
直接带入数据库,update函数我们跟进看看
/ z0 Y2 N4 e4 q* |, {+ D5 v$ e+ b$ Q* P: O# z$ @

& L( x: U; P5 T2 V# C, {public function update($data, $table, $where = '') {  # W: D1 `* g$ s4 T! z( X9 j
       if($table == '' or $where == '') {  & x$ f: ]2 h6 D9 ^4 @7 g
           return false;  % w5 E( A3 Y) b# m8 B
       }  - @* M  J; B: \: r8 [$ d% x7 j0 O
       $where = ' WHERE '.$where;  
! H- X* L% y2 G. j( _% V1 }       $field = '';  9 z8 S' I. i. x/ E2 B0 B2 ~- H
       if(is_string($data) && $data != '') {  
' q' a: W  Y% y$ e2 e           $field = $data;  3 y' r8 h/ ?; N0 P! ]* v; e$ f- r
       } elseif (is_array($data) && count($data) > 0) {  
; J0 f, d+ Z8 u( c4 ]- l           $fields = array();  / B9 H3 `  \4 O
           foreach($data as $k=>$v) {  # R# p* J: K0 T- n2 W; a
              switch (substr($v, 0, 2)) {  ( h$ x) c5 K+ ]/ `
                  case '+=':  
7 i# o( U! O/ [                     $v = substr($v,2);  5 Y: P0 P' x( e) x: |
                     if (is_numeric($v)) {  ( [1 f( A/ L* \4 e
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
0 F' g! Q8 y$ o# G7 Y# k, S1 j4 J                     } else {  
4 ^$ e6 ]  [0 w                         continue;  
5 g0 x& O; D; C7 R; s4 E2 o                     }  
: [, X& ~, r  c" l, y                     break;  , N% M$ Z& z" ]! q. O
                  case '-=':    h& z1 g5 \7 B5 p( ~
                     $v = substr($v,2);  ) m! m" K% k0 l# l0 r/ Y! \
                     if (is_numeric($v)) {  3 p) D# y5 j( D
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  % m; r( j9 g8 e8 @
                     } else {  $ H0 @, S9 `* w6 r6 Y- I0 p3 Q
                         continue;  
" \  D+ [! q( r+ `9 Q; [                     }  8 l- c- t2 j* a2 ]. ?# R. @
                     break;  
9 ]+ {2 L% Q& W) V) b0 P1 ?& W                  default:  6 o3 ^) k' j2 v, g# [, \4 ]7 c/ j
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);    D8 x0 H  o  v
              }  5 z$ i9 q1 ?) C# p6 s9 Z
           }  5 Q& h* _* G* W
           $field = implode(',', $fields);  1 V8 m) K% G& j1 o5 k
       } else {  
, r5 [8 m4 E2 w$ A4 t7 ]2 V           return false;  & z. k5 m. X- E# b2 N& Q
       }  2 Q' Z' s7 V0 K
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  - y4 n8 U% X) @+ y8 t' @% J# l
       print_r($sql);  , m- Z, s% X8 x) y# h3 c0 d. s
       return $this->execute($sql);  7 Y( o1 h, d, }- e, B: k7 v
    } + f: }1 w: a1 S" [$ Y( X
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
7 ^( ^4 p: O. e: {+ @; l! }  g4 c8 m( E# k5 h, s' ?
攻击测试:
; J( \9 A* \$ q- _- d测试地址http://localhost2 T8 n. B/ k$ o% k$ n- e
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句# V9 N3 ?3 A; e9 t8 A

( a0 ]  o; \3 i1 q( b' v
8 F8 n0 ~2 M, h' [# m7 j# ^+ b/ J  `  O" R
) r0 k4 H6 D- A8 s2 L1 S! B- m
+ q2 f! A+ g5 k; p

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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