|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
- z1 Z; R5 F$ t3 r5 J漏洞作者:skysheep5 u6 U1 R$ t" ]9 ~% _% ~
分析作者:Seay# j3 W- |' s: k0 P
博客:http://www.cnseay.com/) W) \2 E! n7 P* G S# X3 ~. M
漏洞分析:2 o- ^" H1 Y" \$ L1 ]# |" R
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。' M' f @4 h% w! \# y* _
# H* c6 M R9 I' t& L6 Y7 N Z9 Y
/ J- u w' t0 F; I
* R, g4 Y, W: p' `public function account_manage_info() { + N: A: j8 Q/ z. ~5 ^
if(isset($_POST['dosubmit'])) { - i9 x' J! `& {, Q: O* Z
//更新用户昵称 9 T3 G! o3 X, c& P9 v. J1 c
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; + ~. k. X+ A/ u
if($nickname) { ! B% k% E5 e6 q
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 4 K% R$ u3 T& Z
if(!isset($cookietime)) { + y0 \+ `0 B7 e, k% _
$get_cookietime = param::get_cookie('cookietime'); , ~- ]9 G1 t# s- R' ~- [9 E4 e
}
& @$ d. s6 P% j O1 L $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); " }! ^% F+ @) w% l$ X2 n$ w
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 3 J9 \4 j7 Y9 ]7 I
param::set_cookie('_nickname', $nickname, $cookietime);
2 `2 ^& m/ S! h. I$ E }
, x* m2 s: a4 i8 \6 d/ O require_once CACHE_MODEL_PATH.'member_input.class.php';
' L% D( V2 }4 v/ S" c require_once CACHE_MODEL_PATH.'member_update.class.php';
% [+ g: R/ D! E$ L8 G7 V/ t $member_input = new member_input($this->memberinfo['modelid']);
( g2 h& d% R8 y" T0 F $modelinfo = $member_input->get($_POST['info']);
5 X9 U& r+ n. ~0 B+ [0 F* S6 z $this->db->set_model($this->memberinfo['modelid']);
- E2 Z0 O( K" {% P* ?. A/ X. `0 { $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
$ H Z6 q" ~7 @. ^1 y! k+ @" w if(!empty($membermodelinfo)) {
- A+ b! K7 `7 o+ F. G c; b $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); : G; J& }, {( @
} else { 8 }9 V$ L4 `4 Z. R
$modelinfo['userid'] = $this->memberinfo['userid']; ) y( V6 S8 v# R/ i
$this->db->insert($modelinfo); + C- ] j" I1 R: `9 ^! y
} " N( w: T2 K- _0 Z& K
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
1 Z5 f0 }+ u7 s# l在\caches\caches_model\caches_data\ member_input.class.php 文件中:4 ~) p# ^$ @5 m; j5 S) s5 [
) d. k5 O" J) I3 y/ V! z" O( p. R' o; n
) n+ p2 P" e9 Z3 V* b' h. qfunction get($data) {
* Z; j9 _1 i$ N% B2 Z8 b $this->data = $data = trim_script($data); " Z+ s3 f* A- X: \: y, h
$model_cache = getcache('member_model', 'commons'); 1 W0 e7 ?& h0 m& V- a+ |5 v
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 3 A0 W! F+ {* o7 M7 h, i
$info = array();
5 o X- ?1 K! p! N6 C% l $debar_filed = array('catid','title','style','thumb','status','islink','description'); Y ?4 o% D2 y) h t B5 K
if(is_array($data)) {
+ v6 R! T1 ]# e4 e- ~2 W7 E foreach($data as $field=>$value) { ) ] l: b( R4 S7 s* S5 Z8 U2 \' e
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 9 K: i! G" X4 {
$name = $this->fields[$field]['name'];
1 B* D$ l9 k2 o" G5 I9 m [2 q $minlength = $this->fields[$field]['minlength']; * I! y \1 R" p8 X J! d
$maxlength = $this->fields[$field]['maxlength'];
. a6 `2 n& V8 M6 H $pattern = $this->fields[$field]['pattern']; % s: U. X: s: R: }% d* N
$errortips = $this->fields[$field]['errortips'];
2 e# F6 x* `# n0 {) w8 ?. B K( f if(empty($errortips)) $errortips = "$name 不符合要求!";
- J. Z2 d& I1 a4 T3 K0 x $length = empty($value) ? 0 : strlen($value); $ p2 [' L: a! ]" [* m. Z
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); * U( q! @- ]* Y" C: m1 G
if($maxlength && $length > $maxlength && !$isimport) { ( I% Q# V, F( n' r
showmessage("$name 不得超过 $maxlength 个字符!");
. E- R9 G! @8 i- W% V } else {
. K1 z" x" P4 n5 W4 ]8 N# A1 J str_cut($value, $maxlength); . X" k. q& J, B s6 s
}
# ?! p; N+ o2 I if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); : D$ a. e+ h9 \0 d6 c$ p0 o
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
$ I4 j. u! j1 P O/ @( j8 l $func = $this->fields[$field]['formtype']; 1 C; p& n0 i+ s: i
if(method_exists($this, $func)) $value = $this->$func($field, $value); ! n7 D6 r. m0 L' z. J9 P, o/ Q
$info[$field] = $value;
! b( `% D* c: V9 r& |' X }
; Q; k& y/ `4 a2 D8 v$ U7 a }
& }+ o( M6 C/ G/ i return $info;
, ^2 C: ?: {6 p }
- y2 a' S5 ]+ b) ]: j2 V, ~5 ?0 dtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,) k+ H; z* U! G, H X* y
0 A6 I3 `$ i6 G- h
再到phpcms\modules\member\index.php 文件account_manage_info函数
' o- S, ~4 t2 G4 ], I# P a过了get()函数之后。
; _! ]& u5 C# c6 C
; y' M& n: Z4 y' P ) e9 R* ?6 ]. B
$modelinfo = $member_input->get($_POST['info']); % ~ ], G8 @+ q7 g$ }
$this->db->set_model($this->memberinfo['modelid']);
1 ^( w5 l7 t- Z, i" {( ^ s $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
5 i9 u/ s. z3 U9 ~! c if(!empty($membermodelinfo)) {
9 K/ S: W3 ?) m" ? $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
! p& m/ J$ P) M' m } else {
0 r+ m. _% z8 @' f: d$ [直接带入数据库,update函数我们跟进看看
; @ a7 c9 t+ F( r4 {1 x0 G3 @9 X* ~1 ]% X5 ]1 p
% ^* ^1 O& v1 F1 qpublic function update($data, $table, $where = '') {
. c) F% r* [! z: ]9 z if($table == '' or $where == '') {
/ S4 C6 b( K6 l: ? return false; % G* {& K1 t) ?) u0 A
} 5 @9 D1 N$ O6 J: L( B5 r8 Y: X& d
$where = ' WHERE '.$where; . D4 w: V. F3 S# F1 B: N# t
$field = '';
3 `% L6 l" i1 e1 \; F if(is_string($data) && $data != '') { & ^ v" ^: ^7 E( A7 r0 V
$field = $data;
* x2 l( J" V6 o: M% S } elseif (is_array($data) && count($data) > 0) {
/ p+ m, Y6 o" ~9 w1 q $fields = array();
; Q: [5 i, A5 ^ foreach($data as $k=>$v) { 6 P: O, U2 W( m# Y ?# p
switch (substr($v, 0, 2)) { + {8 Z3 I/ T) q
case '+=':
6 E! Q! F8 D E0 Z" W $v = substr($v,2);
; [+ ]* {% Q0 u0 f" c if (is_numeric($v)) {
% z. b7 E: ^) X# X3 E' M $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
) Y" Y, V8 H3 d+ j$ e8 g } else {
' y ?1 [8 q: Z4 {6 f: B2 h& F continue; - k% t8 @$ {3 a
}
1 ^8 X# ^ Q5 } break; # y# C7 Q+ X+ M _; E3 y
case '-=':
4 H, _. p& L3 @; s) ~1 _! D/ x $v = substr($v,2);
$ T7 W" E: Q- G6 b4 V, E if (is_numeric($v)) { 2 S5 c5 B0 i" l0 K# l$ L5 c0 i
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
& M0 P6 R2 W' z' N! P. \& F/ K. J) |; Q% k } else {
9 {" l9 ~ |# S: I' I7 R. C' B; S0 \ continue; ; J* N+ @ s/ B7 L! M/ Y( M% N4 V
} ; \* j0 Y# f, @! Q' [
break; ! C! o _7 T6 T. z! x) E n
default: * }% d9 [7 C) T7 a+ k
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
1 R+ K% _- L1 }. W0 n+ K } * S0 I0 v1 Z) g: A2 h: s
}
6 v* I O2 a' q6 O% [ $field = implode(',', $fields);
! {/ C# b4 @( |* ~, P$ ^3 t } else {
" }3 p+ t* B2 o' E, C return false; , T$ t" W2 x4 m3 }
} 7 N4 g+ ?9 S V" R7 I, m
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 5 B3 b/ \. @8 c
print_r($sql); ' {$ S( y* _/ ]0 {- A: n$ A
return $this->execute($sql);
5 v+ H) o( S% m$ k: t: W } $ c. }9 e6 U/ K# q5 Y4 J- S/ a" z- O. \1 ]
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
+ i& X/ a! V+ K5 y3 s. H
; W2 \# N) y1 r1 W# R) O攻击测试:
$ t; x( d1 V/ z& i5 ^测试地址http://localhost& }6 @& \2 a5 A2 O8 q
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
1 }% v; Q8 C) b1 G/ a5 W! F* P/ A4 t* R, [! Z( D
8 d0 p$ [3 T# U) \+ N$ w$ b$ S0 F( m! ]* @! e4 ?
( H! y Y7 P c1 N/ s% `
$ p0 |' y6 h! P) X, G |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|