|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告% y/ g f9 |5 d! `
漏洞作者:skysheep
' [1 n* u5 J. m2 Q, c4 f( `分析作者:Seay
, i/ l$ d# I. W/ }! w9 F博客:http://www.cnseay.com/$ U I7 R. f( p, E
漏洞分析:5 X; b' [. E- a5 {1 |+ c
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。0 m3 l1 B) r/ n/ b6 l
# \0 W& R/ p) l. o8 F+ }
6 F: {: [0 P6 l& W9 D( D; s
# G4 p- R+ o! l& o! R0 I2 Jpublic function account_manage_info() {
% V7 r* n% p0 [) N7 P; q if(isset($_POST['dosubmit'])) { v7 ~+ }6 D5 I6 ~* ~: ]
//更新用户昵称
1 b( N, M. \: [8 U; n* `; V# ` $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
5 G0 M4 j% G2 m) @9 T0 c if($nickname) { # I7 D9 H( L) l/ i4 L
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
T% m2 i) @; Q0 k& Y5 D4 S# j if(!isset($cookietime)) {
, g, c4 a6 q! W5 H; c $get_cookietime = param::get_cookie('cookietime'); 7 p! K' C8 L; G
} [! H' \# e {& m- \' C
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 3 ~* F5 ?, X8 {9 S6 ^
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
w9 H% q5 e- v, k param::set_cookie('_nickname', $nickname, $cookietime); & j/ \0 C% L5 ]0 q
}
0 D/ Z3 U. ^% ~2 s& K require_once CACHE_MODEL_PATH.'member_input.class.php'; % a; U" L; M0 v2 h& \
require_once CACHE_MODEL_PATH.'member_update.class.php';
) l* y3 R; @; y( U $member_input = new member_input($this->memberinfo['modelid']); / d' @" ^ o3 c `
$modelinfo = $member_input->get($_POST['info']);
- c' Y) x5 M( n: ~ $this->db->set_model($this->memberinfo['modelid']); & `$ T/ N' w9 G: f6 A3 H3 v) Y
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); F7 ]" ]: I9 @0 E. n3 c
if(!empty($membermodelinfo)) { # h7 O6 S1 ?6 `3 U+ J* P. E
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
9 [- V4 U) c5 z `9 r% p* n' S* ] } else { 0 ?8 L7 M* y* q; N+ y4 S
$modelinfo['userid'] = $this->memberinfo['userid']; + ]; O% l; I$ i8 ~7 T& V6 Q
$this->db->insert($modelinfo); 0 N3 P$ H* [/ C5 V0 g* N) ]( g/ o
}
. D( Z9 B' b- D. W代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
9 Z6 |; ]) Q) ]# @在\caches\caches_model\caches_data\ member_input.class.php 文件中:* M+ t# f$ `' g1 s
I. C0 m3 e( @7 c2 S
4 L6 y" d/ i* W0 p( e) R" h
+ H1 M# h- M# Q' ?
function get($data) { , ~) q* g% U5 n2 R2 v' `: J d
$this->data = $data = trim_script($data); ) {, N- H$ ?, J+ ?
$model_cache = getcache('member_model', 'commons');
t' p" Y, m6 G+ P( @ $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
0 h: P6 O2 i3 d i9 u $info = array(); . Y* ^ P/ m# H
$debar_filed = array('catid','title','style','thumb','status','islink','description');
! {. s7 U9 m3 ^ if(is_array($data)) {
s. {, y* J1 v* T% h/ Z foreach($data as $field=>$value) {
3 x; q& E. L# j: F2 | E if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
# I1 @/ k k1 {7 ` $name = $this->fields[$field]['name'];
' C5 a& }( I( A- P/ `, U2 X0 z $minlength = $this->fields[$field]['minlength'];
+ x: X7 _6 o, j# h O) ] $maxlength = $this->fields[$field]['maxlength'];
3 a' [1 j+ \4 k0 L W $pattern = $this->fields[$field]['pattern']; 7 {1 D9 E( ]9 i4 u7 h! \/ Z
$errortips = $this->fields[$field]['errortips']; 8 M G8 T( W1 f k* w9 F
if(empty($errortips)) $errortips = "$name 不符合要求!";
; _& ~) H8 @0 c: \ $length = empty($value) ? 0 : strlen($value);
: X' u* L; n! |# i6 g' k, Y: o) ? if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ( n' s5 S) ]4 b
if($maxlength && $length > $maxlength && !$isimport) {
& \; p/ R L' z& a, u4 D showmessage("$name 不得超过 $maxlength 个字符!");
. U9 @9 ~4 |- {' O5 Y! T0 i9 Z4 ?+ o } else { 4 z+ x! c( t8 n$ u+ S7 ^$ \7 B _
str_cut($value, $maxlength); 5 Z- g N9 K6 p
}
$ D# c2 D* T. Q G; B if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
* d; S7 m2 t' W! }" C1 h+ h if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); * X+ G0 m' z( w
$func = $this->fields[$field]['formtype'];
& E" L$ b8 u% o! R' H4 c if(method_exists($this, $func)) $value = $this->$func($field, $value); 3 r5 a( u" k+ r* F
$info[$field] = $value;
, [& u1 x& I9 e6 i+ i } 6 m' |7 b; k6 s9 i$ ` [( ~* ?
} 6 T2 K" M6 ~- O; p* p
return $info; - H/ m' }# X8 h6 Q# e3 ]# g! Y
} % o) q! r0 b7 B5 Y- W6 _) R5 z; _" G
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,& f) f& a( R0 U: }+ q- G6 \
3 J% P1 O0 G0 x X$ ?
再到phpcms\modules\member\index.php 文件account_manage_info函数4 A" \' P0 d8 V
过了get()函数之后。
3 Q/ I7 J8 B5 {) a
; C/ `) b; K3 I6 U$ U* C6 q
. A; H6 f% d' [$modelinfo = $member_input->get($_POST['info']);
2 } H- B% Y/ J0 m' H1 |& n2 C* M $this->db->set_model($this->memberinfo['modelid']);
/ N. g3 h& C9 j2 N $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
1 u U1 `! ` h( J$ |2 e if(!empty($membermodelinfo)) { . Z" ]9 Y9 n" H
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 4 }6 P S7 G4 K2 c- g. F/ V. Y; Y
} else { 5 f; t5 Q6 r: t
直接带入数据库,update函数我们跟进看看1 X+ X" I. [6 e" q
4 q+ V& w. f! U) k" j. h+ u+ k
- W. N6 b% n2 |' Z6 x9 @8 X1 [/ v" w6 a
public function update($data, $table, $where = '') {
3 a: D9 B' U. w/ K4 j4 o( e3 S M if($table == '' or $where == '') {
9 i' ~9 H* M( ]0 A5 `- j; y return false; 0 @) x* J$ J, F4 s
} , `, n: j! t& J) ~
$where = ' WHERE '.$where;
- h+ [" b" x: f0 N% M' M $field = '';
6 w. p6 @# W' u6 P if(is_string($data) && $data != '') {
7 k2 t/ L$ ~+ m4 X' B/ s $field = $data; Q5 P' q9 F2 g3 }
} elseif (is_array($data) && count($data) > 0) { ; t! n7 r. w1 r k6 @
$fields = array(); ' H$ ^+ X4 L0 r$ T. u6 r7 x x
foreach($data as $k=>$v) { - \7 t5 l4 ]$ ~+ C
switch (substr($v, 0, 2)) {
" i( m& t' o1 D. F' o. l# | case '+=':
3 X% K. B J+ Q$ t m/ O! v; O8 o8 S $v = substr($v,2); $ N: _, @% B6 V' k2 Z `
if (is_numeric($v)) {
9 X! j) r4 t3 V $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); ; T) B9 f& b/ A0 |
} else {
. ]9 l- T8 [& k+ V( A) W continue;
# l. t* s+ O5 K# Y } 2 B' ]0 Y3 i- z- \6 ?# e6 ]* `
break; $ H# L2 [5 k# V$ Z4 _* ]5 w3 s
case '-=': 1 _7 B; p4 z, t- L4 D$ I! s& W' L7 y
$v = substr($v,2); 0 J- N/ G2 n% t& z/ W
if (is_numeric($v)) { * p4 [6 S! P1 u; F. O5 w. J& B- P& Z
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 6 f& `6 r! V# [$ [0 M D% I1 P
} else { 1 }0 M4 i3 o, @2 m. i
continue;
2 W1 Q& @( G: `* M1 N l+ s }
1 R* v [4 X3 w; b break;
/ [. y+ h2 D c default:
/ [$ F0 b/ Z% o E9 x' o, b' @ $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
2 h# ~% v6 Z8 d- N } ) h; j. j% s' q9 Z" n
}
! O# O& b0 O- u3 V" H5 W $field = implode(',', $fields);
0 X- r0 x; W8 C2 X0 T: d$ L: ] } else { ) a# E% G- {. `, c0 |
return false;
' X# m7 n, N% O8 k }
. C N- ~. y+ l9 F $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; ( Q: o% M" q& W# ?9 @( Q1 b. H6 x
print_r($sql);
2 ?: l; W1 o2 b; m1 q9 a return $this->execute($sql); 5 U# m! c4 a# g0 x* p
} ( K) q, j: `6 V4 |4 b- ~0 d
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。$ j6 n P( z" i
4 l# i3 r0 b) H
攻击测试:
* B) i7 p" x; v/ A% o7 S1 U- l测试地址http://localhost
+ Y1 g# J. |) N/ [# | 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句3 r* k8 Q! ~# [0 q! J7 l
* h& e0 D; ?( J
/ S/ d& ~3 [2 m. j7 P
2 q+ b0 `4 m0 [* P$ |8 K1 o3 L+ H6 t
$ z9 x2 e3 X& d- R
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|