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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
' L9 h, e$ R) d漏洞作者:skysheep4 ]- t+ h" k4 f/ M/ t8 ^
分析作者:Seay5 d& S. G: o* _! P) z, O
博客:http://www.cnseay.com/
; l& X/ Q+ ~: r0 B' s1 S& Q; q0 P漏洞分析:
6 M# L+ f  [+ x' K) a  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。# e2 I% O* `+ y9 K+ V/ K, k

4 r8 h% ]7 D; s' U  r' i4 O. H# p3 r! @( w
* s( A) ~' l0 F# |( q
public function account_manage_info() {  
; \. l* W+ R8 A       if(isset($_POST['dosubmit'])) {  
/ E# N" p2 W/ {% G; P  ]  p           //更新用户昵称  " G5 [8 l1 |, d  @$ n+ }
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  + |/ b  H8 h6 k" ^' w" C
           if($nickname) {  
& Y! D0 \5 @5 |2 t  Q              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  ; r8 e: d+ k7 T6 A
              if(!isset($cookietime)) {  * t+ v' g' G" s0 N$ x7 R
                  $get_cookietime = param::get_cookie('cookietime');  1 I0 d+ o( U! y9 Q, S' N
              }  
5 ]* G; R! C2 _) c1 l1 z              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  8 e- y& g9 P8 A  L/ d
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
/ k' ~, e, G% s9 Y! L  m% C/ j              param::set_cookie('_nickname', $nickname, $cookietime);  # X" ^* @6 v* Q" E' c7 D
           }  
) |6 `5 W% L( @6 d* N1 s# N% n           require_once CACHE_MODEL_PATH.'member_input.class.php';  
4 v- l  p& B; k2 P' I9 a           require_once CACHE_MODEL_PATH.'member_update.class.php';  ' K; v1 t0 W" d. T
           $member_input = new member_input($this->memberinfo['modelid']);  ; k) b1 y0 n; p6 `
           $modelinfo = $member_input->get($_POST['info']);  
- @0 `$ O( T) L$ r3 J( M1 d' M           $this->db->set_model($this->memberinfo['modelid']);  
8 P$ |. X1 z; R/ ~           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  2 E+ u( T2 L- _# a7 u3 b9 \
           if(!empty($membermodelinfo)) {  
5 V! a. M/ E  J              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  2 ?# |2 F3 ~; L4 w; S7 Q6 r3 i
           } else {  1 Y* b1 ]  X7 F" }- F9 f
              $modelinfo['userid'] = $this->memberinfo['userid'];  ) ?- N: I4 M9 G9 v8 `8 _
              $this->db->insert($modelinfo);  
7 w- s9 I. I3 ^           }
9 z2 E- D9 n( i* I0 _代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
9 f; T: H8 m  x1 L3 }; V: s在\caches\caches_model\caches_data\ member_input.class.php 文件中:
9 w- Q# e* N1 D' y* s; R" Y& T: P% w" c- H7 Y9 ^5 v) y# Y
$ u4 F8 A( @, X1 l
& w, p4 E) F" `. e
function get($data) {  
# o7 k. W: O0 z9 W  e' u       $this->data = $data = trim_script($data);  
9 B+ b" v& Y: s9 F7 h       $model_cache = getcache('member_model', 'commons');  
& Z7 |5 G; u; G0 F; v' v9 N       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  , J. Y% j$ U5 P1 I* T& i
       $info = array();  
* ^4 T* P. U( A       $debar_filed = array('catid','title','style','thumb','status','islink','description');  0 D/ C+ V4 E2 S. c* B$ p/ z% `4 J
       if(is_array($data)) {  5 {0 s; N- ~  A1 w! x3 T/ g
           foreach($data as $field=>$value) {  / t1 @0 {9 A  a# @  _2 v
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
. u% t2 ^1 B% t7 x' f7 e              $name = $this->fields[$field]['name'];  : H; C% `8 o9 S* N' H8 b; K
              $minlength = $this->fields[$field]['minlength'];  
- \; d" q! s& |1 ~              $maxlength = $this->fields[$field]['maxlength'];  
, }1 D" u% a, f' L, W7 g              $pattern = $this->fields[$field]['pattern'];  ! t/ E# Q6 g. W
              $errortips = $this->fields[$field]['errortips'];  % J- L" [6 Z( C: m, i6 V
              if(empty($errortips)) $errortips = "$name 不符合要求!";  . M1 \# i' F. I4 L6 F1 }
              $length = empty($value) ? 0 : strlen($value);  % z% d- ^! W1 O1 P
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  4 [$ q/ D4 y( j2 h* }8 F3 d
              if($maxlength && $length > $maxlength && !$isimport) {  / r& D% K% [( _
                  showmessage("$name 不得超过 $maxlength 个字符!");  / F; h6 S+ R/ j  h+ i  w
              } else {  . }( y3 F+ M) y0 r7 r
                  str_cut($value, $maxlength);  
! x* a$ f! D/ W1 x5 t              }  * A( J& P% ~, b1 h* G% C
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  ( A1 B0 F: A& O8 `) W- G% ^
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
* s4 k: D! U; X* a  c9 f) J* q) s" ?              $func = $this->fields[$field]['formtype'];  
, {) ^$ g* G7 u: T7 j              if(method_exists($this, $func)) $value = $this->$func($field, $value);  3 f) X2 w7 b& n2 O9 i
              $info[$field] = $value;  5 Y& Y9 I7 T$ Z: r' V) _! p" f
           }  
( M8 R( L& }& D; x       }  
/ T/ Y! `1 D/ D0 g% N  O6 [7 C$ Q& C       return $info;  
* b* G5 p6 x  D2 z) }* G7 H    } # Z2 x! F2 e8 }
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
' i% i: t' W) b$ t" |2 F# p; S" m8 c2 n% ~% E4 U
再到phpcms\modules\member\index.php 文件account_manage_info函数  |3 x' q" q3 L& g  M4 p
过了get()函数之后。: V! q4 J* I$ d) J

: @) t& F5 J% a! j0 ^* p
& C( k# }& L% G/ g' d$modelinfo = $member_input->get($_POST['info']);  
& a! T0 `& M9 z) l. y           $this->db->set_model($this->memberinfo['modelid']);  1 e  t/ H- J/ b1 V
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  * }8 s1 S; S$ Z* x' D
           if(!empty($membermodelinfo)) {  ' c% e4 m9 L! H/ F
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  3 z: W& I( v+ Q
           } else {
& @" f; ]8 ?( J1 Z* J/ |直接带入数据库,update函数我们跟进看看% ^) Q; ]& s! ]! t/ f
​
, |# I% I0 |; {: M
0 m3 A: p5 E0 f* W4 a! [4 ~. ]- {& qpublic function update($data, $table, $where = '') {  
2 |  Q/ x8 u4 f) R/ ]9 a. Q! K" B/ _       if($table == '' or $where == '') {  + I& t, W3 q( \3 S
           return false;  - K  E3 o1 Q' ^" ~; L/ w" v. X3 |
       }  
8 G9 ^" ~' i2 q) \3 g0 M% Y6 {4 Z       $where = ' WHERE '.$where;  
) ]3 \# f7 m7 T- H, S' j       $field = '';  ( P& m1 ^6 P  |5 A4 A! s
       if(is_string($data) && $data != '') {  % }' a* F* S1 X$ t1 p1 r. P
           $field = $data;  4 V+ W0 l% [2 ]1 _
       } elseif (is_array($data) && count($data) > 0) {  
" u) b) |  l/ N* \           $fields = array();  , P2 E5 W9 J  I- L5 Q
           foreach($data as $k=>$v) {  
& b8 J5 l& b% P, M- e5 C              switch (substr($v, 0, 2)) {  ( ?! }0 E7 [0 R: e
                  case '+=':  
  C2 Q) K3 j7 @3 B# }                     $v = substr($v,2);  $ B' b; ?2 f2 h) a5 q4 t
                     if (is_numeric($v)) {  
% F  l# n2 J! ~# W+ g, \$ E                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
) q' p0 k+ r! D1 U7 \                     } else {  7 w% H  G7 A% ?! J5 o
                         continue;  . Y! I! W/ Q9 E/ j1 O/ `
                     }  
  c4 f5 y3 \2 A7 L( C. P                     break;  
/ x; g# p1 z: P* T# o2 g2 C4 ?                  case '-=':  
  @3 _9 y( K2 l, a8 k                     $v = substr($v,2);  
2 `% p; O. n, R+ {6 ], e                     if (is_numeric($v)) {  
2 V# |/ t3 R+ F- L! ~                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  3 y7 {: o3 E3 }  F7 D, o1 n6 \
                     } else {  5 S+ J0 F! l9 F% f+ l4 W# v/ `5 }
                         continue;    q5 B% }0 f2 `7 }% m
                     }  # {  G+ L  \5 R( E3 Q$ S
                     break;    |# T+ n" l, E( l0 l$ t
                  default:  
6 _# `# m1 M2 ]6 E- C7 R0 a                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  - G4 X. a7 h  W* L- o, W$ U5 |
              }  
1 ?8 A6 i7 U# n  f* r           }  
4 P" t6 [% g+ w" d1 K5 _           $field = implode(',', $fields);  
: q/ E: o' I( z3 Z       } else {  9 q- y  J6 ]" M; O8 u& [
           return false;  ; b' t8 L# r, J; P% a( t
       }  . L1 g# {4 m# y5 Z  |& N* |
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
( b  z7 b$ K- l2 Y8 U! f       print_r($sql);  
9 L+ `4 c9 }6 x       return $this->execute($sql);  9 U* ^+ B) `& a" Z0 s9 y
    }
9 u  i, d4 p) _' s从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
- \: b' P) E! h8 V0 `/ l$ ^$ g3 f: O4 I8 A" o  p, `6 t3 s: U' p% V
攻击测试:& ?; n8 Z- C2 W( B( l3 b
测试地址http://localhost, H6 o: M6 b; O. y
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句/ E7 N8 _, E3 d) C9 z- O4 n

3 b) u2 J# c; \% ?! E   I% m4 J7 j' O2 |8 l* ~3 ?
( M: j9 `0 Z5 X; K& F$ \5 X

, v: `+ I: {4 U0 M; ^- q2 @" D) y1 P

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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