|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告1 P6 P" ^/ z0 d6 J+ f
漏洞作者:skysheep
1 {1 I$ z7 u: V# I# {6 w+ w分析作者:Seay
/ }' U' X2 n) }( Z. X博客:http://www.cnseay.com/# Z1 v* ]4 j6 j/ H) p
漏洞分析:
1 J6 I; Z; `& F3 U 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
& y) v( {, m$ A3 Z6 a
# t4 v2 g6 b+ M7 r9 u1 @
, K* m# ^* U3 l+ T; A& D- z. x' [
) Z: w& {; ]# i! ^$ d2 ppublic function account_manage_info() { 7 T" v3 d: k5 f( \# k o
if(isset($_POST['dosubmit'])) { 2 k8 l- ]. w+ D8 I
//更新用户昵称
r* p; ]' v' `! u3 n6 B $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
9 }2 |2 g, n% H$ y if($nickname) { 7 f6 }5 W3 J' P+ U7 Y
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
: g, j5 T# Z$ K1 w if(!isset($cookietime)) { & Q3 R# E' h" ~/ o9 d& F
$get_cookietime = param::get_cookie('cookietime'); / x6 Q4 s0 u1 K/ o3 v; P
} 3 e2 f% O. T/ y8 L: y* |
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); + n$ m, Q n! F! d
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
) s; l- H* V% Z* J0 N param::set_cookie('_nickname', $nickname, $cookietime);
, Q1 n9 ~0 X5 T- T# Y" y6 `; u% C6 u } : C1 q5 Q5 f {/ F7 t
require_once CACHE_MODEL_PATH.'member_input.class.php';
+ W8 V: R/ E0 d1 V require_once CACHE_MODEL_PATH.'member_update.class.php'; 4 |; f" _2 ]$ k) h( ^( M1 D+ l
$member_input = new member_input($this->memberinfo['modelid']);
8 E/ C0 g$ v- ~# g: G2 L% i: C $modelinfo = $member_input->get($_POST['info']);
# r( |8 c4 w# \+ y4 Y* X+ x1 T: F $this->db->set_model($this->memberinfo['modelid']); 0 w: G) L' w% o4 ^, G# r+ J$ S
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 5 O% G D# C/ {7 d
if(!empty($membermodelinfo)) {
! Q: @9 H7 o. z9 k $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 2 r5 c. Y7 J6 G+ |9 u
} else { & a1 P$ p; u. W+ h q/ G% V
$modelinfo['userid'] = $this->memberinfo['userid'];
^/ S- Q I/ N $this->db->insert($modelinfo);
P4 K, ~. ]5 K: Z } % m6 O/ K# j; T; }( J3 S
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,( y8 q8 r$ R/ X
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
! N% Y1 e) Z' c. s7 [# P0 m# ~
7 h, y4 @- ]7 G0 u/ n$ Q3 c _. P( A9 H
9 s2 x+ {" x0 V' V/ G1 X& T# rfunction get($data) { 4 y, H! V& c, u; E( d! [
$this->data = $data = trim_script($data); 8 k h; C2 m: r _+ h1 Y
$model_cache = getcache('member_model', 'commons');
+ n4 m" T7 z2 r/ Q9 o4 p1 d# f $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; ( ] l) I9 T8 F8 J. _" ~% v( U
$info = array(); 4 A$ ^. @7 P! N3 i
$debar_filed = array('catid','title','style','thumb','status','islink','description');
+ R, c3 h$ I, w1 V8 J if(is_array($data)) {
0 _9 g& i9 ~( x foreach($data as $field=>$value) { z6 Z8 t& `' K7 D
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
( d6 M$ R4 H( i' _/ ]- d $name = $this->fields[$field]['name'];
4 ^% n% j. B3 {( z$ N $minlength = $this->fields[$field]['minlength'];
* M1 d/ x# m3 p; u8 _7 P# m $maxlength = $this->fields[$field]['maxlength']; 5 S+ p: h. @+ m+ ~+ L; C' u; I
$pattern = $this->fields[$field]['pattern']; ; A) I' v4 W. u% e6 M7 ^
$errortips = $this->fields[$field]['errortips'];
a& n8 v# {5 B' t" o if(empty($errortips)) $errortips = "$name 不符合要求!";
& B6 v3 ^" e6 H; Z! o- J ] $length = empty($value) ? 0 : strlen($value); & i& J2 U. k, ?8 u
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
& Q9 X! I: \# P# H3 C$ \$ z if($maxlength && $length > $maxlength && !$isimport) { 8 Y$ d; k) z+ ~( i) F" I
showmessage("$name 不得超过 $maxlength 个字符!"); ( I# O# X1 W W) S- A/ n
} else { 1 X/ y2 U, Z5 G0 P
str_cut($value, $maxlength); u; B3 D( N7 u; [
}
9 W# G6 X" t2 k U5 I+ f if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 4 O8 m' L9 A- O; h. |+ \! l. Y4 Q
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
0 |- `7 ?% _& G6 u B $func = $this->fields[$field]['formtype']; : ]: `5 S1 r# O! P9 \2 s
if(method_exists($this, $func)) $value = $this->$func($field, $value);
/ j! h* A" u% y, r! t6 ?: X $info[$field] = $value;
C, U: U0 c q/ c U4 T) \; g7 p }
2 i* R/ k1 U/ x! ` }
+ I/ q: a+ L5 W6 h% S7 [ return $info; 2 p9 c3 r' W3 J3 s+ U# W3 G L
}
2 s* e1 X6 l+ Y' ~/ e- N, g6 etrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,+ F2 m. ` T7 K5 q1 E! o; X
( ]0 @. X2 ?2 @3 q+ Z& h: f! I
再到phpcms\modules\member\index.php 文件account_manage_info函数
" R: M; }" @" ]1 Q4 S$ x过了get()函数之后。! ~8 C) J- @' t- b* I4 I2 E
' u& I, `2 N D# [( g9 i
) j6 X- B& \/ [( T- a1 s$modelinfo = $member_input->get($_POST['info']);
$ q$ o* P9 c0 [: [ O8 \ $this->db->set_model($this->memberinfo['modelid']); 5 F: r3 `% ^: h
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
4 Z/ V) j, o: H$ r( Y. b+ I if(!empty($membermodelinfo)) { 4 x, [1 q+ s0 g6 I# H+ t& I( ~* B
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ( k r6 v) V5 X5 t; C L4 F
} else {
w- k* o9 t5 b3 ^( W v6 H) u& a% K直接带入数据库,update函数我们跟进看看
. V! W+ W6 o4 }0 r- Q
1 C$ H9 W0 ?( e
: [6 n" R1 j1 \7 P0 r/ c" e! [public function update($data, $table, $where = '') { ; r3 y3 e. Q y( F
if($table == '' or $where == '') { ( f! y) o5 g3 V) E: K( u) _
return false;
$ b" L9 P! l1 x }
# z: L' l9 }6 q% K' R$ U4 t $where = ' WHERE '.$where;
, \4 l& o9 J0 w; J8 }2 |0 l7 x $field = '';
$ v3 _' A2 |/ I' |3 V4 h if(is_string($data) && $data != '') { 9 |3 U1 ?& Z) x
$field = $data;
& f, K/ Q, [" |% a. z; x- ] } elseif (is_array($data) && count($data) > 0) {
$ U6 D' Y! N8 _# R Y2 L3 g $fields = array();
( M! o( x' H3 [4 U, E' u" s+ Y foreach($data as $k=>$v) {
" Y; R! A) v6 z0 }5 Z switch (substr($v, 0, 2)) {
( d J! J+ j& w& J) G! u4 | case '+=': 7 ?( H/ t( ^0 O3 [! K9 H
$v = substr($v,2); ; f( @" ?" p# _! P9 m
if (is_numeric($v)) { : T! u. I, ?. J; Y) I# [
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
/ b1 m( F y! ?5 W: N- o! o } else {
" F s: f' \0 z2 E continue;
/ R2 a; D& U- S" c0 t } * p3 A& [$ }1 q* J2 g- h' `; K9 m
break;
7 h& n3 _# J( V case '-=':
( [+ k9 m5 _4 h# P1 c( X $v = substr($v,2); ) ?0 U7 U0 ]! n; O- J5 J$ q
if (is_numeric($v)) {
9 ~6 `, Z. a7 u2 u' A $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); ' q: J5 ~/ q) [& U' n% ] `
} else {
& A, j2 X0 G2 T: |* p continue; . U7 i. E" v7 I$ b/ y5 a. W
}
& d/ I- s7 v: P0 |7 a+ j' h! ^7 a break;
" T1 t9 W$ O$ C9 f0 d default:
* t t" S0 z( Q! Q $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 0 e4 C* a; G6 T. G. ^! K; f
} & A9 W g# ]3 Q; _+ k
} / W7 K1 }3 Y: f' j) T6 Q* d9 _
$field = implode(',', $fields);
( Y$ D: O' \; W6 Q* k$ m3 h } else { ; g$ v$ U# C8 [. Q$ \
return false;
( @7 e1 `: Z! N' c( d( T }
4 Y4 |2 v+ x( X7 q. E4 s $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
9 ~$ Q9 g$ o5 X! Q$ ~! n print_r($sql); * u+ t' H |$ d c
return $this->execute($sql); 5 f& h" s& x+ O; F" ^8 I
} : K. w7 E" [; ]5 [8 M6 v
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。5 Y% c& R. m# h, v& d( ?
' Q' |: Q* A; y' h
攻击测试:( D+ b# T! c/ h" K! L0 r
测试地址http://localhost
+ p, K# u1 R; ~$ a) z0 i# B! Z 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句' m" X, C% x4 k- M$ y
0 j# a) G5 h7 G X
0 d2 S( J6 l. Y- q1 ?
) J; i! v# b4 ]* ~
) q6 i4 m9 L9 N+ ~& T9 }# V
2 ^, I7 X; R4 k) E# O% r, Y5 _ |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|