|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
, h% @8 k# m' o# G E漏洞作者:skysheep- u, h8 h" r9 f2 k6 Q6 F! j( _/ W
分析作者:Seay
+ y( L7 |/ c9 G5 N1 K博客:http://www.cnseay.com// X/ y& @1 u O9 E5 x, H7 R
漏洞分析:% {) R) P! _9 c2 v- j) a
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
5 w1 G8 D# b6 f5 `& r! n3 I! f5 D, J7 D; u5 W. r1 ]$ G6 t0 |& ?0 L
% r. l5 c! p% H! r) u' V* Y
& @' h( s0 R3 p' y$ L$ S* G
public function account_manage_info() { ( d+ k: \; ^' a7 s6 Z7 q# u
if(isset($_POST['dosubmit'])) { " e2 u! B+ e) J
//更新用户昵称
9 T) P- H1 g8 i0 ]% x6 u' D, M $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
& n& q- Z9 m5 v' H; x( q5 Q if($nickname) { " Q8 d! X- G t" x, o( }& x
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
9 ^2 r; Z5 ~5 o0 Y5 C; l; X if(!isset($cookietime)) {
$ {/ O* Q _0 N( E $get_cookietime = param::get_cookie('cookietime'); ! V0 ~0 [, B; i# L9 H2 Y |; j4 V
} * ^0 K, U- G# p4 {
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); . A( I- w. k; f/ K7 B
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
L$ t, c& R& m3 j8 y6 u* \ param::set_cookie('_nickname', $nickname, $cookietime);
% j' ]8 H2 N1 k/ q I } 0 \# x! S# m4 j
require_once CACHE_MODEL_PATH.'member_input.class.php'; - Y" C* n4 w9 \4 ~% c1 o. R
require_once CACHE_MODEL_PATH.'member_update.class.php';
/ p! F( {1 e8 v) C/ I9 U1 b% G. m+ b $member_input = new member_input($this->memberinfo['modelid']);
6 G* Y9 E# C* z; F3 x $modelinfo = $member_input->get($_POST['info']);
* Q4 ]* d9 R5 u: q' [6 X$ R $this->db->set_model($this->memberinfo['modelid']); # `! G+ \' I* s* C6 K
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 9 s- S- e5 _! o+ L. c$ L1 Y8 I+ J0 i
if(!empty($membermodelinfo)) {
. t' h( y/ |. X0 m$ ]* ? $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
U H6 ^6 W- D, t0 q } else {
0 R3 C3 t- c) j+ c' i0 x, o" Z $modelinfo['userid'] = $this->memberinfo['userid']; n' z; ]( B/ o0 f
$this->db->insert($modelinfo); ; J0 e2 J* a6 p7 g2 C6 q7 X
}
7 |. f1 l$ D& u6 ^代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
, [6 } M3 X* v2 `) s在\caches\caches_model\caches_data\ member_input.class.php 文件中:: e. J7 ^4 p7 h! O
j7 q D# j% a7 r% x+ r
9 U* t! L$ b8 Z+ G6 a
) _0 z }$ V3 o' a( g1 N4 hfunction get($data) {
2 l; u8 M' u: d3 J V $this->data = $data = trim_script($data);
y8 R9 c q3 I5 @ $model_cache = getcache('member_model', 'commons'); # J- n8 D. h) j, i0 Q" ^
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
3 m3 S5 O7 h8 K1 j+ y4 R4 O/ v: A $info = array();
, w+ o U/ V& n: W( E& D, O$ F $debar_filed = array('catid','title','style','thumb','status','islink','description'); + I5 t L6 V# `2 j& T+ K$ F8 I
if(is_array($data)) {
& \! a1 i. F* ^8 y" a5 } foreach($data as $field=>$value) { : I4 A/ j6 V8 p
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; * ]& c2 x( {$ v0 u7 ?( p- m, Q
$name = $this->fields[$field]['name']; 4 M G3 K7 J) D7 y4 x
$minlength = $this->fields[$field]['minlength'];
' p( {: L7 W* a& m% Z3 ~ $maxlength = $this->fields[$field]['maxlength'];
& E9 r( b+ Y% G $pattern = $this->fields[$field]['pattern'];
_! ~# K; J8 e $errortips = $this->fields[$field]['errortips'];
$ n4 V# O3 D/ F if(empty($errortips)) $errortips = "$name 不符合要求!";
) F# M) A0 y* m& C/ w5 Q! d4 B $length = empty($value) ? 0 : strlen($value);
2 {$ ~3 _& {# z3 H- g F if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); : g0 C, ?( L' D' ]$ `
if($maxlength && $length > $maxlength && !$isimport) { 7 c$ |" {+ w* c+ m
showmessage("$name 不得超过 $maxlength 个字符!");
* H7 Z( a+ p' q } else { 5 G2 c" g6 Y1 {' F% b& Y- F
str_cut($value, $maxlength); 6 d; w* L" w/ D5 J" J
} & v: |$ N8 s$ K/ v, s0 Y8 V
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
! o7 Y& _* o* c if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 4 p+ J2 T0 G# l$ L# _: M# ?
$func = $this->fields[$field]['formtype'];
0 K2 I( M( F) r9 j" K, q if(method_exists($this, $func)) $value = $this->$func($field, $value); 1 p# |' i: X3 w1 F. x- c3 B
$info[$field] = $value;
: T1 n& u# n4 P9 Y6 m }
9 Q2 S' \( i3 d( D4 v } 0 V% u0 P1 q2 r0 o8 ^
return $info;
0 ~: g6 w: y/ f9 h8 w' A } ( y& F& @5 s" y7 Y
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,- E0 C9 t( {8 a( {9 d& o
; q$ U! H t1 l( X: Y) `再到phpcms\modules\member\index.php 文件account_manage_info函数7 n% F1 I& X9 A+ x; j \ I0 \
过了get()函数之后。
3 Z3 D# H) f V9 |8 l8 d6 Y
7 h# z/ E; E/ S ]# {7 @# m7 F4 M$ Y
$modelinfo = $member_input->get($_POST['info']);
& l0 o' C* P! w* ?& L3 u $this->db->set_model($this->memberinfo['modelid']);
7 K2 T& K! `' [& u $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
) T: C) _: M1 Y7 W if(!empty($membermodelinfo)) { ; _6 X7 H7 ]: ]7 r( x) U1 J0 H
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); + w5 M+ K* E/ x
} else {
* e# k9 B: e2 t. @; O8 g直接带入数据库,update函数我们跟进看看
5 K9 q$ g! y" Q7 L9 ^! z
$ h5 Y( G# a2 i O: O; f: O $ R6 Z0 f( {0 f4 F! c# e( v
public function update($data, $table, $where = '') { 5 I& `1 K% A+ r; A6 r
if($table == '' or $where == '') { ) L5 O; I$ s% l% M% Y
return false; 8 g1 q: Y8 K% Z$ x% u T6 z
} * F* Y% [3 r2 T7 l) S& U: p2 f: K3 r
$where = ' WHERE '.$where; ( K. P+ v A. Q( f- v' ? R
$field = ''; 3 l$ G( h2 @* G1 }
if(is_string($data) && $data != '') {
1 X" B8 Y" {- _! i $field = $data;
9 A' v6 ^8 R }2 T C# n0 b } elseif (is_array($data) && count($data) > 0) { - a% g" F7 @7 j5 p
$fields = array(); ) \* P( k2 G+ Q5 D- M
foreach($data as $k=>$v) { 8 w* c& ?4 o3 ?
switch (substr($v, 0, 2)) { ' G; g# X9 g9 z/ f
case '+=':
' f0 t3 k: `$ l! @+ v- K $v = substr($v,2); & V+ q. C* k) @! J. W7 C/ N
if (is_numeric($v)) { ; n1 i: L5 O- b3 B. d2 \, X& [
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
; W6 {' P3 x% a( Y, k4 ^4 l# R } else { ' n$ e! C' G- i
continue;
2 g+ [) |2 v% o- E A; }+ E$ d }
9 ]) V/ |2 q; I0 H; H break; 7 @1 L7 I' s* S% L) U
case '-=': & d/ ]0 r' M4 O- n. R9 \
$v = substr($v,2); ( a% f V5 Q& D( ~# h+ n
if (is_numeric($v)) {
) C i0 [2 a1 m3 R& B- G4 o6 x6 @ $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); $ V+ a6 X6 L' ^& P" n
} else {
; |+ w5 r# g; W* U continue;
/ {+ A* M$ f% K2 a8 O3 f% d6 c+ T }
; l3 d* m; k! v; S+ X break; % v" @9 T. g, B7 G, i
default: + D- r+ j6 |: I: o) l
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); : s& n- Z# z! @; k$ G0 e2 J3 b! d
} 9 c" C B: n/ S5 k
} / S% ?+ B* f d# U: U C' S
$field = implode(',', $fields);
4 |- W( F/ ?+ F } else { + j& X( T& }5 O# e' ]$ Q0 e; K7 G
return false; 0 }, D p1 T+ e( k% Y7 ]' M: _
}
/ m2 R; \) c* O n! p $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; $ H' [; Z' G) R# W# T' v/ b3 v9 z
print_r($sql); ' _- p5 {+ R" x! ?! ?9 N
return $this->execute($sql);
: }1 M% W7 F7 e( R% U- B/ u3 @" f }
' d ?* H. R8 A+ O$ G: q% L从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。1 D6 u% C, i6 p2 D
, }( v" J- r$ F# @攻击测试:
1 T0 A0 X1 f' L: Y测试地址http://localhost$ w$ R5 K' o9 X4 d2 O! L
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句1 v( g$ X: L: u2 y0 Y. M# N* }
! ]% t5 C4 J6 v
B" U0 q( V, X6 \7 x0 h
; w. J" D' r) l
* t$ l7 W9 u8 X7 V% {. ~7 [2 p; v, i. [- R% o$ O3 c
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|