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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告5 W0 G; J5 f: N$ d) m
漏洞作者:skysheep% O6 d+ f  r7 M& P' y" E3 p; o
分析作者:Seay! W7 u) x; M1 v$ B2 i8 Q. [% f
博客:http://www.cnseay.com/9 C# O% D: a; c8 ]
漏洞分析:( v) d+ o. I: m1 w5 e8 Y% y
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。. [$ L+ p; j  ~9 |7 p. \2 x! ~
5 v3 b0 d; Q1 G. R' e) s
5 ]1 |2 \- R. a& e
1 X" [- ^# V, ]( Z$ X5 B/ j
public function account_manage_info() {  
& o' h5 Y9 m0 E* v       if(isset($_POST['dosubmit'])) {  % ~1 `0 [' Y) x
           //更新用户昵称  
" d0 u8 d+ F, `7 |           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
( c4 P( ~% O/ a9 B) b! d0 Q           if($nickname) {  
4 y) V( P" Z5 k% H' u              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
. p- M, C$ x( `/ \# `: d4 W              if(!isset($cookietime)) {  
( _+ h/ A/ M/ ^1 K                  $get_cookietime = param::get_cookie('cookietime');  7 Y6 g. U* p4 ?* g* u. j; S
              }  % M: _2 `" ~& _" X0 G
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  4 e3 p1 w% T; q: q- ~6 q
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
! e1 o6 s/ ]2 \1 T" x              param::set_cookie('_nickname', $nickname, $cookietime);  : E$ V7 s6 B0 S) X! s! B
           }  
: T" x7 v; h" }$ t; T           require_once CACHE_MODEL_PATH.'member_input.class.php';  . T1 j/ T8 U& h1 z
           require_once CACHE_MODEL_PATH.'member_update.class.php';  9 e0 z3 ~& n" m) H! E7 H4 q
           $member_input = new member_input($this->memberinfo['modelid']);    r0 b) q1 v' Y: F
           $modelinfo = $member_input->get($_POST['info']);  
6 O6 N1 {7 ?* |' A2 P           $this->db->set_model($this->memberinfo['modelid']);  
: F3 K" p  t4 D, u6 U8 Z3 _4 G/ s1 _! t           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  6 s) X% f$ M5 G* e$ I
           if(!empty($membermodelinfo)) {  
$ |7 k5 ?, i( S. a: R              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  $ w/ }! X$ |. g$ Y" F6 V6 k0 d
           } else {  9 {1 f3 y4 ^& `% d( t
              $modelinfo['userid'] = $this->memberinfo['userid'];  6 x; d, q8 ]+ L: x$ r9 O
              $this->db->insert($modelinfo);  + [, f  y5 j9 }' `
           } + |6 r& P8 F! W5 D  F
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,# c& {) F9 M5 m
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
( d: U/ |) V& {( D# I- l$ ^3 K% I6 `: `/ Y

, d7 M) C/ l+ q9 a
4 b( q1 j3 h) a0 N% K  M0 Lfunction get($data) {  
+ M0 @: a  r2 N  I       $this->data = $data = trim_script($data);  
/ Z% [2 N$ o2 e% @7 S) J. T7 I       $model_cache = getcache('member_model', 'commons');  % {& [4 B# h2 o' V! I/ g. K. k
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
2 F7 |# e- C( G9 P4 _       $info = array();  
$ y: C7 |6 G7 M6 X8 W1 h       $debar_filed = array('catid','title','style','thumb','status','islink','description');  : b2 j- K3 \: p; o, H
       if(is_array($data)) {  8 T& D' `+ s* w: z
           foreach($data as $field=>$value) {  
; c, [( F' J( u9 J5 K7 {) D+ v              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  3 ]5 |6 C1 Z$ @, e* ~# V, w
              $name = $this->fields[$field]['name'];  
0 E" c7 T+ e6 \              $minlength = $this->fields[$field]['minlength'];  
6 M( B2 ~" n* e8 \9 O              $maxlength = $this->fields[$field]['maxlength'];  
# v& l/ V: w3 ?- C/ K6 Q              $pattern = $this->fields[$field]['pattern'];  
# z. L6 w* z+ E0 H1 S2 ~              $errortips = $this->fields[$field]['errortips'];  
" H* D! \; g& ]! x# w              if(empty($errortips)) $errortips = "$name 不符合要求!";  
8 |; v& Y! V! f/ o) e! p5 v; g              $length = empty($value) ? 0 : strlen($value);  
9 v( Q1 n  j6 j3 X              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  : U! k) P/ C; E# u1 }' \/ O' G
              if($maxlength && $length > $maxlength && !$isimport) {  6 A$ L- O& L( V9 y0 u
                  showmessage("$name 不得超过 $maxlength 个字符!");  , W  Q6 g1 Y# R5 a
              } else {  ; D0 y- d+ ^3 `/ l/ m
                  str_cut($value, $maxlength);  # H- Q6 Y' i9 k- F
              }  
- Y1 c' x/ s5 @& N# C              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
8 n' T) s. ]8 m& [! b! {                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  % c( r) ~' ]$ e+ P4 C
              $func = $this->fields[$field]['formtype'];  ! O+ J0 K7 N- @* y
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
, P7 \5 h( @: Y' }              $info[$field] = $value;  , y1 i2 K: Y4 ~. \7 c
           }  % u# m, J  ^- p3 L/ K
       }  2 \! {2 R; o7 Z# Z- m' h
       return $info;  
' p# I" p0 B) V( v  j  }3 \    } 3 V: N  n' d' V1 Q7 J8 m
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,$ J+ G1 ?8 G" L: n) h
8 A, A  d6 s3 z1 v' ?+ R6 ^
再到phpcms\modules\member\index.php 文件account_manage_info函数
9 X$ V7 A. F& C3 \9 ^. j/ D过了get()函数之后。2 l: B7 ?2 r+ R% I
+ `5 j( k3 z2 |0 k- Y4 o

7 ]% {3 @! f( W/ B$modelinfo = $member_input->get($_POST['info']);  
  G/ Q3 r, ]2 p           $this->db->set_model($this->memberinfo['modelid']);  ' y2 ?0 G+ t/ E: z
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
# b: S0 d9 x* B5 z: Q& O           if(!empty($membermodelinfo)) {  2 I& k3 M6 r* q- Y0 \
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  # O, L3 j* ]; z  h9 M
           } else { % r8 ]) b; J: |) ~; O
直接带入数据库,update函数我们跟进看看' z8 i& n3 c7 F  ?& E! e. X* M

( ]! W+ C8 U: R' ?
* i) M$ w# a" c6 z2 I8 S: rpublic function update($data, $table, $where = '') {  
3 M# O! \+ h* r/ r0 |( F- ]9 s       if($table == '' or $where == '') {  
. m5 Q( K* W- B: U9 z           return false;  
7 s) ]) ?8 t2 P! {7 r       }  
+ H) t& j2 h4 p( i       $where = ' WHERE '.$where;  
3 \9 h) W+ u# }5 _       $field = '';  
4 b+ S$ y! m* i% a- t1 X7 u       if(is_string($data) && $data != '') {  
3 k) B/ r9 N5 j; {           $field = $data;  
% U1 }7 J8 J8 a7 U) U, B       } elseif (is_array($data) && count($data) > 0) {  . A0 L9 g( X5 f1 o
           $fields = array();  
8 D1 r; I& i, J           foreach($data as $k=>$v) {  ) _* U# v% R: }  x0 f
              switch (substr($v, 0, 2)) {  7 R) G0 v3 V5 g' }, H
                  case '+=':  1 {# p% R1 ^+ |5 K
                     $v = substr($v,2);  
, u1 ^9 ~  |1 ]5 @$ q                     if (is_numeric($v)) {  ( W% Q5 t) Y, V7 E+ |) G
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  ( t1 o0 y3 a5 T; q: b7 g( Y6 n
                     } else {  ) R- O& t5 ]9 J" N( @$ N2 Z
                         continue;  
$ W" q9 S* @0 S                     }  
% X( u9 A* b  G+ F* g                     break;  4 R0 v  S+ x- e9 D0 {
                  case '-=':  1 W$ i  t; ]5 L# B% o* \
                     $v = substr($v,2);  - E& u  d4 ?) W7 E/ I
                     if (is_numeric($v)) {  
+ F, A2 S, v: R+ C. N                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
& F  l) B5 s/ F2 b2 @; M/ u2 H                     } else {  2 ^' a% D4 Q! k+ r
                         continue;  2 h# E1 W7 S+ @% x
                     }  8 K% K8 o! o* D% f  m( R8 [3 L% Y
                     break;  
" ?' }# j; W3 O! E) m                  default:    p, t/ r- t) d2 D0 t* {
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
% c) f: L) `5 u: Y8 F8 b9 m# {              }  5 k; h+ H9 Z0 c5 B
           }  
' A' N" @/ D1 g* r  u( \           $field = implode(',', $fields);  
/ c+ r4 D$ B7 \' u9 `  S/ H       } else {  $ u" F& H/ l# J& P' n0 d
           return false;  8 X$ L% ?. b) v. B) l
       }  : L; O6 q" V, O, _  d) }
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  ' Z6 m) d: ?' J* M1 U
       print_r($sql);  5 z4 A: _7 e8 B+ v( R8 Z
       return $this->execute($sql);  " y' r6 J/ u' P! i, f
    } * p5 ~+ a. V# C. `- _7 k+ \
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。" U( Q( T" }6 l2 d& i: N
8 l4 f0 `- s, q- Y
攻击测试:
. U% z- Y3 M) E7 c" `测试地址http://localhost2 H6 e: w+ h6 A' C! `2 H3 d3 C
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
3 t1 E3 ^( \; w3 N$ h  ~: z3 g& N* h, t1 N( t$ Y  C
& o9 L8 x1 e- {# a. x4 S7 Q9 y
5 _% l+ w; n6 B4 k. K% l' p# {- o8 P

5 p/ P& |+ j7 M/ I: w! g
% q! K3 S4 }0 F$ ~: c, v

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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