中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告& f2 A/ e5 \9 b+ g; g3 b+ ]
漏洞作者:skysheep
8 S4 Y7 p* ?* @$ b8 c分析作者:Seay: E& I5 ?  J' G
博客:http://www.cnseay.com/
% S$ }, Y% Y0 w* }漏洞分析:! F( b( D4 N5 W0 V! F
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
/ i. g" n, m& K+ M
. `$ p3 g4 B* F  U- |1 C) Z/ C0 d4 d# Y6 S
$ M) A1 F5 w% A; g3 m
public function account_manage_info() {  
% Y; X" m+ C' {5 V6 Z       if(isset($_POST['dosubmit'])) {  
+ ^$ Y, _  M# c. w           //更新用户昵称  & u" U1 T( U9 n9 q" d  Z! H
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  * o3 ?  b4 J% q7 }( D5 x: d
           if($nickname) {  + D4 t8 S3 o3 n
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
; h9 C" z) b( a              if(!isset($cookietime)) {  0 Y; |8 g9 X+ ^% `( {; ]
                  $get_cookietime = param::get_cookie('cookietime');  4 x$ ~9 o. i& J
              }  2 b5 W. B1 u3 _; t
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
, _2 R1 e! ]  P" |1 q) n0 T3 Y              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  ! x, V" \, O! y+ ?! C0 G3 |
              param::set_cookie('_nickname', $nickname, $cookietime);  ; M  Q" U0 Q/ A2 [
           }  
/ J* R3 Q+ j6 S% k+ _           require_once CACHE_MODEL_PATH.'member_input.class.php';  
2 x8 s1 b* F! c3 S% w           require_once CACHE_MODEL_PATH.'member_update.class.php';  4 y1 H' P7 a& ~, G4 i
           $member_input = new member_input($this->memberinfo['modelid']);  
! R, l6 @  o; E. o) n& R( ~7 i           $modelinfo = $member_input->get($_POST['info']);  
9 K% o% o+ H1 o- g9 V$ C2 I& [" i7 I           $this->db->set_model($this->memberinfo['modelid']);  1 {' ?' Z2 e( ^" ^
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
7 p3 f- i2 l; h) E, n! T3 N           if(!empty($membermodelinfo)) {  
( x- P7 n4 V! A6 Q4 ^  c0 j              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
& a0 H( Y9 \- n( R- I           } else {  
: e7 w- M) X" K" b9 A              $modelinfo['userid'] = $this->memberinfo['userid'];  2 F1 [  K8 h" G6 F0 r
              $this->db->insert($modelinfo);  
8 ?1 ?" _9 X! k0 Z7 S  `! g$ a           }
9 R- _5 d6 F% [! a) Y0 Z$ b: ]代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,* ^, q. X: h5 s5 x
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
6 S0 S8 U: ~: z( @, }
! A/ }! n; ^4 [* @
& i: i& L! I; L) V; B9 Y 1 ?& P' m: ~& ~8 f0 b
function get($data) {  
; _7 E( d% ?  d! R! [       $this->data = $data = trim_script($data);    a) t5 z# Y2 p. U2 B" ]/ O
       $model_cache = getcache('member_model', 'commons');  
* Z: p) i; e# o5 H9 c: e       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
4 `- y1 ]# K4 A! ~2 b! K/ B       $info = array();  
" ^) u. _& h: c! ?& j       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
$ e7 n4 {' i/ t  j$ _  F. W' o       if(is_array($data)) {  
% z9 Q% s$ c' w* ?- W           foreach($data as $field=>$value) {    h8 V1 F+ U" w) Y: G
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  ( y; t( _5 K  r) _! o1 c2 P& V
              $name = $this->fields[$field]['name'];  * U1 Y6 ~) B( }$ [1 a
              $minlength = $this->fields[$field]['minlength'];  . h6 S+ ]; g: O5 c( m
              $maxlength = $this->fields[$field]['maxlength'];  7 t8 I& G" Z/ H0 w( T
              $pattern = $this->fields[$field]['pattern'];  
/ v- U2 I- q. H$ [1 ~2 q! R              $errortips = $this->fields[$field]['errortips'];  0 E  n  z) x( O0 C& @9 D, j# |+ w
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
. g; v/ e9 g! m) F' ]* n+ k! o              $length = empty($value) ? 0 : strlen($value);  ; y4 M* p$ L% s  L2 n! g( s
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
' p2 q9 `5 O. F  N- o              if($maxlength && $length > $maxlength && !$isimport) {  
; ~. b" Y! k& |1 ]                  showmessage("$name 不得超过 $maxlength 个字符!");  0 ?: z4 x4 n0 L; w
              } else {  & d# {) n- {8 F" y* W! h
                  str_cut($value, $maxlength);  % H3 D' q, ^- [% B
              }  
9 I% R3 w# w1 F0 }; B3 p1 q, C7 F              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
7 Y9 ~1 i5 i$ Z: t                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
6 U4 |1 @4 I: ^0 N              $func = $this->fields[$field]['formtype'];  6 e8 l% p( q4 |2 K, }' B
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  1 T. a9 s' ]. D6 J8 Z  s( N( u
              $info[$field] = $value;  . S  q4 q: g- ]2 n: g4 A
           }  ' H% ~/ P- V' O( v% O% Y2 ^. e0 H
       }  + G- M; L7 }' o! b& Y* i
       return $info;  
4 s8 P/ D0 E% K4 E) p    } : Y4 |7 a8 ~( }/ O
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
  i4 Z' b- `. r7 k9 \3 `% x% x! n- z5 g4 m4 q% X) C% P) X
再到phpcms\modules\member\index.php 文件account_manage_info函数2 u7 Q( N/ c. g% H# k
过了get()函数之后。( Q0 a4 ], }; ]
) V$ |  J# o7 o0 c
. M9 ~# j  @: y% ?, D
$modelinfo = $member_input->get($_POST['info']);  + [1 K4 e* A  \7 M+ p6 J' M. C. W
           $this->db->set_model($this->memberinfo['modelid']);  # e. {) q$ T( {4 s
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  - W& A( P4 b8 ^. Y, `( ~) G, ~- J2 l
           if(!empty($membermodelinfo)) {  
& p7 U. j& \) Z              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
( m- |. o) w5 Y/ M           } else { * t# U% d* p8 ^  o
直接带入数据库,update函数我们跟进看看
$ }8 p' ^* c$ R1 o$ ?​3 f* d. V, ^) y& F
' S: {! {, I" U7 w
public function update($data, $table, $where = '') {  
" J8 k9 `% T: c' \/ a6 B0 q( `       if($table == '' or $where == '') {  
$ o4 t; H8 c9 P; u           return false;  
4 ^! M# ]+ Y6 l1 Y1 f5 t7 u       }  - U, f! s$ h8 k6 |) u: `
       $where = ' WHERE '.$where;  ' U: M1 i6 y# a+ |
       $field = '';  
' {* w( n. C1 d% l9 C       if(is_string($data) && $data != '') {  
: ~* p3 S# Z+ o) }           $field = $data;  / l1 t2 ^1 @& A) T9 c7 s; s+ J
       } elseif (is_array($data) && count($data) > 0) {  6 B0 H6 M- \+ R% `4 p
           $fields = array();  ( \- V" }( P' G( Q' F$ v
           foreach($data as $k=>$v) {  
; I- ]% |- F4 ?, R4 Q/ e& O  S              switch (substr($v, 0, 2)) {  ' N5 Y/ y0 U  c1 l* h) N, M/ V' t
                  case '+=':    v4 u/ J$ G( y+ O7 L
                     $v = substr($v,2);  9 `( K9 S+ w* s6 Z1 u- C1 |( y! J
                     if (is_numeric($v)) {  
2 y7 Y, ^5 A9 V! m' \# L                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
( Q" C2 T" U! d# a                     } else {  
# t" Z: ?2 q. D' f                         continue;  / v( c0 m& A! {# Y$ e5 u
                     }  & b2 f4 F- S) [6 @
                     break;  ; y2 N- K3 W) d4 k7 @0 y/ }% x
                  case '-=':  0 j; |. I' N) R+ v' ~
                     $v = substr($v,2);  3 d9 Y. X# i6 A0 F
                     if (is_numeric($v)) {  
5 c/ L! {5 j. q6 O% X. A* ~                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  5 ?5 j$ U5 ^3 l  q+ V
                     } else {  
, V& A5 {0 ^/ A0 h9 i0 D8 \0 }                         continue;  1 d! n# \4 ~! F; V, \+ w. w4 B9 z
                     }  
) a# M( u6 ]6 m1 K# m1 j& ]                     break;  
6 ?, U4 j1 b" J! \4 U- Y% _                  default:  
$ F$ P5 U# U) Y* N+ u& ~% c6 c                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
- q! M  Q  h  H+ B2 s3 r. }              }  
' e1 t1 e: D  H2 H- a           }  $ D% L# z' n# a5 P/ Z
           $field = implode(',', $fields);  
# b3 Q( I4 A2 o/ O       } else {  ' w5 V) l, U2 I. i* t$ R& O
           return false;  
/ f* u& Z! g, e4 f) ]       }  : T& G2 |, w  e9 d/ p3 j7 u4 E
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  # x) q5 ]% g& z: k
       print_r($sql);  
! }$ A4 B  \( g6 [- F5 w6 B9 l& [       return $this->execute($sql);  ' i2 }1 x) }5 H5 N) E4 P
    } # n7 x* x5 p  B+ }  ?# ^
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。& ?6 o& {) _$ v4 q" B! @4 U4 ~
0 p3 p! C- a+ Q
攻击测试:% e% A* D* D& X& w$ L* K& W
测试地址http://localhost2 z! t( m3 }, V- [$ ~
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句! I2 P2 M! O4 E3 @: o8 }$ h
0 v; P" }+ m+ R) C: O2 C
[attach]179[/attach] ) Y  z8 w, J5 ?/ |' K3 p8 R
( d- G  h( F7 }& J
- ?3 d4 G* n. K( ]
[attach]180[/attach]) C; p& p) h# ^4 A$ D





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