|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告9 G. t7 {) _; b, M" c' K+ O# q( ~# |
漏洞作者:skysheep
; K2 _! j, k2 n& ^: A/ V8 U+ E1 ~分析作者:Seay
2 K0 ^) o4 ^5 I0 E! q1 [6 a博客:http://www.cnseay.com// m! _) t+ C* r& t3 K1 g
漏洞分析:
% ~4 b, Q# B K5 ^) Y- e 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
9 X8 ~, E5 n- `# y4 S" I
; F& ~7 L1 p* |: F% c* l6 O5 F4 @0 J1 `) L& I+ l$ X3 i
' G0 g, I8 o4 ~6 |9 n5 x
public function account_manage_info() {
1 F# D* m) _$ R( ~4 i2 C if(isset($_POST['dosubmit'])) { * W+ V, S8 ^: k+ Z2 t/ u6 v
//更新用户昵称 & M% A; {" |8 ~: _7 g; v; A$ s
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
H5 m6 \% q2 _' Z3 W8 U" G9 y) V+ b if($nickname) {
1 Z- @8 ]3 ^4 C) y& c $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
: U6 X$ }8 ?9 b! Q9 a4 U2 e; U if(!isset($cookietime)) {
7 U& F9 k8 k( ? $get_cookietime = param::get_cookie('cookietime');
) r- N3 N; O1 J+ R }
8 E3 N+ Y% P4 C $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 6 ^5 C9 H6 a% c$ _$ x( A
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 0 R8 y+ Z5 c' F" t( f+ q
param::set_cookie('_nickname', $nickname, $cookietime);
' Z# S3 r1 d# C } ( }0 [# k' A" s+ {& p
require_once CACHE_MODEL_PATH.'member_input.class.php';
2 q6 o! [& M( e2 v3 p require_once CACHE_MODEL_PATH.'member_update.class.php'; 7 e2 @1 f5 F& E: H
$member_input = new member_input($this->memberinfo['modelid']);
2 k3 H3 v& R" b0 ~" N# S& j) P $modelinfo = $member_input->get($_POST['info']);
' {! x6 ^! A& l6 ?! M$ M& } $this->db->set_model($this->memberinfo['modelid']); 0 o! t( E) o* ]1 [
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); , v4 |) F; f) Q, W) [
if(!empty($membermodelinfo)) {
8 p" J# [+ i, J5 z" C# @- f. G' A $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
3 q# f* S) A$ e4 X8 X- N" _; y } else { * L$ q1 e. y) O
$modelinfo['userid'] = $this->memberinfo['userid'];
6 z8 ~6 x1 ^! p u, A9 S, v6 l $this->db->insert($modelinfo);
& w S6 \' B* F" C } 0 M. ^" O4 E( I; S
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,( s3 ~% q: ]) N4 l3 O
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
& S: u* ~ l, c6 \
" b/ h/ S1 `+ f4 i0 S5 f. b
% B4 o' P. w8 r% b$ L6 d; Z
X( s! E# w4 Q( e& Afunction get($data) { , K9 Q& l$ \; q5 ~" O9 }: i% `
$this->data = $data = trim_script($data); + M7 D+ m) M" ?% f4 e: o( T
$model_cache = getcache('member_model', 'commons'); * b' ~9 Y! N% t' k3 P- O* M8 O
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
! W1 Y0 v) v* u7 }2 {* w $info = array(); 4 ~3 W' ?3 b U$ A, @
$debar_filed = array('catid','title','style','thumb','status','islink','description');
+ ^, `# }+ c( ?- Q z* u/ K: ~ if(is_array($data)) {
2 ]* o* p D& K foreach($data as $field=>$value) {
7 ]/ p7 R6 D! }4 d: l if($data['islink']==1 && !in_array($field,$debar_filed)) continue; u! k. I2 S6 l# a5 \ N
$name = $this->fields[$field]['name']; ' { y N) U$ z9 ~$ T/ w) ~
$minlength = $this->fields[$field]['minlength'];
% l9 [2 `& P/ O $maxlength = $this->fields[$field]['maxlength'];
$ e+ ?) E: z! ?$ E2 g V $pattern = $this->fields[$field]['pattern'];
# h. B; t/ D4 V' m) b! W $errortips = $this->fields[$field]['errortips'];
* Q( F' ^$ I' m) t! W if(empty($errortips)) $errortips = "$name 不符合要求!"; 7 N7 J1 r7 b9 `, ~- t' ]' f' o
$length = empty($value) ? 0 : strlen($value); , I) e4 Z. H g* P4 B
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
, B4 v8 W, A1 r( N if($maxlength && $length > $maxlength && !$isimport) {
& T' k$ D9 f: L. R7 U showmessage("$name 不得超过 $maxlength 个字符!");
" k$ J4 S, `- f! q- n6 ` } else { : t6 v' ~) M( w0 V. i$ J
str_cut($value, $maxlength); ) p2 Q$ E# o8 D1 S
}
' N5 K" R+ {' h0 L if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
4 v9 [3 g" p+ r" d" A$ B if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
! R* S6 s1 @/ ], ]' o $func = $this->fields[$field]['formtype'];
0 n! P9 Z5 j6 m if(method_exists($this, $func)) $value = $this->$func($field, $value);
, y) P* a. q! m $info[$field] = $value; ; A$ N- _1 C5 g' d) F2 }1 W
}
$ m$ g* I; H+ q0 n$ x) _# L* x5 [ }
! x: c, i$ I9 p0 s9 @3 B0 j- a6 I return $info; / m3 R; U, A7 @* J+ `
} $ s$ L; d5 X* a/ f
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
* C1 B/ O ^. E5 y! M- K" }' ^ ^8 M
再到phpcms\modules\member\index.php 文件account_manage_info函数, g5 @, H+ a4 Z
过了get()函数之后。: x% e7 e8 t/ J$ U6 j3 P( r
# i9 v9 t9 C2 h7 A' T
6 S$ g0 Q4 [; E! ~* I$modelinfo = $member_input->get($_POST['info']); 9 N9 M# e* A8 S4 V( M3 G' p. e V
$this->db->set_model($this->memberinfo['modelid']); 0 j) S7 [' D. I. T( r( |
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
8 {1 ?" p0 C# _) N5 Y if(!empty($membermodelinfo)) { . J/ V' s8 f$ F( I# b2 y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
% T! R; S$ g, |/ B& n } else { . W# u" Y6 ~4 A* m" f( p
直接带入数据库,update函数我们跟进看看 b# D, T5 r V# S, E( @; c& G% f
* D: ~% h5 E* z- Y; Q
' M+ O4 o4 U/ G0 E6 r/ n7 opublic function update($data, $table, $where = '') { ' m1 r- k2 i) `9 d2 M
if($table == '' or $where == '') { K( a& f/ c4 {
return false;
3 w6 m8 N" _% J }
; U. E$ J* q. t $where = ' WHERE '.$where; $ [6 A2 Q m2 H. b; Z* n4 u% o
$field = ''; 2 a1 u5 J- {/ u3 L" w' l
if(is_string($data) && $data != '') {
+ a6 r/ P+ f" T' @ @( V7 ` $field = $data;
3 k: T2 Z4 o L o- f3 w } elseif (is_array($data) && count($data) > 0) {
/ ?9 e* e! F' S& r $fields = array(); 9 J0 z; \* v3 y% e
foreach($data as $k=>$v) {
6 k! y5 C. C0 a2 R+ x# E* b2 r( } switch (substr($v, 0, 2)) { ; B: p8 x( Q; l( E. R/ n
case '+=':
0 u5 u$ n4 g& {+ S6 O $v = substr($v,2);
; K& @/ D' R: E, T: w if (is_numeric($v)) { 4 y" O$ \$ U- G- g3 q
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); " m" v! g/ V: d+ j+ ^( u' M
} else { ! C- V$ o, Z. D' R+ f3 i3 ^$ I$ {6 s
continue;
9 Z8 M+ A1 V% c5 R& u! {$ A } ( e; X Y2 T l% i
break; 9 v. K( [! M, t$ ?; \# M. N: U
case '-=': : m* b2 G7 k! ^2 B! H/ @
$v = substr($v,2);
% q. Z/ e7 Y- j: U& i2 b if (is_numeric($v)) { 8 d6 m, |. E- r i. F! O
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
7 @" q1 D5 e) z. V9 t# T- G4 W: d } else {
& b$ b; J2 S! r* \ continue;
0 F) a' p1 v) {5 q) i/ v/ i( I } 7 ?4 [; J8 t, B
break;
/ j+ F$ J! m- |) a default:
3 ~5 U2 y, W3 o* c! t+ K! o1 A) C $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
$ H$ S9 _9 g, z1 T } & W8 G2 H% `) Y3 F% t4 w2 t
}
. z$ ?# b6 ~% p. g" c K $field = implode(',', $fields);
; i2 b4 E0 p" G% e0 g' B! J } else { # A+ m g+ W/ z( R% B
return false; . a. A1 F4 K: D6 k: {, f3 m; S
} & z" S; s2 C6 ^
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; . e: k9 J# E# J9 S8 r
print_r($sql);
) O3 l5 b7 O9 p6 @ return $this->execute($sql); ' w" U* e& H- G" e( p; ]8 i# F" O# U
} d( w; S6 V, ?: Z+ M5 c& h: v
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
; O% y! B7 s6 |2 F# D6 G
/ t1 }9 |$ C6 Z6 B- u攻击测试:9 b; e8 m+ W6 l1 y
测试地址http://localhost8 D: N# V5 M5 H, I# I, v- Z
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
9 ?% z- [& C1 T3 ?5 a
6 F( c6 h1 J+ f. N0 y % _6 J; m, @: ]3 V
}* t" r& X" p
( }0 T v" Z& K7 c/ `1 \3 r! j6 K$ N( R: l" k3 @
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|