中国网络渗透测试联盟

标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告 [打印本页]

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
9 a0 t7 t1 U. r5 h+ _, n9 [+ n. [漏洞作者:skysheep0 C6 n* q* ]' o) e6 q1 [9 D
分析作者:Seay: s9 p5 D# `7 B+ y4 O- a
博客:http://www.cnseay.com/* O) _1 T; D( K' m
漏洞分析:* n' Z  ^+ b; |0 D* m' E
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。% G! {& B/ z: y. [7 T
. F) ?- l! p& z; _, J
& E3 X0 ~- }0 A3 ]. f
+ F) r) T; r4 t) y6 d9 C4 o
public function account_manage_info() {  
+ Q% }7 A  W) s5 r) n2 J- `1 H       if(isset($_POST['dosubmit'])) {  6 [2 T5 t. u- r/ D
           //更新用户昵称  2 W6 v  F$ T" I/ \8 C2 d
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
1 j2 _& m! A% s3 _; {5 C9 K+ F           if($nickname) {  : a1 W& s9 x5 I7 Z# q" H
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  8 b0 R6 A6 K0 H% _' g
              if(!isset($cookietime)) {  4 V/ p8 H+ U) X* r$ O
                  $get_cookietime = param::get_cookie('cookietime');  ' t2 m0 p; l8 v! Z* k, p
              }    r3 X9 H8 s2 C& {/ w( o3 J2 y
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  7 G- Z$ k8 d. g$ v7 m, e& A# B9 o- {% G
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  6 e, B- u9 f% g5 `
              param::set_cookie('_nickname', $nickname, $cookietime);    h" K0 w& J; L
           }  % d& L% K* f) e2 a4 Z9 D
           require_once CACHE_MODEL_PATH.'member_input.class.php';  6 _9 W  o0 {5 X3 U3 E% e
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
1 C1 Q* ]  s, x6 G  `3 T! O           $member_input = new member_input($this->memberinfo['modelid']);  
) y' ]4 Z+ l! }  a! q$ Z0 h3 p           $modelinfo = $member_input->get($_POST['info']);  ' V$ m% B, h5 R4 Q1 j
           $this->db->set_model($this->memberinfo['modelid']);  . q2 v7 M0 P9 q6 C  ]  h
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
+ i  R- j. d2 ~4 e2 T5 m5 |- ~           if(!empty($membermodelinfo)) {  * P8 Q& F* H$ j- C  \5 Z- C
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  1 _( \- \* A: {: y
           } else {  ; v! X1 X$ Z# q3 w- }9 X
              $modelinfo['userid'] = $this->memberinfo['userid'];  
/ f  e) \6 l, c0 ~              $this->db->insert($modelinfo);  . P$ R# T, q& Q. |# I
           } ( y, R0 y7 a/ u! z" j  W& l( ^
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
' ~# ~% F3 B! b. ]在\caches\caches_model\caches_data\ member_input.class.php 文件中:
$ b! P0 r$ X  M+ [. U
8 d5 `# F) W$ b1 G
1 i0 Z5 z7 r% q9 [: z 2 M- K: K- j) R$ P" h, g6 J
function get($data) {  
9 p) |- N; I% d0 w3 h( Z3 m       $this->data = $data = trim_script($data);  $ q  Q0 R! d3 V3 Z0 Z2 \4 n5 D% E  z
       $model_cache = getcache('member_model', 'commons');  
, N/ O% @: ?8 t, ~, a' U       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
& N# v4 ]% q0 b3 c' p/ _% k       $info = array();  + {1 _* \0 e- [# {8 w" m
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  & M: M' W. `+ Q% x
       if(is_array($data)) {  3 }4 g6 X) i. O2 N8 \! l; u
           foreach($data as $field=>$value) {  
; y2 L* T, Q( {( K              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  0 p9 Z7 l; O) q. g) ~% s
              $name = $this->fields[$field]['name'];    e! ]( H7 `% _, d
              $minlength = $this->fields[$field]['minlength'];  
0 K& B% T8 ]) S2 N7 L1 {              $maxlength = $this->fields[$field]['maxlength'];  
/ x# _/ e/ O& I+ \. f* ~" @2 D              $pattern = $this->fields[$field]['pattern'];  
( D. C9 C6 ~% w5 a" Z4 S5 y              $errortips = $this->fields[$field]['errortips'];  
& O. V# e2 c- ?, H8 l) X, |              if(empty($errortips)) $errortips = "$name 不符合要求!";  
2 j9 o! ^  g: p- H# j! j              $length = empty($value) ? 0 : strlen($value);  
% H- l8 V% a: ?2 l* G* [0 O              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  . d/ G  Z9 L  v. `! E6 ^
              if($maxlength && $length > $maxlength && !$isimport) {  # S4 b: `! A& D1 N/ l
                  showmessage("$name 不得超过 $maxlength 个字符!");  
9 H! S/ A# X9 V& m, O              } else {  
- i6 f( ]5 ~9 |2 R  e7 R; w                  str_cut($value, $maxlength);  
1 R8 @  b/ K/ a2 ?& G              }  
! I, `. b8 k! e% g              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);    V  G, I: p6 C& y2 b6 p
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
" Z: b) h: W( t. |2 F+ P              $func = $this->fields[$field]['formtype'];  
& }, Z! k4 k7 q+ S              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
4 h2 v7 |8 |' @' W  `              $info[$field] = $value;  
/ }- V: c. j+ f: U/ x' L           }  
3 r; k% c, @6 }       }  6 f, z  q8 q& K! A" E& z5 f
       return $info;  7 L' \1 E& X4 N
    } . I: v2 a4 {& D5 ]" {+ ?1 j
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
& S2 N$ O7 \1 `1 V* z
" J& L. U; U( \" n再到phpcms\modules\member\index.php 文件account_manage_info函数
# O+ `/ d. y) s过了get()函数之后。
) `9 j  g4 p+ q1 n
: f, j9 I! x2 }# a6 P $ C# W$ e3 u' B7 M5 I% o; L
$modelinfo = $member_input->get($_POST['info']);  
- e! V7 m: C+ A( c5 c- V; Z1 F           $this->db->set_model($this->memberinfo['modelid']);  # g8 O9 o) K8 D2 y1 K/ B6 c% z  W0 G% P
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  " ?; u* \" ?1 o6 T  G+ n* B" J4 R
           if(!empty($membermodelinfo)) {    B* r9 h( S2 _+ d9 H9 T$ u
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  9 ]4 K. \2 ?' h  V. \/ q8 |. ^
           } else { 5 t& g/ ^$ P0 M# I: N5 J4 ]; s  I0 @
直接带入数据库,update函数我们跟进看看( D7 R3 F$ @9 B

9 r( ]2 K; ^1 @
+ [3 I: E3 r% F% T3 ?; b. npublic function update($data, $table, $where = '') {  
/ m7 S1 R5 ^, @" z. S       if($table == '' or $where == '') {  ; Z4 q+ G) H, A1 c* ^0 j
           return false;  
! Z& z% _, I' Y0 w. h% J$ U       }  
- b; m1 g+ D) g6 R  u       $where = ' WHERE '.$where;  
* t2 p- `: H. S! j3 ]9 o2 l       $field = '';  
8 Z. B$ Q7 U  F2 V$ D       if(is_string($data) && $data != '') {  
4 V6 u8 t/ \3 }2 U$ g           $field = $data;  
+ Z% M# B% F/ }; J/ c       } elseif (is_array($data) && count($data) > 0) {  
# s4 e" s8 J0 T           $fields = array();  
0 S9 {( |8 R6 b, \$ u, U           foreach($data as $k=>$v) {  
6 ^. V. S+ R( Q& o$ E# I! G8 @              switch (substr($v, 0, 2)) {  
$ Z% @1 J: ~& a* O                  case '+=':  / u: m  E# j% W* v; y9 F9 o. b
                     $v = substr($v,2);  2 [5 k* B8 ]+ k6 Y# |) @. p+ v/ z% g
                     if (is_numeric($v)) {  0 F& O1 G! m' n$ d& N" ~2 i
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  2 y- z# a2 I, R& W+ ^/ b0 H. W
                     } else {  
6 Q3 ^( N, h5 a2 y/ i2 Z* D                         continue;  ) U* s4 j5 k* R- @* u# R
                     }  
  X5 O( i; J- t6 x( p" u7 t                     break;  
7 {/ p  O' k5 D. _                  case '-=':  * j* M6 g1 x/ m2 g6 O; m+ G7 o
                     $v = substr($v,2);  6 m  x8 s6 }4 D
                     if (is_numeric($v)) {  ) k: p1 k$ @8 O: n! \, m; a4 s3 N
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
6 g. u8 K" P% D0 Z5 Y7 m                     } else {  
9 {0 ~: l' P7 P) o$ S1 f7 p                         continue;  7 x3 i9 z8 s" L- a: t# n
                     }  
4 S& \- i) [- ~- ^                     break;  ' \5 R0 z( Q, ^7 j+ v. g. b* s
                  default:  ' D, Q3 }- _: e8 _; X- q
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
' F2 K( k- c+ n2 A* S              }    m7 T3 Y, f, A1 z+ y4 z$ l# V1 m
           }  
% E, Z4 Z3 r8 s  I% f# h           $field = implode(',', $fields);  
$ Y) ]' Y4 M7 f8 H+ e       } else {    Y3 X: p8 g) @+ r; d/ E
           return false;  
1 \0 C' t- g$ b- h' e7 H       }  9 U+ L" m" M+ ^: l" ?, P
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
1 S$ X6 A  R2 g. s& I       print_r($sql);  
% N: C: K- \, f$ d4 J( \1 `       return $this->execute($sql);  
3 o5 k1 _; C( z) k    }
) V: ?& h1 g  \从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。, @% R* o8 F7 p# j4 V
0 |; _) z" T& m5 e; m
攻击测试:
, d; R0 N! f+ }; n( L测试地址http://localhost
9 k6 V' {: W7 ?/ t1 V3 O* T6 q  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
+ |2 |3 ]' T% S+ Q( j5 x% c9 j8 q- P1 d" m$ n8 x5 U
[attach]179[/attach] 1 P! `3 P! X0 o( t2 [+ i
, `) L7 B) u% g* P) f3 I

+ j3 D: X$ Q8 ]4 |" ?" X" M[attach]180[/attach]
3 g$ O; z$ ~7 N! N




欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/) Powered by Discuz! X3.2