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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
# a& r% O7 J8 l/ h/ X( P5 P漏洞作者:skysheep
9 O9 G  A$ J. y分析作者:Seay
, u9 p! h! a8 [' d6 _$ E博客:http://www.cnseay.com/1 Q$ W# k7 S. [5 l
漏洞分析:3 D9 W3 O* C7 T& _/ U& K2 S
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。  ?  _7 V( X+ K

+ G5 a0 ^% x# Z  S6 ~
0 x: m/ W+ u/ A' k# X' K 7 H- @8 O+ J* [' d; B$ @' |" I4 @
public function account_manage_info() {  : Z, A7 I5 C5 N5 g
       if(isset($_POST['dosubmit'])) {  
1 G7 S" O3 n0 Q, K           //更新用户昵称  2 ^5 Q9 B1 D2 m$ r2 o, q  p- J
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  - p2 |# J$ n! t
           if($nickname) {  ' ?2 v' Y0 ^) L0 d
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  ! K* K7 g7 {" m% B2 f: Z; L
              if(!isset($cookietime)) {  6 X3 _' M' I0 ]3 P4 s4 A) M- p
                  $get_cookietime = param::get_cookie('cookietime');  
# s# a. ?! }( E! V3 m$ S- F" E              }  5 [" S8 o6 T' R6 L
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  * s4 w: X  o) H- I
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
* c1 N6 [; Z- f, ^5 h4 U$ y              param::set_cookie('_nickname', $nickname, $cookietime);  
+ V# m% W1 Q% ~! X           }  
2 ?) v( W$ `7 F7 d           require_once CACHE_MODEL_PATH.'member_input.class.php';  
; p- g. L4 `& Z9 L& W           require_once CACHE_MODEL_PATH.'member_update.class.php';  
0 s4 B" [) @# E) h& p6 m) S- ~$ t           $member_input = new member_input($this->memberinfo['modelid']);  9 _/ v. H; a# s; C# z
           $modelinfo = $member_input->get($_POST['info']);  
. y) ]$ n' i8 ~1 Z& L           $this->db->set_model($this->memberinfo['modelid']);  + F4 V# {8 k" s
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
  l* U. h+ m4 y( n           if(!empty($membermodelinfo)) {  
2 K1 K/ I4 J8 S/ J7 M: ?) e4 G              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  4 {/ t4 [  g0 ?% X: v) t- F8 M
           } else {  
; [' _( c7 b4 e              $modelinfo['userid'] = $this->memberinfo['userid'];  7 M+ X' {( h" F- u6 M. z6 X
              $this->db->insert($modelinfo);  6 v3 O3 z3 j2 A
           }
; V  g; _) f! v4 d5 k1 `1 X- W代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
' [- q9 G5 Q6 b# ]在\caches\caches_model\caches_data\ member_input.class.php 文件中:; a6 f0 e& k! y, E0 v% N5 f- A- J

0 v. L+ V9 X0 y& }# i9 K3 ]% C# C+ _$ v& Y) J
! O+ n: G) Z. E5 P! P6 k: P
function get($data) {  
3 f/ Q6 H$ \6 w$ ^       $this->data = $data = trim_script($data);  
) ]( M' U. O) [0 s9 D+ e& A6 i8 y" k       $model_cache = getcache('member_model', 'commons');  $ U$ \/ ^, p) y6 f4 y8 N
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
$ j. a5 y% [; F$ u- }: }5 e       $info = array();  6 g. z, h' a$ T
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
; B: V, z6 F; `& V       if(is_array($data)) {  + L: t& E5 ?) |" |/ o/ f
           foreach($data as $field=>$value) {  
: [/ z2 b$ x9 H0 K              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
6 {0 J. p& ?$ E. R- F6 z              $name = $this->fields[$field]['name'];  
/ i/ C' C$ F. Z- d7 Y              $minlength = $this->fields[$field]['minlength'];  
5 l1 }) B- |7 a' u$ S              $maxlength = $this->fields[$field]['maxlength'];  $ p2 v! V# F9 D' I1 |
              $pattern = $this->fields[$field]['pattern'];  
& Z9 W1 X5 w3 W2 \              $errortips = $this->fields[$field]['errortips'];  
5 ?$ ^3 E: V! i% C2 W( w- Q, g              if(empty($errortips)) $errortips = "$name 不符合要求!";  
: b6 \# V& C9 z$ [% K& Z3 J              $length = empty($value) ? 0 : strlen($value);  3 f, ]0 u$ Q8 E) F" e" \1 ^
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  9 n1 ]  ~4 x4 ]# c: d2 T
              if($maxlength && $length > $maxlength && !$isimport) {  
6 O+ Z0 v% g: E                  showmessage("$name 不得超过 $maxlength 个字符!");  
2 C5 g, [* x" U! p6 K' L              } else {  + M' l* p/ ~4 D/ k
                  str_cut($value, $maxlength);  
7 B$ \1 ^/ B. G, _' }6 x              }  
& l1 m, }; S  U' N) Q7 K5 i1 Q9 U              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  8 A2 H0 ^- F4 \+ d. R  _
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  4 ]2 J+ T- l1 z: e
              $func = $this->fields[$field]['formtype'];  
+ y! ^+ V" r7 q" ?" X1 D              if(method_exists($this, $func)) $value = $this->$func($field, $value);  * y* d4 `( Z, w. C4 F$ T& S3 r
              $info[$field] = $value;  
$ S: M# r! L5 o$ u: b1 f           }  8 c6 x# D2 A3 [6 O: c3 l, L
       }  ! [6 z7 f) f$ Y
       return $info;  $ B* G2 m  G) }# b7 ?4 u/ o- O
    }
; w& ~5 K+ L) d  K3 M3 {trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,8 S3 X* Q) O8 p2 z$ N/ H* c
9 d0 G0 o) r3 ^. d( W- J9 h
再到phpcms\modules\member\index.php 文件account_manage_info函数
! J, i% `% d5 c" W, v. |7 {过了get()函数之后。  x, Q" @( C* p2 U* v

. W- z3 Z' B  ]7 X$ H 5 `; m6 a6 P8 e) ^
$modelinfo = $member_input->get($_POST['info']);  
/ F* V, a7 ~5 W; n           $this->db->set_model($this->memberinfo['modelid']);  
: i. g! E3 }6 W2 R- e, c           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  9 m# g; \4 ], G4 ~  ?' |
           if(!empty($membermodelinfo)) {  
: n- \5 u2 ^8 Z( c; B/ a              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ) N$ F( [' u5 G6 |  q/ I) w3 E) _
           } else {
8 D1 \! n: }$ Q' k5 G( n$ _* h直接带入数据库,update函数我们跟进看看. `* u9 r: w/ _9 u

8 F" z7 u/ L. C " a' Q- w( n4 s+ n* [) X& E& S
public function update($data, $table, $where = '') {  , C, ^# X8 \( z* h; U0 O5 j
       if($table == '' or $where == '') {  
8 {5 u, p+ N/ s7 D           return false;  . u# \0 |0 F% C, i
       }  
, h7 _; _+ x: K7 y4 I       $where = ' WHERE '.$where;  9 u3 l5 A% I  y* W% @- h
       $field = '';  
3 g# ~3 i9 h3 Q, B% b8 n( N       if(is_string($data) && $data != '') {  
; \: L+ D# s- ]$ c           $field = $data;  " u: d3 ^2 e( c; Q$ ^" L8 [8 q- o
       } elseif (is_array($data) && count($data) > 0) {  9 i1 N9 k& e2 j# O+ f
           $fields = array();  
& z- [& P9 {8 Q9 O           foreach($data as $k=>$v) {  # G5 t8 @3 Z7 l* M5 J
              switch (substr($v, 0, 2)) {  0 ]0 H. p- @" H* h3 {: g
                  case '+=':  5 X1 n" Y+ C# q# N
                     $v = substr($v,2);  , x# J% }/ X% _) |
                     if (is_numeric($v)) {  
! z9 `& g8 G& a; c+ B                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
* f+ A& s1 _) ]1 k9 H                     } else {  
- J; m! z" s9 o( I& I* n) Y                         continue;  # L5 \& S' B6 o: a1 R
                     }  ; O1 W; u! S9 p" _" T; |
                     break;  
4 A6 u# E9 O" K# S2 @. T                  case '-=':  
% `# a1 Y( d* m+ w* M                     $v = substr($v,2);  
" x9 o) S2 C: P( C) L                     if (is_numeric($v)) {  
: Z! I2 k% x; |$ \. N. W                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
+ p' c8 A+ y1 O, M                     } else {  ) }: m$ H+ d3 S; m2 x3 z: _
                         continue;  ! s$ U  s/ R. i
                     }  
3 z/ g3 A% B4 n0 E7 Y& \" b6 z                     break;  # K# o4 T! I' U0 A) r% v
                  default:    @3 U* [7 P; A
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
6 K8 }7 {3 {0 D              }  0 w1 h4 `; p5 g) D+ B
           }  9 b2 Y8 A9 k! _) L
           $field = implode(',', $fields);  
& s2 n, g7 ?3 E; U1 S% b! Z       } else {  
  s% e4 A6 C* y8 I5 x3 K6 c/ y           return false;  
) G. Y' O% e( B       }  - q( R3 A: Z) `5 Z) H) X
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  & P, c$ Y: d1 d( A' m6 H
       print_r($sql);  7 G! ]3 \5 ]8 j! _& v6 ]. r
       return $this->execute($sql);  
+ O) I- S: |4 o) u+ B* D8 U    } $ D, l; [1 H9 v7 I9 N
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。) l4 D  N0 A6 f

) }) S- v7 f9 u, W7 B# N攻击测试:4 Y1 s, S2 A8 ]- b/ |- B* B8 b' Q& m
测试地址http://localhost6 v, b/ P/ d- @" y# r
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
+ r* u& O. H  U, W. t- m8 x: n  Y* I; `" L
6 \& ~# X  j1 ]! C

! S  y& ?0 P% u+ s
2 i- h9 J: W, ]& r8 n. [' T$ M" {& ?- D8 n% T4 s0 r

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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