中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
$ m. w% I3 X% \5 {3 [, d漏洞作者:skysheep( D8 J: T' s4 G+ W, G; a/ a
分析作者:Seay
4 j2 @3 ~4 d/ E: z7 j# Y; h博客:http://www.cnseay.com/  f) ^- P9 S* \* k
漏洞分析:
( Y, ?/ Z* z  @) [  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。& h- ]7 H8 Y4 y) y
& M- ?. P" e8 [$ W

& O( G+ R, P) ?/ U; h
  G) a3 F- C: t" Z5 s2 ]public function account_manage_info() {  1 c9 r) B5 x! E0 E& I; |
       if(isset($_POST['dosubmit'])) {  
% [' w& S2 d. C% S           //更新用户昵称  $ Y0 p+ Y( x' Z
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
4 v4 Q; a4 Z6 _$ E5 e           if($nickname) {  ( d; E" r9 ^4 x1 ]( K
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
& f- N) k, {" g9 w+ x8 T5 N              if(!isset($cookietime)) {  
. B; x( {4 a! d  v; b8 i* ]                  $get_cookietime = param::get_cookie('cookietime');  
. u: j! _' j: {              }  " _9 e) k, U5 u1 ^' m
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  ! f3 R. ?7 D+ Y/ F
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
; o3 K% b- g7 `* o              param::set_cookie('_nickname', $nickname, $cookietime);  % C; O6 q# s9 Y; A. {" Q
           }  , s2 g" F, R# e: Z! B
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
  ^+ o: o- ?5 d! A# \           require_once CACHE_MODEL_PATH.'member_update.class.php';  0 ~* b7 r: j" [2 s/ X+ \
           $member_input = new member_input($this->memberinfo['modelid']);  % [! Z# g5 w# S- Q* e
           $modelinfo = $member_input->get($_POST['info']);  3 n* v! J* j4 m% T, d# X
           $this->db->set_model($this->memberinfo['modelid']);  3 _. [. n. r% c9 t- k" F
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
" S$ {7 A9 {3 y& H+ }           if(!empty($membermodelinfo)) {  " A; R1 W7 I; p$ z7 j
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
, s' E* s7 R. I( k3 j           } else {  
* B- }6 `4 [6 p+ d/ g1 d              $modelinfo['userid'] = $this->memberinfo['userid'];  & q( _5 F) m5 X; e3 Y$ p
              $this->db->insert($modelinfo);  
$ T1 M5 f: U, I- J           }
) F: ?9 ?- m5 F2 ^+ i% b代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
1 D2 W! I, j# \) w! _在\caches\caches_model\caches_data\ member_input.class.php 文件中:8 P2 {" w. N6 Y/ g

9 [7 ]' y. C: U7 v8 i1 o
) D+ B# q( i: r8 F* C* Z2 f ( @6 o3 ]' R/ R& |
function get($data) {  ; g  E' R3 I1 n8 t
       $this->data = $data = trim_script($data);  
0 i6 k+ K" g1 c3 O9 G2 |% |       $model_cache = getcache('member_model', 'commons');  
- s. w+ Z! a% X& I% V: g2 E4 v3 M       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
/ H5 q' K$ e3 }+ y# J       $info = array();  $ i; Y( q6 ~# s7 I. R& F5 i
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  5 D# a  V, s, t
       if(is_array($data)) {  ; b. ^# g1 f  u/ e
           foreach($data as $field=>$value) {  * g5 \7 V! j  @, }8 f& s' t
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;    {, G5 O5 V4 C; [
              $name = $this->fields[$field]['name'];  & L9 a" A* u1 v2 E
              $minlength = $this->fields[$field]['minlength'];  ' g5 g2 ~% A. s3 ]
              $maxlength = $this->fields[$field]['maxlength'];  
5 ?1 Z* d2 L* v6 q7 d5 W              $pattern = $this->fields[$field]['pattern'];  5 M* {; ?& i% v: }0 `" F  W
              $errortips = $this->fields[$field]['errortips'];  
3 L  C1 X' g2 L: u5 b5 X9 L              if(empty($errortips)) $errortips = "$name 不符合要求!";  ; Y& v+ ^; B" ]: x/ l
              $length = empty($value) ? 0 : strlen($value);  2 O! m" Q, Q3 O6 Q- F& u8 T
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
: N( v, l( J# j: t2 l9 ^+ }              if($maxlength && $length > $maxlength && !$isimport) {  * k' R' y. q: v; |* @
                  showmessage("$name 不得超过 $maxlength 个字符!");  " @, _  X# L: X. D
              } else {  
) T; ^+ ]6 S- w% J                  str_cut($value, $maxlength);  
  }+ A' `% c  k: M2 Y  p: n              }  
1 u2 @, v: C( j; ]6 t              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
1 F) s' `4 w9 M- u# ?                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
+ u! S: Z* Q8 _( ~, N6 C              $func = $this->fields[$field]['formtype'];  
) {$ X9 _  n7 `6 ?) }* ^; D              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
! m9 j+ U5 v" u) m/ y) C% G$ G+ |+ c              $info[$field] = $value;  1 t5 X8 t; V' P# s- U9 C; `: A
           }  
) w6 J2 ]  H: a$ t       }  
* O' s, q- ~9 r5 t       return $info;  
* F( [1 b+ \" v1 e    }
4 k/ L; X0 B) L3 {% [1 ]( v: Dtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,. L! n; Z# s" A; Z4 R1 F9 V
" O) k; C/ A3 g1 d4 v
再到phpcms\modules\member\index.php 文件account_manage_info函数6 N1 d) C3 A7 d/ r0 Z! t( O
过了get()函数之后。( f2 L$ J, W& L5 Y1 {+ V
# Q4 S$ _9 K- z" _6 X# y1 F

. v; r2 C; c4 w$modelinfo = $member_input->get($_POST['info']);  
& r6 }$ K, v2 S9 _& K: ]) w           $this->db->set_model($this->memberinfo['modelid']);  7 q2 _5 _) B1 L' x" q. {( \, z
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  1 Q, {7 v/ }  L- @% l4 j
           if(!empty($membermodelinfo)) {  1 }6 p* l( [; W  q6 {
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
' S" T! H  e7 Y. P% `           } else {
4 M/ }4 ^' X; @: C8 s直接带入数据库,update函数我们跟进看看
0 v8 j) ]" _) U5 s) r0 h7 W
6 _0 R3 R/ \' v" u" U " }" Z9 r6 h' N
public function update($data, $table, $where = '') {  : e  r. @9 x0 Y* T* T$ \
       if($table == '' or $where == '') {  
- X2 d; s' o$ t& }, ?           return false;  3 A: K6 ~2 R4 q4 q7 M$ M5 Q5 _+ u
       }  % t% e/ `5 o4 w& Y. _
       $where = ' WHERE '.$where;  1 _- G4 K$ e9 _* g! i/ a1 `
       $field = '';  2 }- Z+ |2 e" w* i- E: W
       if(is_string($data) && $data != '') {  ! I3 w3 v+ n# d: n: \1 j
           $field = $data;  
2 o3 h9 W% r( Q2 A: b' m& G       } elseif (is_array($data) && count($data) > 0) {  
1 g8 U0 @! {* M) b; Y5 b$ w           $fields = array();  0 q# G6 D1 y( X! A% I' r( g& L
           foreach($data as $k=>$v) {  
% T! q+ O  O7 T* r8 h" e              switch (substr($v, 0, 2)) {  # o- H' D; s/ N& s) `6 b/ j
                  case '+=':  1 t/ o' O9 v& r2 d. ~4 _; v8 Z3 x9 P
                     $v = substr($v,2);  
: v0 V' |4 k# d( `- {                     if (is_numeric($v)) {  
1 t/ w! ?) j/ {+ a8 Z+ n                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
0 Z, Y7 q' |8 K' R4 z* A                     } else {  
; h$ }* W$ V, ~+ n" e2 W5 k                         continue;  ! G! ^! N3 G2 k" F8 E+ E: i1 X! m
                     }  ' q" S8 I0 A2 ^6 @
                     break;  
: |: x5 Z) }% ?" G( K4 g                  case '-=':  
  y% a' B9 v0 E' a2 @1 V                     $v = substr($v,2);  
) K, W$ V- |* o2 f) l                     if (is_numeric($v)) {  
6 {: E7 [1 _4 ~' L, n                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
0 f& k1 S8 c: @" E+ |; {$ l8 c% b. y                     } else {  
2 z7 T* k2 Q' ?' j+ M7 i+ d                         continue;  + x! r, Q$ h" o2 u1 p/ n: ^
                     }  
0 x. q6 E  s! G- W                     break;  # s5 X! @0 k2 J! e9 j
                  default:  6 ^3 C* n$ H1 {3 q
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  : M. @/ X/ m% L* F
              }  - x# f. j9 J4 [* H/ K* h" \3 H! Z+ B
           }  / ?# @( m- o1 u
           $field = implode(',', $fields);  
# E1 @; }5 \, K% D       } else {  2 p! B. n) P, P
           return false;  
" E9 g( P$ Y; u7 Q% o0 s5 U6 x9 f, ^       }  $ `- I* T7 T' q- z7 P3 b2 d
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  4 q/ d+ K4 l, Z5 M# g/ J
       print_r($sql);  
) E  s" p7 y# s6 u3 z" ], X, |       return $this->execute($sql);  
* m, T6 W  q1 F  H% I" i" q    } + P, L1 U1 T$ {& S' j! s
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
& C2 r4 e5 |6 d3 V2 r
% b2 @# M2 `  W% ^/ H攻击测试:
- x* _9 o3 V" V& J测试地址http://localhost
+ Z% y' g0 ?6 i& h" p  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
  G5 o, w0 i3 t! R! V/ k% o' D3 q+ V, j5 I
[attach]179[/attach] . C: o# p# s1 g+ C* ~* L- S

5 t* |& [8 Y6 I4 j7 ~. R; }
6 e% H0 a' ~: c+ H5 D9 [" m; e, \% m[attach]180[/attach]' d8 }! w2 v/ r- s) v





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