|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告2 k q' d% @) V7 g6 t
漏洞作者:skysheep" G, K9 k# e4 r: U; X+ u
分析作者:Seay/ x' z9 A: x, I: `/ s. m& y
博客:http://www.cnseay.com/
$ r% U8 Z- e8 \& s9 h8 \漏洞分析:' J6 A$ }" ]# U4 M9 w+ G7 J g
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。* ~4 b+ W2 {5 j6 G0 l5 E
+ D$ }& N+ W. j# `2 w
/ W' B$ I; H* u3 ]6 B! v
. c0 K, e6 a8 O6 T. d qpublic function account_manage_info() {
4 `, m5 y Z# _% E. q2 [0 u% v if(isset($_POST['dosubmit'])) { 4 I( J" \% u/ B6 H# _" f. M6 E+ C
//更新用户昵称 4 h% X6 D! v' p/ F4 _
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 2 b2 j& x2 d. B; I
if($nickname) { $ C9 n: Q6 E/ e/ `3 X( `
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
$ d. ]" ~4 D) d1 }3 b1 F' N if(!isset($cookietime)) {
+ p& q7 I/ F& P $get_cookietime = param::get_cookie('cookietime'); ) P! D0 Q L" c0 N+ [ [8 I( r2 f
} . f0 t; j- \" [2 I6 N, x' Q; ]/ n
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
: c: }. o/ k9 n' t3 Z0 ` $cookietime = $_cookietime ? TIME + $_cookietime : 0; , l# ^ O7 M2 k d8 b4 C7 y& A
param::set_cookie('_nickname', $nickname, $cookietime); ! ?8 q% S7 i3 F% ~; L
} 4 ?( Y! n% l% E1 `# g$ ~$ ]
require_once CACHE_MODEL_PATH.'member_input.class.php'; 6 W9 f; D1 j0 s2 a; [* L
require_once CACHE_MODEL_PATH.'member_update.class.php'; 8 q, e* W, C5 L0 c6 s( j1 `
$member_input = new member_input($this->memberinfo['modelid']);
7 ~# U1 B* b2 L5 D/ k( t9 z $modelinfo = $member_input->get($_POST['info']);
4 _ H+ E& V) d $this->db->set_model($this->memberinfo['modelid']);
7 @0 ?: P# k8 K, L6 y% Z $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
4 f" q* B% ^8 [ if(!empty($membermodelinfo)) { 3 j" r/ \) A$ s/ V) @
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
" P$ B& J9 k, I } else {
6 T, h$ Z- Z0 H# D% q/ J $modelinfo['userid'] = $this->memberinfo['userid']; 8 \) ^3 }3 L) ?% ^, k, [! f& s
$this->db->insert($modelinfo); 0 G5 C1 s2 }. ]) o+ a. Z. f
}
( C2 y* d# k5 ~- S& k9 g代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
* a0 N6 T0 C Q# W4 n/ O; I& [6 s0 S在\caches\caches_model\caches_data\ member_input.class.php 文件中:
8 J c! o3 p5 J3 E6 v. _1 |
& S! Q# g3 {( L/ K& P" S; X4 r6 U: w+ }
3 R5 N7 t @! I4 M$ |* efunction get($data) { ; d6 b& ]2 N, D1 |4 z6 }; e \! P; W- w
$this->data = $data = trim_script($data);
4 ^- k0 p X' _ $model_cache = getcache('member_model', 'commons');
8 ^( h/ D/ I- M0 z" w0 E; A$ Q $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; ( U( Q. } z# x3 `& x1 B
$info = array(); 5 D$ H) x/ i: ?+ B/ F# O
$debar_filed = array('catid','title','style','thumb','status','islink','description'); 6 y! x9 B) c' @: y9 m% l
if(is_array($data)) {
7 k4 l3 I# r" b' V6 M- D- R foreach($data as $field=>$value) {
( T& Y" b5 O. h4 f' t9 V) a7 F if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
* R/ q- k* _( i $name = $this->fields[$field]['name'];
# ^" x" ^, L2 W$ }6 K9 S1 B! Q $minlength = $this->fields[$field]['minlength'];
3 Z4 r7 B9 N$ v6 T4 j $maxlength = $this->fields[$field]['maxlength']; 8 n% _' r; H4 \4 @& n6 T) w1 X
$pattern = $this->fields[$field]['pattern']; 9 k& A1 I" m4 l! e( A
$errortips = $this->fields[$field]['errortips'];
% z: Y6 v" W& N8 s5 }! r6 K if(empty($errortips)) $errortips = "$name 不符合要求!";
4 R& m" _1 R2 g! b $length = empty($value) ? 0 : strlen($value);
. Z9 H+ ~& N" k& ] if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ' b' q" a8 B8 r H9 P
if($maxlength && $length > $maxlength && !$isimport) {
) s4 X# N+ B4 L. l2 h$ G showmessage("$name 不得超过 $maxlength 个字符!"); ( \/ j1 [% ?4 J) ~+ `! c G! X
} else {
\5 _! h/ t G# P V! [ str_cut($value, $maxlength);
$ @ T5 P4 t# D" V } 6 @7 k$ I# e8 G' I) }
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
* U5 x9 t! V z' R if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); ' F3 Y5 c, e) x7 I
$func = $this->fields[$field]['formtype'];
4 w k5 X" v0 F# b1 Z if(method_exists($this, $func)) $value = $this->$func($field, $value); ' `. S4 O1 g, u8 u
$info[$field] = $value;
# |/ E/ Q3 V+ ~! w! C }
9 t! P, {; S, L/ w2 F }
& T2 ^4 Q' i' R" Q. b8 G% R return $info;
) X! P8 U: C" _4 `: h/ x } $ y" S9 m0 f3 ~
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,& b6 p7 p9 W" x9 e/ Z
9 T2 l2 T- E: M! ]
再到phpcms\modules\member\index.php 文件account_manage_info函数' x, N( {& h3 J3 y) f* s' D
过了get()函数之后。
, _- d1 _" \3 l* W$ E5 Q1 t# f5 t' `9 A2 N) m5 V
9 J) |' B. M# b2 i. g5 k- B# i6 V
$modelinfo = $member_input->get($_POST['info']);
& Y0 Q" G v2 A" X, ` $this->db->set_model($this->memberinfo['modelid']);
6 Y* t' j& ]. K B0 p5 ?8 j# _ $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
9 E9 W8 ^' ~" T: J7 l# q if(!empty($membermodelinfo)) { % R$ m! p+ z4 C9 L
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
- g; }0 }$ E4 l# C- |$ { } else { % m) P# |, I7 S p8 n5 _+ _
直接带入数据库,update函数我们跟进看看7 I6 @# h1 d; {% t$ S
& H: I; i) q5 g* l4 K* t; l
) n) h. ~! ~$ |) g& Apublic function update($data, $table, $where = '') { + K7 L" X# S, |& Z1 w& Z) V/ B
if($table == '' or $where == '') {
K& B" w* i. U N# I# p return false; 5 s8 E2 B; S: `+ s& x# e
} ( Y' |% a1 q U7 I' j! T) Z
$where = ' WHERE '.$where; 7 S8 D3 |! O: R0 S/ N- B* d
$field = '';
3 c4 Y' t# U/ k* f8 @ if(is_string($data) && $data != '') { ( Q. z3 E' | u6 f5 ^8 }
$field = $data;
! k- p6 y8 r/ W5 N- N } elseif (is_array($data) && count($data) > 0) {
, i8 i4 m8 J- R1 n& o $fields = array();
2 i" s/ ?) f$ ?: n! n) O foreach($data as $k=>$v) { $ p& |9 \6 l8 {1 d. e
switch (substr($v, 0, 2)) { ' k3 o. c/ x9 c! Y( E
case '+=':
' S/ c6 ?0 x+ M6 w: H" W $v = substr($v,2);
3 Q3 \7 I# C4 u( k if (is_numeric($v)) {
! G( S I0 Q2 {, q $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
) |6 ?' }; B* h2 y } else { + `2 ^8 ?; z# q1 Q e- h* ~7 L% Y
continue; : [$ p) B/ `- \) D* R; l
}
4 m$ l. i' y4 M) ?5 r, E9 U" x break; 3 E0 P/ Q2 b! y) F% ^4 C: `
case '-=':
% t% ~/ N) ^- \! f$ _& m' ^" q $v = substr($v,2);
8 @/ I8 H. W( ~. Z- K if (is_numeric($v)) {
; Y# s4 I4 M l! f3 o& W $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
$ m1 f( r9 L$ J. M } else { # x7 _/ U- F' @, s2 M& u
continue; / g" m% q7 L+ w4 I& c3 d5 P
}
$ J: B1 E0 B. Y m break;
) R3 c5 k( M0 W& e' h" C. q0 @ default:
3 n/ K1 W5 h: Q s. ~1 P $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); . d* ~0 Q& q. H2 I- [ j
}
! P$ D b0 W; s' q3 A- Q }
5 p# i2 Q# C& p# ~4 W $field = implode(',', $fields);
$ V; {2 V/ U. o! J$ S9 G7 k } else {
/ p& A4 f. Z7 T8 J p) S0 e8 N2 ?0 W2 H return false;
& x, Q6 ]' t/ D: [0 r) {. I }
& e" H8 ]6 V$ [( ?& M( ~+ q $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; , ?- |0 P+ `- q$ h6 ^" |) x# H+ J
print_r($sql);
7 \9 h0 K) E3 Y return $this->execute($sql);
" n- u( g# d' Y' O' r& P. G } 2 }. o k F6 Q& M' ?0 _4 c8 R, {
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
* q- i: J2 n% ^0 t7 J! \
# ~+ M H1 T9 o5 a! O攻击测试:4 Y8 C+ }" d6 u/ @4 Y$ U, s
测试地址http://localhost6 V1 l& F1 V0 ?* V2 k; m4 ~
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
% \* f6 {% s$ v5 _5 j# [# H6 L1 G, e- ~: ?" l: `: Q8 c
7 r0 V3 t* |+ F7 k6 R2 p2 _
9 T; i. o2 H8 O$ R$ M& V& @/ o
0 ~& }! F& P! N3 b# r m% e6 q; m2 \5 M
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|