|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
2 \, r) v; O5 c, I漏洞作者:skysheep! q% i* z, p) C9 M6 U
分析作者:Seay
% j, Q6 {( L! `/ p0 V! b/ B# e博客:http://www.cnseay.com/. k1 |$ d Z2 G& o
漏洞分析:$ _; t$ O" |0 F
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。$ Q' b% K% r) J7 G6 U9 c
7 E$ Y: b, W4 {9 e
* s* J9 y9 h6 H* o4 R. k9 X M% Z
: c4 l! F/ g9 Z ipublic function account_manage_info() {
$ o2 g' v7 {& F' D% a9 J4 D if(isset($_POST['dosubmit'])) { , h: B# O- G( T
//更新用户昵称 . a. c: K+ F4 J7 U; C* h- ^. r6 L
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
( T/ I7 A/ {5 B% L' g if($nickname) {
~& u, {0 e% }# Q9 J& y $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
5 I; D, }6 k) i if(!isset($cookietime)) {
: h' j! @3 D; Z1 ]1 O: ]$ h $get_cookietime = param::get_cookie('cookietime'); / n& \; T; P& q5 H, a
}
* p" T/ T1 c5 J $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
7 `1 b& y4 O u2 \8 J( J2 Z( G5 b $cookietime = $_cookietime ? TIME + $_cookietime : 0;
3 m6 I7 u2 U. _& j( P% o* Z0 S param::set_cookie('_nickname', $nickname, $cookietime); " t0 n' |) ?8 j9 \, p; H+ r3 s
}
4 B3 r0 d3 P( p2 A require_once CACHE_MODEL_PATH.'member_input.class.php';
' J( y; B# G0 v0 E7 i/ j5 w require_once CACHE_MODEL_PATH.'member_update.class.php'; 5 N9 P% Z9 N( D5 D8 i- \
$member_input = new member_input($this->memberinfo['modelid']); 1 `% l" [" o# `) @" _1 ^7 Y
$modelinfo = $member_input->get($_POST['info']); 6 T: C* }- L1 g0 b' G
$this->db->set_model($this->memberinfo['modelid']);
/ K" ^+ g* H6 p $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); % b6 c. e, S9 q0 V
if(!empty($membermodelinfo)) {
% \3 t/ \" X |3 P, P# ~% T2 }3 V $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 6 W0 q6 Y: j' U' N- F: g
} else {
$ r, M7 D Y" q' U- y $modelinfo['userid'] = $this->memberinfo['userid']; 8 t9 h9 S: W! P
$this->db->insert($modelinfo);
) x: V2 H' G* X+ d% a } K5 F) @ ~& Q5 K
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,) p, L0 m) l: E |, X$ A7 z" K0 @
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ m* y/ b2 o9 q2 P( p, J8 B0 k1 s( T, [ F: j& J( d
. S) v; C* l6 n! g ( U* G( L3 G6 d% h5 ^/ y& w& J
function get($data) { + l5 _3 J* P) O8 R) b
$this->data = $data = trim_script($data);
' }) Q8 ?+ |0 m! a $model_cache = getcache('member_model', 'commons'); : v5 N8 p- g/ {( h1 }
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; ' ?# P% h7 D& g$ X; n# `
$info = array();
& V3 U3 ^, z& ^" K- R! E $debar_filed = array('catid','title','style','thumb','status','islink','description');
4 {2 L, D& d1 `# z7 `* B if(is_array($data)) {
* y: O4 _$ t% i foreach($data as $field=>$value) {
. F8 K2 v4 s" j1 J if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
$ c4 M! i% B+ I1 N4 J% c3 { $name = $this->fields[$field]['name']; * Y. E6 U2 {( l
$minlength = $this->fields[$field]['minlength']; : ^ B+ x/ `( k9 { P' n
$maxlength = $this->fields[$field]['maxlength'];
" o& y! _+ h6 e: D" j $pattern = $this->fields[$field]['pattern'];
( Y: Q8 P5 S8 q" l5 P2 l $errortips = $this->fields[$field]['errortips']; % J( k/ U- v! ~
if(empty($errortips)) $errortips = "$name 不符合要求!"; & R# f7 e8 W3 i. l: i
$length = empty($value) ? 0 : strlen($value);
4 Z: e! I" U5 b0 x7 M. Q if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); , X$ b( a% j& D1 \( {+ ~! [( W
if($maxlength && $length > $maxlength && !$isimport) { 2 n3 ^+ i, B3 ]
showmessage("$name 不得超过 $maxlength 个字符!"); , _. j0 {+ O# ~& b* e! a
} else { : b9 s9 \ C" L6 l# b
str_cut($value, $maxlength);
; J, c$ A# B* |3 D; D4 J4 _8 \ } 1 x; T0 a u' v0 e4 u1 v% a
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 5 |6 C8 }6 Q. [) V, T2 }
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 8 B! \2 i( b* N8 P6 L
$func = $this->fields[$field]['formtype']; ' S7 `; z3 }. I: L& M1 ~) U
if(method_exists($this, $func)) $value = $this->$func($field, $value);
/ `% z, ]" G% A+ ? $info[$field] = $value; " C& o" z3 w3 X: U6 Z6 O$ Y
}
, X9 T, R+ W' z }
; {0 t) [5 z" I5 I3 d" u. }* C/ _ b return $info;
0 x% E; ~" q2 G3 U } ' d! D7 r+ l0 @. T( M
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组," U I! L' B0 u7 t; f/ U1 E
9 b2 P9 P# c. J3 C' F% G3 K再到phpcms\modules\member\index.php 文件account_manage_info函数
) H% [- Y7 U: \+ C4 E过了get()函数之后。
) x/ }" p& u8 {0 e; S3 {7 H" o5 U' ^0 g& B. c9 r3 u5 q& x
8 X Q5 j2 `. u- s, O4 a$modelinfo = $member_input->get($_POST['info']);
2 V0 n/ C3 A/ |2 c% j; R& {) x $this->db->set_model($this->memberinfo['modelid']); 5 w: |9 v& f( R3 S4 e+ j) R
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
, ?' r3 F# u4 [# }- O" L) P if(!empty($membermodelinfo)) { ^3 p( \& O5 U7 m( `' t6 l
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
* P( W8 j" q. e$ F } else {
/ }. `3 W8 t' ]9 l0 i直接带入数据库,update函数我们跟进看看, V# _* [6 k0 q
: x! w- z! M! W% Q5 B
, @* Q. V% |# n4 K
public function update($data, $table, $where = '') { # Y9 F/ }9 L8 ~- I) P
if($table == '' or $where == '') { 0 U* o4 N! k& H1 U4 b
return false;
) v1 Z: `3 Y8 I# \: ~ } ) ~' s% b8 s0 Z
$where = ' WHERE '.$where; ' G2 }2 R7 `+ `% x% d( x# o
$field = ''; 3 d1 e- A' K; i+ e" R# k. O2 y
if(is_string($data) && $data != '') { ! i7 k- t0 W# d0 n
$field = $data; ) l1 o$ J8 }7 q/ S/ Z
} elseif (is_array($data) && count($data) > 0) { : U' J' _' y6 E7 ?5 l) C e7 g- J
$fields = array(); ' O( z$ {' g7 i! J p
foreach($data as $k=>$v) { ! _; v$ c) Y# c5 g. m
switch (substr($v, 0, 2)) {
1 h0 j5 p; R0 K8 q% ]2 Q' v5 r case '+=':
5 b/ B7 n8 B8 B+ t $v = substr($v,2); $ C- R: e8 W5 A- e2 W
if (is_numeric($v)) {
4 m k; |. K. j N3 W7 i. r $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); , k5 N7 f) J1 g% b" e
} else { % y' ?& t# |! I7 e5 G; u
continue; ) h/ b! J# f% ~4 w/ v3 E/ c
}
3 Z. E9 b. Q2 e: ? break;
/ K- n9 e0 @! Z T: ?/ B; c+ d case '-=': 4 e" c7 T4 O- i% ~% E2 c5 f
$v = substr($v,2);
% g' x) @5 a# K% p" N/ ]4 K+ [ if (is_numeric($v)) { 8 ]# _1 Y, o0 i8 S
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
; o R% Z# d! N8 k4 X- \) t" P/ N& ~$ a } else { * G6 v3 i: E0 Y; J2 @3 {7 ?
continue;
* g9 X. c; t1 a# B) k' f }
; k& W6 d4 s( Y6 I1 U+ B* ~$ b break;
* {7 Q) v. f9 | O! i' A default: % a: a. [. `( r7 i6 A6 H$ E
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 3 g: @* u" Y; u
} ! J/ H5 F$ Z0 a* m! V
}
N: g7 ^; X+ ~" s+ l( { $field = implode(',', $fields); # C% c, ?* p- h# h# K4 _
} else { 1 |' G& t8 {, |- ~( r
return false;
' F% W7 J: ]0 f- w' U } # k0 B9 b) b" R1 p: e$ b3 z4 |
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
- \( }0 \3 b3 x7 _6 W- N* T print_r($sql);
( R; o0 x1 c; c! w$ j/ L+ E return $this->execute($sql); / ]( I' Q9 q0 V" D) ]& B6 P$ s
} 0 [% Q; } d% y M# q& H3 b
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
. L7 v( C% W: H2 g0 G/ F. Y. n( y0 X+ q. U* P, V4 C. I# Z
攻击测试:$ q! [# r& i4 d* L# e4 t* t& n
测试地址http://localhost. s: \4 ^- J* X$ G( y
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句6 `! D& f) p$ o) H
: E# k2 R1 E6 @, Z' }- }: M
% f* \. w/ a! c) ]8 B: b1 O
. I% z3 \7 q1 m ]8 i5 \
. `/ E3 k# t3 k/ ?- ]7 n: |
: }# Y- g) j* F0 V2 D |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|