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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
0 l+ T5 F& [0 W9 e漏洞作者:skysheep. a- u+ I2 N" u5 t9 w- W
分析作者:Seay. ?6 ^: b! W- x& {0 ?
博客:http://www.cnseay.com/9 Q% F+ h6 v/ c$ D
漏洞分析:
# g  P/ v0 J6 |! a0 X" D  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
) p2 X- R! Y' S
* R  ~  J8 y8 D
5 M: |6 M8 W/ ~0 u) X
2 G! ]2 ^) z: ], ?9 spublic function account_manage_info() {  
2 J5 _. x5 Y% X3 n5 l* G7 N7 P6 ]       if(isset($_POST['dosubmit'])) {  
4 ], ]% Z8 h7 V" @: `           //更新用户昵称  
6 R* g* i4 b2 n           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
" q5 r1 F$ S) j4 G6 Z           if($nickname) {  
) s3 j* H9 Z8 b4 l* Z' m9 s              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
& _! l# j3 v0 B6 A' v9 X              if(!isset($cookietime)) {  - n1 d6 X& I6 s2 U' |0 x0 g
                  $get_cookietime = param::get_cookie('cookietime');  , t4 R+ r$ }- G% _  }
              }  
# {- V6 B: X* ^- Q* ?              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  $ O% w6 d6 Y: O
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
. |& x7 u5 O1 M0 Q! }6 N              param::set_cookie('_nickname', $nickname, $cookietime);  
( @) l1 A) H7 f. J6 Y7 ]! T' q$ O           }  
9 ]3 e  y. q* R+ e. d           require_once CACHE_MODEL_PATH.'member_input.class.php';  
8 H$ R% ]' E* T( Y" v2 _           require_once CACHE_MODEL_PATH.'member_update.class.php';  ) W' `- m2 N! w* t# e
           $member_input = new member_input($this->memberinfo['modelid']);  
- s9 |$ b5 I+ N9 v- J           $modelinfo = $member_input->get($_POST['info']);  - H2 I8 X, p* V! u" a6 Q2 O$ J7 [
           $this->db->set_model($this->memberinfo['modelid']);  
: A- T; I* F3 W* A/ e" P% I           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  9 m1 @; R1 x/ A2 y
           if(!empty($membermodelinfo)) {  $ M4 b% x" \% H' X/ X6 v! p8 S
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
/ v" ~# T9 N4 x5 Q" u           } else {  , E; L+ C( z  N
              $modelinfo['userid'] = $this->memberinfo['userid'];  3 S5 {* m/ q4 y" c
              $this->db->insert($modelinfo);  4 |, L# W' W% {2 \5 S" Y- U& G
           }
1 C$ i! C6 K9 o代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
! n' }5 w! n+ `( a: X9 t在\caches\caches_model\caches_data\ member_input.class.php 文件中:+ r! N2 G: ^6 j! H) H

4 Y0 {7 }8 H: c* w- H; {/ c4 S! W$ s* y  c; H
2 t; l  n5 V  ^# Z
function get($data) {  # L8 N. n7 d( Z- M* c8 F. |$ B
       $this->data = $data = trim_script($data);  ( J7 H5 Z: l* D8 c: {: j
       $model_cache = getcache('member_model', 'commons');  5 {" a% m$ w/ I1 z1 Z' ~% x
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
& |" ?+ e( A6 b6 Y3 O       $info = array();  ; f! j! O+ r7 ?$ g
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
" q# a2 r! ?4 u: F0 i! }4 d       if(is_array($data)) {  
( C# z  o2 v! [3 ?; Z: k           foreach($data as $field=>$value) {  
$ m  `4 y: L" j              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  3 o8 Q7 _) l4 k2 p# `  H
              $name = $this->fields[$field]['name'];  
- e5 z1 [$ k  N8 ^7 j- [" Y- m' \1 a              $minlength = $this->fields[$field]['minlength'];  ' }# W4 }; c. E$ ^( e
              $maxlength = $this->fields[$field]['maxlength'];  ' `1 J  k2 U& b2 p- O* h
              $pattern = $this->fields[$field]['pattern'];  
, c% I4 p1 u' \              $errortips = $this->fields[$field]['errortips'];  
. Y% d; ~5 O+ G, r  ?              if(empty($errortips)) $errortips = "$name 不符合要求!";  6 |) n* h( j  H: Y# \9 ~' H
              $length = empty($value) ? 0 : strlen($value);  
- j/ t" K0 S4 e: e$ r0 q' Y              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
# N* \$ v- Y3 C9 W, z, q$ [+ ]# V7 t              if($maxlength && $length > $maxlength && !$isimport) {  
% L  H5 U$ A- ~                  showmessage("$name 不得超过 $maxlength 个字符!");  
5 E/ N+ w4 i) W+ G              } else {    c. i3 |/ q' ^# U
                  str_cut($value, $maxlength);  
8 n+ Z, y6 D- m- F8 K8 G  ?7 }' ~              }  
- y, z: E/ o- Q* r0 w! \              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  ( R+ e$ ^5 K6 l5 ~& h3 J. w% S
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  $ d! t2 ~8 L% e4 t; x' s
              $func = $this->fields[$field]['formtype'];  
% l- `$ s& J! `9 J* g7 F              if(method_exists($this, $func)) $value = $this->$func($field, $value);  % y/ _7 O1 k7 e( U2 W' i
              $info[$field] = $value;  3 Y7 j1 b5 ]# K; U  U* ~4 e
           }  
6 K' L+ V( l: o5 A3 Y       }  2 d7 ^4 i* _! B* V
       return $info;  ) ]3 q$ i1 @' N4 F( D5 q7 Q
    }
" f8 q5 \- A. F2 Strim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,. Z4 o) _: Q2 G& T+ k' z* V1 Y

5 \' P2 t1 l8 u: t再到phpcms\modules\member\index.php 文件account_manage_info函数4 q: n9 W0 ~5 Q% k. c4 o
过了get()函数之后。- Y: `, N* \# t3 T0 j: w+ E2 ]) f

! H* g6 U$ H+ b0 x+ |8 z! g2 i) S
/ X. v  h8 k" Z' n% W0 Q0 ~# l$modelinfo = $member_input->get($_POST['info']);  3 _% o% t+ {* r; F/ J& R: q
           $this->db->set_model($this->memberinfo['modelid']);  
( u* n6 B4 E( e4 V+ A& L6 C5 u           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
6 U2 s- T8 D/ ]2 b. W/ l, r- k           if(!empty($membermodelinfo)) {  6 O: k0 }5 e% K
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ! b, |" Y' G4 p. l/ J
           } else {   g# X6 U7 q' \6 {# W" ^1 v
直接带入数据库,update函数我们跟进看看
* J) S  I9 q7 j1 Y2 j5 G, [( D: N* B5 P/ H

. v; W2 Q# A, P+ F, ^+ b( Q; @) ^+ @public function update($data, $table, $where = '') {  ) S/ C% ^" q2 U4 ]7 y4 `
       if($table == '' or $where == '') {  
- n+ X9 n0 a. Y( [9 T! Z, _2 o           return false;  $ v1 Z4 Y, ~- b- u
       }  ! L. }1 N" _$ |5 ?
       $where = ' WHERE '.$where;  ' [0 X1 n# O( K; u) m1 l1 t: k
       $field = '';    A. f$ L: }; ^# m$ c' T
       if(is_string($data) && $data != '') {  3 f8 D( C) L( G3 E2 m7 W
           $field = $data;  
- Y) X1 {) V" ?9 g+ A) x' u* V       } elseif (is_array($data) && count($data) > 0) {  
7 r7 Z' s$ x7 P5 p3 `           $fields = array();  
1 V: F+ t$ O5 I1 V           foreach($data as $k=>$v) {  
7 [$ l0 i2 t4 T& D3 l! [7 @              switch (substr($v, 0, 2)) {  
1 ~/ Y- X7 l  n$ Y; G# _) T                  case '+=':  
1 P8 r0 X" {, b' K- b! m; _                     $v = substr($v,2);  - U" P# E' k9 ^  \' x
                     if (is_numeric($v)) {  
" J6 N* B# B8 G; u) ]* {5 C                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  & N# [+ n5 f# k) @' q
                     } else {  3 O9 A) l( S  R' k
                         continue;  0 ?4 U7 @% W' S. A, Z2 q4 ], u& `
                     }  
/ A" t; {) j0 ^5 T, L/ v8 U9 `+ z                     break;  
/ A. s' C: C$ Q6 w1 j' j2 [/ \6 C                  case '-=':  + D7 O) U/ Q3 k3 c
                     $v = substr($v,2);  
' C& [% Z! H6 L                     if (is_numeric($v)) {  0 x4 b9 s: S3 v& M
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
! o6 J4 C! `; R( ]+ B                     } else {  
# ?$ W& t( C( l( t2 d0 Y                         continue;  
4 x7 v8 E% U4 e- [                     }  
# `' G. a2 W; R                     break;  
+ f/ Z# f1 t5 e( |/ p& ]4 a- g% ~' `                  default:  
  g$ I' V3 \: W  k1 ~                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
: J3 N4 }$ C* X: {. ~              }  1 T( U9 _/ H5 E# @+ F; D  b" s; |
           }  
8 z2 c  K; }$ E           $field = implode(',', $fields);  
& j$ \$ ~. U$ k+ S& M* g5 d, G       } else {  
5 V; M9 a6 k0 r; J7 D0 v, r           return false;  ( e, i+ g. g5 z* L, b& f1 M
       }  0 ?4 m: B8 `2 P9 _
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  ; u" F1 @$ |% C1 _$ q6 V
       print_r($sql);  ! r5 @; t; J8 n2 I: H% n- q
       return $this->execute($sql);  4 J/ ~( Y6 _. s3 K5 t
    } # [) ?* u1 u* v1 ?+ `9 F
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
. o, O# s* B2 o/ ^8 H! n9 ?( h3 s& B5 ~6 ~6 n, @3 M
攻击测试:
! a$ i* e8 J. i' U4 V, l测试地址http://localhost6 Q1 |1 U0 X  j# _' x
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句! F! Y* W# o3 L4 I! k2 d7 [
! c( y9 A  j) t1 @( w; i9 H% K9 n
  h/ O. i( E' z' _
, l/ x/ m# F4 k' g! o
0 V; h, L1 o- [, q

5 Z" R3 G3 G/ ?# d. _- a' P! K( v

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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