中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
& f2 A/ e5 \9 b+ g; g3 b+ ]
漏洞作者:skysheep
8 S4 Y7 p* ?* @$ b8 c
分析作者:Seay
: E& I5 ? J' G
博客:
http://www.cnseay.com/
% S$ }, Y% Y0 w* }
漏洞分析:
! F( b( D4 N5 W0 V! F
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
/ i. g" n, m& K+ M
. `$ p3 g4 B* F U- |1 C
) Z/ C0 d4 d# Y6 S
$ M) A1 F5 w% A; g3 m
public function account_manage_info() {
% Y; X" m+ C' {5 V6 Z
if(isset($_POST['dosubmit'])) {
+ ^$ Y, _ M# c. w
//更新用户昵称
& u" U1 T( U9 n9 q" d Z! H
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
* o3 ? b4 J% q7 }( D5 x: d
if($nickname) {
+ D4 t8 S3 o3 n
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
; h9 C" z) b( a
if(!isset($cookietime)) {
0 Y; |8 g9 X+ ^% `( {; ]
$get_cookietime = param::get_cookie('cookietime');
4 x$ ~9 o. i& J
}
2 b5 W. B1 u3 _; t
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
, _2 R1 e! ] P" |1 q) n0 T3 Y
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
! x, V" \, O! y+ ?! C0 G3 |
param::set_cookie('_nickname', $nickname, $cookietime);
; M Q" U0 Q/ A2 [
}
/ J* R3 Q+ j6 S% k+ _
require_once CACHE_MODEL_PATH.'member_input.class.php';
2 x8 s1 b* F! c3 S% w
require_once CACHE_MODEL_PATH.'member_update.class.php';
4 y1 H' P7 a& ~, G4 i
$member_input = new member_input($this->memberinfo['modelid']);
! R, l6 @ o; E. o) n& R( ~7 i
$modelinfo = $member_input->get($_POST['info']);
9 K% o% o+ H1 o- g9 V$ C2 I& [" i7 I
$this->db->set_model($this->memberinfo['modelid']);
1 {' ?' Z2 e( ^" ^
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
7 p3 f- i2 l; h) E, n! T3 N
if(!empty($membermodelinfo)) {
( x- P7 n4 V! A6 Q4 ^ c0 j
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
& a0 H( Y9 \- n( R- I
} else {
: e7 w- M) X" K" b9 A
$modelinfo['userid'] = $this->memberinfo['userid'];
2 F1 [ K8 h" G6 F0 r
$this->db->insert($modelinfo);
8 ?1 ?" _9 X! k0 Z7 S `! g$ a
}
9 R- _5 d6 F% [! a) Y0 Z$ b: ]
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
* ^, q. X: h5 s5 x
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
6 S0 S8 U: ~: z( @, }
! A/ }! n; ^4 [* @
& i: i& L! I; L) V; B9 Y
1 ?& P' m: ~& ~8 f0 b
function get($data) {
; _7 E( d% ? d! R! [
$this->data = $data = trim_script($data);
a) t5 z# Y2 p. U2 B" ]/ O
$model_cache = getcache('member_model', 'commons');
* Z: p) i; e# o5 H9 c: e
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
4 `- y1 ]# K4 A! ~2 b! K/ B
$info = array();
" ^) u. _& h: c! ?& j
$debar_filed = array('catid','title','style','thumb','status','islink','description');
$ e7 n4 {' i/ t j$ _ F. W' o
if(is_array($data)) {
% z9 Q% s$ c' w* ?- W
foreach($data as $field=>$value) {
h8 V1 F+ U" w) Y: G
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
( y; t( _5 K r) _! o1 c2 P& V
$name = $this->fields[$field]['name'];
* U1 Y6 ~) B( }$ [1 a
$minlength = $this->fields[$field]['minlength'];
. h6 S+ ]; g: O5 c( m
$maxlength = $this->fields[$field]['maxlength'];
7 t8 I& G" Z/ H0 w( T
$pattern = $this->fields[$field]['pattern'];
/ v- U2 I- q. H$ [1 ~2 q! R
$errortips = $this->fields[$field]['errortips'];
0 E n z) x( O0 C& @9 D, j# |+ w
if(empty($errortips)) $errortips = "$name 不符合要求!";
. g; v/ e9 g! m) F' ]* n+ k! o
$length = empty($value) ? 0 : strlen($value);
; y4 M* p$ L% s L2 n! g( s
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
' p2 q9 `5 O. F N- o
if($maxlength && $length > $maxlength && !$isimport) {
; ~. b" Y! k& |1 ]
showmessage("$name 不得超过 $maxlength 个字符!");
0 ?: z4 x4 n0 L; w
} else {
& d# {) n- {8 F" y* W! h
str_cut($value, $maxlength);
% H3 D' q, ^- [% B
}
9 I% R3 w# w1 F0 }; B3 p1 q, C7 F
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
7 Y9 ~1 i5 i$ Z: t
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
6 U4 |1 @4 I: ^0 N
$func = $this->fields[$field]['formtype'];
6 e8 l% p( q4 |2 K, }' B
if(method_exists($this, $func)) $value = $this->$func($field, $value);
1 T. a9 s' ]. D6 J8 Z s( N( u
$info[$field] = $value;
. S q4 q: g- ]2 n: g4 A
}
' H% ~/ P- V' O( v% O% Y2 ^. e0 H
}
+ G- M; L7 }' o! b& Y* i
return $info;
4 s8 P/ D0 E% K4 E) p
}
: Y4 |7 a8 ~( }/ O
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
i4 Z' b- `. r7 k9 \3 `% x% x
! n- z5 g4 m4 q% X) C% P) X
再到phpcms\modules\member\index.php 文件account_manage_info函数
2 u7 Q( N/ c. g% H# k
过了get()函数之后。
( Q0 a4 ], }; ]
) V$ | J# o7 o0 c
. M9 ~# j @: y% ?, D
$modelinfo = $member_input->get($_POST['info']);
+ [1 K4 e* A \7 M+ p6 J' M. C. W
$this->db->set_model($this->memberinfo['modelid']);
# e. {) q$ T( {4 s
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
- W& A( P4 b8 ^. Y, `( ~) G, ~- J2 l
if(!empty($membermodelinfo)) {
& p7 U. j& \) Z
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
( m- |. o) w5 Y/ M
} else {
* t# U% d* p8 ^ o
直接带入数据库,update函数我们跟进看看
$ }8 p' ^* c$ R1 o$ ?
3 f* d. V, ^) y& F
' S: {! {, I" U7 w
public function update($data, $table, $where = '') {
" J8 k9 `% T: c' \/ a6 B0 q( `
if($table == '' or $where == '') {
$ o4 t; H8 c9 P; u
return false;
4 ^! M# ]+ Y6 l1 Y1 f5 t7 u
}
- U, f! s$ h8 k6 |) u: `
$where = ' WHERE '.$where;
' U: M1 i6 y# a+ |
$field = '';
' {* w( n. C1 d% l9 C
if(is_string($data) && $data != '') {
: ~* p3 S# Z+ o) }
$field = $data;
/ l1 t2 ^1 @& A) T9 c7 s; s+ J
} elseif (is_array($data) && count($data) > 0) {
6 B0 H6 M- \+ R% `4 p
$fields = array();
( \- V" }( P' G( Q' F$ v
foreach($data as $k=>$v) {
; I- ]% |- F4 ?, R4 Q/ e& O S
switch (substr($v, 0, 2)) {
' N5 Y/ y0 U c1 l* h) N, M/ V' t
case '+=':
v4 u/ J$ G( y+ O7 L
$v = substr($v,2);
9 `( K9 S+ w* s6 Z1 u- C1 |( y! J
if (is_numeric($v)) {
2 y7 Y, ^5 A9 V! m' \# L
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
( Q" C2 T" U! d# a
} else {
# t" Z: ?2 q. D' f
continue;
/ v( c0 m& A! {# Y$ e5 u
}
& b2 f4 F- S) [6 @
break;
; y2 N- K3 W) d4 k7 @0 y/ }% x
case '-=':
0 j; |. I' N) R+ v' ~
$v = substr($v,2);
3 d9 Y. X# i6 A0 F
if (is_numeric($v)) {
5 c/ L! {5 j. q6 O% X. A* ~
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
5 ?5 j$ U5 ^3 l q+ V
} else {
, V& A5 {0 ^/ A0 h9 i0 D8 \0 }
continue;
1 d! n# \4 ~! F; V, \+ w. w4 B9 z
}
) a# M( u6 ]6 m1 K# m1 j& ]
break;
6 ?, U4 j1 b" J! \4 U- Y% _
default:
$ F$ P5 U# U) Y* N+ u& ~% c6 c
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
- q! M Q h H+ B2 s3 r. }
}
' e1 t1 e: D H2 H- a
}
$ D% L# z' n# a5 P/ Z
$field = implode(',', $fields);
# b3 Q( I4 A2 o/ O
} else {
' w5 V) l, U2 I. i* t$ R& O
return false;
/ f* u& Z! g, e4 f) ]
}
: T& G2 |, w e9 d/ p3 j7 u4 E
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
# x) q5 ]% g& z: k
print_r($sql);
! }$ A4 B \( g6 [- F5 w6 B9 l& [
return $this->execute($sql);
' i2 }1 x) }5 H5 N) E4 P
}
# n7 x* x5 p B+ } ?# ^
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
& ?6 o& {) _$ v4 q" B! @4 U4 ~
0 p3 p! C- a+ Q
攻击测试:
% e% A* D* D& X& w$ L* K& W
测试地址
http://localhost
2 z! t( m3 }, V- [$ ~
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
! I2 P2 M! O4 E3 @: o8 }$ h
0 v; P" }+ m+ R) C: O2 C
[attach]179[/attach]
) Y z8 w, J5 ?/ |' K3 p8 R
( d- G h( F7 }& J
- ?3 d4 G* n. K( ]
[attach]180[/attach]
) C; p& p) h# ^4 A$ D
欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/)
Powered by Discuz! X3.2