|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告, M' y$ k1 |, x. k% a& S
漏洞作者:skysheep
0 x5 B1 {. J* [- c- v/ f) C$ h分析作者:Seay8 @3 [" t1 X3 M% J. b
博客:http://www.cnseay.com/' Y6 \/ S3 _9 W3 G! l6 g
漏洞分析:4 i' @2 L1 |5 I/ t; F
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
+ V" v6 U, d5 e3 e! U7 B' v
) L3 M; y0 x/ E" A
8 L% J/ l3 b) U ( n! N- w7 E0 y- h& g) ~
public function account_manage_info() { / v+ A, J" Y% _' g+ a% }
if(isset($_POST['dosubmit'])) { 3 |! m0 R( k9 Z% }& s
//更新用户昵称 / \6 u+ N, c) s% [
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
9 P' z- r. ~6 W if($nickname) { 6 M" { E: @2 T9 _
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); $ e9 v: k/ Q5 g" t) H
if(!isset($cookietime)) {
, `" m1 L1 m+ m! l% d8 ]9 P $get_cookietime = param::get_cookie('cookietime'); ( v7 j! q8 Z v) ?; y! F
}
7 L7 w% J7 O/ s $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
( \9 \8 h f+ T2 H2 T$ c# ~ $cookietime = $_cookietime ? TIME + $_cookietime : 0; 5 H: o: b: l" C8 t$ M
param::set_cookie('_nickname', $nickname, $cookietime);
4 U9 J# W; H8 P3 y0 P } 6 Q" y( d! r/ u8 I$ k
require_once CACHE_MODEL_PATH.'member_input.class.php'; 1 l$ K/ p* z% p' V
require_once CACHE_MODEL_PATH.'member_update.class.php'; " {3 v5 F1 e) Q' D% D7 l# M
$member_input = new member_input($this->memberinfo['modelid']); . S( r5 P, Y- {
$modelinfo = $member_input->get($_POST['info']); 5 y' u, |' T4 u
$this->db->set_model($this->memberinfo['modelid']); 4 y& u# W8 o, r7 Z3 r+ V
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); / w/ b. s6 {5 \ G* t/ d9 N
if(!empty($membermodelinfo)) { / `) |2 f% J) \ ^) ]
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
7 n+ W. M) |. y% @2 ~' y+ t } else { - J9 t* Q8 Q! ~1 E+ G2 d
$modelinfo['userid'] = $this->memberinfo['userid']; , X; c* o0 S4 L1 d8 x$ [
$this->db->insert($modelinfo); + l- [$ V& m9 x& J3 S& l
} i8 a9 I, q! c! j# C z( J
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
% X- P0 @0 y$ E: ?% z& S$ U3 P2 T在\caches\caches_model\caches_data\ member_input.class.php 文件中:
3 r9 E4 O) m' A4 \9 Q) ], y
: Z* _1 h0 |# }# v+ Q, S, e/ a
3 u/ q5 Y; D$ n }- l6 D: l / y# E9 }+ T* q. ~, b
function get($data) {
' h# L4 U4 k& Y3 Q. F" o+ b/ H( g# L $this->data = $data = trim_script($data);
" ?0 P& f1 G l, m8 y9 M2 x) q $model_cache = getcache('member_model', 'commons'); m8 L7 |, H; ^# I0 V1 N; I
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
5 _/ ]; W, L3 }1 |, n $info = array();
$ ]- q: J& e7 q/ B" n3 g: M $debar_filed = array('catid','title','style','thumb','status','islink','description');
6 M4 T; f: E0 U7 x; F if(is_array($data)) {
0 b5 z3 c% J7 f4 D1 E4 |9 \ foreach($data as $field=>$value) { 2 G* R- B; d/ | b# p
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
* t- a6 \; Y3 A) Y4 C: ~ $name = $this->fields[$field]['name'];
3 \1 a, \$ ~) ?7 ^ $minlength = $this->fields[$field]['minlength'];
( j" A9 F, }2 n i1 e $maxlength = $this->fields[$field]['maxlength'];
' C. i) N, @+ z: w. w5 T. c) v $pattern = $this->fields[$field]['pattern'];
4 C4 d3 D" |2 {/ h% }( p0 { $errortips = $this->fields[$field]['errortips']; , i' S3 K& {' k- t
if(empty($errortips)) $errortips = "$name 不符合要求!";
+ ^3 W X, ^; p) l2 [. n2 } $length = empty($value) ? 0 : strlen($value);
3 O' e4 B; o$ h# j% b6 M if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
6 l P: ^7 F1 W% f/ I( V' w7 \: V if($maxlength && $length > $maxlength && !$isimport) {
. F! a; V# _- L, C2 x% u! r+ e showmessage("$name 不得超过 $maxlength 个字符!"); 7 I2 }7 H6 _% }6 y
} else {
& Z: h3 v4 ` b6 n7 j str_cut($value, $maxlength);
2 [" X- L# C- q- @" h } . _: p. T; m; {0 e; e5 n7 S) j
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
; q3 j1 u9 }( J4 y$ x if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
1 A8 E }6 v' ]9 e $func = $this->fields[$field]['formtype'];
: t+ Q$ l$ }8 C; j) j if(method_exists($this, $func)) $value = $this->$func($field, $value); 5 x* o5 i0 s. W3 @: k/ u, F0 F2 C
$info[$field] = $value; - t9 _' ^% q2 `6 N5 F
} + O$ O7 J8 \. t0 I6 b7 l: T4 D
}
+ c( W, B* O( @! V" n* G return $info; 9 K' w4 ]9 G0 e f* a8 I
} ( p& u9 c$ b$ @7 W" a4 A2 N
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
& ~/ n0 O [/ d( J- V
, l. x/ d, ~6 j- X再到phpcms\modules\member\index.php 文件account_manage_info函数
- ?$ {6 D1 s: U$ g5 `3 m) p; M过了get()函数之后。0 t' O A$ p1 j- b2 o4 U' l( T8 n
7 q# h! R' s. a! z; _; s& U
- \3 D' y* m+ C6 i; O8 ^$modelinfo = $member_input->get($_POST['info']);
' Q% ~ Y) D3 m+ x; G6 O$ @7 r $this->db->set_model($this->memberinfo['modelid']); " Y( t$ z9 t/ ?# l# D$ o
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); . L1 `/ C2 @0 u' F
if(!empty($membermodelinfo)) {
/ J2 d |1 q o/ S7 f, y $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
0 b) V+ I: ]# C8 `4 I } else { 9 g; v1 H! e' l+ `
直接带入数据库,update函数我们跟进看看
9 C3 i3 B' J* O: N: u
( x0 U- S+ A% o) Z: c9 S
- s, G/ f8 I, [$ ppublic function update($data, $table, $where = '') { ; b. ^7 V1 L3 C6 T* ?
if($table == '' or $where == '') { 8 k# e. [" b2 ?; b2 Y9 R+ L
return false; - B1 F, f5 [( C8 F1 F c
}
: Q: [8 ?2 X) M4 y: Y* I $where = ' WHERE '.$where; 7 {/ u& |% `' ]- S$ G
$field = '';
% k- _" ~/ ^3 k% A8 l) p6 w% z if(is_string($data) && $data != '') {
/ J( t7 T9 r( j- H8 O $field = $data;
; B, k* U6 ~; M0 U } elseif (is_array($data) && count($data) > 0) { - [0 } a! f' [4 l
$fields = array(); : r2 W4 n" q1 ~% y- E" \
foreach($data as $k=>$v) { ' v9 e2 ?% ]% P
switch (substr($v, 0, 2)) {
3 R ^6 J! K/ j) m5 A$ E5 U case '+=':
6 ]6 q3 M1 N4 j8 H1 k9 r+ O $v = substr($v,2);
! f, E6 ?$ R9 R, A- n) B# C if (is_numeric($v)) { Q* S; M( k6 t; @# Z/ A
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); , k, B, w. n; F/ q
} else {
5 H' x: Y* O1 R- j V- Y t continue;
& Z% e6 \7 O8 f2 O } 4 U) {+ J! |( H* {
break; # u: O# N& {$ g. d, Y
case '-=': 3 z/ r2 a0 Q' A& k8 }
$v = substr($v,2); + h# j) o7 m# a2 k
if (is_numeric($v)) { - _% O/ }! Q. l
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
4 d6 Y# B" U$ U, Y$ Z- p. x( t: Z: M, e } else { ' b/ O, [; U1 K6 |) Q; I- w' L. p8 l% f4 i
continue; 6 D& ?( G" Y. _+ b1 a+ D$ n; b. B
}
% c. t6 L6 S/ Q- u, \: u break; 1 r# _* y& l% {% _3 c
default:
3 n" m' C- d" u+ _ $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
S. L/ A6 u8 Q5 ~( V! j; X* v& ] } ) v' G2 a/ f& @ Q3 N0 X
} 6 i& s" U6 s q+ {! [
$field = implode(',', $fields);
- s" U! {$ M- j } else { , b) n5 S. ]6 S! J, E
return false; 9 `6 S4 G' ]' {6 h! y- w5 S* A
} 0 J2 j1 y, _" c' }
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; / B) ?& ?: e0 o/ t
print_r($sql);
7 t" [9 }$ ^! ^5 L/ ~ return $this->execute($sql);
/ ?8 v# Y$ z3 `: F& H5 u6 x } & N( S4 f s. {7 ]4 _6 u/ M
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
- Y I l$ ~" N( P' \; B+ m
( @& \* F. t. R- F6 g攻击测试:
9 O% i' k3 [. l+ Z! j7 Z1 {测试地址http://localhost
( h0 ~1 O/ b. g6 J7 c* } 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
; i" n( u9 h$ c7 z* ]! ^
8 |! H; R; h9 @% g 8 L4 D# |4 @$ b* z, a" N
0 b3 G. m. ?* n# i8 J7 X$ q0 j9 Y1 x/ K$ _* {, K
, d; I7 x: L1 E% V/ E |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|