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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
% B* {" q* C6 L3 @5 H漏洞作者:skysheep% @) c2 U8 i' H7 \! \
分析作者:Seay4 C4 s! l3 c" X% M
博客:http://www.cnseay.com/* V/ ~- b; o" R+ a( {3 ~
漏洞分析:
# N2 X4 D% g- k# F4 ]$ z+ e  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。- V, N/ o0 R% _# P

9 H; j6 p( A3 h- w' ], f
) _1 A2 v3 m; g, P2 ?0 V ( P/ Y7 g( F" }. b3 L5 q$ u/ E1 D
public function account_manage_info() {  ; ~8 U9 [" r) V) h( m- j
       if(isset($_POST['dosubmit'])) {  + B4 z2 ]' n8 w
           //更新用户昵称  / v' [4 [& K6 b2 {9 m
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  / {) `4 r7 y- t5 F. T6 L
           if($nickname) {  
0 G- P+ S& D# R. p6 s0 f# e              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  8 c8 Q* C8 \4 b5 B( C
              if(!isset($cookietime)) {  
( S3 Y) D6 ~4 S$ H$ L                  $get_cookietime = param::get_cookie('cookietime');  
! d  k6 `5 v3 a              }  
7 |. f; ]0 A  i2 A: E              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  . f! q% d2 w. a' U  {/ t3 e
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  " O; f( M) @1 p2 x4 W
              param::set_cookie('_nickname', $nickname, $cookietime);  
3 }+ a, _4 R4 @' \+ v1 @9 U( Q5 y           }  - U4 A0 m. ]5 }7 r7 L
           require_once CACHE_MODEL_PATH.'member_input.class.php';  , J$ G' x8 K* a; [9 [% v0 R
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
& ?! _3 I8 M- ^           $member_input = new member_input($this->memberinfo['modelid']);  6 K2 ~+ N& L8 l5 c3 d
           $modelinfo = $member_input->get($_POST['info']);  6 ~6 [! w: X& H% q; D9 `5 Y
           $this->db->set_model($this->memberinfo['modelid']);  
9 N6 [' i. ?0 f+ f' U2 b           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
$ [- _4 Y4 g' I- K7 f3 P           if(!empty($membermodelinfo)) {  * T: b" w3 e2 T8 r/ D6 y- \  W; @
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
, H* n& p: Y, I2 b  m4 n           } else {  3 @% [; W$ \& ~; }) f3 e  Y
              $modelinfo['userid'] = $this->memberinfo['userid'];  . t3 ?. D7 u/ X1 o  ?
              $this->db->insert($modelinfo);  1 C0 s4 H+ X2 e* l+ |: C: ?
           }
% i7 F; ?  c$ d' X$ G) F$ r, C代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
2 Z: u9 c% a: y9 K在\caches\caches_model\caches_data\ member_input.class.php 文件中:
9 d5 n) p7 f2 l/ K
% g$ p6 D- z  q2 `  Q5 J3 |4 T* I$ m0 _. C/ T9 e! D) {( T

: \# t- T* m7 i. i3 Y# h+ }6 j; S: lfunction get($data) {  # u8 b* w0 j1 A  p0 R3 M
       $this->data = $data = trim_script($data);  7 D4 T" W' g& k4 {! K
       $model_cache = getcache('member_model', 'commons');  
; h9 m3 W) Z% l       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  4 h7 G1 Q# F" [0 w
       $info = array();  ) ?6 |4 Z; R/ I$ q6 S
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  : @, g1 N, p. T$ Y! ]" [
       if(is_array($data)) {  : g8 q8 x6 S* V- K  G; e7 t
           foreach($data as $field=>$value) {  0 _+ e' C) @- ?/ \' t8 N
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
' N3 O. P: g7 j1 G! @/ O              $name = $this->fields[$field]['name'];  
# t5 I* M* F1 C5 [0 H9 q& @9 H              $minlength = $this->fields[$field]['minlength'];  2 T/ h, \* \5 d; `7 U9 r2 n  E
              $maxlength = $this->fields[$field]['maxlength'];  
" u4 Z% i. M' E3 _              $pattern = $this->fields[$field]['pattern'];  
5 R4 G: V7 [6 ?/ ]0 V              $errortips = $this->fields[$field]['errortips'];  
9 c8 d$ s' m# z* K              if(empty($errortips)) $errortips = "$name 不符合要求!";  / ]1 e( @0 a0 Z  a
              $length = empty($value) ? 0 : strlen($value);  2 T. Q, ?) ~$ U! l/ S
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
3 `% s, @  q* J8 G5 W% t' D              if($maxlength && $length > $maxlength && !$isimport) {  % L* s% V* Z, i! O3 e9 ]! N
                  showmessage("$name 不得超过 $maxlength 个字符!");  6 {5 X" G( o% G6 N# n% g
              } else {  
2 q7 g# y8 J8 ]2 d                  str_cut($value, $maxlength);  # y2 n8 e: |, e1 [; G3 V
              }  ; @4 h5 u# O3 ?3 r1 f0 L
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
2 E6 j& R0 I4 U" z1 K; t7 b                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
' j# q" Q8 H: W0 Q; @# x! R* c              $func = $this->fields[$field]['formtype'];  
9 \: i8 l$ A5 |              if(method_exists($this, $func)) $value = $this->$func($field, $value);  : e& Z! Y7 B0 [8 D$ t( G* p
              $info[$field] = $value;  ( D/ H1 j9 U, a1 w. F1 B
           }  ; P. C/ r. C3 |5 H. S
       }  
8 t9 A5 P6 `2 w4 x0 g- F( F% v       return $info;  ' q- Z. C9 C3 Z, ^
    }
" F4 Y: m& L, ~+ e7 B+ d0 g9 g) g+ rtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,0 q- O$ ~# L/ [- a
/ u! ~' l( K5 H4 W9 _1 }+ c
再到phpcms\modules\member\index.php 文件account_manage_info函数1 H9 t6 U  ?6 z9 ~5 f
过了get()函数之后。
7 p" c6 |3 I% B2 {: p8 r
, y# s$ Q' N9 g
0 _6 i" J7 {6 N$ o$modelinfo = $member_input->get($_POST['info']);  
+ P& R* U  y, }0 I: U/ z           $this->db->set_model($this->memberinfo['modelid']);  3 I( r- X6 O4 v) H5 p2 S& w
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  $ h7 E  U* B* f6 _
           if(!empty($membermodelinfo)) {  
  W1 y+ o$ S% _              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ; O# y$ J, z8 {3 `; I4 q, I- C
           } else {
) j" ~! Z1 |! r( `4 h. Z直接带入数据库,update函数我们跟进看看
1 z/ M7 c0 q. _
. |# h" u" k; X( \: y ' o/ H( r2 v6 X* h. \1 P
public function update($data, $table, $where = '') {  3 m8 g& f8 i4 _4 D* J2 Y  G
       if($table == '' or $where == '') {  
" d* ^7 O# l$ b, @           return false;  
3 h; u/ y8 p/ c4 m2 b9 B       }  
* v/ t* x0 D% g1 ]/ w       $where = ' WHERE '.$where;  
) T+ q# X- @5 S% f8 T       $field = '';  
- N. o+ p6 s0 w' D2 Y9 O/ {       if(is_string($data) && $data != '') {  ! ]9 h) f# V& H! x: g$ p! A
           $field = $data;  1 O; s& K* I! w
       } elseif (is_array($data) && count($data) > 0) {  
3 Y7 I7 N  o4 g) ~' Y           $fields = array();  
1 s! c! [) ~4 |/ P           foreach($data as $k=>$v) {  . M: y9 A6 e! ^" G% V- _
              switch (substr($v, 0, 2)) {  
8 w% m: W; }* n# j) E7 q& u/ n                  case '+=':  
, K7 R" ^- {. k$ ~- S                     $v = substr($v,2);  
- Y  c* E# ?0 v2 R5 z                     if (is_numeric($v)) {  
" m2 \6 U* C$ S* n                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
' |1 Y4 @3 T- g9 t  P$ w                     } else {  3 F4 `, |( r$ Z! n% K( j3 s8 Y
                         continue;  
6 m1 d- T* |) p0 L* k" y                     }  $ {( h, p! J; [& r$ t3 n
                     break;  
3 b0 l6 ?  \' I; W% b                  case '-=':  8 c6 u; J, ^8 h! c1 V! m7 j+ H8 m, U
                     $v = substr($v,2);  ' B" ]' I/ A! {5 ~
                     if (is_numeric($v)) {  8 U" X. }2 i2 J& n* e
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  * g9 f; q- |2 v* Z, e+ I5 l$ @
                     } else {  
( q/ {9 A1 z3 ^2 t4 W                         continue;  . V  j/ C5 A% S# [# _
                     }  + ?5 x) E( I1 t/ c! L4 c1 n, K
                     break;  
. ^% R% c. f/ k5 f) d& b                  default:  0 p' W  l3 @2 W1 M5 O
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  : q6 V& a: }% [" s* N: ?
              }  7 B3 k: w9 O) P( ^
           }  $ ]5 R; [6 h$ ~0 M3 B& e# u. T
           $field = implode(',', $fields);  
) Z: C! ]# T; I$ K  w9 }       } else {  
* ~! j. W* g) A. Y4 H; F6 A           return false;  5 H* g, }* v/ i6 v# g: Y' B% P
       }  * n7 |' ]0 ^0 D5 m1 `) l! H
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  . v. a9 r% i3 z8 E
       print_r($sql);  
, G1 _) Q1 o" x+ p1 E       return $this->execute($sql);  ( B# d3 U0 E. l8 a% o
    } & u& r1 n8 U2 N% x! \# @
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。# y: G+ D2 z# b: n# t2 A1 ]3 K& S
5 |1 m8 D* ^$ ~6 }
攻击测试:  E& V% _1 V# R/ T$ i
测试地址http://localhost( a' Z" d& F2 Z! {5 @" Z
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
- q3 {# _+ W$ O9 R5 E/ X% Q6 r' P( K* ~7 M! |

( _1 L4 Z' \& l1 T7 f+ d4 Q" |( z4 ~+ m9 h4 g% L

  l' Y1 u4 I' O" T
) v2 m; D) q' _4 a

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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