|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
4 X9 S- u) M% ^% c+ `漏洞作者:skysheep- r$ z- e0 J* H! X- I
分析作者:Seay/ y# A- Y) y! {( {) w& a8 T
博客:http://www.cnseay.com/0 b- t* F+ }8 H* c+ O
漏洞分析:
" v' h0 P# `' L0 X1 B' \ 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
4 q% t' a$ O" ]- R0 H* \3 L- [$ ] B$ L- ~( _! @+ Y
* D+ u7 ?( s& X4 V
q+ f6 _/ _! ?2 P3 w1 c& _4 P& Vpublic function account_manage_info() {
) G- W# Z( f2 ]; e4 ]7 p if(isset($_POST['dosubmit'])) {
9 k r5 O# L/ Q. T. v( J' s+ d //更新用户昵称
) M$ w6 [7 N- H% K) |" O! B $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 5 o% H; H% }) ~; @2 v6 C
if($nickname) {
9 d, ]$ |' H L% d- o $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); * u% W! _7 V# U, o4 C$ W
if(!isset($cookietime)) { ; v3 |9 c' R* J* c
$get_cookietime = param::get_cookie('cookietime'); . P! u0 _- A7 [" P1 W
} ; |/ m( q/ c, |/ ?. x3 ]
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 7 R! ]& Q: ~# `# ?5 k
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
4 d( \8 g# f7 T# r% d) x param::set_cookie('_nickname', $nickname, $cookietime); ( K3 j8 K; }5 D
}
. O% g7 x- R5 y# x! s8 x8 D! w, ^4 A require_once CACHE_MODEL_PATH.'member_input.class.php'; - k3 o6 a i+ P, A
require_once CACHE_MODEL_PATH.'member_update.class.php'; $ Q+ u! w5 P- L! F1 i
$member_input = new member_input($this->memberinfo['modelid']); , \0 d2 Y* Q, Z
$modelinfo = $member_input->get($_POST['info']); - t) g/ I" K) M; i7 W
$this->db->set_model($this->memberinfo['modelid']); 4 |4 ]7 _# X u& Z4 j
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
. a* t& U9 j0 w/ T: t6 ~, d if(!empty($membermodelinfo)) {
& c: k. ^, x$ m: t! X( n $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 3 f) a C8 d! N2 _! @( I
} else { # g5 v; g3 b' A4 P% X
$modelinfo['userid'] = $this->memberinfo['userid']; + p! _6 ?: M& W2 F' z& M' D
$this->db->insert($modelinfo);
- N& ]1 {' Q! c } 4 E# S: ~5 r( m: X" O& k2 |5 ^
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
* S+ V1 ]& z9 |1 |) ~" N6 j在\caches\caches_model\caches_data\ member_input.class.php 文件中:8 K9 N! g8 c9 G7 ~ K+ z( s
% J. y9 d2 z1 M$ x4 ~0 ?' d9 P
O7 T) e1 A* y& N: e: N! I- l
0 l8 f2 z3 R! G E+ W+ g- @* Lfunction get($data) {
6 T) U+ N7 L* e; K $this->data = $data = trim_script($data); 2 W, H7 u7 e$ D) I& ^4 }* M" S/ ~7 j" ?
$model_cache = getcache('member_model', 'commons');
* y+ O$ o# J- B $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
" k% i" g% h+ _3 J z. M $info = array(); % R. i- q- R$ C% F1 Q
$debar_filed = array('catid','title','style','thumb','status','islink','description'); 2 E5 D% H/ C7 g6 S$ {8 d9 d `
if(is_array($data)) {
* _! o3 K7 m7 S' }- {$ e foreach($data as $field=>$value) { 1 X# I6 u! a7 g2 f$ u7 p! p- }
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
. \2 P$ L+ T, p d3 ` $name = $this->fields[$field]['name'];
1 l" `% Z5 m, M3 r0 E $minlength = $this->fields[$field]['minlength']; ' {8 R% W3 `# N9 C* n
$maxlength = $this->fields[$field]['maxlength']; & v7 U; [0 f5 U2 |! ]
$pattern = $this->fields[$field]['pattern']; 9 u8 s- V* J# _9 `
$errortips = $this->fields[$field]['errortips']; \1 [) W% g ]* p% O# c0 O N5 D3 E1 g
if(empty($errortips)) $errortips = "$name 不符合要求!";
( v& g) y7 U6 C $length = empty($value) ? 0 : strlen($value);
6 D$ R& o: `1 h3 h if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
+ I* Z6 u6 g) t2 r0 t" V if($maxlength && $length > $maxlength && !$isimport) { 7 i- h; a4 z0 A/ T. f
showmessage("$name 不得超过 $maxlength 个字符!"); 9 h) D! Q- H7 l9 j3 U
} else {
6 n$ {: o8 V. H9 i9 \ str_cut($value, $maxlength);
I* E' [: w7 m } % N0 z5 E+ u! Z0 F4 a* M$ X
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 6 T6 {$ G# q. Z1 g7 |
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); ( M* z" h' C4 @% [8 Y/ R6 S5 J( w% h
$func = $this->fields[$field]['formtype'];
; Z+ _. D: B+ {# v7 [ if(method_exists($this, $func)) $value = $this->$func($field, $value);
7 x* b# E$ a, W8 B5 d $info[$field] = $value;
7 e0 }5 h) Y* Z4 j6 i }
( A# [& X1 _# p7 c6 W; T2 U } ( N% k9 L. U& l' u$ \2 R
return $info;
' R# Z4 H0 `, o# l/ }! ^: U9 b } , ^5 ]8 q9 m4 |: T* n
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,! i8 a0 y# m# W6 |
' x' I( U% Z- ?- k C" F
再到phpcms\modules\member\index.php 文件account_manage_info函数
; }0 k8 t- n2 ?: K' k$ V) `过了get()函数之后。. C7 a& U) @ M) {# ^
6 ?$ o4 X0 I5 n' z7 Q
3 A1 k/ M7 k t( C1 D# l$modelinfo = $member_input->get($_POST['info']);
, c4 N$ J; [$ o9 d6 } $this->db->set_model($this->memberinfo['modelid']); ; d/ ]+ T; V" i3 p ~
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ! ~) j/ c9 q) M/ y% `
if(!empty($membermodelinfo)) {
/ d: o; m5 g, w, ]3 E" q' r8 K $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); + G2 l8 L* D4 e1 L3 D3 }" w
} else { - Q# i: G6 g" ?2 z P* S1 |0 ?
直接带入数据库,update函数我们跟进看看
% `" K+ `) ?. L+ v* E3 O% O" J4 d V. N/ ^. d! ]0 V" B% N
7 r6 G" S& {9 S/ S; p/ W1 n2 ~public function update($data, $table, $where = '') { ( B! t- k& c1 M9 p
if($table == '' or $where == '') { $ N _ R0 C/ a! y9 W; O' A
return false; ' \7 G( P' s# }$ R1 J2 m
}
. [# k2 {2 A& @- d$ I $where = ' WHERE '.$where;
& Q9 y2 J. K3 k3 F6 _$ i) k A $field = ''; 8 Q" K# D* M2 F! w7 z) E
if(is_string($data) && $data != '') { . y! I+ D% u" t
$field = $data;
& P$ K6 D9 l9 i- a0 M0 O- q- C } elseif (is_array($data) && count($data) > 0) { 1 M L1 S8 V7 i( J$ k
$fields = array();
# Z/ ?" [: S1 k; M1 |, U foreach($data as $k=>$v) {
7 S. D: }" @( a" E3 [7 i switch (substr($v, 0, 2)) {
# L6 D: X+ @7 l case '+=': , p7 |2 i U4 r4 }
$v = substr($v,2);
; @% n# \& P0 j: ] if (is_numeric($v)) { 8 h2 N' X6 s5 i" [3 f$ x
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); " X8 Y, q/ g4 ^) p) f
} else { 0 n- O* p, ^/ v" s4 [5 M
continue; & @+ p0 z% [, b* U3 ]6 X1 s
} 4 M( f2 n E. K
break;
7 z, O( `+ O' c: v case '-=': . ^; ?- Q. }7 P
$v = substr($v,2); . s$ A7 i/ ], J, {
if (is_numeric($v)) { ! v9 v( ? A$ ]: X$ V( k
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); . H1 W' O/ g7 S1 a
} else {
8 o# G" f# ^: ^0 } continue;
& U1 X2 W" V0 m }
% L3 @ l/ N0 W. I break; : m7 t m! t+ g* S8 }5 T
default: " \* T% R/ z/ J4 ]
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 6 h/ T0 ~5 f D6 T) H; P6 S
} " @; O# A( G( e8 _9 E9 l. ^
}
0 A9 ~( m3 i: y2 Q+ Y7 ]7 h5 | $field = implode(',', $fields); 3 g8 @0 `% ~7 D) L2 k. T% |7 d
} else {
?" r8 c1 J+ L: P! b; [ return false;
4 b6 U+ I7 d, \, T* G }
/ l7 {+ f1 e9 V" {2 K $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
, \) d/ X$ A0 a2 T8 z* Y6 t7 D print_r($sql);
! R- O4 p% ~' W( X6 Z return $this->execute($sql); 1 [' V9 ^7 I2 k
} / `; A- l" I' R2 p
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
) L6 _, W3 E2 i; C. }# ?7 H9 i8 u; L
/ M# l" L: L. f* F1 G# |攻击测试:& U G4 B, O( W$ c3 v
测试地址http://localhost
2 q$ H0 s0 k3 R5 u( K! b 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
# t% [! ?- l7 t. g8 `- ?0 P N1 J+ a+ L9 P6 Q
' I0 d9 p. r- v* n y. b
8 U" b3 N q; u. u' [
" E7 f5 E- v! c! M2 L! r0 S4 ^, e2 a
* {& ~# \3 ^2 y/ t- E' \! i A( H |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|