找回密码
 立即注册
欢迎中测联盟老会员回家,1997年注册的域名
查看: 2394|回复: 0
打印 上一主题 下一主题

phpcms v9 2013-02-01 会员中心注入漏洞分析报告

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告; v( Z# ]4 t  v3 T' q
漏洞作者:skysheep
# I/ i2 w% X- _2 L. o分析作者:Seay
& h* K- O- E5 `0 F: A) Z, \博客:http://www.cnseay.com/% ?5 l7 W8 Q' m$ {( w
漏洞分析:
7 c0 A4 h8 ]6 m2 ?8 a) d4 d+ `  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
' L2 Y0 n2 R3 Y) Z4 p' Q  v; J, T/ B& s4 B9 [# h9 ^8 v0 q8 t

, V& A# |, `0 E" Z
+ _+ _  N9 h  m6 H: dpublic function account_manage_info() {  
5 ~, R' y% a5 d# p/ J+ o       if(isset($_POST['dosubmit'])) {  4 u# @2 Z1 D$ K
           //更新用户昵称  + I  w6 ~. n/ }) Y' S
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  2 D3 |5 ~. R+ U% U  R7 Z3 f+ D/ p
           if($nickname) {  8 g+ n6 o( j1 s. [. E3 X% B) c
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
2 |8 n$ t: I4 |9 i. {) y; h" l              if(!isset($cookietime)) {  
- D+ x2 I) D$ T                  $get_cookietime = param::get_cookie('cookietime');    E% ^8 t( }8 z* `. G" l% \5 A! Q
              }  4 l0 C+ _+ d  i
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
  i( v+ F# J* G# s+ j! Y4 C. P              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  / z, _2 j! s  H
              param::set_cookie('_nickname', $nickname, $cookietime);  
  X  ?( j& f3 e/ a2 R           }  9 @4 X  z. |* }+ v" r  J
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
$ z( q% {# F" D" P& c6 e- B           require_once CACHE_MODEL_PATH.'member_update.class.php';  1 [7 U, V& E# x# i# A2 K8 E
           $member_input = new member_input($this->memberinfo['modelid']);  8 O8 \) S; W5 m/ \" i9 ]
           $modelinfo = $member_input->get($_POST['info']);  1 z2 `) ]  h* c( ?3 v, {0 z) q
           $this->db->set_model($this->memberinfo['modelid']);  
3 Y- i- h; g, [" O  ^           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  & ^6 |4 Y, q" j; j* [# v3 o+ R/ }8 K. c
           if(!empty($membermodelinfo)) {  
) U. c  J4 j2 G- X* L) G              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
% h4 d5 R& G& C           } else {  . q4 v- {, F; H5 B
              $modelinfo['userid'] = $this->memberinfo['userid'];  
# `" Q7 g$ T1 \, Z2 q% b              $this->db->insert($modelinfo);  
8 c1 G8 l% g! i* E0 l- v           }
: ?% Z5 ^3 G. Y0 |4 d代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
+ a. o2 D9 S5 q' q在\caches\caches_model\caches_data\ member_input.class.php 文件中:  v& g7 L5 o, V9 ], t

6 M' Q9 u: t5 R3 M
5 Q- u- d/ |) K  F- I, M % s. D& i4 Y' `
function get($data) {  8 |& j7 V* z, v" Y' I
       $this->data = $data = trim_script($data);  4 h, a% ]4 Y0 z: i8 m# N5 [3 p
       $model_cache = getcache('member_model', 'commons');  4 y( K9 t3 k4 F" L: S0 D: R
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  * j2 }6 l& C% X+ C/ o
       $info = array();  
2 q. `0 X1 t) K6 {, O! U       $debar_filed = array('catid','title','style','thumb','status','islink','description');  ' K1 S- K- F* u# Z# j$ J& p3 @. T
       if(is_array($data)) {  
. I/ s4 {! w  X7 ~- o           foreach($data as $field=>$value) {  
( I3 G$ b* H2 _' R: H: l, ?              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  & |) j# X3 J' ]$ j+ R
              $name = $this->fields[$field]['name'];  8 \! |* ]7 W" y% Z
              $minlength = $this->fields[$field]['minlength'];  
0 i) I( w- {6 |              $maxlength = $this->fields[$field]['maxlength'];  $ H% v  }9 p0 r
              $pattern = $this->fields[$field]['pattern'];  
/ i" d* v+ w$ s: A9 L              $errortips = $this->fields[$field]['errortips'];  ( l! J7 v6 a4 h' c- F
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
7 p, _$ o$ t) ]" r( k              $length = empty($value) ? 0 : strlen($value);  
# f8 E4 Q- V: ]3 d              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
8 u! M8 J1 o* }. G              if($maxlength && $length > $maxlength && !$isimport) {  5 [% g1 Y2 F" W
                  showmessage("$name 不得超过 $maxlength 个字符!");  
3 w5 D9 l% \: e              } else {  
# p8 F- V! Q: Q+ P7 G: J  S                  str_cut($value, $maxlength);  
! m: y. r3 A$ V% G( L              }  
6 P' G8 _- U, Y4 p. Q6 \! H* s              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
( M" [2 ]* Z9 E" H# ^. h4 S7 i                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  " P8 A6 o  l1 h5 J3 R2 x* j8 u5 w8 k
              $func = $this->fields[$field]['formtype'];  9 v4 b" J$ j/ X: b) ]2 d8 G6 \
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
1 M6 y: [. Z. W              $info[$field] = $value;  
/ y2 J8 l, [1 |% F+ K- P% ?+ c6 i           }  ( H& E/ d7 A0 h0 x  W/ O0 t
       }  
& B; g/ \% c: o$ a/ V       return $info;  : d2 h- E; |2 O3 ]* |! |
    } 4 p; g8 W/ x* f; w1 `) ]! \
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
& x, U/ z6 _# Z
! c0 d; f0 ], Y8 {再到phpcms\modules\member\index.php 文件account_manage_info函数3 d0 D6 o% g: U2 b+ v( G7 t( U
过了get()函数之后。/ y) m/ N# @, {6 j9 e3 D1 j

3 H/ z# l; C' e6 I
4 c9 y+ U5 G: E* U$ u  n$ S" F. b$modelinfo = $member_input->get($_POST['info']);  
$ G3 ?$ t' ^" I% _3 j           $this->db->set_model($this->memberinfo['modelid']);  , b9 E: k/ i/ o( ?
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  ; }+ c# @% j) X; i
           if(!empty($membermodelinfo)) {  - D4 I' I5 N) j3 b0 [5 y
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
6 V' y1 I& y0 h% H% z           } else {
# W2 Y/ h7 A# M! g直接带入数据库,update函数我们跟进看看5 a) h) z: D+ v/ t$ u
​9 }" c# d# ]: n! K5 z! {
- B& G* V) E: E& k$ m& u+ M
public function update($data, $table, $where = '') {  
) ], h( y9 @+ V; R       if($table == '' or $where == '') {  
0 x5 r! y  c1 }, X6 f# U           return false;  9 u2 f) |! k1 E* y# e
       }  
' B* s5 |  F1 ]* {3 {6 V5 f       $where = ' WHERE '.$where;  4 n7 \5 x! G6 R2 C/ p+ R
       $field = '';  
# d( L6 U% ^3 R7 Q4 e       if(is_string($data) && $data != '') {  
/ S; |; S3 `, b# m1 G           $field = $data;  0 y& ^0 {4 F, _& a+ L
       } elseif (is_array($data) && count($data) > 0) {  
2 \0 R9 O5 @4 E9 g6 v( \           $fields = array();  
, T, l1 c! \$ ?( [" L           foreach($data as $k=>$v) {  
' x: Q6 H' X2 T3 n3 y7 Z/ b; ~              switch (substr($v, 0, 2)) {  
$ ~& L+ B) y% g                  case '+=':  
* |1 b  J1 G" u4 Y                     $v = substr($v,2);  
' M+ [8 s, ~, C/ V                     if (is_numeric($v)) {  
3 K0 h6 P. K' f' z/ U9 L/ t. a- T                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  4 T- f8 v& d1 k
                     } else {  
8 M4 D0 N) W. g) y( N2 [* B* D                         continue;  
6 G& ^1 S1 d- \                     }  2 n; z1 F: r8 j; _( V
                     break;  
, s. [$ J* ], U/ K) W                  case '-=':  
! c3 |4 N1 w; O7 u" H                     $v = substr($v,2);  , h0 }9 N5 s$ f
                     if (is_numeric($v)) {  " K. S" j+ ?3 s8 Q2 v. x% @
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  6 v$ V& O% v0 l
                     } else {  2 n! p9 C  ^# Q$ I% _: M
                         continue;  * ?9 \( c3 l  \2 Y
                     }  ! r( S* _( C% u7 S7 Q
                     break;  # x" {9 v# r5 p' ~
                  default:  & _' U* e, g2 ~! O( _! v
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  , @) H- z! {' ~) J4 e
              }  
0 ~7 U8 x+ J: E  U           }  * O# Y: b2 _3 {3 r# N- ^7 f
           $field = implode(',', $fields);  
6 z$ U  t" `1 t" J. h9 H! |       } else {  
$ ]3 E$ S9 v, N1 ?0 s" T' U           return false;  
/ q1 w" N# P- L7 |$ M" X       }  - t. G) p7 ^6 A4 R
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  1 q+ t5 f0 G4 O/ D% c* ?9 z% O9 d  y
       print_r($sql);  # l0 w: v3 x) ~0 Z4 O! s0 w
       return $this->execute($sql);  3 i& J' b; o2 V5 I( z$ s: P
    } 0 N9 s4 j9 A6 r0 P7 s5 w# m
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
) r, Y, N$ Y2 ?3 j+ d7 @0 J5 F7 Z; U" a' {
攻击测试:* X! [, ~# r9 n* z( y0 P. v$ H
测试地址http://localhost
% d# L; L: o; R6 o, V/ P  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句; |# ^% N! ?: a# T) K1 u( U, Y

) |( s8 K. b3 k- n0 [   d8 N+ q$ w/ A/ P9 K1 M; p

- h: Q; |- w5 r0 A: C! {; w+ t- G9 _7 z

& M5 o& M& ~6 [  t: l

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表