|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
, o A) Q! U3 q( e- Z2 O漏洞作者:skysheep8 X) }. K, `7 z, D9 K! R
分析作者:Seay
8 @# l+ M5 g$ ^! j0 A4 X$ @博客:http://www.cnseay.com/
% X' y0 `. R+ C% t* n+ M; ~' q漏洞分析:; }2 z+ k( l( H# o8 K) ^5 m) J' a' u
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
( M' `) y8 l7 [7 N# T" @( c/ x d; U* |, Y1 Q7 ^* u7 y
3 q. W0 n' |" J- d " W1 G9 { i+ q5 B6 j/ v3 u
public function account_manage_info() { ) ~5 J8 h% B! D- `/ s$ O
if(isset($_POST['dosubmit'])) {
2 J+ v/ d8 X5 z( U9 h //更新用户昵称 & U" D4 _$ E% l4 l0 C- K
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
9 u' R) \4 a' U; A8 H0 }+ j if($nickname) { ! k/ N6 h4 G& h6 q9 A; R* N
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 3 D3 h* N9 X* _9 a( L# U/ P
if(!isset($cookietime)) { " @" m- z& j( [' z( d$ F
$get_cookietime = param::get_cookie('cookietime');
8 R0 R5 w) T8 @) Y6 v& e0 F }
% h8 b* o$ E1 K $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); : j$ z3 Y7 w) H' g2 y4 E) s# \
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
+ l+ w, e/ R0 o3 y param::set_cookie('_nickname', $nickname, $cookietime);
$ y# k3 x4 X K& z; V# } }
4 X' `& A: m. ^- X require_once CACHE_MODEL_PATH.'member_input.class.php';
/ Y1 ?1 x% G8 a) \6 r require_once CACHE_MODEL_PATH.'member_update.class.php';
) K! R2 ?# T" W $member_input = new member_input($this->memberinfo['modelid']);
; g9 F+ G) m% v $modelinfo = $member_input->get($_POST['info']); ' f6 c' \7 B p( O) p3 f
$this->db->set_model($this->memberinfo['modelid']);
6 t, a! e0 W `# K- b& U, I1 K $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
; ]: X" c' y6 m. b$ J if(!empty($membermodelinfo)) {
8 v, j; X- _. h5 y) Q $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
" y5 ]- H6 w4 _ } else {
# C; q0 J9 N" n6 M $modelinfo['userid'] = $this->memberinfo['userid'];
$ I& O! q) o8 U6 D+ i: c0 J+ V$ x- ^. W $this->db->insert($modelinfo);
q0 U# i; c" u: K } 7 u @' F, V( Z
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
r; ? ~! j J" ~在\caches\caches_model\caches_data\ member_input.class.php 文件中:. a9 M& I' v/ y& N) Y
1 }) e+ w- b b) m! y
9 d" j( |# q! k- ~+ a7 O: M
/ w+ @* o' B! Kfunction get($data) { + W2 Z, w9 ~7 W
$this->data = $data = trim_script($data);
8 b1 U6 d* w) T- L $model_cache = getcache('member_model', 'commons');
1 b: W* `, @9 E3 B7 n8 ]4 O $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
/ K: N! l. g3 r" ^2 @* k7 u $info = array(); . ^6 g1 f4 B \* X: h! R
$debar_filed = array('catid','title','style','thumb','status','islink','description');
6 c' t3 W$ M# j* S if(is_array($data)) { ( W( Q$ a% Q: A" {" q
foreach($data as $field=>$value) { R& k4 j# B: ?) k+ d0 {' R
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
& u+ d0 [/ _! V) H& v# a2 Q $name = $this->fields[$field]['name']; , s/ J; t; o1 k7 u! O5 w' K1 `
$minlength = $this->fields[$field]['minlength'];
7 M: O2 u4 Q% v$ G6 U $maxlength = $this->fields[$field]['maxlength']; 3 T: e$ O$ Z* }( D0 q
$pattern = $this->fields[$field]['pattern']; d& I( K( }; D, J: K
$errortips = $this->fields[$field]['errortips']; 0 I! C [0 m G; |* T
if(empty($errortips)) $errortips = "$name 不符合要求!"; 5 H; Q( K- j( E3 r
$length = empty($value) ? 0 : strlen($value); 0 \: C6 k. H1 B9 f3 v* D% l# u3 K
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); - h% F* r! _* h& i2 c5 S2 a4 G
if($maxlength && $length > $maxlength && !$isimport) { E% ^: R. m/ j' i
showmessage("$name 不得超过 $maxlength 个字符!");
# L0 `1 M; c! [$ _" p- E, r0 x% I0 [ } else { # M# {0 u+ C* v$ K6 X
str_cut($value, $maxlength);
1 }, ?! s% k3 t4 m, A) i }
: c, r8 ?7 ?6 W5 V" m* C( V K if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
) e" A# W' X- l0 f+ K- q if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
# r k6 ?/ @" w$ n7 j: P. q $func = $this->fields[$field]['formtype'];
0 y4 K! P" W! z: H if(method_exists($this, $func)) $value = $this->$func($field, $value); * @# _& P% l. V( U# H
$info[$field] = $value;
r+ g% B& U' L } 8 ~& M! g5 f0 D# G6 b5 [& I/ _3 \
}
) k+ N9 X( B" ^; U, g return $info; 4 |* D4 z1 @6 n) P* o, r
}
& k5 z8 [/ d: Y! Y. ~trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,0 ]5 V1 Z2 E, Q1 E/ R; O
+ _$ T" O3 [2 T @9 u0 z/ \
再到phpcms\modules\member\index.php 文件account_manage_info函数+ h* ?2 I! e9 A* m" B# [0 f
过了get()函数之后。
5 [0 _8 {; c0 S5 t$ q4 N- k4 P/ _: s* D. s, V q1 f
, K* t! j q; k$modelinfo = $member_input->get($_POST['info']); + ~+ J! @' j: t+ b& T
$this->db->set_model($this->memberinfo['modelid']); 1 Z! u# z; z3 M6 B2 D
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); $ I% m! \( U- ^2 n! V
if(!empty($membermodelinfo)) { , [* g9 D3 p+ D2 `
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
, {9 ?! K2 s/ \2 o0 G2 r } else { / K8 A4 T! s5 M( K
直接带入数据库,update函数我们跟进看看. r" R+ N3 l \1 H' Q# g5 e* h
( g# x8 U5 e0 ] a6 `; Y) g ; D, l3 P5 H3 ]7 G% t$ V5 }
public function update($data, $table, $where = '') { 7 m6 h: t5 z' k+ I! v# |* ~
if($table == '' or $where == '') { 5 F5 L; L. O" o# w3 U. s6 N2 B% w0 M
return false; . X3 \7 z" B0 ?$ r+ C+ C+ i4 Q
}
/ U) V0 H; z0 {' ?) ?6 J4 N# V $where = ' WHERE '.$where;
# }# W5 ?$ _3 k0 C6 Q1 H9 y9 B $field = '';
& Y7 z8 R( ^7 f$ i. j if(is_string($data) && $data != '') { 2 E8 |7 w* j8 N9 J7 G' o
$field = $data; % R: L. c( U1 G" l6 T0 u! G
} elseif (is_array($data) && count($data) > 0) {
2 p Z7 h# n% @. b" G, p7 a $fields = array(); % I5 K { [6 p& X) h
foreach($data as $k=>$v) {
: l# F7 r" ]0 g& F switch (substr($v, 0, 2)) { 2 l, f# x, E9 K8 _/ z. s5 W
case '+=':
$ A4 t! v, u, ^8 d $v = substr($v,2);
) R1 j! h% S2 M if (is_numeric($v)) { : ]. P3 w1 F B" L
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); . g2 E1 q. x! _8 h
} else { $ Q [+ }+ Z, t
continue;
- l$ j! d A5 Y4 p }
7 @- s% I/ \3 x1 y, \ break; ' R3 a2 `, L+ z
case '-=': 3 Q% k! B: q! i, }* |( G
$v = substr($v,2);
, f+ B& y& ^+ [ p if (is_numeric($v)) { 6 {( Y- d' B% J& @. Q9 ` W
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); " M7 d9 A8 l- d7 `
} else { 5 C/ [* h+ K4 s( J2 x2 X, u4 _8 K
continue;
4 A) d. K, q- p) \* F }
0 x# ?" f; _: [: w/ E# C break; + e7 K0 l8 S* v+ x+ C+ C
default: " |0 p" }* h0 o0 x9 d+ v& V/ A
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
1 R7 x# Z% z) @# q; P }
) O! x5 Q# }! b4 H% ]; O; Y( ~ }
9 m" q% f# t+ A8 p; @) e- l0 [& i9 o% c $field = implode(',', $fields); , {$ t* y# Z; a
} else {
% G% p5 k5 G9 ~, I return false;
- [$ d! J! l" u* t1 O; b } % o& t6 j& S0 r5 g
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
- w0 h- D7 x& E+ i' H$ ?: U1 A print_r($sql);
1 B- Y9 q, M0 q; P7 o( M5 \9 p; k return $this->execute($sql);
3 ~/ `& i7 q9 c6 Q' B }
6 L& I8 J( k+ Q1 p; l从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。" d2 B) o0 C. e; C$ s
% y7 x8 Y1 ]. l4 d9 b& y2 W! a攻击测试:/ Z! e3 N" F- D/ j W; [ _
测试地址http://localhost! d! V3 N# O n4 h, Z+ |; Y
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句% c3 C2 y7 T. z" X2 v/ p: o
9 ~& x* m$ U( C) [+ Z# U6 |7 ~0 D
8 F* X. X8 }( A- B3 G4 f* U* h- ?' i9 ^# z
@4 c( @* |9 O. V) w. c5 M5 G- }
9 f& A7 ^1 M6 K5 S
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|