|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告' v' w5 }% y; [' T r. U
漏洞作者:skysheep
5 ~' s9 C/ k0 v' Q% l2 x分析作者:Seay9 y, q3 _) P: R( G
博客:http://www.cnseay.com/
( ]+ V' h3 _, l6 K, S0 \. j漏洞分析:
7 P+ X) L$ b+ l8 {* m 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
% R4 O& d8 A0 Z5 Z. O! H2 y2 U! t; I0 X; }7 K- o2 r p
$ y; S4 i) }, \: H
' ?5 p2 E" X3 M, p' H% [public function account_manage_info() {
7 Y5 C0 p* v9 |2 q2 X( a+ T if(isset($_POST['dosubmit'])) { ! j0 D- y( Q; p, v& p
//更新用户昵称
- `" |2 X2 W3 q- B $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 2 F* a7 S" |6 C; F1 m8 B
if($nickname) { - d' a! U5 r2 p7 v
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); / j: v/ I! D7 ]4 b" _7 f
if(!isset($cookietime)) { ' D& _4 U$ Q0 Y( B% W
$get_cookietime = param::get_cookie('cookietime'); 4 w3 _: `: _" @: `: F) G
} ! }0 w" `0 }9 n4 w" ^# Z
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); ) _1 L' s8 X/ I ?7 _/ J
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 4 y) Y( l2 k0 G3 @ o3 X
param::set_cookie('_nickname', $nickname, $cookietime);
$ e4 G5 U- U: B! s% M! t7 ~1 B9 B } 7 @( {* J! s* c3 M8 b' s4 E+ s! X
require_once CACHE_MODEL_PATH.'member_input.class.php';
! c7 P' R" g' ]2 G: M require_once CACHE_MODEL_PATH.'member_update.class.php'; , S8 K# j% u" [% K" G
$member_input = new member_input($this->memberinfo['modelid']);
# o& M( m3 I( L% d! _ $modelinfo = $member_input->get($_POST['info']);
/ ]7 o( ?/ F Q* @" K; I $this->db->set_model($this->memberinfo['modelid']); % p/ J% C1 e8 _
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
8 | Y' G8 @( K% Z0 e if(!empty($membermodelinfo)) { 2 C7 W. O' p3 a: @ D
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 1 N+ f; N5 X; f" ^ a4 k
} else { 1 B- e6 M% u' `4 j& o; @
$modelinfo['userid'] = $this->memberinfo['userid'];
& o d: B) ^1 F# a $this->db->insert($modelinfo); 8 N9 ]" K9 V: C! i2 G/ S. ?9 E% T, n
}
! F$ F5 M* o. [2 h% I2 x; L代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,5 v; p7 x9 g/ l" R# k; D: v5 P: C$ m
在\caches\caches_model\caches_data\ member_input.class.php 文件中:" r! n9 a# t& j) l. l" q
) ^$ L& H" W/ }! c! Q1 ~" ^
7 Z2 f( G; r6 L" O6 D8 l1 Z4 i6 ^
+ T2 a5 w+ r: z( F7 m! s
function get($data) {
+ i, {0 a$ k1 I9 m: Y7 m$ L; W1 N $this->data = $data = trim_script($data); " {- I/ D, ]% W, ^' `+ g
$model_cache = getcache('member_model', 'commons');
8 R# V+ t# g1 _* T2 G; z! o $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; * g. `# C9 h2 Z s: @5 y8 \
$info = array(); ; R' ^: u3 }# D; r( H( v! Q
$debar_filed = array('catid','title','style','thumb','status','islink','description'); ; {2 W5 B1 u/ u# Z% y$ @
if(is_array($data)) {
( j( a% X% L% E T6 T foreach($data as $field=>$value) { " H' Y; ^) U7 D: t6 {
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
& S6 ]! Y8 a0 y, _; o $name = $this->fields[$field]['name'];
$ V+ V a8 ^* C: k' v: g" a $minlength = $this->fields[$field]['minlength']; 4 Q" o5 J( F1 R; T
$maxlength = $this->fields[$field]['maxlength']; & T% B; @! Z7 G: h0 {6 @: Z
$pattern = $this->fields[$field]['pattern']; & F# I/ ]3 H+ x$ z
$errortips = $this->fields[$field]['errortips']; & q) `' q3 d' N. R/ l r5 u
if(empty($errortips)) $errortips = "$name 不符合要求!";
8 V, f! D; ]# V6 P9 r $length = empty($value) ? 0 : strlen($value);
( D. @. b) ^+ l if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
; _9 j+ H7 j& i) Z if($maxlength && $length > $maxlength && !$isimport) { * @1 e. F# P) B
showmessage("$name 不得超过 $maxlength 个字符!"); 7 }$ n1 G$ D+ c7 f* b3 Z# e
} else {
! Y- _; Q0 I% K2 S1 v: q str_cut($value, $maxlength);
' c* L4 J( @/ O$ m, F/ c+ e8 X% | }
4 A# t" O* s5 q8 b5 v3 U @ if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 2 x7 ?4 D: c" A2 Z; w$ c! \: O
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); " [3 Q0 u4 T" c& R8 ^; Q6 L
$func = $this->fields[$field]['formtype']; ; K- m1 g- E8 c5 h3 F' K5 ~
if(method_exists($this, $func)) $value = $this->$func($field, $value); 2 D5 h+ q" Q2 m; l% N
$info[$field] = $value;
5 f: s7 {! y& e7 \8 M }
' b, m/ F' b J! P }
8 ?# \. j' M; |+ ^0 Y/ C2 r return $info; . B. X- |2 z1 q9 P7 |$ b
} / r3 C: C; w- v$ D% n7 B
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
! B' I7 N/ {8 N) x! N' h4 D1 l5 B! ~
再到phpcms\modules\member\index.php 文件account_manage_info函数
) n5 A5 x7 p( W% S6 E过了get()函数之后。5 Y1 \3 d3 }2 G( D; ~
0 x1 l6 H7 N4 v+ y! c
% w( f* K- x$ h, K$ @ c( O$modelinfo = $member_input->get($_POST['info']); % G |$ T/ M& i2 ^ U- S) u7 f
$this->db->set_model($this->memberinfo['modelid']); 7 F3 I0 S3 H3 M1 r
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
/ i6 o7 w/ X& c S, p if(!empty($membermodelinfo)) {
$ o3 Z2 g- h9 _. w5 n $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
3 E$ L6 u; Y4 e9 p$ a$ ` } else { 6 ?7 W, `7 u/ [1 L2 n
直接带入数据库,update函数我们跟进看看+ w3 L0 C, E* ]6 G- V
+ Y! C, C& u. ~! U4 O2 X
0 {" t. H; }5 Y! `8 xpublic function update($data, $table, $where = '') {
/ K, W. ^2 u1 E* D* C: f if($table == '' or $where == '') { * X5 o l+ q8 B! c
return false; 7 {+ R6 l; w6 ^- a) s4 w2 {9 b, J0 Z
}
) z- l8 Q# {5 M3 U $where = ' WHERE '.$where;
. t2 Q+ k2 n ]9 c" y7 z! ] $field = '';
6 ^# q1 _4 g* H& R1 P3 B* T/ ^ if(is_string($data) && $data != '') { Y( @2 x* X" x9 w7 E; e. p. ?
$field = $data;
j0 O1 A+ }9 N/ R5 f } elseif (is_array($data) && count($data) > 0) {
. x1 P$ ^9 V, X $fields = array(); $ `6 R5 A1 ?# Y& Y2 W# ?/ \9 S
foreach($data as $k=>$v) {
# ^7 |/ b7 K3 e2 J) j7 w switch (substr($v, 0, 2)) { * R* W+ g& y# n1 H
case '+=': ! H) @& {) n& ~1 v
$v = substr($v,2); ! S U4 ]! G) i5 i$ J; f7 ]
if (is_numeric($v)) { ' ~, S8 c9 E: i4 Q
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
/ P: E6 R% f5 s2 v% _- K } else {
* @! O3 x7 r) `0 T" ], A continue; 8 {+ P; I8 E9 _1 l" k9 f/ b" W0 o
} . X- K, u7 q" R) j8 s
break;
$ @4 `1 z2 E( Z' d$ t- a- V6 p& X: T case '-=':
" b: C& F1 |" w$ K$ u $v = substr($v,2); - E1 j! n- m8 i& m6 A& `0 N) V" S ^
if (is_numeric($v)) {
7 z* p) ^! x( X& E9 D; C5 T, { $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
* e+ W- F% ]7 V5 G2 n: T } else { # _8 I2 K X4 e
continue; 7 j0 a4 p* a( I U1 ^' O8 s# [! I
}
3 C* f1 _& S( _% ?- }6 ]3 z break;
) |) n. k7 `3 S# [ default:
6 q& V; z! ^! A% m: Y5 d( x $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); * w4 ^* z) o; W: {( Z
}
Q3 f4 O# O( u6 P/ Q6 x) k }
" O; i' K5 k) t* X! z+ T+ _- Q$ C $field = implode(',', $fields);
) I. X! i4 L3 V' K } else {
5 V. s& f. [$ [1 M return false;
8 C5 a" }4 U: @% ]" j: Y& u! L } , @; ?! T" v) u2 F
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
3 P2 K+ R2 ^" Q9 ~ print_r($sql); : }& I. \4 L, y- E+ o1 f* r/ Y
return $this->execute($sql); * p4 }1 S/ N6 e0 t
}
; ?5 v4 K# B+ Z+ q从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
4 L, c. m& _5 k) O( `6 s! C1 P" v |) V/ W0 T, v) T4 N7 m1 m
攻击测试:
2 f$ q( y! w6 Y0 v测试地址http://localhost5 f9 M; F, N# y# z2 n
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
7 A4 D) V( ~, a0 s! r# P7 b
/ b9 l7 y' a: {! W8 Z; F+ U / t. k2 M5 C7 L. J& } ^8 }
% t% y8 e# d9 V. ?
! Y3 R, v8 c9 |2 [- | T/ r2 f6 ]' J& m4 Y2 C5 j
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|