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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
, `% @$ D" Q6 G9 x9 L( Y漏洞作者:skysheep
- u4 w0 d9 O, \分析作者:Seay# p4 p0 v7 s! Q
博客:http://www.cnseay.com/
, N) a9 w. z6 n! r( \漏洞分析:
. g9 m8 @+ L+ K  ?$ W1 m! G4 I  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
; F( F6 e# v" }; n, y5 j' ]+ @" k* C
9 A6 G' O& x; _0 m* G  P$ r

# n& I# I- ~: A  bpublic function account_manage_info() {  
$ W/ K2 q: l# P1 ]0 s" Z4 o       if(isset($_POST['dosubmit'])) {  6 E2 F" ^6 M- H; f" m/ F* e  p- I
           //更新用户昵称  
, w, H$ Q7 O" i8 k& {  ]5 b           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  * i8 ]4 V3 L4 Q1 d$ K
           if($nickname) {  
6 m5 v7 @0 m1 R1 R' f' H0 Q              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
0 w$ s8 I5 l- b4 g. N              if(!isset($cookietime)) {  
9 s* w# _. k3 A/ a% M& H                  $get_cookietime = param::get_cookie('cookietime');  
+ t) r6 e/ `" c/ e$ W              }    s3 P) X+ Y! _- l1 Y! a. ^
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  5 [. n. {0 d: D; a" J( c# H3 f
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  " x) C! _$ L3 Y% ?3 {. w
              param::set_cookie('_nickname', $nickname, $cookietime);  
4 I1 u# C( j1 T" y" l           }  3 N4 f5 P' Z* L
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
4 C7 ~7 \- L5 @( R6 P           require_once CACHE_MODEL_PATH.'member_update.class.php';  
4 g7 E0 w! M. J           $member_input = new member_input($this->memberinfo['modelid']);  
& {, d4 C0 z. M9 O; n           $modelinfo = $member_input->get($_POST['info']);  ( k5 \& H( ]5 ^! D& C
           $this->db->set_model($this->memberinfo['modelid']);  4 }5 d7 f+ A, }; u! r
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  7 O5 Z$ Z% I# @# `2 A" i+ V$ }
           if(!empty($membermodelinfo)) {  
  W# J) C) _  [7 ~* A  ?% @: c5 d; h              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
& ?4 O: r: v1 ]2 w           } else {  # e* Z* F' f; |5 R
              $modelinfo['userid'] = $this->memberinfo['userid'];  : ]3 i5 z' R2 E" c% |: Y! x( r$ I
              $this->db->insert($modelinfo);  4 I. F# i4 X* n# w2 _/ f/ m
           }
$ {0 Y& A: E% B' b代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,2 m2 I5 p; p+ H+ v6 v, L3 Y
在\caches\caches_model\caches_data\ member_input.class.php 文件中:: O+ s9 |3 m  w! B* U8 h) U& y

. D2 ~& o; {& t: l5 z( U1 Q4 |/ `7 u
: Q/ V( Y  o1 B" [
function get($data) {  * z. ]* g5 O$ |4 C
       $this->data = $data = trim_script($data);  
0 _* I& G% D: U: c$ B  D: T       $model_cache = getcache('member_model', 'commons');  
/ f+ g; @! q# n# X: i       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  - |6 }* o) f: B# c9 W; \# m
       $info = array();  / ~7 `) h$ m" f: f! ]
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  5 t) u; T" ]7 A7 c6 t
       if(is_array($data)) {  ( C& I) x! ^! X5 i
           foreach($data as $field=>$value) {  % W: K2 V( f) i0 }% O
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  3 r) V+ {/ g3 T  E
              $name = $this->fields[$field]['name'];  / L& w4 x% R4 q# m6 H
              $minlength = $this->fields[$field]['minlength'];  3 b; A4 r1 Y6 r+ x) ]" q& G* r% V7 J% F
              $maxlength = $this->fields[$field]['maxlength'];  
; U. T) y. |& m. r              $pattern = $this->fields[$field]['pattern'];  " _) r4 y3 t, M0 ~
              $errortips = $this->fields[$field]['errortips'];  
! O* k8 b$ r' O+ d$ B- z- V              if(empty($errortips)) $errortips = "$name 不符合要求!";  
* x, a. Y; o: c0 J* y) ~' l; m              $length = empty($value) ? 0 : strlen($value);  7 N% @3 N* u/ l7 q
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
7 u3 W4 L; K& T! E7 X* K              if($maxlength && $length > $maxlength && !$isimport) {  . c# h2 Q( i* ~
                  showmessage("$name 不得超过 $maxlength 个字符!");  ; \! ?8 M+ D; S; S/ _' g* P) z
              } else {  
6 u3 Q! W+ o+ n. X                  str_cut($value, $maxlength);  
/ j1 S. @0 N, h. {8 y% i) {0 A& b              }  
/ o$ Q3 h) U0 R6 s              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  8 A1 L4 V- ]  v8 B
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
; U) m4 E1 n' T' x. n2 U/ P' C              $func = $this->fields[$field]['formtype'];  8 B; C1 T2 G1 ?( v
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  7 R0 M7 i0 N" R
              $info[$field] = $value;  
! \- i0 ^; C6 P) Z           }  - G  f% f( u# a* q, e
       }  7 y& `& A4 ~; T0 y) e7 K8 T1 \* h" n$ o
       return $info;  / r0 ~' S0 a" m* H% D9 [8 Q
    } 2 A5 `/ I3 |$ C5 i7 e  l& M. q
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,& m  u8 D4 o3 @! U0 t/ ?

( D* ^' b) V. U9 \" x再到phpcms\modules\member\index.php 文件account_manage_info函数3 |7 P* U* g9 j+ e1 R
过了get()函数之后。2 w; n* L, |; |4 D

2 K% b+ I1 U+ ^1 h' k! [! t& r 4 z: Z# e% g) R9 O0 j& a  Z3 i
$modelinfo = $member_input->get($_POST['info']);  
3 v3 S2 V# y" [4 l           $this->db->set_model($this->memberinfo['modelid']);  
1 k  z8 e2 n% J* D6 ^, Z+ i& ]  L           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
6 {. D4 y% T6 M' {) N           if(!empty($membermodelinfo)) {  : ?, J4 R! T. a) X* d# q
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
  B9 _8 _+ Q- r& l           } else { 4 Z9 Z2 r0 `# Q
直接带入数据库,update函数我们跟进看看& S: N$ a. P0 V
  V+ w# K1 C  T! R, ?$ U! G0 i

8 V- u! m& O6 e! J* L' rpublic function update($data, $table, $where = '') {  
5 G, u4 A, b, d; x/ H6 z       if($table == '' or $where == '') {  
# H& K7 n* N( e2 t4 F% i% x8 k0 p: P           return false;  2 e0 D1 o5 Z, l1 q. H$ Q
       }  ( J: }6 Z8 G/ ?  n$ g* A- C  N
       $where = ' WHERE '.$where;  ' p9 [: x5 G2 t' g* V2 H9 ~- K
       $field = '';  5 K: U" b1 Q5 ]; o# L& o
       if(is_string($data) && $data != '') {  
2 b8 y+ k  O7 J           $field = $data;  1 W  o+ |! @* T# O* z: Y+ }
       } elseif (is_array($data) && count($data) > 0) {  - c' H. \- O6 G
           $fields = array();  
  c% z/ _. @, ?5 w+ [8 d           foreach($data as $k=>$v) {  
* v1 @) S; W7 r6 W              switch (substr($v, 0, 2)) {  9 _$ r1 p* A* H: W' w4 w
                  case '+=':  ; }% H+ W( c; h8 z& x( E
                     $v = substr($v,2);  
$ S9 l! P# k4 N/ L( p- X                     if (is_numeric($v)) {  9 X5 a, v( z9 n8 d+ [
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  0 q# e3 d/ n4 A$ n
                     } else {  , t. Z9 ^6 C/ h4 _- k+ X( K
                         continue;  
+ W% Q. }$ v9 S8 a+ F                     }  ! [: K& W& z" z! R3 c: i# c7 r
                     break;  
" e9 u, J7 P' i                  case '-=':  1 ]4 c* \% L/ B+ A
                     $v = substr($v,2);  # a5 J' H1 z6 l% f7 q: N) `
                     if (is_numeric($v)) {    x: J/ f, i) j, n$ ^( n
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
& {' z2 ^$ b  j- n0 |0 o                     } else {  
4 z5 @9 }8 N& T" z2 w- }                         continue;  
  p! C; \6 z' s8 c+ r                     }  
" U) U$ Y/ L8 k3 g                     break;  
6 i6 Q& r$ t! H8 F: n" h                  default:  
$ {3 S& x  v2 f4 q( t9 P3 a, z8 K                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  1 P7 t1 Q8 `& ]0 }+ I4 c5 t2 F
              }  
0 n% U0 z* a4 ?$ }" d           }  $ W/ a) n3 g; g- S4 J5 T1 V
           $field = implode(',', $fields);  
, ~& a& n% J9 d% n       } else {  % V, ^+ M, f& A4 i' T
           return false;  1 ^0 C$ u8 s- @, N( ^5 u# m2 E
       }  % k! E) q9 Z6 s0 W% c) k+ @
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  # B# b4 B! a( c6 r4 F
       print_r($sql);  
0 n/ @# d3 ~+ h  r, n       return $this->execute($sql);  * i# J. n/ [* ^6 J, e
    }
  J* `3 f- `* {9 v, L; k0 `从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
! \' d6 Z; x7 f/ v/ e+ w% k- c7 a  v9 @7 u
攻击测试:1 p( f' F( N2 d! g& C
测试地址http://localhost( R2 ]6 L. U; Y& v
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
& w6 _% `/ a3 N: D4 w3 H; X2 @2 @1 ~; S7 e# B( v( D
$ d) Z) p- {2 T: C
, T8 z- z/ \3 o

. E) p& L& C9 f$ m, n9 h
9 J2 r* A+ \3 G) @  e

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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