|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告! \3 b( [* k" G8 N
漏洞作者:skysheep- B w+ o1 b' {: L7 N6 p
分析作者:Seay
) q% X3 l! z$ R* @% [7 H4 T, w博客:http://www.cnseay.com/
9 f& |! [" `! T* |( Z. @0 x漏洞分析:
( ?& w0 a0 |7 e/ [& Y0 ?' L! k 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。8 |+ m/ M2 M& B( w# E0 } X
% J! _+ D) I9 n8 C& ]/ h0 |) B
# S5 I1 F. p) g. c# f/ i3 z: I' l
4 J9 k; O$ C* Qpublic function account_manage_info() {
! B+ A4 K) z" m5 X" s, H if(isset($_POST['dosubmit'])) { 5 L% @7 d8 m+ c3 |3 y/ n/ W
//更新用户昵称 9 V1 w$ N, D+ R9 U2 \) T
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
2 [( z% @" N$ P' B; O& v if($nickname) {
9 a, K2 n% J7 w, s; ?) J $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
) i0 s- z) y; e4 k/ c) p if(!isset($cookietime)) {
3 T2 y1 I5 \0 {8 y X$ h/ G# A' ] $get_cookietime = param::get_cookie('cookietime');
( [9 \0 t' ?9 s- `3 j$ Z. ]; C& Q } : w8 u0 x) i( n
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
1 q2 F% ?$ C, {$ V2 W1 J8 x $cookietime = $_cookietime ? TIME + $_cookietime : 0;
* ^3 E7 }3 ]7 D5 F& S Z" V8 { h param::set_cookie('_nickname', $nickname, $cookietime);
0 O9 H) y% Q8 i& g( R0 t! ] } 7 @7 Q3 Z+ M. |# X. Y, ]2 Y
require_once CACHE_MODEL_PATH.'member_input.class.php'; 3 L& I& |1 d& w3 a
require_once CACHE_MODEL_PATH.'member_update.class.php'; ) f/ q) d- [$ ]" _
$member_input = new member_input($this->memberinfo['modelid']); + G1 k4 a# D- q5 g' T2 s" J
$modelinfo = $member_input->get($_POST['info']);
% y" J* d" z, I' E# D+ @1 A $this->db->set_model($this->memberinfo['modelid']); ' k, j9 E" d/ j8 i* E3 L. d2 o
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
3 ]$ E% ?6 O0 ?# s/ F# u if(!empty($membermodelinfo)) { 3 P, l5 o* s' U- \1 x7 {
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
7 d# ]: G0 z- k2 f8 V8 g } else { ; F( a8 C: n) ?
$modelinfo['userid'] = $this->memberinfo['userid']; ! E- R8 X" H6 Y# w
$this->db->insert($modelinfo); $ u* E; g2 y/ M* G# v5 z
}
# I2 o/ j8 [& O7 I$ e" f3 e5 Z代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
u, V/ S0 I$ R5 n3 p2 m% }在\caches\caches_model\caches_data\ member_input.class.php 文件中:
. S! U" m# J3 ]+ p( q6 j! W, [$ s9 B9 A4 `$ y7 ]( W- G' K
( D; T9 l* r7 }) j + N+ O% k4 L0 S$ e: a9 C
function get($data) {
6 ~3 W$ W- N) x- c/ h $this->data = $data = trim_script($data);
1 o: ]9 ]8 t$ L9 q $model_cache = getcache('member_model', 'commons');
+ s7 c5 g' C- A+ q# { ~, } $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; : q- g5 e Q3 }- R1 l) A8 Z: t
$info = array();
6 M) l3 J* t; m3 O/ g) g $debar_filed = array('catid','title','style','thumb','status','islink','description'); * V; }# F* x0 B: y8 y5 x
if(is_array($data)) {
4 ]0 w+ S: i5 E9 j" V foreach($data as $field=>$value) {
/ Z8 m. b" f% v' D if($data['islink']==1 && !in_array($field,$debar_filed)) continue; x+ i. O2 @# l2 P
$name = $this->fields[$field]['name']; : a i& B; P# [! r9 s+ K* o `
$minlength = $this->fields[$field]['minlength']; * p; D( i; b. {, n+ M
$maxlength = $this->fields[$field]['maxlength'];
2 }& s4 @2 }* O( l$ d& N0 ] $pattern = $this->fields[$field]['pattern'];
( g5 N# i2 d, h! l, \$ o $errortips = $this->fields[$field]['errortips']; % i- \4 Y! z3 @' i: S% |
if(empty($errortips)) $errortips = "$name 不符合要求!"; 3 N- Q2 `9 H" Y/ T
$length = empty($value) ? 0 : strlen($value);
a2 {; w1 p0 c2 u/ [ if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 1 q( p9 _- x, L3 k
if($maxlength && $length > $maxlength && !$isimport) { 3 @2 `! W% t m
showmessage("$name 不得超过 $maxlength 个字符!"); 5 } ?2 b5 X* Q Y3 q( S2 I8 A
} else {
$ n9 b- S0 v9 O3 R0 A2 W str_cut($value, $maxlength);
- ~- P) E3 ]7 F6 ^/ T, l5 C } / }0 @ f5 j, Y+ A% L# P
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ) u* V! ]! \6 S* Y
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); / C$ @4 I7 l# j2 ~1 L) |
$func = $this->fields[$field]['formtype'];
, }" e3 y5 a& V0 m! `/ A! V if(method_exists($this, $func)) $value = $this->$func($field, $value); ' g5 K+ R5 u9 j/ p% p g, `
$info[$field] = $value;
) q6 `/ Q$ C, N/ o+ F }
3 N7 X9 }0 D0 S& _3 H: u } + b. ~& V5 {8 W1 C7 u+ j4 u! M$ p
return $info;
+ c" m0 {) W7 j; I% o }
6 w/ |, c' `. m5 ?9 W6 O5 itrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,) Y) _/ a/ \/ B2 D* e9 s
; d- x- D5 d) u7 U5 G \再到phpcms\modules\member\index.php 文件account_manage_info函数' v0 |, d8 T, T4 R& b% L5 K
过了get()函数之后。
; O! N* R! ^$ A) k, n1 R
: I: S; ]' V; n8 I
1 W# O0 Q4 S$ R2 K( w$modelinfo = $member_input->get($_POST['info']); - H. s5 G2 d6 `( J
$this->db->set_model($this->memberinfo['modelid']);
6 E% X* X. h" j" n0 S5 z: V $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
6 S* E" U0 h4 C3 ?8 I; l! @: k" _ if(!empty($membermodelinfo)) {
( r( f) a6 O% t3 K, X: `; o, \, n: } $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); - q5 C6 s0 R& \
} else { 7 C( V5 b, Y, b0 y' T4 e
直接带入数据库,update函数我们跟进看看
0 h) y$ |2 j' T3 h/ A: F: ~; H$ `# V& m6 f7 Y* w
! b8 D, l1 M. u
public function update($data, $table, $where = '') {
+ o3 z( a( s* B" z; ] if($table == '' or $where == '') { 7 s- X- G. i, W9 R. h
return false; , x- ^! l8 Z4 h
} ; U+ ]4 J' c2 \* j$ M8 f& u0 w
$where = ' WHERE '.$where;
n3 Z. R G7 r $field = '';
) Q6 t/ B1 c3 b2 j: _ if(is_string($data) && $data != '') { $ ]$ I3 J. J# _( [
$field = $data;
$ F2 o7 z: c9 w( M3 A, s V( i6 b' ? } elseif (is_array($data) && count($data) > 0) { & t6 {* _- B! c2 e1 t0 y3 T2 v7 Y
$fields = array();
$ l0 b; Q" v* U foreach($data as $k=>$v) {
1 [. t2 t6 ^0 F R+ p9 N switch (substr($v, 0, 2)) {
4 i% j# M- |/ C7 x. g" i C case '+=':
# j/ K! i5 T0 F1 S7 T" {/ `3 z $v = substr($v,2);
- M( V9 p3 V; j/ R if (is_numeric($v)) { % h5 ~3 {' z$ G4 q+ m: V
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); + v% q. T2 k; t- ~4 p* K7 Z+ H
} else { 7 W" y/ _% m! {1 v
continue; ; }3 t9 t$ u4 `9 P
}
" m1 }8 P# j. P; C; E4 \# w break;
. b" r( ~/ i/ G8 [ case '-=': * \3 S9 g7 ^& |# X4 x9 j
$v = substr($v,2); # p+ r x2 g+ A* e% _
if (is_numeric($v)) {
B0 a- P0 T; U+ C& n( Z) i: V: Y $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); . c& U" n) d' p6 Z. o$ {
} else { * a: w( b) ~6 c8 e- v& A" S
continue; 2 M( T: `- ^4 y- E; ^; }' c2 x3 d
} + _, O% F4 r2 O- F4 }
break;
/ A0 S* ^. g& G: j' A9 q3 X* S default: & v# }7 q2 G& a5 f+ v* v
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 0 Z, H. i! b) ?& N6 K9 t- U' C, ] @
}
; `0 A: q: P2 @/ k( i; O5 [ }
: S6 y7 _' F; r) a7 C $field = implode(',', $fields);
' B$ o& \5 o9 [3 D$ L } else { % B6 |) E4 h6 k# F0 L, W+ {6 f- N
return false;
; E4 H: t" g( [3 r9 Y }
. C4 _9 H/ i' x* w& a) R $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 2 h6 Q6 P' T2 @4 H9 s* }" j
print_r($sql); U1 u t" @6 z! O
return $this->execute($sql);
5 _2 N! j6 k2 F' h }
U, @( L8 O8 @从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
9 }) Y8 P4 g, z
+ Z$ P* U0 O1 b4 U7 Q- c7 D攻击测试:
. G5 E/ I$ w$ D/ C- y6 n' j测试地址http://localhost
. b E5 U1 i6 h) a: S 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句# ~ J6 P* a" [) C I
* ~0 K, I# d) _
; Q6 Q% B4 D3 a4 e! k: c0 }* ~! ~2 h1 ^, S% K6 d6 _2 ^: V
! J; |8 e% I% i
. q" S6 P y3 d F) {4 W: | |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|