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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
$ C% Q! ~) @* E* S$ q3 e漏洞作者:skysheep/ n2 F3 ~5 V3 F0 L; ^) F" v# X  Q0 A
分析作者:Seay
* t$ z! ]4 X. A+ c3 ]/ a4 i, a博客:http://www.cnseay.com/( O6 @+ S4 @# j( J
漏洞分析:9 I% I( K! J/ Z; S. C$ q6 C& S
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
' x! ?/ c% c/ p$ O' v$ Q% S7 n0 q1 C( v: o

9 D! g/ H# A, @4 o9 u/ w8 V  C0 Z
9 ?9 u" c9 X4 Npublic function account_manage_info() {  ) L. h* A1 N+ t" X
       if(isset($_POST['dosubmit'])) {  
; P1 J2 ]. N4 B           //更新用户昵称  1 d2 H; T9 _# o
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
/ |! \9 j$ H- M8 H           if($nickname) {  9 B, `$ ^  Q' L' l% ]. y9 F4 j
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
- _; B3 W- z4 X4 A" d              if(!isset($cookietime)) {  3 n+ V, I; h2 o: v
                  $get_cookietime = param::get_cookie('cookietime');  2 K9 ~: Z+ Q6 n4 J
              }  
: |3 h% U5 T. e2 w8 s+ X5 u  o              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  : W& s& Y1 D) @; V0 S
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
! N% y/ S0 q7 v              param::set_cookie('_nickname', $nickname, $cookietime);  
; o) \$ r/ x4 O) J& \0 e$ L           }  
. j- t! y, w& W) `6 r( |$ I' j" z1 O           require_once CACHE_MODEL_PATH.'member_input.class.php';  4 ~1 u+ v0 ~! W
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
$ J- G) g. Q" P! U: T) @, y6 M) |           $member_input = new member_input($this->memberinfo['modelid']);  & V  E  o6 a6 z- K3 i1 R: A6 g# J
           $modelinfo = $member_input->get($_POST['info']);  
6 k0 }* o! ]8 R  f: ?           $this->db->set_model($this->memberinfo['modelid']);  
# N0 V- i5 @3 X/ h0 r# G* |           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
4 s! U- ~' s! E           if(!empty($membermodelinfo)) {  
' G0 p! f6 v. B4 u: _, s7 N$ W              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  % S+ ]8 G8 m; K, k* @' b" Q
           } else {  ! M  L! W+ N* z0 q2 [& b5 L$ W
              $modelinfo['userid'] = $this->memberinfo['userid'];  1 O: T& [! x" }3 U0 i
              $this->db->insert($modelinfo);  
& F( P. S1 B( L6 |7 \; [% D           } - c' V1 `) W7 }3 X/ _) l
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
- u- E3 t- f) @3 k在\caches\caches_model\caches_data\ member_input.class.php 文件中:
+ F) a, ^! U, J+ l  b2 N" Z( ]  R+ z+ C* f3 w* G: M9 c# O6 g/ K1 ^9 [

. A8 d; [' K7 {: K/ |2 o
! k+ W8 T0 G+ G/ G0 X8 Q1 afunction get($data) {  
9 F, K- A, g" H6 t! H& ~* ^- p       $this->data = $data = trim_script($data);  
: m$ S  ^( s) r3 g       $model_cache = getcache('member_model', 'commons');  ! B" \1 k& _/ L
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
1 S2 e3 _; B! I9 K% @       $info = array();  . z$ Z4 z/ [: A4 E* P+ r( r. f
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
2 a+ Z2 m* o7 V& x7 x9 s8 U0 I       if(is_array($data)) {  ( K1 ?4 ~- P0 ]3 Q2 A  B& x
           foreach($data as $field=>$value) {  
, L& K2 G5 s5 M3 x& d! W              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
9 }0 t) L1 y7 H1 d              $name = $this->fields[$field]['name'];  9 W3 r9 t  M; E1 ?8 d! u" J  c
              $minlength = $this->fields[$field]['minlength'];  
4 p7 ?3 E4 [+ ]0 ~* b6 ^              $maxlength = $this->fields[$field]['maxlength'];  
% I2 a* w/ \+ |4 A1 Y7 ^5 U1 S              $pattern = $this->fields[$field]['pattern'];  4 J7 @) h- A/ n- [7 p
              $errortips = $this->fields[$field]['errortips'];  
0 A. h% o4 F3 ^) l! F              if(empty($errortips)) $errortips = "$name 不符合要求!";  & b, G# d6 I( L1 z( G
              $length = empty($value) ? 0 : strlen($value);  2 Z' I0 [7 r. z# S. B
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  + ?$ k+ f) n( V" N; a1 t7 |
              if($maxlength && $length > $maxlength && !$isimport) {  
" f6 T7 }# N1 I' L                  showmessage("$name 不得超过 $maxlength 个字符!");  
: v$ X  ^% }4 }" G* d8 X: r              } else {  $ P/ \4 M$ `' A! [& {) ]' z9 Z4 _
                  str_cut($value, $maxlength);  
% n. c8 d7 C, G! @4 @) S" u              }  " m7 Y+ @* D/ V3 A
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  6 E9 [* Q- O) C' R, r* K* m& e
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
0 R( s0 H( @  A( z( ]+ `6 ^              $func = $this->fields[$field]['formtype'];  ' n4 m" N0 P% s) g: p* M- O1 F
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  4 C1 W, B- p* g2 K
              $info[$field] = $value;  / U, x$ ^0 j) W- M
           }  " x2 C) b& G% F( o; @3 |
       }  
& e6 y( m- `- |) m" X% j* o" g       return $info;  + k5 {9 [) H$ t6 v# W1 e& z
    }
* c6 v2 |" }: h! ]7 P- w7 ^trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,$ M9 u6 P8 K4 j3 e# q6 z% ~
2 a* W# h2 H3 H
再到phpcms\modules\member\index.php 文件account_manage_info函数
: c! L, h4 h2 Y3 g: s5 k0 p; h过了get()函数之后。
7 Y' h1 D8 V, N$ `' P1 o- x: O7 ~; T5 }9 r  l/ W+ K; n. W
+ ^+ s' h1 z( C" Y+ m6 ^# v% E" j
$modelinfo = $member_input->get($_POST['info']);  ' [, g& J. T# m/ s/ u, x$ L
           $this->db->set_model($this->memberinfo['modelid']);  
8 y$ e2 d; K. L           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
% `! p+ S# ?7 L- }           if(!empty($membermodelinfo)) {  $ v+ U* [, _0 i4 |+ ]' f) @
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  + R5 [) \  q( B/ {
           } else {
- A! w- K8 u$ _3 ]5 P直接带入数据库,update函数我们跟进看看+ h: H2 ^. H7 I( K

( I7 u5 d. R& _2 H& P. q4 o/ G 7 |& Q5 B  Y! D
public function update($data, $table, $where = '') {  & ~# J3 n3 E& g7 y. e% b) G
       if($table == '' or $where == '') {  
1 b# J- D8 y* \: s& [3 M           return false;  
. b) {6 h+ \" X4 ]: X       }  4 W- G* l+ j0 K" e: T9 O
       $where = ' WHERE '.$where;  
& y- ^( `0 z5 }6 b+ E) {       $field = '';  2 M# x3 L" t' B6 Y" a1 ?9 z; v5 v
       if(is_string($data) && $data != '') {  
/ v* M: ]5 t  H! J' [9 g# l           $field = $data;  - v0 b! H+ {+ @) Q* g& V0 J
       } elseif (is_array($data) && count($data) > 0) {  
" o$ E1 N: g9 Q1 a* T6 T- t           $fields = array();  
+ ~" U/ L: S3 W           foreach($data as $k=>$v) {  
; Y4 V' t5 ~# k              switch (substr($v, 0, 2)) {  
% F, d& h+ |  Q0 D7 q2 U                  case '+=':  1 I7 _/ g6 f4 d' e) f" A/ a. z
                     $v = substr($v,2);  
! Y, E' B0 P* Q; w. f/ O                     if (is_numeric($v)) {  
& q% v8 S/ U% A) z9 g0 d. _                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
& P1 ~; F1 `2 a9 q- w" v                     } else {  
$ X  R  w: J" o0 F9 Y) M                         continue;  ) N1 P6 ?3 R; k! N- T
                     }    k( K! F5 a* K
                     break;  
% v8 a& r0 ^  i                  case '-=':  ( J* \. O* l5 ~
                     $v = substr($v,2);  
: k* P6 Q: X) M8 G# L9 V                     if (is_numeric($v)) {  ! {2 [- r# r7 R
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
% l6 O, h) I9 F5 m0 ]1 A$ B( E0 ^                     } else {  
: @. f2 q0 }/ n+ ^( d+ X2 `1 e3 Q                         continue;  
& d- Y1 l2 H- J8 d$ t; w( x                     }  
# Q1 X$ e9 v! _" \                     break;  
% L- @# P2 f% I) }1 ?) w) {% v# a                  default:  
9 C4 t  P0 y) @) U                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  % |4 X$ t5 K5 ?
              }  . c9 p8 y/ x0 ~+ ~
           }  
  q7 w! v! K; P4 `+ D+ q% G" V           $field = implode(',', $fields);  6 W# q; \- h, S- Y4 i! c" |) v
       } else {  
( R. i1 k7 G8 l           return false;  8 ]- k  q( U9 H1 W
       }  , n4 f( n  R1 G9 J! N
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;    d$ g  L5 M  k" w; J
       print_r($sql);  
6 k' `2 i2 X% K       return $this->execute($sql);  
4 n  L3 _: i- w  F& i: T8 f* z    }
$ k2 q0 U$ t. G9 |从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。' f  N5 {- M# B7 m* u8 C
9 P0 H% d: a4 \& I6 J4 [
攻击测试:; Z  }% n/ U7 h) t0 z7 N2 p
测试地址http://localhost
# N7 t7 j. @( A9 f0 R  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
$ Q/ |2 a/ m) {
! [3 N/ u/ e' _9 J* B
0 P. }5 I: [3 j$ J
7 l- \9 i1 V/ N$ t' l# d' D+ _+ b4 k

5 R. \. F2 ~4 [& M* w3 |

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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