|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告# N+ N+ \# F* S) Y* x5 ^
漏洞作者:skysheep( e/ ?/ j5 F5 s/ [1 L0 l9 i
分析作者:Seay
9 z1 _. _# `* S X u博客:http://www.cnseay.com/
5 D+ {/ Z8 ]- G" m7 _9 l0 ]漏洞分析:9 U+ c5 o- h& [4 X' l
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
5 _6 u' d' D( ~7 K9 i. r6 @3 Y) \+ T7 w- P1 l' S
) t' u' G- d6 d, e - o+ h& \* V% u/ H' G3 A
public function account_manage_info() { ! F* T) ?1 w" g& W
if(isset($_POST['dosubmit'])) {
. J5 v$ q6 C+ H3 K9 X% r# q4 g //更新用户昵称
- }: u" d7 f. ^' \ $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 3 A* L' e, ]# F
if($nickname) {
: s7 k" u( r& V# q# c3 ] $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
& T8 R# b- K' u% r2 @; { if(!isset($cookietime)) { ; f. Q, L/ @* I3 H( T0 x
$get_cookietime = param::get_cookie('cookietime'); ' U9 y8 a- ^9 c n" m$ w
}
9 j0 y. n! m& c! V. @6 ~: | $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
, i# p/ N9 T( Q ^8 z- ^& A6 c4 o $cookietime = $_cookietime ? TIME + $_cookietime : 0; : Y6 b& P4 |. {( A
param::set_cookie('_nickname', $nickname, $cookietime);
; ~; f& S4 Y2 Q& \9 m }
6 q# O. I6 {2 k( S) p$ ^ require_once CACHE_MODEL_PATH.'member_input.class.php'; " p4 |) M, Z( d1 ?9 S# ?
require_once CACHE_MODEL_PATH.'member_update.class.php'; 9 [+ ^ I. g, {
$member_input = new member_input($this->memberinfo['modelid']);
9 }2 [, I1 ?( R8 \/ k* e% ? $modelinfo = $member_input->get($_POST['info']);
: i/ @$ H: |6 H9 j* X4 Q! l# E $this->db->set_model($this->memberinfo['modelid']); 8 Z) r! r. d; q7 t; R9 y
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); % a& G- ?& G/ q8 b7 m
if(!empty($membermodelinfo)) {
; h: [" D7 X9 x5 R $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
. [2 a( f+ \4 X: h2 j/ E+ r. @ } else { " y. m# Q" Q- d9 \2 U
$modelinfo['userid'] = $this->memberinfo['userid']; 8 c" m. [( ~7 x% H5 ?! \: g
$this->db->insert($modelinfo);
1 C1 a& l3 D. P( u/ l8 Q } ) P" \; R* `8 q2 O, A
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
: g4 Z* d6 d/ P% D5 S" x在\caches\caches_model\caches_data\ member_input.class.php 文件中:+ |& I9 V- m! f m0 n5 q4 G
- z/ H8 Q: v, x$ f
/ t/ s! F1 s+ I% Z
) y) q. {* R: Ifunction get($data) { 1 j, G; Z4 m( B) ^" `, q7 B* a
$this->data = $data = trim_script($data); + A: p3 d* e# ~( Y" n8 g: D3 z
$model_cache = getcache('member_model', 'commons');
/ v' G5 | c4 f2 m- F! }! ? $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; ) ]; M, E! T# N1 Q+ S( f; F+ s# B
$info = array(); # d# N4 a0 D) q
$debar_filed = array('catid','title','style','thumb','status','islink','description');
9 B- Q9 B% |8 m3 v, g# k if(is_array($data)) { 4 V9 v5 u2 G3 k+ z
foreach($data as $field=>$value) {
3 w2 j9 w1 r8 A/ R if($data['islink']==1 && !in_array($field,$debar_filed)) continue; % i* ] G) s) `; ^3 K& m
$name = $this->fields[$field]['name']; 8 ?1 D) k$ R$ X
$minlength = $this->fields[$field]['minlength']; 1 K! ^ F. X' L0 S8 g- a' ^
$maxlength = $this->fields[$field]['maxlength'];
7 [& v* [$ s2 G# e* Q: j, n9 n $pattern = $this->fields[$field]['pattern'];
$ Q6 o8 `$ E0 l: q+ j $errortips = $this->fields[$field]['errortips'];
* M0 ~0 H, J5 s' i, y, S if(empty($errortips)) $errortips = "$name 不符合要求!";
5 @" y6 p7 x) {5 h( n: L: E $length = empty($value) ? 0 : strlen($value);
; G) m9 e$ ^5 ^( I& }, O/ S! J if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 7 p$ H( E7 Z; v! ^( }
if($maxlength && $length > $maxlength && !$isimport) { 2 E7 I, v/ o/ f
showmessage("$name 不得超过 $maxlength 个字符!");
: s" Y, M a$ I; z! r" J2 h } else { " |8 W3 A+ m/ g9 X% k6 S) O
str_cut($value, $maxlength); d2 {( k6 s2 @) w: a2 @( f
}
# n5 F9 l2 J! o( n x if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); # e* G L3 p( y. B) \& ?
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
/ Z; n' _- E( t/ E4 _4 J $func = $this->fields[$field]['formtype']; . h5 j3 f% ^$ c+ J& z# s& [
if(method_exists($this, $func)) $value = $this->$func($field, $value); 5 S& f; r7 r9 b( R( S4 ~0 z
$info[$field] = $value;
/ b) _# D7 Q) z0 G1 r }
8 D K: p+ i3 z0 ^4 p/ d+ F; X- b }
, p8 V0 e% l. n; f. | return $info;
; X# [" @8 L* R }
. H- T$ y6 Z7 K/ B; E9 [trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,3 p1 {' d- T! e$ v( c6 {
4 t. S5 V6 @1 o8 r ]2 c b+ x再到phpcms\modules\member\index.php 文件account_manage_info函数
: ]- x ^( L3 L% q% S/ H过了get()函数之后。5 A0 M6 X9 c1 A; X4 z: n) W: m Q0 D
$ h! m2 I& b7 ?0 X; u! ^
5 M5 `0 }+ E/ Q) p# c( M( k/ b$modelinfo = $member_input->get($_POST['info']);
8 a) L9 @) P, a" x" n $this->db->set_model($this->memberinfo['modelid']);
+ ]. Q! \' @# y- j $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
6 }5 Q9 G, W1 Q) \+ o5 u if(!empty($membermodelinfo)) { 4 X# H; i2 ]1 D8 A H @. A
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
3 k4 ^/ `5 |: j } else {
! L$ L" C" C* H+ }3 O. E8 o直接带入数据库,update函数我们跟进看看
1 k, I5 m; a3 q/ l$ r% ]) C8 {4 U+ V- I5 Z
6 R. F" e- y9 \- \public function update($data, $table, $where = '') {
/ r# T x) c1 p i if($table == '' or $where == '') { # W1 l# Q+ P2 z) b& {* I& b
return false;
. T* T( f( F! b" p1 H, v }
! [& s; w( f$ C- d: i5 j $where = ' WHERE '.$where; 7 ~8 J$ j* E2 {3 a; ]
$field = '';
3 |* ]0 X C% _2 F/ O" Q+ ^ if(is_string($data) && $data != '') {
- X4 w" _4 z. g& `1 g: V8 n5 r $field = $data; - s- z) P6 @: ~' Y; N
} elseif (is_array($data) && count($data) > 0) { ! e% Q9 P$ e, \
$fields = array(); 3 ~+ d# Z0 }" o6 o
foreach($data as $k=>$v) {
) s. [% ~ d' u' n$ D7 a switch (substr($v, 0, 2)) {
% Q7 P+ g7 F& d }& i7 Y- X case '+=': 0 }/ _# x& B% l8 i$ H$ i) ~& x
$v = substr($v,2); # o0 x7 j" g) Q* N
if (is_numeric($v)) {
, U) |8 H- Z: k1 @# S1 w $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
1 t6 x, K' W% w" W. h6 T/ P } else { , y8 ]* j6 X. ~2 ]1 _% u
continue; 5 e9 _/ Y2 K# s( ^4 V2 p& _( W
}
* C( _5 `- h% U- I+ U break; 1 s, x, L; H3 `5 W: Y2 p1 P
case '-=':
9 L2 Q) k3 s1 g( D $v = substr($v,2); 4 c# g" X" ?; h9 H
if (is_numeric($v)) { ' }+ S8 W: ~7 |1 a
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
) Q. V# v* z$ X! S$ A } else { 5 S0 _' f6 v" w6 N6 P
continue;
" _% {+ I' u4 t* n6 h } 1 J4 e% d8 a( |2 K! X
break; + B; L, y2 M5 w
default:
3 H0 A3 M/ v! U1 Z1 j7 e $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
1 t, L8 B* {1 z6 g8 W D4 r( X+ H0 _ }
2 s4 K5 v# U% h7 c }
$ a$ O4 s" \+ z$ h: o T6 i8 @( d $field = implode(',', $fields);
' G* {) Y, s {2 d3 Z5 y } else { 1 k) ?# a: C3 x" B* u: I
return false; 1 b9 a$ H$ v/ Z* j. P/ Y
}
8 A4 e4 S* \6 Y; O; n $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 3 h1 y, y9 y( _: \
print_r($sql);
" a3 I. X; G5 o6 C3 R4 i Z: n return $this->execute($sql);
, ^1 T; k* z6 ?' j }
; X4 M( x. D* d) U4 T" | ^3 p0 i从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
" Y9 c [- F6 h% Z' a
, }2 `* ^2 E) L) T* y- a! k攻击测试:; Q& |& m8 g4 ~) U d
测试地址http://localhost
* m8 _: p' I6 U7 @% O 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句) j& ^+ r( ]! Z9 X" U
2 C* O M% N. W2 h3 }
" H, @0 {4 A# k# V
4 R0 S$ i8 _- Z% }
0 K6 c! u; T3 [% _1 y6 w( `
- i% j: h& N4 n% e+ M5 ]0 Q |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|