中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告# X2 O5 c' t$ [! E% W: b
漏洞作者:skysheep
% B* v% x, @5 Q% e/ b% Q, G1 q  n3 y分析作者:Seay: ?& A- T& U! w3 N+ s
博客:http://www.cnseay.com/
" G1 _: [, q9 ^: K& i漏洞分析:
& J+ i0 G. a$ ?5 t* G2 }  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。+ E- m3 F0 @: Y' R, g4 r+ ^
2 \# R1 H( ?* ^2 Y
; S/ v0 v; q. V9 j$ y8 Z4 p

/ c% J( L0 d$ ?* M6 Bpublic function account_manage_info() {  9 D7 }6 I" t9 B
       if(isset($_POST['dosubmit'])) {    l/ T) R' ~% v# {
           //更新用户昵称  
- G4 b/ o7 m! ^7 a" h0 y9 D7 l           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
/ g$ z! A9 N' K) g% p           if($nickname) {  
  a$ P& d1 c6 E+ Y4 Y' E4 V9 `              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  2 x9 l- @' F9 E0 V
              if(!isset($cookietime)) {  
4 \' m  A. \' K; t                  $get_cookietime = param::get_cookie('cookietime');  
# D8 e) _/ C# M. p3 K              }  7 q8 K8 Q* M  g3 ^9 B! S
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
/ O4 X, y, l- y2 S% e              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
, B7 `2 k9 w2 _              param::set_cookie('_nickname', $nickname, $cookietime);  
' b+ k/ B6 N, r" R, A           }  
: _. U$ H( i+ d# ~           require_once CACHE_MODEL_PATH.'member_input.class.php';  4 z# T8 I/ b0 q  f8 X) |
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
/ y6 ~" i2 K# i9 `' l           $member_input = new member_input($this->memberinfo['modelid']);  * X/ n  t/ O/ r' R9 t
           $modelinfo = $member_input->get($_POST['info']);  
/ g* @0 H: r# {+ x           $this->db->set_model($this->memberinfo['modelid']);  
9 U# _1 h$ [- V& X           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
9 y( i" o' _7 x4 o! S1 Q4 O5 J4 ~           if(!empty($membermodelinfo)) {  
' L1 b7 n- p7 A- C3 P! c              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
1 x; T2 d" b9 O; ^* Z           } else {  
7 g4 z# E! N' n3 \  T              $modelinfo['userid'] = $this->memberinfo['userid'];  & t4 N$ `% d; u8 w' B1 Z
              $this->db->insert($modelinfo);  2 S" S9 J7 |2 p
           }
0 M. T0 z- P) \! ?2 ~代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
  U" W/ Y( o+ A1 N+ ?在\caches\caches_model\caches_data\ member_input.class.php 文件中:$ D: g% g' n: g- Z/ }6 G* \9 f
3 I: a0 I# J, {- C

' t9 Y9 i  \, m! H, f+ F. M& p
6 p: K; U7 l* afunction get($data) {  
4 U3 T! T: ^- o  p# v       $this->data = $data = trim_script($data);  
0 g; b1 p: ~( E' l9 U& g9 J6 ]3 P       $model_cache = getcache('member_model', 'commons');  ; H) I( X8 h2 n. e
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  7 o% \2 _2 _7 Z/ U3 R6 u
       $info = array();  
1 O$ g9 @( j) k       $debar_filed = array('catid','title','style','thumb','status','islink','description');  3 s9 I  A# |- e) L6 n
       if(is_array($data)) {  3 q* Q: J# X2 j& x" [' O. ]& Z
           foreach($data as $field=>$value) {  
) ]" C9 G- Q. [$ P) p; n              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
+ I6 v: `6 K7 e2 O. O              $name = $this->fields[$field]['name'];  
7 p7 t8 u: p! n% k. i2 P% x              $minlength = $this->fields[$field]['minlength'];  
6 d: L2 \! t- d/ ]' W9 k              $maxlength = $this->fields[$field]['maxlength'];  
  P; w: j1 l# p7 Y. h: w              $pattern = $this->fields[$field]['pattern'];  . g! T( C* _3 C# q( q& x1 Z
              $errortips = $this->fields[$field]['errortips'];  
0 t" ^( i3 h" V              if(empty($errortips)) $errortips = "$name 不符合要求!";  
5 @* E7 w: F8 V  D8 B( K              $length = empty($value) ? 0 : strlen($value);  ! d  p" {7 H% o1 X3 i
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  - o' R0 g$ p! I5 `4 M
              if($maxlength && $length > $maxlength && !$isimport) {  ! [, ?- |; K# V
                  showmessage("$name 不得超过 $maxlength 个字符!");  3 e# H# t. `7 B" {) s
              } else {  
5 S$ _% y! j" v; a  Y7 v                  str_cut($value, $maxlength);  ; i  ]; d" u0 Q" d
              }  7 P) K% p5 l/ _0 u$ I, y8 C
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
3 x9 j, ^. h. F" i                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
2 _% @$ y7 H7 W4 A9 R0 p              $func = $this->fields[$field]['formtype'];  
5 {& V. a. A4 @5 |              if(method_exists($this, $func)) $value = $this->$func($field, $value);  5 b+ y. f% Z! I6 p& w# b8 j  N
              $info[$field] = $value;  / r9 e0 U9 \+ U5 O! g+ y
           }  
4 W6 R. P$ p7 f  ~; P       }  
+ o: F# O- x  c4 ~) j' m& I       return $info;  , Z2 F  h: g9 m5 G' A* H- @  f
    } . J. t1 s  C8 k$ L7 s
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
) y& @8 ?- ~3 U* V4 E! c8 r- ^5 b& P: c; |) c9 i! x, @: y
再到phpcms\modules\member\index.php 文件account_manage_info函数3 Q' w$ B: d  n. S
过了get()函数之后。) ]7 f, z2 z+ L2 u2 d) D$ L

; W0 b, Y  |! F
5 c7 r4 @2 l5 F8 r; [  j1 V$modelinfo = $member_input->get($_POST['info']);  + S' y) ]2 r- r. J+ B
           $this->db->set_model($this->memberinfo['modelid']);  1 p1 ~- i" b% `7 f2 k
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));    F% c" k. D: j0 Y. f2 M1 q; q
           if(!empty($membermodelinfo)) {  2 q& B+ o& T$ i$ A
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  1 Q7 U8 d$ @! O' Y7 O- x9 c) l' v
           } else { * O. @5 ]. ]' {8 B3 U8 k
直接带入数据库,update函数我们跟进看看
8 O, a% P8 @- ^1 j$ L1 J% ^
8 c9 z; I) F+ H# @
! h- d0 L* _8 w, P9 q' wpublic function update($data, $table, $where = '') {  . \( I" b- w8 r* G  ~; t
       if($table == '' or $where == '') {    ^0 T/ g8 J) I  m% q6 \3 a
           return false;  
# A5 k$ M( c2 }- S6 v9 s       }  ! }/ [5 L3 [2 Y4 ~% r! X
       $where = ' WHERE '.$where;  " s' Z9 r" f8 S
       $field = '';  * }6 Q8 w8 F, X
       if(is_string($data) && $data != '') {  
, ?5 J" c* g% D           $field = $data;  ) S0 A6 b0 y2 [& ?9 {9 A: E* `
       } elseif (is_array($data) && count($data) > 0) {  
" g; V0 g4 a9 a. m1 q           $fields = array();  $ s0 t8 T  `7 c+ v5 x9 j6 m1 V/ ?
           foreach($data as $k=>$v) {  : G0 L3 W2 C2 q
              switch (substr($v, 0, 2)) {  
& n) W* y( Y+ x: C9 X                  case '+=':  . I  g0 q6 i# y+ ?# q2 K5 x
                     $v = substr($v,2);  
. Z& ~6 _- g  }& ]* y2 g1 ^                     if (is_numeric($v)) {  - A6 T, d4 w! z9 b, x! H# s0 @
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  $ V/ e  p6 M6 m8 D: t3 L
                     } else {    z/ {" U, x+ l, [6 E/ i( z! L
                         continue;  ( S8 M0 F5 y0 z
                     }  9 v8 d" r2 }* @+ I4 E& Q& h3 }6 N
                     break;  9 D5 H8 z( M) f# N2 ?! Y
                  case '-=':  : K& y, N  }# u  K, I& ^
                     $v = substr($v,2);  9 l/ m. Q  Y7 s+ g) G$ |$ A
                     if (is_numeric($v)) {  
- t; q1 ^" U; s5 w, R                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
9 z( V4 D1 I' z. m- B9 `! r                     } else {  
0 a' h2 y, o6 W4 u                         continue;  
  }$ v: d* `' i* W* c8 C1 m9 G                     }  
, e. n" Q4 k5 h6 Y7 ?  H' C1 R6 S                     break;  
* z1 ]. q  H9 w: u+ w  D4 J' y                  default:  
8 ?$ i; ~7 }! \) y* q( Q                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
# N3 A: a0 M2 F: u% A0 n4 N1 J# a              }  3 \3 r% h! r& m( y' r
           }  0 Q; ?' f* B3 T7 s" w7 B
           $field = implode(',', $fields);  9 V, V3 Y* u: s( [7 j
       } else {  - D! Q0 ?2 l& Q: a7 m9 v
           return false;  # p3 w) R7 r4 s4 u. Y
       }  
! l# w, |/ c' y, S7 A5 b6 F5 o       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
- Y6 b: y1 ~) `0 [$ J# G8 a# z       print_r($sql);  ; s  X5 |  Q* j2 i7 ]
       return $this->execute($sql);  8 W3 ]2 g/ Q; K3 V9 ~
    }
9 a. t# n& ~6 |' Y2 X* [$ y从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
3 v' ]. b3 g  b
% W' P+ x5 d% O6 e: v# I1 g) |攻击测试:
( G/ \  d5 b" U& W  [7 a. Z) b测试地址http://localhost8 F5 `9 V5 ^6 l8 F6 j2 O
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句5 U. d) J0 h3 D( m3 ^
* w& Z1 y: c. _$ L: \( p
[attach]179[/attach] : T% N3 B+ s8 `  n' Y

! s9 ^* J& K1 @4 F0 @! b
. i9 W1 k4 c6 @5 t3 O" D[attach]180[/attach]
# |0 a5 M. i8 j: G. T




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