中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
, t2 e& C1 s2 d# U; Z  b) \漏洞作者:skysheep
" }) P& _* X, F  _; F) P分析作者:Seay
8 }) d" B3 k" _% _. F博客:http://www.cnseay.com/
, g8 ^- j6 p4 d' \, H0 i漏洞分析:
3 K- I+ J6 ]. s7 S8 O; N2 m  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。* T, Y3 q8 \1 G$ b

! P- a! z% R4 T. k& s$ E$ i) J+ i. t9 o" ?
' d" h2 r' z! w# C6 N0 g- V, z
public function account_manage_info() {  & n/ D, e/ d% Y' i$ l% @
       if(isset($_POST['dosubmit'])) {    \0 p' K& [* v* H5 g' k' X9 `
           //更新用户昵称  - W# \5 E; N: T- ]
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
% I6 o8 }3 g% V9 g5 }           if($nickname) {  
& c5 r# @7 p; e; q6 V5 M              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
1 q* s8 s4 b- U+ L              if(!isset($cookietime)) {  
7 t* e1 Y9 H8 N0 }                  $get_cookietime = param::get_cookie('cookietime');  
% n& g0 g8 m! X* y              }  
$ B1 ?4 F; ]8 h, f9 E9 T  R. T              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  . g: w) f7 k% ~6 ^) o
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  ) h. [+ W' O9 m# A9 s1 Q7 t1 x% y
              param::set_cookie('_nickname', $nickname, $cookietime);  . V0 X7 [% }0 {& J' x/ A
           }  ! m" |4 i) y8 G0 k! l. l9 l
           require_once CACHE_MODEL_PATH.'member_input.class.php';  . x7 C+ i- J; S
           require_once CACHE_MODEL_PATH.'member_update.class.php';  6 m2 z$ u6 z5 ^+ h  U$ ]% t) ^
           $member_input = new member_input($this->memberinfo['modelid']);  ! J% \, O$ f& D5 \
           $modelinfo = $member_input->get($_POST['info']);  9 `' w" I. M, d0 O& E
           $this->db->set_model($this->memberinfo['modelid']);  ; K& Y; U. u: F& c
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  ( b2 w9 l9 p; Q! n4 @" L) V8 u
           if(!empty($membermodelinfo)) {  
- }, }. f! _) G0 {! P              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ) L" z6 l" ^1 [) w% R) \* Z; U+ H
           } else {  
, s  i: }0 \6 A/ b3 V& O8 J              $modelinfo['userid'] = $this->memberinfo['userid'];  
) j2 j1 J. t6 w9 a9 n8 m$ v% l' T+ o6 s              $this->db->insert($modelinfo);  
% k, a( f5 Y# t           } # q  a1 d* ]4 _; {1 v  _# ]
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,# D( t1 K! [$ j% ^! M; C# q: F
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
; |! D# V( g- {* B1 l3 _3 A6 V% U  J8 h0 P( P! k. N3 Q7 ~
) L5 {, z/ G* ^5 G! G1 c/ p+ Q
8 f' L' M# J5 S/ I4 `+ S2 ]
function get($data) {    R2 h- m6 ~0 Z: j6 R
       $this->data = $data = trim_script($data);  
8 {7 m" s' N- {. h+ q7 k       $model_cache = getcache('member_model', 'commons');  
& r! A' w0 {0 @6 V2 I5 R9 Q       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
* K) b# }& A( a6 y2 C       $info = array();  
' y' m' y" ?! m- A       $debar_filed = array('catid','title','style','thumb','status','islink','description');  , H& m( ^5 H" W/ I2 {% b' W, m
       if(is_array($data)) {  9 q" p$ X6 ~* X3 W
           foreach($data as $field=>$value) {  . u( \4 n/ G% ]0 G
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  * s2 D! {0 {! e, q1 {$ x& T! K
              $name = $this->fields[$field]['name'];  6 l: e9 v9 g3 _! |  u6 y
              $minlength = $this->fields[$field]['minlength'];  - b4 L+ @" w$ @, ?
              $maxlength = $this->fields[$field]['maxlength'];  
6 ]% m! V1 d' r2 @              $pattern = $this->fields[$field]['pattern'];  + O' J, E/ L. D- J! M
              $errortips = $this->fields[$field]['errortips'];  
  d+ Q8 f3 l2 l# y. w- q, b              if(empty($errortips)) $errortips = "$name 不符合要求!";  
; r; e$ b. Y: [: U1 X; M              $length = empty($value) ? 0 : strlen($value);  
1 F2 l2 f' U0 n; o8 \$ b              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  + V2 ~& f) ?( V! k6 M7 {. k+ c
              if($maxlength && $length > $maxlength && !$isimport) {  0 _: x( _3 N1 S
                  showmessage("$name 不得超过 $maxlength 个字符!");  
* h* L) i& Q$ F* p              } else {  
$ M6 w1 ^' x. g  L- u                  str_cut($value, $maxlength);  9 B/ q, \2 c0 j# J
              }  4 I+ H7 a4 T5 i( ]6 Y& q4 n, ?
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
6 h( [( s; t$ @- I6 Q2 B0 }$ A                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  ; Q& q5 T/ S# C- b4 A8 e1 s
              $func = $this->fields[$field]['formtype'];  
8 y9 U; ]* A+ i: N0 U+ x              if(method_exists($this, $func)) $value = $this->$func($field, $value);  ) V: ]9 E+ `. K( }% c" H* k& K' c
              $info[$field] = $value;  
+ B& V/ v% i. ~3 P  R% ^& l5 G           }  3 ^# @. l( x8 T; y
       }  / p" E' K8 l) b
       return $info;  
& a8 y6 y, `) ]8 ^# C( @3 t4 X    } , J: Z, x3 z- @' \' X0 u
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
+ r( x/ f3 o2 I1 G% ]: I& x
) c  U6 R/ I  L* W. T再到phpcms\modules\member\index.php 文件account_manage_info函数
, U2 O# Z# `' y  I* J* G; G过了get()函数之后。
: g( M: a1 P5 ]4 T
% @: D# e# @3 Q; \( E9 N
# K# z7 C, |0 j) Q6 U0 u/ ]$ n$modelinfo = $member_input->get($_POST['info']);  
( x3 h& i6 r* {* N* |" k7 }* Y           $this->db->set_model($this->memberinfo['modelid']);  
' y# f0 c9 q/ w3 ~" ^8 n# @/ ^           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  $ x& F7 P! `4 l* n* B9 |1 X
           if(!empty($membermodelinfo)) {  " o( `* }1 S6 ?* X7 Z3 t
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ' W. i$ p9 Z0 v
           } else {
# Z, T7 _6 V, |$ \3 ~8 M/ |直接带入数据库,update函数我们跟进看看3 G# ]  f3 H6 U4 S& ?% X; M2 S' r
4 I& b) t- E/ B8 o1 d- t- I

6 x& |! I/ j. _. @" p2 Jpublic function update($data, $table, $where = '') {  0 e0 U  K4 p* x/ k( P! O
       if($table == '' or $where == '') {  
$ @6 X" c! {' V$ ^           return false;  
% l0 a& K$ q8 F7 ^- V       }  ; s9 I: g* V, a4 L+ o. s
       $where = ' WHERE '.$where;  2 g. K+ d9 n: d
       $field = '';  * m2 w4 @( ?( }+ Q
       if(is_string($data) && $data != '') {  
8 y- x0 M; M( w/ b           $field = $data;  
4 Q2 c5 Z/ K3 O8 L; }       } elseif (is_array($data) && count($data) > 0) {  
7 A4 Z- d- K1 e9 s' M3 }           $fields = array();  % x6 k) _  a4 q2 b
           foreach($data as $k=>$v) {  5 a) n) g/ n: \  P
              switch (substr($v, 0, 2)) {  
  X! N1 Y: f6 _7 V$ K  |+ S! M1 Y                  case '+=':  
( t! N1 }+ B: Z                     $v = substr($v,2);  8 R; s8 b( ^6 ]& {( ^
                     if (is_numeric($v)) {  
$ P4 ^: ~5 r. H: s2 o                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  " t) s9 Z; x& E) J0 R# S: y7 Y
                     } else {  ' J7 `8 V; V: @. t& T7 W& u
                         continue;  5 I% U5 @) L, a. V4 \% [' ^0 T; x
                     }  ( M  M* a, f5 P) c
                     break;  
2 n5 E& r. ^; x  J+ N8 z' \                  case '-=':  ; o& K- h4 I# v# i2 N
                     $v = substr($v,2);  
3 A* u% I5 J. p, A8 |4 H8 a                     if (is_numeric($v)) {  
8 V; A; n( \8 R, N% [( h                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  + R8 z6 M, n: \3 {2 N( j9 b
                     } else {  
1 N5 J% }- v# r2 l/ {' _                         continue;  
& V- |$ h! x! [, w. [: K1 h                     }  
/ G) i' m" d& {0 ]' D; Y, I                     break;  - A: }& k/ R3 r$ @0 P4 c. E8 a
                  default:  
3 Q1 C* d9 e" w+ O& q3 f& L                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
9 S! y% [( g2 h( _( y              }  
6 w* C9 u' X' h4 m, C! G           }  : O0 k& e9 Q- o& J% U
           $field = implode(',', $fields);  
+ K( V& q0 X; H8 k       } else {  
$ e9 i5 }1 }/ A7 w* c           return false;  8 ]" l! q0 p& C! s
       }  ) E: p, e$ H( o# q* W" F3 C
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  # Z, N8 g- i; J. U. Z: K( z7 z
       print_r($sql);  
( |+ A) f) W$ ^0 M  R- `( Q       return $this->execute($sql);  8 W' C; R3 o3 n6 g: b
    } 0 O5 m/ p/ ?4 @* `; ^9 C) P9 i
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。  L5 l( s" f/ H( [% g

. f5 I. `2 q( o! y, b8 k6 U7 s7 v9 N攻击测试:. C& ~1 G4 ^9 e4 o
测试地址http://localhost9 q" I5 k* q" }7 R" M
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句' T0 v8 e9 d$ i) P

, T& v0 _. M, Z8 Z# y[attach]179[/attach] 9 R& s7 q1 P5 X- S
; @6 w1 M. R% P3 a  U
% S- O# P" H% M, y$ G8 s
[attach]180[/attach]$ h1 H0 j; r4 F" ~/ K9 C/ k( V1 W& i





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