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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告6 l2 G) |; E. x. f' e8 r3 c
漏洞作者:skysheep
2 q: T/ w7 F: _! _5 F分析作者:Seay
4 C. h) L- m1 }& _6 h, |$ i, L5 [博客:http://www.cnseay.com/, [: P" @: V: |, j+ i
漏洞分析:9 b9 C9 s5 {" W- @3 N
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。- t+ R$ Q* |" a# a/ k
- G+ O0 g2 D* a, D/ P% k
$ o- R( m  R0 d- z: q

. A' S7 p2 z/ a% Q5 U  G4 z, Vpublic function account_manage_info() {  ; d( A# p  ]4 `$ [2 ~6 ?& B, H
       if(isset($_POST['dosubmit'])) {  0 J+ o7 U6 t+ i& a1 u: G5 U
           //更新用户昵称  
6 K7 A* D5 b( b           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  0 u) u3 ^9 b) R0 p  g
           if($nickname) {  
9 ]) t) I, j$ M9 x6 I5 c% ]              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  " ]& U8 V8 |1 O/ c1 V/ s- v! }0 L3 |* \
              if(!isset($cookietime)) {  1 z' b+ j+ ?" ~, \% u! o
                  $get_cookietime = param::get_cookie('cookietime');  
$ ?  \: A+ @& z3 d              }  
, ~! B! ?0 B8 f              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
$ \# s( `% I) l. U2 C- O/ e              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  # u$ j  `. B; ~
              param::set_cookie('_nickname', $nickname, $cookietime);  0 f  F  P- v* P- G" e
           }  
# B2 [2 W% R* [           require_once CACHE_MODEL_PATH.'member_input.class.php';  " V; k7 T4 [! k2 ?/ r- ~! p( ~+ B
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
- I* I! O( c+ d  C0 f  O           $member_input = new member_input($this->memberinfo['modelid']);    \! n" P; m7 O, u+ Y
           $modelinfo = $member_input->get($_POST['info']);  $ Y, V4 s; |/ w! [) x) Y
           $this->db->set_model($this->memberinfo['modelid']);  
4 P. j) W) `4 L) z+ ~$ m( e4 ?           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  ' k8 e5 A/ I7 B' X/ ?
           if(!empty($membermodelinfo)) {  
/ ]4 ]  S. |- }; Y$ g              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
( z  I$ P; @9 j  ~           } else {  
! H6 n' t# N0 N) |6 F: ]* x. b9 y              $modelinfo['userid'] = $this->memberinfo['userid'];  ' l0 {( [2 w/ N
              $this->db->insert($modelinfo);  3 V+ c4 r2 @% d) _4 S' k
           } & z1 d/ G+ a# p) X( {! z
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
& @9 z- r: |9 r6 `, a6 Y4 d在\caches\caches_model\caches_data\ member_input.class.php 文件中:! q1 Z7 ^% S9 \' ^7 K" R/ Q9 o+ M
" Y1 P3 P* {3 d$ y/ t0 P) p/ M, |

# x, D. S* X+ b. @( s: B; S
' D6 {- |% B1 T0 nfunction get($data) {  
4 k) T1 h+ e5 r9 l8 z1 \" f; ^       $this->data = $data = trim_script($data);  ( ~4 w& |9 q) p' b  m4 s$ d3 n' G
       $model_cache = getcache('member_model', 'commons');  - ?8 u" C0 U; _1 C
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
) m" N+ e" N0 K- c6 r. m       $info = array();  / G9 |& `( V6 {+ }, f# M, K
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
- g1 }6 t; Y& A$ g- K/ Q* ^       if(is_array($data)) {  2 G9 T( y6 y8 o/ o8 p
           foreach($data as $field=>$value) {  4 ^! L% Z$ P' T: X3 n
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  " {9 w6 ]! P5 i3 m# d7 M7 U4 e' y
              $name = $this->fields[$field]['name'];  
+ {- I1 s" D3 p/ [7 E% t% m              $minlength = $this->fields[$field]['minlength'];  
( I9 P& q" a( l' p, J              $maxlength = $this->fields[$field]['maxlength'];  
' h8 P" x) I  H. ^' Z6 z              $pattern = $this->fields[$field]['pattern'];  8 K& j: X; \6 ^/ _" Q
              $errortips = $this->fields[$field]['errortips'];  
# c6 Z8 B) v3 H% F# E8 D( G) G              if(empty($errortips)) $errortips = "$name 不符合要求!";  3 Q+ Q, y6 ]& I- a0 T- @- k
              $length = empty($value) ? 0 : strlen($value);  6 ]; G3 ^+ ^, ?/ j' T
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  * O8 X! m1 W. V2 L8 t
              if($maxlength && $length > $maxlength && !$isimport) {  
0 J) P5 q5 Z$ M5 N, Q                  showmessage("$name 不得超过 $maxlength 个字符!");  
: p8 J5 \7 W; |0 t8 I- f              } else {  4 Y; d9 u4 [9 i5 j8 g+ ^( H
                  str_cut($value, $maxlength);  
0 m/ U0 d+ K/ n! l, s              }  
. h: l. g6 U. i( e3 e: k0 j              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  ! @+ Z8 L/ E4 z% F3 l
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  1 p- B0 @) N' g
              $func = $this->fields[$field]['formtype'];  
2 _8 M$ s/ s8 K# }              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
0 F* t1 C: M. }% `              $info[$field] = $value;  7 v) ^4 E+ `. w$ Q, {  `% f* F
           }  . a* n% S7 A9 x: X6 `
       }  
" E; w1 d6 j' A- D       return $info;  . p7 Z3 R7 e- u4 c: q
    }
9 \4 h: n) U5 [trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
3 }( |  V, e9 R+ t
8 R# n6 B+ \5 Y, j  T9 w  r再到phpcms\modules\member\index.php 文件account_manage_info函数
, u1 T/ ?$ `# g3 b, [, T. u! r过了get()函数之后。  _) Z  u; e5 Q7 f

2 A/ a4 `2 l& ~9 k0 `, A + ~3 \9 d7 \# [; O! V
$modelinfo = $member_input->get($_POST['info']);  . W2 Q1 \; j% }7 |" C9 n) X. J
           $this->db->set_model($this->memberinfo['modelid']);  5 a% k% d  O6 x  R7 S, K; T$ d! ?
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
6 Q& P' V5 I* t0 d  t           if(!empty($membermodelinfo)) {  
& U0 k, o  _; ]* n' G* N+ s. p$ ?( g              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ) \% c8 Y; w$ t6 D: ^% `' z2 i0 r1 p3 Q
           } else {
5 ~6 S8 Z' M/ o3 V, _+ q; [直接带入数据库,update函数我们跟进看看- p1 d& ~+ v" p9 L& H

- J  L- |! l0 Z3 C8 u " a8 m9 b  T3 v- U$ b0 B
public function update($data, $table, $where = '') {  / C$ d# }9 G* E. N8 z3 k
       if($table == '' or $where == '') {  
3 Y5 F. l7 v- _7 E5 S3 L% t           return false;  
' B( N6 m2 D5 d: M3 \; H       }  
, X6 d/ L& E2 t2 W$ ~9 O       $where = ' WHERE '.$where;  
0 w5 d5 k6 D6 T; o       $field = '';  # V  S- b; c. B7 f9 _# O" D
       if(is_string($data) && $data != '') {  
) |; }2 K7 G+ F/ U# ]0 j           $field = $data;  " j' M7 }5 C; |+ P/ }
       } elseif (is_array($data) && count($data) > 0) {  ' n3 {7 c5 h, v! e$ n7 T
           $fields = array();  
/ g5 [$ p! {. w- X' d           foreach($data as $k=>$v) {  " O6 n) m* X# `0 E/ G: s6 H
              switch (substr($v, 0, 2)) {  
+ C( s; Q# Y- ]6 f                  case '+=':  6 m# w1 C- {" A/ ^- h
                     $v = substr($v,2);  " m" E  H1 C7 A4 F8 |
                     if (is_numeric($v)) {  ) c7 X3 B3 p2 T/ H5 |* [
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
/ h" V, d( b0 j                     } else {  
/ K* X, c, |7 J$ X) Q                         continue;  
7 r( v4 H( ]7 K4 ?2 c% l8 S2 A                     }  
4 b0 w6 ?9 M% a3 P. \( J                     break;  
' V7 B" o! g9 p9 N& S' V                  case '-=':  3 t1 _: ~, i, J% U% n
                     $v = substr($v,2);  
, B8 O' Y7 q% k                     if (is_numeric($v)) {  
; R& j% c0 H5 s& [7 Z6 Y5 N                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
- Z+ v) N4 I1 L9 m1 j                     } else {  
( S1 u9 ]+ r2 f/ y+ r4 B! q7 S                         continue;  
9 i6 I6 A+ \: ^; k0 Y' {                     }  
0 o+ L; N" M7 ^% ^  D                     break;  ; D* {2 X4 _5 |# h* m1 ^* d
                  default:  8 G: s, G% \2 f  t! f5 h, X* x& b
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
8 ^4 J. Y* D( S# x- g0 V% p9 W              }  
7 ~& k0 O+ K+ h* P, |- i$ S0 l           }  9 f3 t7 M7 r* n( A, ?
           $field = implode(',', $fields);  
/ s# @0 ~- f1 M& Y       } else {  
5 R0 b6 u( ^- D+ p& F/ v' E0 ?           return false;  
6 H' o( [3 I8 o# W, ^& r+ j       }  
* G$ s# ?8 \6 x       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
9 W" A- I; b5 N       print_r($sql);  
) Z, {/ |  d* R( h) N       return $this->execute($sql);  
5 c8 K" w, B+ |    }
6 G2 F9 |. n) H& a+ \从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
$ L- s" t% j5 W( a4 l9 b$ b- E4 v, C8 p/ j
攻击测试:
( }& j. ~: h, k5 p$ B/ y- @# Z7 Y测试地址http://localhost: C; J/ b% z! e: P3 L
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句' O# n3 W, T' C6 P: C
* {) p2 \+ |% F. C+ [! e
2 o' ]2 L0 u/ ~$ R$ A

. ]& r4 `/ e+ Y
* [2 U9 @8 p, b( d0 E/ H/ j" M) `4 H" f2 M2 g6 T1 }, }* f% }

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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