|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
. X( \- |7 ^- `7 }漏洞作者:skysheep# _9 v- s+ T h7 l
分析作者:Seay" t# y2 d1 {' ^. }" V
博客:http://www.cnseay.com/
9 ~9 e( b, D' a漏洞分析:
+ \5 {: w4 J/ |$ ~ 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
* ^5 I# H/ b" l1 z3 n5 H
& R% w8 c+ \7 \' J' h* p( i# D, m T4 T! M. A
0 h4 l7 ?9 U ~+ o( Zpublic function account_manage_info() {
U% {( C& B7 s8 N" I, L if(isset($_POST['dosubmit'])) {
j, K4 ~2 D& G' |9 f/ n //更新用户昵称 / ]0 o9 a4 H) ]1 M, l& y1 v& ]
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; / h4 m. r8 g) | C
if($nickname) {
4 F* v6 t$ ]4 H, D" n5 h $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
- {$ |) x% W" E if(!isset($cookietime)) { 9 ] x7 t9 v; d/ l$ s0 K) }
$get_cookietime = param::get_cookie('cookietime'); , Q# \- ^: Y M9 B8 `5 ^0 l
}
7 k1 L" c$ H# [- c. t6 k $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
Y) ?+ a! x: t9 ~* Z9 V0 M2 c $cookietime = $_cookietime ? TIME + $_cookietime : 0;
# G7 k0 A3 X2 D& w6 r param::set_cookie('_nickname', $nickname, $cookietime); % \- s" v$ A% [/ o
} 7 d9 a) @7 L0 r$ Q5 K. n y! a
require_once CACHE_MODEL_PATH.'member_input.class.php';
7 J. i3 l2 `4 S: ] M6 g$ b ?0 k require_once CACHE_MODEL_PATH.'member_update.class.php';
/ W" q% W5 x' L) c $member_input = new member_input($this->memberinfo['modelid']); : }* P. C! j5 d! m
$modelinfo = $member_input->get($_POST['info']); 3 q- t/ Q2 b ?+ I) a5 L
$this->db->set_model($this->memberinfo['modelid']);
2 h; b1 x6 i# }6 [ $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
2 r7 Y5 h6 p- d8 [! T3 ^2 @ if(!empty($membermodelinfo)) { 4 U# Z( ~) l" p& k
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
( P: Y; W( S& o. M$ C. C } else {
/ ?4 ], }! w& e" Q$ E0 J $modelinfo['userid'] = $this->memberinfo['userid']; * V0 z* C1 Q/ [
$this->db->insert($modelinfo); ! W5 N: {7 p( R$ Y$ {
} 6 ]( Y1 z0 X: U' J
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
$ }; ^8 U# b7 \9 n/ H" z在\caches\caches_model\caches_data\ member_input.class.php 文件中:
( a% C3 K/ a. s, T9 ]# L ^$ z8 r O& K- M
B4 I( f4 K$ T. w: p3 Q8 l
0 s" L9 T7 K$ n* ^function get($data) {
2 V, W- B: i1 p$ B* b6 ~ $this->data = $data = trim_script($data);
( |) ?0 D. _* B# a! V$ N $model_cache = getcache('member_model', 'commons'); ' [* w x; g+ \+ p/ Q
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
5 ]$ h( c$ i1 ?1 J. B; p $info = array(); % w. u" }7 b9 Z
$debar_filed = array('catid','title','style','thumb','status','islink','description'); 4 @8 M8 b, ?& b! q: V7 x
if(is_array($data)) { ! C0 L6 W% m% d- i% D
foreach($data as $field=>$value) { . ~: t) p4 P; x* }
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 1 y, ^9 q/ Y7 V8 Z: s
$name = $this->fields[$field]['name'];
" @/ w. i5 m' [" a0 t $minlength = $this->fields[$field]['minlength']; ( l6 s( r9 Y" e7 f S% x
$maxlength = $this->fields[$field]['maxlength'];
h# z- y; A, ~8 U8 ] $pattern = $this->fields[$field]['pattern']; , E E& | y; r# s
$errortips = $this->fields[$field]['errortips'];
6 s/ i- ~- ?# d4 Y" B5 J M if(empty($errortips)) $errortips = "$name 不符合要求!";
9 Y4 G( @/ \+ Y8 ] $length = empty($value) ? 0 : strlen($value);
3 y, }0 O/ ?9 k if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
5 v/ ?% U( ^$ C9 M% }. v. r if($maxlength && $length > $maxlength && !$isimport) {
, B! \. S5 Q |6 b showmessage("$name 不得超过 $maxlength 个字符!"); : [9 M- L4 C1 B" ^' c& [" H
} else { 1 D6 Z% h' C7 Q" B! P
str_cut($value, $maxlength); . r6 [! h% r( X' L
}
% `7 {2 c- {2 U6 k% ~3 e: n2 I if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
0 o% [' z% H, g& u7 T if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
) E' Y! z4 q) J* n7 k7 _& P/ ~ $func = $this->fields[$field]['formtype']; ) A: L- C+ h0 q* Y4 z6 j
if(method_exists($this, $func)) $value = $this->$func($field, $value);
% @# H; x! l% n1 ` $info[$field] = $value;
3 A5 Z Z. c8 [# L2 H2 A! l( I } $ E% t5 O2 c2 z& ]! U z
}
# |6 z/ b. B& h4 B" [' U7 L8 v return $info;
' E! }* C/ O5 s7 W }
: n( D0 T5 w, t4 e! Q2 btrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
1 ^, H- ~9 a+ K! D4 Y2 y3 Z
3 n/ |" w: _; U+ @再到phpcms\modules\member\index.php 文件account_manage_info函数, c7 T& v4 ?7 q" C& P% a
过了get()函数之后。0 F: d( X5 J+ P) k: J9 Z% X( k8 R
# s8 s* k: v+ i
" k1 e. t' |. C# |! S# @# L
$modelinfo = $member_input->get($_POST['info']);
, B: `, Y& j0 _3 S) A' b- z $this->db->set_model($this->memberinfo['modelid']); ; _) }9 c O! ` L4 ]6 F
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 6 N1 J+ o0 L3 } V2 Z. e" S4 g
if(!empty($membermodelinfo)) { 2 R+ g; [ q& R7 g0 S) o* u8 M7 u
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
. S1 [! K, K- d( v d1 u, b } else {
5 _( X6 f4 v. _$ e" H直接带入数据库,update函数我们跟进看看
5 K" V0 M' ^+ G$ e& s" O% [
7 y& G* x( w! T4 X( | ( v& A" S8 }$ v4 m7 Y+ n- x
public function update($data, $table, $where = '') { 6 P' D8 Y. k( h6 H7 ]! _" S3 A
if($table == '' or $where == '') { ! q& K! I0 ~. j0 ~
return false;
' d" E8 n3 |( a# {, z } 2 B5 F: ~5 ]7 W# M' w% w) u$ V7 n
$where = ' WHERE '.$where;
3 v" L5 }3 o' f4 t* X( K% z $field = ''; : i/ A# m+ v% i, s
if(is_string($data) && $data != '') { 8 R2 @4 ? N2 [8 J. z
$field = $data;
1 l: I$ z& b# @, U: Y2 n" k1 Q) R- T } elseif (is_array($data) && count($data) > 0) { # ~, _% B1 b( e
$fields = array();
% J) K1 f) Q; S4 \* H/ e foreach($data as $k=>$v) {
8 c, V, Q* Q/ b4 k0 x1 A0 M switch (substr($v, 0, 2)) {
) r& z/ @* u# i5 d9 \/ c7 C. e case '+=':
0 y% N1 J3 D0 P) S" u9 {1 q $v = substr($v,2); + C0 \2 F& C- J) D! G) T
if (is_numeric($v)) {
- |# Q+ H. S6 x* j7 ?9 _ $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 6 n* C3 q+ ]7 X c, c
} else {
" C2 D2 Q$ v. t/ s8 O6 j8 B: \0 X$ F continue;
% l+ J: k% ^' U6 s } 4 u- Y7 Z) d7 N$ s6 @ `' H5 J
break;
0 F `; s$ d+ Q% H& w4 B( [ case '-=': ) r8 Z+ e& Z5 R% d
$v = substr($v,2); , I; a& O C5 u% n" i& d% ^
if (is_numeric($v)) { ) ?2 |$ e$ d0 ~& z4 S1 V# u
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); + m( {' Z6 D2 S& `/ ]' S
} else { ) ^2 l z5 v8 ~
continue;
, h( c( j8 V' L! ^& X }
' j4 T$ f% S5 K* b, ? break; & e5 T) s- u- ]9 D c% b
default: - |$ d- ~' `9 M- @7 G2 B3 t
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
+ Z( w- U+ Q1 s+ g6 q } % C* u9 C. Y6 e# Z# E8 N5 P3 {) r
} 7 r: z% Z( h3 \" O4 \4 x \
$field = implode(',', $fields); 6 k" _ S( d6 K9 b* P
} else { # O6 O6 Q3 l6 v) B3 k$ q
return false;
6 d# a/ h$ p; A- n, m- R } 6 C% ?' V$ R c4 v
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
/ I! {0 V( C( J% {; C0 H7 Q/ \ print_r($sql); / I6 u6 z2 L( g# |4 Z" @* y
return $this->execute($sql); ) m n6 s) Z' v
}
: m# h: ]% t' {9 o' ]从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。9 J6 d; Z" @" \1 V" b- O2 N
% C+ w' k) x; j% C/ P
攻击测试:, ?* g9 s9 O: O- Q! \! P
测试地址http://localhost' t4 F' |# x& K; Y
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句9 L0 K! C# J9 ^* q0 d' [
# d$ \$ P& ]+ Q* K 6 P8 P+ v# g7 v& ]0 k
& V3 Q% c* ?% `3 L: \ |/ y* @( K1 _& l$ d( @6 i: W0 b6 }* I
! f) J1 X, R- ~0 s' Y& z& G |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|