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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告( i. S$ g% B* y" g
漏洞作者:skysheep$ E5 X+ R2 u  \6 E' d
分析作者:Seay
" m, v0 S8 O! P0 y" ^博客:http://www.cnseay.com/
) E0 O$ o: \- @9 V8 R, d漏洞分析:6 [  E6 C1 b. k5 L9 f: ~
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
1 h2 d8 w' h) |. d# m$ s$ D: O/ [0 H+ b$ U5 Y$ R2 e( x( D- c

' }6 |4 ^+ |* f+ O
$ j, s3 O* `  y3 F1 N' Opublic function account_manage_info() {  
4 _  e' V% L1 }, q       if(isset($_POST['dosubmit'])) {  
+ i" ]- l) C  B0 A, y4 v& |           //更新用户昵称  
7 Q+ p$ n( k, M1 a7 O           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
2 P4 Y7 ~2 k# l1 J, q/ v+ s           if($nickname) {  
; |' q1 y5 [6 @1 D/ `              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  8 M5 W+ B! O2 q6 O9 D0 _( k
              if(!isset($cookietime)) {  3 p; V# T. d# |( A& `6 p
                  $get_cookietime = param::get_cookie('cookietime');  
# p/ _! N8 a! [6 h7 x& ?$ Q1 L+ F              }  1 F% V, b  C! i, |+ v
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  1 `# Q+ M. O* k  F( x# L; k8 V
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
  f5 [, k7 G; ^- R7 e              param::set_cookie('_nickname', $nickname, $cookietime);  ! v0 {; j2 R4 W! Z- a* ^( @# P2 q
           }  
- U! S7 y, e) }  x! y           require_once CACHE_MODEL_PATH.'member_input.class.php';  ; o- D; g( p( F" Z0 T! D( R. N
           require_once CACHE_MODEL_PATH.'member_update.class.php';  5 a( }) O! j2 _1 e. z- {
           $member_input = new member_input($this->memberinfo['modelid']);  ; d, u6 H5 R5 e, Z
           $modelinfo = $member_input->get($_POST['info']);  + o0 A# i) M2 l* g
           $this->db->set_model($this->memberinfo['modelid']);  # s% b) \! r/ D, E. U5 Q8 d
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
/ d1 k. H9 F) C           if(!empty($membermodelinfo)) {  * D8 d; N- Z$ q/ o! k
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
& {' M& h, B. `  v' h9 @. A& D8 A           } else {    \% F( R  R$ z8 r1 s0 W
              $modelinfo['userid'] = $this->memberinfo['userid'];  $ r9 r6 x7 V  |* |  I$ X+ F/ H
              $this->db->insert($modelinfo);  $ i' J% k) O. N" M0 {1 w' p  g
           }
% n" M9 t: u& M1 u代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,) j( S8 }% ?. i  h7 k  @
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
6 Y5 w* F. S0 B, l; N5 ~7 d) a  B0 ]& d. g2 R  b
" n" s1 K# \7 `" |: e6 X+ d
: T$ j! p; C4 ^6 s- C  [' f& q
function get($data) {  0 |" q9 f" u/ M
       $this->data = $data = trim_script($data);  5 {6 L3 S* h5 f8 R
       $model_cache = getcache('member_model', 'commons');  
9 Y- @' u# b' d6 c6 X( y% @( H       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
, z" F  r" K' d       $info = array();  
6 a) A" A  H& {; J  N       $debar_filed = array('catid','title','style','thumb','status','islink','description');    S. v* p5 U0 r1 H
       if(is_array($data)) {  
0 ~, g  E; p7 O& }- I           foreach($data as $field=>$value) {  
1 ]1 S8 Y; L9 o) F: A# i( T- r              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
) s3 |* o4 U! }( U8 x1 H              $name = $this->fields[$field]['name'];  " N: r/ d, K8 o* d
              $minlength = $this->fields[$field]['minlength'];  2 b# [2 _/ n7 k6 b" D9 A
              $maxlength = $this->fields[$field]['maxlength'];  ( V7 M( r- U  o" j9 r& b
              $pattern = $this->fields[$field]['pattern'];  
% z& o9 s& V" u. x' J$ s+ f              $errortips = $this->fields[$field]['errortips'];  ; J5 d% X9 b2 P5 z3 x- l
              if(empty($errortips)) $errortips = "$name 不符合要求!";  8 D# M* I# A" r* H- p  A
              $length = empty($value) ? 0 : strlen($value);  . E( o3 O8 f/ [
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
! J" b. q7 h3 V              if($maxlength && $length > $maxlength && !$isimport) {  
  l  w' _5 s2 B% ?                  showmessage("$name 不得超过 $maxlength 个字符!");  ! f- L" b4 K8 Y% s! X) q
              } else {  
5 d$ m# F. F" t4 F' B/ Y                  str_cut($value, $maxlength);  6 m) L8 x" r3 w. _/ ?2 E: @% I
              }  
. ~; i) n2 D& m# ?: {              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  9 ^1 {, ^8 f6 A& Z8 Z
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
8 ~& ?  g/ q  K1 A- h4 [              $func = $this->fields[$field]['formtype'];  
% J, {: [9 c' ~2 V              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
9 d7 |7 R0 w9 a' Z) G              $info[$field] = $value;  
: k1 |/ ~& L: [% X           }  ( l( p1 J0 Y  {* j
       }  : G7 [( \- @9 {
       return $info;  # }/ N3 N/ y) P% ]
    }
' m6 T% G3 m3 H6 ]4 B6 ptrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,5 n0 G  Y3 K  L8 L( v* |
, N8 C" B4 E' ]7 B- {8 P$ L4 v1 u- S" ^
再到phpcms\modules\member\index.php 文件account_manage_info函数
1 x* h  R  z  ^/ M过了get()函数之后。7 j9 Q- Z- @3 }

* k* W. _+ B9 b" w
& n! v' P4 ]. q$modelinfo = $member_input->get($_POST['info']);  
7 s5 G: n- n: G0 v; L           $this->db->set_model($this->memberinfo['modelid']);  
3 k1 X' C4 @  N8 ?$ p* `           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
- _) J) X7 ^! S' v9 x) @0 w           if(!empty($membermodelinfo)) {  6 m) G3 ^4 Q+ y/ M( a
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
  b5 J$ ~# X! C           } else { ! d/ k) K- w/ N8 e, g6 V
直接带入数据库,update函数我们跟进看看- l; [( U3 ~2 G0 h% F

0 H& X8 N8 {$ \ . g2 E: c& S6 M2 C2 z+ w8 b
public function update($data, $table, $where = '') {  2 {' L3 {+ {1 S: ]2 r; M; U' l
       if($table == '' or $where == '') {  
& _/ i3 ?6 `5 T) n( U' u5 I+ v) M  s           return false;  , `* M+ c9 X6 c# f6 S' _, M
       }  4 R$ o5 m: v: C' G' ~- L& J2 R
       $where = ' WHERE '.$where;  " D4 f0 }/ h" B& A( n. [' G
       $field = '';  & }  r" J/ f  ?* q+ F' n0 X
       if(is_string($data) && $data != '') {  
+ j6 V' S2 f  b3 s" U           $field = $data;  2 L+ H, i% V% P4 z/ ?; l+ C
       } elseif (is_array($data) && count($data) > 0) {  / {, z. a- v1 i- I/ e* d
           $fields = array();  : @" m2 ^6 l, P0 U8 T
           foreach($data as $k=>$v) {  " c( q$ \4 k) m) a+ r3 l6 r& @( Q" k
              switch (substr($v, 0, 2)) {  ( ~' c/ D2 I) _% y% m' ~
                  case '+=':  3 S& r( O( k8 v4 v) u
                     $v = substr($v,2);  
" B# l3 d+ p* R2 }1 z                     if (is_numeric($v)) {  # ^/ I1 s5 H3 k' W% j0 X' ?3 t! X" H
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  ; x% d/ e4 W1 G, R7 M- i0 l
                     } else {  ; j2 Q. x. ^* W" q
                         continue;  
( ?3 p% S, t; a6 P) a/ @! w8 M                     }  ! L7 ^4 f% o2 u. Y7 W0 }
                     break;  
- `) p9 S* ^' a& p: ^                  case '-=':  
" L* z# U& O1 R3 E) c2 Z. H                     $v = substr($v,2);  
% m" v3 |% d& t                     if (is_numeric($v)) {  
1 d, X; y# H% ~+ t3 E) ?& |6 {1 _                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
! X  k' I$ b; v. M# E# x' U                     } else {  
% [+ E. |: M$ _                         continue;  + S, z! [" Z# D7 T* K+ g& Z" W
                     }  " B0 Y/ U) Z8 r2 c2 `( i
                     break;  
7 f6 p1 R$ s3 p  q5 m% [& R0 I                  default:  
& Q5 z2 @5 w: d( ]  L                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
( z8 q/ e; r) n  m9 i, a              }  ' ]+ _/ {( ]9 m8 Q
           }  6 L# y! L# G0 ~
           $field = implode(',', $fields);  / p1 ^) z: X9 B
       } else {  
( B! m8 W, A1 d) ^1 [6 a5 d           return false;  
7 O5 Q! |2 D& l4 t  Y       }  0 S1 o$ O2 z. A  X' v. V2 ]
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
7 h$ f  q# l7 ~* n       print_r($sql);  6 C) a, n2 C8 u2 m6 a3 }
       return $this->execute($sql);  ; V7 ?, A3 \; Y. A6 [$ H
    } 2 H2 s" b7 `* Z4 ^3 a# l
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
8 E; ?' n2 d( }! g9 u! L
" ^! V7 n. H! d5 P$ a8 r攻击测试:
9 [$ t7 F" ^7 |5 ^1 W测试地址http://localhost
2 \5 U0 v6 @  r: C8 ?3 d. ]6 h  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句+ k: @# l0 W; [/ o9 Z* d
  F) b3 s. U9 O6 J+ }0 }
( [6 }( t) m* J

# `2 d* c" G1 P
' b' S7 h+ p. m
4 a+ m! B3 ^9 S; L) d7 K

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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