|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
; N1 R& G9 A7 r% C7 a漏洞作者:skysheep
. u5 j) e7 o) B' o+ n分析作者:Seay
2 S! v! {0 p0 G4 n1 }$ n ~博客:http://www.cnseay.com/) l' n8 V7 A# y; V/ F5 v
漏洞分析:1 Q" v+ L/ G# \, m) b# y3 o' x# n
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
4 v8 y/ e# w- w6 W3 F$ k' q0 D( o. T1 D4 y& O
" D" X. M0 B% `* n
( o8 h; Z3 ~! ^6 m1 Opublic function account_manage_info() {
2 w7 [5 U3 W# R5 {7 X& c( O- L( a if(isset($_POST['dosubmit'])) { 8 D C* q J7 Y- g8 `1 |5 k; Y! w
//更新用户昵称 ( N# }9 P2 k3 V {: |
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
- d$ C2 |" S/ `* d! p: X if($nickname) {
W, `( _3 {5 l $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
( n# k3 u6 E, ]* r$ i5 o a/ e if(!isset($cookietime)) {
- R/ I* ? k* U7 V $get_cookietime = param::get_cookie('cookietime');
5 j' J k' g. P: b) D5 G. F* G }
* Z2 ?7 L" C6 b/ A# I $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 1 h4 H+ q) {( p: i% {! S" [
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 6 d4 g% _# w% A- J2 z* r- ]/ g
param::set_cookie('_nickname', $nickname, $cookietime);
( n: w' }0 C6 d2 g }
6 f9 d0 B% y1 R7 s: C1 @0 Z require_once CACHE_MODEL_PATH.'member_input.class.php'; ' _7 i, o+ r2 [$ h
require_once CACHE_MODEL_PATH.'member_update.class.php'; ( ~, z" y2 Y; `( u* z7 `- d
$member_input = new member_input($this->memberinfo['modelid']);
9 i) ?/ D0 z; ? $modelinfo = $member_input->get($_POST['info']); $ G% d; I$ q5 c/ C! Z5 N+ h
$this->db->set_model($this->memberinfo['modelid']); ( D( K% e p7 E
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
. [* X+ y, O7 B( S if(!empty($membermodelinfo)) { 5 ?- S( Y4 Y5 o+ Z: D/ x! Y! B
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
; P. s3 J* A' r5 W7 G } else { 6 K: E: v3 V* B9 o& N
$modelinfo['userid'] = $this->memberinfo['userid'];
9 d' [( C0 e+ h9 W3 U O $this->db->insert($modelinfo); % j7 Y' e- H, D- [
}
* ]$ G9 D$ o8 m代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
" y! w6 Z1 H" r3 R$ L2 t在\caches\caches_model\caches_data\ member_input.class.php 文件中:9 x- M9 L+ ]) d5 K, C! J# L
! v* z$ c G- d( `0 r5 n
; u! T) ^5 O% \) ^2 a4 N
9 u3 i' l2 ~$ [. J+ ?function get($data) { ( L9 w/ o4 c: y$ A5 }, Q& t2 Y& Y
$this->data = $data = trim_script($data); , C4 r" G& x; s4 [
$model_cache = getcache('member_model', 'commons'); : R: a9 S1 V' }
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
4 [7 V0 c* C7 U5 Q $info = array();
" G1 A' I; g& N- l4 |7 Y7 f& F. d $debar_filed = array('catid','title','style','thumb','status','islink','description'); 5 N O3 O p0 X3 N' m* `7 j
if(is_array($data)) {
4 n [- T1 q0 Y7 Y4 x/ Z foreach($data as $field=>$value) { & C: ^$ D( k: {4 f- V( b# N) ^; E; _
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
, [8 ~6 [, M4 e $name = $this->fields[$field]['name'];
3 Z, T) {8 I$ H, }6 n $minlength = $this->fields[$field]['minlength']; ! G, W. Z* c1 Q8 ]
$maxlength = $this->fields[$field]['maxlength']; ; ?" E6 U0 x; X( W
$pattern = $this->fields[$field]['pattern']; , z1 b+ ~1 Y+ C# f0 ]
$errortips = $this->fields[$field]['errortips'];
, y0 u5 V9 w; U; |& F+ ] if(empty($errortips)) $errortips = "$name 不符合要求!";
9 q1 j+ p' t. ? $length = empty($value) ? 0 : strlen($value);
- U# O4 a0 l3 w6 ]4 h7 i: v/ R if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); $ Y$ n/ d, H& w8 y
if($maxlength && $length > $maxlength && !$isimport) { # M2 m' U1 O5 M/ D# a
showmessage("$name 不得超过 $maxlength 个字符!");
; B. I0 k3 q$ p+ [: P4 i1 n' _ } else {
8 n( g1 M5 X) e, G- }+ t str_cut($value, $maxlength);
# ]+ T8 s7 J' _$ m4 p } " [/ G- h- L8 _0 @+ x& k
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
% z# Z* K+ L& e% n9 e if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
* t1 t6 ?( H+ Y+ n/ V7 @ $func = $this->fields[$field]['formtype']; 8 \+ W% g5 b3 u4 v6 u: S3 \
if(method_exists($this, $func)) $value = $this->$func($field, $value); - `3 [' v' W# o& q m
$info[$field] = $value; 2 w( ]" B; v. s0 |. m4 {) x
} q5 a2 e7 e( i9 W: W2 ^% l
} . [7 p; R# b# O& ?% K
return $info; $ t/ N5 m2 \9 u( F
} ) R* B0 |. a! R! R4 _ v+ _1 w& y3 T
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
' p' N8 G! V( Y" O7 N9 ?5 ~ O. R5 }" e- p/ X J c7 b
再到phpcms\modules\member\index.php 文件account_manage_info函数9 C8 X5 Z3 _1 h- `8 m, n/ O7 H
过了get()函数之后。
0 v& s I1 {3 W8 W5 K
- P8 t4 B0 j& H+ i o! @/ D! a . b: C% {' q+ t% j$ M2 y1 w( Z* t" B( Q
$modelinfo = $member_input->get($_POST['info']);
/ a, }+ S S! J- C k $this->db->set_model($this->memberinfo['modelid']); ) b- v, w) O7 o: }/ S& i
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
% t" W k y4 `& K2 u3 ?$ n if(!empty($membermodelinfo)) {
; {. L2 g6 W# V9 j# n8 ^' e: { $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); . Y9 }2 A9 _0 ?# q3 f) @& v
} else { 0 V6 D) V& y& v! t m
直接带入数据库,update函数我们跟进看看( |+ f- X, ]7 A7 m$ n" F
% a1 F1 x/ S* c9 T0 B- C
( g; z4 H6 N" }! @9 r% Hpublic function update($data, $table, $where = '') { # e8 Z% n4 v s
if($table == '' or $where == '') { * ]; e4 e8 q; H- r
return false; ; f1 P% n; a; q6 _' l. X' b% P5 d
} & n& {% H* L6 g9 h! w" i4 S& M
$where = ' WHERE '.$where; 5 H* t% O7 U# N' N. j+ _
$field = '';
- B* N( l, N4 R if(is_string($data) && $data != '') { - P' y; B" ]. A0 G
$field = $data;
/ ~9 v7 }6 U. A6 {9 r } elseif (is_array($data) && count($data) > 0) { 8 k9 W/ y1 A- P/ @
$fields = array();
% {3 [9 g2 ]1 F" _/ q0 W foreach($data as $k=>$v) {
0 ^$ {$ |& {( c. @2 P switch (substr($v, 0, 2)) { * R% S5 s- G1 M: i: U3 Q5 x7 i; p
case '+=': % X$ v8 z* e! y/ h+ p' C, R
$v = substr($v,2);
: M. O4 }% L% o% C; R if (is_numeric($v)) {
0 A+ r3 r) [; Y& j' V/ s! w6 M) H. v $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); M" B# E, x! S. h, D2 d7 J+ v
} else { 8 |! b; ^$ z1 j ]$ z
continue; # ^) |" |7 I6 _% @7 e4 ~) {# F6 h( p
} 1 y- x, H5 `/ f5 o# q
break; : j m+ s$ _, e8 s3 Z4 |
case '-=': - ^9 ?8 Q2 E3 B( Y+ k0 v, [
$v = substr($v,2); ' ?$ T7 e# ]* s! _7 p' s: o
if (is_numeric($v)) {
9 _& }" p5 q; p4 o9 h $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); # b3 D4 F( I0 m
} else { / p0 M9 i: \" L# S) U9 u
continue;
3 E3 }$ Y% C; E. ?4 ]. S, [ }
+ r0 d$ O: p. @' Z# G break; $ \1 Z1 X8 l2 \& g, n6 w
default: # s/ @. H' I# P/ ^. ` s: R) x
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 0 \0 `! z/ R' ?4 }6 E
} 2 \% t( b. U$ j3 _8 B3 y
}
/ j6 i( D( x# g# s- Z G $field = implode(',', $fields);
$ G' \8 _8 N+ }# q) c) _+ N K } else { ( U" K: [( r( c, o& c& D( A" }% B1 O
return false; ' z6 ~" f9 `6 t
}
- j7 K6 Q- h m* ]5 r* d $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; ' P: }7 F4 F" w U/ m" D* ? U6 j
print_r($sql); _! r: C: S8 t: |( {
return $this->execute($sql); : _# O0 K* a; ^% E1 ]! F
} 3 R. d5 }2 a" p! N( t# C1 j( [& L
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
8 O+ f. j) c& g) V j
5 Y& M3 t$ o2 T- b攻击测试:, O; ~& e; F* W5 Z N
测试地址http://localhost( J, N5 a( j! u9 H
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句: b) s$ M. }& D) q6 [/ x
3 G- H+ E' U3 {0 n7 N ]9 y2 S
( e& j* N: H# }$ \8 \* j" A/ v. G0 m) ^. K" K0 W- Q
8 Z7 I% H1 p5 h! d' D' ?
9 G5 M0 U" \6 P* j2 }+ h* ? |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|