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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告) }; Y5 r; X5 F$ v% V( y+ ~
漏洞作者:skysheep
# r6 v6 {" {, A7 Y+ h0 ]* {2 f分析作者:Seay
  n) S9 m6 \) W1 h: ?博客:http://www.cnseay.com/6 _9 a2 s! s7 Q: ^
漏洞分析:' U! Z. @3 _. p, j. e
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。$ d( I# |$ q5 X- L, E  \

/ P* G2 a. M' q  h7 U. `) J! l; D& q, q- T

) @, f4 c  ~2 R- L" `7 lpublic function account_manage_info() {  " ~4 j  k6 b9 S5 X$ J
       if(isset($_POST['dosubmit'])) {  & s; i- i6 `5 i0 E
           //更新用户昵称  & [1 i3 ^/ P0 e# Z& h) j! \, i
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  ' S# w1 ]: }# k+ q) i4 v% v/ x
           if($nickname) {  
# X: y- L, D% N, d) f              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  " ~! S7 o& W, {: T
              if(!isset($cookietime)) {  4 E1 M; ^" c+ S8 u% e
                  $get_cookietime = param::get_cookie('cookietime');  , l' r; G/ O& V: S8 |
              }  
% U: a# _& z( u( \4 V9 ^              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
& p  ^0 S; e$ L, U! n3 p              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
1 x, e9 c6 K* j( n! A# U: g              param::set_cookie('_nickname', $nickname, $cookietime);  
0 E" f: ]' |, L0 m           }  * g5 t5 v3 I. s' r
           require_once CACHE_MODEL_PATH.'member_input.class.php';  $ u, o$ w3 u% D: _- T$ o
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
/ L" n( o( w- T9 n0 I; }           $member_input = new member_input($this->memberinfo['modelid']);  1 ^" S& R7 Q8 i+ C/ I
           $modelinfo = $member_input->get($_POST['info']);  
8 b# x/ M- v5 Q6 n           $this->db->set_model($this->memberinfo['modelid']);  
7 l" @: m! `$ D' i: S           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
) M3 }0 \9 t+ _  K5 j  E. B           if(!empty($membermodelinfo)) {  
/ P0 W' F% q7 @# ~/ M              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
. q& w1 Q2 a  g' n0 p0 C3 A, O  I           } else {  
1 k0 K3 E! J! W              $modelinfo['userid'] = $this->memberinfo['userid'];  
4 s$ ]+ ~" N; C0 Q, |+ l              $this->db->insert($modelinfo);  
: Q- _) D- k- M6 [0 \- p  i* q4 W           }
0 R* G( y. f. j9 O4 s$ J2 U) A代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
7 ^) M! i4 U* a在\caches\caches_model\caches_data\ member_input.class.php 文件中:
% A* B' e( ]4 N; W' d) C. R: {* ?
3 b  H. P; |1 h" j

( o) x4 J: Q* Pfunction get($data) {  - V; f% m" ^6 g. k9 p. w
       $this->data = $data = trim_script($data);  
8 J1 s3 D, q5 y- y2 H5 z' U       $model_cache = getcache('member_model', 'commons');  / S7 f9 C% L; T
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
6 U6 B6 A( w& V9 U4 m1 `       $info = array();  * u4 K) \3 ?% b6 [& K
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
+ n5 n0 H& n  G  K6 Z, u- L       if(is_array($data)) {  4 V9 J; o8 n" ^7 I/ ?+ A, o4 C
           foreach($data as $field=>$value) {  
8 u2 Q1 F9 t+ T# j9 L              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
' f. T9 V2 V  a0 w; j9 B7 i              $name = $this->fields[$field]['name'];  8 m, g  u1 d% c" y
              $minlength = $this->fields[$field]['minlength'];  
! \3 I$ |9 p6 q              $maxlength = $this->fields[$field]['maxlength'];  
2 b$ G, C2 X3 Y0 p5 I              $pattern = $this->fields[$field]['pattern'];  1 X3 i+ h# c* \! q  L9 _3 a7 z& h
              $errortips = $this->fields[$field]['errortips'];  2 M6 t, y0 o$ z& ?* V
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
  [* b2 M- W3 R  i( P              $length = empty($value) ? 0 : strlen($value);  " W5 r3 H3 [7 _# y
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  3 t# d8 y6 K% t+ A+ A) x5 ?- r5 }
              if($maxlength && $length > $maxlength && !$isimport) {  - [! _3 ?4 y0 F  \! [. R
                  showmessage("$name 不得超过 $maxlength 个字符!");  * h' B' E+ \% J6 W+ {& s
              } else {  
8 d+ V1 q. u" I                  str_cut($value, $maxlength);  
, X( ?3 ?6 S0 u- F) k/ U- w              }  4 C  o" j# g2 O: F5 S  d
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
% a2 ?0 f* U# C' V% w6 U9 U                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  ) L8 m$ @; p5 \* c0 v) v
              $func = $this->fields[$field]['formtype'];  
1 o2 i9 |6 s1 N' F3 Z% a) E+ u              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
& D2 _3 P0 B4 _; b: l: o* K              $info[$field] = $value;  8 w$ y$ g' h' {2 r
           }  5 L; ~( O/ Y$ B  f2 V( N
       }  
+ k0 }' p; p' @& z% l% K       return $info;  : u+ ^2 x6 K: P; q. a. c
    } ; c2 V5 X* C. @1 P+ S7 `
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
0 u6 A2 {- w) S- h9 |, J& P; M. K- w  r( Q5 L5 |9 l  i+ M" x
再到phpcms\modules\member\index.php 文件account_manage_info函数
! V: v; Z* q6 Q/ K& K  s过了get()函数之后。
; u! Z; [& m/ c6 A! J/ O: Y0 l' e" d! u9 R5 c. R
: d: W# I2 `- W* ]1 W
$modelinfo = $member_input->get($_POST['info']);  
( M0 F0 {6 E2 ^; T7 L           $this->db->set_model($this->memberinfo['modelid']);  
5 d0 E! u$ Z# l( C3 \           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  ' T6 d; {8 _" T, F7 v
           if(!empty($membermodelinfo)) {  ' @$ C" l; G- s; _
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
+ I* j/ f6 m0 `; ]; n; z3 q) T( k           } else {
( a3 c7 [+ n) a/ d( ]0 D8 }$ `直接带入数据库,update函数我们跟进看看
+ w  o" G  ^% l& f& N/ r7 d5 o) T
% Y: ~! L! `- D  @ $ e. v+ L4 c& O$ V: v( x* K
public function update($data, $table, $where = '') {  ! B# r$ X3 q5 h' m
       if($table == '' or $where == '') {  7 Z2 `& K" s( x
           return false;  
) a/ _- {; m1 ^* [0 v) l; z       }  9 b1 x! |% L% O1 b% v2 O+ S5 C
       $where = ' WHERE '.$where;  
/ z! w. r5 F2 s3 e       $field = '';  
& M3 O' L3 h9 _. z9 X  F       if(is_string($data) && $data != '') {  ) ^/ w; E, L: S/ c3 k' N3 x
           $field = $data;  ! a7 T: n) \2 x6 j( G
       } elseif (is_array($data) && count($data) > 0) {  ( U! l; m. q; _# x- C6 v
           $fields = array();  
9 n: J; _% b% r           foreach($data as $k=>$v) {  
7 E$ t6 R& i  e  d6 b& z9 E6 H              switch (substr($v, 0, 2)) {  
. l/ V- f& e4 [5 p2 H- E; w" Y                  case '+=':  
  t  o! c. c8 n% X9 u& t7 N! G% y; e                     $v = substr($v,2);  
% l+ A$ O1 P, q9 U                     if (is_numeric($v)) {  
2 s- P; M7 a; O! V                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  7 M  |  O7 L" x' j  X$ @7 I' N7 M$ T
                     } else {  
1 X% u" _. z: q  u, I! m                         continue;  
  I7 S! E6 B, ?                     }  
3 A9 u. K* l& b9 j8 q4 c/ ?8 s" O" @                     break;  5 \, u. q2 e! y" x1 k
                  case '-=':  
1 o! S, A; E# d. ~6 c                     $v = substr($v,2);  
& k8 o. J4 N: E  E, \                     if (is_numeric($v)) {  
" f. F8 ^( d% ~. U8 ^& e                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
0 c: u4 H" L2 p7 l- X                     } else {  
, T8 U- T. H: p$ b. V* K                         continue;  
8 q. K! O( Q: l) p' `                     }  
" H7 Q1 J4 t. f                     break;  ) J9 z: [0 l; |% B: k6 q' e! Y
                  default:  ( I& W, [* m  u9 D; ^& ?
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  ; t. g% ]' I5 n' f2 T( e3 z$ x
              }  
7 H+ ^9 |* n# k& o7 s9 [; d' o5 q           }  
/ q+ O$ z+ @$ V+ b           $field = implode(',', $fields);  
/ G$ E  s4 x) m; z6 e! z) d( L* l       } else {  
7 P8 k6 w2 F9 C+ W           return false;  
1 L: u& D: C" A" t9 Z* ?1 m8 \' y" u       }    _# G+ i* @+ @2 m- W' y
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
( M( }* X' M4 G. v9 k' D* L       print_r($sql);  ) [# h3 v$ p* Q" |' f! O
       return $this->execute($sql);  
) b' M/ c2 p+ I5 N7 v* j    }
. g! k" @9 t9 I5 }从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
3 V6 R; `) T) k/ \# @: a) Y! E4 g* G1 y$ \7 B$ b) s
攻击测试:
+ w0 U* l0 r% \  W8 G/ C测试地址http://localhost
# Y: l6 N/ m+ U% `8 R! b  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句# J- R; D8 h$ D/ y$ F
/ c- W& K4 `6 [$ V# N/ o$ i1 N8 e

% ^6 |- u4 y$ `+ C% D, O( i, X- G' ~) r
& X+ A% i; g: T0 Y) d  W

  {  }$ r9 ~& O- [" K

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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