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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告) e& \2 |! Y$ h/ g; W7 O0 `8 D
漏洞作者:skysheep
) ?; E7 C3 n8 b' x1 [2 ~1 x" _) J" H分析作者:Seay0 K3 v5 L. K" S. @
博客:http://www.cnseay.com/
# A  Z1 l5 I& X/ m, W0 _& \漏洞分析:
- @' H( I  Q$ ^7 F4 J' a9 D( s  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
* Q* C  _0 r0 v$ n7 L& |2 M
2 M$ f3 e% v6 V% K
  I! @  `9 Y6 h! w5 b% ?0 L   n* ^& M6 a' R/ a8 w" k! \
public function account_manage_info() {  : G# w, G  M% d, n
       if(isset($_POST['dosubmit'])) {  
4 @4 R9 z% G) L% Q) e6 B           //更新用户昵称  / _4 |- X3 u) B2 m9 a
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
3 V8 n9 k; s& _           if($nickname) {  6 h7 ?* C* O4 e3 Z
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
) Y3 O' m8 l3 M, C! c! ~              if(!isset($cookietime)) {  7 j7 R$ b7 T. K/ P/ q
                  $get_cookietime = param::get_cookie('cookietime');  
7 x; y( `/ q, S" L- I1 D& Y( [              }  
5 _7 z: D7 Y# i1 U              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  8 e6 i' `' N' H& U
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
7 Y. K0 K( x' Y9 z: z4 V              param::set_cookie('_nickname', $nickname, $cookietime);  4 X  @. g# O4 H+ i1 V% b
           }  0 P# E( d# j3 y- F" @# |
           require_once CACHE_MODEL_PATH.'member_input.class.php';  7 {4 ?3 |  ~/ h) J0 _  a
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
3 D4 e7 ]6 w4 T7 t9 v! ~- ~           $member_input = new member_input($this->memberinfo['modelid']);  
9 l2 |9 t4 N; e7 C           $modelinfo = $member_input->get($_POST['info']);    Z& y/ I, h; B
           $this->db->set_model($this->memberinfo['modelid']);  8 g; W, U0 F) q6 h* W2 L  F  i
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
/ S2 H" H+ j7 ?( O: z- k* `* ]& I& j- N           if(!empty($membermodelinfo)) {  ' t" y$ k! ~$ l* I: O- y
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  - V' l% o5 y  m; ]8 N
           } else {  
, d% w: J0 i( {              $modelinfo['userid'] = $this->memberinfo['userid'];  4 c* s* F4 ^# |( O! J; L
              $this->db->insert($modelinfo);  3 o/ U0 F, O3 G* `
           }
1 ~$ |8 G# S' C3 D' U% Z代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,2 x+ V. x% W: M, x( i( R
在\caches\caches_model\caches_data\ member_input.class.php 文件中:7 |; X+ f: c# |# ?. Z  G5 P
: Z5 y# ?; ^. d  k. D; [1 f& U
+ J; p- e- @& R* l$ a- y8 J
# M' c2 j$ N9 X% H9 q) }# _
function get($data) {  
. T7 |. _% k- H# [' s* b       $this->data = $data = trim_script($data);  
% M" l& m0 X: ~' s7 k/ t       $model_cache = getcache('member_model', 'commons');  
/ @3 O% y( i+ O9 R* _' e6 R       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
& U7 V* [; o) x, G1 S5 R       $info = array();    U6 h! b; G; E9 [( _* l9 A" K
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
6 B6 C, B7 O+ p" t; O3 k1 W, p       if(is_array($data)) {  " ]( E" z$ x! _) Y3 Z  M  A2 }4 z
           foreach($data as $field=>$value) {  0 F7 b1 Z% Q. x5 ~$ g
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  # z9 N# s# m: Q9 ~, g& u0 q  M+ ?  U
              $name = $this->fields[$field]['name'];  
% X6 l9 I# L, F6 H              $minlength = $this->fields[$field]['minlength'];  4 z0 v# a% O$ s5 Z5 G0 k: ~1 w
              $maxlength = $this->fields[$field]['maxlength'];  7 G3 u4 f8 I4 s; b/ ]
              $pattern = $this->fields[$field]['pattern'];  ' Y4 ~0 u* t# L$ P- R! Y7 ^4 V  v
              $errortips = $this->fields[$field]['errortips'];  
. {6 }* T, L' J6 Q7 A* m              if(empty($errortips)) $errortips = "$name 不符合要求!";  
7 U: ]! N* z; _: O; R' ?              $length = empty($value) ? 0 : strlen($value);  8 X: V/ I( x. F. N6 `
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
% Q7 Q/ u$ m7 W6 \% ]7 ?; C/ S1 `' w              if($maxlength && $length > $maxlength && !$isimport) {  1 _! I& h! ]9 T3 g  Q6 n( b. w
                  showmessage("$name 不得超过 $maxlength 个字符!");  
/ l) L( o; q9 i3 s9 U1 J7 q              } else {  3 W. f" t, v3 e1 Y7 c" t/ }
                  str_cut($value, $maxlength);  ( H8 ~5 K* Z: B0 m  A8 ]" w; @
              }  
1 t6 P3 I0 J* q: z. p- t3 ?              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  ; ]5 }: J. U* n
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  3 A: e1 j6 [9 p! T+ P
              $func = $this->fields[$field]['formtype'];  " d2 z, |$ m3 x3 Z! A  l+ K5 K6 N3 L
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  2 u' ^9 [3 C$ t- @! k
              $info[$field] = $value;  
5 n9 h! I* R( o8 E+ o% u           }  
8 z9 E+ y7 z( N; ?: X# p       }  
" q0 I. T' p6 q8 @       return $info;  
3 ], |- p, u/ |: P6 X- T    } * U/ \$ j7 ~1 d8 x  `& n) T- g
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
" x( u3 T7 L6 Q8 K6 z6 r3 x( S$ c& O2 f
再到phpcms\modules\member\index.php 文件account_manage_info函数" p0 l5 B5 A: i. E
过了get()函数之后。7 N  c% B3 R" [; n' K, J3 X5 C  L
. p( T. ]1 e5 `) Z/ P# V
5 Q. z1 ^& F3 E+ ]5 I' p: P$ V
$modelinfo = $member_input->get($_POST['info']);  2 T/ p' z8 H6 i
           $this->db->set_model($this->memberinfo['modelid']);  
' V) ~& E' ]7 E9 X- ~- c0 u           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
% F% Q% [7 r4 g6 [& P+ j8 h           if(!empty($membermodelinfo)) {  0 d& U  g8 p; v) f) j' ?& r
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
5 q" \  j( J. N- k           } else { + V8 y5 ?5 x( u$ X( A* Y0 n
直接带入数据库,update函数我们跟进看看
4 n( X7 E6 b8 Q. ^' C. V- |: |  T& |

2 f* }* _# O  s, u) t6 ]# Xpublic function update($data, $table, $where = '') {  . c* o& v, d6 J4 Q3 j6 M4 u
       if($table == '' or $where == '') {  3 V: C  `; Z  }- b/ S0 e! o+ m
           return false;  
* Q1 i5 {; ?7 a3 Q       }  
: Q- u/ q1 G! y! Z3 K       $where = ' WHERE '.$where;  # q9 e. {& x2 o  m3 ~* |/ @
       $field = '';  ; P% x' G  Y4 C6 C% j
       if(is_string($data) && $data != '') {  % e$ @8 |$ G' B* R/ |8 u
           $field = $data;  & O. ~; B! s5 h: x
       } elseif (is_array($data) && count($data) > 0) {  5 r! J( p) Y7 f6 {; y
           $fields = array();  
; s& i' B1 W5 z" b  p. a           foreach($data as $k=>$v) {  
5 @" s# U7 P: O              switch (substr($v, 0, 2)) {  
+ x5 D7 _; \6 L6 E+ I  `3 V. ^! H                  case '+=':  / U5 k4 D$ w( K. d/ a
                     $v = substr($v,2);  
! T9 e) Z  ]7 S9 p                     if (is_numeric($v)) {  
! k1 v" _5 n# d$ \; W6 L3 ~                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  4 h6 i6 N7 q% k: B: }) U
                     } else {  
; K# G7 y: F" h" U" m5 M5 w                         continue;  
/ q% S5 W$ o' w                     }  
* z7 v" F) K% J$ Z  F* w                     break;  . q2 O* y0 c; B/ g1 `
                  case '-=':  
8 F' p# M& l# d) y  L5 g7 Y- b4 p                     $v = substr($v,2);  * h- f4 S+ X3 c' D6 \: {" [
                     if (is_numeric($v)) {  
$ s1 Z& P4 [9 @                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
9 d5 k' ?: x; F                     } else {  
6 U" W% g- b1 r( o. S                         continue;  0 x3 T0 A& ^* c+ b$ w% w) o6 h
                     }  , j$ `+ U' h$ c* o1 E( B
                     break;  
. O, f" ^5 g6 V4 L8 v% e# {6 e                  default:  9 P! U) _$ l0 ]4 \8 \5 r9 C6 [( O
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  9 X7 X) n1 y$ \4 W. k0 O3 R0 T
              }  4 ~' I$ N$ j0 ^' N& B* E* [/ |
           }  ) q+ q7 O( R4 k9 ~1 E2 |( ?
           $field = implode(',', $fields);  $ x3 `* K4 `! @' i1 v! `6 g- M& o
       } else {  " \4 ~- r2 t# W( j. Q1 ?
           return false;  ( y2 @1 z1 Y8 Y% G3 v+ W
       }  # T" P3 K0 d/ F# L
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
3 C4 Q3 x1 X* a6 k+ n       print_r($sql);  
6 |# B% C. A/ ^9 t/ w$ O. q2 g       return $this->execute($sql);  6 c  \+ H/ R* {( k: y
    } 6 d$ J# F. d0 G4 v
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。) k+ x  j% k, B9 @& h* u: k
% D. h" P) _* F4 M8 {3 ?
攻击测试:
% k& i8 Z9 ?0 X$ z  ?) d测试地址http://localhost
8 g5 U6 Y9 I1 W. v9 b% w, j5 r  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
7 j$ O: H6 b) R& I% J, m$ V5 |. w1 v+ _: K4 f
6 P8 V( A* {  H* j% B! R9 ]
, S( o' Z& a; j. [0 j: |
6 l6 a6 `' x1 c3 _+ T
8 L( E4 U" r6 M1 Q0 }2 q4 W+ a

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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