找回密码
 立即注册
查看: 2348|回复: 0
打印 上一主题 下一主题

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告% m0 p2 D" M0 _
漏洞作者:skysheep3 s7 `0 Z% e% T, q* n+ H& t) A
分析作者:Seay
% s  u3 Q2 ?7 @6 O3 M& C, k8 c博客:http://www.cnseay.com/
* o4 O: n" m/ x. Q- [" j- H# f漏洞分析:! s+ e0 M7 i- b) F, E3 N
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
0 }1 A* _' _/ T, d! Q! [  J' r4 F1 I# ?  _

9 X8 [$ z0 c- j0 F( ], H : M, m9 j8 g% V& b5 ?
public function account_manage_info() {  7 I5 s  b* Z7 S( F6 i; t9 g. Y8 J
       if(isset($_POST['dosubmit'])) {  $ M0 ]# }! Y% }4 ^* I& v" u# c* t# `: x
           //更新用户昵称  
6 r* I9 U! i1 ?           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
2 p4 M  q! c/ C- `2 r8 H( ?2 q+ i           if($nickname) {  & A# B% k6 `  Y1 U
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  1 {" n0 k5 |0 `; Q
              if(!isset($cookietime)) {  # U$ X! _8 U. k3 |0 [
                  $get_cookietime = param::get_cookie('cookietime');  
2 w2 N2 Y, e, U  ], _' R& W  ?              }  " a; ]3 S2 I5 H& p4 w1 E) C
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  ; o% `6 ^+ c7 Q8 _: f/ o
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  ! ~& y7 L; P# W$ @& l/ ?
              param::set_cookie('_nickname', $nickname, $cookietime);  ! v6 N4 J& [* Q; M( F! U
           }  
) L; ?- o7 l9 p8 H6 t* l           require_once CACHE_MODEL_PATH.'member_input.class.php';  
! V: d* ^: f$ k; ~) c* K$ @; H- Z           require_once CACHE_MODEL_PATH.'member_update.class.php';  
% X' _8 b9 L' i7 X/ K' K8 l+ b+ z           $member_input = new member_input($this->memberinfo['modelid']);  # m1 N: V1 e3 B5 _% Y
           $modelinfo = $member_input->get($_POST['info']);  
# t+ ^2 A' a& b& E3 }  K* R           $this->db->set_model($this->memberinfo['modelid']);  
) [. p, c* ]# M" t           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
3 d( U" v5 o3 P: G           if(!empty($membermodelinfo)) {  2 s+ s: T. Z) ?5 B! y
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ( d! _. f8 s" {7 k
           } else {  
5 C% N5 A; Y1 F* ^              $modelinfo['userid'] = $this->memberinfo['userid'];  
" H+ d+ G, y" Z, Y4 j7 |' e$ C( O              $this->db->insert($modelinfo);  1 \$ H4 L& |. D2 W) |) {3 B4 F( p7 y
           } & J4 x; b, A3 `7 ~. q0 w
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,5 K; @  N6 [) P" H% Q8 w# X
在\caches\caches_model\caches_data\ member_input.class.php 文件中:' ]& j1 T! x, W& |  L# A
3 g: p1 h- T  m& W' F4 q1 G
  T- ~* f% W: p, M$ z' l7 `

" V% i+ w. o0 v- Ufunction get($data) {  
' Z! D) r" B9 X+ [9 \7 v" T       $this->data = $data = trim_script($data);  
" z2 [5 \. O# A/ D       $model_cache = getcache('member_model', 'commons');  ) K& K4 v. G0 h3 Z/ y
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
0 X% ^: p9 K7 F' X: @" S       $info = array();  
+ H" |& g0 W6 c5 R9 O       $debar_filed = array('catid','title','style','thumb','status','islink','description');  ( A& K) Z( C' w9 `& e
       if(is_array($data)) {  ' J. K' Q9 V$ ^
           foreach($data as $field=>$value) {  + w+ J0 M; n7 _' L# q* S
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
9 x: B7 l2 J/ z& ?' J' \: [              $name = $this->fields[$field]['name'];  * L: P/ F) h  L) X; d& Q8 y
              $minlength = $this->fields[$field]['minlength'];  , ]1 F# }. K2 A+ K  r, c( \
              $maxlength = $this->fields[$field]['maxlength'];  
6 g  O  n% R$ m8 c              $pattern = $this->fields[$field]['pattern'];  
- d. g3 v6 U+ {) o, ?              $errortips = $this->fields[$field]['errortips'];  $ C9 a) h/ a' d* [9 C% u% d! Q
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
0 T5 Z" n- J! t3 n/ u              $length = empty($value) ? 0 : strlen($value);  0 x% O3 s- f) B% \( f
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
/ u  P$ R8 H8 m4 v: H) P0 l              if($maxlength && $length > $maxlength && !$isimport) {  
5 C0 E* H/ m* T$ H! T! j2 V; z                  showmessage("$name 不得超过 $maxlength 个字符!");  " T' w! V4 S" j& i8 F
              } else {  ) k4 m6 T( B; n  e
                  str_cut($value, $maxlength);  4 G% l8 B; t% ~
              }  
& C+ C/ }( F  q" @- c              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  ) C9 A) C1 O% w1 X" S
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  2 p4 S5 \! C9 G7 r
              $func = $this->fields[$field]['formtype'];  1 G$ _6 B2 O: \7 B% S1 m- i% g
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  5 W' h& A0 g# y+ P
              $info[$field] = $value;  
# _- h1 Q3 z' H           }  
" `; U/ X3 E3 t5 Q2 P' F       }  
/ ?1 e9 I/ o, M2 K1 n       return $info;    T" r& n% C, X% f3 O9 N5 f  s
    }
* A+ |4 _, ^, f/ itrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,7 M& z) e, Z" v

0 \# `; h' W6 P再到phpcms\modules\member\index.php 文件account_manage_info函数& r1 P2 O3 H. N  h* D* L: O
过了get()函数之后。
/ M9 j! }; v1 j8 u  z$ f) d7 u+ Y) x: d# X% f) Z

) j6 A) ]0 ]. e' c3 D$modelinfo = $member_input->get($_POST['info']);  
5 X$ L  Y8 ?3 s+ x           $this->db->set_model($this->memberinfo['modelid']);  
2 V5 o- p, B, W$ M9 {2 U           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));    R9 p: r; \5 L  I
           if(!empty($membermodelinfo)) {  ' E1 `; S* W% m2 s0 ]
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  7 t. _. n* B. w& ]4 v7 u
           } else { 1 d) Z* Y' u3 W( Q! ]6 P, }
直接带入数据库,update函数我们跟进看看
& r+ `/ K$ M0 s9 k+ j1 t
  W- O5 s4 }) x  ~+ r! h* F
0 M9 Q; o' T, j$ @1 Q  Gpublic function update($data, $table, $where = '') {  
0 R: L, O; }5 R+ H8 T+ c5 d- q  N       if($table == '' or $where == '') {  9 u# J( S) v) U1 @7 ^6 L8 h
           return false;  2 f9 I: R; a' D- X$ j
       }  # S  a0 [* U" [5 `
       $where = ' WHERE '.$where;  4 b3 |% [# [& s8 B4 O( B- c8 Z% ^) C& R
       $field = '';  
4 S2 {3 n7 e: \       if(is_string($data) && $data != '') {  
: `' N* Q1 ^* H0 K+ K; t, V4 j           $field = $data;  : y5 Z' K$ E4 H% M
       } elseif (is_array($data) && count($data) > 0) {  
% M3 k, d1 N3 m/ `& J" s* I4 G           $fields = array();  - l8 R0 f6 ]( o+ ^( a6 p9 e6 A
           foreach($data as $k=>$v) {  
. I0 U& a. K* ~              switch (substr($v, 0, 2)) {  
: G( r' K( e* I4 V: a6 S; @                  case '+=':  
' A8 S4 }1 Q4 W) e4 ~$ l4 z                     $v = substr($v,2);  # r, Q' ?, y) {' v4 ^/ U. S
                     if (is_numeric($v)) {  
* P2 e1 b/ j: g; c                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  7 J4 n' |( Z$ V! i
                     } else {  
. U9 d1 V0 s7 t& u9 \                         continue;  
& ?4 Q* Q8 I/ T! d4 ~                     }  # G# s2 E" s! e! e$ g- W' B5 p' }1 }
                     break;  : L# r! y' h( X- C, ?
                  case '-=':  
" k, m4 V; m( w: @  q  M: |+ ?                     $v = substr($v,2);  
6 p! q5 [( V7 Y+ Y$ P3 V. a1 r. R' ?                     if (is_numeric($v)) {  
$ V7 ]2 Z+ s. v8 E                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  9 U% K7 F7 K+ n' b3 Y7 V  h& E
                     } else {  8 r) r: Y0 x# D4 c) w
                         continue;  
+ D* a$ @) n- S( J                     }  
# K& [3 f. \1 s+ [/ n( k- t                     break;  
: P% ~' @3 F9 v1 @                  default:  
& |/ T% q. ^' [9 N                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  % o3 j6 ^; }! h$ L( i/ ]$ z1 `
              }  ( H/ g% j+ F4 r) [8 N
           }  3 t+ Z4 L$ E# K; r  w2 r. u, I/ {3 |
           $field = implode(',', $fields);  
& Q' B" h& q2 b9 G1 t: L       } else {  
) \( f; ~) S* d7 [           return false;  
! m. R) N1 b: `% P5 }2 s- j4 ?+ ^! |       }  
& n& H3 X7 I. L) G2 k, U       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
, o2 q5 A1 B% R$ w; w3 E       print_r($sql);  0 n% n; {! w, w- S' u& N
       return $this->execute($sql);  
: Q0 H9 R! I5 Z/ G8 }0 u0 K" K    }
7 W: b8 l. q* c+ L. i. b从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。1 q* P& [; F( c; Q5 c+ k, I' }

" N; M5 X3 H& e, U( }7 o8 L攻击测试:- ~# f2 ^& a& i% t, W5 ~, U! m
测试地址http://localhost
6 |) w8 I  D9 G2 M& W  X  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
% s+ m: A# U/ v7 r2 x4 Y5 H" M6 I, ?& y8 z9 d; u6 j. X
" x* o, Q7 B- T# [0 a- P9 M2 i* m

' k- @7 n  z( o- P: G: k" }3 t/ \6 ~/ x. ~% i/ F- U
; Y) \( Q5 x, _4 w

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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