|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
" K' ^/ T; v' q: d- X3 G漏洞作者:skysheep
6 l5 B6 C" [) z4 D1 O' E分析作者:Seay
% k' i& d1 V- f8 |% ]博客:http://www.cnseay.com/
" f' ?" I8 G5 Q$ x漏洞分析:9 `. R# @4 L1 u, W5 w
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。& v0 d) D+ V Z; q3 u1 @3 B3 v
5 q ^9 R W* y
9 M% v8 O* V1 w$ q) y % Z7 e; }3 R# q: _. S& `, Q1 A! A3 s
public function account_manage_info() { : B- L2 ^4 J$ l$ }0 @! B2 P5 v
if(isset($_POST['dosubmit'])) {
, R6 m% q" F# Y, U( T //更新用户昵称 ' G4 m$ v' _ p# z) r7 l- }& a
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; ) |9 z( d4 f$ `$ i7 j% p
if($nickname) { 4 B/ l: }# t: T- B+ n: W9 W, J! i( m1 m
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
9 K! J3 B. `, _5 c/ L if(!isset($cookietime)) { % f; w7 \0 u7 o$ K: p P
$get_cookietime = param::get_cookie('cookietime'); 0 s c2 ^8 j. v5 l% A) ~3 F
} ( ]. K" m3 I% z' S9 Q4 F. P% P/ \$ R
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 7 |" i4 y" H6 o1 A' c) o+ j
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
4 r4 x3 p/ V# E param::set_cookie('_nickname', $nickname, $cookietime); # i% j: j& z$ Z. m1 ?: Y& v) G
} / x- a3 Z# b9 V z( \
require_once CACHE_MODEL_PATH.'member_input.class.php';
p |' V% u4 T/ X) f1 D require_once CACHE_MODEL_PATH.'member_update.class.php';
: @& @$ c% U } e $member_input = new member_input($this->memberinfo['modelid']); 8 v$ q9 d* j5 p
$modelinfo = $member_input->get($_POST['info']);
; P/ V- a9 q5 D+ A% M $this->db->set_model($this->memberinfo['modelid']); 5 `1 q* n0 J$ q) _3 ]# q
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
: }* T% j9 e( Z% R( j( k: W if(!empty($membermodelinfo)) {
, c& O4 n7 T. X- v6 l $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 2 |2 w( F, R7 m
} else { & }4 p/ P _4 [5 J
$modelinfo['userid'] = $this->memberinfo['userid']; ) X& _# ?" J( r( k- }) j
$this->db->insert($modelinfo); ; ]( [$ Y- [. h6 V0 ?& b3 g0 F/ a
}
2 M% d+ {& L+ {/ ~3 Q- E代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
1 |& T1 Z* F' W% E在\caches\caches_model\caches_data\ member_input.class.php 文件中:
. S) |7 u7 I, Z3 f) L5 S3 n* E6 \1 V! U9 a3 C* I" Y
* v( o: q' r+ [( I: Y4 @
" l8 i# e; r: f0 \4 Dfunction get($data) { " u: S+ L0 @1 l. n- k( O
$this->data = $data = trim_script($data); 8 R3 {* r8 X0 n2 e; I; G
$model_cache = getcache('member_model', 'commons');
8 p, X+ b' N9 E $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; % p! v6 d- G1 b0 D
$info = array(); 4 @" ~- Q9 A+ ^
$debar_filed = array('catid','title','style','thumb','status','islink','description'); + i; [4 d) m+ S6 ?$ B
if(is_array($data)) {
4 N4 z) h. M8 O, h4 N3 n foreach($data as $field=>$value) {
% g% C1 O! }4 m0 s; t$ ^" C0 r0 M# ^ if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 9 d4 T9 H# b& x( H/ T z% w4 h
$name = $this->fields[$field]['name'];
# Q6 d$ k" j+ F( v9 [* F6 X2 G) \ $minlength = $this->fields[$field]['minlength'];
4 D& i/ t8 @6 z/ }( k7 Y $maxlength = $this->fields[$field]['maxlength'];
3 T) f- U* M# Y+ P" ~% O $pattern = $this->fields[$field]['pattern']; 7 K! N, G6 x/ y- V9 ^1 Z
$errortips = $this->fields[$field]['errortips']; 2 W- O- j# e" G/ y; X2 s: e
if(empty($errortips)) $errortips = "$name 不符合要求!"; 6 {! R! E3 J2 f
$length = empty($value) ? 0 : strlen($value);
, c1 I0 Y6 p. A6 a( y# H; t: A if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
, A2 K% l( o/ x if($maxlength && $length > $maxlength && !$isimport) { : H# @, X0 M% }" h! J' ^$ `, \0 G
showmessage("$name 不得超过 $maxlength 个字符!");
3 \" ]/ k9 f; ] P* a3 C } else { & V9 f& D1 N+ P% N. v
str_cut($value, $maxlength);
' r, a3 u. l3 @; k8 `2 x }
& X3 `7 W* n! G' U% D: m8 W' W& G; x if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
5 m/ g) }; D$ u& A. q1 S A) f if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
) q* S5 P) `4 b $func = $this->fields[$field]['formtype']; ; r! M+ C% s8 A& v" ~
if(method_exists($this, $func)) $value = $this->$func($field, $value); - A, z2 T D; v5 n! f2 S. e
$info[$field] = $value; 4 E1 j, }- q( S5 Y# l% O
}
6 M# A3 _9 z! U } # \* o8 U& m$ [$ T
return $info; 4 x v. f& G) e' q2 b8 z* Z
} ( n$ m5 B1 l! \/ {4 m
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
4 }, C5 k' z/ I4 {* z- c1 U' g+ `/ j0 [6 u; G, H+ z, G
再到phpcms\modules\member\index.php 文件account_manage_info函数; \. S# r8 {* J; \& J
过了get()函数之后。& I/ o0 T% a1 H3 _8 U
& B# G$ S; s- Z+ }2 E 8 ]2 F3 X# |. G* l6 Z2 m
$modelinfo = $member_input->get($_POST['info']); 6 W# v2 l2 g- Z2 V
$this->db->set_model($this->memberinfo['modelid']);
( ^5 B3 W& G9 I/ Y2 R. B) ? $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
+ g; A' L5 j6 x7 x if(!empty($membermodelinfo)) {
3 V2 ]9 w4 k0 e4 I $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ; t+ @, i1 X$ `7 s+ [8 X5 Y e# {
} else {
0 l" A Y+ V" H- v" q% p直接带入数据库,update函数我们跟进看看# A; q' E: Q% {; [+ Q
0 ?* x1 s; j# `4 i5 b& s+ J 2 u" C' K$ l4 s' q ^# `9 j
public function update($data, $table, $where = '') { ) e8 L1 y0 }/ P" B
if($table == '' or $where == '') { ( z9 I9 S1 J0 W. Z9 H' @4 d
return false; ! e+ S8 z; X. H$ a. s. I; C! |
}
; @5 O5 k) k. [4 y& _% a $where = ' WHERE '.$where;
. l1 w% J+ G M ~2 q+ a $field = '';
# t' x5 [$ G% k$ s6 ]7 K. a if(is_string($data) && $data != '') {
( M0 U- H5 j- ?- k $field = $data;
6 R% ^! Y$ |( K7 d; `" o } elseif (is_array($data) && count($data) > 0) { 0 I G% y) @$ g- ]. {
$fields = array();
, r/ @6 ]3 k8 A1 X! U U( o foreach($data as $k=>$v) {
1 j7 R7 h) h- H/ I switch (substr($v, 0, 2)) {
" V" g6 g) \) Q. y case '+=': + y7 n0 b: n K8 G$ H2 W
$v = substr($v,2);
2 C$ a& N8 Y: ? e, [ if (is_numeric($v)) {
6 e! F+ F. w8 G/ g7 h* A% v5 W $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); # L# T" |( j7 [8 e; i3 L
} else {
* u6 \% H; V2 E- Y. A; H( j continue; 9 T# v0 ~( A: N) |" C( P6 x
} / l- D3 ~" Y& H9 R' |
break;
% [4 F9 c) n% j- e case '-=':
8 R1 J3 S& l. n! O $v = substr($v,2); 3 v! h- q$ D9 d1 @6 `' m' _
if (is_numeric($v)) { ) e* H9 @5 R2 p m, Y8 Q
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 5 j. ^" D! V3 X8 @( a! v$ f
} else { * d/ t8 ^! v8 ^/ S$ U
continue; # B* W7 S2 n( b, r: O% [7 G
}
/ J( [6 w7 \1 U' T: t* S: h6 V break;
1 n H6 o4 q5 x# y8 o2 J, Z default:
4 L' P$ c& ?. l' W1 s7 X3 P7 i) N $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 8 \6 k% g0 Q# K
}
# } E6 R& A2 f9 W3 B } & s( C8 E) g! K9 }
$field = implode(',', $fields); 8 S4 h9 x! h' {, g
} else {
3 i6 l- p6 P. n: ~* s return false;
v) W. c; \3 t$ l6 { }
7 e0 R) n1 |! E! {( v5 J $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
7 D: B; h) s4 R" K6 ], M print_r($sql); 3 v. j3 @4 M5 R. Y4 H1 M
return $this->execute($sql);
% R+ E4 c4 ~, s- ^ } x: Y8 m% {6 }/ ^/ H+ `5 Z2 _
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。3 C8 g2 q: k: h& f
9 V, n1 l6 y2 k1 F8 u* \攻击测试:0 ~3 I. @: U. {$ r
测试地址http://localhost- n8 u" \$ p$ e1 u
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
. y* a5 _1 c# r# m9 y# p
5 Y: C5 z1 k% y
b$ W8 @' ]9 }/ A6 Q2 q0 {: ^. H
' E% @: E4 h" I' R% g! u5 X' e7 F9 p
: ^" h" Z# _, ~) X& J( E4 }3 c$ G |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|