中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
. \) d2 W T3 f. n7 F
漏洞作者:skysheep
0 d, _' n5 T" p+ [/ G- ?
分析作者:Seay
! K- C1 m. H' z
博客:
http://www.cnseay.com/
9 b) {5 J ]/ d( k! S9 `0 w+ g0 c, C5 V* [
漏洞分析:
: u7 w* z, w l0 ?
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
& f t2 O) U L" D: J/ e
* o1 N- k9 q+ ~) O4 }1 n7 z
- S5 m. w$ z: ~1 f7 R2 H
- |5 N( y& c. f) I1 U- S
public function account_manage_info() {
. G# T$ H5 b9 w( _& w: N# `- y! t
if(isset($_POST['dosubmit'])) {
1 \ z: T5 K, Z+ U% L
//更新用户昵称
, V' U4 S7 }5 ]$ p* S4 u" C2 U
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
8 e7 I3 e; P7 U& c. v7 A* t
if($nickname) {
; N |) j3 y. O$ M) R3 e
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
- B6 p/ Z9 H4 I+ L) }
if(!isset($cookietime)) {
" s+ q* r4 p# `# @: v: `
$get_cookietime = param::get_cookie('cookietime');
P0 f0 h g' d8 ~
}
: W" K0 L h6 E' B3 u/ k
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
! B- {' p" s6 c! C6 H
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
" ^6 N; U0 h, z8 y: @# B
param::set_cookie('_nickname', $nickname, $cookietime);
. c; g* C1 c; P& v
}
5 e4 I( c9 X, T, y; w" l7 e
require_once CACHE_MODEL_PATH.'member_input.class.php';
' f5 j& R* X0 q
require_once CACHE_MODEL_PATH.'member_update.class.php';
' y+ E9 d0 \3 C+ L
$member_input = new member_input($this->memberinfo['modelid']);
5 T$ T" g0 L$ {6 U- O: q" K" M
$modelinfo = $member_input->get($_POST['info']);
' A. t6 O$ H& \
$this->db->set_model($this->memberinfo['modelid']);
0 J* k5 r% Q2 r7 D, j3 r( A5 e
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
- [5 a2 y3 j x9 L0 U
if(!empty($membermodelinfo)) {
. j# T: \- f& u# J+ Z; g2 j( K" @
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
9 Z; g' e4 G& Z& H5 j3 p0 h; _
} else {
0 D2 [8 e0 k( \' T
$modelinfo['userid'] = $this->memberinfo['userid'];
8 v& b( H( `: `
$this->db->insert($modelinfo);
+ p4 Y$ V+ P4 x U! A0 C+ q# I# P
}
' f [" L M/ h7 X7 X- O
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
6 }* I ? ~; h$ O3 M
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
& q9 g; b5 W7 t z! S, m" h9 g
- x* B1 b1 a6 C. A3 L. {/ u. Q' y
+ t. w' j( J0 S/ S
7 q' q/ v4 O# f6 w7 n
function get($data) {
3 i+ N6 b I- l- a: ?) R' _$ n& t
$this->data = $data = trim_script($data);
- f: |9 x# `1 V0 S
$model_cache = getcache('member_model', 'commons');
?! W/ r/ O- ^% X" K e$ H! _1 `
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
: s' Z$ V: h3 q9 Z% s9 `+ f) {' o' C! V
$info = array();
" G) A. Z: _" o# M: v' J2 `( {4 n+ J
$debar_filed = array('catid','title','style','thumb','status','islink','description');
A- z( R0 f i! U& X. v
if(is_array($data)) {
) [ V% y: t- z# b& O0 w2 O" B+ ?
foreach($data as $field=>$value) {
! I& ^0 b6 U- q8 F9 S7 M
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
, D, [ o9 j8 K9 Q5 }
$name = $this->fields[$field]['name'];
, x$ x ?& H6 L5 K. x
$minlength = $this->fields[$field]['minlength'];
4 }, ?; R+ D0 B. V- o
$maxlength = $this->fields[$field]['maxlength'];
9 z% H ?* ]1 B3 l8 s
$pattern = $this->fields[$field]['pattern'];
' P$ R; `, m/ ^" R6 |7 w
$errortips = $this->fields[$field]['errortips'];
' h( c0 i- @: p$ ^" j0 L
if(empty($errortips)) $errortips = "$name 不符合要求!";
; h# }7 l7 G% g$ h
$length = empty($value) ? 0 : strlen($value);
/ X, ?0 I# q5 W" {& n$ }5 B' c
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
% o2 g7 k2 k! b' K& [2 y; U
if($maxlength && $length > $maxlength && !$isimport) {
2 s* P3 \$ o: L
showmessage("$name 不得超过 $maxlength 个字符!");
$ i8 A @' z# x
} else {
; g0 Y7 m- _8 A$ _. C
str_cut($value, $maxlength);
0 |% f, w1 H2 C
}
0 _% y# o: }1 F4 Y6 a% f# |8 V
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
2 T8 y( a! ^" I0 _! B
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
( g" f2 S' s8 c
$func = $this->fields[$field]['formtype'];
5 o5 c% W7 L% v. B! T7 H+ i
if(method_exists($this, $func)) $value = $this->$func($field, $value);
3 i3 o5 ]6 @( c( t
$info[$field] = $value;
; k5 Z7 f6 A, A
}
5 T9 h9 @5 ~" |1 L0 P
}
0 Q3 C I, i* z4 }) T7 Q; y
return $info;
5 ?1 t/ E# e O) n4 F) h* w
}
7 f* ?& ~ } I. n" c4 t2 p
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
, q0 F! E4 ^1 v( K
# ^& l }. I. e+ V4 n
再到phpcms\modules\member\index.php 文件account_manage_info函数
+ |. C4 U) U3 x& V/ c9 Y; _
过了get()函数之后。
5 A0 v$ I% k) K2 B1 v
# H8 B8 U! Z! Y; E
! |2 k* H' b! c* r* c* d
$modelinfo = $member_input->get($_POST['info']);
; k4 w' A6 o3 Z! O' [# T$ ] X
$this->db->set_model($this->memberinfo['modelid']);
2 `) J# M8 w z! U7 @: T# W
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
+ e6 F/ \1 }' X
if(!empty($membermodelinfo)) {
8 v( U |8 f6 g, K: e
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
7 b) \5 z9 d: J! I# C
} else {
\* y5 |( u. D. ]4 J. B) d
直接带入数据库,update函数我们跟进看看
: j& {3 t c* n* g1 ^5 M; T3 Q, N7 f
, u0 U5 N1 J. v8 A* _
1 y n' k1 L$ d% W
public function update($data, $table, $where = '') {
- t9 Q/ P2 `: \2 h3 g
if($table == '' or $where == '') {
2 H9 S" j6 ?6 R$ x; ]
return false;
( g I6 I& e" h& `: U7 ]
}
\3 b0 R% e$ `! A4 R" Z
$where = ' WHERE '.$where;
6 o# S7 ]7 N0 b; ?" @& [( J
$field = '';
+ F" w ?% h4 n9 I w# v
if(is_string($data) && $data != '') {
9 s6 U3 m- I" N6 J- y5 [0 a3 w' w
$field = $data;
( n4 w/ P! Y) N
} elseif (is_array($data) && count($data) > 0) {
+ f. a9 k% O" k8 C/ H% c
$fields = array();
( J2 S# {( M- K3 C) t9 S, l
foreach($data as $k=>$v) {
2 Q% q( Y' r5 i9 j/ W a
switch (substr($v, 0, 2)) {
! z; S. e/ `6 t( {. L% |
case '+=':
/ K. I; n2 q, l$ |
$v = substr($v,2);
6 T+ Y" v% g$ m+ v8 k
if (is_numeric($v)) {
4 c- |* x$ b, d0 ?4 [
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
2 b+ _3 N$ s+ o+ [) ~' _
} else {
0 Q" l V4 j6 Z3 J# `( ]
continue;
: ?3 W; G4 r& Z# h. @2 ^/ ]" w
}
" S' z: q( p! ?
break;
% E1 |9 H6 |% d) H0 F8 q( `
case '-=':
8 k# m8 Z" v% D8 c$ Q' M* W" W2 J
$v = substr($v,2);
6 P3 E- X8 R: L/ k) N
if (is_numeric($v)) {
- g7 O& f1 Z9 m' S3 s
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
1 {0 a- S9 J' j" g0 p& D8 C
} else {
$ k% x0 H, ?: w% J
continue;
# j8 A5 u) q1 B1 j
}
( [, z9 x3 o" A$ e! Z# |7 J
break;
' M6 K4 |$ r) e
default:
0 W" E: M9 o3 K0 J
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
- y+ g) h h: F
}
" f% P0 t1 s) i: u
}
& a0 I# a# x& u+ L# J
$field = implode(',', $fields);
9 T7 l" g; p7 n1 S& O2 D$ `
} else {
! o9 y/ t( a! ~3 q
return false;
- m2 j* e+ T2 {8 H$ Q/ F6 s" p6 S
}
4 @4 I& ]' L" B5 z
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
( u6 T$ ?: B6 S8 k$ v3 s( c
print_r($sql);
/ P/ E2 u2 B3 Z1 k1 G
return $this->execute($sql);
4 i( H7 J/ Q3 o
}
/ Y8 ?$ `3 Z/ @. |% D
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
" Z( @- O( d' K4 K2 B' }6 i8 f
/ @3 H# P! V, V w- G
攻击测试:
- B3 m- P( K$ _
测试地址
http://localhost
x3 L5 g l% [' _ q/ {
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
9 g5 N/ g% n t! m6 K
# x! k" S( S: o) @
[attach]179[/attach]
: A# ?8 v7 J) J
# \, G" a8 L7 L( j& l$ w* c
: K6 z" B- D7 E3 H+ c
[attach]180[/attach]
) v' s5 j0 J9 G4 t6 A4 I
欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/)
Powered by Discuz! X3.2