中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告! z3 ~- F9 |" Q6 \+ _4 {$ l
漏洞作者:skysheep4 G5 h/ o! x5 M. e6 k
分析作者:Seay4 a; d2 N8 i# q! s
博客:http://www.cnseay.com/
( V/ o3 }" j* B1 r3 w3 r' k9 z6 a漏洞分析:9 L0 M, W' L0 a
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。( t$ T7 g2 L( g* B2 b) r

1 x, I0 V) E6 T5 E! p) [
* n  C0 d: l) g  ^ ; l; e, A* N9 o4 m, _
public function account_manage_info() {  ; Q0 J; u& u; C0 O& G
       if(isset($_POST['dosubmit'])) {  ( |6 G: o2 A, R6 B, R2 J
           //更新用户昵称  1 B8 Z/ K( {$ g% Z+ m
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
4 O2 x; m* Y$ K/ `/ D1 c$ J           if($nickname) {    J' R7 n+ Y% ~. n
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  ) M* v, X. O2 k' i6 H" S+ [
              if(!isset($cookietime)) {  
2 E' }9 p5 Y# W& h8 Z+ a                  $get_cookietime = param::get_cookie('cookietime');  7 f3 c1 d$ W1 C3 H
              }  
- W0 O( k, F, C$ l% [' o              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
4 E* ^3 a- t& ]* K" @. s+ j              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  7 |  q% C' a0 t& ^1 l
              param::set_cookie('_nickname', $nickname, $cookietime);  & m7 c: j. z: g9 K
           }  7 @% q- O+ F7 R: C. Q
           require_once CACHE_MODEL_PATH.'member_input.class.php';  ! G- z" l, w" p  Q
           require_once CACHE_MODEL_PATH.'member_update.class.php';  & U; y. R4 O3 {7 i
           $member_input = new member_input($this->memberinfo['modelid']);  / H: J+ w  ^7 a1 j7 o  j/ u% O
           $modelinfo = $member_input->get($_POST['info']);  
3 B0 {8 r! p7 d# d, Q( g* `# V/ a           $this->db->set_model($this->memberinfo['modelid']);  ) \' W1 {7 K5 T* {( B
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
+ G0 ^( U. F% q$ y7 O' F4 W& |           if(!empty($membermodelinfo)) {  
/ Y0 X8 \! o2 G! }              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  $ h% J4 z9 B7 c8 i$ q2 A/ o
           } else {  
( g( v; a* N& g5 I, k0 {              $modelinfo['userid'] = $this->memberinfo['userid'];  
; z. w; j; Z) t% A' }: {# d1 ]6 [2 d              $this->db->insert($modelinfo);  
. ?3 U% o4 o! Z, E           } 1 k* a3 M$ }; I( f! I
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
4 i5 v% l' {$ `, `% d" L4 X在\caches\caches_model\caches_data\ member_input.class.php 文件中:
" t( [  G) x* ]  U
# P2 V! L% j4 z. W0 B2 ]# q% |, r" m) Z3 h- G( e& f0 ]
% a4 l9 K) P6 [- H7 X
function get($data) {  
9 w* c! t& b( b$ R- G0 x9 `9 a       $this->data = $data = trim_script($data);  
& S, \5 x' j3 G& S9 z: E  J" y' M       $model_cache = getcache('member_model', 'commons');  
( E$ u4 s/ h; \" [       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
& X/ d1 A1 [2 b       $info = array();  
1 O0 N. q( Z, ^1 A% b& Q; m; R       $debar_filed = array('catid','title','style','thumb','status','islink','description');  2 F1 `; w& V9 T* h, J0 v
       if(is_array($data)) {  
) ]( w6 f# x7 n9 g! f; Y9 s           foreach($data as $field=>$value) {  & S: L6 M9 j2 V# J+ X
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  , J) o6 ]8 @3 r* w/ O9 Y
              $name = $this->fields[$field]['name'];  
" ]& ?6 u1 C- l; v3 J& H              $minlength = $this->fields[$field]['minlength'];  
9 l' [3 E$ K( V              $maxlength = $this->fields[$field]['maxlength'];  & f' z  p- M/ w
              $pattern = $this->fields[$field]['pattern'];  % e4 @! e1 {5 r& g9 J: k4 T- {
              $errortips = $this->fields[$field]['errortips'];  
; A1 y. |# p4 J4 @              if(empty($errortips)) $errortips = "$name 不符合要求!";  $ H  n, G( `/ K, y0 N8 j2 \
              $length = empty($value) ? 0 : strlen($value);  / m$ H* X" C# I5 r0 Y
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
5 X0 m( ]- \3 p" ]              if($maxlength && $length > $maxlength && !$isimport) {  # _) h# H7 m  ^2 `1 w
                  showmessage("$name 不得超过 $maxlength 个字符!");  ) U. d  Q. U! I
              } else {  , ?% R* ^" z9 B
                  str_cut($value, $maxlength);  
- t/ N, R, `. M% T  w              }  
* o# @1 m$ I0 \- v/ W  J              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
; n" ]% y# i$ d6 ?                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");    [" L. g1 g) c5 F1 T# B
              $func = $this->fields[$field]['formtype'];  8 z! T! v4 N- d* T- ^; y
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  * }& j" l8 l4 [3 s8 l
              $info[$field] = $value;  . Y, x" i, V) O2 w3 z
           }  
# z4 t7 \' d  U" p, y" k( d       }  9 d/ b/ X: x; H) E/ e& v
       return $info;  # N1 \& \, @$ C5 r* ~6 L- Z5 L: S
    }
4 }; N$ v( M; r" vtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,8 C9 C0 A0 t. v; J% M5 J
4 H( ]6 w9 Z' S  C( X7 V
再到phpcms\modules\member\index.php 文件account_manage_info函数) a: ]  A  a3 G5 W8 A! Z
过了get()函数之后。' F6 a5 ]8 q( U  Q

& z: f$ i5 J+ x8 G# r3 G* S
4 d: G# U: |; a" F$modelinfo = $member_input->get($_POST['info']);  2 s5 ^$ g) @' V1 m/ z# |  @
           $this->db->set_model($this->memberinfo['modelid']);    d( n5 W- c  z+ Y2 |$ L
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
/ q, d% B2 e! V1 X; y2 Q           if(!empty($membermodelinfo)) {  
$ M, Q, W) h, z; r              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ' G% O. A  R' \( o. _
           } else {
% F+ m) M' N) k6 x直接带入数据库,update函数我们跟进看看
* _# [9 s/ o( ~7 P3 }/ \1 W- z. Y& l2 l% q5 X) H! R

3 k  E# I: u: opublic function update($data, $table, $where = '') {  
6 g, w3 [/ H' m! I1 @8 b       if($table == '' or $where == '') {  
# a) \, h4 P/ u1 h: @8 ]: o           return false;  
1 T' c. p# l4 q       }  
$ N9 Z) ?+ [- R* U( @5 R+ w       $where = ' WHERE '.$where;  
6 c7 i- ?( D! t* f; p8 A  h- a       $field = '';  * l6 B/ J6 M5 n4 Q
       if(is_string($data) && $data != '') {  & d, F# ~5 }* ?0 I- V( E
           $field = $data;  5 L2 D( f# @; E8 P* p& V
       } elseif (is_array($data) && count($data) > 0) {  
6 I4 p- @0 E7 s" n# E           $fields = array();  . d% O: w4 r% E; m; Z, g+ ?# p
           foreach($data as $k=>$v) {  
# ~# z* I+ Z. P3 x3 C0 j: d              switch (substr($v, 0, 2)) {  . E/ k! P; N& a- w8 f( a
                  case '+=':  
9 f+ L. b: b, h3 k- u                     $v = substr($v,2);  # B0 D2 C4 |/ b  y- M
                     if (is_numeric($v)) {  
) U- W, E: K& d3 O8 [                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  * v& S( S! _" ]3 i+ _. l8 K
                     } else {  
+ m- B" F& b: z( z                         continue;  
  Q$ d) _0 b. G                     }  : d, B# p* ~( E$ {4 T) p% p: `
                     break;  
4 C! S, H! H& d& w, U                  case '-=':  % ]0 ~; [  h! W. N$ K9 E
                     $v = substr($v,2);  / E9 e, O7 t1 R) j
                     if (is_numeric($v)) {  
0 w* v* W6 L+ b) w                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  ) R7 l( z8 a: d& }
                     } else {  * N$ B) j# k; w3 J; U3 Z
                         continue;  
8 }( q& q2 G& i, O                     }  - A% K" u- L; f) _5 Y, D" f
                     break;  + Z# |: v" [1 I5 X5 j3 u2 G3 O
                  default:  2 ?6 u& [; {6 |% A7 l
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  1 b, `/ p2 U2 d! P6 m
              }  - b4 _# \! w* }7 C. n) C2 [
           }  ) j' {4 @8 ^; K& B
           $field = implode(',', $fields);  
; T* k/ s: ?+ [1 q+ j1 h       } else {  
8 w8 h' r/ |6 e+ h           return false;  
: Z$ U7 d/ A6 ]/ c4 k: `5 a* G2 t       }  
6 R3 v. Z4 g$ {0 P% p, D- @       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
! ?1 i' e! g7 x; b. `/ {2 l4 q7 |       print_r($sql);  & ~5 H% k3 m) M7 \: l
       return $this->execute($sql);  
  c# h& }2 ~: s8 [    } 9 w+ H+ w: F$ i7 ~$ d8 e
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。) V0 S4 X0 a+ V9 X: B8 C. y

* j- Y3 ]" R+ B6 z' o4 A4 N# E攻击测试:: h8 q0 R0 q4 n" J0 l/ L3 y
测试地址http://localhost
9 m3 j; Q* I% h3 d  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句" {4 N+ K8 t1 v, J# R/ d( c; J* Q

2 \8 b3 f. j1 V[attach]179[/attach]
' S3 k1 h  w* G2 r" E+ h+ E
& W* r6 W; m" ]$ g. d9 }; d# l6 E- v
[attach]180[/attach]8 P8 G8 I5 _/ d& q( P





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