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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告6 P! Y& P- c  o# h2 W9 C* U, S) s2 P
漏洞作者:skysheep7 x" k+ L0 w  e: A) P, e6 {
分析作者:Seay
/ y% ]6 y9 y; p* O博客:http://www.cnseay.com/& l9 e3 t% u( Z$ a- B' H+ ?
漏洞分析:
9 H  h2 G3 F! \' W: y2 q  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。  Z# p) v9 c( z4 [3 }  z) s
  E6 ~4 ^! f5 s7 o; l

. R5 j% _. e$ j' r- } , F- O2 V6 @3 K; p. m3 `  j
public function account_manage_info() {  
& L& W' i1 ?. u$ j       if(isset($_POST['dosubmit'])) {  
& h% E& \2 I" g1 y: v& a6 `           //更新用户昵称  * t/ y* q7 k6 z1 M+ p. V
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  " }. M5 [( }' U/ d
           if($nickname) {  
3 x& C, H4 f" c' r$ ^              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  / w$ t  W5 H& J0 b2 v5 X$ c
              if(!isset($cookietime)) {  
; F+ K0 w) u2 |* a5 \                  $get_cookietime = param::get_cookie('cookietime');  - l8 k! T( b% C0 o0 q
              }  , X# k3 k! S' v. j
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  * @9 v! U2 d2 @6 v( v. j
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  % y  U9 a. E7 `4 y" Y) {
              param::set_cookie('_nickname', $nickname, $cookietime);  
2 c  _. i# n, d: V6 V9 e           }  6 h7 f, b7 p4 k
           require_once CACHE_MODEL_PATH.'member_input.class.php';  ! C3 D0 V: |  W0 }
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
! T) s; U# g9 p3 O6 c$ e3 Q           $member_input = new member_input($this->memberinfo['modelid']);  / m4 c6 [/ l7 N( {' s6 b) P; u
           $modelinfo = $member_input->get($_POST['info']);  " `, X' V$ t. ^3 Y9 y
           $this->db->set_model($this->memberinfo['modelid']);  
( S$ v' n8 K$ l. Z: G) d           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
" Z/ [% D" J$ o( f) d( A           if(!empty($membermodelinfo)) {  
3 F; r. k' b, a; J* U6 I& G              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  & @) X! e% Q2 D" @3 ?6 G7 ~8 K
           } else {  ; W5 g# J& Y) }' G" r
              $modelinfo['userid'] = $this->memberinfo['userid'];  0 U; m$ f( C4 `) q
              $this->db->insert($modelinfo);  ( q7 B) v& H7 C9 D7 ^
           }
% R+ a- \1 [% U3 l2 j& `5 k3 M2 C代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
; K+ Q8 \: q2 _$ p* j在\caches\caches_model\caches_data\ member_input.class.php 文件中:
$ w4 o  b# D2 e7 ~/ J/ f) F& y: z3 U; u$ v1 q: {- m9 D+ F

. x5 d* |2 t6 d4 G8 n( z
3 U0 y6 A' `+ l! `: P+ Nfunction get($data) {  
+ q; \1 L8 I- R! _4 D% G! Y1 W& Q; q       $this->data = $data = trim_script($data);  
2 {! Q: b6 O0 E  x& I* n7 ]" P       $model_cache = getcache('member_model', 'commons');  - ?+ H/ v" F/ e( }! [
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
+ P0 N- P+ z' P1 i: Y) W4 C       $info = array();  
, m( {5 M: t2 d/ x! l       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
2 g$ K1 Y3 I% m  w       if(is_array($data)) {  & i- l- h1 O- M( G9 Z- l
           foreach($data as $field=>$value) {  / Z) o) H" Z6 L; ^. U( N
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  * X/ T7 ?0 O+ g* `# Y& h# w3 y
              $name = $this->fields[$field]['name'];  8 X: O1 g+ {; w  ~2 j" S
              $minlength = $this->fields[$field]['minlength'];  3 U/ t; h% ?+ G! D" J6 x5 Q
              $maxlength = $this->fields[$field]['maxlength'];  6 F. b8 P( j" ~- c8 s2 }1 {
              $pattern = $this->fields[$field]['pattern'];  6 G2 ~! V1 F; X8 X
              $errortips = $this->fields[$field]['errortips'];  
4 h+ r3 `5 ~4 F6 g$ C( ~              if(empty($errortips)) $errortips = "$name 不符合要求!";  
+ i: b3 h% D4 k" q; ^3 o: J              $length = empty($value) ? 0 : strlen($value);  ! Y& J3 H+ K' \5 ]
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
" A  u  D8 O! u$ @4 i' R7 T* b! K              if($maxlength && $length > $maxlength && !$isimport) {  . p$ `1 e$ c& X1 H' k
                  showmessage("$name 不得超过 $maxlength 个字符!");  9 S5 l- o2 ~' c9 h9 [8 o
              } else {  
' D1 _, B0 B5 _5 t1 b6 u                  str_cut($value, $maxlength);  
/ U# Q, x% {2 a6 t% P( n& F7 p              }  
# E& `, n: r' A: W1 D2 c# Z              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
9 [& \; P# H* c: Q$ g( }                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
- z: p; j/ p7 d- O; I              $func = $this->fields[$field]['formtype'];  
( J9 l( D4 V6 V  u3 z9 R# ?              if(method_exists($this, $func)) $value = $this->$func($field, $value);  " R: a$ S, j! c& K
              $info[$field] = $value;  
' X4 w$ O6 H0 {           }  
* A' B# I- O% H7 d7 y3 O       }  ( O6 v: f1 C& R2 t( Z! B+ D7 B
       return $info;  
) A6 x# y' P( s7 X4 }    } ( D4 g- D3 M* b
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
' @) T/ L# `; s2 Z! m+ f4 g3 [3 B! Y9 L. K+ B$ n( L
再到phpcms\modules\member\index.php 文件account_manage_info函数
4 F9 A! f" p6 T9 y过了get()函数之后。
: W, G3 p. |2 N  X' r
, B6 K5 `) Z  V* k7 C1 ?
/ ]+ i* p# q8 ~% y$modelinfo = $member_input->get($_POST['info']);  * F1 V5 k6 K- F/ |+ v
           $this->db->set_model($this->memberinfo['modelid']);  
  N* E% ]' @3 r           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  ' |$ i7 H: c7 v7 N  u* H, S6 L
           if(!empty($membermodelinfo)) {  
2 ?6 M' D, H% J+ L; p8 Z( I# m              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
1 x5 v' b+ \$ n7 F* i           } else {
( O9 ]( J% A. R0 ]7 c直接带入数据库,update函数我们跟进看看4 I. _# P% H, p2 B! h

9 y6 \- E, c. ?: k  h 7 g6 `, a7 b3 H2 i* w. i
public function update($data, $table, $where = '') {  
( R' J( w! a6 d6 H0 R       if($table == '' or $where == '') {    N3 p, f7 {' a1 p& s
           return false;  + e) D$ f2 |# R8 w9 v# o
       }  
# D# |& P2 a) T2 k1 b       $where = ' WHERE '.$where;  4 s% H8 f3 ?& ]1 V
       $field = '';  
0 ]8 d# H5 i% b3 d. M       if(is_string($data) && $data != '') {  ( r3 R/ G$ O, N  `* r- @
           $field = $data;  / U( x5 O  B! o4 x
       } elseif (is_array($data) && count($data) > 0) {  
! I# F1 I) f  T. ]8 O% f" t' u           $fields = array();  
3 L0 {' }0 }) C1 ]) S% D$ r           foreach($data as $k=>$v) {  
+ p4 f( q" s: \6 T; ]              switch (substr($v, 0, 2)) {  + `2 y. p8 ]8 U/ v4 B- `; w
                  case '+=':  
& N. h' w. {+ F2 Q- q1 a' f                     $v = substr($v,2);  
5 e6 e9 Q. q$ `) t; ~                     if (is_numeric($v)) {  
+ C* |! U" N& A/ G! Z8 N' R                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
9 V! P; x2 o) i& `7 z: r4 {+ d+ R                     } else {  
" y0 A! s$ `, }3 ?, b" ~                         continue;  ( F! \) }. r% d" |
                     }  
+ d" d4 y" o+ e0 z( H% E# {7 |0 y; i: E+ z                     break;  
5 H5 I2 j, R  \9 J4 `$ Y1 }3 t  p                  case '-=':  
9 Z7 H- z* V% o8 R' e                     $v = substr($v,2);  
: t6 z6 d6 B  i+ \8 I, P                     if (is_numeric($v)) {  # I" Z* `0 f$ {5 S6 Q
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  - m/ [+ s* ]) {/ }: L
                     } else {  # q6 S" F8 U* ?8 R6 V5 M
                         continue;  
, [  u# w& X# M8 X                     }  0 M" Z  g2 V. q% J5 e
                     break;  
1 ^4 z- |1 j  J/ o1 B/ Y) |                  default:  
9 ~; Q. {- _2 j% B! y* G                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
9 v8 q- e  z* j4 {4 n1 a              }  
& g* Q1 y5 Z, w* ^) ]# e' I! D           }  9 I: n$ J% H! ?
           $field = implode(',', $fields);  # J2 P* V  x) W
       } else {  : n2 U9 ^1 S2 d; i2 z0 A" n
           return false;  + n4 b. D3 c8 O( B2 l$ V9 x
       }  
6 g& n. @. o( c- t1 w       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  ) p; F# n% g3 D
       print_r($sql);  
$ y: G' M5 y! y2 x5 `  q8 c       return $this->execute($sql);  
# M  ]  H2 M. R. h1 a. D    } 5 V5 @9 ^& S. R* O
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
- Q) d5 @! L7 I, B; ~, [: }2 d( t
  u1 S$ }# P3 y# G& |" E攻击测试:
  G+ q7 Z; l; c" E1 Z( P/ f测试地址http://localhost7 C; E- ?# J7 A% V% t0 c4 R4 t) X
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
' G5 a- g" K8 B1 o7 a9 L) o
' a# `% M8 b" k3 m7 D+ q# ` ( C1 I7 G" S! F: W

! `! h( }3 }# b& y* i* |% Y/ L0 N% a3 {8 R* q# @/ g
  _  H' F$ Q+ D$ Q

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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