找回密码
 立即注册
查看: 2546|回复: 0
打印 上一主题 下一主题

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
! x; ~8 \( B5 L( n, {% u+ B9 A漏洞作者:skysheep
7 C' g6 j0 R# {% z( q9 T分析作者:Seay
8 p( {" {6 w6 d) [( F博客:http://www.cnseay.com/$ b* Z/ S" Q$ T, Y
漏洞分析:( w, _( k  d  C& P0 f+ @3 U' D
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
6 a4 P2 d$ ?" ]+ A6 D( z, R) C( U5 g8 {* N% A+ h% r- W$ p
3 w$ l6 S* u3 a% d$ B
5 A* O& u/ e, p' N" o3 Y' J0 n
public function account_manage_info() {  
  V( A+ @9 Z7 L& h8 f! z; \* t       if(isset($_POST['dosubmit'])) {  ; X& Y# B# L  ^5 T1 H$ ~' ]
           //更新用户昵称  % e5 v4 o" @2 a) q8 G
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
: P! v( Y" D3 ?& Z4 H- ^           if($nickname) {  ( G% b" _& s8 m
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
4 y0 g6 p' P6 t; [9 T; w2 ?              if(!isset($cookietime)) {  9 v) O# w9 _' H) r8 x, c
                  $get_cookietime = param::get_cookie('cookietime');  " h( ~3 y+ D* ]  }2 `1 T5 y/ R
              }  ; F9 f: c6 U" B9 w8 z
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  ' ?- V' w$ E0 z; o3 V% n5 l
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
8 I, `1 |8 W3 B1 n% Q8 l4 V/ p              param::set_cookie('_nickname', $nickname, $cookietime);  
& o% U) g" y0 s* L2 i           }  
6 T! H& C* j/ g& f4 `1 n* w           require_once CACHE_MODEL_PATH.'member_input.class.php';  
% ~1 T/ ^/ t, Z           require_once CACHE_MODEL_PATH.'member_update.class.php';  * y& t4 I0 X" [* Z1 d- e! Z( V
           $member_input = new member_input($this->memberinfo['modelid']);  , j9 E5 A# V% I( H8 `
           $modelinfo = $member_input->get($_POST['info']);  
/ u7 h6 R) T" O8 P2 A" K2 D: y, w5 g           $this->db->set_model($this->memberinfo['modelid']);  
5 q. @1 T! y$ ~+ ~           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  , n' W4 b8 |) \! F6 i
           if(!empty($membermodelinfo)) {  / j" f2 i+ K' M- r' ]; D- p7 L
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  5 L8 H2 X* T; L: l7 Q6 @: k
           } else {  : T: J7 o0 C" u
              $modelinfo['userid'] = $this->memberinfo['userid'];  
- a- x8 h" o; Y5 S+ p" v              $this->db->insert($modelinfo);  
/ r! x+ Q. ^8 P           }
2 k' B3 c; v3 \2 w代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
1 x$ y4 Z" h0 b6 K+ ]在\caches\caches_model\caches_data\ member_input.class.php 文件中:
& g4 `& X& X6 e" l0 K" T
$ J$ s' N- U4 S6 Y# K" [  E$ F# f
, T6 G; a- X7 o& G" f; x% ^
* P6 t, }0 k- Z, ^) @8 Rfunction get($data) {  
9 ~; Y5 T5 z  d' H& T( q- Z2 [& y       $this->data = $data = trim_script($data);  " Q# t; z- c) r
       $model_cache = getcache('member_model', 'commons');  / x! ?0 r' {/ j2 B9 ?  @
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
  H) g, ~, V" n7 Z& p       $info = array();  
3 n0 {) c0 L3 X0 f0 M- n% n; }       $debar_filed = array('catid','title','style','thumb','status','islink','description');  . _" |; L: x. x- P1 C% l9 S
       if(is_array($data)) {  ; b* ]/ Z8 C$ I
           foreach($data as $field=>$value) {  
5 }) m8 }* H4 }1 G5 o5 u9 A: z. e              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
. C+ R6 o* T. D' l( {* [4 k              $name = $this->fields[$field]['name'];  
3 K4 g' N% B$ \2 b. O              $minlength = $this->fields[$field]['minlength'];  
  u# k! a4 @+ G% j0 d              $maxlength = $this->fields[$field]['maxlength'];  
& R6 L- D8 R0 n' o+ E, p              $pattern = $this->fields[$field]['pattern'];  7 A7 f6 [; Z0 a: Y9 Q! O- C
              $errortips = $this->fields[$field]['errortips'];  ! M* C' h1 D; x
              if(empty($errortips)) $errortips = "$name 不符合要求!";  ( _) ~5 b9 L6 D" r4 d5 q: ]
              $length = empty($value) ? 0 : strlen($value);  
6 u" }# u" ^2 ?- ~8 o! L: i. m              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  ( W0 z. x3 E0 B7 B# Y) \
              if($maxlength && $length > $maxlength && !$isimport) {  
! N( l+ x6 |0 e; F' a. b                  showmessage("$name 不得超过 $maxlength 个字符!");  
9 e2 C4 u( M+ p8 k9 F" F              } else {  + R/ v3 K8 L4 |0 G3 r
                  str_cut($value, $maxlength);  
6 S$ B8 [! W, d( t3 Y7 Y# @              }  0 \* A/ [8 o. o( ]9 y
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
! R: Q& F1 `- X6 ]4 l1 m* j                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  % Z4 j& n9 O1 {9 U- P) c: v
              $func = $this->fields[$field]['formtype'];  
. v7 C1 _/ g, z# [! P; U              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
: F4 `! j3 K( }0 m              $info[$field] = $value;  
2 b3 B, {" O; H           }  
3 A6 Z# e2 r0 M$ ^! G! \       }  
) O; h4 @0 o5 X       return $info;  9 [6 {* {, {, t' y* c
    }
4 o9 p6 P4 P" l7 N/ G+ K) o1 Strim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
2 J3 Z4 N1 `3 O7 j, `7 s5 d! j: e* w- ~& ^! l; M# I
再到phpcms\modules\member\index.php 文件account_manage_info函数
' C- r( }9 T. I0 x1 g) S- n过了get()函数之后。9 `/ D  m9 w* w$ `' q5 z+ r
6 \( r' T; h. m

% H) _  |/ w. g: D# ~3 M. O$modelinfo = $member_input->get($_POST['info']);  
; K. F$ r- L! M' ~; c           $this->db->set_model($this->memberinfo['modelid']);  . j: e/ U9 ~2 c1 n; d- A! `
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  & Q/ }( `  F) z& d4 x+ X4 j2 c
           if(!empty($membermodelinfo)) {  : j$ [' X- B3 z9 a+ ~/ Q# Y
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
: H% ~# d) w8 K% A; h1 P) D           } else {
' Z; V  ~, p  p% X直接带入数据库,update函数我们跟进看看% p) I* W# `5 x+ |! D8 w

/ K3 n' b# f' E: C: ?# }+ a$ E % M9 n7 {  P! s4 @
public function update($data, $table, $where = '') {  + Q8 K3 y& h1 \* s8 @
       if($table == '' or $where == '') {  
. L  i7 [, C6 t- i# }           return false;  
+ q! u( U- r* F. X; C7 ~4 g. x       }  # p: Y8 Z' a; e: w7 L
       $where = ' WHERE '.$where;  1 k5 J1 ^% m0 H
       $field = '';  ' p% b& H/ @# W" b9 g
       if(is_string($data) && $data != '') {  * u* R* e# a# T3 D* D$ \
           $field = $data;  % `/ k" T& t" \3 a0 w* T
       } elseif (is_array($data) && count($data) > 0) {  
0 S$ o2 P" r: |           $fields = array();  
9 ]' w0 `, w9 B4 R. y$ e, ~           foreach($data as $k=>$v) {  
; E9 S. {" F5 L; E  h              switch (substr($v, 0, 2)) {  
) q0 a! t" q+ E  l% w4 V2 W                  case '+=':  ; p9 b: E. ], H. S* R  `) g* W; e
                     $v = substr($v,2);  7 @5 a  K. `; u) m; K, H$ @
                     if (is_numeric($v)) {  
4 l$ q% C6 {4 b                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
9 d7 t4 y9 p0 S2 X, E( g                     } else {  
! V; N9 F2 `" _3 ~$ `                         continue;  
# k$ u9 l' D' Q- z, q. J* Q& K                     }  
3 O- M4 s+ U8 Z4 ~1 @                     break;  ) ?- t4 g8 [0 ]" V0 n( w1 @5 z! C
                  case '-=':  
! i8 s) k) t! K                     $v = substr($v,2);  
1 v4 ?$ o9 u/ ?                     if (is_numeric($v)) {  
8 \, I' }3 `  j4 d0 u( L/ W$ z                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
. N1 U" {8 ], L$ e/ J' k                     } else {  
0 P2 j2 u. X  H( K                         continue;  2 ~9 Z1 Y# Y# z6 r1 v7 Z
                     }  
; M2 h) a( m; V6 t                     break;  . G2 i, ~5 v3 q$ ^# M' |
                  default:  
2 V" r4 l  p$ l' |                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  1 H; ?' I# {' H# J2 g
              }  # [: N8 k# N- H
           }  
! O, o9 e& \* Q; o8 L2 E           $field = implode(',', $fields);  : P- ~. C4 B  X! f4 I
       } else {  
' U$ G$ @5 E  Y7 F: O( x# v           return false;  ) `5 D* _! B3 o  f
       }  
- |2 A: Q) }0 E; _) Z8 ?1 T       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
" u# y; P8 ?2 h9 \4 |* G; c9 w- R1 A       print_r($sql);  
6 q+ w% Y9 z5 q3 T& {& s       return $this->execute($sql);  # }) b. r% L/ s7 Z9 W
    }
" x% _9 u9 ~" }/ j  J! A  l6 m从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
: J! }' R7 f) r9 H1 i  C4 ~- z+ [6 w+ Y. |5 v) l6 j# n
攻击测试:; q) F; i. y4 g1 _+ b8 C  @1 z, r
测试地址http://localhost9 B) C' m( f) ]9 M
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句" H: d* Y2 v, w% S) {! M7 `

' r9 i; b$ P  e* ~6 W3 ^/ z % M: j( |0 _7 a' V1 U& M" o( Q

! h" F9 X8 r0 k9 b3 w( `$ W+ B0 B
9 e! v3 N5 Y0 i% z, y
' x- M' o$ D, J+ ~2 v5 J

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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