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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
4 O% @) T9 K  i  _( q) v$ r漏洞作者:skysheep
& U4 J1 w2 c7 v6 T分析作者:Seay% [) k; }1 d; M( b- R9 d) K' V4 b% S
博客:http://www.cnseay.com/
# k7 @7 {8 I. ~, ~漏洞分析:+ u$ H. _8 d5 L
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。9 g0 c3 m. i- c0 c/ N
; U4 @0 Y4 J( V
% k' S9 j% s! K$ e5 D

- c' `& D$ N9 Z4 v7 Xpublic function account_manage_info() {  
  G; V4 X. B* G+ l       if(isset($_POST['dosubmit'])) {  
1 x2 w" T# W# V, H& x) I           //更新用户昵称  ' _; G3 {- {/ G% X9 [
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  # C- x% l2 I' C; N9 N
           if($nickname) {  
# b: z) ?7 t2 a: a. v$ p              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
  s- N! d& e( }9 s4 H              if(!isset($cookietime)) {  
5 w5 h: j! V9 _* K                  $get_cookietime = param::get_cookie('cookietime');  
: M8 J1 J9 F6 S" \: b              }  8 d' Z, l; j0 B$ Y7 v6 A2 K# Y
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  ; N* ?& h! _) ^2 e. n8 b
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
) j# X  K" P  H4 b              param::set_cookie('_nickname', $nickname, $cookietime);  ; a# ^' P0 {5 \& C* z. H; i& y
           }  * R6 a* {) ?& U' l, p
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
- |: Q3 _7 E! @* o  {           require_once CACHE_MODEL_PATH.'member_update.class.php';  
2 C/ q( g& A! ^. B           $member_input = new member_input($this->memberinfo['modelid']);  ; a( {/ [; H& k& W0 v! p
           $modelinfo = $member_input->get($_POST['info']);  8 \; \- u1 U6 h( R0 E; F
           $this->db->set_model($this->memberinfo['modelid']);  
- _) w  S* c- F" V/ z; N           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  + E, o; L/ [6 n
           if(!empty($membermodelinfo)) {  * j$ u/ N7 b# Z
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  % t0 c( e4 x* `6 D
           } else {  
$ {* K; r! q5 o5 |8 D" y1 i- v              $modelinfo['userid'] = $this->memberinfo['userid'];  * J4 a7 n  D, }' u5 s+ g
              $this->db->insert($modelinfo);  
+ B, D4 G' B5 S3 ?4 g" B           }
; X9 Y5 F( I" d7 k代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,6 l8 g# V3 S/ _2 W( m. d- {" h, @
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ j- e# ~5 X3 P  [. ?- ^# c' D( m$ O* \! N' m# |
6 |1 v! f) d  }% w
9 Z2 H1 E" ]' C$ F# H- D
function get($data) {  
2 i3 ]/ I9 Q: b) x! V       $this->data = $data = trim_script($data);  
# k2 \4 X1 |2 K, r  t0 a7 B       $model_cache = getcache('member_model', 'commons');  
/ L' t% S) P, P0 C& g       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  : t( a+ w8 p" J
       $info = array();  
6 G, ^3 [8 o5 G7 ~, {       $debar_filed = array('catid','title','style','thumb','status','islink','description');  6 w2 g  ^6 l& D0 N1 X
       if(is_array($data)) {  1 W9 H" m' I! c, ?/ s9 y
           foreach($data as $field=>$value) {  # n1 n6 }" @! \5 |' [, B
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
9 G7 i) I' D/ N6 [              $name = $this->fields[$field]['name'];  ) |- `* c1 L  Y$ ?) b2 j' u# t
              $minlength = $this->fields[$field]['minlength'];  / a! D" {" t6 v0 R
              $maxlength = $this->fields[$field]['maxlength'];  
  {/ i5 `" X/ P+ w  ^              $pattern = $this->fields[$field]['pattern'];    g* d' P: A' D
              $errortips = $this->fields[$field]['errortips'];  ; {. K0 [4 n8 x6 U; F; q' ]9 o0 P9 Q
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
; L. o0 v" e% N, I              $length = empty($value) ? 0 : strlen($value);  * R* R# Y' a4 \5 E# `; Z  H; F
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
6 l" `/ A9 F! _; Z+ i% J7 N* i              if($maxlength && $length > $maxlength && !$isimport) {  ; n: J. m8 n8 Q% M4 W
                  showmessage("$name 不得超过 $maxlength 个字符!");  
1 R6 T9 h( u* D% S              } else {  
7 o' e, t: s7 h, @* t% |                  str_cut($value, $maxlength);  ! m  v# X8 ^9 N0 P
              }  
1 d$ s% Q, ^: U# K# Q9 U- X              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
* e! ]+ ^8 x# k9 C% y4 e                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  ; k! `% y0 |/ v9 M5 u* c
              $func = $this->fields[$field]['formtype'];  
  U/ p3 z. P3 g( Q! e              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
; C2 O8 d- J1 h              $info[$field] = $value;  * C: t* k0 J: T+ C9 f
           }  
2 y5 \# P1 j3 P1 Y) |! q0 Z( ~       }  
6 T9 i6 A5 F% R: L       return $info;  
, t8 r0 |( s# Y& \# @& G/ e7 M" m7 @    }
4 A) U5 y7 Y/ }( E$ \# `trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
" Q  h+ k- F9 J
. R' R. g1 i6 d6 T7 S1 y再到phpcms\modules\member\index.php 文件account_manage_info函数! n5 W# I# ]/ e. m* a' i0 m
过了get()函数之后。! i$ y$ v% e$ L% d
0 L  j. S$ w7 R: j$ l2 D

6 M$ |( B6 y8 h) t4 N1 a$modelinfo = $member_input->get($_POST['info']);  
% \$ |$ V3 @( F* y# c           $this->db->set_model($this->memberinfo['modelid']);  7 n# x  C  x3 h: p2 {
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  / a7 o  l+ H- _! y/ x
           if(!empty($membermodelinfo)) {  
. v0 G; N/ E* w+ m. k( r8 S              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
, i3 p8 G8 W$ M# E           } else {
$ H# w7 K" J1 ]1 E直接带入数据库,update函数我们跟进看看) O; M% u7 _9 i

  r- b* F) e7 A/ j
( _: L1 b: A* f. S; N/ i; x! X% _5 npublic function update($data, $table, $where = '') {  ( {3 h/ r! J0 D3 e
       if($table == '' or $where == '') {  9 \, a1 G. q. e5 L( {
           return false;  6 _/ G  {3 U+ A: P" x
       }  & F- q5 H6 F1 J* M, k1 K
       $where = ' WHERE '.$where;  
) z" w8 m" [4 ?5 h7 E0 A) Y       $field = '';  
6 K# ^" s0 `" y1 f9 r1 ~% j       if(is_string($data) && $data != '') {  : G& Y4 n7 n& b
           $field = $data;  
4 g$ J3 X+ [' M6 b! K- i       } elseif (is_array($data) && count($data) > 0) {  
% c& l* ^; [  h. b: D           $fields = array();  ! @: R/ F: P8 h$ h- E/ u) ~/ o
           foreach($data as $k=>$v) {  
3 b' Y2 h2 p8 Q) ?/ Z5 k              switch (substr($v, 0, 2)) {  
0 T3 E- v+ |: V                  case '+=':  
; I9 [; p3 k0 Q) s1 F4 l                     $v = substr($v,2);  
+ C8 h) m1 `) g) R0 \. M3 r* f                     if (is_numeric($v)) {  
# D5 q0 r' r6 c& [+ ^2 g                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
2 {. i% m, y4 Y& ]2 L5 n                     } else {  1 l5 G: T4 E  O6 ]& V" ^0 _
                         continue;  ; Y. a; A, [  X/ s2 `1 D9 v
                     }  
1 e6 X  a/ v# v# x9 e                     break;  . }# O# ]3 N+ x2 |
                  case '-=':  / P5 B5 m: R# S1 F; ^" {9 j
                     $v = substr($v,2);  
' a. y) {  W0 n                     if (is_numeric($v)) {  
4 h" O: X/ }( J- A1 L& L                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  ; h- L4 |3 i9 r6 p9 G  _
                     } else {  
( p" |4 U! q  g) u2 `                         continue;  
5 [* @8 }. V3 P, A4 u5 x7 m5 R$ \: d                     }  
, @/ v* r. l# X, {" q                     break;  
! ]3 [9 R& N. \- F$ s$ g+ U0 v                  default:  0 r4 J* M/ i9 e, k; p8 d5 l7 r
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
0 o1 S% d0 B- S+ o              }  
6 V* g% U3 ]6 n7 _           }  
" @5 N" p8 `# T: i0 m           $field = implode(',', $fields);  
( G' P, ?9 S! ]# V6 h# m       } else {  
5 q. `5 {" M8 m' r           return false;  
8 Y/ G( |* r9 }, d' O       }  + \% Z# k' B. s: O
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
2 x; r+ z1 W2 Q/ W) ]" j. K       print_r($sql);  
' d) _8 N: w/ n0 K( d       return $this->execute($sql);  
7 p3 B8 ?7 x$ W8 K    }   C  ]; d# N% y: J" j& t/ |& o$ v
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。5 ~$ j- n% T0 X( Y# t- _7 ?: J

- s/ I* `. n1 x  ]6 i! {* g3 A9 B+ A& k攻击测试:
, l8 g' K+ z  O测试地址http://localhost9 ]+ U$ r2 B' }$ }5 T! B( d
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
# L2 \2 R* u3 ^8 f4 T' |
' }/ a; A: C/ M4 h& s0 i" H& j  m
) M9 v5 `/ ~, q3 n/ X" n( q# F/ X( p! g  [

3 M* D" G; u  p. T+ b1 y' p" l
$ k& G- J# i4 o( m' V, Q& x1 y$ U

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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