|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
0 l+ T5 F& [0 W9 e漏洞作者:skysheep. a- u+ I2 N" u5 t9 w- W
分析作者:Seay. ?6 ^: b! W- x& {0 ?
博客:http://www.cnseay.com/9 Q% F+ h6 v/ c$ D
漏洞分析:
# g P/ v0 J6 |! a0 X" D 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
) p2 X- R! Y' S
* R ~ J8 y8 D
5 M: |6 M8 W/ ~0 u) X
2 G! ]2 ^) z: ], ?9 spublic function account_manage_info() {
2 J5 _. x5 Y% X3 n5 l* G7 N7 P6 ] if(isset($_POST['dosubmit'])) {
4 ], ]% Z8 h7 V" @: ` //更新用户昵称
6 R* g* i4 b2 n $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
" q5 r1 F$ S) j4 G6 Z if($nickname) {
) s3 j* H9 Z8 b4 l* Z' m9 s $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
& _! l# j3 v0 B6 A' v9 X if(!isset($cookietime)) { - n1 d6 X& I6 s2 U' |0 x0 g
$get_cookietime = param::get_cookie('cookietime'); , t4 R+ r$ }- G% _ }
}
# {- V6 B: X* ^- Q* ? $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); $ O% w6 d6 Y: O
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
. |& x7 u5 O1 M0 Q! }6 N param::set_cookie('_nickname', $nickname, $cookietime);
( @) l1 A) H7 f. J6 Y7 ]! T' q$ O }
9 ]3 e y. q* R+ e. d require_once CACHE_MODEL_PATH.'member_input.class.php';
8 H$ R% ]' E* T( Y" v2 _ require_once CACHE_MODEL_PATH.'member_update.class.php'; ) W' `- m2 N! w* t# e
$member_input = new member_input($this->memberinfo['modelid']);
- s9 |$ b5 I+ N9 v- J $modelinfo = $member_input->get($_POST['info']); - H2 I8 X, p* V! u" a6 Q2 O$ J7 [
$this->db->set_model($this->memberinfo['modelid']);
: A- T; I* F3 W* A/ e" P% I $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 9 m1 @; R1 x/ A2 y
if(!empty($membermodelinfo)) { $ M4 b% x" \% H' X/ X6 v! p8 S
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
/ v" ~# T9 N4 x5 Q" u } else { , E; L+ C( z N
$modelinfo['userid'] = $this->memberinfo['userid']; 3 S5 {* m/ q4 y" c
$this->db->insert($modelinfo); 4 |, L# W' W% {2 \5 S" Y- U& G
}
1 C$ i! C6 K9 o代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
! n' }5 w! n+ `( a: X9 t在\caches\caches_model\caches_data\ member_input.class.php 文件中:+ r! N2 G: ^6 j! H) H
4 Y0 {7 }8 H: c* w- H; {/ c4 S! W$ s* y c; H
2 t; l n5 V ^# Z
function get($data) { # L8 N. n7 d( Z- M* c8 F. |$ B
$this->data = $data = trim_script($data); ( J7 H5 Z: l* D8 c: {: j
$model_cache = getcache('member_model', 'commons'); 5 {" a% m$ w/ I1 z1 Z' ~% x
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
& |" ?+ e( A6 b6 Y3 O $info = array(); ; f! j! O+ r7 ?$ g
$debar_filed = array('catid','title','style','thumb','status','islink','description');
" q# a2 r! ?4 u: F0 i! }4 d if(is_array($data)) {
( C# z o2 v! [3 ?; Z: k foreach($data as $field=>$value) {
$ m `4 y: L" j if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 3 o8 Q7 _) l4 k2 p# ` H
$name = $this->fields[$field]['name'];
- e5 z1 [$ k N8 ^7 j- [" Y- m' \1 a $minlength = $this->fields[$field]['minlength']; ' }# W4 }; c. E$ ^( e
$maxlength = $this->fields[$field]['maxlength']; ' `1 J k2 U& b2 p- O* h
$pattern = $this->fields[$field]['pattern'];
, c% I4 p1 u' \ $errortips = $this->fields[$field]['errortips'];
. Y% d; ~5 O+ G, r ? if(empty($errortips)) $errortips = "$name 不符合要求!"; 6 |) n* h( j H: Y# \9 ~' H
$length = empty($value) ? 0 : strlen($value);
- j/ t" K0 S4 e: e$ r0 q' Y if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
# N* \$ v- Y3 C9 W, z, q$ [+ ]# V7 t if($maxlength && $length > $maxlength && !$isimport) {
% L H5 U$ A- ~ showmessage("$name 不得超过 $maxlength 个字符!");
5 E/ N+ w4 i) W+ G } else { c. i3 |/ q' ^# U
str_cut($value, $maxlength);
8 n+ Z, y6 D- m- F8 K8 G ?7 }' ~ }
- y, z: E/ o- Q* r0 w! \ if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ( R+ e$ ^5 K6 l5 ~& h3 J. w% S
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); $ d! t2 ~8 L% e4 t; x' s
$func = $this->fields[$field]['formtype'];
% l- `$ s& J! `9 J* g7 F if(method_exists($this, $func)) $value = $this->$func($field, $value); % y/ _7 O1 k7 e( U2 W' i
$info[$field] = $value; 3 Y7 j1 b5 ]# K; U U* ~4 e
}
6 K' L+ V( l: o5 A3 Y } 2 d7 ^4 i* _! B* V
return $info; ) ]3 q$ i1 @' N4 F( D5 q7 Q
}
" f8 q5 \- A. F2 Strim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,. Z4 o) _: Q2 G& T+ k' z* V1 Y
5 \' P2 t1 l8 u: t再到phpcms\modules\member\index.php 文件account_manage_info函数4 q: n9 W0 ~5 Q% k. c4 o
过了get()函数之后。- Y: `, N* \# t3 T0 j: w+ E2 ]) f
! H* g6 U$ H+ b0 x+ |8 z! g2 i) S
/ X. v h8 k" Z' n% W0 Q0 ~# l$modelinfo = $member_input->get($_POST['info']); 3 _% o% t+ {* r; F/ J& R: q
$this->db->set_model($this->memberinfo['modelid']);
( u* n6 B4 E( e4 V+ A& L6 C5 u $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
6 U2 s- T8 D/ ]2 b. W/ l, r- k if(!empty($membermodelinfo)) { 6 O: k0 }5 e% K
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ! b, |" Y' G4 p. l/ J
} else { g# X6 U7 q' \6 {# W" ^1 v
直接带入数据库,update函数我们跟进看看
* J) S I9 q7 j1 Y2 j5 G, [( D: N* B5 P/ H
. v; W2 Q# A, P+ F, ^+ b( Q; @) ^+ @public function update($data, $table, $where = '') { ) S/ C% ^" q2 U4 ]7 y4 `
if($table == '' or $where == '') {
- n+ X9 n0 a. Y( [9 T! Z, _2 o return false; $ v1 Z4 Y, ~- b- u
} ! L. }1 N" _$ |5 ?
$where = ' WHERE '.$where; ' [0 X1 n# O( K; u) m1 l1 t: k
$field = ''; A. f$ L: }; ^# m$ c' T
if(is_string($data) && $data != '') { 3 f8 D( C) L( G3 E2 m7 W
$field = $data;
- Y) X1 {) V" ?9 g+ A) x' u* V } elseif (is_array($data) && count($data) > 0) {
7 r7 Z' s$ x7 P5 p3 ` $fields = array();
1 V: F+ t$ O5 I1 V foreach($data as $k=>$v) {
7 [$ l0 i2 t4 T& D3 l! [7 @ switch (substr($v, 0, 2)) {
1 ~/ Y- X7 l n$ Y; G# _) T case '+=':
1 P8 r0 X" {, b' K- b! m; _ $v = substr($v,2); - U" P# E' k9 ^ \' x
if (is_numeric($v)) {
" J6 N* B# B8 G; u) ]* {5 C $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); & N# [+ n5 f# k) @' q
} else { 3 O9 A) l( S R' k
continue; 0 ?4 U7 @% W' S. A, Z2 q4 ], u& `
}
/ A" t; {) j0 ^5 T, L/ v8 U9 `+ z break;
/ A. s' C: C$ Q6 w1 j' j2 [/ \6 C case '-=': + D7 O) U/ Q3 k3 c
$v = substr($v,2);
' C& [% Z! H6 L if (is_numeric($v)) { 0 x4 b9 s: S3 v& M
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
! o6 J4 C! `; R( ]+ B } else {
# ?$ W& t( C( l( t2 d0 Y continue;
4 x7 v8 E% U4 e- [ }
# `' G. a2 W; R break;
+ f/ Z# f1 t5 e( |/ p& ]4 a- g% ~' ` default:
g$ I' V3 \: W k1 ~ $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
: J3 N4 }$ C* X: {. ~ } 1 T( U9 _/ H5 E# @+ F; D b" s; |
}
8 z2 c K; }$ E $field = implode(',', $fields);
& j$ \$ ~. U$ k+ S& M* g5 d, G } else {
5 V; M9 a6 k0 r; J7 D0 v, r return false; ( e, i+ g. g5 z* L, b& f1 M
} 0 ?4 m: B8 `2 P9 _
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; ; u" F1 @$ |% C1 _$ q6 V
print_r($sql); ! r5 @; t; J8 n2 I: H% n- q
return $this->execute($sql); 4 J/ ~( Y6 _. s3 K5 t
} # [) ?* u1 u* v1 ?+ `9 F
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
. o, O# s* B2 o/ ^8 H! n9 ?( h3 s& B5 ~6 ~6 n, @3 M
攻击测试:
! a$ i* e8 J. i' U4 V, l测试地址http://localhost6 Q1 |1 U0 X j# _' x
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句! F! Y* W# o3 L4 I! k2 d7 [
! c( y9 A j) t1 @( w; i9 H% K9 n
h/ O. i( E' z' _
, l/ x/ m# F4 k' g! o
0 V; h, L1 o- [, q
5 Z" R3 G3 G/ ?# d. _- a' P! K( v |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|