找回密码
 立即注册
查看: 2774|回复: 0
打印 上一主题 下一主题

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告$ J' ^6 {; d7 g+ p6 I0 c- a
漏洞作者:skysheep
2 {! D$ u3 @/ b% v7 a分析作者:Seay6 Y3 L9 q  Z3 E- _) m9 a; V: M
博客:http://www.cnseay.com/2 v& C+ J; z9 L
漏洞分析:
) b  o5 E3 R  F5 E1 ]  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。7 z0 m4 O7 z. @3 p0 o) C; u
. ~$ Z4 _$ o* s7 d, Q# i

' g3 S" L' t" K; J 4 J, H5 s0 w& m! K
public function account_manage_info() {  ) Q, O; R7 h! d; K
       if(isset($_POST['dosubmit'])) {  2 q5 k3 k: _) x1 X/ W+ H
           //更新用户昵称  0 {0 p5 c0 u$ ~/ x
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
* z1 p& W( X. K1 U3 J9 M8 a6 `           if($nickname) {  " N% i8 Z1 [! D. O5 ^. ^
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  6 p+ H# |1 L) u$ a  E
              if(!isset($cookietime)) {  
' B, H; A7 n7 e                  $get_cookietime = param::get_cookie('cookietime');  
* \1 o+ W3 T8 _              }  # ?# G( h& t" k4 l& i3 M2 y
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  / R% R% A. j9 [* G; A4 K3 P
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
* h4 @% y- p* c/ ^0 Q1 Z              param::set_cookie('_nickname', $nickname, $cookietime);  : c" d9 X. S( l1 z: {
           }  
) O' O, a8 T5 a2 Q: @           require_once CACHE_MODEL_PATH.'member_input.class.php';  # }7 I8 k! k  f% b+ B
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
% S8 g" U. L0 t  B           $member_input = new member_input($this->memberinfo['modelid']);  
/ |/ W% [7 m: W, q8 M9 L  R& s# Q           $modelinfo = $member_input->get($_POST['info']);  : g2 j( i7 |+ W8 i, e
           $this->db->set_model($this->memberinfo['modelid']);  - [' Z. W0 a, P, J4 B" D0 {
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  9 v2 I' H! Y. }3 ~: p
           if(!empty($membermodelinfo)) {  ( Z% W4 r3 J5 V4 O3 j1 }
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
) H9 K3 ?& C: V' D) }( ?           } else {  $ [, E. D9 ?; C
              $modelinfo['userid'] = $this->memberinfo['userid'];  
$ o5 H& h+ t! H+ F              $this->db->insert($modelinfo);  $ s$ {& y  V% |! G6 I1 i, f- O
           } 0 U1 m7 a. P; E7 k8 s
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
7 {) Z% d4 L, q3 G在\caches\caches_model\caches_data\ member_input.class.php 文件中:5 {) f2 k% g$ i  R  T# n0 Q

  L' i% C/ }, v1 w" a" l' h9 V( n
" H0 F  N8 K* D  `" {; k . w$ A4 h0 [% B, Z0 c3 N0 I
function get($data) {  
4 c7 W8 ^. H/ o, K0 @       $this->data = $data = trim_script($data);  ' V4 R. S; Z: X( x: M7 P. e
       $model_cache = getcache('member_model', 'commons');  
  D: x1 l9 O) ?$ }       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  1 ?$ E: b% C  B- ^7 ^7 B2 N
       $info = array();  * e  p% J) T( [5 x, j: J
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
3 }  u9 ?& j* t+ p6 b% I3 b$ D! i       if(is_array($data)) {  / s* j, t: w5 h. T. T6 B- ^( H
           foreach($data as $field=>$value) {  
: }/ B) Y/ @4 k8 t: ?! V              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
* k" h+ H0 J$ o              $name = $this->fields[$field]['name'];  : q, \/ d1 G: ?( i
              $minlength = $this->fields[$field]['minlength'];  
2 k4 M/ u/ M$ A9 l4 d              $maxlength = $this->fields[$field]['maxlength'];  
; H6 q6 N6 ^/ T# I' h, w" m# D              $pattern = $this->fields[$field]['pattern'];  6 d+ v7 X# G- k# Q9 Y. P  i2 L& l
              $errortips = $this->fields[$field]['errortips'];  
$ G% B* O/ {1 c$ M6 V% p; u3 M$ @              if(empty($errortips)) $errortips = "$name 不符合要求!";  5 U* t! o* H- n) S3 |& N. ~
              $length = empty($value) ? 0 : strlen($value);  
3 ~# g2 |0 o) G" ?5 C4 Q7 A              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  ; D& D; B. I4 W0 L
              if($maxlength && $length > $maxlength && !$isimport) {  
& L3 W% |  i* _                  showmessage("$name 不得超过 $maxlength 个字符!");  
" c- T" u. w9 a0 ?% x              } else {  , ^2 c2 s6 A( h+ X. l$ G
                  str_cut($value, $maxlength);  0 c& j( U5 K8 u. t4 Q8 }" z7 c( n
              }  4 t. P( y8 \8 }  P: Y
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
5 A& L! m# u4 l1 T2 T                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  " V3 |0 y& d# `7 ?/ T
              $func = $this->fields[$field]['formtype'];  
# m! L, d. n3 l3 ]- m- K( n              if(method_exists($this, $func)) $value = $this->$func($field, $value);  ! }) V; l% Z8 h/ v' I, @
              $info[$field] = $value;  # Y) ~; Y4 n9 z
           }  
* z, D+ i) p; h; l4 U: F       }  
! P9 J+ v& y/ p: e  s       return $info;  & l9 z3 U% U: S. w! K
    } % F2 ?4 l# d& U
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,/ Z! |" n' A% e& ?

7 k. a' }2 W1 R+ i, m  g* h再到phpcms\modules\member\index.php 文件account_manage_info函数+ W8 M4 d6 M1 ~. y7 f) {6 w( C
过了get()函数之后。
# j8 Q2 q" e3 k& ^) I% L8 h4 x5 i6 S9 P, x! a, C. [
& q, Y9 U1 ?4 K
$modelinfo = $member_input->get($_POST['info']);  
* R  {4 U. Q7 }/ [+ X0 l           $this->db->set_model($this->memberinfo['modelid']);  ' ?- R2 A/ c& I4 w& n( A
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  ( g; z' ~- U+ S; d) @  L& {) S, G
           if(!empty($membermodelinfo)) {  ) s7 Z% {1 b9 P# q9 l* S
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
/ b3 t( t8 Q* J# A: j( Q           } else {
2 Y% U* F  W5 P' r3 |直接带入数据库,update函数我们跟进看看
, Y' E& f: i+ U; g. u- U: E* t& S. G/ E# I5 y& C
1 y9 l. P: d" s7 k9 ^
public function update($data, $table, $where = '') {  - j' N' p$ y% i  b7 v: T5 u% L8 M
       if($table == '' or $where == '') {    ]& V8 E" M8 d
           return false;  
6 ]) J* r; Z3 \: H8 h9 a       }  
: T9 Q5 J+ [" |  w5 a       $where = ' WHERE '.$where;  , g: ^2 C6 d, S1 R( k
       $field = '';  1 M9 h7 M+ T$ L( ?6 i: Z
       if(is_string($data) && $data != '') {  
7 E- l, T3 ]4 `7 R' c           $field = $data;  8 z0 G/ T: L1 M+ R/ b# K4 J2 l
       } elseif (is_array($data) && count($data) > 0) {  
6 m* U* x. C' U# G           $fields = array();  & t% a/ t% G8 u$ D# j: j" u3 I% }
           foreach($data as $k=>$v) {  " |; @( `9 Z- K4 o! o& H# M+ _
              switch (substr($v, 0, 2)) {  $ {. g0 q! Q7 o9 I4 i/ o% {( f
                  case '+=':  6 x! @! m) T, d7 K( ^) X
                     $v = substr($v,2);  
$ q$ x) z% R2 j4 M" V, ?                     if (is_numeric($v)) {  " l! L# ^0 @( U' s+ k9 ~+ m" G8 M' s
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
. }# \% V# t0 i$ X( C) H9 o* n  h                     } else {  + h" l; l* a5 S3 D
                         continue;  
+ n$ @9 E" x9 z  w: G                     }  " Z3 q; r& {5 L
                     break;    b& B% o) @( {# X. E$ d
                  case '-=':  7 Z1 h* a9 h' [, Z, E- [- E
                     $v = substr($v,2);  ! l* V+ j1 d% Y5 }6 N
                     if (is_numeric($v)) {  
# n# Z7 S8 z$ ^( c% [' n# B                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
" o' [% @- Z1 Q                     } else {  
6 j0 A1 m3 d6 Q% z                         continue;  
: N/ h& k, |6 A5 J" s                     }  
6 ?- w2 N7 r9 p+ m# B" a" a                     break;  5 K& i9 b6 f! B
                  default:  
- Z5 K" I7 U5 T% C; v                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
0 V2 S4 E5 @$ N9 {* D! ?              }  
; D# K, Q' k2 k           }  ' a2 |8 o* w8 ~
           $field = implode(',', $fields);  7 E5 @) @/ F  A) r7 L+ n5 S
       } else {  2 f+ m, U4 ^/ a# I# M( Z
           return false;  4 E2 i- U+ B6 w' @, \
       }  
9 m* H, O; P* |4 z; a& y% q       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  - h# o, _# M/ U0 e+ a6 u
       print_r($sql);  3 x: i& x0 q0 [3 f$ I% ^& r3 U
       return $this->execute($sql);  9 v3 C; i2 f3 n  p9 [8 f( g' ?
    } 4 T3 F, d# v7 P! L
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。$ h& S5 \3 q4 R+ a9 \" Q2 i
7 P9 {( g5 c/ Z# y: U1 {
攻击测试:, i* f" ^- |: G# a- ]2 v6 v
测试地址http://localhost9 X- Q* i+ f( {9 p: V# H
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句! g3 ^4 J+ v& Q( |) x
8 u$ ^$ i3 y- j- g# e, p$ ?

6 j% U, H2 b7 I- ~& J* [
) j6 |5 \( H. r; k2 M8 d; V
: ]2 D$ z8 \$ y( z" u
4 ~  @( i) h! T

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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