|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告' Q6 j1 \1 P8 E* }
漏洞作者:skysheep' D; T* D. i& N& E
分析作者:Seay ~& l5 {' t7 R
博客:http://www.cnseay.com/4 T: i* @/ d- f. X2 s
漏洞分析:# a' s1 g+ h& z7 Y
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
! p- J5 W6 y% V8 \: n8 p) _' h5 R" N2 u4 N* |/ `% U# X, U3 N
7 _/ ]4 X. \% i' ^1 v: b5 ~/ G
% |; J# _& r$ y' ~; _2 dpublic function account_manage_info() {
1 @* o8 i3 o+ \" S$ ` if(isset($_POST['dosubmit'])) {
0 T; r3 b- K4 q" a/ u& _ //更新用户昵称
) \0 E- X: B6 @( B $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; # s* s4 a2 p* z0 s6 r# M
if($nickname) { & Q1 u8 ]9 i9 y, D8 |( X1 N( S
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 5 g. R' x* k+ i H
if(!isset($cookietime)) { 9 f% G3 i% E: [
$get_cookietime = param::get_cookie('cookietime'); 4 @( X d* v0 `+ o! I8 W( C2 C
} + B8 @: F" C6 d$ }% V
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); : Q! A" ~# F4 X8 G8 t
$cookietime = $_cookietime ? TIME + $_cookietime : 0; ' A5 m! D2 m/ D, O* Y
param::set_cookie('_nickname', $nickname, $cookietime); ' ]+ {, f1 T# W
}
: x8 F c2 W$ [7 V% c require_once CACHE_MODEL_PATH.'member_input.class.php';
, o. \: s! \3 q4 U' R8 x t require_once CACHE_MODEL_PATH.'member_update.class.php'; 8 D* K- A. d. Q: K1 G/ j8 ]: n& V: P
$member_input = new member_input($this->memberinfo['modelid']);
1 J4 ]& J" H( [/ Q, z $modelinfo = $member_input->get($_POST['info']);
: |, j( {8 s3 P- I# O $this->db->set_model($this->memberinfo['modelid']); ! g: t6 z; G5 w0 M7 n
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); + E9 K& N+ ?: s
if(!empty($membermodelinfo)) {
1 ?! \, X% B c( } $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
( H( x5 G" \' X$ ~2 y% O } else { 5 H- N+ [) I: s0 a. e1 I
$modelinfo['userid'] = $this->memberinfo['userid']; + J5 }! J0 T" ^4 {+ h( D
$this->db->insert($modelinfo); ; Y2 H# g0 d+ _2 V2 C
}
$ n7 l! E) P7 }& X j4 ?代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
( L; Y( V' I. K# R在\caches\caches_model\caches_data\ member_input.class.php 文件中:
7 ]0 X( i( w. ~1 \, ]+ y/ w f2 T% j
. M7 o( C- z8 F% t% [
5 Y$ L% @ J/ G3 A1 @function get($data) { 3 M# A( W6 [. G" ]
$this->data = $data = trim_script($data);
# V- `% Y& f4 I/ p $model_cache = getcache('member_model', 'commons');
# y1 y1 T/ _1 _1 i5 G% M- b $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; : u: \( c8 Y# @& E l
$info = array();
9 X3 j# }. s! N $debar_filed = array('catid','title','style','thumb','status','islink','description');
0 K1 Q' _ ]# l$ E* t( F if(is_array($data)) {
9 j8 d0 x* g+ b foreach($data as $field=>$value) { / i l: u. H7 D! r" h
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 6 a' |2 C* I; m# w+ V z1 A
$name = $this->fields[$field]['name']; 4 @7 W7 S& X" r. L) |
$minlength = $this->fields[$field]['minlength']; ( U" y$ g9 M8 Z- ~! N1 z- Z
$maxlength = $this->fields[$field]['maxlength'];
0 N) Y( u# O- b; \ A6 y6 x7 ]5 A* g $pattern = $this->fields[$field]['pattern'];
* A4 c W& h" y0 w5 u' c1 j" S. B6 E# ~% N $errortips = $this->fields[$field]['errortips'];
* z' v+ q$ E. v4 y- P* B9 M, ~ if(empty($errortips)) $errortips = "$name 不符合要求!";
; f: E" J8 _; d" `& o) v $length = empty($value) ? 0 : strlen($value);
! i" z0 P. Z# f if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); }6 G) S7 z" B- B
if($maxlength && $length > $maxlength && !$isimport) {
. d4 j" x5 O7 j" J7 d7 ^- i showmessage("$name 不得超过 $maxlength 个字符!"); / D6 t) v: n2 `* |8 ~7 w) f* s
} else {
" c3 ?% I% e4 {" f9 \7 f% V; K str_cut($value, $maxlength);
1 n- F5 |- X4 t$ S, W }
) L: G! _6 E! \# h- O0 F% _0 T1 y if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
. I( N% }! k* {0 H if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); ; ~3 O! p4 n5 V" T
$func = $this->fields[$field]['formtype'];
- Y7 i4 V4 M) e% i T. F if(method_exists($this, $func)) $value = $this->$func($field, $value);
, ]6 {, ^- A( m. r, B+ m $info[$field] = $value;
: v3 z+ ?4 O( g$ x7 k$ c& L0 i }
, v9 _$ ]% Q1 l& |7 Y W1 f } 4 E2 w7 ~! q! L8 D7 y3 [
return $info;
/ O+ S0 ?: z$ n* {( I) g. m" P6 v } + ]) z: C- d1 W- m3 L: _
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,' E' v$ [8 ?# t# ]5 N
4 G. r6 J' Y7 o9 k* F% k4 P- b& A再到phpcms\modules\member\index.php 文件account_manage_info函数' h; w/ {) w% `& s D) \- Y1 u0 b
过了get()函数之后。 [8 d# j3 e7 v- y4 Q& @% T
1 t' x/ S @2 E* C2 [, S
5 J9 c7 d+ ~3 w$modelinfo = $member_input->get($_POST['info']); }( g! y3 i% V& q4 s1 d
$this->db->set_model($this->memberinfo['modelid']);
9 Q7 R% \/ F% ^; |% N: @& D, L$ ~ $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
- n8 ?3 U0 J: B" t/ I' ^0 t if(!empty($membermodelinfo)) {
4 @- |2 P+ v5 t $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 5 s* u: b8 n. |$ E* b
} else {
# ~. W* b1 H0 R5 {* J$ N直接带入数据库,update函数我们跟进看看0 H" Q5 I5 }* L8 |5 u8 V7 x# |0 K
" h5 U: Y# z# G3 Q- g1 Q
3 F2 y4 ]9 [. g3 B6 Q9 v2 [% vpublic function update($data, $table, $where = '') { 3 l9 w# M9 W" y2 q' Q
if($table == '' or $where == '') {
0 k2 ? Z( l( A' f, Y* \4 _ return false; : Y( z/ x3 P6 D8 @
} " w8 n: p8 f+ w% P) M5 w
$where = ' WHERE '.$where; . }9 E" I3 E& E: @. ~/ f: `) w
$field = ''; 2 K; e$ g5 |9 }; |- y# ~' l
if(is_string($data) && $data != '') { ; d* O( s) D# }! G
$field = $data;
^, d& b. {# | } elseif (is_array($data) && count($data) > 0) {
- a( O" H& [5 c+ M, V+ C $fields = array(); 9 f& E$ r, s- ] M% y! Z4 P: C
foreach($data as $k=>$v) {
5 h& _2 t4 `9 I: f switch (substr($v, 0, 2)) { ' k8 I! {' J0 @) k& `
case '+=':
; l* g$ P+ ]* J $v = substr($v,2);
, i7 J* G" z, b% U) W5 W if (is_numeric($v)) {
$ X( H" R8 \+ M$ O* c5 B $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 3 W! r2 ^/ n' a9 @4 ?# D' ~& R4 i; h
} else { 7 a/ H" W* b0 y1 n: p8 N& _
continue;
) E1 m! F, |7 r: S! Q } 1 z; H6 I+ R. b( X& p- k
break; ! p* Q5 B% p L, P' |& y; L& F0 y
case '-=': 6 H0 }7 }8 [+ U3 k& T( v9 t( x
$v = substr($v,2);
/ Q- F, ^# c) L7 k if (is_numeric($v)) {
5 k# @' ]" v5 \4 X* I $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); * p I$ B2 Q6 J# T& u7 N8 C
} else { % C- T, ]( g2 H$ j; ?( t4 Z
continue; . a* p; q n" G
}
1 [& j# L3 p" H' U# T$ z break; 6 L- p, V) P+ g2 @, H8 _: Z
default: 4 c+ V. a3 _2 a7 p
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 2 x0 O. n( G4 l2 _6 p. r6 x
} ; m. n' f$ |& S% g
} * Q! C, e, U* Y3 N' v+ f
$field = implode(',', $fields);
& a7 b8 O1 [5 d) k. F, e+ K% o } else {
- v( {0 y: s4 ]2 `5 W" |! o& F return false;
' Y, E$ R- A' F } 0 C7 K) \: B ~- g
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; + f* a, v5 b* V) K* I% E" Y
print_r($sql);
" s6 _% D% k. [# M: ^. G9 i7 w return $this->execute($sql); 0 Q( N4 r) U: ~
} . H. H5 c$ O) {1 p" Y
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
: S) p6 y9 {2 U a2 c0 ?8 f s4 r
攻击测试:4 |0 }2 {4 a8 m6 K" v0 L
测试地址http://localhost$ o# y4 d' G7 N4 ^0 I i8 w9 ^8 f7 U
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
" h" w7 F7 }* E+ _) T' a5 W8 w- b8 q# |) R4 j) s$ V) s
: Y; N/ {$ R( \( y0 Y# F# \6 E" K; N5 O/ o7 J3 S; s$ H
; I( b- k1 U! }2 f3 @ H' \# v
) s2 u2 j( Z& W6 t: K3 L7 S |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|