中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
( A0 @3 ?0 O2 I) \
漏洞作者:skysheep
7 c0 ]3 n9 Y& S& i
分析作者:Seay
5 G2 K; D9 {3 Z6 n& P( ?
博客:
http://www.cnseay.com/
9 v, M9 q+ i: c# t' T
漏洞分析:
5 f: D5 S6 q& _2 ^6 G
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
: d `. g* K# `3 w: f; R0 T8 O
- i2 v0 W. E! _9 m6 s- g6 |
8 z6 ~$ O* |' ?3 N
+ A: _ L4 c% n: M! B
public function account_manage_info() {
8 f2 R) }. A* @. p8 [
if(isset($_POST['dosubmit'])) {
, B9 [/ p7 H& X% v7 r: d0 X
//更新用户昵称
$ M7 _/ b* u; I' g6 `$ ^! C1 f5 L1 k
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
3 H, \4 {- j- g8 h
if($nickname) {
0 M& g! W& _! t( U/ m _9 z3 g
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
) v' J4 I. C8 [+ Y
if(!isset($cookietime)) {
( E" h7 x# U7 Y ?7 R
$get_cookietime = param::get_cookie('cookietime');
+ c) B+ S# ]* G H3 M
}
' [( b: C, f9 h
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
/ K- _# U# y$ Q0 r& l
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
% k- A" Q, R0 _( c, z
param::set_cookie('_nickname', $nickname, $cookietime);
4 ?/ }- t( D3 V4 p" |6 H# k
}
' w- Q5 s9 |. d- \" d; M+ \
require_once CACHE_MODEL_PATH.'member_input.class.php';
* H3 a2 p( u/ o+ A m
require_once CACHE_MODEL_PATH.'member_update.class.php';
+ U( z- A5 e2 ]! n& x7 Z/ W, O' ~
$member_input = new member_input($this->memberinfo['modelid']);
$ u* x5 g5 E5 U8 U" \2 L0 |/ x$ r
$modelinfo = $member_input->get($_POST['info']);
! f+ c) m. L7 f9 c* h' l* j
$this->db->set_model($this->memberinfo['modelid']);
( G: }4 @% [0 q1 g0 `
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
, X# Q" S' ?! X2 r% A* \% r
if(!empty($membermodelinfo)) {
) P' I; F( ~1 d4 D; X$ i5 m- x
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
L% I$ M0 L6 D$ y6 `5 O- L
} else {
, S" R/ ], Q9 r3 V! _- k
$modelinfo['userid'] = $this->memberinfo['userid'];
$ R2 |9 o9 @+ Z) S( i2 J: c6 }0 Z1 O
$this->db->insert($modelinfo);
2 C8 v. P% X) X. l* W/ |$ y
}
/ L0 e7 V; n! X( o4 G0 M; @- ?1 E
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
T4 N6 a3 J; e9 s4 A; F( O% c4 G
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
5 w2 D0 I) D% y* I1 y7 r
* D5 Z- A f% ^
; K2 ?) l& r7 F
" T1 Q- v Q# y d$ V% Q5 t
function get($data) {
O( E3 n3 `" n, g. Q
$this->data = $data = trim_script($data);
0 U1 o, u& D" q) I& q/ c
$model_cache = getcache('member_model', 'commons');
1 ?3 N1 y# E8 N; x1 H
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
; s, f+ Q) V2 @: M( B
$info = array();
8 {$ C" w2 m' }1 [; Z$ G
$debar_filed = array('catid','title','style','thumb','status','islink','description');
# ~. ~8 B R) E+ W% }
if(is_array($data)) {
0 b5 q& }. U0 ?* J" x* }+ m
foreach($data as $field=>$value) {
% X$ B) r3 O9 G* M0 q
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
o" r2 w" z+ O* r
$name = $this->fields[$field]['name'];
: K, q# U0 v& l. W: M1 U
$minlength = $this->fields[$field]['minlength'];
1 o: _( g: d) m1 @# V
$maxlength = $this->fields[$field]['maxlength'];
2 }! B% c* O2 C4 e1 `& R! L/ B
$pattern = $this->fields[$field]['pattern'];
3 v, F& V; \# R# s( I
$errortips = $this->fields[$field]['errortips'];
# B/ A" V1 ]- \+ ^5 B8 E- l
if(empty($errortips)) $errortips = "$name 不符合要求!";
+ k" A/ V4 U: ~$ n7 v5 @
$length = empty($value) ? 0 : strlen($value);
$ W+ X0 Q0 ~' j
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
6 x( b h0 |2 ~1 L0 H! x
if($maxlength && $length > $maxlength && !$isimport) {
) u, [7 o7 H; U, E
showmessage("$name 不得超过 $maxlength 个字符!");
x1 ^: W+ d1 X% q/ U6 x# c; X
} else {
: V. ^( b: b: K$ T
str_cut($value, $maxlength);
! ~) l& q4 ~8 ^5 Z- u" j
}
. |- `% N8 v% T' O
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
3 _% J, M- G G7 z
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
+ b c( |' a: b3 k9 _
$func = $this->fields[$field]['formtype'];
" M; ^9 L8 r( ~* ?% ~
if(method_exists($this, $func)) $value = $this->$func($field, $value);
; X# R/ G( k. Q' ]* q9 J9 a
$info[$field] = $value;
2 I5 n: J1 L8 H. \. E7 d
}
' O* j% V' Q9 Q& X6 Y9 F$ i- l
}
& e8 s) _$ g9 _6 D: |; g
return $info;
- Z. _8 L4 V9 l* k5 f! y! T
}
# w# B7 v% E) ~) r2 F8 J' K
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
0 C0 n8 t+ z2 |& j
+ t; b/ Q0 t" H" d+ ^3 i) H3 N
再到phpcms\modules\member\index.php 文件account_manage_info函数
+ `/ B2 j: K/ n" N3 O1 N$ p, }% l
过了get()函数之后。
8 a% E9 T# C, B. H
9 w8 `/ E) D8 u) q
9 T5 X2 M& u+ Q c9 L& U
$modelinfo = $member_input->get($_POST['info']);
3 s9 f3 R4 v8 A7 P8 Q9 A5 C/ |
$this->db->set_model($this->memberinfo['modelid']);
$ m* p' W- _- W
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
* r' w" m; `6 O: T2 ~
if(!empty($membermodelinfo)) {
! O/ x) N% e+ g) [
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
. a- k5 K3 v* q; a
} else {
& \5 J3 ~( z. Z
直接带入数据库,update函数我们跟进看看
2 b. c4 G7 J+ j' s
/ r, `% N( F/ f! }# m5 H
6 d; E" ]/ [# U9 S( Q1 W5 B. F
public function update($data, $table, $where = '') {
4 w [5 a( Q! y1 {( [' b& p, H
if($table == '' or $where == '') {
( u" G5 Q# W+ }$ l6 I# E7 w
return false;
) p& ~6 u9 U' q5 v+ x& d
}
+ F) ]0 B* Q0 G1 j3 ?
$where = ' WHERE '.$where;
% V6 }8 D' z/ [+ t( W- o+ q
$field = '';
, q6 P, p9 V p5 ]7 U* {- ~
if(is_string($data) && $data != '') {
. ]1 g7 A+ q4 ?9 ]
$field = $data;
W0 A3 e m- e* H1 o
} elseif (is_array($data) && count($data) > 0) {
- E" D. R# s$ ]
$fields = array();
- }1 I8 e. d9 U% N/ G! j
foreach($data as $k=>$v) {
7 z) H2 A x4 l* a+ @; Z
switch (substr($v, 0, 2)) {
' L* v8 R6 O: K+ R' K! C" T7 t9 v
case '+=':
, Q4 m" N( u7 j, g' ]( E. e$ F) C
$v = substr($v,2);
1 y( F8 e, a4 x: ^+ B. `4 s" M
if (is_numeric($v)) {
, {2 k9 p" z- o7 }1 L# G# T B" R
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
2 F( i( r5 t( w
} else {
6 f6 p: ]7 q; Q q$ J2 Z/ l
continue;
9 L- X/ D& ?0 V6 ?' A. q" z$ l0 S
}
' H& e2 A4 D8 ^7 H
break;
, m5 d. f/ b; R+ P7 H" Y
case '-=':
& R% w$ i" ~. d8 O) V
$v = substr($v,2);
/ _9 ?6 n! z6 R, u
if (is_numeric($v)) {
; X$ B# S3 x$ M
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
# X3 `, O p; ^% p2 Y" N
} else {
( Y) {% n5 X- |% u
continue;
9 L4 d" L% \+ ^$ n
}
! j% H+ B$ q8 A0 a! J& c
break;
1 R% |5 W2 w4 r g# Y: Q! O- n
default:
6 {5 i0 Y1 h. T# C* J# Y p( z
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
6 u0 M& ]5 r% P/ i4 `+ u& Q; L( \
}
4 G5 P4 e: [/ C1 u
}
6 S, W1 R' j- t3 [* V. d
$field = implode(',', $fields);
# q6 h' H3 {3 r) G* L- S5 U
} else {
# f" m( Q @" s5 q9 f0 p
return false;
! U y3 D$ n% v$ o, M' c3 w
}
9 P, ^& B( I% x& _* c# O! h1 c
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
2 [. G A5 a3 w. F$ r- O
print_r($sql);
9 n5 G9 `# c4 F3 s- w* m
return $this->execute($sql);
! o( v7 X+ r S2 U+ c/ k
}
6 l! |4 Q: J0 h' U
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
; f0 g n+ n% P- B
) X# t! C2 L+ C
攻击测试:
( K4 V( ?! [' {! Y4 I8 K
测试地址
http://localhost
) e( K$ B; N$ T, Y7 x
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
* E2 ~5 M' X; L% w/ g
6 `) r5 ~9 g( j% r$ g9 ~
[attach]179[/attach]
0 {$ E0 ?% S# f* y* e" {; g
2 e. a% [) }# C7 D: l0 t* Q
4 T1 P4 p: w7 ]5 I) g
[attach]180[/attach]
8 L1 a4 r# _- U5 W! k$ }7 a
欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/)
Powered by Discuz! X3.2