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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告4 B1 n$ X! h  {
漏洞作者:skysheep
; T1 p3 N  I' I5 t分析作者:Seay
6 Z& }( M5 a: D- r) M博客:http://www.cnseay.com/. S  @/ d+ f9 ^) v5 I$ E
漏洞分析:' C/ l- S; @7 }7 i4 Z
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。8 D( p6 `1 p, Q  ^
  a6 _+ E1 D* I/ X4 Y* o2 ]
4 K% i( N, ]0 i1 G" q! q

" B* Q) L& b- l8 O3 u* n- Bpublic function account_manage_info() {  
9 L1 m% A; l7 S& x8 E, l2 ^       if(isset($_POST['dosubmit'])) {  
! ]! g, m& `$ h' i& v           //更新用户昵称  ) Z8 c- O4 y6 e% N3 @# B
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  * c7 h$ U0 |- A& U
           if($nickname) {  3 C8 }& j1 o# _" ], B$ [* B
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
- e+ o" \7 r" s              if(!isset($cookietime)) {  
" J$ W2 P2 M; p8 U3 Y                  $get_cookietime = param::get_cookie('cookietime');  2 `, v9 Z% e6 i1 E
              }  
* X5 ]( I8 N/ I. N% E1 s              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  4 ?6 G0 T7 a& L: o5 a
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
' }% j# y' ?+ n              param::set_cookie('_nickname', $nickname, $cookietime);  
8 f$ @, ]% g" Q# }6 y% I           }  
6 c# E- m/ h1 B# p' R           require_once CACHE_MODEL_PATH.'member_input.class.php';  ' I! h2 {* c9 L5 i# q& T: T' _
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
- `4 x0 N8 n! Q- u: w, w& t* w- E           $member_input = new member_input($this->memberinfo['modelid']);  
! F, {1 a* U, ~6 v& H& b           $modelinfo = $member_input->get($_POST['info']);  
# T8 [, {1 r+ G3 @5 n6 E* c+ P* u           $this->db->set_model($this->memberinfo['modelid']);  * J. `+ s( \$ ]
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
7 _" e5 I  `2 j2 \5 ~4 ~! i7 v- v" E: A           if(!empty($membermodelinfo)) {  & V8 B; [1 e, ^' z+ s
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
( R4 x( i  w! F$ V1 N- D( _           } else {  0 x8 H6 d5 p: ]9 V! O5 m) P
              $modelinfo['userid'] = $this->memberinfo['userid'];  
5 C& |2 @6 j8 B( i" J              $this->db->insert($modelinfo);  # F* B, n+ a, ?  Y8 B
           } & P$ ^  L  I; u3 T9 r; Y/ U& D
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
, F; T0 [1 [" N, Z2 D$ b2 ?. k+ ]在\caches\caches_model\caches_data\ member_input.class.php 文件中:
& j! t( Y% S, r0 ~: W( r' F/ @% M) q: m1 @7 _; \+ g6 C; Y9 _

( p( r# k! Y1 x. \5 u+ o
; S5 h9 d4 O& P* I. F2 _8 f# w# y/ zfunction get($data) {  . _3 c" \  p5 a3 \! w8 t% A, ]' D
       $this->data = $data = trim_script($data);  : h) f5 l. o3 q. M* W
       $model_cache = getcache('member_model', 'commons');  ! ~/ p2 G& ~& Z" k& Y
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  $ V9 f& q# L) j% _6 m
       $info = array();  
. c  D( f! H7 H2 i, L6 a       $debar_filed = array('catid','title','style','thumb','status','islink','description');  5 z& f8 J) b# s
       if(is_array($data)) {  5 X, M9 o, n9 F. P, X) v
           foreach($data as $field=>$value) {  
' h9 y  q& H/ ^0 u8 B3 Z              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  1 E% [1 I( {5 ?  j; K
              $name = $this->fields[$field]['name'];  1 U' h) H+ N- j4 K
              $minlength = $this->fields[$field]['minlength'];  
: q. Y! v; ^/ e4 @: P$ b( a* x              $maxlength = $this->fields[$field]['maxlength'];  
- |1 C8 e, }, Q1 f% n- c- p) S3 m              $pattern = $this->fields[$field]['pattern'];  + v0 T5 o- |1 q) L3 S7 Y: r( C
              $errortips = $this->fields[$field]['errortips'];  / t. O- Q  o3 @# j* B# V5 ?: p
              if(empty($errortips)) $errortips = "$name 不符合要求!";  : X- b7 {1 o  j6 Y4 P
              $length = empty($value) ? 0 : strlen($value);  
# N0 I7 g  r1 w7 N: [$ A              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  / X  A4 h5 j% k4 {4 m
              if($maxlength && $length > $maxlength && !$isimport) {  
$ x, _2 u, e( u3 Z" H# `& a                  showmessage("$name 不得超过 $maxlength 个字符!");  ; {: I2 E, k# `$ X8 u; f3 w  p
              } else {  
  o9 `$ t5 F$ q8 Q9 G0 D                  str_cut($value, $maxlength);  
5 t; Z  X: P. w$ f" R8 P              }  # o* b6 u: r* O- K1 a% y
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
$ K% {, r; x( a. k) X                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  4 ^, i! F/ z* q! j
              $func = $this->fields[$field]['formtype'];  ) b( X! `" V9 ]3 |. Q
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
7 N5 S2 `6 `8 d3 H& D              $info[$field] = $value;  : l& C# C+ R. b$ T- m  m
           }  + P% }! X6 o: f2 C' t$ H
       }  3 D" D' u+ M$ l0 u1 c
       return $info;  
- F& f6 W, p6 H& b4 o0 X3 O    } 3 x6 x# c4 t" l7 `1 D4 P5 P# [6 u
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
: L% }- y) _3 E" i; V
4 P( w# U. p2 M8 O. Z' Z% t再到phpcms\modules\member\index.php 文件account_manage_info函数; k# r- ~# h% ?: S( V
过了get()函数之后。5 C3 s- s+ j: u# D

3 f% c' K+ r9 y; {0 e4 Q5 U  }0 h& F + o1 D" B( }0 D1 d* v: c) U9 m7 _- n
$modelinfo = $member_input->get($_POST['info']);  & q+ b( E2 g2 R6 [
           $this->db->set_model($this->memberinfo['modelid']);  
" y8 r' w9 E# C, m1 S           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  . {6 w2 @7 d( A2 U
           if(!empty($membermodelinfo)) {  
* {# D4 V* u9 U- V% A* g              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
- N; H' |( L, G$ y8 v. `           } else {
- x; ?4 c+ W; W* [直接带入数据库,update函数我们跟进看看
" ~; o: f3 a8 t' ^+ X. X
, X( o+ [! A/ w, w( i8 h
% Q% ~. X$ Q. u7 u7 e7 I+ ~+ Z1 |public function update($data, $table, $where = '') {  ) a8 O- @2 Q0 \! N" W5 N
       if($table == '' or $where == '') {  ! q' W# @) f9 C. t/ H
           return false;  
. z9 _$ @3 T1 D# A& q( E, [3 g       }  2 T% H2 r2 z# t' T- ~, o8 }. X1 e
       $where = ' WHERE '.$where;  
% e0 l7 d. F9 J: Z" m, a, }: |5 I8 C+ D       $field = '';  / o- V" T' S( C; G! V. {
       if(is_string($data) && $data != '') {  % Z; w: i4 P. x0 ^) l
           $field = $data;  3 |% t/ F% I- w1 H4 h
       } elseif (is_array($data) && count($data) > 0) {  
0 h) m! U* a8 o           $fields = array();  
  g9 ?! k5 q* z; I- D6 P/ Z6 B& a           foreach($data as $k=>$v) {  3 w9 F8 h1 \0 n% q' ?
              switch (substr($v, 0, 2)) {  
7 T8 Z, f( G* _" b                  case '+=':  & C5 L+ i2 B8 L, v8 N/ T& o
                     $v = substr($v,2);  . J' `: q, T* e# q3 A# Q
                     if (is_numeric($v)) {  
2 }( B8 K8 c  @4 ]* V" r                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
2 K$ Y* g7 [' i3 |- R4 x% g" A                     } else {  
4 f( a% q1 w: ^& U( E. b                         continue;  
* I# X. U; I- e& n9 Y9 G3 L* b                     }  
% j8 e0 K2 X2 U( r" Q- V; ]( o                     break;  ; A, u2 C& [6 i$ a
                  case '-=':  , [9 O: `2 h8 J) R3 V
                     $v = substr($v,2);  ! ]1 P) j% |' I# {* a+ A
                     if (is_numeric($v)) {  ! g, ]" A& f# g4 `
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
3 N" U. f: v6 g0 _: `) a                     } else {  
; H' _1 C! x$ a# \6 b                         continue;  1 q, N  O" ?0 C) c$ ?* u6 h
                     }  
; G4 D8 F+ j' v1 B) `                     break;  9 U" Y3 L: z, r4 @& ~* [( b, z; ]
                  default:  
5 a! c) ?) k4 m" p9 |' {4 Q                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
' n" o* S, }6 W  B% {  N              }  & l& q* A- i. c5 R" E( P% h, g
           }  : u) X7 ~) w9 M8 h( R
           $field = implode(',', $fields);  
: u& M5 k( N8 j       } else {  - F+ Q: u! \0 G2 q% i/ C& t$ d! O7 b
           return false;  3 q( B. ?7 w" H  R, N& b, g
       }  : _  }* c/ }- R6 b1 B- h6 @: u
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  7 x+ |) `! r+ c& N4 |, D
       print_r($sql);  
2 B9 D4 C& n! ^2 l! y2 R       return $this->execute($sql);  ! ]! S7 F. i" U7 Q2 u. H
    } 9 _/ `7 Y( a" x* {; ]
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。5 O) n5 U; R8 m, k

$ C; x) [- l& `! P攻击测试:
/ B* h+ o+ Y9 \; F; ^1 T测试地址http://localhost
4 T3 \: O6 e+ Z' X8 ]/ u. S  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
. O* c0 k, v& g$ n  k
9 z& O& L3 ^5 C( C+ B) V/ K : B7 K: `' ?( Y

# B+ x; c$ T9 w: j; B% p
6 f; ?0 c6 u0 ~2 [; ?5 c) H  ~+ q5 q2 \' l' t2 x# D

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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