中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
; \1 T" R" ~) A1 K! d
漏洞作者:skysheep
$ \+ q+ a6 Q% Y1 s/ G2 G
分析作者:Seay
/ _$ b# z8 R- Q; e) ?" y
博客:
http://www.cnseay.com/
- V2 I1 s6 q4 m+ n5 a7 D
漏洞分析:
T; a* k% I9 E/ b* N4 f
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
, g$ |; k6 E1 R9 q3 _
* ?. z6 W8 o; T- m- o/ ]
) I/ G1 h9 d c6 @5 |3 v
5 o) F5 c1 h6 E4 j. [% \
public function account_manage_info() {
5 c# Y) P* ~( Y z* l: T
if(isset($_POST['dosubmit'])) {
3 p/ {" j, l8 a
//更新用户昵称
5 M9 v) h% X5 {/ n
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
1 ~. ^, ]$ N, R9 K$ O
if($nickname) {
; p! l4 R9 Q8 z( @0 b- |
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
& S: D- z- `, R! U( P) \/ `
if(!isset($cookietime)) {
& }3 Y V! T" s! |8 Z; q
$get_cookietime = param::get_cookie('cookietime');
; h& S# N- B4 R X- D
}
( [) W1 m, S# K$ B" t' S
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
+ s0 U- _9 ^+ V, H$ L% F
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
2 J$ @5 ^: j) G( X
param::set_cookie('_nickname', $nickname, $cookietime);
" a# k9 e1 `: G- }( Z& l s
}
0 f7 v/ s0 k$ o" z* F
require_once CACHE_MODEL_PATH.'member_input.class.php';
' w/ H9 \4 O, H `) n3 M
require_once CACHE_MODEL_PATH.'member_update.class.php';
' b! ?* q0 w# D
$member_input = new member_input($this->memberinfo['modelid']);
/ i, d9 a! _$ W6 g2 u% d1 C
$modelinfo = $member_input->get($_POST['info']);
% N5 }" Y' n Y) }
$this->db->set_model($this->memberinfo['modelid']);
5 H' B: c7 Q7 q3 W. F2 ]/ E
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
3 Y1 X' E2 a3 l' c5 H( ?# ]/ r: {3 L
if(!empty($membermodelinfo)) {
" W! `. g$ [9 U# x) j" r
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
# b0 g, r: w8 K ]7 K" {+ _
} else {
( S# Y" d+ b3 P9 E! R1 H
$modelinfo['userid'] = $this->memberinfo['userid'];
$ R; i; F0 Y" f# v
$this->db->insert($modelinfo);
: p+ D, ^2 K1 n5 e; k
}
( {; _3 ~" P( t D3 t9 J o
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
8 m9 ?$ f# ^+ w6 O0 r: Z
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
( J, Z0 [- v3 j3 v0 n9 N
' p9 K3 Z* ]' r. J; t T
T. _. ^. U' z0 E& @% [2 V7 k
* J: \: t2 U d5 a
function get($data) {
; g/ k, z+ h4 c* L' ~) j0 y
$this->data = $data = trim_script($data);
" O8 _/ @, i6 q' V
$model_cache = getcache('member_model', 'commons');
4 i/ U4 C4 U7 v" q" N: ]
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
% a2 q/ q/ h' |/ g' J
$info = array();
1 O( _- ?' Y2 i7 C: x; }
$debar_filed = array('catid','title','style','thumb','status','islink','description');
. A# g6 d5 U6 ^2 [8 Y% D
if(is_array($data)) {
9 _. s5 \% B+ {4 ]9 Z8 G$ Z
foreach($data as $field=>$value) {
+ P/ w. s- e) l
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
' z0 F4 k6 U4 s% b0 H
$name = $this->fields[$field]['name'];
2 x# p7 p( ^8 R; x: V @
$minlength = $this->fields[$field]['minlength'];
) ?- E3 ^/ U1 a# g0 _" K
$maxlength = $this->fields[$field]['maxlength'];
* W" v# N' R3 \$ t0 k$ q f
$pattern = $this->fields[$field]['pattern'];
q6 N' X, @+ }* }% ?$ d
$errortips = $this->fields[$field]['errortips'];
' [9 ~) @5 S8 j9 Z
if(empty($errortips)) $errortips = "$name 不符合要求!";
5 A1 v0 [: R. ]( O' _
$length = empty($value) ? 0 : strlen($value);
! h7 K& V/ ]0 Y9 e
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
: u' ]: i4 [& e0 Z. j; p$ y0 @
if($maxlength && $length > $maxlength && !$isimport) {
$ Q3 G) v$ N. Z- a" K. B! ]
showmessage("$name 不得超过 $maxlength 个字符!");
: m- D& @ j T+ I, p" w
} else {
% }) K/ v* C" O/ y7 p5 l9 Q+ y
str_cut($value, $maxlength);
$ L* d# h2 q o! \% I1 b5 J/ L
}
6 X$ U+ C& V. ?3 U1 a5 L
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
% {. j s! u" D b6 t. d0 l& {
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
' `7 c8 U& ^1 g: o2 i* L+ o
$func = $this->fields[$field]['formtype'];
* L. f" j* N( j/ U g: a7 w& F) l
if(method_exists($this, $func)) $value = $this->$func($field, $value);
8 `" r* Y c! I/ W; e
$info[$field] = $value;
/ P x/ N* b; @/ e7 `6 h
}
1 ~& c5 t% E% ]; z% U% X2 F
}
& P0 v$ x7 M& V
return $info;
- e: i% A& t, ]5 T. Y+ A
}
% h/ P* ~( Q, w! r' H! a
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
/ {# z8 C) p8 d5 q; t
$ u$ b5 M/ Q a5 z
再到phpcms\modules\member\index.php 文件account_manage_info函数
8 t% g3 ^2 k$ M0 j
过了get()函数之后。
7 C u+ [3 Z# F& Z
3 p, @7 W3 d% l3 ^
( g% A1 }( |) m3 w% L
$modelinfo = $member_input->get($_POST['info']);
3 q+ y2 R# m0 B$ q! K
$this->db->set_model($this->memberinfo['modelid']);
: z, v4 n( y0 X9 g% A+ V$ {9 K
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
/ h& h- l' F. M( M6 D& N
if(!empty($membermodelinfo)) {
p8 t4 v# J$ @
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
p1 P8 t/ L& x( R7 \
} else {
! `8 ~! M- I) u4 b) z
直接带入数据库,update函数我们跟进看看
0 V1 Y* Y$ x% v$ ]
: R% T- e$ m- f# v+ U
4 E2 z4 ~. ?3 n: O# ?
public function update($data, $table, $where = '') {
- p9 }, j: J# }5 e- ]* C
if($table == '' or $where == '') {
+ P1 y$ e0 ~# H- b
return false;
. d, `( R! {) U' O4 o4 h
}
0 t9 J x+ s7 K% h8 }+ F
$where = ' WHERE '.$where;
1 s6 z6 l' n: x
$field = '';
, `& s% a9 f6 l# {
if(is_string($data) && $data != '') {
4 j" D0 W q6 A- ~9 B
$field = $data;
# Z& t2 ^ @" ~( q L
} elseif (is_array($data) && count($data) > 0) {
( ~! k$ z+ W9 y
$fields = array();
9 T% T a6 E5 O! Z" U
foreach($data as $k=>$v) {
. S# V+ ~. @/ Y0 |# @
switch (substr($v, 0, 2)) {
9 A+ H2 c$ R# G1 X6 }
case '+=':
" g% u/ m$ [4 {4 w, I0 C( {! {
$v = substr($v,2);
( }! g5 {2 U' A# y* H) l" J7 `, ?
if (is_numeric($v)) {
8 w0 ^) L/ ]6 h1 ~5 T! w0 S
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
4 q; @: x, w8 n! j9 q" C: I
} else {
1 q2 F) Q. z! L7 D4 B" F& `
continue;
4 l$ M9 ?8 ]/ z: d
}
; }# f5 R/ X' ]9 z( ]# K& l9 m0 _$ X
break;
6 x+ d' ]/ G. D+ M, E& C
case '-=':
4 }% @7 F( s8 t3 G) C% n2 T
$v = substr($v,2);
' w, o! c+ w! V f r3 w& N9 k! j
if (is_numeric($v)) {
8 M, ]( r2 ]% M/ @ O6 H
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
' ^1 f* H6 `; i, E6 O, o
} else {
5 H9 U3 T9 j# T4 h/ j3 s- Y
continue;
" h7 W- v, \: I) q' M
}
( m& m' P6 {, s- e* F# P+ X, J3 [
break;
% B( ^% @% M- s( E
default:
7 o6 ]! a+ t( C, X
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
: b6 u5 N; H6 \% V- z$ n( ?% J$ M
}
6 K9 V' X5 o. G% `5 k/ R
}
; k8 G2 h5 B$ A/ O. t! P
$field = implode(',', $fields);
" q, k/ t" c4 ]5 |# o! f! P
} else {
* T& }; S; c+ K) o. u6 k: N! ?
return false;
2 m& f" D( L/ C
}
# }/ h& N* T9 A, \8 O+ v, d
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
2 [1 `7 a2 F5 K( k# v2 T! R
print_r($sql);
6 A/ x- ^3 D; r7 I6 {
return $this->execute($sql);
6 s. M' C# G. q5 W' w; ~7 e
}
: B" X7 @7 ?/ D' v. `% L6 o5 m
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
4 u, Z% q% A) u$ B0 H2 k
0 g4 v# p( b" [" u
攻击测试:
S! E* N9 N& A8 H G& F& u
测试地址
http://localhost
1 j# V4 \, }/ u3 O7 x: n
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
9 h! N# Q# k5 Q- R& W7 W/ Q
% z7 Q+ b: Y0 ^
[attach]179[/attach]
9 C' C' \% a" r8 t% R, h
' j3 z; { g% |% l0 B& u4 D
0 Z9 B7 L& R, O1 C
[attach]180[/attach]
* D1 o, x8 o. t- l5 i
欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/)
Powered by Discuz! X3.2