|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告' L* [! p% I2 Z! `& M z5 l& _
漏洞作者:skysheep
8 ?$ ~( G4 c( x$ p. r分析作者:Seay# z4 l/ S" B3 Y) ]( p4 u
博客:http://www.cnseay.com/
0 d& I8 h7 m5 i6 J9 D2 t# P3 G漏洞分析:2 v; t0 @! h) S; R" R
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
, I2 q! {& \ z) K4 y
* A/ U ~& ~# I/ D+ k* u
; N+ l q9 V# E. x, E( Y# r9 A# _. l
$ v X( W* {% Z- Qpublic function account_manage_info() { % X/ |/ j3 n; W' G. l
if(isset($_POST['dosubmit'])) { + U2 O# N- m |& t+ F1 |
//更新用户昵称 7 I4 R* v" v% {
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 9 r- Y3 D8 f/ h `1 ?
if($nickname) { & f5 ^7 H8 Q- i/ `7 O
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ; C5 _$ r8 ~. i$ c# ^' \
if(!isset($cookietime)) { - V" [$ s h7 A+ y! M4 l/ K
$get_cookietime = param::get_cookie('cookietime');
# Z6 n* e, e6 a. }7 ~) j }
0 d. Q& h' P" R1 W @. @- ] $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
; k" H. t& q. t# B3 j% H; p0 | $cookietime = $_cookietime ? TIME + $_cookietime : 0; 5 z' Q; F2 h0 n: f1 q) F: N( T
param::set_cookie('_nickname', $nickname, $cookietime);
; q: E: w. ~: Q# | }
% \8 H4 V, e* o' z require_once CACHE_MODEL_PATH.'member_input.class.php'; + N# r3 H+ Z2 F O5 \* h6 C/ L
require_once CACHE_MODEL_PATH.'member_update.class.php';
9 s8 W& \7 @6 s( Q c" k5 ~ $member_input = new member_input($this->memberinfo['modelid']);
. I: d. R) V! {. H# e $modelinfo = $member_input->get($_POST['info']);
' x* n3 l/ b4 g' K% D $this->db->set_model($this->memberinfo['modelid']); . Y0 y5 s; h- L5 p
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
5 _- W. P( ^; k+ `8 n4 \ if(!empty($membermodelinfo)) { 4 o/ w4 Y+ l, y( j( w/ t1 }7 m
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
' n! a% e! B2 i y4 ^* K2 ^! R } else { ( q) [3 }$ X7 ~2 o! t j, T
$modelinfo['userid'] = $this->memberinfo['userid']; # u% o1 `8 T1 |
$this->db->insert($modelinfo); 5 ]+ U% i$ X8 A- d- |
} / P4 V" X c7 |! I6 k
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
" s6 ]8 E- S: U0 c; e0 K6 u2 B在\caches\caches_model\caches_data\ member_input.class.php 文件中:% R) Y' {' B: o. |: U6 K! h# s6 W
- A M( ^) @' v( G$ @8 P. `
. f$ J Z* b7 m% `/ k( M
9 F% n, A3 p) @8 H# T: D5 j) `function get($data) { 4 [5 N3 R' H! t6 H4 H" M; s
$this->data = $data = trim_script($data); + k) F8 ^4 p' L. t
$model_cache = getcache('member_model', 'commons'); 9 Y4 e& v# D w" K) k2 Y1 D+ b
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 6 p; [. d U [5 C# T
$info = array(); 9 f2 K' v# M7 S8 s8 v7 H9 x+ J
$debar_filed = array('catid','title','style','thumb','status','islink','description');
% Z/ _8 C, C$ N/ E% ]/ m/ G if(is_array($data)) {
' y, w0 _- F: f1 w foreach($data as $field=>$value) { - h9 V: h; E3 ~% H: T2 h' P/ h
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
4 z* I* d$ i) m7 [: \ $name = $this->fields[$field]['name']; h+ }; A. ]5 e# g0 G
$minlength = $this->fields[$field]['minlength'];
: S ?9 Y& c6 o $maxlength = $this->fields[$field]['maxlength']; * q$ y, I6 ~1 u9 f5 g' o8 Y
$pattern = $this->fields[$field]['pattern']; 0 r: ?2 I. `6 u6 @; W$ c
$errortips = $this->fields[$field]['errortips']; , B2 n2 `% m6 v& U; }7 s2 V q X
if(empty($errortips)) $errortips = "$name 不符合要求!";
; C" K7 D" x' Z& X( n0 e8 q9 j $length = empty($value) ? 0 : strlen($value); : l" {. a8 j) [6 u) u( y7 E1 r( V
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ( i# Q; b0 e9 z! X/ F' B% r
if($maxlength && $length > $maxlength && !$isimport) { / g, x5 s4 Y E
showmessage("$name 不得超过 $maxlength 个字符!"); 1 \% G ]# ~ k6 k
} else { 6 [8 ^8 g" C' C8 c H
str_cut($value, $maxlength); , s% ?! u' s1 Y0 R, Q+ q
} 3 B6 u8 I c. S' t( v8 g6 D
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
# |, D' s4 Z! I- [2 q4 m* K if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
* o- _$ @8 k: T5 ?9 G $func = $this->fields[$field]['formtype']; # B3 G' U0 U4 i3 n
if(method_exists($this, $func)) $value = $this->$func($field, $value);
' I5 x. V/ U- i2 i; o $info[$field] = $value;
5 S" [' k! k; {0 S- S! ~( ?+ G } 7 \7 [, w% p' ~+ }
}
/ l/ y" N3 K6 ^. W% S( r return $info; " c3 S& \& L# ^5 \( ?& C
}
9 {# J Y* @4 ~' gtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,; q9 p4 Y5 k3 o/ O' a! P$ j
! _0 D( Z8 I% E! ]再到phpcms\modules\member\index.php 文件account_manage_info函数
8 O+ p5 l! T; K; ]过了get()函数之后。9 ]. k( E. v( P8 T! a" k3 \
7 ~' C$ @) Z$ z' V, H
$ U4 A( E$ y8 U2 g% K" l$modelinfo = $member_input->get($_POST['info']); 5 z9 | A+ {, p# q! n
$this->db->set_model($this->memberinfo['modelid']);
# p: x4 Z1 e+ e: B1 x* ~3 t $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 4 {3 T/ o+ N+ z2 R) s/ z
if(!empty($membermodelinfo)) { * K" j2 w+ {" z ?2 s3 D/ a
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 5 K6 J" F9 a* W' I
} else { / m! w5 T* x* F7 _" U N
直接带入数据库,update函数我们跟进看看2 U/ [, S% m7 G& B! U3 d% x' f4 q+ p
- s( O8 Y5 c( @: U; @ T" `
# Y# ~, ^, [& _7 t# r
public function update($data, $table, $where = '') {
: M0 Q u& l8 a6 c if($table == '' or $where == '') { . B, N6 b, K) w8 y+ t- {
return false; 6 v N0 J+ z) Z* T4 }
}
+ v. t% v+ }4 [0 Z $where = ' WHERE '.$where;
0 q* U9 r6 h( s. c& a" I4 I+ f6 i3 } $field = '';
N g) n/ e- r* N if(is_string($data) && $data != '') { " J: D; _$ g, p6 x7 G% A# S# M1 a
$field = $data;
, b) k' } X+ v' u, M2 m } elseif (is_array($data) && count($data) > 0) {
+ `0 W) C6 \* ~( A$ l $fields = array(); 1 M" b$ t! _5 l. J2 S, c$ \ R
foreach($data as $k=>$v) {
$ {9 }; n$ K# `5 F' ~ W- x switch (substr($v, 0, 2)) { ( d2 P- P6 F! Q) p `8 n
case '+=':
8 `9 _4 [5 V7 q+ h $v = substr($v,2);
! C( v `; U) v9 a+ f1 Q if (is_numeric($v)) {
( Q0 |6 L& W: ^+ L! ?7 w $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 6 w( o& s$ B a
} else { 7 [5 R* z4 {; A7 }( j X+ q& F
continue; # j C2 _( z5 [" ^, c' Y
}
; f" S5 h T2 X6 }# N break;
0 _1 ]' f, e' N, V case '-=':
: q; |# n& l$ ^' ~0 T $v = substr($v,2); 6 ]3 _& |7 G4 {' l. l" h& p6 e8 \
if (is_numeric($v)) { 6 i) r/ V9 v4 q2 g4 B5 T
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
6 Q4 v+ g& n! q4 N1 ~" C. t2 Z } else {
D' f( g6 W' H; ?) E# d9 }3 m. Q continue; ! j" E6 J' ~: u6 g) |$ x( T+ J
} N* Q. g9 v0 g; F& Y" ?
break;
! q1 D" Z/ F' n- B default: : b8 a. r3 M( S/ G/ Q6 f
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 2 Q8 i( S4 c. @2 {" H
}
9 D1 r2 ~5 E" h" L# ` n$ h4 } }
) {- j2 n1 L+ F |8 N3 F/ j7 O( ? $field = implode(',', $fields); / G& m7 h7 T" T
} else { 0 j4 ]1 W. ?$ b( `3 v
return false; ; [% w% o& C! p+ A; F, z
}
4 I) p B7 f0 M$ U, P $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; $ W& j7 U* W2 a
print_r($sql); 9 }9 u6 L# r( o2 t4 j+ E5 u
return $this->execute($sql);
6 g7 Z" Z/ X H4 u2 M: n/ | }
# I2 V* \; H3 W; M, u从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
+ S/ m0 K9 t- a+ p( Z
# Y0 T8 ?$ p4 n6 ~ j; q4 |攻击测试:
0 a. |5 S, ?* m: k测试地址http://localhost
/ @$ L# s: S+ P$ d8 w- ^/ e n 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
/ W" A. @% ~" A) @0 v! O2 h( o! q I$ x3 k9 [
' z) q3 L5 l; y+ T9 I) K( ^' D. _
5 d8 F: t7 {5 J3 M' V/ o
# q0 T6 U: S" B' o0 a$ @- ?4 H: J- x1 |1 f- G5 t3 A
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|