|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
+ D; Y+ H, C8 F7 z6 {9 i" Z. p漏洞作者:skysheep1 L, s$ [1 }* m% i
分析作者:Seay; P( B/ b) |3 N
博客:http://www.cnseay.com/% e9 P) H% }& w: N5 i! A
漏洞分析:
7 u5 s/ r% N2 `& a" S( d4 s* \ 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。6 a! `! I% e" u- f& k
% M# H( C$ T7 F& A, v7 L
l1 Y( s% `8 l+ z' y8 ?5 h
4 M# s$ }4 o4 T6 u: {- g- V) j$ T) Ipublic function account_manage_info() { : U, j4 V- o4 ?3 U: I, @
if(isset($_POST['dosubmit'])) {
* c1 Z' F( [$ L. q //更新用户昵称 $ m& X7 Z y, e0 w, K
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
. c7 `6 W( d; r( E5 { if($nickname) {
) b7 @( ^* i6 v0 v# a% S9 R $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
8 u: g# O3 W2 t# [+ U9 \ Z if(!isset($cookietime)) { . ]2 J; B! Y% A/ \& |3 l2 P3 U
$get_cookietime = param::get_cookie('cookietime');
. i$ j8 s5 P- O3 O4 `7 e+ K1 H1 _6 } }
* M9 ]: f! C; n4 ]* c $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); ) @6 P% ?, n$ a3 a
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
* P1 b# H6 Q: t/ M4 C param::set_cookie('_nickname', $nickname, $cookietime);
, @7 Y: H z! a7 B }
5 b: Z. m3 l/ w0 Y require_once CACHE_MODEL_PATH.'member_input.class.php'; ; P* o) b; O5 T
require_once CACHE_MODEL_PATH.'member_update.class.php';
1 w' R& X2 k2 p! P $member_input = new member_input($this->memberinfo['modelid']); ! z8 A( Q2 G8 _3 m
$modelinfo = $member_input->get($_POST['info']);
9 ]7 S) l) z: J+ [' }# { i $this->db->set_model($this->memberinfo['modelid']);
- z$ o6 V }' x5 f! c5 ^) [( a5 j $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
, l- l1 W) d4 R V8 \ if(!empty($membermodelinfo)) {
9 _6 n, J9 G; n0 A" _, t6 H' n4 a" ] $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
: W( b7 T M; \3 s } else { , k; ?8 a+ `! |. M2 t4 n% s, p
$modelinfo['userid'] = $this->memberinfo['userid']; , [- `: K/ P# x% I) k
$this->db->insert($modelinfo);
! W5 R# ~4 k6 U }
6 O! E `2 e. J. N4 @8 C. r; B: u代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,& v7 [" w; n+ g! o; `' h7 i+ U
在\caches\caches_model\caches_data\ member_input.class.php 文件中:4 P" g! \' \( B+ C; [3 x6 A
5 x4 h* @0 \2 R9 E; t. n
1 l" @, T+ E6 F5 t* f; @
1 i. `, Y8 w% Mfunction get($data) {
" r' k2 w4 Z+ b5 Q& ]8 ` I $this->data = $data = trim_script($data); I, Y4 u7 z. r5 }2 ]
$model_cache = getcache('member_model', 'commons'); $ `# i5 i( \5 g1 n2 ]
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; ! r$ e9 |4 D8 ~
$info = array();
% q' S6 t9 [) l# N7 o( t/ w/ t $debar_filed = array('catid','title','style','thumb','status','islink','description');
" c" H2 V# p% ]. s- G4 W$ I0 q4 s if(is_array($data)) {
! Q) N0 A V M- k9 w( c foreach($data as $field=>$value) {
$ m1 z" r; o* i6 @ if($data['islink']==1 && !in_array($field,$debar_filed)) continue; & n. I2 Z, S. g9 m" B8 Q
$name = $this->fields[$field]['name']; " N5 u7 `$ X- _: U. `4 j W
$minlength = $this->fields[$field]['minlength']; ! T2 {# b' @$ q4 }
$maxlength = $this->fields[$field]['maxlength']; 3 e" X# D) L6 K' d
$pattern = $this->fields[$field]['pattern']; + w0 A) H7 r s5 x7 t
$errortips = $this->fields[$field]['errortips'];
+ J" B5 n1 ^3 w& _$ c( D if(empty($errortips)) $errortips = "$name 不符合要求!"; , V; x# ]7 }! s5 ]4 \. [
$length = empty($value) ? 0 : strlen($value);
, e6 E) C# G: L* d# Z# o) m if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
- R# A: |# \4 j0 g* N' C7 Z8 a4 @# _ if($maxlength && $length > $maxlength && !$isimport) {
! @, \$ A; Y- y c showmessage("$name 不得超过 $maxlength 个字符!"); ; E7 p4 _7 A# |; E* N
} else { ! E Q$ n# n' A8 f
str_cut($value, $maxlength);
2 v, ?5 G+ C l, O" m } , j0 |8 v: i* x( k/ Q- n
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ' W8 @6 f6 J+ O( \. ^
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); - D8 [4 E* S1 t3 |: w
$func = $this->fields[$field]['formtype']; + m1 d' Y: C; w% A! H
if(method_exists($this, $func)) $value = $this->$func($field, $value);
% s( z9 Y7 p2 o H7 O* \ $info[$field] = $value;
! h7 X- S6 g7 V }
; Q) U" i' t7 P, Q! r } , v" Z4 Y& N, g' T& m+ @
return $info;
( P- H7 ]5 _3 j2 k U) E }
7 r/ H4 V, c2 n9 a# p8 p( g! Itrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
: t. _5 s" Y" _: @& @/ s; Q, m# B
再到phpcms\modules\member\index.php 文件account_manage_info函数0 n' e$ B4 r7 _- N- r9 d
过了get()函数之后。
# B- X2 W3 U: j t* R, f4 O6 }* w1 ~4 c( U* Z
8 Z$ c. n2 x, W4 D
$modelinfo = $member_input->get($_POST['info']); 1 m1 H4 [- @1 p, g, H; R
$this->db->set_model($this->memberinfo['modelid']); & _, Z- F; E, f( ~( E
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 8 W% \: W2 y- n9 L; O
if(!empty($membermodelinfo)) {
# _; r$ w6 [* z8 M+ g, O $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 7 V Z: k2 b, {4 O3 w, F( t3 X$ g R
} else { * S( f7 Q" T; m2 p: w& B% e( v
直接带入数据库,update函数我们跟进看看
9 ]' U# g! Q, R0 v/ s2 h
9 m$ q; @5 J+ A5 Q4 u' d. v
0 E. D# L( }" Bpublic function update($data, $table, $where = '') {
, ~9 X: r H5 c. m% ` if($table == '' or $where == '') {
! h3 s% C$ `9 [( V# p5 p! V4 D& ~ return false; & _0 I6 \# ?* k5 R
}
8 h7 T2 y% A: ]6 A1 ^4 B $where = ' WHERE '.$where; * } k" D0 s6 Q6 d
$field = '';
, ?% ` @* y3 a! W" |# y if(is_string($data) && $data != '') {
) O/ B$ \9 M, c3 }4 A: l $field = $data;
' q& j+ t/ Y5 S% u2 P! |# @ } elseif (is_array($data) && count($data) > 0) { Q# i( Q$ p, E5 ]
$fields = array();
3 g/ m2 D1 \) X( B c foreach($data as $k=>$v) { 9 V4 y# t% X! ^/ S9 j
switch (substr($v, 0, 2)) { * v/ o$ j3 r: t% I/ f# W
case '+=': " Z$ t2 U: f( [8 ^( |/ O# ~! u
$v = substr($v,2);
; M; F* g( @9 P6 b2 ~0 F8 K9 m if (is_numeric($v)) {
+ X. [# k& [# w- U; g $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
( k' i" e7 b# d2 x, ? } else {
, \# h. P w% ?$ p8 o, m- M$ g" }; ? continue;
& U, c& O6 ?$ y% q6 R }
0 S3 R7 C v$ h* F break;
* \+ n. R* h# L- G1 N- a9 T case '-=': [+ N# e: Y: n: b7 n" x7 x* ^
$v = substr($v,2);
* i2 B: z' R8 R/ {" L' M! T3 C if (is_numeric($v)) {
. f/ l( @/ j$ m7 B $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); ) |& ^8 T% Q- K- @& m; ^
} else { 5 [1 c0 I! K9 ?- _: J+ j1 K
continue;
* A/ }6 C# V$ X: W& |) }% B } * I2 C- |5 E5 F0 P/ U- A. m
break; / D& @5 u1 A2 W( K, e3 d/ p
default: / K+ p3 ~2 ]( X9 c
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); : v/ L8 y" G0 o" \, |! g4 K
}
7 N6 o# u" v$ L5 s$ V6 n* g }
& l" ^# X, y" Z+ ^ $field = implode(',', $fields); ' h; a8 m/ U3 _, O, H0 y
} else { $ @3 @2 I; `5 Z- _; Y
return false; & D- P) [ x9 d
} 6 j/ [ \! c6 Z- k* S( F0 j x
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
& A- g% a- X* K$ a' r print_r($sql);
- q" y6 C+ @3 I0 @3 y return $this->execute($sql); 5 ` Z1 u0 [6 Z& {1 v! `
} 7 y& Q7 |1 ~. L# ^9 l
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
, t, P4 h) q8 m) V$ x
6 L! ~: ^8 y8 W' z1 g攻击测试:
) G) ~6 Q% Q$ T测试地址http://localhost
! m3 b6 m' W* _( q- L" A 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句0 h/ ]' N! e7 u+ T' a
& p4 N# o! e2 s0 P& f& ~
( s, O, N1 H% F7 j; ]3 c" d% q* c D% O! `. K4 _ K
4 S+ b. S! J/ \' r: K9 e0 v. E; t4 o8 _
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|