|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告6 P! Y& P- c o# h2 W9 C* U, S) s2 P
漏洞作者:skysheep7 x" k+ L0 w e: A) P, e6 {
分析作者:Seay
/ y% ]6 y9 y; p* O博客:http://www.cnseay.com/& l9 e3 t% u( Z$ a- B' H+ ?
漏洞分析:
9 H h2 G3 F! \' W: y2 q 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。 Z# p) v9 c( z4 [3 } z) s
E6 ~4 ^! f5 s7 o; l
. R5 j% _. e$ j' r- } , F- O2 V6 @3 K; p. m3 ` j
public function account_manage_info() {
& L& W' i1 ?. u$ j if(isset($_POST['dosubmit'])) {
& h% E& \2 I" g1 y: v& a6 ` //更新用户昵称 * t/ y* q7 k6 z1 M+ p. V
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; " }. M5 [( }' U/ d
if($nickname) {
3 x& C, H4 f" c' r$ ^ $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); / w$ t W5 H& J0 b2 v5 X$ c
if(!isset($cookietime)) {
; F+ K0 w) u2 |* a5 \ $get_cookietime = param::get_cookie('cookietime'); - l8 k! T( b% C0 o0 q
} , X# k3 k! S' v. j
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); * @9 v! U2 d2 @6 v( v. j
$cookietime = $_cookietime ? TIME + $_cookietime : 0; % y U9 a. E7 `4 y" Y) {
param::set_cookie('_nickname', $nickname, $cookietime);
2 c _. i# n, d: V6 V9 e } 6 h7 f, b7 p4 k
require_once CACHE_MODEL_PATH.'member_input.class.php'; ! C3 D0 V: | W0 }
require_once CACHE_MODEL_PATH.'member_update.class.php';
! T) s; U# g9 p3 O6 c$ e3 Q $member_input = new member_input($this->memberinfo['modelid']); / m4 c6 [/ l7 N( {' s6 b) P; u
$modelinfo = $member_input->get($_POST['info']); " `, X' V$ t. ^3 Y9 y
$this->db->set_model($this->memberinfo['modelid']);
( S$ v' n8 K$ l. Z: G) d $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
" Z/ [% D" J$ o( f) d( A if(!empty($membermodelinfo)) {
3 F; r. k' b, a; J* U6 I& G $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); & @) X! e% Q2 D" @3 ?6 G7 ~8 K
} else { ; W5 g# J& Y) }' G" r
$modelinfo['userid'] = $this->memberinfo['userid']; 0 U; m$ f( C4 `) q
$this->db->insert($modelinfo); ( q7 B) v& H7 C9 D7 ^
}
% R+ a- \1 [% U3 l2 j& `5 k3 M2 C代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
; K+ Q8 \: q2 _$ p* j在\caches\caches_model\caches_data\ member_input.class.php 文件中:
$ w4 o b# D2 e7 ~/ J/ f) F& y: z3 U; u$ v1 q: {- m9 D+ F
. x5 d* |2 t6 d4 G8 n( z
3 U0 y6 A' `+ l! `: P+ Nfunction get($data) {
+ q; \1 L8 I- R! _4 D% G! Y1 W& Q; q $this->data = $data = trim_script($data);
2 {! Q: b6 O0 E x& I* n7 ]" P $model_cache = getcache('member_model', 'commons'); - ?+ H/ v" F/ e( }! [
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
+ P0 N- P+ z' P1 i: Y) W4 C $info = array();
, m( {5 M: t2 d/ x! l $debar_filed = array('catid','title','style','thumb','status','islink','description');
2 g$ K1 Y3 I% m w if(is_array($data)) { & i- l- h1 O- M( G9 Z- l
foreach($data as $field=>$value) { / Z) o) H" Z6 L; ^. U( N
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; * X/ T7 ?0 O+ g* `# Y& h# w3 y
$name = $this->fields[$field]['name']; 8 X: O1 g+ {; w ~2 j" S
$minlength = $this->fields[$field]['minlength']; 3 U/ t; h% ?+ G! D" J6 x5 Q
$maxlength = $this->fields[$field]['maxlength']; 6 F. b8 P( j" ~- c8 s2 }1 {
$pattern = $this->fields[$field]['pattern']; 6 G2 ~! V1 F; X8 X
$errortips = $this->fields[$field]['errortips'];
4 h+ r3 `5 ~4 F6 g$ C( ~ if(empty($errortips)) $errortips = "$name 不符合要求!";
+ i: b3 h% D4 k" q; ^3 o: J $length = empty($value) ? 0 : strlen($value); ! Y& J3 H+ K' \5 ]
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
" A u D8 O! u$ @4 i' R7 T* b! K if($maxlength && $length > $maxlength && !$isimport) { . p$ `1 e$ c& X1 H' k
showmessage("$name 不得超过 $maxlength 个字符!"); 9 S5 l- o2 ~' c9 h9 [8 o
} else {
' D1 _, B0 B5 _5 t1 b6 u str_cut($value, $maxlength);
/ U# Q, x% {2 a6 t% P( n& F7 p }
# E& `, n: r' A: W1 D2 c# Z if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
9 [& \; P# H* c: Q$ g( } if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
- z: p; j/ p7 d- O; I $func = $this->fields[$field]['formtype'];
( J9 l( D4 V6 V u3 z9 R# ? if(method_exists($this, $func)) $value = $this->$func($field, $value); " R: a$ S, j! c& K
$info[$field] = $value;
' X4 w$ O6 H0 { }
* A' B# I- O% H7 d7 y3 O } ( O6 v: f1 C& R2 t( Z! B+ D7 B
return $info;
) A6 x# y' P( s7 X4 } } ( D4 g- D3 M* b
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
' @) T/ L# `; s2 Z! m+ f4 g3 [3 B! Y9 L. K+ B$ n( L
再到phpcms\modules\member\index.php 文件account_manage_info函数
4 F9 A! f" p6 T9 y过了get()函数之后。
: W, G3 p. |2 N X' r
, B6 K5 `) Z V* k7 C1 ?
/ ]+ i* p# q8 ~% y$modelinfo = $member_input->get($_POST['info']); * F1 V5 k6 K- F/ |+ v
$this->db->set_model($this->memberinfo['modelid']);
N* E% ]' @3 r $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ' |$ i7 H: c7 v7 N u* H, S6 L
if(!empty($membermodelinfo)) {
2 ?6 M' D, H% J+ L; p8 Z( I# m $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
1 x5 v' b+ \$ n7 F* i } else {
( O9 ]( J% A. R0 ]7 c直接带入数据库,update函数我们跟进看看4 I. _# P% H, p2 B! h
9 y6 \- E, c. ?: k h 7 g6 `, a7 b3 H2 i* w. i
public function update($data, $table, $where = '') {
( R' J( w! a6 d6 H0 R if($table == '' or $where == '') { N3 p, f7 {' a1 p& s
return false; + e) D$ f2 |# R8 w9 v# o
}
# D# |& P2 a) T2 k1 b $where = ' WHERE '.$where; 4 s% H8 f3 ?& ]1 V
$field = '';
0 ]8 d# H5 i% b3 d. M if(is_string($data) && $data != '') { ( r3 R/ G$ O, N `* r- @
$field = $data; / U( x5 O B! o4 x
} elseif (is_array($data) && count($data) > 0) {
! I# F1 I) f T. ]8 O% f" t' u $fields = array();
3 L0 {' }0 }) C1 ]) S% D$ r foreach($data as $k=>$v) {
+ p4 f( q" s: \6 T; ] switch (substr($v, 0, 2)) { + `2 y. p8 ]8 U/ v4 B- `; w
case '+=':
& N. h' w. {+ F2 Q- q1 a' f $v = substr($v,2);
5 e6 e9 Q. q$ `) t; ~ if (is_numeric($v)) {
+ C* |! U" N& A/ G! Z8 N' R $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
9 V! P; x2 o) i& `7 z: r4 {+ d+ R } else {
" y0 A! s$ `, }3 ?, b" ~ continue; ( F! \) }. r% d" |
}
+ d" d4 y" o+ e0 z( H% E# {7 |0 y; i: E+ z break;
5 H5 I2 j, R \9 J4 `$ Y1 }3 t p case '-=':
9 Z7 H- z* V% o8 R' e $v = substr($v,2);
: t6 z6 d6 B i+ \8 I, P if (is_numeric($v)) { # I" Z* `0 f$ {5 S6 Q
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); - m/ [+ s* ]) {/ }: L
} else { # q6 S" F8 U* ?8 R6 V5 M
continue;
, [ u# w& X# M8 X } 0 M" Z g2 V. q% J5 e
break;
1 ^4 z- |1 j J/ o1 B/ Y) | default:
9 ~; Q. {- _2 j% B! y* G $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
9 v8 q- e z* j4 {4 n1 a }
& g* Q1 y5 Z, w* ^) ]# e' I! D } 9 I: n$ J% H! ?
$field = implode(',', $fields); # J2 P* V x) W
} else { : n2 U9 ^1 S2 d; i2 z0 A" n
return false; + n4 b. D3 c8 O( B2 l$ V9 x
}
6 g& n. @. o( c- t1 w $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; ) p; F# n% g3 D
print_r($sql);
$ y: G' M5 y! y2 x5 ` q8 c return $this->execute($sql);
# M ] H2 M. R. h1 a. D } 5 V5 @9 ^& S. R* O
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
- Q) d5 @! L7 I, B; ~, [: }2 d( t
u1 S$ }# P3 y# G& |" E攻击测试:
G+ q7 Z; l; c" E1 Z( P/ f测试地址http://localhost7 C; E- ?# J7 A% V% t0 c4 R4 t) X
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
' G5 a- g" K8 B1 o7 a9 L) o
' a# `% M8 b" k3 m7 D+ q# ` ( C1 I7 G" S! F: W
! `! h( }3 }# b& y* i* |% Y/ L0 N% a3 {8 R* q# @/ g
_ H' F$ Q+ D$ Q
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|