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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
2 \, r) v; O5 c, I漏洞作者:skysheep! q% i* z, p) C9 M6 U
分析作者:Seay
% j, Q6 {( L! `/ p0 V! b/ B# e博客:http://www.cnseay.com/. k1 |$ d  Z2 G& o
漏洞分析:$ _; t$ O" |0 F
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。$ Q' b% K% r) J7 G6 U9 c
7 E$ Y: b, W4 {9 e
* s* J9 y9 h6 H* o4 R. k9 X  M% Z

: c4 l! F/ g9 Z  ipublic function account_manage_info() {  
$ o2 g' v7 {& F' D% a9 J4 D       if(isset($_POST['dosubmit'])) {  , h: B# O- G( T
           //更新用户昵称  . a. c: K+ F4 J7 U; C* h- ^. r6 L
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
( T/ I7 A/ {5 B% L' g           if($nickname) {  
  ~& u, {0 e% }# Q9 J& y              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
5 I; D, }6 k) i              if(!isset($cookietime)) {  
: h' j! @3 D; Z1 ]1 O: ]$ h                  $get_cookietime = param::get_cookie('cookietime');  / n& \; T; P& q5 H, a
              }  
* p" T/ T1 c5 J              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
7 `1 b& y4 O  u2 \8 J( J2 Z( G5 b              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
3 m6 I7 u2 U. _& j( P% o* Z0 S              param::set_cookie('_nickname', $nickname, $cookietime);  " t0 n' |) ?8 j9 \, p; H+ r3 s
           }  
4 B3 r0 d3 P( p2 A           require_once CACHE_MODEL_PATH.'member_input.class.php';  
' J( y; B# G0 v0 E7 i/ j5 w           require_once CACHE_MODEL_PATH.'member_update.class.php';  5 N9 P% Z9 N( D5 D8 i- \
           $member_input = new member_input($this->memberinfo['modelid']);  1 `% l" [" o# `) @" _1 ^7 Y
           $modelinfo = $member_input->get($_POST['info']);  6 T: C* }- L1 g0 b' G
           $this->db->set_model($this->memberinfo['modelid']);  
/ K" ^+ g* H6 p           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  % b6 c. e, S9 q0 V
           if(!empty($membermodelinfo)) {  
% \3 t/ \" X  |3 P, P# ~% T2 }3 V              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  6 W0 q6 Y: j' U' N- F: g
           } else {  
$ r, M7 D  Y" q' U- y              $modelinfo['userid'] = $this->memberinfo['userid'];  8 t9 h9 S: W! P
              $this->db->insert($modelinfo);  
) x: V2 H' G* X+ d% a           }   K5 F) @  ~& Q5 K
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,) p, L0 m) l: E  |, X$ A7 z" K0 @
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ m* y/ b2 o9 q2 P( p, J8 B0 k1 s( T, [  F: j& J( d

. S) v; C* l6 n! g ( U* G( L3 G6 d% h5 ^/ y& w& J
function get($data) {  + l5 _3 J* P) O8 R) b
       $this->data = $data = trim_script($data);  
' }) Q8 ?+ |0 m! a       $model_cache = getcache('member_model', 'commons');  : v5 N8 p- g/ {( h1 }
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  ' ?# P% h7 D& g$ X; n# `
       $info = array();  
& V3 U3 ^, z& ^" K- R! E       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
4 {2 L, D& d1 `# z7 `* B       if(is_array($data)) {  
* y: O4 _$ t% i           foreach($data as $field=>$value) {  
. F8 K2 v4 s" j1 J              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
$ c4 M! i% B+ I1 N4 J% c3 {              $name = $this->fields[$field]['name'];  * Y. E6 U2 {( l
              $minlength = $this->fields[$field]['minlength'];  : ^  B+ x/ `( k9 {  P' n
              $maxlength = $this->fields[$field]['maxlength'];  
" o& y! _+ h6 e: D" j              $pattern = $this->fields[$field]['pattern'];  
( Y: Q8 P5 S8 q" l5 P2 l              $errortips = $this->fields[$field]['errortips'];  % J( k/ U- v! ~
              if(empty($errortips)) $errortips = "$name 不符合要求!";  & R# f7 e8 W3 i. l: i
              $length = empty($value) ? 0 : strlen($value);  
4 Z: e! I" U5 b0 x7 M. Q              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  , X$ b( a% j& D1 \( {+ ~! [( W
              if($maxlength && $length > $maxlength && !$isimport) {  2 n3 ^+ i, B3 ]
                  showmessage("$name 不得超过 $maxlength 个字符!");  , _. j0 {+ O# ~& b* e! a
              } else {  : b9 s9 \  C" L6 l# b
                  str_cut($value, $maxlength);  
; J, c$ A# B* |3 D; D4 J4 _8 \              }  1 x; T0 a  u' v0 e4 u1 v% a
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  5 |6 C8 }6 Q. [) V, T2 }
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  8 B! \2 i( b* N8 P6 L
              $func = $this->fields[$field]['formtype'];  ' S7 `; z3 }. I: L& M1 ~) U
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
/ `% z, ]" G% A+ ?              $info[$field] = $value;  " C& o" z3 w3 X: U6 Z6 O$ Y
           }  
, X9 T, R+ W' z       }  
; {0 t) [5 z" I5 I3 d" u. }* C/ _  b       return $info;  
0 x% E; ~" q2 G3 U    } ' d! D7 r+ l0 @. T( M
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组," U  I! L' B0 u7 t; f/ U1 E

9 b2 P9 P# c. J3 C' F% G3 K再到phpcms\modules\member\index.php 文件account_manage_info函数
) H% [- Y7 U: \+ C4 E过了get()函数之后。
) x/ }" p& u8 {0 e; S3 {7 H" o5 U' ^0 g& B. c9 r3 u5 q& x

8 X  Q5 j2 `. u- s, O4 a$modelinfo = $member_input->get($_POST['info']);  
2 V0 n/ C3 A/ |2 c% j; R& {) x           $this->db->set_model($this->memberinfo['modelid']);  5 w: |9 v& f( R3 S4 e+ j) R
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
, ?' r3 F# u4 [# }- O" L) P           if(!empty($membermodelinfo)) {    ^3 p( \& O5 U7 m( `' t6 l
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
* P( W8 j" q. e$ F           } else {
/ }. `3 W8 t' ]9 l0 i直接带入数据库,update函数我们跟进看看, V# _* [6 k0 q
​: x! w- z! M! W% Q5 B
, @* Q. V% |# n4 K
public function update($data, $table, $where = '') {  # Y9 F/ }9 L8 ~- I) P
       if($table == '' or $where == '') {  0 U* o4 N! k& H1 U4 b
           return false;  
) v1 Z: `3 Y8 I# \: ~       }  ) ~' s% b8 s0 Z
       $where = ' WHERE '.$where;  ' G2 }2 R7 `+ `% x% d( x# o
       $field = '';  3 d1 e- A' K; i+ e" R# k. O2 y
       if(is_string($data) && $data != '') {  ! i7 k- t0 W# d0 n
           $field = $data;  ) l1 o$ J8 }7 q/ S/ Z
       } elseif (is_array($data) && count($data) > 0) {  : U' J' _' y6 E7 ?5 l) C  e7 g- J
           $fields = array();  ' O( z$ {' g7 i! J  p
           foreach($data as $k=>$v) {  ! _; v$ c) Y# c5 g. m
              switch (substr($v, 0, 2)) {  
1 h0 j5 p; R0 K8 q% ]2 Q' v5 r                  case '+=':  
5 b/ B7 n8 B8 B+ t                     $v = substr($v,2);  $ C- R: e8 W5 A- e2 W
                     if (is_numeric($v)) {  
4 m  k; |. K. j  N3 W7 i. r                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  , k5 N7 f) J1 g% b" e
                     } else {  % y' ?& t# |! I7 e5 G; u
                         continue;  ) h/ b! J# f% ~4 w/ v3 E/ c
                     }  
3 Z. E9 b. Q2 e: ?                     break;  
/ K- n9 e0 @! Z  T: ?/ B; c+ d                  case '-=':  4 e" c7 T4 O- i% ~% E2 c5 f
                     $v = substr($v,2);  
% g' x) @5 a# K% p" N/ ]4 K+ [                     if (is_numeric($v)) {  8 ]# _1 Y, o0 i8 S
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
; o  R% Z# d! N8 k4 X- \) t" P/ N& ~$ a                     } else {  * G6 v3 i: E0 Y; J2 @3 {7 ?
                         continue;  
* g9 X. c; t1 a# B) k' f                     }  
; k& W6 d4 s( Y6 I1 U+ B* ~$ b                     break;  
* {7 Q) v. f9 |  O! i' A                  default:  % a: a. [. `( r7 i6 A6 H$ E
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  3 g: @* u" Y; u
              }  ! J/ H5 F$ Z0 a* m! V
           }  
  N: g7 ^; X+ ~" s+ l( {           $field = implode(',', $fields);  # C% c, ?* p- h# h# K4 _
       } else {  1 |' G& t8 {, |- ~( r
           return false;  
' F% W7 J: ]0 f- w' U       }  # k0 B9 b) b" R1 p: e$ b3 z4 |
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
- \( }0 \3 b3 x7 _6 W- N* T       print_r($sql);  
( R; o0 x1 c; c! w$ j/ L+ E       return $this->execute($sql);  / ]( I' Q9 q0 V" D) ]& B6 P$ s
    } 0 [% Q; }  d% y  M# q& H3 b
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
. L7 v( C% W: H2 g0 G/ F. Y. n( y0 X+ q. U* P, V4 C. I# Z
攻击测试:$ q! [# r& i4 d* L# e4 t* t& n
测试地址http://localhost. s: \4 ^- J* X$ G( y
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句6 `! D& f) p$ o) H
: E# k2 R1 E6 @, Z' }- }: M
% f* \. w/ a! c) ]8 B: b1 O
. I% z3 \7 q1 m  ]8 i5 \
. `/ E3 k# t3 k/ ?- ]7 n: |

: }# Y- g) j* F0 V2 D

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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