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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告' W! j+ V! H' f" ]3 H
漏洞作者:skysheep' s( d7 ^# Y& r! P
分析作者:Seay
$ m# r; ?( x. O- @: n' O' x9 x$ T博客:http://www.cnseay.com/# M' r$ p% h/ c' Y7 h+ X
漏洞分析:# O' D# u- g; X( v2 z
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
) f: m) L3 U1 ?) z7 [1 t6 {4 T& P- {. ~" ]7 z. X5 q; X! |

# E  F; B  b7 O# k 3 b6 P: e# o# {. _  S# N
public function account_manage_info() {  
7 J* g, u' H, Y       if(isset($_POST['dosubmit'])) {  
" ?$ e( q  I! ]/ l8 _           //更新用户昵称  1 g: d: Q+ G9 U2 G7 s" Y" t
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
# F8 ?9 t; O( z1 [7 G! _           if($nickname) {  
" R. V4 \  Y* u              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
8 T- B' h0 Z8 J- i7 f9 W* ^* C* D              if(!isset($cookietime)) {  9 x& w/ o7 q6 m
                  $get_cookietime = param::get_cookie('cookietime');  + c! c0 }* i$ E9 q4 a$ w& L/ E7 n
              }  
* Q5 e6 l3 d4 ]( A6 Q3 o+ K              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  , o# h3 @9 v5 Z
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
% t0 f+ }, b  n8 H  x              param::set_cookie('_nickname', $nickname, $cookietime);  & v5 ~% J3 H  n8 l6 N$ d# e* a
           }  
' N# @9 ^% C4 p1 D- A5 [           require_once CACHE_MODEL_PATH.'member_input.class.php';  6 V( N& I2 U; p6 J& N/ B# n4 i
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
) b/ i4 }% O1 t$ z6 P           $member_input = new member_input($this->memberinfo['modelid']);  7 \7 L6 k! m; {! d
           $modelinfo = $member_input->get($_POST['info']);  3 B3 [# r! U# e  F& P" N
           $this->db->set_model($this->memberinfo['modelid']);  * I! W8 Y8 s, M( f
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  ! x2 F  b# I  I" K; y
           if(!empty($membermodelinfo)) {  - T/ }& O* n7 g8 M* N$ [. }( z
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
" O5 l  j5 Q" G, ?$ [3 w           } else {  ( ]: f, \7 ]3 ^* ^9 j' t
              $modelinfo['userid'] = $this->memberinfo['userid'];  6 S" p+ x# y+ y7 O  \
              $this->db->insert($modelinfo);  7 |1 c, B% F" e3 ?0 B6 G/ @
           }
, Z* g5 z& M# m. ^& c4 _& a代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,! q+ k* Y) u7 N/ A$ z# F
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ |- |* y7 D) n4 ?6 U
% P2 j1 v* P/ [& Y( z
: I  j  z$ }( d % Z' M2 s" \2 A# q. ?
function get($data) {  
3 M1 _: i- V4 c# O0 N7 E6 W4 R       $this->data = $data = trim_script($data);  
9 K7 \+ v% b0 o       $model_cache = getcache('member_model', 'commons');  * Y- D/ Q0 h7 p, U
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
; \9 [# Z+ w. q* ~& y       $info = array();  + |$ D1 x- W+ o* ]8 K
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
! c. n! \" c& ^5 S' z3 s' q       if(is_array($data)) {  3 M' ]" h2 H" s7 P9 y2 \; ?
           foreach($data as $field=>$value) {  ( E3 a+ D' ^: X5 E" y1 z
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;    a( {% i1 S% B1 J7 ^; p# |! s
              $name = $this->fields[$field]['name'];  0 O) L8 o  ~8 D0 M3 W: F+ Y3 ^
              $minlength = $this->fields[$field]['minlength'];  
) v# d6 ^- L: [9 I6 k0 j              $maxlength = $this->fields[$field]['maxlength'];  
) z1 P# j# Z1 |6 I3 I. u2 {              $pattern = $this->fields[$field]['pattern'];  8 F9 Y( g2 @, x- K( f
              $errortips = $this->fields[$field]['errortips'];  3 K4 c4 ~9 q* [7 |
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
6 d; U- T7 N& @              $length = empty($value) ? 0 : strlen($value);  1 u6 X) T( k: ^; o! y
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  * h, Y/ o7 t8 A% F% F( a  l5 S
              if($maxlength && $length > $maxlength && !$isimport) {  
) N9 }' [' m# [  ]7 ^6 ~6 x                  showmessage("$name 不得超过 $maxlength 个字符!");  
' A( ~; Y4 D8 y              } else {  % F6 i+ _; V9 l+ K& R2 K
                  str_cut($value, $maxlength);  
: d' X& o' `/ L; Y8 Y1 T/ J              }  / g" c/ O7 ~! R$ z  r7 |
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
  c5 H* U$ Y* s2 n) \+ h* o  ?                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  5 o+ K2 H' B1 Z3 [
              $func = $this->fields[$field]['formtype'];  4 W  d* H* _2 p# h9 B( J0 \- ?; [
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
' m$ d9 y& d2 W* L8 g$ A              $info[$field] = $value;  
5 Q( p9 @4 h; p           }  1 u3 ^( }+ P4 |
       }  # }! Q& D4 N% C6 p- C* m5 T6 f5 n
       return $info;  * o+ V" F9 q: _" n6 r
    } 7 ~/ T0 N$ _4 T: q9 P: f7 B8 @
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
- m: g8 `( I+ A: y* t% H  s$ H, c/ O* t8 G6 e; {  Z
再到phpcms\modules\member\index.php 文件account_manage_info函数( T7 @- w) o& p6 ~
过了get()函数之后。
: H3 c" w) E# j3 L" l, R6 S% y# U8 n! y2 s, G: G- g, z/ Y

# ~# u, b& X6 Y9 B  {$modelinfo = $member_input->get($_POST['info']);  : E: q+ h  |' Z
           $this->db->set_model($this->memberinfo['modelid']);  " P. W: k5 n, f" @
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
9 o" V3 Q/ J+ G5 I' d5 J: Y           if(!empty($membermodelinfo)) {  
% j$ ?* Z# M; G+ |( }7 A/ M              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  $ z( ]$ H5 g. Y/ r& T8 C# {4 f
           } else { . X: x- F9 f- b/ u
直接带入数据库,update函数我们跟进看看
5 S. Y5 B- c+ ^! s5 P- G+ Z9 I% I# y4 l2 H

# G# J3 K$ \5 W2 p  B$ epublic function update($data, $table, $where = '') {  
$ t! p" [) f6 k, D7 Q, T       if($table == '' or $where == '') {  
8 R/ s% b0 d, g1 g: H           return false;  1 S) v4 q: p$ B- X
       }  3 M9 @* v; A! w( U4 J& o
       $where = ' WHERE '.$where;  
) M) p" m  l+ W# b       $field = '';  
' ^: p4 S8 v/ f& H4 m/ L# y. ~       if(is_string($data) && $data != '') {  
" l" q) [) |$ @/ ?) D1 q% p" d! P           $field = $data;  2 n) \) v) J2 H1 A  k0 P$ f( ^
       } elseif (is_array($data) && count($data) > 0) {  
( Y( ?. {/ C4 w           $fields = array();  $ A2 T9 B8 I! Q$ c5 X" s9 t
           foreach($data as $k=>$v) {  ' _9 f8 z3 H# P, D, ~
              switch (substr($v, 0, 2)) {  8 Q  j2 B- s3 q# E+ [  a6 s
                  case '+=':  
9 ]8 s0 V) c2 O% e/ ^                     $v = substr($v,2);  
! G- q% k$ L4 \  I6 ]8 c9 O                     if (is_numeric($v)) {  
* i; u0 R: y% O, O$ P( ^/ y  D                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
/ x# ?  j) L% G' |3 E% m6 r                     } else {  ' I3 ]; l2 j& e% t/ w
                         continue;  # e! K# x2 w; I5 h: `2 ^4 \
                     }  % b4 }; U9 }  `$ F1 T
                     break;  + h7 w0 j7 s% @) G# P3 W3 e
                  case '-=':  
% P! Z8 c* r/ l5 u- M! T4 ~+ @                     $v = substr($v,2);  3 y' k; W* B) L) r3 S% w
                     if (is_numeric($v)) {  
1 x% a' H7 F% U                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
. V2 F. G; a  d8 s  F6 H                     } else {  4 h; @! R  I  \7 c, z* q
                         continue;  
1 @8 e' y- Q) a                     }  & l3 @% M7 J& p0 J1 K( n! r2 ~
                     break;  
" z2 j+ v8 K8 z) ^                  default:  
0 h/ o( o1 I5 H' z. r; v  I                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
. z: g$ Y9 r2 ~: i: g6 R              }  8 I% b& h4 X# W) i$ L/ @+ O( l
           }  
1 X- }* M* m: Z7 H2 m0 q           $field = implode(',', $fields);  
, |4 M# J" ?- h4 ]% t) B+ l       } else {  
1 {) S7 k# t% F& J) b2 V' J$ ?           return false;  
% A; p$ G9 U  r! \% _# ~       }  
/ s2 t$ O5 ?& [* u       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  / x( q. u2 U5 w; n. V# s
       print_r($sql);  
: R; B8 t; t# I$ C) F) {0 `% X1 n! P       return $this->execute($sql);  
+ E3 J& a9 D) z6 U5 c0 I- u7 E    } 4 K3 p  w7 I* |0 t
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。3 u4 X9 Y( k" m$ g5 ^

" D6 C7 J* {) a/ u攻击测试:
# h5 {  o; W+ H+ A7 g3 p# y测试地址http://localhost
. l% Q5 }1 Z; M7 k# ?! y# r  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句1 k0 u) \# [" I0 h/ t, m7 U: b

; b1 S; m" i% i; e9 A; C: ~
* b. g# m8 U* y4 f  ~, S4 ~% J1 h% U2 C7 x0 {% a

7 R0 \; n- S& c; Q9 Q$ p
/ G: S" `1 j! R

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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