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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
% S: z3 ?+ s8 R) e漏洞作者:skysheep6 Y' u: @2 K( ?1 t
分析作者:Seay
* a+ E: ~1 n5 A0 D3 A" {' j+ f- m博客:http://www.cnseay.com/; m8 y' e  J( V
漏洞分析:& U7 s% G9 n" O/ B) U0 d3 A% ?- G
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
' [9 y% n0 U( g9 g9 G2 U; f& m) }5 F

: n: r8 y  ?  E, W4 f9 G
. A7 e7 ^- `& R7 ~9 l" k- lpublic function account_manage_info() {  
% p, s. G1 u& H3 k9 @  i  X       if(isset($_POST['dosubmit'])) {  
* F1 r' r9 |8 X) y) h- |3 c           //更新用户昵称  7 M. u( h5 b" a2 Q0 W; |
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
: N, A3 ~0 H) T8 `7 m$ _, ~           if($nickname) {  
4 T! I% g; V% S, K4 ?              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
) V. z! J! ^  ?              if(!isset($cookietime)) {  4 K1 C3 `, c( F+ ?0 q4 Y
                  $get_cookietime = param::get_cookie('cookietime');  ) ]9 z. L0 {, I" `+ P- U" F
              }  
* H2 Z: d6 H, n# z9 g              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
6 C" G% H8 ~# v% s' W. G8 }              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
4 u' M+ |, C* k) s- B; c              param::set_cookie('_nickname', $nickname, $cookietime);  % c; q% q6 E/ w: r* L+ n6 b) \
           }  
! z1 x5 g3 K2 O$ w" P" f5 N           require_once CACHE_MODEL_PATH.'member_input.class.php';  % ^# B0 k' r! j2 t
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
1 A, d- ^! N$ c0 S           $member_input = new member_input($this->memberinfo['modelid']);  
2 {: b) a8 M/ Y, P3 f! `           $modelinfo = $member_input->get($_POST['info']);  4 _/ L. a% Y( b- h6 a
           $this->db->set_model($this->memberinfo['modelid']);  ' Z" Q7 @- t2 T% V
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
& l% y6 C  I. |  |! o- ?           if(!empty($membermodelinfo)) {  ! J: h: K4 _, I2 B, m3 o. \
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
' p  N# @* q6 p           } else {  + t* H+ o0 u( Q9 D: [
              $modelinfo['userid'] = $this->memberinfo['userid'];  
" f6 t" {) Y. P. g              $this->db->insert($modelinfo);  
9 \9 @" m8 E' v2 `- o& |3 J, Z. B: a* j           } + v: i) G0 k. U: a3 y" m, D5 o+ B# f% x
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
5 J( K/ Q6 r0 x( Y7 b! h5 U在\caches\caches_model\caches_data\ member_input.class.php 文件中:' M" N, c! {: ?0 V/ U% k
0 o( h. `7 F* m% N8 Z4 ~7 _
( b; x5 B2 Q7 G7 B# h
% s* g8 B$ o* t8 O5 v
function get($data) {  # m. `3 }1 T: z* K
       $this->data = $data = trim_script($data);  
* a) v$ l8 _, f  ^" q8 T       $model_cache = getcache('member_model', 'commons');  
1 a) c; ?) r6 w: M8 N) ^; ~       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  & H% W! B; \/ M3 X+ e' M8 D5 a
       $info = array();  
% d7 x( k! R7 D. _# u. _  B       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
* a- V$ @9 f, G       if(is_array($data)) {  7 ~1 y0 \% `- ^, F) e
           foreach($data as $field=>$value) {  # ?4 N  {9 [' j9 v- N
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
2 F* S7 r8 ~; u$ q              $name = $this->fields[$field]['name'];  
7 ?6 p2 }' H# ^5 ~              $minlength = $this->fields[$field]['minlength'];  ; o" ?6 d) o- y5 m. w
              $maxlength = $this->fields[$field]['maxlength'];  " u  a8 ]3 B2 U$ W" [
              $pattern = $this->fields[$field]['pattern'];  & Z( f! J: {0 k- F
              $errortips = $this->fields[$field]['errortips'];  * o- N& @; m3 G* r) g+ P4 X
              if(empty($errortips)) $errortips = "$name 不符合要求!";  7 D! m3 Z/ j( N; `7 x
              $length = empty($value) ? 0 : strlen($value);  " }' Y* X& l- M' u) ?
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
  T% B5 f3 ^5 Q4 o              if($maxlength && $length > $maxlength && !$isimport) {  4 ~* H, Q, |  H0 A: W, F; U' y( U
                  showmessage("$name 不得超过 $maxlength 个字符!");  2 U! j! e7 g2 R& r
              } else {  
" |- l) p! t7 [                  str_cut($value, $maxlength);  
: y' ?& I0 q, q  o3 B% H              }  + W; j: I$ a* x% u9 j0 {
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
2 I1 x4 @8 C# O3 @' T                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
2 [5 V/ k2 p' P. O5 e% i              $func = $this->fields[$field]['formtype'];  6 E8 O& f  L! i" n! h
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  # V6 z) B9 ^0 d& X0 E" I! F  @3 }
              $info[$field] = $value;  
: n9 Z3 M! T' m) Z; d1 `/ ]           }  
" y( k5 D; m2 U0 c  ?2 D4 S" z+ A       }  # `3 N! g( o1 |: l% |
       return $info;  ( [/ F% ^) _5 r% c
    }
! P% M1 t9 X; |6 _$ i# E; _6 Htrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
% o6 a4 q# M: g* J
) {$ o7 F& F3 x- l- F; E再到phpcms\modules\member\index.php 文件account_manage_info函数( k, w" s. S* d  h# I
过了get()函数之后。
8 y! x* M3 [0 `0 B$ |6 w, a! {* s
. W6 u$ m4 c' S8 U- i. E
- A* M. d( k& Y( [8 a$modelinfo = $member_input->get($_POST['info']);  1 H3 M5 _4 T7 k* A9 _! q
           $this->db->set_model($this->memberinfo['modelid']);  
& k4 M- z3 D" [; j; t           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  7 m- W/ Z" N) C; _
           if(!empty($membermodelinfo)) {  
; h2 H# o1 @# P- _3 y& Y; Y              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
9 X+ ]1 W+ d8 f% K7 `5 D           } else { 3 B9 I3 B+ Q- x  p
直接带入数据库,update函数我们跟进看看/ i/ b; ~/ x, ^+ P# |' ]4 `

# K: k7 d8 Y* F1 i, [5 \% B - O, f% H2 v& d$ W7 p
public function update($data, $table, $where = '') {  $ P. a$ u" L  P4 v8 x
       if($table == '' or $where == '') {  
2 ^' S0 G: h; o7 Z" B. F4 ]: n' C           return false;  8 i% f" u- N! @; `3 P$ M+ @3 Q
       }  
' _( c2 _/ J8 \, z/ b       $where = ' WHERE '.$where;  : R# {: ]' o: d: R
       $field = '';  
$ I5 i8 ~' |) g- b3 I$ m, C       if(is_string($data) && $data != '') {  - J- y2 B+ r. v1 K* |+ e
           $field = $data;  $ N2 \9 Q" Z$ |
       } elseif (is_array($data) && count($data) > 0) {  
- |* c) Q" [0 |7 b8 ~, z1 J           $fields = array();  
7 i3 N. X! A4 \3 p           foreach($data as $k=>$v) {  
( e4 z/ u  X0 P. Q( W, k) z+ g0 Y2 y              switch (substr($v, 0, 2)) {  
6 L. W& y2 X4 V* j% R' D                  case '+=':  
- F& Z0 C( M; J* |                     $v = substr($v,2);  
+ y$ m1 |+ {3 J( {+ T                     if (is_numeric($v)) {  & o6 v0 u* d, l# g* T  o
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
4 p$ f; S6 D* @' ~( S. }                     } else {  
2 o* d2 `" N. u2 f- ^3 a                         continue;  
" ~$ \6 J- K5 \, A. _                     }  
! X/ c$ _9 I0 f( Z                     break;  ; Q# T5 i. Z2 V$ r8 A, D+ B
                  case '-=':  
8 r0 A$ k: s1 [+ L                     $v = substr($v,2);  ; m' e: r( t$ T
                     if (is_numeric($v)) {  
1 ~: s2 r1 f/ O5 B1 P9 Q                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
! Q# ^( Q9 q& s4 N                     } else {  $ Q+ E, P' S( ^' L2 u! X
                         continue;  
9 n) Q- }: S6 Q                     }  
! \7 c2 ?2 Q* T1 @3 C                     break;  
) t4 h- ]" {6 c( U                  default:  : |7 t. i, N5 F) q5 i0 n+ i
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
& m2 J& \1 y5 q; t7 ^) P! D              }  
( _1 L7 o8 \1 F  n- _           }  
) S( Y0 [  n  F3 J* O           $field = implode(',', $fields);  0 r, f- A) H, a- O5 Y/ j9 {5 G4 C
       } else {  - @8 s' b- x7 i$ |0 y3 n& H7 K
           return false;  
* }* n( U. x+ Z. r       }  / f! O6 {! t7 K$ d7 F; ~9 H
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
6 f5 a* }5 O- T9 n       print_r($sql);  
9 f' J! [" [1 s) ^7 l       return $this->execute($sql);  
2 v- D5 ]0 ^8 p! g! E9 ~3 @9 z, i    }
! d* R0 K5 W: @0 M5 I2 v& d从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。, `+ u' s" a4 J+ J6 R. M0 q3 W; n
& ^, }) i8 _0 e1 _( j; V" [
攻击测试:7 z, m% t! a2 P1 C
测试地址http://localhost+ ]% m6 D, ~1 T- @, R, ~8 L  B( b
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句5 n1 V$ F1 t/ L4 m- h; X5 M7 M5 V

. H' N! e0 R, Q8 A& L5 t7 B
% E* ^! D8 f$ d6 d: K
' }& f; F5 K0 ~3 `. r8 K4 s; V
  B2 d0 u( L) o& [; {
% z* {8 a9 O4 F; ?$ [

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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