中国网络渗透测试联盟

标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告 [打印本页]

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告. \) d2 W  T3 f. n7 F
漏洞作者:skysheep
0 d, _' n5 T" p+ [/ G- ?分析作者:Seay
! K- C1 m. H' z博客:http://www.cnseay.com/9 b) {5 J  ]/ d( k! S9 `0 w+ g0 c, C5 V* [
漏洞分析:
: u7 w* z, w  l0 ?  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
& f  t2 O) U  L" D: J/ e* o1 N- k9 q+ ~) O4 }1 n7 z
- S5 m. w$ z: ~1 f7 R2 H
- |5 N( y& c. f) I1 U- S
public function account_manage_info() {  . G# T$ H5 b9 w( _& w: N# `- y! t
       if(isset($_POST['dosubmit'])) {  1 \  z: T5 K, Z+ U% L
           //更新用户昵称  
, V' U4 S7 }5 ]$ p* S4 u" C2 U           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
8 e7 I3 e; P7 U& c. v7 A* t           if($nickname) {  ; N  |) j3 y. O$ M) R3 e
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
- B6 p/ Z9 H4 I+ L) }              if(!isset($cookietime)) {  " s+ q* r4 p# `# @: v: `
                  $get_cookietime = param::get_cookie('cookietime');    P0 f0 h  g' d8 ~
              }  : W" K0 L  h6 E' B3 u/ k
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
! B- {' p" s6 c! C6 H              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
" ^6 N; U0 h, z8 y: @# B              param::set_cookie('_nickname', $nickname, $cookietime);  . c; g* C1 c; P& v
           }  
5 e4 I( c9 X, T, y; w" l7 e           require_once CACHE_MODEL_PATH.'member_input.class.php';  
' f5 j& R* X0 q           require_once CACHE_MODEL_PATH.'member_update.class.php';  
' y+ E9 d0 \3 C+ L           $member_input = new member_input($this->memberinfo['modelid']);  5 T$ T" g0 L$ {6 U- O: q" K" M
           $modelinfo = $member_input->get($_POST['info']);  ' A. t6 O$ H& \
           $this->db->set_model($this->memberinfo['modelid']);  0 J* k5 r% Q2 r7 D, j3 r( A5 e
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
- [5 a2 y3 j  x9 L0 U           if(!empty($membermodelinfo)) {  . j# T: \- f& u# J+ Z; g2 j( K" @
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  9 Z; g' e4 G& Z& H5 j3 p0 h; _
           } else {  
0 D2 [8 e0 k( \' T              $modelinfo['userid'] = $this->memberinfo['userid'];  8 v& b( H( `: `
              $this->db->insert($modelinfo);  
+ p4 Y$ V+ P4 x  U! A0 C+ q# I# P           }
' f  [" L  M/ h7 X7 X- O代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,6 }* I  ?  ~; h$ O3 M
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
& q9 g; b5 W7 t  z! S, m" h9 g- x* B1 b1 a6 C. A3 L. {/ u. Q' y

+ t. w' j( J0 S/ S 7 q' q/ v4 O# f6 w7 n
function get($data) {  3 i+ N6 b  I- l- a: ?) R' _$ n& t
       $this->data = $data = trim_script($data);  
- f: |9 x# `1 V0 S       $model_cache = getcache('member_model', 'commons');  
  ?! W/ r/ O- ^% X" K  e$ H! _1 `       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
: s' Z$ V: h3 q9 Z% s9 `+ f) {' o' C! V       $info = array();  
" G) A. Z: _" o# M: v' J2 `( {4 n+ J       $debar_filed = array('catid','title','style','thumb','status','islink','description');    A- z( R0 f  i! U& X. v
       if(is_array($data)) {  ) [  V% y: t- z# b& O0 w2 O" B+ ?
           foreach($data as $field=>$value) {  
! I& ^0 b6 U- q8 F9 S7 M              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  , D, [  o9 j8 K9 Q5 }
              $name = $this->fields[$field]['name'];  
, x$ x  ?& H6 L5 K. x              $minlength = $this->fields[$field]['minlength'];  
4 }, ?; R+ D0 B. V- o              $maxlength = $this->fields[$field]['maxlength'];  
9 z% H  ?* ]1 B3 l8 s              $pattern = $this->fields[$field]['pattern'];  
' P$ R; `, m/ ^" R6 |7 w              $errortips = $this->fields[$field]['errortips'];  ' h( c0 i- @: p$ ^" j0 L
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
; h# }7 l7 G% g$ h              $length = empty($value) ? 0 : strlen($value);  / X, ?0 I# q5 W" {& n$ }5 B' c
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
% o2 g7 k2 k! b' K& [2 y; U              if($maxlength && $length > $maxlength && !$isimport) {  2 s* P3 \$ o: L
                  showmessage("$name 不得超过 $maxlength 个字符!");  
$ i8 A  @' z# x              } else {  
; g0 Y7 m- _8 A$ _. C                  str_cut($value, $maxlength);  0 |% f, w1 H2 C
              }  
0 _% y# o: }1 F4 Y6 a% f# |8 V              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
2 T8 y( a! ^" I0 _! B                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  ( g" f2 S' s8 c
              $func = $this->fields[$field]['formtype'];  5 o5 c% W7 L% v. B! T7 H+ i
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
3 i3 o5 ]6 @( c( t              $info[$field] = $value;  
; k5 Z7 f6 A, A           }  5 T9 h9 @5 ~" |1 L0 P
       }  
0 Q3 C  I, i* z4 }) T7 Q; y       return $info;  
5 ?1 t/ E# e  O) n4 F) h* w    }
7 f* ?& ~  }  I. n" c4 t2 ptrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
, q0 F! E4 ^1 v( K
# ^& l  }. I. e+ V4 n再到phpcms\modules\member\index.php 文件account_manage_info函数+ |. C4 U) U3 x& V/ c9 Y; _
过了get()函数之后。
5 A0 v$ I% k) K2 B1 v
# H8 B8 U! Z! Y; E
! |2 k* H' b! c* r* c* d$modelinfo = $member_input->get($_POST['info']);  
; k4 w' A6 o3 Z! O' [# T$ ]  X           $this->db->set_model($this->memberinfo['modelid']);  2 `) J# M8 w  z! U7 @: T# W
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
+ e6 F/ \1 }' X           if(!empty($membermodelinfo)) {  
8 v( U  |8 f6 g, K: e              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  7 b) \5 z9 d: J! I# C
           } else {   \* y5 |( u. D. ]4 J. B) d
直接带入数据库,update函数我们跟进看看
: j& {3 t  c* n* g1 ^5 M; T3 Q, N7 f
, u0 U5 N1 J. v8 A* _
1 y  n' k1 L$ d% Wpublic function update($data, $table, $where = '') {  - t9 Q/ P2 `: \2 h3 g
       if($table == '' or $where == '') {  2 H9 S" j6 ?6 R$ x; ]
           return false;  
( g  I6 I& e" h& `: U7 ]       }  
  \3 b0 R% e$ `! A4 R" Z       $where = ' WHERE '.$where;  
6 o# S7 ]7 N0 b; ?" @& [( J       $field = '';  
+ F" w  ?% h4 n9 I  w# v       if(is_string($data) && $data != '') {  9 s6 U3 m- I" N6 J- y5 [0 a3 w' w
           $field = $data;  ( n4 w/ P! Y) N
       } elseif (is_array($data) && count($data) > 0) {  + f. a9 k% O" k8 C/ H% c
           $fields = array();  
( J2 S# {( M- K3 C) t9 S, l           foreach($data as $k=>$v) {  2 Q% q( Y' r5 i9 j/ W  a
              switch (substr($v, 0, 2)) {  
! z; S. e/ `6 t( {. L% |                  case '+=':  / K. I; n2 q, l$ |
                     $v = substr($v,2);  
6 T+ Y" v% g$ m+ v8 k                     if (is_numeric($v)) {  
4 c- |* x$ b, d0 ?4 [                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  2 b+ _3 N$ s+ o+ [) ~' _
                     } else {  
0 Q" l  V4 j6 Z3 J# `( ]                         continue;  
: ?3 W; G4 r& Z# h. @2 ^/ ]" w                     }  " S' z: q( p! ?
                     break;  % E1 |9 H6 |% d) H0 F8 q( `
                  case '-=':  8 k# m8 Z" v% D8 c$ Q' M* W" W2 J
                     $v = substr($v,2);  6 P3 E- X8 R: L/ k) N
                     if (is_numeric($v)) {  - g7 O& f1 Z9 m' S3 s
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  1 {0 a- S9 J' j" g0 p& D8 C
                     } else {  
$ k% x0 H, ?: w% J                         continue;  
# j8 A5 u) q1 B1 j                     }  ( [, z9 x3 o" A$ e! Z# |7 J
                     break;  ' M6 K4 |$ r) e
                  default:  
0 W" E: M9 o3 K0 J                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
- y+ g) h  h: F              }  " f% P0 t1 s) i: u
           }  & a0 I# a# x& u+ L# J
           $field = implode(',', $fields);  
9 T7 l" g; p7 n1 S& O2 D$ `       } else {  ! o9 y/ t( a! ~3 q
           return false;  
- m2 j* e+ T2 {8 H$ Q/ F6 s" p6 S       }  
4 @4 I& ]' L" B5 z       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
( u6 T$ ?: B6 S8 k$ v3 s( c       print_r($sql);  / P/ E2 u2 B3 Z1 k1 G
       return $this->execute($sql);  4 i( H7 J/ Q3 o
    }
/ Y8 ?$ `3 Z/ @. |% D从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。" Z( @- O( d' K4 K2 B' }6 i8 f
/ @3 H# P! V, V  w- G
攻击测试:- B3 m- P( K$ _
测试地址http://localhost  x3 L5 g  l% [' _  q/ {
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句9 g5 N/ g% n  t! m6 K

# x! k" S( S: o) @[attach]179[/attach] : A# ?8 v7 J) J
# \, G" a8 L7 L( j& l$ w* c
: K6 z" B- D7 E3 H+ c
[attach]180[/attach]) v' s5 j0 J9 G4 t6 A4 I





欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/) Powered by Discuz! X3.2