中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
; \1 T" R" ~) A1 K! d漏洞作者:skysheep$ \+ q+ a6 Q% Y1 s/ G2 G
分析作者:Seay
/ _$ b# z8 R- Q; e) ?" y博客:http://www.cnseay.com/
- V2 I1 s6 q4 m+ n5 a7 D漏洞分析:
  T; a* k% I9 E/ b* N4 f  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
, g$ |; k6 E1 R9 q3 _* ?. z6 W8 o; T- m- o/ ]
) I/ G1 h9 d  c6 @5 |3 v
5 o) F5 c1 h6 E4 j. [% \
public function account_manage_info() {  5 c# Y) P* ~( Y  z* l: T
       if(isset($_POST['dosubmit'])) {  3 p/ {" j, l8 a
           //更新用户昵称  5 M9 v) h% X5 {/ n
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  1 ~. ^, ]$ N, R9 K$ O
           if($nickname) {  
; p! l4 R9 Q8 z( @0 b- |              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
& S: D- z- `, R! U( P) \/ `              if(!isset($cookietime)) {  & }3 Y  V! T" s! |8 Z; q
                  $get_cookietime = param::get_cookie('cookietime');  ; h& S# N- B4 R  X- D
              }  
( [) W1 m, S# K$ B" t' S              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  + s0 U- _9 ^+ V, H$ L% F
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
2 J$ @5 ^: j) G( X              param::set_cookie('_nickname', $nickname, $cookietime);  " a# k9 e1 `: G- }( Z& l  s
           }  0 f7 v/ s0 k$ o" z* F
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
' w/ H9 \4 O, H  `) n3 M           require_once CACHE_MODEL_PATH.'member_update.class.php';  ' b! ?* q0 w# D
           $member_input = new member_input($this->memberinfo['modelid']);  
/ i, d9 a! _$ W6 g2 u% d1 C           $modelinfo = $member_input->get($_POST['info']);  % N5 }" Y' n  Y) }
           $this->db->set_model($this->memberinfo['modelid']);  5 H' B: c7 Q7 q3 W. F2 ]/ E
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
3 Y1 X' E2 a3 l' c5 H( ?# ]/ r: {3 L           if(!empty($membermodelinfo)) {  
" W! `. g$ [9 U# x) j" r              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  # b0 g, r: w8 K  ]7 K" {+ _
           } else {  
( S# Y" d+ b3 P9 E! R1 H              $modelinfo['userid'] = $this->memberinfo['userid'];  
$ R; i; F0 Y" f# v              $this->db->insert($modelinfo);  : p+ D, ^2 K1 n5 e; k
           }
( {; _3 ~" P( t  D3 t9 J  o代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
8 m9 ?$ f# ^+ w6 O0 r: Z在\caches\caches_model\caches_data\ member_input.class.php 文件中:
( J, Z0 [- v3 j3 v0 n9 N
' p9 K3 Z* ]' r. J; t  T
  T. _. ^. U' z0 E& @% [2 V7 k
* J: \: t2 U  d5 afunction get($data) {  
; g/ k, z+ h4 c* L' ~) j0 y       $this->data = $data = trim_script($data);  " O8 _/ @, i6 q' V
       $model_cache = getcache('member_model', 'commons');  4 i/ U4 C4 U7 v" q" N: ]
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
% a2 q/ q/ h' |/ g' J       $info = array();  
1 O( _- ?' Y2 i7 C: x; }       $debar_filed = array('catid','title','style','thumb','status','islink','description');  . A# g6 d5 U6 ^2 [8 Y% D
       if(is_array($data)) {  
9 _. s5 \% B+ {4 ]9 Z8 G$ Z           foreach($data as $field=>$value) {  + P/ w. s- e) l
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  ' z0 F4 k6 U4 s% b0 H
              $name = $this->fields[$field]['name'];  
2 x# p7 p( ^8 R; x: V  @              $minlength = $this->fields[$field]['minlength'];  ) ?- E3 ^/ U1 a# g0 _" K
              $maxlength = $this->fields[$field]['maxlength'];  * W" v# N' R3 \$ t0 k$ q  f
              $pattern = $this->fields[$field]['pattern'];    q6 N' X, @+ }* }% ?$ d
              $errortips = $this->fields[$field]['errortips'];  
' [9 ~) @5 S8 j9 Z              if(empty($errortips)) $errortips = "$name 不符合要求!";  5 A1 v0 [: R. ]( O' _
              $length = empty($value) ? 0 : strlen($value);  ! h7 K& V/ ]0 Y9 e
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  : u' ]: i4 [& e0 Z. j; p$ y0 @
              if($maxlength && $length > $maxlength && !$isimport) {  $ Q3 G) v$ N. Z- a" K. B! ]
                  showmessage("$name 不得超过 $maxlength 个字符!");  : m- D& @  j  T+ I, p" w
              } else {  % }) K/ v* C" O/ y7 p5 l9 Q+ y
                  str_cut($value, $maxlength);  $ L* d# h2 q  o! \% I1 b5 J/ L
              }  6 X$ U+ C& V. ?3 U1 a5 L
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  % {. j  s! u" D  b6 t. d0 l& {
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
' `7 c8 U& ^1 g: o2 i* L+ o              $func = $this->fields[$field]['formtype'];  
* L. f" j* N( j/ U  g: a7 w& F) l              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
8 `" r* Y  c! I/ W; e              $info[$field] = $value;  / P  x/ N* b; @/ e7 `6 h
           }  
1 ~& c5 t% E% ]; z% U% X2 F       }  & P0 v$ x7 M& V
       return $info;  - e: i% A& t, ]5 T. Y+ A
    }
% h/ P* ~( Q, w! r' H! atrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
/ {# z8 C) p8 d5 q; t$ u$ b5 M/ Q  a5 z
再到phpcms\modules\member\index.php 文件account_manage_info函数8 t% g3 ^2 k$ M0 j
过了get()函数之后。
7 C  u+ [3 Z# F& Z
3 p, @7 W3 d% l3 ^
( g% A1 }( |) m3 w% L$modelinfo = $member_input->get($_POST['info']);  3 q+ y2 R# m0 B$ q! K
           $this->db->set_model($this->memberinfo['modelid']);  
: z, v4 n( y0 X9 g% A+ V$ {9 K           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
/ h& h- l' F. M( M6 D& N           if(!empty($membermodelinfo)) {    p8 t4 v# J$ @
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
  p1 P8 t/ L& x( R7 \           } else { ! `8 ~! M- I) u4 b) z
直接带入数据库,update函数我们跟进看看
0 V1 Y* Y$ x% v$ ]: R% T- e$ m- f# v+ U
4 E2 z4 ~. ?3 n: O# ?
public function update($data, $table, $where = '') {  - p9 }, j: J# }5 e- ]* C
       if($table == '' or $where == '') {  
+ P1 y$ e0 ~# H- b           return false;  
. d, `( R! {) U' O4 o4 h       }  0 t9 J  x+ s7 K% h8 }+ F
       $where = ' WHERE '.$where;  1 s6 z6 l' n: x
       $field = '';  , `& s% a9 f6 l# {
       if(is_string($data) && $data != '') {  4 j" D0 W  q6 A- ~9 B
           $field = $data;  
# Z& t2 ^  @" ~( q  L       } elseif (is_array($data) && count($data) > 0) {  ( ~! k$ z+ W9 y
           $fields = array();  9 T% T  a6 E5 O! Z" U
           foreach($data as $k=>$v) {  
. S# V+ ~. @/ Y0 |# @              switch (substr($v, 0, 2)) {  
9 A+ H2 c$ R# G1 X6 }                  case '+=':  
" g% u/ m$ [4 {4 w, I0 C( {! {                     $v = substr($v,2);  ( }! g5 {2 U' A# y* H) l" J7 `, ?
                     if (is_numeric($v)) {  8 w0 ^) L/ ]6 h1 ~5 T! w0 S
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
4 q; @: x, w8 n! j9 q" C: I                     } else {  
1 q2 F) Q. z! L7 D4 B" F& `                         continue;  
4 l$ M9 ?8 ]/ z: d                     }  
; }# f5 R/ X' ]9 z( ]# K& l9 m0 _$ X                     break;  
6 x+ d' ]/ G. D+ M, E& C                  case '-=':  
4 }% @7 F( s8 t3 G) C% n2 T                     $v = substr($v,2);  
' w, o! c+ w! V  f  r3 w& N9 k! j                     if (is_numeric($v)) {  
8 M, ]( r2 ]% M/ @  O6 H                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  ' ^1 f* H6 `; i, E6 O, o
                     } else {  
5 H9 U3 T9 j# T4 h/ j3 s- Y                         continue;  " h7 W- v, \: I) q' M
                     }  
( m& m' P6 {, s- e* F# P+ X, J3 [                     break;  % B( ^% @% M- s( E
                  default:  
7 o6 ]! a+ t( C, X                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  : b6 u5 N; H6 \% V- z$ n( ?% J$ M
              }  
6 K9 V' X5 o. G% `5 k/ R           }  
; k8 G2 h5 B$ A/ O. t! P           $field = implode(',', $fields);  " q, k/ t" c4 ]5 |# o! f! P
       } else {  * T& }; S; c+ K) o. u6 k: N! ?
           return false;  2 m& f" D( L/ C
       }  # }/ h& N* T9 A, \8 O+ v, d
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
2 [1 `7 a2 F5 K( k# v2 T! R       print_r($sql);  6 A/ x- ^3 D; r7 I6 {
       return $this->execute($sql);  6 s. M' C# G. q5 W' w; ~7 e
    } : B" X7 @7 ?/ D' v. `% L6 o5 m
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
4 u, Z% q% A) u$ B0 H2 k
0 g4 v# p( b" [" u攻击测试:  S! E* N9 N& A8 H  G& F& u
测试地址http://localhost1 j# V4 \, }/ u3 O7 x: n
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
9 h! N# Q# k5 Q- R& W7 W/ Q
% z7 Q+ b: Y0 ^[attach]179[/attach]
9 C' C' \% a" r8 t% R, h
' j3 z; {  g% |% l0 B& u4 D
0 Z9 B7 L& R, O1 C[attach]180[/attach]
* D1 o, x8 o. t- l5 i




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