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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告% x8 h7 e, K& Y( P0 ^2 p
漏洞作者:skysheep, V/ C5 t* y1 d7 \. A
分析作者:Seay
3 }2 O  b" c' \8 A  r: K博客:http://www.cnseay.com/
3 M$ r6 _/ d2 L4 r漏洞分析:; D1 C$ z/ X' k; f% n4 G% A& y6 L/ J
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。1 z- h8 f' ~! ]. q/ h6 X
! |3 R5 s9 d, E, W1 I' k; U/ e

) v: d* N$ b4 v* ?$ ]8 A
5 G7 r# E  D7 _* ]public function account_manage_info() {  
  O) K1 p0 I7 n7 ~3 E9 o' B- {       if(isset($_POST['dosubmit'])) {  " ?7 x0 a& C5 o# R( e
           //更新用户昵称  
1 G  b6 m3 E) w& F. ?           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
- j$ E# m- L9 p8 x7 g6 Z+ }           if($nickname) {  
& v8 o) m3 V" h7 X              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
; x* C. C2 F1 K& [9 M              if(!isset($cookietime)) {  ! [* r! H9 Z& t" @+ {% g
                  $get_cookietime = param::get_cookie('cookietime');  
7 W' ^5 ]1 ?$ `( ~              }  . S3 j% [5 f! [8 k
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
0 E; r* J' T+ S/ \              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  ) Z- |) |& E$ `- i. i4 p
              param::set_cookie('_nickname', $nickname, $cookietime);  
/ t5 D5 m: Z! E! h/ P7 H           }  5 D$ P) i3 ]3 n5 R$ R; B/ P1 l, l
           require_once CACHE_MODEL_PATH.'member_input.class.php';  0 x2 u3 u* |: l! L# {
           require_once CACHE_MODEL_PATH.'member_update.class.php';  8 V. d/ C9 M5 P' L! N. l
           $member_input = new member_input($this->memberinfo['modelid']);  ! e1 ^1 I8 |0 F- h
           $modelinfo = $member_input->get($_POST['info']);  & b' C( }  p& L6 X
           $this->db->set_model($this->memberinfo['modelid']);  # N9 T8 A7 I4 P' W% }  S7 P- f  Y
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
2 w3 M; h4 H6 l+ O* Q           if(!empty($membermodelinfo)) {  
  [) Q" s0 l2 W/ Y              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
( s8 R5 L& j: m5 f" C7 G+ e           } else {  
! X6 u. O- i" Y; L3 w( i- ~, @              $modelinfo['userid'] = $this->memberinfo['userid'];  % l/ j% T  M$ i2 Y  u3 f
              $this->db->insert($modelinfo);  
$ G7 D- e% p. }3 E' K; @* E6 V           } : H' V7 k/ G+ a) j7 E% q* u8 z4 H
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
7 V  _" O+ G4 {1 |$ J+ M在\caches\caches_model\caches_data\ member_input.class.php 文件中:
% P, {. \& C6 l( t/ J3 w4 {! _6 W
, Z- B/ h9 J- \% _" R
7 x* i" C7 ~* a
function get($data) {  
- f6 A5 p% M2 r6 }% j1 H* I       $this->data = $data = trim_script($data);  
8 H. D. W; t( a       $model_cache = getcache('member_model', 'commons');  5 C- E- a9 x( Z% J4 Q: ^
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
+ Z( ~4 X" h& ~. @' |$ X! W# P* o       $info = array();  
3 [. K7 W2 ]1 {5 _* \; u       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
+ {; A& L+ @6 [/ Z7 K$ x5 r/ |       if(is_array($data)) {  
* d" B) f8 b$ a           foreach($data as $field=>$value) {  ; W1 {* T9 S; c( Y8 @% }
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  , D- S6 u3 P1 t# P8 i
              $name = $this->fields[$field]['name'];  % Z, v: p, A+ b1 ^7 ~4 v5 o2 N; t
              $minlength = $this->fields[$field]['minlength'];  & Q0 E0 n# |( @# J+ \) Y9 \# P
              $maxlength = $this->fields[$field]['maxlength'];  
( z2 I  E* H  e3 E" l              $pattern = $this->fields[$field]['pattern'];  # M" n3 _8 G8 b" e
              $errortips = $this->fields[$field]['errortips'];  & F$ f: N; n* c3 `
              if(empty($errortips)) $errortips = "$name 不符合要求!";  7 w7 T) p7 O0 E6 t/ J& [4 \
              $length = empty($value) ? 0 : strlen($value);  7 V" n: l$ p% j# F
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
3 ]! Q) g2 N) Y9 q; m8 ]              if($maxlength && $length > $maxlength && !$isimport) {  
! F2 _" Z5 W/ z/ P8 {* G+ s                  showmessage("$name 不得超过 $maxlength 个字符!");  
" A7 }$ z1 L" o2 w/ b2 h0 p              } else {    k; ^0 J$ o: A3 U% d
                  str_cut($value, $maxlength);  
2 g, Q1 \4 r' Q# u              }  
% I- T( n2 c/ g$ X3 d0 _8 I              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
" Q8 m" M  x$ g                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
( H) J4 l. C4 b4 x/ g" w              $func = $this->fields[$field]['formtype'];  
0 H1 r" Y' Q  L9 i- S              if(method_exists($this, $func)) $value = $this->$func($field, $value);  ' O7 w/ a! X) s& _6 J1 E
              $info[$field] = $value;  $ I& e1 K8 C  l
           }  9 r- d: H  U& F  a) Y4 C
       }  
$ y7 W3 u) e# L9 f3 {       return $info;  
. |# F3 `: O- p    } 2 [6 v8 U+ X9 n: p& O
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,: r& }! n% j* |4 X7 G; F

  M' k+ t* L' t: x2 j再到phpcms\modules\member\index.php 文件account_manage_info函数
6 L% \  t/ C1 A/ j- z过了get()函数之后。/ Z# ~* l7 w. n; V

) _$ l; i0 m; h
: b3 n9 @; o/ {1 c: ~9 j$modelinfo = $member_input->get($_POST['info']);  ' y) a% V6 P" f" k" G, T
           $this->db->set_model($this->memberinfo['modelid']);  
  Y3 m: }/ I1 ?& s* h           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
2 g" v4 ?! \$ U" q7 C           if(!empty($membermodelinfo)) {  0 @7 H4 c: V7 k/ q
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
1 y8 k% y, V/ k4 u- d           } else { . l3 C2 p/ l* |* S
直接带入数据库,update函数我们跟进看看" W$ |3 g9 X+ m5 k$ l* r- R

) ]0 V$ i& R3 }% s( L
7 T' [$ C9 q* P6 [4 ~6 i2 B2 J, Bpublic function update($data, $table, $where = '') {  5 F* \! q1 `- k1 T+ p" V1 H0 K4 L" `
       if($table == '' or $where == '') {  + b3 X8 b; V: P# O, H" ?
           return false;  : V! D+ t' ]3 r5 p
       }  
0 J* g- h1 T5 d: M  J. |' r       $where = ' WHERE '.$where;  
: z# U) R. [1 \; j- N9 F, V       $field = '';  
- i& e' n3 ^2 Y% W% H. U/ }       if(is_string($data) && $data != '') {  
8 w% {% u! S0 [4 p& ~5 J% L           $field = $data;  3 N! f0 {5 I% L  A7 Q
       } elseif (is_array($data) && count($data) > 0) {  4 _3 u  D7 H1 Q5 ?  q& O2 E
           $fields = array();  
( M2 V. W1 T. C9 k           foreach($data as $k=>$v) {  
. l; u  D0 z9 v              switch (substr($v, 0, 2)) {  - B- }: }& Q3 Q# j! \, U
                  case '+=':  
& r5 U5 T$ H4 o5 Q% }8 [7 c1 O: v- d                     $v = substr($v,2);  
& z4 h1 @! @7 h+ S  ]! ^# [' i; n                     if (is_numeric($v)) {  
  u! y+ A, U; M                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  7 r( b% B. o4 A/ K! A6 O6 a$ \) \
                     } else {  % U9 S) W6 l. S5 n( h: f; z
                         continue;  " G" l2 g0 S3 {
                     }  % w) r+ R" m" W, `1 {6 z
                     break;  
$ z* w' C- {$ ?6 N+ {, I0 u                  case '-=':  
: y: a! J) M- w9 K% s9 n+ q                     $v = substr($v,2);  
+ O$ {9 t4 D: }5 Q7 q+ @8 n                     if (is_numeric($v)) {  ( n( f2 r; e/ }% b
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  : B4 e9 u# g% z
                     } else {  
  Y0 q4 M$ g+ Y0 p* C                         continue;  
+ A" D9 I+ `4 I6 p! s* [8 ~8 q( p                     }  
: F4 H# F! y- d; D& T! k% }                     break;  / S, p& K% ?4 R. j8 @
                  default:  
  X! O7 }  V# T2 i                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  : ]  \* q3 C/ ^% V! B
              }  * d% A9 \9 K( @% w5 q
           }  
8 l' f) r; v& |! X           $field = implode(',', $fields);  
& S1 R5 Y1 ?( h, A. _       } else {  ' g& ~4 t2 e8 O& \' z5 U: y! m
           return false;  6 F/ p' H( m8 Q4 X8 S
       }  
$ I7 c+ k2 G: ?6 C$ X       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  . t' D* B5 I9 ~% u4 T  P
       print_r($sql);  ' u/ V3 O& F5 s- u
       return $this->execute($sql);  
' J) B. m- y2 F9 u4 t    } 5 E  w" |  n2 v, X( f" R3 z
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
+ _. L+ o/ Y( ]* e
8 Q5 T" Y" k/ z' f% U$ [1 m6 k攻击测试:, Q2 _: G( Y7 E8 Z  Q8 f3 O7 q
测试地址http://localhost* W9 r% m* l) d0 |* e! L' S0 e. `
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
+ U4 \1 ^* {9 A( {; o: R' a( T- h- Y# x& a' F+ ]8 p1 H

, C) V  u! V: f5 e4 w, d. O  \1 y7 `  R: Y) `
& ^2 g  T' [" R& t
7 y; w6 t, [/ a$ G& j5 m) i! C& c" z

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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