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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
: y; e0 V1 ~9 j漏洞作者:skysheep7 L8 f1 L8 Y) J# g' c9 X
分析作者:Seay* q& n( ~& y2 ?& g
博客:http://www.cnseay.com/! l7 x; n6 F! _4 l0 O! Z
漏洞分析:
6 F) g- I7 X0 l* Y  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。; h2 U. P2 Q2 i/ ^0 G. u

, O# N3 ^. ~/ ?5 l) r
9 ^0 b6 o5 n' B! Q7 H 3 G/ w# @: P$ f7 C( Q- v/ X( `
public function account_manage_info() {  
/ c" n0 s: p! _0 B% g       if(isset($_POST['dosubmit'])) {  
( k8 E( p3 o2 [) `           //更新用户昵称  ( z; E+ |' H& J" Q0 Y; p' P( j
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  ; [: Q/ }6 ~3 C: }, Z- \+ Y
           if($nickname) {  ' [8 b5 `0 k' X, s. ]
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  7 i' S- u5 U6 A3 B& \3 |
              if(!isset($cookietime)) {  
! G! Q7 R$ s3 n                  $get_cookietime = param::get_cookie('cookietime');  
5 o$ W3 @6 s6 _: i  ~+ R" J              }  
, f5 ]3 V$ s$ t2 L              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
" P5 y5 c/ j8 A: z$ z              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  3 ~. j* i4 |8 }# |1 L1 @
              param::set_cookie('_nickname', $nickname, $cookietime);  
8 s9 k  V2 B  W' j; _           }  9 y) k! Z7 Y3 |2 v) I
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
  X+ ]" l% n( H           require_once CACHE_MODEL_PATH.'member_update.class.php';  ) z- t+ _3 d; `2 k( ?; c% Y
           $member_input = new member_input($this->memberinfo['modelid']);  * M+ N/ T3 f; l5 h
           $modelinfo = $member_input->get($_POST['info']);  7 g$ ?4 ?; @6 w9 A: }
           $this->db->set_model($this->memberinfo['modelid']);  
$ p) \( w; o, `% E: |9 t           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  * k# Y5 f$ G8 m3 [- J" k
           if(!empty($membermodelinfo)) {  0 S4 l2 I* h- P
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  3 q$ S! ~2 H' e" J: C0 ?- q
           } else {  
, y: `- |/ |% Z) J              $modelinfo['userid'] = $this->memberinfo['userid'];  
" y, T- Q2 |4 V4 y, U              $this->db->insert($modelinfo);  
1 g: n$ V( o  R           } 4 C# l* l) V# t& ~* Q) f9 r+ k
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
  x* y, u# A; g6 q% e  I在\caches\caches_model\caches_data\ member_input.class.php 文件中:
5 C- f% C, ?/ G1 [5 s$ `) ?+ P3 J% ]2 c* H/ X$ w+ w
" z2 q+ G: {! x/ Z- c$ T

) F# C% D1 W9 Z9 P& j! x' j) Ofunction get($data) {  
2 x  g1 G+ x% R2 q- |. d& B: |       $this->data = $data = trim_script($data);  + y. h" o% s$ d) M- Y, }4 k
       $model_cache = getcache('member_model', 'commons');  
0 D( Q4 z2 |+ H6 Y  b1 ?; T       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  7 A% V# N% _1 ^5 r
       $info = array();  ( G3 q3 ?7 Q) ~' y+ |7 r2 u# s
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
3 Z/ A6 U$ o, ]8 F9 w6 ^2 l5 ?       if(is_array($data)) {  
8 b$ ~$ n! @! u9 ]% e9 W1 }           foreach($data as $field=>$value) {  
3 F- \5 j* A# V" v              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
$ `0 M% ~2 y/ N              $name = $this->fields[$field]['name'];  
6 V6 u# Q2 |8 e              $minlength = $this->fields[$field]['minlength'];  
% z) R; i& L* s- P- h4 h              $maxlength = $this->fields[$field]['maxlength'];  
6 y3 C  f0 M/ ?1 Q$ v              $pattern = $this->fields[$field]['pattern'];  " c5 m0 j# j/ }5 U" N( z
              $errortips = $this->fields[$field]['errortips'];  
+ x! {4 P2 `- e. c7 R- o  c              if(empty($errortips)) $errortips = "$name 不符合要求!";  
5 D  m: |. ?5 v  n7 d' A) q8 X" E              $length = empty($value) ? 0 : strlen($value);  7 u# O! S4 L  z6 t; W: P" D
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
5 F% [. Q' U# N  ^- n/ h) X              if($maxlength && $length > $maxlength && !$isimport) {  
! T/ Z$ ]) c7 Z                  showmessage("$name 不得超过 $maxlength 个字符!");  
6 w+ o2 B0 ?8 _. M$ J              } else {  
% h/ V8 Q0 @, t4 c, t                  str_cut($value, $maxlength);  
" k$ `% d) s' \. J$ a8 n              }  . J7 b5 \; |6 a% m8 z* h$ s% Q
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  0 G9 _3 e) K! V) ]
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  6 p' E2 ^( A) ]# }4 A* p8 Z- |
              $func = $this->fields[$field]['formtype'];  
3 _9 Y2 S% h. a# T+ H              if(method_exists($this, $func)) $value = $this->$func($field, $value);  0 M& }  T/ C" }  v) b
              $info[$field] = $value;  
/ ?& x8 Q; }4 G/ g* b1 H0 ?           }  " u$ o; O  u" ^& T1 m! P5 a
       }  6 @% |: C1 [: L% D' {. h; H
       return $info;  : y0 b5 z( {$ x! ?
    } / n. H4 u2 n2 q6 [: v# ^( M0 E) |7 S6 a
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
/ S) a9 c# {* E- m0 x0 C! K, H; o6 a& F. ^
再到phpcms\modules\member\index.php 文件account_manage_info函数
: w1 ~- F! F$ H过了get()函数之后。
. _1 }  [0 x" f# {5 y% a7 W1 }, T, ?8 b7 Z; v* d3 r! A. Y$ o
) G$ C9 a0 g  y5 Z6 |8 O$ H+ T
$modelinfo = $member_input->get($_POST['info']);  
8 j& o2 P4 j5 j9 `; ^5 T6 G" |           $this->db->set_model($this->memberinfo['modelid']);  - [  _6 ]& J2 n& K/ Z
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
7 \8 o: ^/ r  P4 b% g           if(!empty($membermodelinfo)) {  
; b4 u/ \$ [! e. ^              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
& n$ q5 F: A4 x4 d" J. O           } else { * ^5 j/ [2 |) S) q9 B3 d2 N- A
直接带入数据库,update函数我们跟进看看" f" p/ ]. t" s0 R
0 L2 f. D, {0 g- Z+ B
7 s) D6 b; j. u' z$ Y" b
public function update($data, $table, $where = '') {  
; R" z7 R% [; G" s; j       if($table == '' or $where == '') {  
4 M- M$ W8 Z2 Y  m$ u; o6 q           return false;  
+ ^- ]+ X  u  j' b3 F4 s9 M       }  
' w1 F$ b& T, w. x       $where = ' WHERE '.$where;  
, q7 g9 I7 q* M% ^0 m; H       $field = '';  & B& M, ~% m$ y& o/ a
       if(is_string($data) && $data != '') {  
! O" ]) i2 @4 D) k2 o: B           $field = $data;  # r6 |) z* Z1 G5 l1 n) B; l
       } elseif (is_array($data) && count($data) > 0) {  
' Z6 F+ ?: H! g           $fields = array();  
5 H9 W( g0 ?: x7 G  _' b5 h6 E           foreach($data as $k=>$v) {  9 V9 b4 v9 r+ h% A7 ~! a" Q
              switch (substr($v, 0, 2)) {  
) }( q6 }0 _2 u9 M  S                  case '+=':  
/ ~. P* R8 z  D: F" T                     $v = substr($v,2);  : ?9 k/ ]! i- M/ }  l! w$ L' e- I
                     if (is_numeric($v)) {  % F8 K0 G, r9 V, u9 d
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  # k! N$ ~3 F1 q" o6 c9 e
                     } else {  # g5 N5 |/ T# q- G
                         continue;  
' w! v  s# X2 K! M+ y                     }  
/ X& V1 x8 }, K( g) Q+ |                     break;  
0 t) t8 [3 F+ ]1 G5 ~                  case '-=':  " }7 K4 D3 d, y& ^+ O# C
                     $v = substr($v,2);  * D0 i' o0 K) x+ o" H8 ~; J. K
                     if (is_numeric($v)) {  + x( W6 [$ B0 A- |9 @/ X! ?
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
6 J% L4 I& H; B                     } else {  6 v+ N3 v& L( A1 a! e/ W: ?
                         continue;  
5 p/ T* n8 v& k2 ?2 {                     }  
/ n( p0 x) r* X2 N; Y                     break;  $ H, i+ a, l/ x) j3 H( |
                  default:  % P. V& J7 o) o( |
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
# d$ w) m5 h# W$ w              }  7 I( N; b) G1 w: W
           }  
* w/ U* H% F' V4 p* P' S           $field = implode(',', $fields);  
0 n% [* J0 B, T       } else {  $ o. F/ u  Y5 g% \9 r% t+ C' P3 e
           return false;  ; U2 n! p! F. D$ E1 ^- h
       }  
' j8 i4 L% ]+ A1 O5 B* g9 H/ n       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  / o9 B. S" w2 \* b$ x
       print_r($sql);  
1 t' Z2 L. o5 o- P) U# K       return $this->execute($sql);  
' m1 P1 M; g) C    }
- n/ R' F# p4 P$ K6 q从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
. R) {; e/ y# B& o$ F) l! z1 v5 V
攻击测试:
- g6 b7 C5 t) x  `0 [测试地址http://localhost
5 q" e# T8 c' A4 a2 g6 F2 K$ A: y6 Q* Z  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句0 X& [; `/ e1 e* f. W
* G8 u- S) h$ a7 T* o( Q0 Z* V. L
# K9 ]! }% _( n
4 }! }5 F8 M& j
1 k5 d0 m/ T1 x$ Q% @
; V/ }6 @) A# C

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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