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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
; N1 R& G9 A7 r% C7 a漏洞作者:skysheep
. u5 j) e7 o) B' o+ n分析作者:Seay
2 S! v! {0 p0 G4 n1 }$ n  ~博客:http://www.cnseay.com/) l' n8 V7 A# y; V/ F5 v
漏洞分析:1 Q" v+ L/ G# \, m) b# y3 o' x# n
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
4 v8 y/ e# w- w6 W3 F$ k' q0 D( o. T1 D4 y& O

" D" X. M0 B% `* n
( o8 h; Z3 ~! ^6 m1 Opublic function account_manage_info() {  
2 w7 [5 U3 W# R5 {7 X& c( O- L( a       if(isset($_POST['dosubmit'])) {  8 D  C* q  J7 Y- g8 `1 |5 k; Y! w
           //更新用户昵称  ( N# }9 P2 k3 V  {: |
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
- d$ C2 |" S/ `* d! p: X           if($nickname) {  
  W, `( _3 {5 l              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
( n# k3 u6 E, ]* r$ i5 o  a/ e              if(!isset($cookietime)) {  
- R/ I* ?  k* U7 V                  $get_cookietime = param::get_cookie('cookietime');  
5 j' J  k' g. P: b) D5 G. F* G              }  
* Z2 ?7 L" C6 b/ A# I              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  1 h4 H+ q) {( p: i% {! S" [
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  6 d4 g% _# w% A- J2 z* r- ]/ g
              param::set_cookie('_nickname', $nickname, $cookietime);  
( n: w' }0 C6 d2 g           }  
6 f9 d0 B% y1 R7 s: C1 @0 Z           require_once CACHE_MODEL_PATH.'member_input.class.php';  ' _7 i, o+ r2 [$ h
           require_once CACHE_MODEL_PATH.'member_update.class.php';  ( ~, z" y2 Y; `( u* z7 `- d
           $member_input = new member_input($this->memberinfo['modelid']);  
9 i) ?/ D0 z; ?           $modelinfo = $member_input->get($_POST['info']);  $ G% d; I$ q5 c/ C! Z5 N+ h
           $this->db->set_model($this->memberinfo['modelid']);  ( D( K% e  p7 E
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
. [* X+ y, O7 B( S           if(!empty($membermodelinfo)) {  5 ?- S( Y4 Y5 o+ Z: D/ x! Y! B
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
; P. s3 J* A' r5 W7 G           } else {  6 K: E: v3 V* B9 o& N
              $modelinfo['userid'] = $this->memberinfo['userid'];  
9 d' [( C0 e+ h9 W3 U  O              $this->db->insert($modelinfo);  % j7 Y' e- H, D- [
           }
* ]$ G9 D$ o8 m代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
" y! w6 Z1 H" r3 R$ L2 t在\caches\caches_model\caches_data\ member_input.class.php 文件中:9 x- M9 L+ ]) d5 K, C! J# L
! v* z$ c  G- d( `0 r5 n

; u! T) ^5 O% \) ^2 a4 N
9 u3 i' l2 ~$ [. J+ ?function get($data) {  ( L9 w/ o4 c: y$ A5 }, Q& t2 Y& Y
       $this->data = $data = trim_script($data);  , C4 r" G& x; s4 [
       $model_cache = getcache('member_model', 'commons');  : R: a9 S1 V' }
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
4 [7 V0 c* C7 U5 Q       $info = array();  
" G1 A' I; g& N- l4 |7 Y7 f& F. d       $debar_filed = array('catid','title','style','thumb','status','islink','description');  5 N  O3 O  p0 X3 N' m* `7 j
       if(is_array($data)) {  
4 n  [- T1 q0 Y7 Y4 x/ Z           foreach($data as $field=>$value) {  & C: ^$ D( k: {4 f- V( b# N) ^; E; _
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
, [8 ~6 [, M4 e              $name = $this->fields[$field]['name'];  
3 Z, T) {8 I$ H, }6 n              $minlength = $this->fields[$field]['minlength'];  ! G, W. Z* c1 Q8 ]
              $maxlength = $this->fields[$field]['maxlength'];  ; ?" E6 U0 x; X( W
              $pattern = $this->fields[$field]['pattern'];  , z1 b+ ~1 Y+ C# f0 ]
              $errortips = $this->fields[$field]['errortips'];  
, y0 u5 V9 w; U; |& F+ ]              if(empty($errortips)) $errortips = "$name 不符合要求!";  
9 q1 j+ p' t. ?              $length = empty($value) ? 0 : strlen($value);  
- U# O4 a0 l3 w6 ]4 h7 i: v/ R              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  $ Y$ n/ d, H& w8 y
              if($maxlength && $length > $maxlength && !$isimport) {  # M2 m' U1 O5 M/ D# a
                  showmessage("$name 不得超过 $maxlength 个字符!");  
; B. I0 k3 q$ p+ [: P4 i1 n' _              } else {  
8 n( g1 M5 X) e, G- }+ t                  str_cut($value, $maxlength);  
# ]+ T8 s7 J' _$ m4 p              }  " [/ G- h- L8 _0 @+ x& k
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
% z# Z* K+ L& e% n9 e                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
* t1 t6 ?( H+ Y+ n/ V7 @              $func = $this->fields[$field]['formtype'];  8 \+ W% g5 b3 u4 v6 u: S3 \
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  - `3 [' v' W# o& q  m
              $info[$field] = $value;  2 w( ]" B; v. s0 |. m4 {) x
           }    q5 a2 e7 e( i9 W: W2 ^% l
       }  . [7 p; R# b# O& ?% K
       return $info;  $ t/ N5 m2 \9 u( F
    } ) R* B0 |. a! R! R4 _  v+ _1 w& y3 T
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
' p' N8 G! V( Y" O7 N9 ?5 ~  O. R5 }" e- p/ X  J  c7 b
再到phpcms\modules\member\index.php 文件account_manage_info函数9 C8 X5 Z3 _1 h- `8 m, n/ O7 H
过了get()函数之后。
0 v& s  I1 {3 W8 W5 K
- P8 t4 B0 j& H+ i  o! @/ D! a . b: C% {' q+ t% j$ M2 y1 w( Z* t" B( Q
$modelinfo = $member_input->get($_POST['info']);  
/ a, }+ S  S! J- C  k           $this->db->set_model($this->memberinfo['modelid']);  ) b- v, w) O7 o: }/ S& i
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
% t" W  k  y4 `& K2 u3 ?$ n           if(!empty($membermodelinfo)) {  
; {. L2 g6 W# V9 j# n8 ^' e: {              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  . Y9 }2 A9 _0 ?# q3 f) @& v
           } else { 0 V6 D) V& y& v! t  m
直接带入数据库,update函数我们跟进看看( |+ f- X, ]7 A7 m$ n" F

% a1 F1 x/ S* c9 T0 B- C
( g; z4 H6 N" }! @9 r% Hpublic function update($data, $table, $where = '') {  # e8 Z% n4 v  s
       if($table == '' or $where == '') {  * ]; e4 e8 q; H- r
           return false;  ; f1 P% n; a; q6 _' l. X' b% P5 d
       }  & n& {% H* L6 g9 h! w" i4 S& M
       $where = ' WHERE '.$where;  5 H* t% O7 U# N' N. j+ _
       $field = '';  
- B* N( l, N4 R       if(is_string($data) && $data != '') {  - P' y; B" ]. A0 G
           $field = $data;  
/ ~9 v7 }6 U. A6 {9 r       } elseif (is_array($data) && count($data) > 0) {  8 k9 W/ y1 A- P/ @
           $fields = array();  
% {3 [9 g2 ]1 F" _/ q0 W           foreach($data as $k=>$v) {  
0 ^$ {$ |& {( c. @2 P              switch (substr($v, 0, 2)) {  * R% S5 s- G1 M: i: U3 Q5 x7 i; p
                  case '+=':  % X$ v8 z* e! y/ h+ p' C, R
                     $v = substr($v,2);  
: M. O4 }% L% o% C; R                     if (is_numeric($v)) {  
0 A+ r3 r) [; Y& j' V/ s! w6 M) H. v                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);    M" B# E, x! S. h, D2 d7 J+ v
                     } else {  8 |! b; ^$ z1 j  ]$ z
                         continue;  # ^) |" |7 I6 _% @7 e4 ~) {# F6 h( p
                     }  1 y- x, H5 `/ f5 o# q
                     break;  : j  m+ s$ _, e8 s3 Z4 |
                  case '-=':  - ^9 ?8 Q2 E3 B( Y+ k0 v, [
                     $v = substr($v,2);  ' ?$ T7 e# ]* s! _7 p' s: o
                     if (is_numeric($v)) {  
9 _& }" p5 q; p4 o9 h                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  # b3 D4 F( I0 m
                     } else {  / p0 M9 i: \" L# S) U9 u
                         continue;  
3 E3 }$ Y% C; E. ?4 ]. S, [                     }  
+ r0 d$ O: p. @' Z# G                     break;  $ \1 Z1 X8 l2 \& g, n6 w
                  default:  # s/ @. H' I# P/ ^. `  s: R) x
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  0 \0 `! z/ R' ?4 }6 E
              }  2 \% t( b. U$ j3 _8 B3 y
           }  
/ j6 i( D( x# g# s- Z  G           $field = implode(',', $fields);  
$ G' \8 _8 N+ }# q) c) _+ N  K       } else {  ( U" K: [( r( c, o& c& D( A" }% B1 O
           return false;  ' z6 ~" f9 `6 t
       }  
- j7 K6 Q- h  m* ]5 r* d       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  ' P: }7 F4 F" w  U/ m" D* ?  U6 j
       print_r($sql);    _! r: C: S8 t: |( {
       return $this->execute($sql);  : _# O0 K* a; ^% E1 ]! F
    } 3 R. d5 }2 a" p! N( t# C1 j( [& L
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
8 O+ f. j) c& g) V  j
5 Y& M3 t$ o2 T- b攻击测试:, O; ~& e; F* W5 Z  N
测试地址http://localhost( J, N5 a( j! u9 H
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句: b) s$ M. }& D) q6 [/ x

3 G- H+ E' U3 {0 n7 N  ]9 y2 S
( e& j* N: H# }$ \8 \* j" A/ v. G0 m) ^. K" K0 W- Q

8 Z7 I% H1 p5 h! d' D' ?
9 G5 M0 U" \6 P* j2 }+ h* ?

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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