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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
, o  A) Q! U3 q( e- Z2 O漏洞作者:skysheep8 X) }. K, `7 z, D9 K! R
分析作者:Seay
8 @# l+ M5 g$ ^! j0 A4 X$ @博客:http://www.cnseay.com/
% X' y0 `. R+ C% t* n+ M; ~' q漏洞分析:; }2 z+ k( l( H# o8 K) ^5 m) J' a' u
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
( M' `) y8 l7 [7 N# T" @( c/ x  d; U* |, Y1 Q7 ^* u7 y

3 q. W0 n' |" J- d " W1 G9 {  i+ q5 B6 j/ v3 u
public function account_manage_info() {  ) ~5 J8 h% B! D- `/ s$ O
       if(isset($_POST['dosubmit'])) {  
2 J+ v/ d8 X5 z( U9 h           //更新用户昵称  & U" D4 _$ E% l4 l0 C- K
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
9 u' R) \4 a' U; A8 H0 }+ j           if($nickname) {  ! k/ N6 h4 G& h6 q9 A; R* N
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  3 D3 h* N9 X* _9 a( L# U/ P
              if(!isset($cookietime)) {  " @" m- z& j( [' z( d$ F
                  $get_cookietime = param::get_cookie('cookietime');  
8 R0 R5 w) T8 @) Y6 v& e0 F              }  
% h8 b* o$ E1 K              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  : j$ z3 Y7 w) H' g2 y4 E) s# \
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
+ l+ w, e/ R0 o3 y              param::set_cookie('_nickname', $nickname, $cookietime);  
$ y# k3 x4 X  K& z; V# }           }  
4 X' `& A: m. ^- X           require_once CACHE_MODEL_PATH.'member_input.class.php';  
/ Y1 ?1 x% G8 a) \6 r           require_once CACHE_MODEL_PATH.'member_update.class.php';  
) K! R2 ?# T" W           $member_input = new member_input($this->memberinfo['modelid']);  
; g9 F+ G) m% v           $modelinfo = $member_input->get($_POST['info']);  ' f6 c' \7 B  p( O) p3 f
           $this->db->set_model($this->memberinfo['modelid']);  
6 t, a! e0 W  `# K- b& U, I1 K           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
; ]: X" c' y6 m. b$ J           if(!empty($membermodelinfo)) {  
8 v, j; X- _. h5 y) Q              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
" y5 ]- H6 w4 _           } else {  
# C; q0 J9 N" n6 M              $modelinfo['userid'] = $this->memberinfo['userid'];  
$ I& O! q) o8 U6 D+ i: c0 J+ V$ x- ^. W              $this->db->insert($modelinfo);  
  q0 U# i; c" u: K           } 7 u  @' F, V( Z
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
  r; ?  ~! j  J" ~在\caches\caches_model\caches_data\ member_input.class.php 文件中:. a9 M& I' v/ y& N) Y
1 }) e+ w- b  b) m! y

9 d" j( |# q! k- ~+ a7 O: M
/ w+ @* o' B! Kfunction get($data) {  + W2 Z, w9 ~7 W
       $this->data = $data = trim_script($data);  
8 b1 U6 d* w) T- L       $model_cache = getcache('member_model', 'commons');  
1 b: W* `, @9 E3 B7 n8 ]4 O       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
/ K: N! l. g3 r" ^2 @* k7 u       $info = array();  . ^6 g1 f4 B  \* X: h! R
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
6 c' t3 W$ M# j* S       if(is_array($data)) {  ( W( Q$ a% Q: A" {" q
           foreach($data as $field=>$value) {    R& k4 j# B: ?) k+ d0 {' R
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
& u+ d0 [/ _! V) H& v# a2 Q              $name = $this->fields[$field]['name'];  , s/ J; t; o1 k7 u! O5 w' K1 `
              $minlength = $this->fields[$field]['minlength'];  
7 M: O2 u4 Q% v$ G6 U              $maxlength = $this->fields[$field]['maxlength'];  3 T: e$ O$ Z* }( D0 q
              $pattern = $this->fields[$field]['pattern'];    d& I( K( }; D, J: K
              $errortips = $this->fields[$field]['errortips'];  0 I! C  [0 m  G; |* T
              if(empty($errortips)) $errortips = "$name 不符合要求!";  5 H; Q( K- j( E3 r
              $length = empty($value) ? 0 : strlen($value);  0 \: C6 k. H1 B9 f3 v* D% l# u3 K
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  - h% F* r! _* h& i2 c5 S2 a4 G
              if($maxlength && $length > $maxlength && !$isimport) {    E% ^: R. m/ j' i
                  showmessage("$name 不得超过 $maxlength 个字符!");  
# L0 `1 M; c! [$ _" p- E, r0 x% I0 [              } else {  # M# {0 u+ C* v$ K6 X
                  str_cut($value, $maxlength);  
1 }, ?! s% k3 t4 m, A) i              }  
: c, r8 ?7 ?6 W5 V" m* C( V  K              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
) e" A# W' X- l0 f+ K- q                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
# r  k6 ?/ @" w$ n7 j: P. q              $func = $this->fields[$field]['formtype'];  
0 y4 K! P" W! z: H              if(method_exists($this, $func)) $value = $this->$func($field, $value);  * @# _& P% l. V( U# H
              $info[$field] = $value;  
  r+ g% B& U' L           }  8 ~& M! g5 f0 D# G6 b5 [& I/ _3 \
       }  
) k+ N9 X( B" ^; U, g       return $info;  4 |* D4 z1 @6 n) P* o, r
    }
& k5 z8 [/ d: Y! Y. ~trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,0 ]5 V1 Z2 E, Q1 E/ R; O
+ _$ T" O3 [2 T  @9 u0 z/ \
再到phpcms\modules\member\index.php 文件account_manage_info函数+ h* ?2 I! e9 A* m" B# [0 f
过了get()函数之后。
5 [0 _8 {; c0 S5 t$ q4 N- k4 P/ _: s* D. s, V  q1 f

, K* t! j  q; k$modelinfo = $member_input->get($_POST['info']);  + ~+ J! @' j: t+ b& T
           $this->db->set_model($this->memberinfo['modelid']);  1 Z! u# z; z3 M6 B2 D
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  $ I% m! \( U- ^2 n! V
           if(!empty($membermodelinfo)) {  , [* g9 D3 p+ D2 `
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
, {9 ?! K2 s/ \2 o0 G2 r           } else { / K8 A4 T! s5 M( K
直接带入数据库,update函数我们跟进看看. r" R+ N3 l  \1 H' Q# g5 e* h

( g# x8 U5 e0 ]  a6 `; Y) g ; D, l3 P5 H3 ]7 G% t$ V5 }
public function update($data, $table, $where = '') {  7 m6 h: t5 z' k+ I! v# |* ~
       if($table == '' or $where == '') {  5 F5 L; L. O" o# w3 U. s6 N2 B% w0 M
           return false;  . X3 \7 z" B0 ?$ r+ C+ C+ i4 Q
       }  
/ U) V0 H; z0 {' ?) ?6 J4 N# V       $where = ' WHERE '.$where;  
# }# W5 ?$ _3 k0 C6 Q1 H9 y9 B       $field = '';  
& Y7 z8 R( ^7 f$ i. j       if(is_string($data) && $data != '') {  2 E8 |7 w* j8 N9 J7 G' o
           $field = $data;  % R: L. c( U1 G" l6 T0 u! G
       } elseif (is_array($data) && count($data) > 0) {  
2 p  Z7 h# n% @. b" G, p7 a           $fields = array();  % I5 K  {  [6 p& X) h
           foreach($data as $k=>$v) {  
: l# F7 r" ]0 g& F              switch (substr($v, 0, 2)) {  2 l, f# x, E9 K8 _/ z. s5 W
                  case '+=':  
$ A4 t! v, u, ^8 d                     $v = substr($v,2);  
) R1 j! h% S2 M                     if (is_numeric($v)) {  : ]. P3 w1 F  B" L
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  . g2 E1 q. x! _8 h
                     } else {  $ Q  [+ }+ Z, t
                         continue;  
- l$ j! d  A5 Y4 p                     }  
7 @- s% I/ \3 x1 y, \                     break;  ' R3 a2 `, L+ z
                  case '-=':  3 Q% k! B: q! i, }* |( G
                     $v = substr($v,2);  
, f+ B& y& ^+ [  p                     if (is_numeric($v)) {  6 {( Y- d' B% J& @. Q9 `  W
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  " M7 d9 A8 l- d7 `
                     } else {  5 C/ [* h+ K4 s( J2 x2 X, u4 _8 K
                         continue;  
4 A) d. K, q- p) \* F                     }  
0 x# ?" f; _: [: w/ E# C                     break;  + e7 K0 l8 S* v+ x+ C+ C
                  default:  " |0 p" }* h0 o0 x9 d+ v& V/ A
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
1 R7 x# Z% z) @# q; P              }  
) O! x5 Q# }! b4 H% ]; O; Y( ~           }  
9 m" q% f# t+ A8 p; @) e- l0 [& i9 o% c           $field = implode(',', $fields);  , {$ t* y# Z; a
       } else {  
% G% p5 k5 G9 ~, I           return false;  
- [$ d! J! l" u* t1 O; b       }  % o& t6 j& S0 r5 g
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
- w0 h- D7 x& E+ i' H$ ?: U1 A       print_r($sql);  
1 B- Y9 q, M0 q; P7 o( M5 \9 p; k       return $this->execute($sql);  
3 ~/ `& i7 q9 c6 Q' B    }
6 L& I8 J( k+ Q1 p; l从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。" d2 B) o0 C. e; C$ s

% y7 x8 Y1 ]. l4 d9 b& y2 W! a攻击测试:/ Z! e3 N" F- D/ j  W; [  _
测试地址http://localhost! d! V3 N# O  n4 h, Z+ |; Y
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句% c3 C2 y7 T. z" X2 v/ p: o
9 ~& x* m$ U( C) [+ Z# U6 |7 ~0 D

8 F* X. X8 }( A- B3 G4 f* U* h- ?' i9 ^# z
  @4 c( @* |9 O. V) w. c5 M5 G- }
9 f& A7 ^1 M6 K5 S

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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