|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
, a I- ]9 A8 t& A/ }漏洞作者:skysheep1 e& G) U! B8 g2 o$ n u
分析作者:Seay+ {1 }6 R* B( I$ [
博客:http://www.cnseay.com/
8 l% w1 M0 P. B( [漏洞分析:
$ }! }: z; m* F" E S8 m$ L 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
% `% \( J/ H3 k: L
" O# [0 a( g+ A r) ^7 C, T2 G5 G$ h" w1 k6 `
" h0 }6 Q" I* f1 q
public function account_manage_info() { 0 _: `7 U7 _5 P/ j. z
if(isset($_POST['dosubmit'])) {
t& a* y0 P% {5 z. s; j# Q //更新用户昵称 : F+ |+ Q# R/ ^/ ~. D& u- b. c* h
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 8 W- g5 G$ V% E9 n* w
if($nickname) { / I( \( D0 g3 o( o
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ( S ~- |* |, j/ g# V0 a3 G
if(!isset($cookietime)) { # k' B5 M! Q) G1 `2 W
$get_cookietime = param::get_cookie('cookietime'); & L5 g( g1 u' a* ^3 o
}
( |6 M1 J8 o4 g6 T $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); * E5 }- ?! v( C2 R
$cookietime = $_cookietime ? TIME + $_cookietime : 0; * b$ g8 ^7 Q# D+ O
param::set_cookie('_nickname', $nickname, $cookietime);
5 m$ S* F+ S! N0 M5 O% p( j } 9 A5 s) y- w, T. X4 e
require_once CACHE_MODEL_PATH.'member_input.class.php';
# m! g- A( o1 I9 k0 }: {. `% E4 m require_once CACHE_MODEL_PATH.'member_update.class.php'; 2 t% ` m: i+ t/ g" |- c
$member_input = new member_input($this->memberinfo['modelid']);
' l4 |9 y* L: l; I+ ] $modelinfo = $member_input->get($_POST['info']);
% n8 @' _$ d/ z" f2 z7 K: u, H- g $this->db->set_model($this->memberinfo['modelid']);
7 r5 i; B1 k4 t4 | ^+ y $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
- c9 Q8 z3 r, Q if(!empty($membermodelinfo)) {
) G! E/ X6 m. g/ H% `/ b $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
* S1 I8 s) w8 I, D3 o7 j5 j } else {
, H* m* e- o! P1 r- J $modelinfo['userid'] = $this->memberinfo['userid'];
4 L% }& b6 n1 V# C7 A0 ~ $this->db->insert($modelinfo);
- |, z0 b4 C, G9 I! W/ X: @# x }
% { Y* D5 ?6 M- u9 o _代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,' v; ]8 h! s, I, a# I3 s0 E5 O
在\caches\caches_model\caches_data\ member_input.class.php 文件中:) m/ Q( G# Q) \2 W& _3 K1 j
9 y) t: V4 k) [4 q$ X& I
0 j0 p( \6 f$ n. R
! _- e% G9 F/ Qfunction get($data) { * _" o% \# v; u/ t. D, X2 ]
$this->data = $data = trim_script($data); * {* m( I0 j3 k
$model_cache = getcache('member_model', 'commons');
$ |" Z# i1 L- Z# \$ `" ] $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
8 v& j' p2 a, I6 F. J7 N $info = array();
! v! O; l- S. X& s* S $debar_filed = array('catid','title','style','thumb','status','islink','description'); 8 l" G# r0 |, s3 L" t
if(is_array($data)) { 6 i- j/ @% b7 j/ g" ~0 u4 e
foreach($data as $field=>$value) {
3 z0 T2 a9 R6 b# @/ f$ A if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
( |) {4 ~7 |* n, [4 ~' r0 q $name = $this->fields[$field]['name'];
2 Z4 S* b* S- ]: e+ @# a( { $minlength = $this->fields[$field]['minlength'];
# C% L* @$ p$ x$ N% o/ ? $maxlength = $this->fields[$field]['maxlength']; 6 a" r0 _! J0 z4 w, P$ j
$pattern = $this->fields[$field]['pattern'];
8 F' p+ M$ S. I, X/ v, N' I2 T/ ^ $errortips = $this->fields[$field]['errortips'];
+ o+ b& G. V7 I5 F) O if(empty($errortips)) $errortips = "$name 不符合要求!"; 2 U1 j5 {! p e* h! e$ O4 ?. p
$length = empty($value) ? 0 : strlen($value);
; y& g8 t& {/ |! p/ B% E) a if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ; {6 |6 I7 M5 L# P
if($maxlength && $length > $maxlength && !$isimport) { 5 s/ B0 Q; r# p
showmessage("$name 不得超过 $maxlength 个字符!");
8 \" a: f5 D# A6 w+ L$ ~ } else { n2 Q" h7 D+ R0 M
str_cut($value, $maxlength); 5 g8 U8 W( h7 l! ^, h
} ( ~2 N( _& M- O9 I
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
% y: G3 K& b$ _0 e, G8 }' [ if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 6 d5 `( m- G! j; T3 M8 \! ^
$func = $this->fields[$field]['formtype']; 6 Q' i" S% [. i$ C
if(method_exists($this, $func)) $value = $this->$func($field, $value);
8 S8 P, j H, H! X+ B $info[$field] = $value; & e. m8 x! J m* w5 N& S
}
: M/ ?( l/ ~2 }& { }
& J! F3 p0 T4 m return $info;
0 g# o- `# M1 \9 U }
( o4 Z0 X8 Y* L5 `8 btrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,# B3 u1 F# x; U
6 _3 `" K, W) A& y6 k9 `% k9 p: q4 W- ~再到phpcms\modules\member\index.php 文件account_manage_info函数
V3 ^% K& l: J+ |' @3 [. }过了get()函数之后。; X( ~2 g# Z X. a
) P9 a3 U' {/ f! s5 L3 _" z
9 d. U! f; S# N2 ^% M
$modelinfo = $member_input->get($_POST['info']);
! c v- p1 k/ c $this->db->set_model($this->memberinfo['modelid']); 4 J. X& D% P- z
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
1 a+ t& ^! @" r2 K/ ]8 x' @ if(!empty($membermodelinfo)) { - V: P% l: P J" M; }
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 1 D2 ]* N; A# I5 \5 a. `5 Y; }! l
} else {
v( P3 \; D3 T/ V+ c5 K直接带入数据库,update函数我们跟进看看5 d3 B8 K4 t/ `. g. N. }
4 {3 M' ^1 T5 R1 {
! s, s5 x5 e5 N ^+ U
public function update($data, $table, $where = '') {
4 S& w. ^* }% G$ k# N if($table == '' or $where == '') { 9 E0 g' Y2 \( d. p! m u
return false; 6 \5 R$ {8 j5 w5 j. g- V
}
/ \, F6 f/ b. Z" D3 Z9 ]1 e! \ $where = ' WHERE '.$where; % l, E- [" S b; m: R
$field = '';
: c! l+ {- q& |& Q2 _( Y if(is_string($data) && $data != '') {
' b! t7 @9 I$ k+ f# B $field = $data; 0 u$ F0 W2 Y1 P( l+ k4 D( W' A s
} elseif (is_array($data) && count($data) > 0) { 2 w* e0 {; J: B$ G1 Y# s) R1 p2 e
$fields = array();
0 M8 t* S7 k- t, Y9 j; w6 W foreach($data as $k=>$v) {
2 B& A1 e' d2 ~4 k, { switch (substr($v, 0, 2)) {
9 E6 L: A8 {+ @0 \: F a case '+=':
0 I! ?5 L7 N( K/ B" T $v = substr($v,2); * ~8 E" S2 `5 F, c g& ^
if (is_numeric($v)) {
7 i: q {, d5 L/ T4 N2 ? $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
2 u0 |' V: m' {( }7 U% I) o" o* U } else {
( ]: e9 \9 M7 z9 Y6 y continue; ; [4 f* ]" }' [. y
} 9 p! v9 N! F2 u# R8 m; I
break;
3 t2 z9 U1 N& c |) x* _ case '-=':
7 t: }" x2 ~* w2 W2 n P5 l $v = substr($v,2);
' u3 I( B& X$ t4 A1 u) s) u* t* B if (is_numeric($v)) {
5 D: G2 q1 ~: Y2 o9 f" c $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
9 y0 w% ~+ O M) _ } else { 8 X/ | c, Q# U i2 \
continue; ' L& c& N* {; a
} - V% V0 J% W$ f
break;
8 }0 k/ o! E* f default: $ n: i K: w* y4 D, Z: B5 e2 R$ x
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); ; q/ f9 S! |% i
}
) L/ L" E! ]8 t+ d/ F1 ] } , G% N* U7 a5 L6 f; c
$field = implode(',', $fields); * J$ W) `4 D8 c
} else { ' O$ F7 R6 x) N
return false; z9 F& z8 k* Y; G* c* l2 T
}
* m; t) ?4 k* M5 w! L' V $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 3 P# ^: V# f$ U0 P4 o( H
print_r($sql);
9 G4 L2 ~& |# h7 k9 F. n& X0 M return $this->execute($sql); 5 G# j, j0 h+ a% @8 |8 H4 ^
} 5 b2 C+ r3 c! A+ M
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
) g* N0 Q8 M: A. V" p0 }# _3 ^* v1 \" b# z" y6 c9 D/ X0 Q7 o
攻击测试:
; r: Y3 a7 Y6 A1 W测试地址http://localhost h( v; Q" D- M
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句3 N, ~# j. d; O4 E
) [+ q J& U7 Y- x2 F
( o, k6 ^" N. f |+ {% h: n$ T% {) z8 c5 y
5 x1 Q1 Z5 S% o6 G7 U4 ?
* d& }4 N) N7 w. }' R
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|