|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告' J8 J1 T T) x: ~
漏洞作者:skysheep$ Y1 \/ n" o% W
分析作者:Seay
* x" X& D# I* d; e博客:http://www.cnseay.com/
: l; y& i" y8 a& q# l* B; n漏洞分析:
. J5 i% x% S. w& d. n& ]$ g0 P 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。+ I" ?( m8 P, N
2 B8 M( p, i& d+ `3 l9 L
- V5 J# N: [& m3 G) P
5 E+ x4 R8 s) gpublic function account_manage_info() { $ q% Q0 X9 ?# V6 A
if(isset($_POST['dosubmit'])) { : t0 ^1 e7 a# {" n3 F4 t
//更新用户昵称 7 @+ g9 Q9 S- A) @0 T+ s' }
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 7 b& f! j) A) p, y/ d! h
if($nickname) { 7 t6 j- X& A; ~, v
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
0 ~6 G* z/ O5 M. N8 u2 ? if(!isset($cookietime)) { * s" l* w8 U9 M7 V! ^* {5 V
$get_cookietime = param::get_cookie('cookietime'); & t, y% z {% f; m
} ! k+ F6 s5 Y$ y7 D N% W' \
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
3 V4 G3 T3 P+ C- X, x, k' d* \( x $cookietime = $_cookietime ? TIME + $_cookietime : 0; @6 @8 k3 G3 b5 S& M% r4 U* a+ }
param::set_cookie('_nickname', $nickname, $cookietime); / p$ c' D3 r; Q/ o% h
} # _( i& v+ g- W9 d
require_once CACHE_MODEL_PATH.'member_input.class.php'; : l2 _8 s( q' \) a8 m' _6 W2 I
require_once CACHE_MODEL_PATH.'member_update.class.php'; 7 A4 B. r4 u$ C- Q* h9 ]% Y! a
$member_input = new member_input($this->memberinfo['modelid']);
: _/ c; E4 p7 L; o8 O% l: o $modelinfo = $member_input->get($_POST['info']); 3 e& @, d$ _: ?8 u! e
$this->db->set_model($this->memberinfo['modelid']); " F3 T6 |$ L- q% O, S6 F8 [7 b
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 4 w* ^" n% s4 D7 o4 O1 Z8 s
if(!empty($membermodelinfo)) {
Y) `" Q3 b! J: |6 G $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
7 R0 r2 t5 h5 h2 Y; ~$ K1 _/ j } else {
6 X3 F' g" K# b7 \1 q+ r2 c $modelinfo['userid'] = $this->memberinfo['userid']; - n& p8 w7 \% W9 v* u
$this->db->insert($modelinfo); 7 K* ]1 s2 g$ z, _0 L( G+ s! C
} F: y3 [2 V9 S: C
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,# \6 M& U! n: o8 u# R9 R
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
O; }/ a7 Z! O3 E2 f$ U+ G% u* B1 h ]2 W1 L, \( Q
* Q% Y$ V+ J i* s' y
+ r; M( ]+ E3 Q$ D _function get($data) { x% b1 e$ b3 x/ m6 d
$this->data = $data = trim_script($data); 4 q0 s) |* m0 N& n p
$model_cache = getcache('member_model', 'commons'); 4 H: b) G' R7 ]" R2 ^
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; - Y! V* G( p+ O/ r" g; _
$info = array(); 9 Q* H! f8 `, v% j; F+ W+ I# e
$debar_filed = array('catid','title','style','thumb','status','islink','description'); 5 ~- O$ s7 v# R5 Q
if(is_array($data)) { " s7 s& S4 f, d3 s, ?0 Z1 G1 b
foreach($data as $field=>$value) {
9 M( q0 T2 p R. Y$ j" K% Y if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 8 [! q# Q% l! R2 m0 q6 T/ b
$name = $this->fields[$field]['name'];
2 X+ N+ e- Z6 B! U $minlength = $this->fields[$field]['minlength']; 7 e' _8 M- e" N: j5 R: ^
$maxlength = $this->fields[$field]['maxlength']; * I, A- j5 b# w+ a6 c. q: R+ P
$pattern = $this->fields[$field]['pattern']; 8 X: Y4 X$ R, }7 L
$errortips = $this->fields[$field]['errortips']; + o z9 B. y& R
if(empty($errortips)) $errortips = "$name 不符合要求!"; ; U: }2 q6 g- |, N2 e
$length = empty($value) ? 0 : strlen($value);
5 o# p1 ~6 O9 }1 Z' S( |" n if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 5 F- Z! ~0 S, S i2 w$ N5 J
if($maxlength && $length > $maxlength && !$isimport) { . [- I0 `* y/ \6 P
showmessage("$name 不得超过 $maxlength 个字符!"); 1 |5 Z4 f f) c% K
} else { % S1 ]: @( y0 C3 u2 `1 A5 i9 ^; ]
str_cut($value, $maxlength);
- `9 u, ?! q5 ^- Z# S } $ x& K& x3 g; u0 X6 U* O0 S/ R- K* |3 H
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ; Y+ U( Q7 Z" a7 K
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 3 I+ F- u9 p. [8 T; a9 o
$func = $this->fields[$field]['formtype']; , ? \) r, e# w5 i$ c+ @; K
if(method_exists($this, $func)) $value = $this->$func($field, $value); 4 Q* ]4 g p+ H B4 E1 d
$info[$field] = $value;
, \1 e" c/ z8 w9 w2 E$ |) W/ E }
) i. g% s- l7 a$ J0 N o' A }
, m" ?5 `2 w5 s2 D# U1 G return $info; 5 U5 I- _1 J; J/ h* m C) |
}
7 e1 r. `5 I& d, ktrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
3 g; B' |3 T1 B0 @
: T8 O5 e4 }5 M+ D再到phpcms\modules\member\index.php 文件account_manage_info函数2 s& c u5 p* W) H+ g# E, ^& ^2 i( }% m
过了get()函数之后。: h g# q& g0 _- k1 x1 v& Y
4 T3 G# C9 G4 Q4 w
" a$ r- I4 q0 i2 z$modelinfo = $member_input->get($_POST['info']);
( Q: G1 V$ r' Z$ W7 K- M $this->db->set_model($this->memberinfo['modelid']); $ T% V' w% a" V [( j( @
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
" u2 w2 x# u- ~2 B5 x if(!empty($membermodelinfo)) {
) |( B3 W. Q. g+ B $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); z( r6 t a8 A6 k: g2 e! a9 u0 `
} else {
# \' `" n: N+ A$ Y9 P% R2 [9 G5 t直接带入数据库,update函数我们跟进看看
% I5 Z5 D+ H0 B' g* u- r3 z. l/ i; {0 a0 R1 E
: ~+ L# [. y+ l& p# U3 upublic function update($data, $table, $where = '') { 3 X: E5 w/ R2 T3 X3 j
if($table == '' or $where == '') { 6 A7 P+ v6 Y- s. o1 Q
return false; # a6 i" R9 S& A7 C8 Y
}
1 ?; f- J: J. n2 o( p8 j! w $where = ' WHERE '.$where;
) e8 m$ V' u6 i( x, f( M4 r; Z $field = ''; : G* p, Q1 c$ N% Z# `, O
if(is_string($data) && $data != '') { # d x- I( Y$ K: I0 G% _, }! ?
$field = $data;
6 }- d* {; j! V9 U; P/ u5 U } elseif (is_array($data) && count($data) > 0) {
5 o' a; T) x G8 V& E( _ $fields = array(); 9 E" X8 z$ h3 t; R* u0 \( ^) W6 a* A
foreach($data as $k=>$v) { 4 ^" p/ H" y$ M, [5 X' r
switch (substr($v, 0, 2)) { & k9 w2 A- k _& W* x: ^
case '+=': % ^" q! H5 f" S6 F9 T
$v = substr($v,2); , ?3 _* [1 x6 s( d6 @) S' v+ j
if (is_numeric($v)) { / i1 v+ Y3 M; Z+ c7 f
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
% L% i J$ H5 H2 {* } } else {
5 a6 z% V* a& P0 c+ ^ continue; ! H* O& P7 v7 w4 \7 O
}
+ p9 F( \! }4 e# i. y( y8 m& D2 `. M break;
6 Q9 H% Z c1 f) g, v5 B case '-=':
8 U0 G/ `4 R8 W0 P $v = substr($v,2);
7 x4 h# x1 Q; V& B/ O" q2 s if (is_numeric($v)) { : X% e1 ^" d) L% }/ b O
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 9 P! M: C/ F1 o e8 D, s
} else { 3 M1 @8 Z* D8 H- I) X h
continue;
4 F% s, B" ] a/ n9 E }
9 X7 t0 g8 ?* q& i break; 2 u/ l" Y3 P9 v8 J% {4 `# p
default:
; z' a; u& y, v% c $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
/ s: P/ T' u$ O; q( ?6 C& y, J }
7 F) R& e: t8 l: l( a8 a }
; u: t( L; E+ j' K$ p $field = implode(',', $fields);
; x/ F7 W4 _ J5 G( Q } else { " h4 L9 S$ l5 X, b. [. s
return false;
, H! j. J1 ` J# U1 d# R } : G4 C9 f6 n; w- g% R+ X5 t1 D0 u
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
5 w; j$ l8 D7 p- U! \1 G. D$ w print_r($sql);
0 b1 p" V# U. F return $this->execute($sql); # `/ Y6 B+ y8 r7 A* C9 ^" f9 n
} 3 \7 B7 b t: H' G# {) f3 H" J9 g
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
$ c4 @+ i- r4 z, Q* d( q- {/ L/ Q; j- C$ V
攻击测试:6 \* M$ K6 z3 ]8 A }0 E
测试地址http://localhost3 k$ |* H3 O( G: s2 s
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
: j: s6 s( ?7 w& x# X' V% j: |8 A
9 c* s0 U* b1 `; ? , E: Q) @6 x4 k2 e/ i' p
' f0 x6 q0 @0 u: f8 Q' i9 S$ x" U4 H5 [0 i, Q& v% t! n
4 D0 z3 q- K2 Y* f |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|