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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
- W4 ~. O% C3 d漏洞作者:skysheep" r0 E( ~8 Z6 |! C
分析作者:Seay
& {1 G: k8 [# d7 l" y4 r. d博客:http://www.cnseay.com/  b! x9 g7 y$ \, p
漏洞分析:
* ?' `* ]" T9 H9 f! r$ q  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。; c4 i7 X/ e) N6 P( l

, Y" Q6 @1 g7 e+ ?
1 z# Z6 ^2 b% Q, @4 W9 z & U" {" X" S, S7 K' ]
public function account_manage_info() {  
" m$ u, s! F# l2 V2 M       if(isset($_POST['dosubmit'])) {  / n4 ]$ S2 x2 R9 f* i
           //更新用户昵称  
' _6 J$ G) z* x8 i% j* y           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  * D7 \9 O, C$ F, i6 a5 x# a& m
           if($nickname) {  % [. e  L% q2 h" R3 k
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  ' A5 w! f! m( J, v- ]. z( o
              if(!isset($cookietime)) {  / y% }3 G& P9 V
                  $get_cookietime = param::get_cookie('cookietime');  
# d! U3 n3 r' {2 Q; K              }  6 `2 K; J9 T/ q! n
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
* f  X- F3 z( {: ]. a* T+ ~, k              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
& n6 D3 t$ w- s& h8 u; x              param::set_cookie('_nickname', $nickname, $cookietime);  ' E4 ~' n& L) c
           }  
& S: Q$ D1 c7 N( h* S8 g. J* a           require_once CACHE_MODEL_PATH.'member_input.class.php';  - C9 u+ C* k* V5 L
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
" u7 C- S3 P5 q: `           $member_input = new member_input($this->memberinfo['modelid']);  ) n$ P, X4 W* y7 n* \. i( U
           $modelinfo = $member_input->get($_POST['info']);  ! Y# y! |1 t* U/ e! B
           $this->db->set_model($this->memberinfo['modelid']);  
4 E: ~& y+ k1 j3 t* H7 {           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  ' C5 s6 I* T4 A' r$ h+ B' w( N3 ^. L
           if(!empty($membermodelinfo)) {  & G  p) s, r5 I* i3 @* N* c2 o
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
) ?& s9 @- y3 m: D/ l4 _: \) T           } else {  
6 F' k$ B5 ~$ b/ P              $modelinfo['userid'] = $this->memberinfo['userid'];  ( y" m4 t* J4 N( x+ ?/ T$ U2 i
              $this->db->insert($modelinfo);    ?$ }3 m, L! T8 }( j8 P
           }
3 ?7 C' J5 X" z! N: Y9 Z* t代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
2 z! G* W$ o  D/ ^在\caches\caches_model\caches_data\ member_input.class.php 文件中:
% a; l7 s' ~$ i4 ^+ K
8 ]3 R' N' d" R) T, c% U8 r2 _2 i) f( A2 S
& ]1 Q1 y! \- k/ Z5 w* M
function get($data) {  & Q% ]) J/ Q; \* B' E+ M
       $this->data = $data = trim_script($data);  
! [2 k$ t) \8 V       $model_cache = getcache('member_model', 'commons');  7 g+ S* V' P; a- y# E% W* P: m/ w
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  : O4 X# H6 S( c0 y5 v
       $info = array();  3 ^/ ?7 i' y! I$ G
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
" l% v/ I* T6 i9 U/ D       if(is_array($data)) {  3 N& X3 A# @. R1 P
           foreach($data as $field=>$value) {  % [; T+ k* K, R0 H# T* V# z
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  6 t; N7 @% p# \  O4 X
              $name = $this->fields[$field]['name'];  
/ Q- c3 w' d' z              $minlength = $this->fields[$field]['minlength'];  
$ S) H) ?. X% |5 [- n+ ^8 q              $maxlength = $this->fields[$field]['maxlength'];  
9 @# R, O/ R: i& C9 e              $pattern = $this->fields[$field]['pattern'];  
9 s/ y. [( }6 Z1 j1 e8 S2 \8 `              $errortips = $this->fields[$field]['errortips'];  
- E/ [/ ~) ^5 o' o. C7 {              if(empty($errortips)) $errortips = "$name 不符合要求!";  
5 h) s% O% T! R! T: x( N              $length = empty($value) ? 0 : strlen($value);  
8 G  d. i2 p8 L: V              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
$ d- z3 \: T0 W; U              if($maxlength && $length > $maxlength && !$isimport) {  
+ [, y$ G+ V/ X% h1 Z                  showmessage("$name 不得超过 $maxlength 个字符!");  $ }1 Z( c; z/ @0 Y5 p4 V# ^' I
              } else {  
( |" |+ c+ l8 q1 d6 i' z                  str_cut($value, $maxlength);  
5 y5 ^, w& Q5 O# v/ h- a! s              }  
/ Q0 X9 d, z1 i5 H' c& w  [              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  : o" B. H4 O5 U- U* N5 [
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  : l* F* c& s1 [; |* q% b- U
              $func = $this->fields[$field]['formtype'];  3 x" ?# ^% W3 h* f" u
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  7 m# J% `. b5 `/ \
              $info[$field] = $value;  
) b* r# s* k$ ^5 a: Q0 j3 g* y           }  + k: f$ @& {9 V! L% [7 c. n9 y
       }  . l2 O% Z3 ^" ?9 h3 s2 U
       return $info;  
! c7 a) n2 d) K4 ?8 {' Q5 F, V4 W" k    }
* o4 {7 a6 X5 r1 A5 utrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
& n- a1 v& |4 j& G
/ I" P4 v6 {# n& }. K; i再到phpcms\modules\member\index.php 文件account_manage_info函数! [1 W2 N3 ?% ~3 v& {
过了get()函数之后。) H: K  I/ m( I) Q" X, H- e

: V. R% l) R1 |6 c; M5 w + H6 H+ X- m) ~. G* E6 Z6 q
$modelinfo = $member_input->get($_POST['info']);  : C7 w: d; |: j& y! r
           $this->db->set_model($this->memberinfo['modelid']);  
* y3 ^* ?; R& ^- a- t0 S! \) q           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
, e, T0 p3 k* o4 U           if(!empty($membermodelinfo)) {  + _, Q9 C& Q- F
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  $ u. e3 q- k2 _7 V8 u
           } else { & m4 Y  r& q! H4 D
直接带入数据库,update函数我们跟进看看
. j, F  s* x* H0 w) P
0 g8 l7 e; R0 o3 k 7 J$ n5 k/ l+ e/ [
public function update($data, $table, $where = '') {  
& m; ]. R; w% e( L) F: E- D       if($table == '' or $where == '') {  
6 ^  n3 _; K! O8 l% |, I6 _/ T           return false;  
# r  x- w. j# H. _8 i3 j       }  
% T, g! j6 r4 i+ E3 j* ^- J) e       $where = ' WHERE '.$where;  
: r7 _; {5 d7 B6 _       $field = '';  4 k7 Z5 I, J* L& O  t
       if(is_string($data) && $data != '') {  
) j9 |; {# h3 b# h9 X           $field = $data;  * q/ T: s8 m9 x% p4 D
       } elseif (is_array($data) && count($data) > 0) {  
& X7 A1 }3 Z$ }$ V, T7 ~' |           $fields = array();  
5 ]! c7 G* I7 p; t9 K* l6 |& M           foreach($data as $k=>$v) {  
2 _# O/ w* ~; O$ B3 o6 _7 R8 ?0 r" \( g: F              switch (substr($v, 0, 2)) {  
* a& d) h5 c$ H/ U                  case '+=':  , M  C' a# v1 U0 }8 A
                     $v = substr($v,2);  : P& ?2 ~) I6 C* q, G6 z4 |+ q
                     if (is_numeric($v)) {  # S) w$ {2 E  N. d) E1 o" V
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  ' a; c8 D8 F8 }. p. ~8 y
                     } else {  1 t' _; Q: |1 l, \3 O0 A
                         continue;  
, N* q& Q0 C; k1 r0 w! }- m. x                     }  ) z' e: M! F% L# i" H6 F
                     break;  
' ^" X( j0 i! }9 s/ i                  case '-=':    d  C4 ]7 r4 k5 q( G% q& A( M/ p
                     $v = substr($v,2);  & u8 R9 T0 V' `
                     if (is_numeric($v)) {  
9 R. o' ^  ^8 ^' I                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
. z. f; r5 P1 A  z  ~                     } else {  
3 R& Q0 m8 N' W) L# p: y, s1 X                         continue;  ! v  H+ ]- z. q
                     }  4 V/ `; A4 j' k8 p
                     break;  ) ?/ T# D6 ^: v
                  default:  
( c! m, u0 t6 O, y3 M% k# Q: s                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
+ }% E: E( x7 p; a$ Q              }  * A# a6 r$ w8 W3 W" e) G4 C
           }  
0 G* b, {6 Q- {6 \           $field = implode(',', $fields);  
/ D- C! j7 W& |       } else {    o& A( F! P) Y9 N
           return false;  / e( c, Q0 _* U8 h
       }  * o; X: Z* x. T  B
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  0 v4 d! U8 q+ v2 [! f( E& D8 }
       print_r($sql);  
  ?5 Z% [  N/ z) r$ y) }       return $this->execute($sql);  ! h, q2 q1 P: D
    } 2 V" ~* r2 S% E+ }
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。+ K+ V3 U$ r* u1 l  S* V

5 k5 F6 B& K) e攻击测试:: v, R( `8 b3 Z( }
测试地址http://localhost+ k1 a, N) d1 \/ ?  ]0 K# W
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
% j; m" o' k, t! ]) c( m3 |: h. J! g3 R" _- c' W

% V- u9 z6 d9 P7 ~5 t# N/ W. R8 B% A3 |- E- k! i

2 L- Q1 X' Y/ V* e# n5 ]) N* {  v, s1 w7 X4 s6 G+ b) q

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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