|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告* |7 G. ?3 k# |# A; s- W, F
漏洞作者:skysheep4 a3 m) X! C1 c$ R! n0 v" r
分析作者:Seay
6 l" c9 X2 {2 o4 }0 j5 s, L! L6 g博客:http://www.cnseay.com/# R, S- k" z2 [+ z7 y) X
漏洞分析:
+ }$ [' h) H! ~/ l C 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
4 N, W6 v2 t4 ~3 v& A# A; V9 M" }
* p- e' R; i2 t* @' e& T
* r6 m+ C7 G3 r- {' H: q3 p 9 m; |( G/ l5 T
public function account_manage_info() { 9 a1 ]# c; J) q+ C0 a* {' Z
if(isset($_POST['dosubmit'])) {
4 ?1 T" |6 [) ] //更新用户昵称
1 I8 ?2 p1 ^5 {: C* z4 _ $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
7 a) U) B# v' @8 v if($nickname) {
& i0 m0 j6 P( E! l $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
% G/ A/ E# Y2 n: D( }9 q" x5 \' k if(!isset($cookietime)) { 3 E. J- O. [8 ]$ l% L
$get_cookietime = param::get_cookie('cookietime');
9 p O8 I$ y( Y5 B2 P! l, x }
3 q* z; J3 @: U- ^ $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
& o' u2 I6 ~, |4 a, {8 \5 M* { $cookietime = $_cookietime ? TIME + $_cookietime : 0; 5 ^2 y6 m; P2 R. W
param::set_cookie('_nickname', $nickname, $cookietime);
, u2 x8 }9 m7 ]5 R" Y$ v U6 m } " s. Y0 ^! U; V
require_once CACHE_MODEL_PATH.'member_input.class.php';
$ E) |+ `) o6 G' ~7 j require_once CACHE_MODEL_PATH.'member_update.class.php'; 8 U( `; G2 T- O
$member_input = new member_input($this->memberinfo['modelid']); / ^, K6 N1 \, l7 X6 ~3 _
$modelinfo = $member_input->get($_POST['info']);
+ y. [( s2 `2 m4 Z. Z0 y( \8 R8 ^+ h3 ^ $this->db->set_model($this->memberinfo['modelid']); 2 E- k( }+ v4 ~/ K5 q d* ?4 [
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
4 z! @9 r3 q% \4 h if(!empty($membermodelinfo)) { 3 d2 t: X8 j( \* }& o0 t
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 8 \$ z- u2 T. j( B8 M, {. K
} else { # f1 b( ?% d8 s% @" o
$modelinfo['userid'] = $this->memberinfo['userid']; % T* p( c$ j$ a1 q* G6 [
$this->db->insert($modelinfo);
) |! M8 N# n% V4 I v }
* y1 J% y& a" W6 C5 E) W代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
6 q5 i; |& |+ Y在\caches\caches_model\caches_data\ member_input.class.php 文件中:& }, t! J4 f' K
5 ?' u$ E% G4 ^' |- z+ ~1 n- w' c& z
( g- D5 \0 y7 d+ L. K
6 O1 f5 f, ^& C; ~4 e7 W& ]6 C! hfunction get($data) { 2 g# y! }, ?, j
$this->data = $data = trim_script($data); / Z, t/ t5 Q/ L3 r% _
$model_cache = getcache('member_model', 'commons');
' N% E3 h4 }* v; S7 L# Z" a $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; ) G! v6 K& C1 N" _6 R! t
$info = array(); 0 ~$ z4 W! D/ n6 }
$debar_filed = array('catid','title','style','thumb','status','islink','description');
" e; l. Y2 \9 n8 u0 D6 ^/ s5 _' V6 @ if(is_array($data)) {
) i( E% g R, m8 U7 @1 i9 K foreach($data as $field=>$value) { # u! y( H$ O7 L% _4 w& `, \+ Q0 h
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; ( x5 g) v& b; T5 [, Y% V
$name = $this->fields[$field]['name']; . O- K4 z1 @$ h5 g* S n
$minlength = $this->fields[$field]['minlength'];
F1 M* E6 g8 s( ?- e8 l $maxlength = $this->fields[$field]['maxlength']; h7 V4 v# ^6 K8 ~ B
$pattern = $this->fields[$field]['pattern'];
1 V! s% P4 Q. S; u& d2 b& n* f7 } $errortips = $this->fields[$field]['errortips'];
- t: ?& O( S' I% t* ? e if(empty($errortips)) $errortips = "$name 不符合要求!"; + k N# A5 j' y V% s6 b% p0 g
$length = empty($value) ? 0 : strlen($value);
6 ]2 ]$ T9 P. b- H5 P7 e+ I' ~ if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ) p9 B/ b" I. B* d" e; N
if($maxlength && $length > $maxlength && !$isimport) { . M) n% c: T1 a" C- m. [; a& @' R4 U
showmessage("$name 不得超过 $maxlength 个字符!"); ( T$ w ?1 w7 p2 {1 l6 @
} else {
( n- ?2 u4 @; d7 a, w8 j8 w' @ k str_cut($value, $maxlength);
4 P/ P; `3 y& B) v }
# w' o+ X% i2 Q9 r% {) M if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 9 O1 Y1 Y' u" {, B- b/ }1 E7 I
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
7 Q g! L9 a. Y: d' h* p $func = $this->fields[$field]['formtype']; : L) N2 S, d4 Z/ j6 ^) C/ L
if(method_exists($this, $func)) $value = $this->$func($field, $value); " Y& r2 R8 K. h! Y3 Y6 j" m" ~
$info[$field] = $value; 0 ?" R0 ~5 [0 C9 B* q6 U! r- A
} 2 r" k; D& T* t9 F" _- Q: _
}
& P3 V3 V0 S4 P$ y) H return $info;
6 m0 i; g h- q) q } & | z5 Z9 x l5 \
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
6 {1 ~& P" A: ?
1 d" k7 `: Y, D) D, G. A$ r9 o3 P再到phpcms\modules\member\index.php 文件account_manage_info函数
2 R: S% H* I, g! e% X( A6 ^1 s过了get()函数之后。
1 y& w5 E! ~& s, H% _9 Q
$ H$ `$ v. _# _$ R; @" { 7 [8 P: _5 ]. m0 G+ N" x
$modelinfo = $member_input->get($_POST['info']);
, h/ R9 ^) t9 l7 u4 X R6 Q1 u $this->db->set_model($this->memberinfo['modelid']);
% R9 `0 n4 w. g, n# M6 A" S $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ' O% ?: m- a+ [' l
if(!empty($membermodelinfo)) { 0 Y3 L7 H: w9 k# G6 P5 s7 X: X o
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); " C$ Q# V% k& U7 P$ ?9 \
} else {
1 _0 o+ H0 _: r, \直接带入数据库,update函数我们跟进看看# R j" w! Y6 w' m# n& a1 z' X2 S0 @
7 A- v3 w9 Z/ T7 ~7 ~9 X # K& u; p5 q5 ~; Z' T
public function update($data, $table, $where = '') {
% l; D3 U9 B; e' S if($table == '' or $where == '') {
$ _2 k! T# d9 G+ R. E$ P return false;
^, j8 w6 h5 X$ c; P } 8 O7 i j3 v% o: Y; o8 g
$where = ' WHERE '.$where; % b; `6 E g; [/ x/ k: g; z* d7 [4 G
$field = ''; : h6 H; W9 `4 f% x6 {/ V
if(is_string($data) && $data != '') {
3 r) a) F' w k. ~. J $field = $data;
9 B7 R/ O* z- I" _6 J' y% c4 J } elseif (is_array($data) && count($data) > 0) { . |! _# f1 v1 Q$ H* }
$fields = array(); % j. b. t6 W6 M9 C
foreach($data as $k=>$v) { ! I! t2 ?% c% M1 E! }
switch (substr($v, 0, 2)) {
3 }- \9 E7 Z2 ?; P$ ?6 G case '+=': ; `; }: z# M/ r- I. ?
$v = substr($v,2);
, V5 F* J+ @5 k if (is_numeric($v)) {
/ D+ W4 |4 b: b& |8 w- d9 k $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); : g, C( {! f5 _8 b
} else { , Q+ X9 z. T! T6 X& ^1 o
continue;
% c" X3 T: ?8 t/ D! c, Z m3 |. S8 H }
; H& g) k9 A# H9 I break; ) ^# `$ y2 H8 x" w9 S& `# M
case '-=':
$ B$ p9 I5 d1 k% S; e T, \& t8 B $v = substr($v,2);
( n1 N. {0 ^4 A/ K if (is_numeric($v)) {
) @+ f! B1 t, n( Z& C $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
& @$ h( u* x) ]$ }/ _8 T } else { E4 K$ c* a7 y. P6 {* L
continue;
" d* f6 C2 a7 {6 B& X! L d }
n0 R/ ^, P1 k) d* a break;
5 T' M- N, Q& c; i' Z1 X default: & ~- \- [5 G' t- N( G! n. ]+ s
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
1 O! G* {+ e y" o7 l/ g/ ~ } 9 o* b) V/ q3 {! J, p9 V$ m; Y
}
. Q1 z0 B: u1 a. Y$ [0 G r" J $field = implode(',', $fields);
& X' |) q8 i( C8 ?, a# k } else {
. Z* ?: A i K$ h% j return false;
1 z% b! j" G3 R/ l' Z8 }/ v0 C; \ }
. @. c) Q2 s& a $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; ; `: \. f6 ]: s/ |8 t
print_r($sql);
% Y0 d$ I1 Z; x8 k$ G R return $this->execute($sql);
, ]/ a- S2 w8 ?$ P% l }
, D5 s0 M5 @. Y0 N( V; \. b从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。/ k @9 Q* ?8 ~5 Z9 C) T
1 E( h% P3 W/ H& n
攻击测试:
, j) Y6 d( O x3 Z$ d: h% q测试地址http://localhost- \4 g7 ?* O" p" w6 u
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
; C& V: P+ r7 b- ]
j1 L5 l7 K8 u. F' l9 b % @* v7 e) ^+ x. f
( y, X' s3 h) p# C3 Q. i
: s8 M# X m' G- o
4 ?8 o) }6 O7 B# ^' t" @ |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|