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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告, A- R- m1 D' V, f# j4 A
漏洞作者:skysheep! p# s3 H& r# W
分析作者:Seay  Z; u! v( R0 O. q- Y7 g# L
博客:http://www.cnseay.com/
! `& q. G% ?" E" X5 b漏洞分析:& D# x( M* y' P) w! t; E4 U2 \; u
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
, d( r9 U. u1 ~1 L  r4 }& E2 }  v1 M0 Z7 B
& X! ^8 E  R& q
$ S& Y* Y5 f) |( A6 x/ i
public function account_manage_info() {  ' `- \' t" L- \& W( }
       if(isset($_POST['dosubmit'])) {  
  g$ R3 N& d" Q5 V  q! s3 [           //更新用户昵称  ) Y- `! @' q2 T  m5 K
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
7 b9 Q8 j, a* ^9 S; R+ b           if($nickname) {  
' p) ]# \( w% f# w* d3 r* z! c              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
! d0 R% c& I. |2 s              if(!isset($cookietime)) {  , b+ c1 [" I, B/ I) p4 b' ?' }
                  $get_cookietime = param::get_cookie('cookietime');  ) O4 G. x& E9 A" s
              }  
, ?9 N2 c) J, t7 g* `* w              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  # r4 A0 R5 H+ q
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  8 h, n0 w9 W/ P& v3 ]& E6 o
              param::set_cookie('_nickname', $nickname, $cookietime);  & d9 B3 u( P+ @1 J
           }  ' g1 d8 d4 y9 L& \; R
           require_once CACHE_MODEL_PATH.'member_input.class.php';  $ W& F! U7 _' Z, t# Z7 T
           require_once CACHE_MODEL_PATH.'member_update.class.php';  " O1 D1 b/ p% {' L
           $member_input = new member_input($this->memberinfo['modelid']);  5 k% `/ Q" \3 A# x* U
           $modelinfo = $member_input->get($_POST['info']);  
- l- h2 Q7 x5 z6 q+ v           $this->db->set_model($this->memberinfo['modelid']);  
/ O' n1 m: p" P2 d$ G. d" C7 O           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  $ S1 ]  P4 w2 h/ V8 w# ~
           if(!empty($membermodelinfo)) {  ; h$ n9 G% e0 \5 t' L/ ^: V
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  8 e% ^2 Q( I5 W: e6 Y
           } else {  
+ F) V0 e  }' C. [3 O% Y              $modelinfo['userid'] = $this->memberinfo['userid'];  
, s& A5 @' Q+ B              $this->db->insert($modelinfo);  
) C% H1 ?% I0 u) U$ f4 X" x, S$ {           }   X9 P. {6 n; x5 D% k
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,: D+ a; Y, C7 W0 q7 ~
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
) J- a0 U8 R0 H3 E# C# h2 h# I. g& R

% ]. Y! Y* P) h
; p* _5 V+ k9 G" Ufunction get($data) {  
$ b( b0 Z- R5 z# ~       $this->data = $data = trim_script($data);  
% g1 ^$ s' p! K7 z* B& ^       $model_cache = getcache('member_model', 'commons');  
" d0 j4 u) Q2 q, z7 O6 o! o. a7 f) h7 r       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
5 B; ]% e8 S) g6 o0 M) w* ~       $info = array();  
1 L+ i! I3 a7 o0 x  ]       $debar_filed = array('catid','title','style','thumb','status','islink','description');  % S+ B9 n7 j! _+ L* u
       if(is_array($data)) {  
' \2 {6 ~, t$ b+ ?! J           foreach($data as $field=>$value) {  
7 ]; e& J8 E$ j, x2 S+ p: u  x              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  - T4 f. T" J& A9 ^! X2 z6 [: D
              $name = $this->fields[$field]['name'];  
. B8 }6 J( u+ M" f5 R              $minlength = $this->fields[$field]['minlength'];  
- H7 M/ c# N+ G$ |1 g              $maxlength = $this->fields[$field]['maxlength'];  
6 {" r& B  S7 \9 A8 z              $pattern = $this->fields[$field]['pattern'];  
& U; v+ o& v! a" `# h! f3 ^              $errortips = $this->fields[$field]['errortips'];  
; o/ O  S8 `* T! P              if(empty($errortips)) $errortips = "$name 不符合要求!";  
4 ?' L7 g2 D" N" ]              $length = empty($value) ? 0 : strlen($value);  & L8 Z& @/ w4 v7 F6 U' F0 C
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
6 O8 n/ o9 N2 V8 \/ _+ L              if($maxlength && $length > $maxlength && !$isimport) {  
* B# `( J- k3 P, {/ Q$ W3 z                  showmessage("$name 不得超过 $maxlength 个字符!");  
2 j/ c  V$ \  i, X) [1 n              } else {  2 b' |) B' u1 |7 a- L
                  str_cut($value, $maxlength);  & K* J( A; X% x3 D; m
              }  
4 k6 Z( ]) h  d0 \. l7 m# W% m9 z3 d              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);    j" Z  ~3 f' \4 t+ T
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  - E3 I3 u4 a! S% {
              $func = $this->fields[$field]['formtype'];  
& k% o  u* g# l0 h7 B: u              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
* D1 h1 s+ b6 h( Z& R+ }              $info[$field] = $value;  5 d+ G- O6 ?8 ~+ m- O6 \  L& U( ]
           }  2 R+ Z& w& d( }' Q5 F4 |
       }  * V* `  P. J3 U8 E/ N
       return $info;  
+ T  z( @8 H! k' U5 Z& `    } 2 i" L$ f8 X; s% T
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
' U8 P2 D5 o( C& x2 s
* _0 D5 E/ i( n  r再到phpcms\modules\member\index.php 文件account_manage_info函数
9 m" Z+ c, m, a0 L/ D4 s过了get()函数之后。' E2 C0 b, n* S- F: Q" e6 q

& L* [1 G* a! L  b! |$ V% d5 n" \
- J5 Y$ E9 f, [$modelinfo = $member_input->get($_POST['info']);  : ~3 H" X0 |2 y8 H5 m3 m' s& T
           $this->db->set_model($this->memberinfo['modelid']);  
4 }2 ?9 ^% i; K  r           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
+ b; C2 I7 C) b" X1 b4 O& i) f8 x# w           if(!empty($membermodelinfo)) {  
4 a3 |7 q  b2 \1 l              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  / Y  W: c4 p- ~( ^# Y: H
           } else { 7 O! d1 h7 G2 x' s" m4 W
直接带入数据库,update函数我们跟进看看8 `. E8 v; u4 @3 W
, F2 y: I# o/ s5 Y5 w, f

- y& F7 b8 y+ U8 N3 e9 K$ Apublic function update($data, $table, $where = '') {  6 k' w9 t1 t/ D' P2 {3 z* q
       if($table == '' or $where == '') {  
* {8 _4 K0 v" s( d4 ?# o6 _           return false;  ! w; B# F4 p, i; y- S% x
       }  4 _" a- k5 W7 ?" ^' b  p; @! |
       $where = ' WHERE '.$where;  
# R! O9 j& M" h) j4 K       $field = '';  : r0 Y8 @+ R6 q2 w' p" L3 M
       if(is_string($data) && $data != '') {  
6 c8 G) \4 F2 M* @2 o$ h0 i           $field = $data;  
. S: ]7 X" Y: w" C" K& z       } elseif (is_array($data) && count($data) > 0) {  3 n3 g# {7 `0 Q" Q' b
           $fields = array();  1 ~' k8 O; \) R9 q% k& \8 H
           foreach($data as $k=>$v) {  4 D# m! P7 ^4 g5 a$ Q9 ^7 H
              switch (substr($v, 0, 2)) {  
" W% n  \# Q- h7 {5 J                  case '+=':  
; G6 [2 T) c$ B* Q1 k5 U                     $v = substr($v,2);  
+ V8 R( O+ R4 K2 r, l, A                     if (is_numeric($v)) {  
/ F' u" ?  B8 l$ T                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  . O" y! `  g+ P! E( ]# G
                     } else {  2 k$ b! m; [# P/ D1 v7 b- }9 J
                         continue;  - z& \: h1 j! y4 Z3 E
                     }  9 D3 B( A( ?) N0 g1 z: h: r
                     break;  6 X9 l) b; b+ Q+ |' m
                  case '-=':  # h9 e6 G) e% \0 p; Q
                     $v = substr($v,2);  % G  p2 {$ e5 K7 V1 b
                     if (is_numeric($v)) {  
% C* M5 H% f+ Z$ o                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
0 z/ ~/ z( K* C; c8 D; v0 d7 S' o0 D                     } else {  5 e0 o. H. Q" h8 A: w6 L, e% t. ]9 |
                         continue;  
2 x9 P* s) E' v% k/ b- g                     }  
: q' z+ n$ g4 X& v  F! K7 ]5 \                     break;  2 ~. B! y$ M7 G3 I
                  default:  
' d0 \; p8 G0 O% Y. t: M                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
2 }% p; x" D5 Z$ R              }  
8 }# n% G+ i. |$ t$ h- k" s           }  ) W' y5 D& I& V
           $field = implode(',', $fields);  * u3 ^$ m, _2 Q- Z
       } else {  
) t' e3 [2 N: P. t) J# p/ U           return false;  8 h! q8 Z6 z( v4 r
       }  ) i2 ?+ Q2 c9 u, r7 x6 r$ q$ ^
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
8 E# X. N. k& {2 s       print_r($sql);  
! I  ~7 f. h2 T8 k. ^( I( k. _" [       return $this->execute($sql);  
5 O3 J: z; ]# y$ `5 X4 i5 Q    }
! I5 f1 E$ S& Y3 _从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
8 V) A4 Z3 n" Z& Q$ `  i5 l! p
. ?$ E# u1 V+ E8 X攻击测试:4 q) s; L3 n; L0 n; j( N, u
测试地址http://localhost+ p$ Y- k, E* y: A& N* b, y
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
8 g# t1 ~5 b2 Z  `
8 b$ P: M: Q* u, m: Z% G ; F2 q! g  y# h3 v: \
7 T/ f( i4 H) T# ]; p! g) O9 \

8 v& c9 j0 m2 |/ d
' t1 W  {# ?  F( ?0 ^8 _

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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