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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告* z0 _9 E, j% q1 c1 ~5 R, o  Q, x
漏洞作者:skysheep+ d% I( w- e) Z; h3 C
分析作者:Seay
4 l7 \& z3 ~( T1 e& z, b( o' O博客:http://www.cnseay.com/+ ^$ K3 O- Z; V9 a' r5 A
漏洞分析:
# r8 j, B  `( H: ~  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。* |, U5 E- }3 H* S

" w9 D3 I5 U  m  P9 M3 N6 P( i
3 p6 }9 |2 g+ q# Z0 s
$ J. e0 _% X1 @/ T. cpublic function account_manage_info() {  
2 Z  Z8 T  @1 p- B' z       if(isset($_POST['dosubmit'])) {  
" b7 ?1 m- ~5 F1 y: O0 o           //更新用户昵称  2 I. l  @9 J1 N" D0 z/ d( T
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  ' Y( r2 Z5 |% [2 G; z( E% d
           if($nickname) {  
, c4 B9 a9 B1 K              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  0 x% s: o2 z/ N# e% s3 M
              if(!isset($cookietime)) {  . g3 n' i+ x9 q* t% N/ n
                  $get_cookietime = param::get_cookie('cookietime');  
1 }' L, l% o/ l              }  , Y) E! |! G5 f# U3 o# C3 j; a
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  / J6 K4 K: U6 n2 Z9 Z9 n) Z" O
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
4 o. Y7 R7 M) K3 B/ \: [6 C              param::set_cookie('_nickname', $nickname, $cookietime);  
. K; U2 v4 e0 C$ X) c% ~; J           }  
6 N5 j3 b  T" h7 a* e8 R9 ^: K! l           require_once CACHE_MODEL_PATH.'member_input.class.php';  
' Q' k+ r1 E. T9 V           require_once CACHE_MODEL_PATH.'member_update.class.php';  / K4 b1 X. W$ ~9 p( u0 }; d
           $member_input = new member_input($this->memberinfo['modelid']);  
% u  o" h, |7 V* j7 y; {           $modelinfo = $member_input->get($_POST['info']);  
* X1 \  ]/ t- {# u% @! L4 W( E           $this->db->set_model($this->memberinfo['modelid']);  6 d: f+ b$ S$ q5 t
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  : Q# j% o: W4 C6 I# N
           if(!empty($membermodelinfo)) {  , [8 w& y: s# D6 T3 \+ H
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ! x* G! _/ h$ B/ m
           } else {  
8 a; m. Z' Y# k% ]! u8 [2 U              $modelinfo['userid'] = $this->memberinfo['userid'];  ( u$ n) [$ w3 J. C1 B2 _: n8 e7 X
              $this->db->insert($modelinfo);  
. k/ M6 A/ j0 E# R           } 1 ?( o0 Y. K' `
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看," \! h% _  W* ^
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
6 m% R0 ?* u  U0 y
7 F  a! Y. O; I5 r" `
* u% O% ]% g2 y3 P
* o, \( Q/ F& a  hfunction get($data) {  
1 K3 I' \9 J. N' v* @* c       $this->data = $data = trim_script($data);  7 ?9 B; b" B' M8 c8 y8 M# a
       $model_cache = getcache('member_model', 'commons');  ( o$ u: g. |2 W1 ^; z
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  % w& Z; P! a& J! T; q
       $info = array();  
' j- y0 U9 W( n" T       $debar_filed = array('catid','title','style','thumb','status','islink','description');  - }4 `  N' d9 E4 n7 W( w; v. c
       if(is_array($data)) {  
: t! {- K+ j. r4 Q" H  Q           foreach($data as $field=>$value) {  + v. q5 L; |$ Y+ Q5 G) z
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
3 ~* I9 r" Q3 o              $name = $this->fields[$field]['name'];    c1 ?- `7 Q  V+ N5 V
              $minlength = $this->fields[$field]['minlength'];  3 b( D: q/ j8 R5 W* [# W! `2 ^0 x: N
              $maxlength = $this->fields[$field]['maxlength'];  & v4 v" u4 X4 r- K% y
              $pattern = $this->fields[$field]['pattern'];  - v" s* R1 w) S0 S7 z  h" l( d
              $errortips = $this->fields[$field]['errortips'];  
3 H: Q9 T" _; M# {1 Q- y8 z              if(empty($errortips)) $errortips = "$name 不符合要求!";  
' J! Y) R- c7 y5 J. q              $length = empty($value) ? 0 : strlen($value);  
; D5 {- {1 j& H1 _  M              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  / m3 s6 h; B; a& y$ z# ]5 t( ?3 b
              if($maxlength && $length > $maxlength && !$isimport) {  " B9 i' a$ g9 v( b5 \9 {/ [  b
                  showmessage("$name 不得超过 $maxlength 个字符!");  
9 V; C4 Q1 E% }9 ~- a+ F% N              } else {  
3 i& ~7 r3 `% y1 e                  str_cut($value, $maxlength);  
6 O5 U5 o& y+ S7 T              }  
5 g# x1 Z3 _  v" T  k% h$ \1 g              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  " S9 R* F& W& ?" C# U/ X- Z
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
' H! m/ r& N" T/ U! b* A+ S  D  b              $func = $this->fields[$field]['formtype'];  
: q) J! r* [* T! x4 [              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
& u1 `# }2 b. O4 T% O              $info[$field] = $value;  
$ w: p6 b9 |8 S: r. v$ D$ Q1 h5 ?           }  
* [- y; o$ p2 Z5 A: n! Q# ?       }  
; A- V/ t% B7 r% T% `8 C" K( Y       return $info;  ; c1 ?( j2 M/ |) h+ V$ b8 k1 ]8 m
    } 1 z& Y* `+ v1 [8 z- |
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,  @0 k; u2 E4 Q1 y$ g! g  \

, s0 J& U7 I. x' d' H再到phpcms\modules\member\index.php 文件account_manage_info函数
" J% m( {! X& v  ^过了get()函数之后。( P6 S# O; y- f

- A: H6 p7 |' @; R
3 s1 _: g, \$ V7 X$modelinfo = $member_input->get($_POST['info']);  
) X8 S/ C( }) U1 A7 h9 n6 ]           $this->db->set_model($this->memberinfo['modelid']);  
$ N9 _  r+ L# s& L- j+ X           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  * M/ H* N+ U+ B2 \) w; w: N5 q
           if(!empty($membermodelinfo)) {  
1 q$ Z5 L6 F7 J. A& |              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  , n9 P9 j5 K) L
           } else { $ z; N4 P2 M5 @5 u
直接带入数据库,update函数我们跟进看看" k4 ~* X2 w# ~
​, V- S6 T4 j" @, A" K* N

! ?. k8 s& s: k% Upublic function update($data, $table, $where = '') {  - Q$ @5 ]6 R$ g1 w% b
       if($table == '' or $where == '') {  1 O, \( j- n: m) b3 G% I4 a
           return false;  1 b- \& O# E" Q  C8 Q/ H
       }  
9 i0 Y, a6 N3 m9 P! l2 q" J+ j       $where = ' WHERE '.$where;  7 ]6 k# f# V# {1 [, u  K; F6 j
       $field = '';  
* c' ~) {- U+ J4 A& ]( |# l6 F       if(is_string($data) && $data != '') {  . E9 I: S# W' p4 D
           $field = $data;  
, M/ P. W4 L9 {5 H6 m- V0 F/ G       } elseif (is_array($data) && count($data) > 0) {  , \  B/ p! p9 z" w7 P0 q
           $fields = array();  7 }" H  q) O* V/ l; j! V/ a
           foreach($data as $k=>$v) {  9 D; x- M7 _  X5 [. ?9 m3 D
              switch (substr($v, 0, 2)) {  . K: u9 O$ Z! d; M3 ]: Q! L
                  case '+=':  + C, \  X' b, l8 ^0 r
                     $v = substr($v,2);  ' |! Z2 l4 K& A7 X% M
                     if (is_numeric($v)) {  
# d) m0 S& M% O# l' }6 H                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
7 m# }' b0 u( N0 o1 C                     } else {  ! Z( o0 E6 Z" K$ N$ F
                         continue;  . t( Q" }6 v3 i7 O- U6 Q
                     }  ! {1 F- s8 V4 f! Z
                     break;  
0 ?1 \$ [  C5 R$ g- \$ l  g. B. w; u                  case '-=':  7 x( M6 S  I  P. ~  r; E6 `$ d
                     $v = substr($v,2);  , ~8 H/ I. J; R* y2 m% D; s6 ^
                     if (is_numeric($v)) {  
$ [% a3 H2 m2 J  u- o                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
; |2 ^5 e, D$ ^8 V                     } else {  
, W! X" F* x6 k. D) T- T8 ?8 o                         continue;  . n$ D0 n1 g4 }1 s. Z; r# E7 u
                     }  
' k. }+ Q/ y* B2 k$ r; P                     break;  
8 v4 U# ^& R4 R1 z7 }$ N, U                  default:  3 Z: d0 b& n# f6 g! _9 s! J( E0 N
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  % Z$ `; j+ n4 r! Z/ H7 K- S
              }  2 N2 s" ]) D: `  b) P
           }  
0 p3 ?- P  \/ Q* G) j  \4 H  o           $field = implode(',', $fields);  
( W- q/ c# M4 h! [9 |1 j' Y6 u1 j       } else {  0 L, v' C/ e" a& V, B* u2 O
           return false;  
- {7 d) a5 P' q5 {- x       }  
6 d8 H1 M9 u7 P       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  : b* G6 P0 i+ f+ g! \8 k5 F1 u) f
       print_r($sql);  
& _$ n" i  c7 Z& h/ O7 s2 \       return $this->execute($sql);  
" ?9 J/ r& Z& D0 Z: q    } 3 ?' q+ F" V( Y8 ]
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
/ n/ ]* O0 |/ W* y3 b1 }6 I' _: t! a. z3 ?2 S& }% ?6 f+ S; E) }3 n
攻击测试:
3 e6 m" |. b# F" G8 H0 v0 {6 s/ G测试地址http://localhost
5 F. H3 z% g  U" C; T  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句8 H/ S$ Q0 q9 y8 i) W9 n
$ b' m/ o8 E3 R/ w

4 T8 e+ w4 L$ ~, D5 `2 p% r* n9 U" u0 b* O

; E; [! J3 R) T9 y7 V- B! c+ i; n6 \3 b; I

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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