中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
3 r: o" f; V3 B2 |( y
漏洞作者:skysheep
% e9 G# J* m; {
分析作者:Seay
2 B( h4 x2 X V( z# |, `- }
博客:
http://www.cnseay.com/
& s2 w+ Q* a3 q* J; C
漏洞分析:
3 S. F) A; W$ I
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
5 O4 m+ C' q# p; c; v% t" I1 w
! z( E5 H, Y: R M, [/ b
5 a; B3 _9 U+ S% b) Z. U* v
0 i% q, }) v. M1 s
public function account_manage_info() {
& _! Z7 ~5 X {
if(isset($_POST['dosubmit'])) {
0 n: u; ^3 m- E8 H' H
//更新用户昵称
/ W$ ^7 H3 B0 H8 j
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
7 a7 t5 I4 g* i+ G! q7 b* }
if($nickname) {
' q1 _: F& k4 H' x B0 s6 x
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
' T, O/ z e6 ?: L D
if(!isset($cookietime)) {
* W7 b/ V7 k6 Q4 d: q! B
$get_cookietime = param::get_cookie('cookietime');
3 k/ y& y: K8 f0 W& y# t% B
}
5 ^$ p# {7 u' ` m
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
8 j3 d3 M, g/ B, }
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
! a; E) E7 m- C/ \
param::set_cookie('_nickname', $nickname, $cookietime);
, c* C1 a" }! Q: P3 h
}
7 j5 g/ C5 z) o7 z4 F' p5 j' P
require_once CACHE_MODEL_PATH.'member_input.class.php';
0 r% ^" H2 F, u6 ]; u* x V
require_once CACHE_MODEL_PATH.'member_update.class.php';
6 s5 g& j: V- U2 G" X7 Q' e+ N
$member_input = new member_input($this->memberinfo['modelid']);
5 l* \) j1 O, R; J0 b+ O+ Y- i/ a
$modelinfo = $member_input->get($_POST['info']);
* l" {3 e& G7 {' P
$this->db->set_model($this->memberinfo['modelid']);
' _# q [8 n( N/ x$ d' U" j
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
( J% i7 U" `7 @, c+ o
if(!empty($membermodelinfo)) {
# C, I7 x. g$ ?1 B& \
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
; e, W2 T2 R! _6 E" j# X8 v
} else {
; q8 R% i8 l: f$ X" U: y
$modelinfo['userid'] = $this->memberinfo['userid'];
: u7 {( L* S' P: I5 z+ {+ J" z
$this->db->insert($modelinfo);
9 X) ]$ ~1 Z4 B
}
8 d$ J, J/ f9 t- k8 v
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
4 T: f2 @: L0 e' G! n) u
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ n" M* v1 O4 W8 o' {, |
# t. r' p3 T& {0 b
+ J( P# A4 D& ~, u% }
! T- c9 F& _6 e( O( J! ~
function get($data) {
$ p8 p5 c8 G( p: L- y9 R% E
$this->data = $data = trim_script($data);
( S+ p9 z+ m" g* z H$ Q2 _% F; s. R: H
$model_cache = getcache('member_model', 'commons');
1 Q& [2 U& ^+ e8 b, @9 L# I: \ t, K
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
6 C5 w l& }( f3 J/ M
$info = array();
' j' a8 T s. [; ^3 m
$debar_filed = array('catid','title','style','thumb','status','islink','description');
. Z% D1 R$ Z h' F% b- h. {. ^; L* r3 a
if(is_array($data)) {
* a+ e! j/ Y' w; h5 Y
foreach($data as $field=>$value) {
j2 H: N7 `! ~- B6 u% D
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
* j+ Q6 `# Q6 f: i6 j K) I
$name = $this->fields[$field]['name'];
0 x7 g4 n& p+ x4 ^; J# e- O! D, l3 ` O
$minlength = $this->fields[$field]['minlength'];
! t0 p/ R+ z; A- }8 r$ c
$maxlength = $this->fields[$field]['maxlength'];
# j/ h( `' G0 v3 a1 c' M: E/ [. j
$pattern = $this->fields[$field]['pattern'];
$ H1 M5 B6 n+ n
$errortips = $this->fields[$field]['errortips'];
# T) H# G& ]; E5 I) {
if(empty($errortips)) $errortips = "$name 不符合要求!";
3 q9 _& ?# l/ t6 U v
$length = empty($value) ? 0 : strlen($value);
J" f# ?, w) N0 h+ n: i( B
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
" j' K8 U2 N; p6 J9 f9 d5 Q2 i- H
if($maxlength && $length > $maxlength && !$isimport) {
4 D7 k! {, x1 p/ ~4 u( u% w" z
showmessage("$name 不得超过 $maxlength 个字符!");
9 x/ @4 {: g! }- s( S0 |0 `1 c; A
} else {
( [0 V j2 T; r7 E5 N
str_cut($value, $maxlength);
! W+ } _' B4 d$ Q! u3 l
}
* I1 n/ @. Q) t6 [; K5 ~: z) Q9 U
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
# }! y# t$ K2 @0 \* r
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
% {) ]5 L9 ?4 u" o% P
$func = $this->fields[$field]['formtype'];
8 {& X" H% N+ n/ t
if(method_exists($this, $func)) $value = $this->$func($field, $value);
/ e" V' o% u( V8 ]! y
$info[$field] = $value;
: y3 Y$ Q# ~2 Y; Q; |
}
9 O. X: r H; C6 J; ~4 o' B' a
}
& @* P8 \; R5 S& \4 d0 g1 P
return $info;
' d V- t6 y( q; c/ L! c( |
}
3 m3 \9 X& X$ G R
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
8 P" o7 u( ?" r, p+ ?: b; l% \
: O% r) N- ]* O# D) e- z. o
再到phpcms\modules\member\index.php 文件account_manage_info函数
. J, ]5 ~; I2 p f$ f
过了get()函数之后。
2 b, l, h% P% C6 M
% y% ]: {4 w% v
, m) A! j1 [5 G1 F6 w5 t
$modelinfo = $member_input->get($_POST['info']);
. s5 H1 h) J2 _: ]. B
$this->db->set_model($this->memberinfo['modelid']);
! w* |; _ K1 `4 }
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
( A( j1 y$ D* ?" N4 m: Y
if(!empty($membermodelinfo)) {
+ }8 c' l5 h- u* l' i
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
7 l6 l O( Z1 J8 W
} else {
2 p) q( n- T# e& q
直接带入数据库,update函数我们跟进看看
+ q9 p7 f" C* N2 g
F$ y. {. ]9 b4 {$ _
, ^$ u. Q9 t8 f0 r* ?
public function update($data, $table, $where = '') {
& B& m, i: [4 P/ y5 ~! K5 z$ `
if($table == '' or $where == '') {
- V: ^; {1 B5 e% W6 Q+ n2 M
return false;
* ^, L+ U) I6 ]
}
3 E; O$ ?4 d+ X; Z6 r
$where = ' WHERE '.$where;
6 @2 {5 J" C, V% o- O9 n1 C
$field = '';
+ K3 Z4 p# {" f5 q6 p- |$ `$ @
if(is_string($data) && $data != '') {
, v" c" L1 y; \3 r, a
$field = $data;
! c2 \( f! d- P, d0 `/ r8 z
} elseif (is_array($data) && count($data) > 0) {
. O# i* V. @$ \7 @6 B6 i k
$fields = array();
# d0 [, j2 ^" V9 H
foreach($data as $k=>$v) {
! q/ b! d# D" l7 w( N( w
switch (substr($v, 0, 2)) {
( J s$ ?" s6 e" t* z: U' I8 K
case '+=':
' c/ y) p. R% e! u X* G; K
$v = substr($v,2);
& }1 r* n* v( L% f7 N
if (is_numeric($v)) {
$ i: k7 Z$ G+ t* i0 o4 V
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
3 X+ t1 @1 x! O8 s
} else {
1 s/ B# s7 S3 w8 ]
continue;
G* |8 C* V! v+ L2 q
}
' O3 n0 P/ c& d( {
break;
8 f2 k3 v# u/ N b% }: e
case '-=':
; k7 C; [# A U# F% @/ b
$v = substr($v,2);
6 l% A, ?- E& _7 _% P
if (is_numeric($v)) {
3 d% {% X2 w! P6 d& E; J
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
' z( h6 J3 K8 B% M8 f' A" m# F
} else {
/ Y; x) a* ^2 r: i- x* m/ I' d2 L
continue;
7 `/ L1 p! v/ J# t% w4 D
}
1 }/ a, F0 i" p% Z7 `
break;
5 X, x0 P( T& E5 c
default:
' X( i6 |' ~8 @; j! g0 Y4 U
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
# t6 c% Z+ L; x# w
}
6 j9 Q. a$ d5 O' w5 n5 f2 R
}
8 }6 L8 O+ w* W$ ^) |
$field = implode(',', $fields);
7 Q4 F, t7 m- ?
} else {
6 S. N/ K3 t- y$ B' j! O
return false;
) G$ |! O# Z8 d% j) R5 m* v
}
/ ]8 q" x O" o. E1 l3 G! V3 |
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
% l/ e/ Q. s" N( E. j1 @& k$ R( d+ a
print_r($sql);
, I2 q3 K' W3 u3 T1 ~* |- n: ~- M
return $this->execute($sql);
3 h; ~! a8 g) y3 d8 Q
}
" J1 P2 l; a. d8 L$ ^
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
. V. R6 U$ A. v& X# K! t5 W+ ~9 W
7 N: @3 r v6 W) U7 S8 I
攻击测试:
+ _" ? @2 ]2 n x9 B# Z. A8 `3 ^
测试地址
http://localhost
7 I& X2 ~8 Q7 t
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
3 v- D, p8 E0 w1 X# ~7 D6 ^8 d
1 p0 q' R6 w% c% A
[attach]179[/attach]
$ _8 Z' T9 @9 @; @# [$ a) v' ?
2 N( X6 I) }( e( I. {
# f* E/ e; @ `" @6 s4 c
[attach]180[/attach]
h3 z" N5 C9 _: P1 H. h9 Y# X
欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/)
Powered by Discuz! X3.2