|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告' W! j+ V! H' f" ]3 H
漏洞作者:skysheep' s( d7 ^# Y& r! P
分析作者:Seay
$ m# r; ?( x. O- @: n' O' x9 x$ T博客:http://www.cnseay.com/# M' r$ p% h/ c' Y7 h+ X
漏洞分析:# O' D# u- g; X( v2 z
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
) f: m) L3 U1 ?) z7 [1 t6 {4 T& P- {. ~" ]7 z. X5 q; X! |
# E F; B b7 O# k 3 b6 P: e# o# {. _ S# N
public function account_manage_info() {
7 J* g, u' H, Y if(isset($_POST['dosubmit'])) {
" ?$ e( q I! ]/ l8 _ //更新用户昵称 1 g: d: Q+ G9 U2 G7 s" Y" t
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
# F8 ?9 t; O( z1 [7 G! _ if($nickname) {
" R. V4 \ Y* u $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
8 T- B' h0 Z8 J- i7 f9 W* ^* C* D if(!isset($cookietime)) { 9 x& w/ o7 q6 m
$get_cookietime = param::get_cookie('cookietime'); + c! c0 }* i$ E9 q4 a$ w& L/ E7 n
}
* Q5 e6 l3 d4 ]( A6 Q3 o+ K $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); , o# h3 @9 v5 Z
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
% t0 f+ }, b n8 H x param::set_cookie('_nickname', $nickname, $cookietime); & v5 ~% J3 H n8 l6 N$ d# e* a
}
' N# @9 ^% C4 p1 D- A5 [ require_once CACHE_MODEL_PATH.'member_input.class.php'; 6 V( N& I2 U; p6 J& N/ B# n4 i
require_once CACHE_MODEL_PATH.'member_update.class.php';
) b/ i4 }% O1 t$ z6 P $member_input = new member_input($this->memberinfo['modelid']); 7 \7 L6 k! m; {! d
$modelinfo = $member_input->get($_POST['info']); 3 B3 [# r! U# e F& P" N
$this->db->set_model($this->memberinfo['modelid']); * I! W8 Y8 s, M( f
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ! x2 F b# I I" K; y
if(!empty($membermodelinfo)) { - T/ }& O* n7 g8 M* N$ [. }( z
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
" O5 l j5 Q" G, ?$ [3 w } else { ( ]: f, \7 ]3 ^* ^9 j' t
$modelinfo['userid'] = $this->memberinfo['userid']; 6 S" p+ x# y+ y7 O \
$this->db->insert($modelinfo); 7 |1 c, B% F" e3 ?0 B6 G/ @
}
, Z* g5 z& M# m. ^& c4 _& a代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,! q+ k* Y) u7 N/ A$ z# F
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ |- |* y7 D) n4 ?6 U
% P2 j1 v* P/ [& Y( z
: I j z$ }( d % Z' M2 s" \2 A# q. ?
function get($data) {
3 M1 _: i- V4 c# O0 N7 E6 W4 R $this->data = $data = trim_script($data);
9 K7 \+ v% b0 o $model_cache = getcache('member_model', 'commons'); * Y- D/ Q0 h7 p, U
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
; \9 [# Z+ w. q* ~& y $info = array(); + |$ D1 x- W+ o* ]8 K
$debar_filed = array('catid','title','style','thumb','status','islink','description');
! c. n! \" c& ^5 S' z3 s' q if(is_array($data)) { 3 M' ]" h2 H" s7 P9 y2 \; ?
foreach($data as $field=>$value) { ( E3 a+ D' ^: X5 E" y1 z
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; a( {% i1 S% B1 J7 ^; p# |! s
$name = $this->fields[$field]['name']; 0 O) L8 o ~8 D0 M3 W: F+ Y3 ^
$minlength = $this->fields[$field]['minlength'];
) v# d6 ^- L: [9 I6 k0 j $maxlength = $this->fields[$field]['maxlength'];
) z1 P# j# Z1 |6 I3 I. u2 { $pattern = $this->fields[$field]['pattern']; 8 F9 Y( g2 @, x- K( f
$errortips = $this->fields[$field]['errortips']; 3 K4 c4 ~9 q* [7 |
if(empty($errortips)) $errortips = "$name 不符合要求!";
6 d; U- T7 N& @ $length = empty($value) ? 0 : strlen($value); 1 u6 X) T( k: ^; o! y
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); * h, Y/ o7 t8 A% F% F( a l5 S
if($maxlength && $length > $maxlength && !$isimport) {
) N9 }' [' m# [ ]7 ^6 ~6 x showmessage("$name 不得超过 $maxlength 个字符!");
' A( ~; Y4 D8 y } else { % F6 i+ _; V9 l+ K& R2 K
str_cut($value, $maxlength);
: d' X& o' `/ L; Y8 Y1 T/ J } / g" c/ O7 ~! R$ z r7 |
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
c5 H* U$ Y* s2 n) \+ h* o ? if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 5 o+ K2 H' B1 Z3 [
$func = $this->fields[$field]['formtype']; 4 W d* H* _2 p# h9 B( J0 \- ?; [
if(method_exists($this, $func)) $value = $this->$func($field, $value);
' m$ d9 y& d2 W* L8 g$ A $info[$field] = $value;
5 Q( p9 @4 h; p } 1 u3 ^( }+ P4 |
} # }! Q& D4 N% C6 p- C* m5 T6 f5 n
return $info; * o+ V" F9 q: _" n6 r
} 7 ~/ T0 N$ _4 T: q9 P: f7 B8 @
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
- m: g8 `( I+ A: y* t% H s$ H, c/ O* t8 G6 e; { Z
再到phpcms\modules\member\index.php 文件account_manage_info函数( T7 @- w) o& p6 ~
过了get()函数之后。
: H3 c" w) E# j3 L" l, R6 S% y# U8 n! y2 s, G: G- g, z/ Y
# ~# u, b& X6 Y9 B {$modelinfo = $member_input->get($_POST['info']); : E: q+ h |' Z
$this->db->set_model($this->memberinfo['modelid']); " P. W: k5 n, f" @
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
9 o" V3 Q/ J+ G5 I' d5 J: Y if(!empty($membermodelinfo)) {
% j$ ?* Z# M; G+ |( }7 A/ M $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); $ z( ]$ H5 g. Y/ r& T8 C# {4 f
} else { . X: x- F9 f- b/ u
直接带入数据库,update函数我们跟进看看
5 S. Y5 B- c+ ^! s5 P- G+ Z9 I% I# y4 l2 H
# G# J3 K$ \5 W2 p B$ epublic function update($data, $table, $where = '') {
$ t! p" [) f6 k, D7 Q, T if($table == '' or $where == '') {
8 R/ s% b0 d, g1 g: H return false; 1 S) v4 q: p$ B- X
} 3 M9 @* v; A! w( U4 J& o
$where = ' WHERE '.$where;
) M) p" m l+ W# b $field = '';
' ^: p4 S8 v/ f& H4 m/ L# y. ~ if(is_string($data) && $data != '') {
" l" q) [) |$ @/ ?) D1 q% p" d! P $field = $data; 2 n) \) v) J2 H1 A k0 P$ f( ^
} elseif (is_array($data) && count($data) > 0) {
( Y( ?. {/ C4 w $fields = array(); $ A2 T9 B8 I! Q$ c5 X" s9 t
foreach($data as $k=>$v) { ' _9 f8 z3 H# P, D, ~
switch (substr($v, 0, 2)) { 8 Q j2 B- s3 q# E+ [ a6 s
case '+=':
9 ]8 s0 V) c2 O% e/ ^ $v = substr($v,2);
! G- q% k$ L4 \ I6 ]8 c9 O if (is_numeric($v)) {
* i; u0 R: y% O, O$ P( ^/ y D $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
/ x# ? j) L% G' |3 E% m6 r } else { ' I3 ]; l2 j& e% t/ w
continue; # e! K# x2 w; I5 h: `2 ^4 \
} % b4 }; U9 } `$ F1 T
break; + h7 w0 j7 s% @) G# P3 W3 e
case '-=':
% P! Z8 c* r/ l5 u- M! T4 ~+ @ $v = substr($v,2); 3 y' k; W* B) L) r3 S% w
if (is_numeric($v)) {
1 x% a' H7 F% U $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
. V2 F. G; a d8 s F6 H } else { 4 h; @! R I \7 c, z* q
continue;
1 @8 e' y- Q) a } & l3 @% M7 J& p0 J1 K( n! r2 ~
break;
" z2 j+ v8 K8 z) ^ default:
0 h/ o( o1 I5 H' z. r; v I $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
. z: g$ Y9 r2 ~: i: g6 R } 8 I% b& h4 X# W) i$ L/ @+ O( l
}
1 X- }* M* m: Z7 H2 m0 q $field = implode(',', $fields);
, |4 M# J" ?- h4 ]% t) B+ l } else {
1 {) S7 k# t% F& J) b2 V' J$ ? return false;
% A; p$ G9 U r! \% _# ~ }
/ s2 t$ O5 ?& [* u $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; / x( q. u2 U5 w; n. V# s
print_r($sql);
: R; B8 t; t# I$ C) F) {0 `% X1 n! P return $this->execute($sql);
+ E3 J& a9 D) z6 U5 c0 I- u7 E } 4 K3 p w7 I* |0 t
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。3 u4 X9 Y( k" m$ g5 ^
" D6 C7 J* {) a/ u攻击测试:
# h5 { o; W+ H+ A7 g3 p# y测试地址http://localhost
. l% Q5 }1 Z; M7 k# ?! y# r 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句1 k0 u) \# [" I0 h/ t, m7 U: b
; b1 S; m" i% i; e9 A; C: ~
* b. g# m8 U* y4 f ~, S4 ~% J1 h% U2 C7 x0 {% a
7 R0 \; n- S& c; Q9 Q$ p
/ G: S" `1 j! R |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|