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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告$ E% p8 @9 \7 c$ T! r4 j5 Z  c! W# _
漏洞作者:skysheep9 ^' ~! [) y" _, x9 o
分析作者:Seay
3 l$ D6 Q7 Y1 D1 \: s) {! m; ^博客:http://www.cnseay.com/
; |9 k. a4 j- n" q" n$ P漏洞分析:8 S  M4 t9 X4 |, M3 {/ r
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
4 n5 _$ q' Q8 Z; D" H" O
1 d- l7 Z* X  E5 {& ^
3 R+ U( `: N) o4 I0 y0 a
' s4 u5 j. _; G: Epublic function account_manage_info() {  
* F, t# H" W( H5 e, X       if(isset($_POST['dosubmit'])) {  
2 h7 M8 |3 M+ ?4 s1 O9 ]; J           //更新用户昵称  
1 S8 x! H: Z* k7 P( l2 M  M; k           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  ; ^& @! `6 r& R+ r4 h9 G4 H( V
           if($nickname) {  
; `3 T* Z+ ^' q              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
$ p9 M& y2 Q( z! p8 r              if(!isset($cookietime)) {  
2 ^" @: D7 R6 J6 B( O7 @3 M* l                  $get_cookietime = param::get_cookie('cookietime');  $ F/ u" [' {" Z- m
              }  
: t/ x$ h8 b3 a6 Q" V- h  f              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  6 }% s) t  S& @3 R0 d6 c
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
. h, o+ h  s* S. i. W              param::set_cookie('_nickname', $nickname, $cookietime);  
/ W$ c9 l* e% g- \* M4 U           }  
0 ^) X% y: z& h/ s           require_once CACHE_MODEL_PATH.'member_input.class.php';  
- i$ q+ {# B7 |           require_once CACHE_MODEL_PATH.'member_update.class.php';  " ~( ~. R% D: P9 _; C+ e8 I! J
           $member_input = new member_input($this->memberinfo['modelid']);  . N- F: z" Z" e  E: J
           $modelinfo = $member_input->get($_POST['info']);  
1 c3 N" z/ Y+ _           $this->db->set_model($this->memberinfo['modelid']);  
7 H$ I4 J' g/ L. e2 q% h           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
. i2 _; Q8 k( ?4 P0 `% a           if(!empty($membermodelinfo)) {  
5 m; D4 x; e& L              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  $ u1 H9 K' w  x4 j+ X
           } else {  
, |6 c1 X! r5 @3 A& G              $modelinfo['userid'] = $this->memberinfo['userid'];  
! [4 R  s& m1 f$ C              $this->db->insert($modelinfo);  % [1 W8 v, y3 ?( }8 b6 K2 S8 j) _6 z( [2 K
           } ) o, c9 z& K. U: [( x
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
* `# x3 w. W2 O- L6 V# Q% c在\caches\caches_model\caches_data\ member_input.class.php 文件中:- A  X$ b9 g5 f  a

# b8 o" s# _- M3 U0 _0 ~4 H/ r
- y* z, Z0 R( _5 q. q3 Q3 l3 N 1 k  z: E6 m& ~  C. _9 v2 }: ~* y
function get($data) {  2 c: d+ Q9 i% h. f3 k. \
       $this->data = $data = trim_script($data);  1 M/ x3 T9 t/ _+ m
       $model_cache = getcache('member_model', 'commons');  
& g/ W0 a" G1 s. u  N       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  9 {( I( V- e1 T, s0 @
       $info = array();  
6 c9 Y. x* ], J7 t" o3 }       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
1 D# W9 B5 X" r4 ]' o       if(is_array($data)) {  
5 u6 S' R& ]) p" n( N8 o) F           foreach($data as $field=>$value) {  
. {' Q! \+ F$ q              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
, Y( s& x6 d" d  D* b              $name = $this->fields[$field]['name'];  4 V# ]/ X/ _, ^8 K& r
              $minlength = $this->fields[$field]['minlength'];  
% ^+ W- a% O! N# U1 S              $maxlength = $this->fields[$field]['maxlength'];  * R: \/ u9 O# e+ Q1 A/ S) Q
              $pattern = $this->fields[$field]['pattern'];  
% b* e& e  o/ x1 m, k: q              $errortips = $this->fields[$field]['errortips'];  
6 P) {* ]8 g( m1 v' ?              if(empty($errortips)) $errortips = "$name 不符合要求!";  
1 `3 S1 E3 w" f4 y  s7 x              $length = empty($value) ? 0 : strlen($value);  
3 ^4 I( G1 J/ e              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  & j3 N$ u0 d" B' R" ?) U, o
              if($maxlength && $length > $maxlength && !$isimport) {  / d0 p: Q2 [* R. }; ^
                  showmessage("$name 不得超过 $maxlength 个字符!");  " o) f6 I6 B$ R7 a7 I8 ~& u$ [: e
              } else {  
! k( R; \* E5 [; ^                  str_cut($value, $maxlength);  
- P. M' Y" q/ f" a              }  ( ^3 @0 t1 K8 Z; O: o) Z2 p; k
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
; J8 c0 A+ `* b7 }$ @, z* e                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  7 Q& h+ l0 _* Y" ~: H
              $func = $this->fields[$field]['formtype'];  
5 O6 V& g, S  a3 u6 @& H6 l              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
6 C  E- o# s9 q7 i1 k8 x" a              $info[$field] = $value;  / b# l. b$ C0 w, }* s1 f
           }  - }% q% i( q! A- B; }
       }  5 w* l; S+ M1 Z8 c  Z2 P9 f
       return $info;  + \  {. {3 g7 y1 f, t$ I
    }
: x1 ^8 ]5 x7 ztrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
3 ~- {+ H- k9 Z5 U( [, S1 d8 ?, V2 Z* z# d
再到phpcms\modules\member\index.php 文件account_manage_info函数
4 j: ]! R7 `8 \% Y( l( j) _4 w. q过了get()函数之后。1 y- H. R+ X' |7 `
0 E4 r! r+ n1 X
7 C) o( a) M2 N) j
$modelinfo = $member_input->get($_POST['info']);  
3 {% e, j. p7 q) G           $this->db->set_model($this->memberinfo['modelid']);  4 `* T9 Z" W. y! k
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
: W- R# q+ q* X; u, R! N           if(!empty($membermodelinfo)) {  + w0 V. {: f+ Z. L! ~% u
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
$ G$ ~6 k* T) d5 }1 k           } else { : S3 A; J; N5 J' h
直接带入数据库,update函数我们跟进看看, T0 W* A1 n3 [9 a/ G3 A8 ]+ s

1 K' i5 ~4 s/ ^* Z' o  h3 e3 p" n$ z ' O: u2 J' J3 J/ Q- q2 m0 C
public function update($data, $table, $where = '') {  
8 T$ `% l* u. ^' S       if($table == '' or $where == '') {  0 z( `' ?# p$ o' z% C  k
           return false;  
) A4 O* l6 s3 X+ ^, Z$ P0 o" T       }  
8 d. O" t1 _- X5 I* C: h       $where = ' WHERE '.$where;  
2 y2 C0 J8 `6 _6 W       $field = '';  ! M/ a5 y: y' L/ ^' D$ g
       if(is_string($data) && $data != '') {  $ r& ?) k# Y) U! e/ @
           $field = $data;  + I% s$ l% {* d, {7 z
       } elseif (is_array($data) && count($data) > 0) {  
1 ^8 y0 C9 T0 [; a* t( k           $fields = array();  " ~" G, Z8 [8 o
           foreach($data as $k=>$v) {  ' _, d! C+ _5 j, T: w
              switch (substr($v, 0, 2)) {  
. L& u: L) [% F4 K1 [5 C( L1 ^7 V                  case '+=':  0 B/ N- z! d- V& o' M4 {2 e$ b
                     $v = substr($v,2);  
! I  t( b- _0 E( p                     if (is_numeric($v)) {  
" v5 ]3 V0 {- ?; r7 x0 B( P0 {                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  8 _) h  ]7 j; u4 h3 x8 }+ D
                     } else {  
+ [+ h) Y1 w, y0 g+ `' Y- @                         continue;  - X( x5 a) [: z4 O& t% f
                     }  2 }4 L! ?& n1 A' b0 X5 d
                     break;  
1 Q' [& t# p3 h; c/ y" e                  case '-=':  6 b0 o) L8 P) H$ {3 H+ p
                     $v = substr($v,2);  ) t% g9 z4 D: S4 b
                     if (is_numeric($v)) {  4 \: q# b# f( M: C
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
5 X7 R' F) F4 G; I4 d/ ~+ Q                     } else {  
# W+ Z  g+ H6 }' V                         continue;  
- m' T0 b- E" X, H6 r3 Z" T1 \6 ]                     }  
5 R) J. i" \' f! H& ?& {                     break;  - S+ H: n5 _2 K. g* ~5 L
                  default:  5 i+ i  u: f3 U! z8 e) Y
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  . w$ u6 K: {" V" D& ]3 }
              }  
# K" d6 f0 {( }* ~- V' U# I           }  ! t% T7 m% |+ a! \, ?9 D
           $field = implode(',', $fields);  6 w, ^$ e2 U4 D
       } else {  
% z9 z, z" I) A           return false;  6 p: \( n$ V$ ]% q4 p8 h
       }  . d5 a  F$ t- X3 U
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  % I+ G6 k3 J( a! a
       print_r($sql);  
4 o6 w& s9 q. `0 H9 |9 X       return $this->execute($sql);  * k) }4 j. Z5 n6 j
    } ( @, B& g* z5 G/ m. z  F/ `( a: s
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
- W$ L! k$ R$ C# y; r( x, @9 M  a- b+ c
攻击测试:) R  n( Q1 K, _1 g' E1 C* E  L0 s
测试地址http://localhost
4 X; C4 p9 S/ `  o) f* k$ g  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
9 W# X. J4 f# L6 F
" E- J& P2 k7 j, y8 D# u# I
+ g: o1 K# r9 o* _" C" z! x2 h- Q* b* r$ [! E( i* N

1 z: C& @9 U/ u
% A4 m& C; `2 s$ D- P  v

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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