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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告) Y4 H( P5 H. l9 F* a# y
漏洞作者:skysheep7 }# r$ W6 V- Y8 A% N: B" {8 ~
分析作者:Seay% n' k" d. P1 n9 y/ S6 b/ I: W  |1 w
博客:http://www.cnseay.com/1 O% `6 o5 N2 ?3 b. Z/ z" c
漏洞分析:
1 r" z4 E+ r/ b3 R0 a; o6 e3 q  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
+ s& A( p' l5 ~2 j$ ?, m3 k! ?: ]
: b) G7 b9 t+ o, W- p; a+ O- a

7 B* t' s1 E( S! W% P0 D. qpublic function account_manage_info() {  
1 {5 W, ^7 @2 h" U       if(isset($_POST['dosubmit'])) {  
4 ?- W" o" d* g; w; s           //更新用户昵称  , x4 I; J% g3 {, C# O
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
8 E( X" D" z" l3 w& U7 i7 t           if($nickname) {  ' Q+ q! D9 h% j9 P, {3 x7 j
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  & {$ O" R6 D0 x" L
              if(!isset($cookietime)) {  
! l& q* b# T* ]4 p2 y                  $get_cookietime = param::get_cookie('cookietime');  4 `# _$ j/ p* y! e$ ]4 \# o& Y
              }  
/ ]! A7 K# U- }3 ?, J) l  L7 m+ F              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  . x! Y! G* q- ~* o. N
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  8 S0 D7 n3 E8 ]& u' F
              param::set_cookie('_nickname', $nickname, $cookietime);  
7 b. k# \  o+ Z& |& O- r           }    F0 O% L# T( K! q9 C' C
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
6 A5 Y3 L  q; T5 q1 e           require_once CACHE_MODEL_PATH.'member_update.class.php';  3 o& d+ O7 L$ I: l  ^3 j% X. i
           $member_input = new member_input($this->memberinfo['modelid']);  
0 f. `8 Z& O/ v8 i$ @, a9 N, x6 \           $modelinfo = $member_input->get($_POST['info']);  
; L* K) k+ l" P1 f           $this->db->set_model($this->memberinfo['modelid']);    e# A; p) P. r6 M# L7 `
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
% R0 |4 L: ~+ t- g+ M% [           if(!empty($membermodelinfo)) {  
" D2 \: I7 W# ]6 e. e              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  8 z  q$ u% d1 g3 A' A- G
           } else {  9 J7 V5 i8 d0 o- _# l* e, w$ T  G6 I
              $modelinfo['userid'] = $this->memberinfo['userid'];  
; ?7 \; K# R) k6 C; o- [              $this->db->insert($modelinfo);  
# K, @" B0 b: L8 I. R3 L           } ; h' }4 ^3 R/ G1 ^& |7 S* |0 Y
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,$ h7 X8 f" A+ v& Q7 `" P
在\caches\caches_model\caches_data\ member_input.class.php 文件中:# U' ~- E! g# a7 X$ t0 g, n7 N! x

5 ^: k2 w, m. x1 r/ |1 N4 F! c
7 K0 X- z3 Z6 s  _/ ?# M: A$ U5 s
( w+ m2 O# J; \$ m; {7 Z' I$ Ufunction get($data) {  
1 s- }! ^; S7 X$ T       $this->data = $data = trim_script($data);  
2 h0 k% p* [) N* Q9 q       $model_cache = getcache('member_model', 'commons');  7 L5 L7 |  V) r: U' Y" ]& Q
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
, n" H/ q5 |3 W       $info = array();  
3 L/ @, G. C& o! a- A       $debar_filed = array('catid','title','style','thumb','status','islink','description');  + p2 F7 X: h: {, [/ z) [6 q
       if(is_array($data)) {  
* i* `/ W9 k, m           foreach($data as $field=>$value) {  . U. j; C# p' i5 `  g9 {6 N2 X2 ?8 x
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
; N0 h7 A( \1 J1 M6 L% w! V              $name = $this->fields[$field]['name'];  . X& H7 |% ^; |3 A
              $minlength = $this->fields[$field]['minlength'];  $ e1 ^9 q( W8 T
              $maxlength = $this->fields[$field]['maxlength'];  
+ K+ m- Y# J/ i% b! b) `' S: T              $pattern = $this->fields[$field]['pattern'];  ; j" {+ S2 ~1 N6 K& A1 ]
              $errortips = $this->fields[$field]['errortips'];  
' d, ^+ Z7 f$ `& Q; c              if(empty($errortips)) $errortips = "$name 不符合要求!";  , }( S, P! H4 _
              $length = empty($value) ? 0 : strlen($value);  
' Z; d7 y- Y/ x5 j9 L              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  . D) T1 b; [3 _# E
              if($maxlength && $length > $maxlength && !$isimport) {  
; Q/ k/ q5 R( n+ K% {' J                  showmessage("$name 不得超过 $maxlength 个字符!");  - u* Z4 S. ]. G  h, E3 I
              } else {  
, Z0 h. m* Y' |/ A0 m8 P7 X2 K                  str_cut($value, $maxlength);  * ^2 d% \) x0 t, n% J) }0 b) g
              }  ; E, r1 S  {( V$ w
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  2 ~% C3 Z6 ^$ @  Y
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
/ H! K8 V9 W; h              $func = $this->fields[$field]['formtype'];  
. N5 y( n' ~3 a6 T+ t              if(method_exists($this, $func)) $value = $this->$func($field, $value);  6 v' q& Z  w3 D, U: h: L
              $info[$field] = $value;  / B7 S/ \$ A1 o5 N
           }  " r: C0 T+ u  _4 b) X
       }  / C2 `' R# E8 d" C, Q0 E3 ?" \
       return $info;  
& A, u& e8 T# L# r" {3 ^4 Q# a! u    } 5 W; a3 `0 J, `, a
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
- G, f  T: d; `$ B! a3 P4 t4 E. e4 W! B# H' ~; Y
再到phpcms\modules\member\index.php 文件account_manage_info函数6 e+ t( K3 `& I3 D+ G6 B0 z4 c
过了get()函数之后。- \2 ?" `+ _$ n. m
; h( [% R% z7 {. R

9 C- S( k1 ~# Q9 \* l$ i$modelinfo = $member_input->get($_POST['info']);  
" c; Y- a$ j% O. C" h           $this->db->set_model($this->memberinfo['modelid']);  0 n/ p4 _3 V6 F: c$ `( X
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  " p, A  _6 L& j; p- y9 a4 a8 ~4 m
           if(!empty($membermodelinfo)) {  " k1 E3 o( `! n
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  - I: F0 J9 @  `6 M3 V+ F/ |; o0 C
           } else { $ ]/ ], t0 {5 O/ m
直接带入数据库,update函数我们跟进看看6 b5 m1 i' a' i5 k$ i2 C
; Z4 M$ z- X" x6 c, X

& R) L& G0 n8 K6 O/ O! {public function update($data, $table, $where = '') {  . [& y( C/ y5 d# }0 z% z7 K8 |# K1 h
       if($table == '' or $where == '') {  
3 t3 X# g+ u; c) h0 Z           return false;  & Y$ d3 e/ y4 M
       }  
2 Z( G! c" ^# E# I& v       $where = ' WHERE '.$where;  
; N: P! J, |5 x0 e- y. x       $field = '';  6 ?2 r) A$ n9 F8 a, E* D: A0 |6 |1 F
       if(is_string($data) && $data != '') {  
& g$ y# t: E. `' T. Q0 r. T/ j           $field = $data;  
1 s/ r, h* @- J* n' S) p! _       } elseif (is_array($data) && count($data) > 0) {  
, \* Y9 y7 n, i. j           $fields = array();  5 h- e5 t) n: C+ X) W* ]* N/ B* I
           foreach($data as $k=>$v) {  % e. {1 T  S7 U% V' E
              switch (substr($v, 0, 2)) {  & h9 C/ [- [; b5 j
                  case '+=':  + J( T- }( H1 v/ M- y( |/ z
                     $v = substr($v,2);  ' h# A* M) I8 l4 Q1 Z
                     if (is_numeric($v)) {  % e1 s9 Y1 g7 g) A$ X/ K) T8 ?
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  7 u" m! M8 s: Z( D
                     } else {  # V: C3 W  O7 I4 M1 ^: ~) u0 V6 D
                         continue;  
. B. O6 o- d$ b2 H0 T* P                     }  
( f$ J+ U7 Y: \. L& i                     break;  
) A4 |0 T& }' O& y% e! {5 ]                  case '-=':  8 ^8 q" A( a, B/ G0 L2 T. f
                     $v = substr($v,2);  
* i9 \* W6 }- `" }5 r1 Y                     if (is_numeric($v)) {  & R- e5 R/ u# G2 V! _
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  , ?1 g. ~  j, `, ?6 p, F
                     } else {  # D: O9 W3 K& }# K6 p3 h
                         continue;  
1 K8 d+ D9 ^. j' U, C                     }  7 V2 o' w' g' k# Z3 ?
                     break;  % Q( ~9 }2 s  s' \
                  default:  , z$ a2 {; F' c6 F' o' F7 ?9 n4 |. f
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  : [3 b1 u2 K% p! G+ p, R
              }  
  M& Y, g# D, w9 q6 J           }  
3 @+ D2 i8 g; o           $field = implode(',', $fields);  9 M/ G1 o) Y% I2 i+ U
       } else {  2 x# Z% z/ i8 I! h
           return false;  
" i" E* h. n. \" U; c4 ]       }  
6 R4 l1 Z3 H* R1 `$ c3 d4 _/ h       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  4 U% `  K( }, D( _- o; H. e
       print_r($sql);  
% C! S5 F/ @8 `3 M6 X       return $this->execute($sql);  " H1 H- i6 F4 g" e  T  l' N' K0 r2 u
    }
: f' l4 m& I- [! V# @从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
# ]& C) S1 \# z+ T: d0 e' ]' X8 T9 A+ s/ j
攻击测试:% d* D! {8 I# W1 m7 I) ^- }" _0 O
测试地址http://localhost
# k0 {. r% w) e0 f$ C. d  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
& v) D% C- M( A# ?& G: I9 {
" b! `/ M3 F; w. L# u4 \" R1 l& \( R   E8 w6 G& h9 o  b* m8 W
* w$ B' g0 W8 w  R
0 s' X- }' v8 s! z5 `
! N3 I) Y0 d" m& H

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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