|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告7 [; ?- n( C3 Q- E3 h% M) Z5 p) g
漏洞作者:skysheep
: Y& l- Q% l5 R' u& \0 |, a% g4 |分析作者:Seay
2 r2 c$ O, }; a7 c& M6 h' r3 c博客:http://www.cnseay.com/$ K) a9 u2 u# j# _; Y l# j
漏洞分析:3 Q {5 d/ V5 X
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。. D9 w D5 {) F* X/ [
3 k! o! p. Z& J) u0 g5 ?: z$ ~
# [ B+ K( r0 }# c2 c
: r6 q7 ]# a; t7 v- ~public function account_manage_info() { ' G# F! O; _5 D# h
if(isset($_POST['dosubmit'])) { . F* B- @0 P, a# i
//更新用户昵称
' M) r5 `0 ^6 D! M" L) D $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
$ R: G7 ~3 i% ? if($nickname) { C* C8 \( r* @3 e' @* d
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
% l1 Q. v8 r/ h5 F; @% P* V if(!isset($cookietime)) {
& e' Y& Y t3 u% T& \7 Y $get_cookietime = param::get_cookie('cookietime');
( U8 I: O/ l& ]. F } & |6 I0 T5 s( T; ~0 K/ S# B0 i
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 3 u6 g- K3 c- J) ?9 i! B' `. @
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
4 G* c' f9 U5 V6 A/ |% A6 ^* ^+ I param::set_cookie('_nickname', $nickname, $cookietime); * f* u* f$ \' D" `/ O9 A9 u
} 2 F! ?, `9 J$ A6 k
require_once CACHE_MODEL_PATH.'member_input.class.php'; 2 o! C2 G! `$ v; N! e
require_once CACHE_MODEL_PATH.'member_update.class.php';
t" `2 E" ^; f2 z4 R8 h $member_input = new member_input($this->memberinfo['modelid']);
7 A# [. y7 Y- l- h8 m( i6 J $modelinfo = $member_input->get($_POST['info']); $ z( W7 i' |- ^4 d
$this->db->set_model($this->memberinfo['modelid']); % |. O) p$ W- A5 ]& i
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); % F2 I& ^/ z2 }$ J1 k
if(!empty($membermodelinfo)) { $ ~. g3 ^: H0 t3 Y K `
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
: i- P) b+ y1 D' o) R2 n } else {
* J$ V! R3 s6 R0 J1 R* m' Z $modelinfo['userid'] = $this->memberinfo['userid'];
2 w7 Q, s# y6 ?3 \& M" |8 I $this->db->insert($modelinfo);
- i- L) [$ f) g! @3 E }
0 y% v7 R9 P [. Q6 a2 T) I% g代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,9 M; Z- C) I7 i1 }8 R) u
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
) ]) S' F# R) n4 l% S8 F0 b+ o8 u( M
2 B9 L4 M0 p8 @0 E' \
: y) n5 x- T# q) T1 ~& R0 d0 S
; L" X% x1 u! Bfunction get($data) { 0 B6 w A; p! Q v9 f, v" z
$this->data = $data = trim_script($data); 8 N. _6 X0 y* N, k, U) _
$model_cache = getcache('member_model', 'commons');
E% F) ?7 ^" Z $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
8 |) Z1 T8 l0 t $info = array(); 0 S0 j A8 b/ g+ I3 q
$debar_filed = array('catid','title','style','thumb','status','islink','description');
% ^( ?9 `5 K; O! ^: L3 R% y if(is_array($data)) { - \/ ]2 L" n! O- }6 Q4 q
foreach($data as $field=>$value) {
2 i% ^0 O8 q; }+ G/ }1 T if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
6 S0 t/ {6 _7 J" \" B $name = $this->fields[$field]['name']; Q/ {0 `* V8 j- i
$minlength = $this->fields[$field]['minlength']; . t0 F& t$ T; J ]+ _
$maxlength = $this->fields[$field]['maxlength'];
+ a" w, a5 K1 P& ^7 e& U $pattern = $this->fields[$field]['pattern'];
* ^# V( {( O) u2 Q1 U9 V& u $errortips = $this->fields[$field]['errortips']; : D, l3 A. `$ q6 Y( f2 ~
if(empty($errortips)) $errortips = "$name 不符合要求!"; ! Q, K6 N( T+ @9 I; i7 W/ i9 C
$length = empty($value) ? 0 : strlen($value); 3 k, X9 B9 T) w5 S
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
0 T) n* w( t# Q" j8 u5 O if($maxlength && $length > $maxlength && !$isimport) {
' {+ L% G* H6 R& y8 r' z showmessage("$name 不得超过 $maxlength 个字符!"); 4 h( v' y/ g* l( j
} else {
% n e: k: z. x, A+ t: T& M+ G% y str_cut($value, $maxlength);
7 D7 y/ K9 ^. ]) ]" G7 \( l# D }
6 h3 q& t5 f0 j if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
- y# I$ a3 q( R0 h if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); ( a) `1 r- f# O3 k3 {. l! O
$func = $this->fields[$field]['formtype'];
0 T! f/ V5 m+ `; j; c if(method_exists($this, $func)) $value = $this->$func($field, $value); . q5 [1 x: W3 e8 `/ h1 K n
$info[$field] = $value;
: b/ e3 _ R' V7 x } ( A# u$ ~* @0 A. S q0 H
}
5 b' S5 x: _+ U5 s return $info;
7 Z6 ?+ A3 A8 u } 8 V4 L( Z8 a; p- o5 B* u
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,8 b3 g) B& @9 q! M
* n6 N9 X* O, [& M4 }3 e6 i! h9 z
再到phpcms\modules\member\index.php 文件account_manage_info函数7 u8 m$ M: r# m; B: h! D
过了get()函数之后。
; y6 j) C$ F. B5 E/ ]+ d- \6 L* n6 p- A4 {# b8 s
5 B; _9 m0 ?/ J3 E; ~
$modelinfo = $member_input->get($_POST['info']);
i8 H8 W" e6 j ^; J. @9 I3 z- h $this->db->set_model($this->memberinfo['modelid']);
6 _: S" o$ v+ v5 ` $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ; N, I0 ?9 w/ O l
if(!empty($membermodelinfo)) {
6 P4 u8 S+ R% ^ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
% y; {1 B& q0 n7 X Q4 Q } else { $ d6 t5 {; H+ Q- W9 V% I
直接带入数据库,update函数我们跟进看看
# H9 U7 ^1 H6 m! H
+ G# _' y9 }: b
& Q6 Z W- v6 [. N" Lpublic function update($data, $table, $where = '') { + q4 y* ~4 i3 `& V8 f
if($table == '' or $where == '') {
. M. F2 c9 j6 N( x0 L return false; / L/ I5 w/ @8 A1 S$ q9 i4 c% H( I
} 6 z4 d7 Q4 {, O
$where = ' WHERE '.$where;
l6 n" ]7 ^" @9 I3 {3 c2 V $field = ''; % `, V3 F/ s/ Y
if(is_string($data) && $data != '') {
+ D" h4 ]% F' A0 @ $field = $data; 5 G5 m# O5 k" W- x4 o
} elseif (is_array($data) && count($data) > 0) { ; `8 R- E; n8 K- s
$fields = array();
9 j6 U1 o; o2 ]5 ^. H+ L* X foreach($data as $k=>$v) { : M0 E1 s- k4 S6 {
switch (substr($v, 0, 2)) { 8 ^- ]: Q% }% O9 l8 l
case '+=':
1 {" J; D; q+ S/ _6 K $v = substr($v,2); 2 S' O- |- b3 H- P/ t9 B
if (is_numeric($v)) {
" p, f. {* @( }; L $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 5 f7 t( f9 ~7 w
} else {
9 z9 L* b9 _. X. @& u9 X* N continue; ' r+ R7 ^0 z9 t4 M0 u
} + l/ B9 Q6 B; x
break; 2 `: a* }% ?* v" z7 @1 c+ a
case '-=': 2 i' f" e- `3 N
$v = substr($v,2);
6 L8 |* r, r( M P* h* U if (is_numeric($v)) {
+ |/ y4 ~+ ]+ I4 d2 h. S, A% l $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 6 v1 I( H% E& I4 u) Z; O, |
} else { ! K- u% S: o0 d. ?; A* V4 r: |
continue;
* u& x9 W" l1 C }
: `5 }$ k) M' h break; * \; T0 F6 Y; V1 `
default: : a3 j! ~# s" h) o
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 6 c v; W' p) z
} 4 f3 j( C3 ^ ? l( i4 \1 v# e# d
} 9 Z* Z/ @, g" b5 V
$field = implode(',', $fields);
, P8 Q, [0 m3 s. W$ s } else { % H! x! ?" d- c4 }1 ?7 Y
return false;
, r |4 f5 K1 ]& j( q6 O$ ` } 3 A: p# Q4 x# ?$ I. I; }, b
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
0 U2 E9 r! ?, D0 d; U0 j print_r($sql);
$ f# P4 w4 t- F" X return $this->execute($sql); " g* `* }6 N0 Q) x; H) l* V
} 0 h; r6 x; t `; b9 e, ]
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。5 H7 w" U4 }3 e+ E# w
0 N( f2 \% Z; N& V8 i, ~4 ~7 i攻击测试:& G% ]2 k3 I; `" \; x: H" p
测试地址http://localhost! ~6 _5 E- j/ E# _3 b( g6 U& ?
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句, O* ]' b- k/ Y" B/ l1 {" H0 G
: Q! J2 Z0 B: D% G; i/ u
* n3 }. A' |2 \1 {( `( F" `1 a- ` z: d5 J' n0 s6 {
4 O# \/ p3 l0 Q/ ?& r0 u9 [% I; X/ f/ o& E1 k: ~6 {
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|