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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告" v3 }8 w+ Q4 b5 X( H1 M# B
漏洞作者:skysheep& r3 S% u  `( p* x+ P
分析作者:Seay+ H/ u2 t; R! E- f  Y
博客:http://www.cnseay.com/4 z) l, z) C6 S4 I! k
漏洞分析:
/ @$ e7 v( u* y) X, \  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。" j: r' z9 `. T- z- M

9 Q! t  p3 {) W6 S. m' j
+ T. a+ f- ~, s- Y, m0 Z" G9 b 4 Y( w5 a2 d+ ?* {' t- |2 z
public function account_manage_info() {  1 w! m4 z0 x' y
       if(isset($_POST['dosubmit'])) {  
9 R1 M) e  j% f" R8 X           //更新用户昵称  
* Y" _' z+ b& K, s. I4 [           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  1 @1 M5 c& U0 G7 [; U
           if($nickname) {  9 [# q& K! l- g0 o; a5 G! m4 W
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  4 p8 [5 N/ ~  J5 O
              if(!isset($cookietime)) {  + F' D0 Z' o5 [! Z/ d6 q
                  $get_cookietime = param::get_cookie('cookietime');  
- D6 U# ^3 [" c2 I, Q              }  4 W: W+ {9 M, l3 j6 S
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  6 b; R, ]7 ^3 w  s9 Y9 R* c
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
& P8 }! v* W# N7 P& _, f              param::set_cookie('_nickname', $nickname, $cookietime);  
! T7 g/ c/ s9 W. s1 ~0 W/ |9 G" ]           }  
- Y9 ^" W8 e2 ^! s( F) _" k2 j           require_once CACHE_MODEL_PATH.'member_input.class.php';    I- G; \9 [! W5 X0 m: b5 V
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
0 n; q; u* x% O8 K  c0 G9 ]           $member_input = new member_input($this->memberinfo['modelid']);  
0 h0 `; ~$ D  {- M, @9 h* G3 `' L           $modelinfo = $member_input->get($_POST['info']);  ; V! f$ D1 Z2 j7 H% X0 o
           $this->db->set_model($this->memberinfo['modelid']);  - M6 R0 \; G: p( r' r+ R3 ]
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
9 [; y* x  d( G6 f- e- B! _& F% X- h           if(!empty($membermodelinfo)) {  
/ \8 `; C. r; G0 u' d; R              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
& O" q) D% r: G* u6 N/ L( {           } else {  
+ F1 |' j6 ^$ Z, Z* y8 `              $modelinfo['userid'] = $this->memberinfo['userid'];  
' t5 y  ?, ]6 J& l1 T              $this->db->insert($modelinfo);  ( o. K1 v' Q' S& Q' S
           } * n  o' O$ V6 @& n
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
' |/ y4 `( p' K! W4 u在\caches\caches_model\caches_data\ member_input.class.php 文件中:
0 ]" [' C* S5 \2 L. Q2 W
$ [; \( L* U) r- X' w2 \# Q. q$ q- n+ l, G$ D$ s
1 Y7 ]2 @+ ^( a
function get($data) {  : m( L6 Y0 `* a& ]
       $this->data = $data = trim_script($data);  
  g" W" K& j. q0 S       $model_cache = getcache('member_model', 'commons');  6 k, |# b# ^4 j% J2 H
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
. t& |; H$ r& O0 T! U% C& @% u2 Q$ S       $info = array();  
( v2 q% s# V- r8 G" l2 I       $debar_filed = array('catid','title','style','thumb','status','islink','description');  # o$ _* l, Q9 T. O3 x5 U
       if(is_array($data)) {  & ]# Q# z6 z7 A+ J
           foreach($data as $field=>$value) {  : a" o+ K& h4 T0 W
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  ' J9 E$ r$ Y( q( s' M% m
              $name = $this->fields[$field]['name'];  - m! e' y& V  }8 z! M
              $minlength = $this->fields[$field]['minlength'];  
. t" X. C# ?8 F; I" Q6 v/ D              $maxlength = $this->fields[$field]['maxlength'];  $ J( c3 u" i$ ~
              $pattern = $this->fields[$field]['pattern'];  
1 M( ^/ {6 Q8 ?. V- g0 m: b              $errortips = $this->fields[$field]['errortips'];  
( b) J& Y7 `- W. t3 u% F              if(empty($errortips)) $errortips = "$name 不符合要求!";  
. J" n4 k* ^/ N* L* }              $length = empty($value) ? 0 : strlen($value);  
7 d* d# G: G" h8 t              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
5 P2 R/ J6 C' @; X. p" x# q              if($maxlength && $length > $maxlength && !$isimport) {    G- ]# F+ O( T( Z8 V
                  showmessage("$name 不得超过 $maxlength 个字符!");  
8 X9 J9 D9 |* e8 ?/ j              } else {  % Z8 E. d* F# F7 i; I
                  str_cut($value, $maxlength);  3 F. Y7 r, n- `4 ~, W' X$ B
              }  
1 }% s) ~8 Q" o0 x6 U% l3 Q: U              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  ! h6 y4 J% r2 D: O$ \4 S) O7 T
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  * z; N1 ]: q' K; l
              $func = $this->fields[$field]['formtype'];  
, `7 B+ P- j9 Z              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
6 g+ {% Q% [3 y/ |+ M              $info[$field] = $value;  $ u( J3 N" Z. R  d+ e
           }  . b, L5 i/ n0 a: H' z$ I! [
       }  . |4 K: K) b) A! d" W4 v
       return $info;  
( U" X" {( t$ H! j    }
8 j8 E" y4 \& y0 Atrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,$ f+ }$ G5 f. F, r! `" i% K4 }

  Q: [/ X# ~" ?. ]# ?# ~  {7 |再到phpcms\modules\member\index.php 文件account_manage_info函数" P2 L' [/ J2 w& s; o
过了get()函数之后。
6 `2 p. v! r( ^( J: h
$ U  h% F8 n- h& O$ c # Z8 j( |3 ^: m0 d6 L
$modelinfo = $member_input->get($_POST['info']);  
3 N- Z: t7 Q/ _, [' [2 y9 N           $this->db->set_model($this->memberinfo['modelid']);  " f% L$ {% Z/ |0 I! j" M8 {
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  . S* {" F, V! _# O0 O2 m- s; ]
           if(!empty($membermodelinfo)) {  * q; N, X# t% y- \
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
' I: C6 R/ z. f$ i' }6 r# l           } else { 6 E3 M5 d% `* r, x* x
直接带入数据库,update函数我们跟进看看9 F% {) t$ M8 F

0 b2 C$ K2 A! k+ \1 c& u9 O6 p, }( K
, X! x+ C& J- o6 H1 E8 d% _public function update($data, $table, $where = '') {  
* G: }5 y$ [$ X- ^+ [2 J& c       if($table == '' or $where == '') {  
) s+ m" U+ X; ]; O           return false;  
: f7 U" [+ ]1 d$ d3 V       }  
' n3 w+ ?4 |) s7 \; q, [       $where = ' WHERE '.$where;  ( A4 K6 [& p+ h- Y2 n
       $field = '';  
% Q, q* C3 w+ e8 u0 f8 o0 F9 A       if(is_string($data) && $data != '') {  
/ x9 r$ M( L) A& q& {           $field = $data;  % Z" V0 U: `, n- g
       } elseif (is_array($data) && count($data) > 0) {  + S6 _+ Q0 z! R0 ?( _
           $fields = array();  0 v, X0 X+ ]2 y  [& m& E. V3 [
           foreach($data as $k=>$v) {  
* r) H6 L0 _9 Q6 [0 _& \+ K6 l              switch (substr($v, 0, 2)) {  
! J  k5 X! k. B7 `7 C* u' V3 `                  case '+=':  4 r: Q0 z+ o3 J/ w* v5 N. a
                     $v = substr($v,2);  # E) N4 m4 i( N
                     if (is_numeric($v)) {  / p4 A# j5 n& T
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  ' m+ j+ l  v% T3 y
                     } else {  
/ ~  H: l" X; }                         continue;  
7 d" Q1 [# T* h; U+ c% K& i. D                     }  # l. n6 C" F! E% [( e6 a$ d! J
                     break;  
; v2 H' p1 h- J$ ]4 G                  case '-=':  
, g3 ^2 C5 e) t7 h) T; k, Z                     $v = substr($v,2);  
+ x3 e! G( i1 ]+ @: u* O3 H                     if (is_numeric($v)) {  & o7 y3 E) Z0 N- ]( Q6 |
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  , H' i2 h% D3 O4 o
                     } else {  ) h/ y5 B% N) w
                         continue;  
0 ^" t7 K  O' _; ]8 O/ u6 G) X+ t                     }  
) [: z. `' N) u3 g0 A                     break;  6 o) W, x- j/ Q/ u4 |+ U: u
                  default:  # M2 \/ o' D% x* P& H! \
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
. j2 l# T2 F: g. T; c              }  
* ^1 r$ u- V& H" i           }  
" G1 r% @  a5 P3 H9 Z           $field = implode(',', $fields);  
- `) @3 }8 N9 \, F1 C       } else {  ! T% }% a! s  S/ ]# w
           return false;  2 U5 I% U& {) k$ n
       }  * j+ l: O3 t0 t
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  # m. e/ [; D( U
       print_r($sql);  
, I: {" r2 I  R! a8 [0 h       return $this->execute($sql);  0 t  v& d2 ~. O
    } + B& W# h( P7 Q  S) M
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。+ j! p" e% a, g/ g. a: o
6 h( _( [5 D% ^$ y/ O; C6 a
攻击测试:
- m  K0 t! Z% v' M) R7 b5 i6 x测试地址http://localhost
' x7 E. I) k9 L  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
" Q4 w2 Z% C% T% L4 ?& R
( j2 p4 t" T2 p7 c) t# r+ w. @3 N ' m" L" v8 t+ N2 Q  h
4 z! f0 Z0 |- J7 K3 y9 \
7 ~7 Y( M. G. M7 i6 u5 S2 Y

, k" J# [0 ?3 A7 S5 b0 d# s/ J

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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