|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告' M& }' u- U4 o
漏洞作者:skysheep; W( u# h: A5 W! E; E
分析作者:Seay
! v. Q( a7 Z* b2 J! \7 [1 D0 [博客:http://www.cnseay.com/& n& T( G+ B2 D: a* r7 G: }" \) w
漏洞分析:
s9 R$ @3 q! r& w1 c- c 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
- }6 _) X( F. }2 n
: O6 P2 f: v2 }4 M. i" c4 T' _7 \7 Q- V. p1 [! E+ `
. C3 o) [; r# A2 Q$ Q/ K* B% ]public function account_manage_info() {
5 B' G/ g3 I+ P. b& o1 S1 Z$ ^" F( H6 C if(isset($_POST['dosubmit'])) { & ]/ K! b; e2 J* O: g
//更新用户昵称
9 [( r8 H4 @' p( T( i6 g6 Y$ T $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
, C* O+ K& N! E) ^ if($nickname) {
4 a$ R, I+ o/ [' a9 c, Z $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); " ^7 j% \; r( I; w7 }. d. c' {4 _- \
if(!isset($cookietime)) { 1 g' J/ s9 K, i
$get_cookietime = param::get_cookie('cookietime'); + h: D d8 g6 v( I, K1 G# f
} ; U( W% _1 @/ ]% s* `2 D! d
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); + O' r7 x" p1 ]3 _1 t! C
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
' @7 ?0 s3 m; @$ k! J param::set_cookie('_nickname', $nickname, $cookietime); % P5 L! G9 E( T: s5 T- j
}
1 K* o, `' h" P0 a0 A! W, W require_once CACHE_MODEL_PATH.'member_input.class.php';
7 K* t3 J; g: C _9 c* H require_once CACHE_MODEL_PATH.'member_update.class.php';
* w- s7 u0 c9 z2 { $member_input = new member_input($this->memberinfo['modelid']); z0 R+ c J, o, ~5 t
$modelinfo = $member_input->get($_POST['info']);
) R a0 \) \9 W. Q: z5 l+ H $this->db->set_model($this->memberinfo['modelid']); + q7 U( u0 j; a2 t8 z S
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); , n: f3 n" O3 u
if(!empty($membermodelinfo)) {
8 x! [ R: W- d+ g. d( L5 F $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); , E/ r' N/ x% \2 J, j
} else { ! K% x. x+ F! n: L6 w9 R
$modelinfo['userid'] = $this->memberinfo['userid']; $ R0 C/ V; w- Q( i. V
$this->db->insert($modelinfo); : Z" Y' j$ E# [6 J: S% s* d
}
* P! A( ?% L: e6 ?, V- ?7 y+ y3 s代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,) Q# |) U% x% y1 h+ `
在\caches\caches_model\caches_data\ member_input.class.php 文件中:8 _1 C; [/ q+ L5 N! S8 E
8 N P& p9 X' \3 D; I8 }
/ t3 F- w1 S; P0 G$ ]9 P3 e' ]4 G
. R0 C* m/ {% _$ S5 r R# Cfunction get($data) { * D) f6 C b& z, v( m# p% X
$this->data = $data = trim_script($data);
* B; h6 e& {+ @, {4 I& E" C $model_cache = getcache('member_model', 'commons');
* i+ D2 u0 C9 e $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
: h3 B( M! m" R6 Q# W& c $info = array(); - O% a/ K% _ W/ X
$debar_filed = array('catid','title','style','thumb','status','islink','description');
9 e' {( J- `3 D( r if(is_array($data)) { % ^7 S% |% I% C& X* `$ T3 e
foreach($data as $field=>$value) {
4 A* ~1 Y- N0 o& W/ g if($data['islink']==1 && !in_array($field,$debar_filed)) continue; $ r8 T" ~& P7 j: S- ]
$name = $this->fields[$field]['name']; ' x& ~# x z. G. g( ~+ g* @
$minlength = $this->fields[$field]['minlength']; # u7 d1 `% A0 a- ?+ j
$maxlength = $this->fields[$field]['maxlength'];
! T, t: V0 e. T7 o $pattern = $this->fields[$field]['pattern'];
e9 K/ c u( T2 d $errortips = $this->fields[$field]['errortips']; : V, R. N5 U0 U/ F7 H' I
if(empty($errortips)) $errortips = "$name 不符合要求!";
; P* g7 W$ `/ d, h* [ $length = empty($value) ? 0 : strlen($value);
$ _) b6 R$ v( |# p( a n2 C% [ if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); : L0 R x4 f$ ~% @" K1 N/ o1 B
if($maxlength && $length > $maxlength && !$isimport) {
! H) O% I- O* Q* m* F3 B showmessage("$name 不得超过 $maxlength 个字符!");
) A/ g; P( Y4 s* F# r3 P+ z } else {
0 q5 K: C" m, T1 m% _ str_cut($value, $maxlength); ' A: G. Z9 |8 w+ M- t) }1 h0 ~5 S
} ( A$ R* \3 v3 R- x
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ' l) H8 d8 `# `4 Y
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
5 f8 T6 u. |# U1 w% C! W $func = $this->fields[$field]['formtype']; 0 l5 d; d$ h& L7 I4 u
if(method_exists($this, $func)) $value = $this->$func($field, $value);
4 y @0 l$ k* E2 c) d+ _7 \2 h $info[$field] = $value; 5 x9 c+ ?, h" t. k
}
: L6 ~- _" N, W- D8 q K* s$ Y }
4 G5 \: f/ C" F" B' W3 T/ q- X return $info;
6 K; O7 Y& \$ ]' s& ]5 R) E } 6 Y6 P3 O `- m" Y X
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,4 } [: k% s3 v7 }. u2 ?4 a0 D
. j% y* H% K" B% \( Q- u; K
再到phpcms\modules\member\index.php 文件account_manage_info函数
, Y2 t, ^, f% p3 H' L- z. X; j过了get()函数之后。; T/ s2 c. d# g: ^$ G9 ^; N
0 R2 L$ J6 y( x
+ ~" V5 J/ d; o$modelinfo = $member_input->get($_POST['info']);
( b' H8 P) @8 P6 l* X $this->db->set_model($this->memberinfo['modelid']);
& ^6 @. Z; u k- L' K& q( S3 j- F $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
1 P/ d* `) [+ Z. P/ A! a, U; r. g if(!empty($membermodelinfo)) {
; i( `0 D, k W3 w I. j+ I* Y $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); # C, v9 m1 L7 q3 ]: K( @
} else { 8 u/ w* [& ^0 D7 J( o
直接带入数据库,update函数我们跟进看看
$ }' c$ @# D, ? Y6 K/ B2 q. L
6 z y# s4 i& T E3 w$ ?" r5 t& g
: G+ X9 a# `! t5 \" l& npublic function update($data, $table, $where = '') { + h1 Z) D; i+ Z9 q0 w7 j2 C- I
if($table == '' or $where == '') { 9 _8 C7 `: ^& g& \
return false;
" y6 P) c- Y. _ I3 Z, F } 7 R9 d& V/ W" R8 ]* U+ _' X
$where = ' WHERE '.$where; % D5 |& |' P. w" M
$field = ''; / z- M1 d1 P& j* K. R7 E2 T
if(is_string($data) && $data != '') { # ~- b( i( n9 q3 q' @) O3 g4 F4 |
$field = $data; 4 e" j# R3 r8 q3 k+ w
} elseif (is_array($data) && count($data) > 0) { + p g9 C' e, ]' C* J& j) b
$fields = array(); ! @6 b! _+ G2 a+ u- R
foreach($data as $k=>$v) { * t+ }. p8 P3 n4 p9 j
switch (substr($v, 0, 2)) { 5 z/ y6 f4 Y/ w* Y
case '+=': ) V& \( z- H, f' ~! q5 z R- b
$v = substr($v,2); $ z; p7 L7 K+ V; u3 b5 o
if (is_numeric($v)) { / b$ { ?4 O2 O4 ]9 i, A6 F
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 7 _* t& P/ P# I% j+ J$ d- f
} else { ; `: W7 j% b4 C6 U7 s( `# }1 ^
continue;
1 @. g7 P, H V5 e } 7 M# F# G; a2 |
break;
5 L, N- {" v7 _9 g& c case '-=': # O& N9 I& o/ s0 O9 O) [) p8 L
$v = substr($v,2); 9 f/ q! }( ~& }& E- U
if (is_numeric($v)) {
& K: @5 e$ m3 G, _9 H4 n7 d $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 1 o& U- o7 q8 M
} else {
+ u" ]- X$ O Q: s continue; 9 J' T4 _. e& R# Y
} % h1 H* o$ \4 _
break; ' W9 }( f/ |2 X( D. r/ N/ ?
default:
9 W0 F! F- m+ ]* L3 M8 ]3 C% s5 i $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 6 s( Z- ~' {( l. y0 T* x
}
' v4 e3 N5 ] c }
) F Y. u# t/ J/ e, h $field = implode(',', $fields);
8 z) o" h" c) F3 t+ t& x) j } else {
3 k g B! G% m7 r return false;
$ g. m; W: m5 o2 }8 m" \# T3 w } 7 N4 h/ s V T- _- b& c
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
, V$ H4 [9 H8 H' [ print_r($sql);
: b; _3 d& a' j5 r return $this->execute($sql); 6 X. O5 [/ l; B3 Z
} 0 h: \6 [7 A( K5 U# `
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。, u. b# ] `; z+ Z
9 u+ }9 y7 n7 P- a% E' P' w6 @2 r
攻击测试:
9 J% Q0 G% I( ?/ r测试地址http://localhost
7 A9 V8 O3 y+ T9 U, Z 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
. U# L2 E5 p. F! ?! S4 U
- U. L- \9 u* X: ]" t% V* |6 x4 C: K * M, y& w. M6 l5 P7 T# k
: E3 [# A+ ~" g& q9 b9 ?% w( T
5 a: v4 }- \) `2 W/ v$ z. Y
. p$ B ~; `5 v+ r2 g* | |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|