|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告) Y4 H( P5 H. l9 F* a# y
漏洞作者:skysheep7 }# r$ W6 V- Y8 A% N: B" {8 ~
分析作者:Seay% n' k" d. P1 n9 y/ S6 b/ I: W |1 w
博客:http://www.cnseay.com/1 O% `6 o5 N2 ?3 b. Z/ z" c
漏洞分析:
1 r" z4 E+ r/ b3 R0 a; o6 e3 q 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
+ s& A( p' l5 ~2 j$ ?, m3 k! ?: ]
: b) G7 b9 t+ o, W- p; a+ O- a
7 B* t' s1 E( S! W% P0 D. qpublic function account_manage_info() {
1 {5 W, ^7 @2 h" U if(isset($_POST['dosubmit'])) {
4 ?- W" o" d* g; w; s //更新用户昵称 , x4 I; J% g3 {, C# O
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
8 E( X" D" z" l3 w& U7 i7 t if($nickname) { ' Q+ q! D9 h% j9 P, {3 x7 j
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); & {$ O" R6 D0 x" L
if(!isset($cookietime)) {
! l& q* b# T* ]4 p2 y $get_cookietime = param::get_cookie('cookietime'); 4 `# _$ j/ p* y! e$ ]4 \# o& Y
}
/ ]! A7 K# U- }3 ?, J) l L7 m+ F $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); . x! Y! G* q- ~* o. N
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 8 S0 D7 n3 E8 ]& u' F
param::set_cookie('_nickname', $nickname, $cookietime);
7 b. k# \ o+ Z& |& O- r } F0 O% L# T( K! q9 C' C
require_once CACHE_MODEL_PATH.'member_input.class.php';
6 A5 Y3 L q; T5 q1 e require_once CACHE_MODEL_PATH.'member_update.class.php'; 3 o& d+ O7 L$ I: l ^3 j% X. i
$member_input = new member_input($this->memberinfo['modelid']);
0 f. `8 Z& O/ v8 i$ @, a9 N, x6 \ $modelinfo = $member_input->get($_POST['info']);
; L* K) k+ l" P1 f $this->db->set_model($this->memberinfo['modelid']); e# A; p) P. r6 M# L7 `
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
% R0 |4 L: ~+ t- g+ M% [ if(!empty($membermodelinfo)) {
" D2 \: I7 W# ]6 e. e $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 8 z q$ u% d1 g3 A' A- G
} else { 9 J7 V5 i8 d0 o- _# l* e, w$ T G6 I
$modelinfo['userid'] = $this->memberinfo['userid'];
; ?7 \; K# R) k6 C; o- [ $this->db->insert($modelinfo);
# K, @" B0 b: L8 I. R3 L } ; h' }4 ^3 R/ G1 ^& |7 S* |0 Y
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,$ h7 X8 f" A+ v& Q7 `" P
在\caches\caches_model\caches_data\ member_input.class.php 文件中:# U' ~- E! g# a7 X$ t0 g, n7 N! x
5 ^: k2 w, m. x1 r/ |1 N4 F! c
7 K0 X- z3 Z6 s _/ ?# M: A$ U5 s
( w+ m2 O# J; \$ m; {7 Z' I$ Ufunction get($data) {
1 s- }! ^; S7 X$ T $this->data = $data = trim_script($data);
2 h0 k% p* [) N* Q9 q $model_cache = getcache('member_model', 'commons'); 7 L5 L7 | V) r: U' Y" ]& Q
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
, n" H/ q5 |3 W $info = array();
3 L/ @, G. C& o! a- A $debar_filed = array('catid','title','style','thumb','status','islink','description'); + p2 F7 X: h: {, [/ z) [6 q
if(is_array($data)) {
* i* `/ W9 k, m foreach($data as $field=>$value) { . U. j; C# p' i5 ` g9 {6 N2 X2 ?8 x
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
; N0 h7 A( \1 J1 M6 L% w! V $name = $this->fields[$field]['name']; . X& H7 |% ^; |3 A
$minlength = $this->fields[$field]['minlength']; $ e1 ^9 q( W8 T
$maxlength = $this->fields[$field]['maxlength'];
+ K+ m- Y# J/ i% b! b) `' S: T $pattern = $this->fields[$field]['pattern']; ; j" {+ S2 ~1 N6 K& A1 ]
$errortips = $this->fields[$field]['errortips'];
' d, ^+ Z7 f$ `& Q; c if(empty($errortips)) $errortips = "$name 不符合要求!"; , }( S, P! H4 _
$length = empty($value) ? 0 : strlen($value);
' Z; d7 y- Y/ x5 j9 L if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); . D) T1 b; [3 _# E
if($maxlength && $length > $maxlength && !$isimport) {
; Q/ k/ q5 R( n+ K% {' J showmessage("$name 不得超过 $maxlength 个字符!"); - u* Z4 S. ]. G h, E3 I
} else {
, Z0 h. m* Y' |/ A0 m8 P7 X2 K str_cut($value, $maxlength); * ^2 d% \) x0 t, n% J) }0 b) g
} ; E, r1 S {( V$ w
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 2 ~% C3 Z6 ^$ @ Y
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
/ H! K8 V9 W; h $func = $this->fields[$field]['formtype'];
. N5 y( n' ~3 a6 T+ t if(method_exists($this, $func)) $value = $this->$func($field, $value); 6 v' q& Z w3 D, U: h: L
$info[$field] = $value; / B7 S/ \$ A1 o5 N
} " r: C0 T+ u _4 b) X
} / C2 `' R# E8 d" C, Q0 E3 ?" \
return $info;
& A, u& e8 T# L# r" {3 ^4 Q# a! u } 5 W; a3 `0 J, `, a
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
- G, f T: d; `$ B! a3 P4 t4 E. e4 W! B# H' ~; Y
再到phpcms\modules\member\index.php 文件account_manage_info函数6 e+ t( K3 `& I3 D+ G6 B0 z4 c
过了get()函数之后。- \2 ?" `+ _$ n. m
; h( [% R% z7 {. R
9 C- S( k1 ~# Q9 \* l$ i$modelinfo = $member_input->get($_POST['info']);
" c; Y- a$ j% O. C" h $this->db->set_model($this->memberinfo['modelid']); 0 n/ p4 _3 V6 F: c$ `( X
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); " p, A _6 L& j; p- y9 a4 a8 ~4 m
if(!empty($membermodelinfo)) { " k1 E3 o( `! n
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); - I: F0 J9 @ `6 M3 V+ F/ |; o0 C
} else { $ ]/ ], t0 {5 O/ m
直接带入数据库,update函数我们跟进看看6 b5 m1 i' a' i5 k$ i2 C
; Z4 M$ z- X" x6 c, X
& R) L& G0 n8 K6 O/ O! {public function update($data, $table, $where = '') { . [& y( C/ y5 d# }0 z% z7 K8 |# K1 h
if($table == '' or $where == '') {
3 t3 X# g+ u; c) h0 Z return false; & Y$ d3 e/ y4 M
}
2 Z( G! c" ^# E# I& v $where = ' WHERE '.$where;
; N: P! J, |5 x0 e- y. x $field = ''; 6 ?2 r) A$ n9 F8 a, E* D: A0 |6 |1 F
if(is_string($data) && $data != '') {
& g$ y# t: E. `' T. Q0 r. T/ j $field = $data;
1 s/ r, h* @- J* n' S) p! _ } elseif (is_array($data) && count($data) > 0) {
, \* Y9 y7 n, i. j $fields = array(); 5 h- e5 t) n: C+ X) W* ]* N/ B* I
foreach($data as $k=>$v) { % e. {1 T S7 U% V' E
switch (substr($v, 0, 2)) { & h9 C/ [- [; b5 j
case '+=': + J( T- }( H1 v/ M- y( |/ z
$v = substr($v,2); ' h# A* M) I8 l4 Q1 Z
if (is_numeric($v)) { % e1 s9 Y1 g7 g) A$ X/ K) T8 ?
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 7 u" m! M8 s: Z( D
} else { # V: C3 W O7 I4 M1 ^: ~) u0 V6 D
continue;
. B. O6 o- d$ b2 H0 T* P }
( f$ J+ U7 Y: \. L& i break;
) A4 |0 T& }' O& y% e! {5 ] case '-=': 8 ^8 q" A( a, B/ G0 L2 T. f
$v = substr($v,2);
* i9 \* W6 }- `" }5 r1 Y if (is_numeric($v)) { & R- e5 R/ u# G2 V! _
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); , ?1 g. ~ j, `, ?6 p, F
} else { # D: O9 W3 K& }# K6 p3 h
continue;
1 K8 d+ D9 ^. j' U, C } 7 V2 o' w' g' k# Z3 ?
break; % Q( ~9 }2 s s' \
default: , z$ a2 {; F' c6 F' o' F7 ?9 n4 |. f
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); : [3 b1 u2 K% p! G+ p, R
}
M& Y, g# D, w9 q6 J }
3 @+ D2 i8 g; o $field = implode(',', $fields); 9 M/ G1 o) Y% I2 i+ U
} else { 2 x# Z% z/ i8 I! h
return false;
" i" E* h. n. \" U; c4 ] }
6 R4 l1 Z3 H* R1 `$ c3 d4 _/ h $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 4 U% ` K( }, D( _- o; H. e
print_r($sql);
% C! S5 F/ @8 `3 M6 X return $this->execute($sql); " H1 H- i6 F4 g" e T l' N' K0 r2 u
}
: f' l4 m& I- [! V# @从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
# ]& C) S1 \# z+ T: d0 e' ]' X8 T9 A+ s/ j
攻击测试:% d* D! {8 I# W1 m7 I) ^- }" _0 O
测试地址http://localhost
# k0 {. r% w) e0 f$ C. d 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
& v) D% C- M( A# ?& G: I9 {
" b! `/ M3 F; w. L# u4 \" R1 l& \( R E8 w6 G& h9 o b* m8 W
* w$ B' g0 W8 w R
0 s' X- }' v8 s! z5 `
! N3 I) Y0 d" m& H
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|