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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
5 `. e3 g( T, \+ S漏洞作者:skysheep
. H3 f: O9 [9 i6 \分析作者:Seay2 c6 Q) p$ x- F# y1 f) Q1 X) v
博客:http://www.cnseay.com/
+ A  C! u0 Z6 x漏洞分析:
+ Y7 o, }8 F6 K  q; ~* N/ N1 ^  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
! k# B9 u8 x  E# M9 k2 _9 x3 p9 [

" N! {( P6 h: @ & a, G; ^% D" A; y" X; R) c2 j
public function account_manage_info() {  ! D- u2 j! F) F
       if(isset($_POST['dosubmit'])) {  1 `& w) z; |) O/ R
           //更新用户昵称  
) [8 R$ t- R5 R( S  U2 a3 v/ H           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  9 \: J5 P9 z' b
           if($nickname) {  
  \* x) A+ x! V8 ^8 _$ _+ Q& W& t              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
0 j1 W' q7 d) L              if(!isset($cookietime)) {  * Y% N9 Z/ b7 c& N
                  $get_cookietime = param::get_cookie('cookietime');  
2 P$ \0 w7 t3 G  l: ~% |( H              }  1 U5 K) C6 G+ k/ `
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
3 g- w  e) l& e6 K; G+ ^( u              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  , X0 u2 q( ~8 V# ?9 @$ L
              param::set_cookie('_nickname', $nickname, $cookietime);  
2 W9 J' n0 C( ~; n+ f/ X8 ]           }  ' G$ Y' y/ p. l( f
           require_once CACHE_MODEL_PATH.'member_input.class.php';  7 |* {' u, z" \; }
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
9 q, g9 ^; u7 d4 u- G5 [           $member_input = new member_input($this->memberinfo['modelid']);  
. q! |( r! e9 W1 D( V" H  w. I, D           $modelinfo = $member_input->get($_POST['info']);  & d% L3 M5 B2 Q! N8 A
           $this->db->set_model($this->memberinfo['modelid']);  
! b8 Y; ]' n/ a2 N           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
, d. s8 ]" [/ C/ {' ^           if(!empty($membermodelinfo)) {  
6 v9 `8 \# ]. |* h/ U              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ! ?+ d* i2 I( v* Z* F
           } else {  
) e9 G; c: u  _( ~* J              $modelinfo['userid'] = $this->memberinfo['userid'];  0 w+ `  r5 J9 z6 E
              $this->db->insert($modelinfo);  
  }  d: f. ?0 J1 ^" k/ |           }
- C1 V) g# y" Y4 D3 u- t' |代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,% f4 _$ j4 x# j
在\caches\caches_model\caches_data\ member_input.class.php 文件中:5 [0 Q/ d& E/ J. F: Y9 Q0 T% A
% t. S5 ^4 m( A
" ?0 _% I4 \3 Y- B$ O
" F7 {' v3 k) N; M1 Y. Y
function get($data) {  1 e) t' t. w$ R- D- L
       $this->data = $data = trim_script($data);  ! X) D. g5 t- z, w' F0 I
       $model_cache = getcache('member_model', 'commons');  ( q+ f0 o7 s6 x# Z
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  7 Q& d; m$ y. ?$ ^
       $info = array();  
0 s# y  q/ H  I. x7 q       $debar_filed = array('catid','title','style','thumb','status','islink','description');  , p1 S) j  H+ |- k* v
       if(is_array($data)) {  / m6 H$ O% P/ Z+ E6 D
           foreach($data as $field=>$value) {  
* H2 f3 ~6 v9 E8 |              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  6 {/ F* a) x3 a! X3 K7 {
              $name = $this->fields[$field]['name'];  ( P, D8 C- D' Z% I& F
              $minlength = $this->fields[$field]['minlength'];  
! K* _; R% e6 M1 N9 O8 F              $maxlength = $this->fields[$field]['maxlength'];  
) ]* N3 Z7 ~8 Q3 s5 Y1 A& {              $pattern = $this->fields[$field]['pattern'];  7 @+ E, p8 L: L" X8 W
              $errortips = $this->fields[$field]['errortips'];  . R  P2 a7 b% x7 z
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
! O3 ]  |. S' w              $length = empty($value) ? 0 : strlen($value);  , z0 v+ t$ [5 o( X
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  1 }' J# e$ |$ }+ M3 b8 `6 M$ R
              if($maxlength && $length > $maxlength && !$isimport) {  
* {- D5 D' g2 R  ]. Y- N                  showmessage("$name 不得超过 $maxlength 个字符!");  
9 V/ h7 i8 _* [# K              } else {  
- b7 V& r# _) X' ^) M' `6 n: {                  str_cut($value, $maxlength);  " g0 N2 F7 ~% q3 k2 ]
              }  ; w/ O& ~  ^- g# X- z
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
+ ~- t- [' u3 q2 u                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  4 a+ G0 N6 i- O+ v( G( T
              $func = $this->fields[$field]['formtype'];  
: G& g1 W' E8 A2 [" t  ~              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
) T4 Q, e$ `  ?9 f% n              $info[$field] = $value;  + E3 ?' I8 V4 D& s- q
           }  0 t# F* |0 b6 ]. ^7 d
       }  8 R$ \' ?4 U6 I
       return $info;  
+ F: f" w0 E/ D5 \    } ' e3 P$ _" Y6 A6 o0 `  |6 E6 f
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,$ _$ [3 z/ [: `$ \) q( [
. S5 V8 G6 N1 G8 x1 o
再到phpcms\modules\member\index.php 文件account_manage_info函数2 X' A* \5 I) |( t6 }" _9 D* q
过了get()函数之后。$ y' n% M: W3 u3 j3 Q7 Y- f: r- Q2 J
, \( @/ T& B: p  K* t( I- w1 @
  C0 B9 G) {: o) L3 E% B4 X
$modelinfo = $member_input->get($_POST['info']);  $ i$ |6 n( G- R9 ]
           $this->db->set_model($this->memberinfo['modelid']);  : e, r+ ^% |, ^9 e) E7 T8 V
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  - i/ w1 ?6 \) k
           if(!empty($membermodelinfo)) {  
' Z3 O% ^2 r. U2 ~% [              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
0 ^& M" \8 `  g: {/ |! H% J4 P+ D           } else {
: [- x9 B: K- R/ x/ f3 A直接带入数据库,update函数我们跟进看看
1 i) _% k& @  _+ K, F6 b# ?( B" |) h/ H- ^3 a- z3 S
+ m8 I" ~1 }4 C3 \) o, V/ s
public function update($data, $table, $where = '') {  " C" }; \6 D3 a" i7 H5 e
       if($table == '' or $where == '') {  
2 p3 C2 H5 g" Q& B# X) G2 {           return false;  
, a* Y6 a- I  V9 q4 m0 b       }  
5 H& k3 O- U. q$ j" g       $where = ' WHERE '.$where;  ! Q( R& N  N; t# ]- U' O
       $field = '';  
  g  g0 V# t9 i6 e* @) {       if(is_string($data) && $data != '') {  
( y+ X) u) q4 E3 y* R% ]           $field = $data;  ) ~: X( Y1 \- F# F+ r
       } elseif (is_array($data) && count($data) > 0) {  
, n% e. S% x: _9 L4 ^( o           $fields = array();  ; X- ?! F* z( w3 \4 ?9 m4 n: R% n
           foreach($data as $k=>$v) {  
" }: R( P+ ]: v! m              switch (substr($v, 0, 2)) {  
3 i3 L1 o2 w3 V4 t                  case '+=':  
$ p6 i9 K; d1 N                     $v = substr($v,2);  
0 ^: e- d8 S9 {6 U+ o' L+ u                     if (is_numeric($v)) {  0 r4 M7 K) R$ N4 c3 g+ U
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
+ t" n9 v/ x2 R7 F3 d4 g                     } else {  
6 N& O/ l6 H/ J4 d: U" f3 i                         continue;  
) Q! s3 _+ q7 \2 p$ K% q# s. \; \                     }  
# e1 f4 w9 ^' c: `6 q  L# Q1 Z                     break;  
( [1 B, d8 ]) s3 q                  case '-=':  5 B& i) q2 I3 Q, I3 X
                     $v = substr($v,2);  + o. s# a$ g/ Y' g2 Z
                     if (is_numeric($v)) {  * V" @/ `) Z, T" R9 \' R3 P
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
0 F7 V' r( X8 v( Q0 k                     } else {  
, [) M2 x, ^$ C& e( L                         continue;  
0 G! X* z) ^; @4 `/ p                     }  3 q5 u( I5 [4 k" b- U' O1 R
                     break;  
" A9 y0 ?3 `- N; e                  default:  ( B% R8 J% D* R2 m8 h, s
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
8 d* k3 R. V5 |5 n5 `              }  7 I: L5 C$ c+ ~8 c1 q+ W
           }  . o$ ~% m' v7 n6 j
           $field = implode(',', $fields);  
4 m  l5 w5 y8 a! F3 G       } else {  
# I4 k+ ]9 g0 F& {4 N3 Q           return false;  ) Q1 _$ v4 i- Q1 q! O3 F8 Z5 V1 ~
       }  
4 u: e' I. @2 R+ x0 j' ^       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  . g- a  W' ^* _/ Q5 m
       print_r($sql);  
  {4 v: _3 D4 |4 b       return $this->execute($sql);  
5 _+ n1 E( D+ h  T. q; a! l$ x3 W    } 9 z& B* o% f0 S" }( L
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。) B6 K* q1 `! K; {
$ b9 D0 L) G( H6 t! \
攻击测试:
2 _6 |: E: G/ K! }  I4 K. Q$ p, F( e测试地址http://localhost" p2 V' r; }- a! y* m! H
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句9 u  M7 @9 z' O  C5 H
9 T3 Z# w3 o& Q$ w7 Q

+ H8 ?0 R7 x* U7 c
+ E# V7 K7 i) x/ A' ^5 c- c0 j! d$ D9 R4 I8 C
0 I. a6 k' V: w1 k7 h- C

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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