|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
& V3 n* O* m* X漏洞作者:skysheep
$ y6 g5 M! p2 H4 d" L6 ~+ U分析作者:Seay
1 B; g- Z: Z. p) F" p2 w! v4 J. w博客:http://www.cnseay.com/
7 f/ I, e) A0 w% L: Z1 o6 N6 q漏洞分析:
6 p& ^4 D" o6 a8 b/ R( q3 o 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
7 r" B: j/ @6 h/ d! E. {/ r. W2 \2 f9 Y0 ^) b: N3 u
8 ]! Z" O; u% v! P0 e
: D1 u; J" {+ Z. N
public function account_manage_info() { 4 @" ^; A4 S. E% t: ~0 ^
if(isset($_POST['dosubmit'])) { 0 w+ I6 T! C8 t8 j
//更新用户昵称
" J! G- s. \/ D }- @ $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
/ P7 O# L; d. Y% p if($nickname) {
' H4 y" j. ~# b $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
, i* L, G/ n1 h5 t7 I if(!isset($cookietime)) { ! p1 f' `, E* V$ `. F7 A3 d& c
$get_cookietime = param::get_cookie('cookietime');
' ?( w, G8 C0 R& @; P) Q9 v; X } z0 k9 O! a# O! P( B( m
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
% V. g6 s$ d9 \6 r0 C0 j0 @ $cookietime = $_cookietime ? TIME + $_cookietime : 0; 5 v' \- e6 O, n
param::set_cookie('_nickname', $nickname, $cookietime);
0 [+ Q, k) j3 n5 t }
9 D @( c& r2 i) f1 y% e# l require_once CACHE_MODEL_PATH.'member_input.class.php'; 2 A; K$ i! i+ _3 @; k5 O, C" c
require_once CACHE_MODEL_PATH.'member_update.class.php';
! R, Z. t6 A, F9 W3 T( K3 s $member_input = new member_input($this->memberinfo['modelid']); ) j( a( W* x& [ l) i7 b+ ^
$modelinfo = $member_input->get($_POST['info']);
9 u- `$ U( x+ E8 O% L; ~ $this->db->set_model($this->memberinfo['modelid']); + P: S7 I+ t/ S( Y; j
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
7 b$ P5 F1 z. E' s/ J if(!empty($membermodelinfo)) { # s! \3 l Y3 i' f( ^* G
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 8 M- _$ e3 n7 g! N+ m1 m) L
} else {
. z' v: e. F- ?2 u% u; ~+ O$ U $modelinfo['userid'] = $this->memberinfo['userid'];
( T1 O, B5 X) o. w5 V( S/ w $this->db->insert($modelinfo);
@& o- ^; ^# v# E: x }
4 ]: w" x. g8 t代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,/ _: X# k5 W( E
在\caches\caches_model\caches_data\ member_input.class.php 文件中:5 x$ r# ]% k5 a7 y8 L4 a9 s
A9 c _2 d; U
: u; q$ @3 O) i, ?0 j
9 ?! q$ j8 k! ?2 N. @
function get($data) {
5 U) M ?' |$ a. q% L: Q! H $this->data = $data = trim_script($data);
0 s* D ?/ r2 G. T4 y4 T $model_cache = getcache('member_model', 'commons'); : ~2 X9 }5 f- }6 k a$ q: {
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; : [ U- d" r- N6 i: _
$info = array(); ! v) r- [2 k0 z
$debar_filed = array('catid','title','style','thumb','status','islink','description'); 7 Z2 y: r2 W' _ }0 s. A" [
if(is_array($data)) {
# J1 P6 }0 Q: q. v foreach($data as $field=>$value) { 9 M: X/ S. S6 E# l$ g' [0 v% ` z
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; . f8 v( q6 F# t6 t. l7 m
$name = $this->fields[$field]['name']; % a* d8 f6 r+ s* j; \; n+ j
$minlength = $this->fields[$field]['minlength'];
. o$ h) G# [; m $maxlength = $this->fields[$field]['maxlength'];
5 z: d i. v) ?6 `9 ~. M; c $pattern = $this->fields[$field]['pattern'];
8 I5 E" S1 d7 N6 J $errortips = $this->fields[$field]['errortips']; ; I4 x9 p; h7 f6 X2 M* _
if(empty($errortips)) $errortips = "$name 不符合要求!"; * H* ^# A7 X. f( q$ Z$ Y
$length = empty($value) ? 0 : strlen($value);
& R( Q' W/ l$ y* j. P if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
D! ^5 N7 n6 x% Z) K' t if($maxlength && $length > $maxlength && !$isimport) {
) D6 G& U7 ]: A. C$ |6 S7 d- u showmessage("$name 不得超过 $maxlength 个字符!"); ! A/ M5 Z5 T, J0 P) W+ L
} else {
6 s, E6 c9 O2 q( |9 g0 S" k str_cut($value, $maxlength);
% Q+ S! R5 k; O2 B } + p! Y, \: n7 Z! `6 }( \$ ?
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
; k+ d* N8 n- k/ \1 l if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); " L3 L1 F% R w* x
$func = $this->fields[$field]['formtype'];
' t, p2 v6 q+ I if(method_exists($this, $func)) $value = $this->$func($field, $value); 6 ^ A" E; v! \. h
$info[$field] = $value; 4 A1 _- U3 V; y8 t
} ) G/ u& M5 Q! ]: Z, h' Q# K5 s& H
} ' ~9 B* `% ]4 q8 k# |
return $info; ( \9 g! X) o4 A- H: R5 s* c& ]
}
: _; X5 |. D( W, Z, \; t& z d- m! O! \trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
: Z: b. R5 |+ {) r l' m; |: b
) @/ q$ f g4 k再到phpcms\modules\member\index.php 文件account_manage_info函数
+ S9 ~- X! P4 T$ _3 ~! n D4 u过了get()函数之后。3 a. {* P8 V# ~" m' B- @
- f6 h6 A( c- ?& J& x8 ]
4 U( R7 u$ M1 T+ p2 e1 p5 X Y# r$modelinfo = $member_input->get($_POST['info']);
/ V+ j/ x f8 V. }7 E: I- I* e! s $this->db->set_model($this->memberinfo['modelid']); - N3 X% S# h0 A) E
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 2 b5 I% |- J' Y1 R0 w* S9 w, C
if(!empty($membermodelinfo)) { : D0 U) g7 C0 _
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
9 K- D T1 c# c# Y; \. V } else {
V" ^, ~ \9 [( }1 `) Y" V# u9 d! W直接带入数据库,update函数我们跟进看看
8 U% h4 r7 ~" k+ _, q d
- T+ h' Q) b; l4 Y7 a$ l * g$ X: \/ n6 S2 D1 M
public function update($data, $table, $where = '') {
* @4 N" I1 w+ ~1 k1 b8 O if($table == '' or $where == '') { 9 O( I3 L1 q$ A& X V& I
return false; 1 ?) W+ S) T: X2 c" L
} x N4 ~# h+ m+ d4 ? {
$where = ' WHERE '.$where; 4 l% t7 I' d5 ?
$field = '';
/ R4 T2 f2 @& ] if(is_string($data) && $data != '') { . O; E7 M3 k; B& l& E, w
$field = $data; 4 c, O# Q& d' O
} elseif (is_array($data) && count($data) > 0) {
, `! i+ A9 J. C$ }+ ], J7 b: g( k $fields = array();
# Z2 j/ [ R7 f( i: i foreach($data as $k=>$v) { 2 b0 z8 \1 j0 _" ~/ x/ Y; b R0 g/ r
switch (substr($v, 0, 2)) { 8 X* I! D' N0 R1 {
case '+=':
! s' ?8 L& ~8 g! A& O $v = substr($v,2);
+ E' r8 S( Q5 b; z7 Z7 }( e! c* F* @7 H if (is_numeric($v)) {
/ [) l2 }' ]/ X+ {# @* ^6 ~6 k $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); . |# L5 ^4 X' M# ~2 t6 J/ R
} else {
, x; g2 m8 q9 v continue; " b, L/ F0 I6 A7 \' {
}
+ ]3 ]2 V8 [7 Q, t" r& s break;
1 v3 D6 S* }2 ~ case '-=': / ~$ @; m9 y# @" Y6 S' X, j
$v = substr($v,2);
7 l2 e6 A6 g9 M, Q if (is_numeric($v)) { * d6 U0 W: U+ x- m7 a
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); ( m; `; j) E/ i8 I, z) Y
} else { ( g* n) N" ]7 W( X
continue;
- q; o! l9 M: Z" z } 4 d/ c$ R0 I& g( [& W
break; . Z" B I+ p6 [% t, O
default: # k+ @+ D! y! Z- Y) X1 S% O
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); ( h5 P! h0 E8 C! m: V8 R! i K" {' [& A
} * u7 i2 R. x0 y- n
}
1 u/ X6 s$ g- v+ n9 _1 c $field = implode(',', $fields);
' w8 s4 F7 \ \4 P8 t } else {
. B y% ?# D( B. N5 T4 ] return false; " J3 H% H8 W% N- l0 p
} + z. e* y' M& E7 B2 `* G
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
) Z# M5 {$ T8 J( L# u- \5 f5 J print_r($sql);
5 Y7 {- }8 U! B return $this->execute($sql); " M) `' m( _/ S' P2 _: @/ f
}
3 q$ j: |( L! W! D5 a& V& O4 d& d从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
: G3 C1 p( ~$ z5 o# `- ~
* q8 j, V/ e3 y, d1 u3 ^9 t9 a) T攻击测试:, f, [" Z" O- k+ G: R
测试地址http://localhost4 G$ G: C5 {# f3 W
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
" x1 f5 e4 R8 x) J! }2 e
5 L( _3 o3 u; ?. @ & Y0 p/ o _5 w
+ n; X( A9 y* a/ L& ]& Z7 G6 }
' p- a6 q( r7 i# P1 z" K& t) c
% V. o7 a0 v8 c8 ~5 y" z) d |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|