中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
9 a0 t7 t1 U. r5 h+ _, n9 [+ n. [
漏洞作者:skysheep
0 C6 n* q* ]' o) e6 q1 [9 D
分析作者:Seay
: s9 p5 D# `7 B+ y4 O- a
博客:
http://www.cnseay.com/
* O) _1 T; D( K' m
漏洞分析:
* n' Z ^+ b; |0 D* m' E
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
% G! {& B/ z: y. [7 T
. F) ?- l! p& z; _, J
& E3 X0 ~- }0 A3 ]. f
+ F) r) T; r4 t) y6 d9 C4 o
public function account_manage_info() {
+ Q% }7 A W) s5 r) n2 J- `1 H
if(isset($_POST['dosubmit'])) {
6 [2 T5 t. u- r/ D
//更新用户昵称
2 W6 v F$ T" I/ \8 C2 d
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
1 j2 _& m! A% s3 _; {5 C9 K+ F
if($nickname) {
: a1 W& s9 x5 I7 Z# q" H
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
8 b0 R6 A6 K0 H% _' g
if(!isset($cookietime)) {
4 V/ p8 H+ U) X* r$ O
$get_cookietime = param::get_cookie('cookietime');
' t2 m0 p; l8 v! Z* k, p
}
r3 X9 H8 s2 C& {/ w( o3 J2 y
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
7 G- Z$ k8 d. g$ v7 m, e& A# B9 o- {% G
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
6 e, B- u9 f% g5 `
param::set_cookie('_nickname', $nickname, $cookietime);
h" K0 w& J; L
}
% d& L% K* f) e2 a4 Z9 D
require_once CACHE_MODEL_PATH.'member_input.class.php';
6 _9 W o0 {5 X3 U3 E% e
require_once CACHE_MODEL_PATH.'member_update.class.php';
1 C1 Q* ] s, x6 G `3 T! O
$member_input = new member_input($this->memberinfo['modelid']);
) y' ]4 Z+ l! } a! q$ Z0 h3 p
$modelinfo = $member_input->get($_POST['info']);
' V$ m% B, h5 R4 Q1 j
$this->db->set_model($this->memberinfo['modelid']);
. q2 v7 M0 P9 q6 C ] h
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
+ i R- j. d2 ~4 e2 T5 m5 |- ~
if(!empty($membermodelinfo)) {
* P8 Q& F* H$ j- C \5 Z- C
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
1 _( \- \* A: {: y
} else {
; v! X1 X$ Z# q3 w- }9 X
$modelinfo['userid'] = $this->memberinfo['userid'];
/ f e) \6 l, c0 ~
$this->db->insert($modelinfo);
. P$ R# T, q& Q. |# I
}
( y, R0 y7 a/ u! z" j W& l( ^
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
' ~# ~% F3 B! b. ]
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
$ b! P0 r$ X M+ [. U
8 d5 `# F) W$ b1 G
1 i0 Z5 z7 r% q9 [: z
2 M- K: K- j) R$ P" h, g6 J
function get($data) {
9 p) |- N; I% d0 w3 h( Z3 m
$this->data = $data = trim_script($data);
$ q Q0 R! d3 V3 Z0 Z2 \4 n5 D% E z
$model_cache = getcache('member_model', 'commons');
, N/ O% @: ?8 t, ~, a' U
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
& N# v4 ]% q0 b3 c' p/ _% k
$info = array();
+ {1 _* \0 e- [# {8 w" m
$debar_filed = array('catid','title','style','thumb','status','islink','description');
& M: M' W. `+ Q% x
if(is_array($data)) {
3 }4 g6 X) i. O2 N8 \! l; u
foreach($data as $field=>$value) {
; y2 L* T, Q( {( K
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
0 p9 Z7 l; O) q. g) ~% s
$name = $this->fields[$field]['name'];
e! ]( H7 `% _, d
$minlength = $this->fields[$field]['minlength'];
0 K& B% T8 ]) S2 N7 L1 {
$maxlength = $this->fields[$field]['maxlength'];
/ x# _/ e/ O& I+ \. f* ~" @2 D
$pattern = $this->fields[$field]['pattern'];
( D. C9 C6 ~% w5 a" Z4 S5 y
$errortips = $this->fields[$field]['errortips'];
& O. V# e2 c- ?, H8 l) X, |
if(empty($errortips)) $errortips = "$name 不符合要求!";
2 j9 o! ^ g: p- H# j! j
$length = empty($value) ? 0 : strlen($value);
% H- l8 V% a: ?2 l* G* [0 O
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
. d/ G Z9 L v. `! E6 ^
if($maxlength && $length > $maxlength && !$isimport) {
# S4 b: `! A& D1 N/ l
showmessage("$name 不得超过 $maxlength 个字符!");
9 H! S/ A# X9 V& m, O
} else {
- i6 f( ]5 ~9 |2 R e7 R; w
str_cut($value, $maxlength);
1 R8 @ b/ K/ a2 ?& G
}
! I, `. b8 k! e% g
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
V G, I: p6 C& y2 b6 p
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
" Z: b) h: W( t. |2 F+ P
$func = $this->fields[$field]['formtype'];
& }, Z! k4 k7 q+ S
if(method_exists($this, $func)) $value = $this->$func($field, $value);
4 h2 v7 |8 |' @' W `
$info[$field] = $value;
/ }- V: c. j+ f: U/ x' L
}
3 r; k% c, @6 }
}
6 f, z q8 q& K! A" E& z5 f
return $info;
7 L' \1 E& X4 N
}
. I: v2 a4 {& D5 ]" {+ ?1 j
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
& S2 N$ O7 \1 `1 V* z
" J& L. U; U( \" n
再到phpcms\modules\member\index.php 文件account_manage_info函数
# O+ `/ d. y) s
过了get()函数之后。
) `9 j g4 p+ q1 n
: f, j9 I! x2 }# a6 P
$ C# W$ e3 u' B7 M5 I% o; L
$modelinfo = $member_input->get($_POST['info']);
- e! V7 m: C+ A( c5 c- V; Z1 F
$this->db->set_model($this->memberinfo['modelid']);
# g8 O9 o) K8 D2 y1 K/ B6 c% z W0 G% P
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
" ?; u* \" ?1 o6 T G+ n* B" J4 R
if(!empty($membermodelinfo)) {
B* r9 h( S2 _+ d9 H9 T$ u
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
9 ]4 K. \2 ?' h V. \/ q8 |. ^
} else {
5 t& g/ ^$ P0 M# I: N5 J4 ]; s I0 @
直接带入数据库,update函数我们跟进看看
( D7 R3 F$ @9 B
9 r( ]2 K; ^1 @
+ [3 I: E3 r% F% T3 ?; b. n
public function update($data, $table, $where = '') {
/ m7 S1 R5 ^, @" z. S
if($table == '' or $where == '') {
; Z4 q+ G) H, A1 c* ^0 j
return false;
! Z& z% _, I' Y0 w. h% J$ U
}
- b; m1 g+ D) g6 R u
$where = ' WHERE '.$where;
* t2 p- `: H. S! j3 ]9 o2 l
$field = '';
8 Z. B$ Q7 U F2 V$ D
if(is_string($data) && $data != '') {
4 V6 u8 t/ \3 }2 U$ g
$field = $data;
+ Z% M# B% F/ }; J/ c
} elseif (is_array($data) && count($data) > 0) {
# s4 e" s8 J0 T
$fields = array();
0 S9 {( |8 R6 b, \$ u, U
foreach($data as $k=>$v) {
6 ^. V. S+ R( Q& o$ E# I! G8 @
switch (substr($v, 0, 2)) {
$ Z% @1 J: ~& a* O
case '+=':
/ u: m E# j% W* v; y9 F9 o. b
$v = substr($v,2);
2 [5 k* B8 ]+ k6 Y# |) @. p+ v/ z% g
if (is_numeric($v)) {
0 F& O1 G! m' n$ d& N" ~2 i
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
2 y- z# a2 I, R& W+ ^/ b0 H. W
} else {
6 Q3 ^( N, h5 a2 y/ i2 Z* D
continue;
) U* s4 j5 k* R- @* u# R
}
X5 O( i; J- t6 x( p" u7 t
break;
7 {/ p O' k5 D. _
case '-=':
* j* M6 g1 x/ m2 g6 O; m+ G7 o
$v = substr($v,2);
6 m x8 s6 }4 D
if (is_numeric($v)) {
) k: p1 k$ @8 O: n! \, m; a4 s3 N
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
6 g. u8 K" P% D0 Z5 Y7 m
} else {
9 {0 ~: l' P7 P) o$ S1 f7 p
continue;
7 x3 i9 z8 s" L- a: t# n
}
4 S& \- i) [- ~- ^
break;
' \5 R0 z( Q, ^7 j+ v. g. b* s
default:
' D, Q3 }- _: e8 _; X- q
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
' F2 K( k- c+ n2 A* S
}
m7 T3 Y, f, A1 z+ y4 z$ l# V1 m
}
% E, Z4 Z3 r8 s I% f# h
$field = implode(',', $fields);
$ Y) ]' Y4 M7 f8 H+ e
} else {
Y3 X: p8 g) @+ r; d/ E
return false;
1 \0 C' t- g$ b- h' e7 H
}
9 U+ L" m" M+ ^: l" ?, P
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
1 S$ X6 A R2 g. s& I
print_r($sql);
% N: C: K- \, f$ d4 J( \1 `
return $this->execute($sql);
3 o5 k1 _; C( z) k
}
) V: ?& h1 g \
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
, @% R* o8 F7 p# j4 V
0 |; _) z" T& m5 e; m
攻击测试:
, d; R0 N! f+ }; n( L
测试地址
http://localhost
9 k6 V' {: W7 ?/ t1 V3 O* T6 q
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
+ |2 |3 ]' T% S+ Q( j
5 x% c9 j8 q- P1 d" m$ n8 x5 U
[attach]179[/attach]
1 P! `3 P! X0 o( t2 [+ i
, `) L7 B) u% g* P) f3 I
+ j3 D: X$ Q8 ]4 |" ?" X" M
[attach]180[/attach]
3 g$ O; z$ ~7 N! N
欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/)
Powered by Discuz! X3.2