|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
8 O& k" f* C$ B( ?9 r# ]' t漏洞作者:skysheep* v! k! M) [+ r) `
分析作者:Seay3 t* W0 e' R' C+ {
博客:http://www.cnseay.com/. e3 c* L* ]0 \2 E$ ^5 k8 ?
漏洞分析:, K# A5 _. n/ ^ w9 l$ m
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。- ^5 Q( p$ r3 I% L. n
1 v( A( {) E% L. h1 ]/ v7 g& n' w W% X% y: j I
|: u: |4 I% m' R
public function account_manage_info() {
; o( N* j1 Y: J2 O if(isset($_POST['dosubmit'])) { . T/ J8 [" F1 U4 J- C. B
//更新用户昵称 U( d1 p) V7 B+ P
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 2 x( @! o; [) J; m; @2 D8 s$ q
if($nickname) { 0 Q- Z$ j! s7 C: \! q
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
7 O6 b/ R8 D# l/ B8 M7 P' g& I: k if(!isset($cookietime)) { * H* }* m( V- \" n. ~7 O2 _& q! C
$get_cookietime = param::get_cookie('cookietime'); % `) z: h' m$ g0 [7 r7 r. G
}
" O# s, E( K5 s# |& t/ }& C $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
, Y X; ?; u" T& }# T p: Z0 t $cookietime = $_cookietime ? TIME + $_cookietime : 0; 7 M9 F3 V5 {/ m( f5 ?& i
param::set_cookie('_nickname', $nickname, $cookietime); 5 n% I' ]1 y+ }0 x ^8 F C6 K
}
; y) o6 r' M2 y2 Y6 Q2 s: P require_once CACHE_MODEL_PATH.'member_input.class.php'; 6 i8 h; Q& ]' A& L3 B3 w/ H7 Y
require_once CACHE_MODEL_PATH.'member_update.class.php'; ! Y, K# z0 u" k. ?; ~4 k& O
$member_input = new member_input($this->memberinfo['modelid']);
4 E3 E. y* E( T0 a $modelinfo = $member_input->get($_POST['info']); / q. {& X* d9 K1 a$ b
$this->db->set_model($this->memberinfo['modelid']);
$ n+ e2 g& J1 ]) x" G# `* { $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ; b( S/ D b9 s& J" a
if(!empty($membermodelinfo)) { 0 u' n1 g# B0 s Z1 C% w
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 3 u' z7 Q) I8 s' D- d* T
} else {
& g9 ?; ?/ N: n- F7 |) n6 e $modelinfo['userid'] = $this->memberinfo['userid']; * H& F8 Y( g- ]6 D6 C: L# h
$this->db->insert($modelinfo); + O9 Q! W/ T: D- ?
} 7 l9 y/ N7 l8 y4 z" Z/ Y8 m# A4 T
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
1 X7 v9 b) U* l' l: [在\caches\caches_model\caches_data\ member_input.class.php 文件中:! w3 \; N7 y, k' q2 Q! \7 [
6 s7 n, t; H: J* P- x
3 B. k% `4 o1 c/ F4 k: N7 R
- U9 N+ t0 s8 }, Y( R( Q2 ffunction get($data) {
3 M5 U, y7 k: t5 i: O! o $this->data = $data = trim_script($data);
$ j* x. o I% O$ d $model_cache = getcache('member_model', 'commons');
- G. \3 s! X0 {# \5 d' ^, ?# w $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 9 O( ^+ x" K9 A% B1 ]
$info = array();
- ~+ f7 Y# H' v" w $debar_filed = array('catid','title','style','thumb','status','islink','description');
6 w7 o9 F. M; b' i" W if(is_array($data)) { c% V! @8 {( l- s: c" ~
foreach($data as $field=>$value) { : w. m1 R5 y1 p q3 d! h
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
5 q# r8 Q: A! P% J/ n' N $name = $this->fields[$field]['name']; . q9 u- W0 C( }$ L8 t# j: M0 f
$minlength = $this->fields[$field]['minlength']; 5 o/ J6 m/ l9 y; J& a2 X1 Y- [! O& w
$maxlength = $this->fields[$field]['maxlength']; - Y5 V. K, r" L/ O- u9 {
$pattern = $this->fields[$field]['pattern'];
! _! A8 P7 Y/ i# B) L/ l: e- g $errortips = $this->fields[$field]['errortips']; , g% p$ M2 F0 ~( h( a
if(empty($errortips)) $errortips = "$name 不符合要求!";
) X& O. q: n% V/ M $length = empty($value) ? 0 : strlen($value); , y( U/ v6 p/ |0 {( Y8 O: V) u# ]
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); f7 y% b% h/ D) }5 d$ _4 n$ M
if($maxlength && $length > $maxlength && !$isimport) { 9 [. q( f5 `/ p7 }
showmessage("$name 不得超过 $maxlength 个字符!"); 1 T, Z% N0 w' g4 n
} else { - f" R) Q e( I$ R8 \/ c
str_cut($value, $maxlength); . ~) k& j" i7 C% u$ {' P9 @& k; Z, s( z
} . y. G( T! ?+ @. I/ C2 k) ?% q
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ! X- j7 U( d% Q; c; N) d; Q
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
* J6 D4 M: Q. d3 n& V+ l0 z $func = $this->fields[$field]['formtype']; & D' v, [) ^" L K1 R
if(method_exists($this, $func)) $value = $this->$func($field, $value); 4 A' g; j( @4 t7 p) }
$info[$field] = $value; 9 R( V& G6 B0 o! J
}
1 K6 f$ Y, } ` }
0 Y6 u( e0 W' @ d6 I' c, o return $info;
2 m" ^6 m# s$ p" f7 r } . p& X' V. i; t$ `, y
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,7 q1 J/ P' M3 @9 T
, g' u& Z5 b7 ` G" S
再到phpcms\modules\member\index.php 文件account_manage_info函数+ w6 A' }$ {* U' L7 k; x. K' f
过了get()函数之后。
' C& p9 i2 L) A4 |6 }' {7 ]( h C& L& M; [% j4 q* e8 @+ h
8 d- D& Z$ |, }
$modelinfo = $member_input->get($_POST['info']); - d% b4 M! C' e5 [1 i
$this->db->set_model($this->memberinfo['modelid']);
$ V# b( P9 h& j' A, W$ }7 i6 l $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ' k& C$ B) z* L# T5 `
if(!empty($membermodelinfo)) {
+ m$ G; b3 Q' d0 U $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 7 I# p; _" R+ u: R" n9 E5 _
} else {
4 j3 A, n4 w7 q直接带入数据库,update函数我们跟进看看# }/ W: O" l+ h# r% h, p, A6 J& Z
* p" w7 j! ]+ w' T( d9 ~ + U1 d2 X1 a& j- J
public function update($data, $table, $where = '') { / Q+ a3 D6 T- I
if($table == '' or $where == '') {
) Y( B( n! V3 M$ k8 i: g& a7 ~ return false;
$ {, W" Y9 T0 ~; k; I8 A+ Z }
2 |9 S0 o: ~+ ^" t, g. k; | $where = ' WHERE '.$where; * _$ x2 s/ j6 c- _
$field = '';
$ D8 I7 b" ?, [, v if(is_string($data) && $data != '') { - a$ Q) S/ R. |% M8 ^0 d0 P% G+ o( f
$field = $data;
# S- ^) z/ J. p( @! I } elseif (is_array($data) && count($data) > 0) {
2 N! W+ y; G2 u* D $fields = array();
1 z! _& K0 F" Y% J foreach($data as $k=>$v) { 0 v% g6 K# M2 D% f, y
switch (substr($v, 0, 2)) { F: R+ v' R" |6 v, M7 \
case '+=':
* F2 N0 D; Y, I0 c9 Z* X $v = substr($v,2);
- w" G" P0 S& ?8 N+ h1 J; K( W if (is_numeric($v)) { . n4 h' Y2 N& E0 C, D5 S- U0 c( N' W
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); ! c$ v" E4 u2 H9 @) c
} else { : ^3 }* j# e A
continue;
& J! h8 ~2 a! F- O } / u# w& B# P4 a. B! z. U
break;
3 w/ \) E7 O4 G, S+ A' X" u7 B( @ case '-=': # V+ Z( p9 L) t2 E
$v = substr($v,2);
( s! D/ E! ~2 E2 H6 e% ] if (is_numeric($v)) {
- G, i+ L/ Y, e0 A/ O) O& E $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
. B0 ?1 L% B+ Y) a/ \1 g } else {
|* ?+ A, ~* B B. N! I# e continue;
& j/ X* _1 J2 B# J } 3 V* k: V" Z* Z+ C. `
break; : d7 w! V8 Y7 J# L! K" G. r
default:
+ _ |. R/ d7 U+ {) O $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
4 u/ F0 ^5 s! ?4 y7 D9 X }
! R4 Y9 D1 C- H5 h7 ` }
7 T1 V0 R( S( n/ k1 v" O2 i $field = implode(',', $fields);
1 x0 g! L1 J/ ^: j } else { / g4 P# D+ _- H% y1 _4 l
return false;
5 z3 |, f m" d [- {/ L }
; g% |3 W6 ^" D8 F6 V $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; " B4 H. G' c# J( F j8 a* ]; W. g* Q
print_r($sql); , P" Y0 b6 _5 {7 A5 \
return $this->execute($sql); ; @* S/ D/ T0 F ?# w2 b5 F
}
& K% Z6 a5 P. ~ `4 b$ [" |从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。, |7 f P; q5 H' @5 |1 a- y
$ Z7 C+ p) k3 ? m% m0 I攻击测试:+ `9 S) d# R* C
测试地址http://localhost( X, z7 b( x y7 W% w
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
* k! o. P! R7 |5 `6 Z8 \
+ S6 R/ H+ T C6 L% ?0 ], W5 c 9 |: z* V- K! k
& E8 A. n. ?* P3 T. h. h. I3 J; ]7 n- x" j" `7 Y6 v, E
1 e) ^. q/ f% f. {$ L) Z) ^ |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|