中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
3 r: o" f; V3 B2 |( y漏洞作者:skysheep
% e9 G# J* m; {分析作者:Seay2 B( h4 x2 X  V( z# |, `- }
博客:http://www.cnseay.com/& s2 w+ Q* a3 q* J; C
漏洞分析:
3 S. F) A; W$ I  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
5 O4 m+ C' q# p; c; v% t" I1 w
! z( E5 H, Y: R  M, [/ b5 a; B3 _9 U+ S% b) Z. U* v
0 i% q, }) v. M1 s
public function account_manage_info() {  & _! Z7 ~5 X  {
       if(isset($_POST['dosubmit'])) {  
0 n: u; ^3 m- E8 H' H           //更新用户昵称  / W$ ^7 H3 B0 H8 j
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
7 a7 t5 I4 g* i+ G! q7 b* }           if($nickname) {  ' q1 _: F& k4 H' x  B0 s6 x
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  ' T, O/ z  e6 ?: L  D
              if(!isset($cookietime)) {  
* W7 b/ V7 k6 Q4 d: q! B                  $get_cookietime = param::get_cookie('cookietime');  3 k/ y& y: K8 f0 W& y# t% B
              }  
5 ^$ p# {7 u' `  m              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  8 j3 d3 M, g/ B, }
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  ! a; E) E7 m- C/ \
              param::set_cookie('_nickname', $nickname, $cookietime);  
, c* C1 a" }! Q: P3 h           }  7 j5 g/ C5 z) o7 z4 F' p5 j' P
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
0 r% ^" H2 F, u6 ]; u* x  V           require_once CACHE_MODEL_PATH.'member_update.class.php';  
6 s5 g& j: V- U2 G" X7 Q' e+ N           $member_input = new member_input($this->memberinfo['modelid']);  5 l* \) j1 O, R; J0 b+ O+ Y- i/ a
           $modelinfo = $member_input->get($_POST['info']);  
* l" {3 e& G7 {' P           $this->db->set_model($this->memberinfo['modelid']);  ' _# q  [8 n( N/ x$ d' U" j
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
( J% i7 U" `7 @, c+ o           if(!empty($membermodelinfo)) {  
# C, I7 x. g$ ?1 B& \              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ; e, W2 T2 R! _6 E" j# X8 v
           } else {  ; q8 R% i8 l: f$ X" U: y
              $modelinfo['userid'] = $this->memberinfo['userid'];  : u7 {( L* S' P: I5 z+ {+ J" z
              $this->db->insert($modelinfo);  
9 X) ]$ ~1 Z4 B           }
8 d$ J, J/ f9 t- k8 v代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
4 T: f2 @: L0 e' G! n) u在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ n" M* v1 O4 W8 o' {, |
# t. r' p3 T& {0 b
+ J( P# A4 D& ~, u% } ! T- c9 F& _6 e( O( J! ~
function get($data) {  
$ p8 p5 c8 G( p: L- y9 R% E       $this->data = $data = trim_script($data);  ( S+ p9 z+ m" g* z  H$ Q2 _% F; s. R: H
       $model_cache = getcache('member_model', 'commons');  1 Q& [2 U& ^+ e8 b, @9 L# I: \  t, K
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  6 C5 w  l& }( f3 J/ M
       $info = array();  ' j' a8 T  s. [; ^3 m
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
. Z% D1 R$ Z  h' F% b- h. {. ^; L* r3 a       if(is_array($data)) {  
* a+ e! j/ Y' w; h5 Y           foreach($data as $field=>$value) {    j2 H: N7 `! ~- B6 u% D
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
* j+ Q6 `# Q6 f: i6 j  K) I              $name = $this->fields[$field]['name'];  
0 x7 g4 n& p+ x4 ^; J# e- O! D, l3 `  O              $minlength = $this->fields[$field]['minlength'];  
! t0 p/ R+ z; A- }8 r$ c              $maxlength = $this->fields[$field]['maxlength'];  
# j/ h( `' G0 v3 a1 c' M: E/ [. j              $pattern = $this->fields[$field]['pattern'];  
$ H1 M5 B6 n+ n              $errortips = $this->fields[$field]['errortips'];  # T) H# G& ]; E5 I) {
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
3 q9 _& ?# l/ t6 U  v              $length = empty($value) ? 0 : strlen($value);  
  J" f# ?, w) N0 h+ n: i( B              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
" j' K8 U2 N; p6 J9 f9 d5 Q2 i- H              if($maxlength && $length > $maxlength && !$isimport) {  4 D7 k! {, x1 p/ ~4 u( u% w" z
                  showmessage("$name 不得超过 $maxlength 个字符!");  9 x/ @4 {: g! }- s( S0 |0 `1 c; A
              } else {  ( [0 V  j2 T; r7 E5 N
                  str_cut($value, $maxlength);  ! W+ }  _' B4 d$ Q! u3 l
              }  
* I1 n/ @. Q) t6 [; K5 ~: z) Q9 U              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  # }! y# t$ K2 @0 \* r
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  % {) ]5 L9 ?4 u" o% P
              $func = $this->fields[$field]['formtype'];  
8 {& X" H% N+ n/ t              if(method_exists($this, $func)) $value = $this->$func($field, $value);  / e" V' o% u( V8 ]! y
              $info[$field] = $value;  
: y3 Y$ Q# ~2 Y; Q; |           }  
9 O. X: r  H; C6 J; ~4 o' B' a       }  & @* P8 \; R5 S& \4 d0 g1 P
       return $info;  ' d  V- t6 y( q; c/ L! c( |
    } 3 m3 \9 X& X$ G  R
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
8 P" o7 u( ?" r, p+ ?: b; l% \
: O% r) N- ]* O# D) e- z. o再到phpcms\modules\member\index.php 文件account_manage_info函数
. J, ]5 ~; I2 p  f$ f过了get()函数之后。2 b, l, h% P% C6 M
% y% ]: {4 w% v

, m) A! j1 [5 G1 F6 w5 t$modelinfo = $member_input->get($_POST['info']);  
. s5 H1 h) J2 _: ]. B           $this->db->set_model($this->memberinfo['modelid']);  
! w* |; _  K1 `4 }           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
( A( j1 y$ D* ?" N4 m: Y           if(!empty($membermodelinfo)) {  + }8 c' l5 h- u* l' i
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  7 l6 l  O( Z1 J8 W
           } else { 2 p) q( n- T# e& q
直接带入数据库,update函数我们跟进看看
+ q9 p7 f" C* N2 g  F$ y. {. ]9 b4 {$ _

, ^$ u. Q9 t8 f0 r* ?public function update($data, $table, $where = '') {  
& B& m, i: [4 P/ y5 ~! K5 z$ `       if($table == '' or $where == '') {  
- V: ^; {1 B5 e% W6 Q+ n2 M           return false;  
* ^, L+ U) I6 ]       }  
3 E; O$ ?4 d+ X; Z6 r       $where = ' WHERE '.$where;  6 @2 {5 J" C, V% o- O9 n1 C
       $field = '';  
+ K3 Z4 p# {" f5 q6 p- |$ `$ @       if(is_string($data) && $data != '') {  
, v" c" L1 y; \3 r, a           $field = $data;  ! c2 \( f! d- P, d0 `/ r8 z
       } elseif (is_array($data) && count($data) > 0) {  . O# i* V. @$ \7 @6 B6 i  k
           $fields = array();  
# d0 [, j2 ^" V9 H           foreach($data as $k=>$v) {  
! q/ b! d# D" l7 w( N( w              switch (substr($v, 0, 2)) {  
( J  s$ ?" s6 e" t* z: U' I8 K                  case '+=':  
' c/ y) p. R% e! u  X* G; K                     $v = substr($v,2);  & }1 r* n* v( L% f7 N
                     if (is_numeric($v)) {  
$ i: k7 Z$ G+ t* i0 o4 V                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  3 X+ t1 @1 x! O8 s
                     } else {  1 s/ B# s7 S3 w8 ]
                         continue;    G* |8 C* V! v+ L2 q
                     }  ' O3 n0 P/ c& d( {
                     break;  8 f2 k3 v# u/ N  b% }: e
                  case '-=':  
; k7 C; [# A  U# F% @/ b                     $v = substr($v,2);  6 l% A, ?- E& _7 _% P
                     if (is_numeric($v)) {  
3 d% {% X2 w! P6 d& E; J                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
' z( h6 J3 K8 B% M8 f' A" m# F                     } else {  / Y; x) a* ^2 r: i- x* m/ I' d2 L
                         continue;  7 `/ L1 p! v/ J# t% w4 D
                     }  
1 }/ a, F0 i" p% Z7 `                     break;  
5 X, x0 P( T& E5 c                  default:  ' X( i6 |' ~8 @; j! g0 Y4 U
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  # t6 c% Z+ L; x# w
              }  
6 j9 Q. a$ d5 O' w5 n5 f2 R           }  8 }6 L8 O+ w* W$ ^) |
           $field = implode(',', $fields);  7 Q4 F, t7 m- ?
       } else {  
6 S. N/ K3 t- y$ B' j! O           return false;  
) G$ |! O# Z8 d% j) R5 m* v       }  
/ ]8 q" x  O" o. E1 l3 G! V3 |       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  % l/ e/ Q. s" N( E. j1 @& k$ R( d+ a
       print_r($sql);  , I2 q3 K' W3 u3 T1 ~* |- n: ~- M
       return $this->execute($sql);  3 h; ~! a8 g) y3 d8 Q
    } " J1 P2 l; a. d8 L$ ^
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。. V. R6 U$ A. v& X# K! t5 W+ ~9 W
7 N: @3 r  v6 W) U7 S8 I
攻击测试:+ _" ?  @2 ]2 n  x9 B# Z. A8 `3 ^
测试地址http://localhost7 I& X2 ~8 Q7 t
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句3 v- D, p8 E0 w1 X# ~7 D6 ^8 d
1 p0 q' R6 w% c% A
[attach]179[/attach] $ _8 Z' T9 @9 @; @# [$ a) v' ?
2 N( X6 I) }( e( I. {

# f* E/ e; @  `" @6 s4 c[attach]180[/attach]  h3 z" N5 C9 _: P1 H. h9 Y# X





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