|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
8 \4 l8 J7 h2 }6 c g漏洞作者:skysheep
$ R. m+ c8 x# f2 a& o4 d分析作者:Seay D2 @6 Z9 \# M7 x% p8 ^% R
博客:http://www.cnseay.com/
1 `2 _+ W* g O7 `漏洞分析:# y8 y/ Q8 f- _
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
# j! X; E. Z, U9 `' j" G' g0 y( i6 T2 Z+ g2 u
; Z9 v! M! ]! G' u$ J
: P0 Q* I( V6 I) Tpublic function account_manage_info() { : T8 ]4 m0 n6 [- \7 u* d6 S* W& J
if(isset($_POST['dosubmit'])) { " I4 F- ?3 [& U! K+ Q2 n) j4 P
//更新用户昵称 6 O- U% A" z' A2 y
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 7 |7 S; \/ M+ |* o) s' `
if($nickname) { % S* e1 |6 b5 |3 R: N
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ; D1 O( \$ D9 d! K& ~1 m
if(!isset($cookietime)) { 4 J4 z4 i0 h( @, {# ~& j
$get_cookietime = param::get_cookie('cookietime'); 3 ~% W3 o! d# D( M a; T/ b
}
D6 [) q1 g0 H6 c& W3 b $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
7 G5 P+ U ^! |4 K2 S% h4 U $cookietime = $_cookietime ? TIME + $_cookietime : 0;
6 w7 q3 n( ~& } param::set_cookie('_nickname', $nickname, $cookietime); 7 n5 s; f; F% r+ ]
} 5 ^5 d& g# ^# o/ {8 N- n7 S" B
require_once CACHE_MODEL_PATH.'member_input.class.php';
- \- W$ A; W0 [5 A# c! F require_once CACHE_MODEL_PATH.'member_update.class.php'; 4 Q |9 R- s4 g4 P9 A
$member_input = new member_input($this->memberinfo['modelid']); # r0 N6 C; W: A* \( A
$modelinfo = $member_input->get($_POST['info']); 8 E# r2 Z7 H J0 b
$this->db->set_model($this->memberinfo['modelid']); $ [, [+ c% C9 v/ \6 Q* d' |4 N
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
+ L. L9 |+ Z) i if(!empty($membermodelinfo)) { % L* }' K5 f$ U8 q
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ' L' a% |1 y% M7 }3 G( T! C- O
} else { * {" D _) V! {' l" ]% v6 ?: X) h
$modelinfo['userid'] = $this->memberinfo['userid'];
& O0 o, ]. S# j& p1 O* |# K, @: i8 Y $this->db->insert($modelinfo); + m# y- ^, L2 G& i- C' L$ x; Q
}
! i! M& k- ^% h4 T8 U代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,( y7 o7 S* e; u0 W
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
# @2 {( q0 i' U$ Y
# `( h" T" A; B4 H" ?
; l5 E" z, ? D
; e" o1 e3 w0 S3 cfunction get($data) {
' d# {1 y" [% ~ $this->data = $data = trim_script($data); . z+ H8 @. k5 E
$model_cache = getcache('member_model', 'commons');
% q3 `5 H& K- Z- o' g* m $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 5 }4 u/ p$ `# e0 j- W( ?. W
$info = array(); 6 C8 x. k) O, u9 y3 P; l* @3 B
$debar_filed = array('catid','title','style','thumb','status','islink','description'); 4 j' L8 c: X3 Z" r& ~! p
if(is_array($data)) {
, ]' Y- s; f" f foreach($data as $field=>$value) {
1 N6 d3 S; W* a+ S2 R if($data['islink']==1 && !in_array($field,$debar_filed)) continue; ; L! ~) p2 T/ @3 [& g3 H0 E. ]
$name = $this->fields[$field]['name'];
( ~7 ~% X# [ f" A1 J $minlength = $this->fields[$field]['minlength'];
6 V% v. x8 j2 I* s% J; X& F4 p $maxlength = $this->fields[$field]['maxlength'];
5 @6 W5 |5 t7 w9 a% ` $pattern = $this->fields[$field]['pattern']; + Y/ T. j- t: M# Z7 ~+ D9 d+ K
$errortips = $this->fields[$field]['errortips'];
- ~" B' O; z& ~( ^; [) z if(empty($errortips)) $errortips = "$name 不符合要求!";
8 D' Z. J" z! b" s- [ $length = empty($value) ? 0 : strlen($value); ( T {6 `7 }/ i* u
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); # H3 C2 l' Q# o+ d( Z
if($maxlength && $length > $maxlength && !$isimport) { - E F* e$ m/ T) N: [' @
showmessage("$name 不得超过 $maxlength 个字符!");
$ c8 y% E: }8 p# A) D1 ` } else {
" I, D7 l8 o5 ^) f str_cut($value, $maxlength); 2 y5 b8 g! e8 D2 |5 E
} 2 e1 p" x7 f( `7 e' I( C& k5 m
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
( a9 }# q4 G5 Y% K; ^ if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
( y4 }& d( S+ t# O $func = $this->fields[$field]['formtype']; 8 c' v( p: @/ x' h x' i% X) q
if(method_exists($this, $func)) $value = $this->$func($field, $value);
- `9 O0 n% [, r$ c $info[$field] = $value; / w/ G( u8 n7 B) U8 q0 w0 \
}
! [& h. u3 G9 T9 h3 R } , {) E. u5 c- _6 g( i
return $info; ( o" M- K1 q* A( B" @( C
} " O6 I( m" B- V2 v/ W7 E
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,- Q# K+ v' I& ]% P& s% X: A
3 {! f0 f1 W" c. N% \ S& M
再到phpcms\modules\member\index.php 文件account_manage_info函数' ?9 d& Z3 l5 J2 F4 n% H
过了get()函数之后。
/ t7 m! e( J/ o: H/ G7 ?: `
6 g; W7 F7 z. v: h# _
+ M1 M; `, |# ^$ n$modelinfo = $member_input->get($_POST['info']);
& Q# G6 w: N% D) D0 @ $this->db->set_model($this->memberinfo['modelid']);
. F9 G4 p' C s $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
1 G% Y1 y, _: r if(!empty($membermodelinfo)) { , t' P: M4 f, C
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 8 [7 R) m! r5 I* i( J" k
} else { - c! n |: l' E, K
直接带入数据库,update函数我们跟进看看
/ z0 Y2 N4 e4 q* |, {+ D5 v$ e+ b$ Q* P: O# z$ @
& L( x: U; P5 T2 V# C, {public function update($data, $table, $where = '') { # W: D1 `* g$ s4 T! z( X9 j
if($table == '' or $where == '') { & x$ f: ]2 h6 D9 ^4 @7 g
return false; % w5 E( A3 Y) b# m8 B
} - @* M J; B: \: r8 [$ d% x7 j0 O
$where = ' WHERE '.$where;
! H- X* L% y2 G. j( _% V1 } $field = ''; 9 z8 S' I. i. x/ E2 B0 B2 ~- H
if(is_string($data) && $data != '') {
' q' a: W Y% y$ e2 e $field = $data; 3 y' r8 h/ ?; N0 P! ]* v; e$ f- r
} elseif (is_array($data) && count($data) > 0) {
; J0 f, d+ Z8 u( c4 ]- l $fields = array(); / B9 H3 ` \4 O
foreach($data as $k=>$v) { # R# p* J: K0 T- n2 W; a
switch (substr($v, 0, 2)) { ( h$ x) c5 K+ ]/ `
case '+=':
7 i# o( U! O/ [ $v = substr($v,2); 5 Y: P0 P' x( e) x: |
if (is_numeric($v)) { ( [1 f( A/ L* \4 e
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
0 F' g! Q8 y$ o# G7 Y# k, S1 j4 J } else {
4 ^$ e6 ] [0 w continue;
5 g0 x& O; D; C7 R; s4 E2 o }
: [, X& ~, r c" l, y break; , N% M$ Z& z" ]! q. O
case '-=': h& z1 g5 \7 B5 p( ~
$v = substr($v,2); ) m! m" K% k0 l# l0 r/ Y! \
if (is_numeric($v)) { 3 p) D# y5 j( D
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); % m; r( j9 g8 e8 @
} else { $ H0 @, S9 `* w6 r6 Y- I0 p3 Q
continue;
" \ D+ [! q( r+ `9 Q; [ } 8 l- c- t2 j* a2 ]. ?# R. @
break;
9 ]+ {2 L% Q& W) V) b0 P1 ?& W default: 6 o3 ^) k' j2 v, g# [, \4 ]7 c/ j
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); D8 x0 H o v
} 5 z$ i9 q1 ?) C# p6 s9 Z
} 5 Q& h* _* G* W
$field = implode(',', $fields); 1 V8 m) K% G& j1 o5 k
} else {
, r5 [8 m4 E2 w$ A4 t7 ]2 V return false; & z. k5 m. X- E# b2 N& Q
} 2 Q' Z' s7 V0 K
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; - y4 n8 U% X) @+ y8 t' @% J# l
print_r($sql); , m- Z, s% X8 x) y# h3 c0 d. s
return $this->execute($sql); 7 Y( o1 h, d, }- e, B: k7 v
} + f: }1 w: a1 S" [$ Y( X
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
7 ^( ^4 p: O. e: {+ @; l! } g4 c8 m( E# k5 h, s' ?
攻击测试:
; J( \9 A* \$ q- _- d测试地址http://localhost2 T8 n. B/ k$ o% k$ n- e
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句# V9 N3 ?3 A; e9 t8 A
( a0 ] o; \3 i1 q( b' v
8 F8 n0 ~2 M, h' [# m7 j# ^+ b/ J ` O" R
) r0 k4 H6 D- A8 s2 L1 S! B- m
+ q2 f! A+ g5 k; p
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|