|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告4 |! j. u) x) F4 y. e
漏洞作者:skysheep
' |! Q# i) ^; I7 A7 Z4 u分析作者:Seay' I$ |% h% D( q; V
博客:http://www.cnseay.com/ X( p% @1 Q) q# h2 G
漏洞分析:
6 V; E2 x4 n T6 u0 X% q2 q 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。& R" A; Q$ b. Q6 Q
/ N& |. o7 n: N& E. b- T( d) p9 d; C8 @
" [+ ^% y1 m, m& [0 s; M) d/ J5 o
1 S/ G) a2 ~2 {9 n+ W. Vpublic function account_manage_info() {
0 B1 a! E0 u. ]6 }1 |4 B if(isset($_POST['dosubmit'])) { ! N7 J5 v2 l% q* g& l
//更新用户昵称
; C9 ^$ |; h! U: Z- \ $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; : E5 t$ C }# `! Y# H0 W2 I
if($nickname) {
0 m( C8 l! Q' O $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ( i* l! T9 T3 S) G8 G
if(!isset($cookietime)) {
3 F4 t5 R1 Y6 ^7 T7 Q $get_cookietime = param::get_cookie('cookietime');
" _& G8 O( D; D4 { Y$ Z }
5 m. ~) K% h$ V; z0 p, N $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); * H2 L( H* v- N6 ?+ s8 [$ O! H1 E
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
! a8 ]7 p& F. e" v* {: | param::set_cookie('_nickname', $nickname, $cookietime); * L7 K, L/ v6 _$ t, T0 S
} 7 c* I, I9 c6 r
require_once CACHE_MODEL_PATH.'member_input.class.php';
, S5 w8 H+ ^ Z5 E! _3 {" @5 M require_once CACHE_MODEL_PATH.'member_update.class.php'; ) _/ M! [2 T& |
$member_input = new member_input($this->memberinfo['modelid']); ; ~# g0 e. k: B
$modelinfo = $member_input->get($_POST['info']); & W# O* l# F& S2 C8 p8 B' p8 q1 d
$this->db->set_model($this->memberinfo['modelid']); . m+ w7 Z, h$ ~ N! ~ n
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
9 v& x+ P. ^# C8 w9 q if(!empty($membermodelinfo)) {
: c7 z6 w3 |- F, \2 ~+ E7 N $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 5 c) b1 _/ w) R7 G
} else { ( J" b* }) [3 U* r6 M8 D4 ?$ K
$modelinfo['userid'] = $this->memberinfo['userid'];
, p9 k3 o- c+ I& j2 L* g* P) [& o3 } $this->db->insert($modelinfo); 2 `1 l8 v o D8 p% V3 \
} 5 N) b1 p1 j& k) E. B0 H5 O( B
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
$ p9 `6 g: l4 w+ x* B* t1 l* U在\caches\caches_model\caches_data\ member_input.class.php 文件中:" W. D9 f9 M4 B6 |. L: c' h i
" ^) N* K4 K8 t6 |7 Q+ I
# Q5 S$ l! k- S! ]: c
8 E/ Y: n. h3 S+ H7 j( afunction get($data) {
& a: a; a* x2 t $this->data = $data = trim_script($data);
% G1 c7 y. c0 P2 n' Z $model_cache = getcache('member_model', 'commons'); n7 u; C8 a- T L! M' Q) p6 C
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
- y1 A( ?; K P$ c5 t4 k0 G* T $info = array();
- Z r }4 F$ S! t- S $debar_filed = array('catid','title','style','thumb','status','islink','description'); 2 X+ s- X5 B% o0 n' X
if(is_array($data)) { ! L7 }/ p7 V. \
foreach($data as $field=>$value) { # S( N, ?1 i; E6 z' v R- o* W: \
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; - O+ i4 V% C# E/ |, d
$name = $this->fields[$field]['name']; ) O! I6 J" U/ i$ c
$minlength = $this->fields[$field]['minlength'];
3 ~; y1 ?! L6 c# d( q+ H $maxlength = $this->fields[$field]['maxlength']; $ S @, t7 g; K! ?
$pattern = $this->fields[$field]['pattern']; 7 y5 z: L* [ R7 H3 W' p
$errortips = $this->fields[$field]['errortips'];
+ K- E9 t- j$ G" @; O if(empty($errortips)) $errortips = "$name 不符合要求!"; 7 X P9 H5 l }. C$ m2 s* o& E6 z
$length = empty($value) ? 0 : strlen($value);
g' U3 R' B: k" s9 K. y' R# s* p if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 5 @3 f) P3 p: t" O& r
if($maxlength && $length > $maxlength && !$isimport) {
, \! H- j6 H/ G showmessage("$name 不得超过 $maxlength 个字符!");
& S3 o S1 u n# X! v% F } else {
( F! F1 z9 S1 J" g6 S str_cut($value, $maxlength);
( Q& _% U5 u q$ Q } 3 C/ c" b7 }9 `
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); " u. n2 [8 R/ I; G
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
3 N# B% p; q3 G3 O$ o $func = $this->fields[$field]['formtype'];
- e4 Y# c" i6 l& J$ m; `9 ]' U if(method_exists($this, $func)) $value = $this->$func($field, $value);
6 ~2 Q5 h1 z/ d% G( m ~ $info[$field] = $value;
5 H8 \! l% n* J2 H3 M }
0 V1 H! V) v- b7 L' Y$ ^/ m0 q }
9 T0 T# m2 b- } return $info;
. y* K8 Q6 P7 R }
0 B5 ?; L8 I% ~8 ftrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
: x ~% {# S' @1 v9 c( `; @, |% o) A5 l
再到phpcms\modules\member\index.php 文件account_manage_info函数
5 m8 H( e, a: N% G- `$ D: w过了get()函数之后。
8 n: Z) m% `( i5 V0 }' W P! V8 N5 t+ D
, x' j- C5 J9 F% `' X: M
$modelinfo = $member_input->get($_POST['info']);
5 j; V f1 b9 K5 ^5 I& x $this->db->set_model($this->memberinfo['modelid']); 7 I b, r" z8 S5 N( t& u
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 4 _8 R+ s3 |* Z; a
if(!empty($membermodelinfo)) { $ [3 g6 n5 h5 S6 b/ b' ?* O
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); h7 h" ~! \; ^8 x6 m
} else {
% p$ z& q/ q% U' b2 J& h直接带入数据库,update函数我们跟进看看
6 A* x2 I5 R' b4 L. Y4 F# A2 u7 X3 {" n. p
6 @& D, D; c" @) Vpublic function update($data, $table, $where = '') { ! ^ ?0 n3 S7 w, @# A, L
if($table == '' or $where == '') { 0 N" R" y; W9 \" ^- u
return false; , c8 A) y( t/ B6 x; @* l
}
0 m B, S# |( ]9 a- [- e% ^ $where = ' WHERE '.$where;
( u3 X; v5 l9 p3 @ $field = ''; ; [' w2 D" N- z% T0 w4 o
if(is_string($data) && $data != '') {
) }% |- E0 M4 N $field = $data; 9 t" F( H" H2 l9 E
} elseif (is_array($data) && count($data) > 0) {
; ~$ C! R% H/ p. x0 z $fields = array(); ' n) S; \+ Y! ]+ g' d0 C' X
foreach($data as $k=>$v) { $ L$ w R+ T! m1 s3 \2 `
switch (substr($v, 0, 2)) { 2 d9 N% I9 _5 |$ A& P/ I* a
case '+=':
0 p- o7 q q% p2 U4 Z" g' ?3 F- H $v = substr($v,2); 6 v1 ?" U" P* z
if (is_numeric($v)) {
/ h: |( G% N& u9 Q: _6 R2 x $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 6 x3 {+ X: f9 ]. o0 |
} else {
- I) D) k2 P. U0 n$ Y. i continue; " o& z+ j3 f: v( @
} " S& A8 B- |9 V$ ^& p8 t, r) z
break; - R n0 H' o: x6 Y+ e4 H5 `
case '-=':
~+ t8 p+ ]' H( i# F6 D $v = substr($v,2);
7 v: q* d2 ?( Z# Y4 {6 r+ C if (is_numeric($v)) { 3 T* u4 x/ E ^2 `; D; C& K
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
: A6 |# x9 e2 n' S& ~0 r5 b# u$ I$ U8 } } else {
0 F* ^$ ]8 U( B continue;
( }0 I6 O; m1 {' ~ } ) {, h7 V p8 j
break; 8 x$ q2 O K: Z7 O
default: + n' [* z) C F3 m/ C
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 0 L! @6 _6 f) t6 P6 Y+ G
}
/ G+ Y k6 }# E( p o- i$ y }
7 c8 E) d2 Q9 K' `# K+ L) e- V; s) V $field = implode(',', $fields);
; }# a. S& I3 W$ l9 H } else { " Y% ~6 q+ I. h& t
return false; 4 P1 ]6 Q5 |6 q% B$ q
}
8 ~; d3 m$ `1 R9 e4 e; ~. Z $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; ( N3 z: T# r, `+ F. X, x
print_r($sql);
6 w, ~" T* G$ X) `- m1 `7 s+ x$ U9 P return $this->execute($sql); ! m. J; X) o- i+ ]& n$ W
}
: s( D- ?4 v% _1 b9 v8 L. k- t从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。" \0 T0 ]$ N$ \& X
E" V* @& k5 z$ w+ t& ?. h; l* Z5 k攻击测试:0 O) N$ D* ?' [ m5 i' b( a
测试地址http://localhost: v* R' s& d% q0 q# A
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句) o7 m: ^+ S5 |
; I, ^) E9 Y8 I9 m( v6 ^* T
* C8 y4 n9 _( Q
5 k g! A( [9 L. R9 l
# O) Y' H9 X6 Z0 l6 S! H z0 z2 T( W# H0 i0 Y; s1 q" p1 R
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|