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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
5 W) W& A- i- \! @+ _$ F漏洞作者:skysheep' I6 p9 K# q/ a, y1 |; S
分析作者:Seay
5 @* O+ i' y% N3 N& i博客:http://www.cnseay.com/
( h, Y' |3 Q+ g# J. |漏洞分析:% k/ f: k" r  K
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
5 s# B3 }2 b+ k$ G6 p# G  v: Q
* S7 t) \9 N& [4 ]& y9 H3 {6 R: J: Q
' e( U, X# p' m8 c0 f
  q2 Y% p4 J( V& Q3 Ipublic function account_manage_info() {  : j- J! X( q1 y  h
       if(isset($_POST['dosubmit'])) {  
! g; h0 {; G/ A( t# {# a           //更新用户昵称  
; K! k2 p) @1 q. Z$ x- W           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
. X$ [! h6 Z7 `2 j9 ~9 G  n           if($nickname) {  8 l& W2 W+ z% }  t6 m! `; P) m
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
# Q$ U# _: q8 i$ U  a' ]- y. J              if(!isset($cookietime)) {  
9 B! @- d2 X6 |% O1 E$ f% {' z                  $get_cookietime = param::get_cookie('cookietime');  , ~, t7 J* i" B. o4 G
              }  
( E( ?  G8 Z2 n              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
7 y5 \0 e# x7 ^" ]8 C; b/ n              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
. f; Z0 _3 [3 T' \" X1 ?* K              param::set_cookie('_nickname', $nickname, $cookietime);  
$ e$ a3 d) c( C. t           }  
/ L9 r, h# i/ \2 N           require_once CACHE_MODEL_PATH.'member_input.class.php';  ; \  j' d7 v4 g8 Z9 H
           require_once CACHE_MODEL_PATH.'member_update.class.php';  ) q/ F% Z) r  m/ m, I) t
           $member_input = new member_input($this->memberinfo['modelid']);  
+ H4 ]6 D5 ?& z$ v0 s. A1 d           $modelinfo = $member_input->get($_POST['info']);  
, f& Q& J- D" Q3 N" f           $this->db->set_model($this->memberinfo['modelid']);  ) r% o" m0 [+ F/ d4 }
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  * s2 V" ]# g2 g$ S
           if(!empty($membermodelinfo)) {  
$ x8 Z4 E- I6 |+ ?; E9 g              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  1 E& f" G$ B$ y  i* M3 o
           } else {  
& k" |8 P3 c/ A              $modelinfo['userid'] = $this->memberinfo['userid'];  " f. t& g( y; B2 P3 q
              $this->db->insert($modelinfo);  
- ?+ w& U8 l! `# F: w3 [2 `           }
0 H4 W) e; R9 ~' c: e# u* g$ \2 B代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,0 v2 d9 X; O0 U! \
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
3 }- f- ?" w4 \3 Y
. N2 x+ j6 u5 j+ ~5 P& l( m+ J7 h8 M# g8 p0 S& I8 L* o

, o7 _  N2 Q, t) ]/ u$ b; Hfunction get($data) {  5 N+ ~2 z3 Y' H  @* t3 x
       $this->data = $data = trim_script($data);  
7 v5 k4 `6 `7 [0 W' |) e       $model_cache = getcache('member_model', 'commons');  4 s4 F3 B$ Z3 z5 x* \
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
7 G2 j/ ^$ V( @; w. N       $info = array();  
3 M. ^- B' w( V$ j6 d       $debar_filed = array('catid','title','style','thumb','status','islink','description');  ( V1 A+ x4 m3 x. @) f
       if(is_array($data)) {  ! a- |3 b/ H* x/ `
           foreach($data as $field=>$value) {  3 T) ?* a+ E3 R0 R) G/ W
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  + V1 b' A' S# }5 S
              $name = $this->fields[$field]['name'];  
2 Q3 u, e: O- [. k* `$ t3 c+ a( [              $minlength = $this->fields[$field]['minlength'];  
5 X+ W6 n7 o9 N) f" m5 j              $maxlength = $this->fields[$field]['maxlength'];  , I- @# x2 Q' ^4 I) C$ ]+ }1 G4 }
              $pattern = $this->fields[$field]['pattern'];  8 J0 C8 |: G+ X; o1 C8 Z' ?
              $errortips = $this->fields[$field]['errortips'];  
% q! \8 R  O5 g" t6 X+ w& y              if(empty($errortips)) $errortips = "$name 不符合要求!";  
+ ^& M  t. a5 `1 L! `  W9 L* A8 U6 }9 q              $length = empty($value) ? 0 : strlen($value);  
/ i. Z0 g7 z, h              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
. Y' N- }/ s" b+ X3 j              if($maxlength && $length > $maxlength && !$isimport) {  ! M& e1 h* h4 K4 G% E- S
                  showmessage("$name 不得超过 $maxlength 个字符!");  . X" ?1 j6 i* }* \! ~9 D
              } else {  
4 \5 i" @' m! E2 l9 ^                  str_cut($value, $maxlength);  
( Z- l4 ]( w8 U8 }! ^9 T* o              }  
& m; E9 S; d/ Z& }- u              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
" b8 u( [9 q3 h, W3 Q& K9 E                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  ; b& K1 N9 f$ S! a0 f. L. |
              $func = $this->fields[$field]['formtype'];  $ V; W" z7 Y& m! O& n  z7 i
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
" L) r" N3 c5 T% n5 j; t2 n              $info[$field] = $value;  , b( P- ?' g. Q
           }  ; d. S9 y! A( X# r* A1 l( V5 I
       }  * ]  ~* S& g7 U( T- C
       return $info;  
: k' j$ i4 x" |8 T& v    }
: H6 T- b7 S$ e+ V4 h& Ktrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
9 L: D! S# Y' |! n1 t. G8 Y) T% ~; ~) O; e1 W
再到phpcms\modules\member\index.php 文件account_manage_info函数
: {5 p2 P, F$ S2 G" O过了get()函数之后。
  ]- W) F, e+ d9 [/ f. t! y
/ n# z1 a8 T* U# \8 q" P
, v  I3 Y& ^3 K3 g( r, \# F! n2 D$modelinfo = $member_input->get($_POST['info']);  * L7 A5 `  ^# I* D4 m5 x
           $this->db->set_model($this->memberinfo['modelid']);  4 X8 Q+ x0 j  ~) y
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  4 b9 G7 F) ?: |: R. k9 Z
           if(!empty($membermodelinfo)) {  
* y' j+ P" t! y$ R9 ^( k              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  % F" h/ a5 X; l5 B+ P2 k/ ~6 V
           } else {
0 C! e' N9 {: Z  L0 r( }直接带入数据库,update函数我们跟进看看! H, A- d, b1 q- X, L  [8 Y

' V4 e3 q2 c' Q% d7 ]% ^1 Y $ ^0 D! `% ~1 o
public function update($data, $table, $where = '') {  ( l- R' d* r+ f9 Q  U
       if($table == '' or $where == '') {  
5 X" S& _" Q/ Z9 O( y! L           return false;  $ J4 r5 x2 X7 [9 ?8 S+ t) ]
       }  
- j7 l: v- G& P4 U/ M9 t4 j       $where = ' WHERE '.$where;  
1 O6 V0 e7 ~2 P! X: `  ]- q* e       $field = '';  + u& W9 z6 F: l3 x) A) t1 d
       if(is_string($data) && $data != '') {  / S; E3 P7 c4 M" M
           $field = $data;  
. q! D. r& ^8 y       } elseif (is_array($data) && count($data) > 0) {  * p9 j( v' P' W- m1 ^# y0 l
           $fields = array();  
7 w4 e# q. y. g4 D# I5 P           foreach($data as $k=>$v) {  
& n% ?8 O, c  y# G              switch (substr($v, 0, 2)) {    A2 o. W& q' I; `& Q; J3 j) X3 M
                  case '+=':  
' C# B, e& u* i/ P                     $v = substr($v,2);  
% x8 b/ c5 O. q4 b& o' o                     if (is_numeric($v)) {  8 f4 R5 C( a* ?: Q/ c3 B
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
; b7 f, \$ D; A) |) D# p% @3 P4 K                     } else {  
* F+ Y" O) |9 y4 X3 P                         continue;  5 H6 _/ f4 S8 ~( P# l
                     }  
; T# v- t8 I( [* z; d" F                     break;  . @, B6 D4 }. ], [5 c
                  case '-=':  
- L; y  i% {* H% o! @" m2 [, C                     $v = substr($v,2);  ; E$ M; A3 k2 W1 {
                     if (is_numeric($v)) {  
$ m6 e9 d3 c5 K                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  - C; ~7 e: k0 T3 l% ~
                     } else {  7 }; R: n/ p8 A7 _# ~
                         continue;  8 d1 L0 I& T- h3 L- W( e, n
                     }  
/ }# |" v/ R' f( ~                     break;  $ k+ ~, ~# T$ k6 N  }, V
                  default:  
# W" m% w$ y8 x; D7 `" x2 w& F. Y                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  0 H: n0 r; q6 `( w+ q
              }  
$ L3 {% ]* S& h5 S           }  
' c- D: R: X" B( ^4 ]5 ?           $field = implode(',', $fields);  
5 F, m9 v6 W; A9 A0 v7 g, j       } else {  4 q5 F. x# t5 _; [% V) b
           return false;  ; B6 p8 _' X! s+ B  @
       }  
/ U) Z" H* w8 d0 I* u, a$ e       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  ( i9 M( g6 C' N! C0 Z9 E8 M
       print_r($sql);  ( L5 h" d# S- O# x+ K/ g# L/ U
       return $this->execute($sql);  
4 j. j3 {1 _) {    } . n; N2 A; b7 E0 B& ?- r
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
+ [& s, ?6 ]/ x$ i$ f$ i6 R( x/ J: M* ^8 i* Q% d" \7 C7 i
攻击测试:
9 A5 v  \# d/ P1 h9 x: A: x测试地址http://localhost' y% M* f  \$ o7 r0 S
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句  V/ B' j+ Z3 y- F; w0 G+ \
8 u, C6 ^2 }& X' B
" x: f" \2 H7 n& v" s  o
9 s& u+ k; o, ~8 h+ h4 |+ g4 o
) T3 R. h2 O/ a6 \2 _6 C# W
% n6 n. f- _5 X8 s

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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