|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告: X9 ?2 l( a( x
漏洞作者:skysheep+ T& I4 r) S- D+ M. s! D5 X
分析作者:Seay; e x* \1 x) g. N4 T9 J0 @# n
博客:http://www.cnseay.com/* b* y1 A1 x4 U
漏洞分析:
0 t+ ?0 b" ]" A# C( l 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
) B3 }$ O$ T) A8 t T+ t5 r% R: F( `- v
4 c( I2 K. n1 s7 X! {9 S$ T2 ]3 { - G- {1 P/ A$ n- T/ P: Y
public function account_manage_info() { % z/ e$ M* L8 H" m
if(isset($_POST['dosubmit'])) {
4 |- ?- ^8 H7 a //更新用户昵称 % y! i' p$ f) r& U" n' Y# `6 p
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
# b$ l0 D3 `/ D4 y0 C; W: | if($nickname) {
& e- `, t: K$ v4 \0 B. R9 A $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); & x7 i. B" i6 E, i* e5 x
if(!isset($cookietime)) { % S: o5 P2 D \. N# ^7 ]
$get_cookietime = param::get_cookie('cookietime');
* v3 Q { R6 f* D }
% i; P- Q. v: G5 d- K $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 2 d2 V( n% q$ k: p* o: N1 u- M* c8 J) K
$cookietime = $_cookietime ? TIME + $_cookietime : 0; . A/ Y1 }' P3 D0 f4 D" u
param::set_cookie('_nickname', $nickname, $cookietime);
5 X1 ~ x* C" {7 W8 j7 }% V }
P8 h) e6 f- u) I require_once CACHE_MODEL_PATH.'member_input.class.php'; ! D7 M7 @7 d0 w/ `. O, A
require_once CACHE_MODEL_PATH.'member_update.class.php';
7 H/ b, |9 C6 v# i $member_input = new member_input($this->memberinfo['modelid']); : }) q! J+ N6 J/ [+ w. y, P
$modelinfo = $member_input->get($_POST['info']);
' i6 k! `* K) K$ H8 p $this->db->set_model($this->memberinfo['modelid']); 7 ~, s; d, p. ?6 l& C
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 2 w# l3 B; j7 e9 i
if(!empty($membermodelinfo)) {
0 F U/ l$ o* i ~( p' y: h+ f* ^ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
$ B: D& x6 P. H+ V } else {
. B! m9 j7 W" x" n' | $modelinfo['userid'] = $this->memberinfo['userid'];
4 Q0 h) G! J A4 v: z $this->db->insert($modelinfo);
% ~3 Q' B2 L, `7 Q+ ?7 g } 1 ?. n/ u( u; y. G; h
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
4 u4 }/ i/ D+ h1 a' o在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ ? @/ w e4 o: p d- q- @3 ?9 d* }' F
" }, U$ P3 U3 s9 ^
/ N) ~4 [% a' W2 F5 ^: |1 Bfunction get($data) {
( z( j3 E) v+ n1 X, I# E $this->data = $data = trim_script($data); 4 G' P6 L: j7 V5 \* F% r
$model_cache = getcache('member_model', 'commons'); 1 z D' c7 u0 h& G
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
5 d0 h& r5 D5 g5 `5 N8 X3 o7 m $info = array();
5 |1 r. t4 {) i; Z $debar_filed = array('catid','title','style','thumb','status','islink','description'); Y; E7 f3 B- U8 L, p1 o% g3 X- Z
if(is_array($data)) {
4 C; T: G1 z8 [+ P6 x* w5 `6 M foreach($data as $field=>$value) {
4 N3 n/ d {( M, I i4 O; [1 A if($data['islink']==1 && !in_array($field,$debar_filed)) continue; " _+ C( s6 B' U
$name = $this->fields[$field]['name'];
$ S& u4 w2 S/ Z4 ]4 |+ j $minlength = $this->fields[$field]['minlength']; : p+ K& G% P" D" `
$maxlength = $this->fields[$field]['maxlength'];
! i) y o. W: {0 g1 R5 u$ D$ j $pattern = $this->fields[$field]['pattern'];
' V3 i" U; O) `8 W* ~* p2 Z $errortips = $this->fields[$field]['errortips']; + Q( Y3 H: h5 A# t/ T4 y! q9 |. @- q
if(empty($errortips)) $errortips = "$name 不符合要求!"; " w; n0 [8 C' B, X, ^
$length = empty($value) ? 0 : strlen($value); " Y/ W% j3 u! L; t
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); + c7 m2 l" M$ ]. L
if($maxlength && $length > $maxlength && !$isimport) {
. C' ]6 m" o0 T3 m6 J2 I" n showmessage("$name 不得超过 $maxlength 个字符!");
- m+ q: \/ C, w+ W8 J2 n2 j1 b2 V } else {
: ]1 j5 o; H5 B: ]1 X str_cut($value, $maxlength); 7 I* ^! p$ B4 t5 |3 O( h' [' R
}
" r7 u/ ]% c2 j+ m1 ]6 V if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ; C2 ^* @9 E1 C. [7 T( X s. A
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 1 Q3 f! Q, W8 {
$func = $this->fields[$field]['formtype'];
2 z* ]- w, ~ r4 u* ?" ~ if(method_exists($this, $func)) $value = $this->$func($field, $value); 8 B) b* ], _7 i9 ]4 `/ I2 G
$info[$field] = $value;
1 s) i* ]5 U% o5 U, r }
; t! m/ e: G9 s6 y& T } 8 b/ b# v/ Y4 x4 Y
return $info;
+ A: v7 ^7 H3 |$ x Z6 b. r }
. \" s, U: a' y& mtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,1 f0 O& U) I) c. E$ E* ^6 I' P
/ q4 b5 O' c! p5 r) |& n再到phpcms\modules\member\index.php 文件account_manage_info函数
, Y1 K( s( G) {; f$ B过了get()函数之后。
% q0 p- a' G- P8 D* i2 I% t; M, A9 Y8 b2 E8 Z+ E0 ]4 Y
$ Q8 K/ ^! C- Q7 W! V$modelinfo = $member_input->get($_POST['info']);
$ I ?7 K5 g3 e $this->db->set_model($this->memberinfo['modelid']); ; |4 G; S- A5 k1 ? d6 S
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); [5 f9 ^8 T" G- i9 |$ r
if(!empty($membermodelinfo)) { D& C% I! \5 `- M
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ) c* e7 X {; d Z6 x/ Q0 Y1 @5 X
} else {
& x( n8 y5 {' F# |$ E& }/ Z直接带入数据库,update函数我们跟进看看
6 z0 v1 ?* t8 t4 h( C0 ?- q
0 k5 i6 u6 k7 ^: y4 ]5 u/ a8 G w
3 A2 P# b: ]7 j4 }; J( A5 p+ @public function update($data, $table, $where = '') { . w! j$ L# C: |6 ~" g
if($table == '' or $where == '') {
# }# p8 I5 S S" j( c4 b d% O return false;
- t' P$ |$ M; h% ~7 q' r }
- G/ i3 C9 K; a& o2 w $where = ' WHERE '.$where;
( C6 O+ a9 g+ y& N `5 \+ z $field = ''; 8 t; o% x1 e/ ~3 q) y6 `0 j
if(is_string($data) && $data != '') { & L/ f- b& N( I" `- V
$field = $data; + W2 M6 h) k. x/ H5 |" f0 ]
} elseif (is_array($data) && count($data) > 0) { / ~( \: a8 q" ^1 z3 ^ K3 `
$fields = array();
" s: |# b$ d! @# {9 L( C: t foreach($data as $k=>$v) {
# J. r' x0 A% b( u! I: b& }8 B+ C9 V switch (substr($v, 0, 2)) {
$ w B* V) w4 X& f3 @' u case '+=':
/ v9 T- ~0 q v/ D1 w" r" y' H $v = substr($v,2); 3 \' `; p) @& l7 _/ F! R" v# E( S: v
if (is_numeric($v)) { ) O z8 T" B' b o+ @; E8 x: {
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
0 @; R$ j: b" z } else { 7 {; g. K9 q0 U5 I
continue;
; X) ^0 f5 ^# M! ? } 2 \9 G3 {- J- |, {* f8 f
break; : y0 S J1 t+ s4 h, n
case '-=': 0 l' H/ _5 B, Z& m. v" b: f
$v = substr($v,2);
' W% q) @2 t \. | if (is_numeric($v)) {
& s, `. t2 z g5 l X $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); ! U9 U; s/ R% C+ f+ J! M
} else { ! R2 v7 D+ K4 U1 a* k3 x. p
continue;
( G% F2 ]6 s, j6 F- g5 c }
" j0 b$ z* F1 R% z9 \' z) i break;
9 ]7 T4 Y6 g% T$ _$ _+ l8 i$ F default:
& z7 z& h- R/ L $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); : I6 Y' h1 t$ j$ g/ s
} % |" ?2 f7 o# l7 v
} , f V& l( w- e5 W ~/ X
$field = implode(',', $fields);
1 k3 M; t. J0 L: Y1 @ } else {
2 o! i. O0 h" Y; G! I/ }( I return false;
$ Z7 H+ }% \2 N& G } " ?1 e2 y4 A( n; f4 V4 h
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; / l. M% }( D$ J! y) t) Y& h
print_r($sql); / v; s% O8 Y! _3 ^
return $this->execute($sql);
( F. g( p) I2 D E: ? }
. W8 G, E7 v6 o: N7 i8 w从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。2 a0 P4 Z9 @! `& c( |, R
- M3 s+ q& V0 `% i4 s$ A/ R攻击测试:
+ }9 q$ k$ W4 o测试地址http://localhost7 g+ X! u0 _7 O% j8 x7 U
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
f+ W6 v9 m5 H
+ u; ^- U9 H! {0 l
# O4 ~6 \9 ^7 k, l7 R3 b& R7 z9 r* N0 N; Y: Z
' q" Q6 E0 M: w" k" k9 R* S* j0 w, \9 N
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|