中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
! z3 ~- F9 |" Q6 \+ _4 {$ l
漏洞作者:skysheep
4 G5 h/ o! x5 M. e6 k
分析作者:Seay
4 a; d2 N8 i# q! s
博客:
http://www.cnseay.com/
( V/ o3 }" j* B1 r3 w3 r' k9 z6 a
漏洞分析:
9 L0 M, W' L0 a
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
( t$ T7 g2 L( g* B2 b) r
1 x, I0 V) E6 T5 E! p) [
* n C0 d: l) g ^
; l; e, A* N9 o4 m, _
public function account_manage_info() {
; Q0 J; u& u; C0 O& G
if(isset($_POST['dosubmit'])) {
( |6 G: o2 A, R6 B, R2 J
//更新用户昵称
1 B8 Z/ K( {$ g% Z+ m
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
4 O2 x; m* Y$ K/ `/ D1 c$ J
if($nickname) {
J' R7 n+ Y% ~. n
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
) M* v, X. O2 k' i6 H" S+ [
if(!isset($cookietime)) {
2 E' }9 p5 Y# W& h8 Z+ a
$get_cookietime = param::get_cookie('cookietime');
7 f3 c1 d$ W1 C3 H
}
- W0 O( k, F, C$ l% [' o
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
4 E* ^3 a- t& ]* K" @. s+ j
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
7 | q% C' a0 t& ^1 l
param::set_cookie('_nickname', $nickname, $cookietime);
& m7 c: j. z: g9 K
}
7 @% q- O+ F7 R: C. Q
require_once CACHE_MODEL_PATH.'member_input.class.php';
! G- z" l, w" p Q
require_once CACHE_MODEL_PATH.'member_update.class.php';
& U; y. R4 O3 {7 i
$member_input = new member_input($this->memberinfo['modelid']);
/ H: J+ w ^7 a1 j7 o j/ u% O
$modelinfo = $member_input->get($_POST['info']);
3 B0 {8 r! p7 d# d, Q( g* `# V/ a
$this->db->set_model($this->memberinfo['modelid']);
) \' W1 {7 K5 T* {( B
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
+ G0 ^( U. F% q$ y7 O' F4 W& |
if(!empty($membermodelinfo)) {
/ Y0 X8 \! o2 G! }
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
$ h% J4 z9 B7 c8 i$ q2 A/ o
} else {
( g( v; a* N& g5 I, k0 {
$modelinfo['userid'] = $this->memberinfo['userid'];
; z. w; j; Z) t% A' }: {# d1 ]6 [2 d
$this->db->insert($modelinfo);
. ?3 U% o4 o! Z, E
}
1 k* a3 M$ }; I( f! I
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
4 i5 v% l' {$ `, `% d" L4 X
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
" t( [ G) x* ] U
# P2 V! L% j4 z. W0 B2 ]
# q% |, r" m) Z3 h- G( e& f0 ]
% a4 l9 K) P6 [- H7 X
function get($data) {
9 w* c! t& b( b$ R- G0 x9 `9 a
$this->data = $data = trim_script($data);
& S, \5 x' j3 G& S9 z: E J" y' M
$model_cache = getcache('member_model', 'commons');
( E$ u4 s/ h; \" [
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
& X/ d1 A1 [2 b
$info = array();
1 O0 N. q( Z, ^1 A% b& Q; m; R
$debar_filed = array('catid','title','style','thumb','status','islink','description');
2 F1 `; w& V9 T* h, J0 v
if(is_array($data)) {
) ]( w6 f# x7 n9 g! f; Y9 s
foreach($data as $field=>$value) {
& S: L6 M9 j2 V# J+ X
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
, J) o6 ]8 @3 r* w/ O9 Y
$name = $this->fields[$field]['name'];
" ]& ?6 u1 C- l; v3 J& H
$minlength = $this->fields[$field]['minlength'];
9 l' [3 E$ K( V
$maxlength = $this->fields[$field]['maxlength'];
& f' z p- M/ w
$pattern = $this->fields[$field]['pattern'];
% e4 @! e1 {5 r& g9 J: k4 T- {
$errortips = $this->fields[$field]['errortips'];
; A1 y. |# p4 J4 @
if(empty($errortips)) $errortips = "$name 不符合要求!";
$ H n, G( `/ K, y0 N8 j2 \
$length = empty($value) ? 0 : strlen($value);
/ m$ H* X" C# I5 r0 Y
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
5 X0 m( ]- \3 p" ]
if($maxlength && $length > $maxlength && !$isimport) {
# _) h# H7 m ^2 `1 w
showmessage("$name 不得超过 $maxlength 个字符!");
) U. d Q. U! I
} else {
, ?% R* ^" z9 B
str_cut($value, $maxlength);
- t/ N, R, `. M% T w
}
* o# @1 m$ I0 \- v/ W J
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
; n" ]% y# i$ d6 ?
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
[" L. g1 g) c5 F1 T# B
$func = $this->fields[$field]['formtype'];
8 z! T! v4 N- d* T- ^; y
if(method_exists($this, $func)) $value = $this->$func($field, $value);
* }& j" l8 l4 [3 s8 l
$info[$field] = $value;
. Y, x" i, V) O2 w3 z
}
# z4 t7 \' d U" p, y" k( d
}
9 d/ b/ X: x; H) E/ e& v
return $info;
# N1 \& \, @$ C5 r* ~6 L- Z5 L: S
}
4 }; N$ v( M; r" v
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
8 C9 C0 A0 t. v; J% M5 J
4 H( ]6 w9 Z' S C( X7 V
再到phpcms\modules\member\index.php 文件account_manage_info函数
) a: ] A a3 G5 W8 A! Z
过了get()函数之后。
' F6 a5 ]8 q( U Q
& z: f$ i5 J+ x8 G# r3 G* S
4 d: G# U: |; a" F
$modelinfo = $member_input->get($_POST['info']);
2 s5 ^$ g) @' V1 m/ z# | @
$this->db->set_model($this->memberinfo['modelid']);
d( n5 W- c z+ Y2 |$ L
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
/ q, d% B2 e! V1 X; y2 Q
if(!empty($membermodelinfo)) {
$ M, Q, W) h, z; r
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
' G% O. A R' \( o. _
} else {
% F+ m) M' N) k6 x
直接带入数据库,update函数我们跟进看看
* _# [9 s/ o( ~7 P3 }
/ \1 W- z. Y& l2 l% q5 X) H! R
3 k E# I: u: o
public function update($data, $table, $where = '') {
6 g, w3 [/ H' m! I1 @8 b
if($table == '' or $where == '') {
# a) \, h4 P/ u1 h: @8 ]: o
return false;
1 T' c. p# l4 q
}
$ N9 Z) ?+ [- R* U( @5 R+ w
$where = ' WHERE '.$where;
6 c7 i- ?( D! t* f; p8 A h- a
$field = '';
* l6 B/ J6 M5 n4 Q
if(is_string($data) && $data != '') {
& d, F# ~5 }* ?0 I- V( E
$field = $data;
5 L2 D( f# @; E8 P* p& V
} elseif (is_array($data) && count($data) > 0) {
6 I4 p- @0 E7 s" n# E
$fields = array();
. d% O: w4 r% E; m; Z, g+ ?# p
foreach($data as $k=>$v) {
# ~# z* I+ Z. P3 x3 C0 j: d
switch (substr($v, 0, 2)) {
. E/ k! P; N& a- w8 f( a
case '+=':
9 f+ L. b: b, h3 k- u
$v = substr($v,2);
# B0 D2 C4 |/ b y- M
if (is_numeric($v)) {
) U- W, E: K& d3 O8 [
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
* v& S( S! _" ]3 i+ _. l8 K
} else {
+ m- B" F& b: z( z
continue;
Q$ d) _0 b. G
}
: d, B# p* ~( E$ {4 T) p% p: `
break;
4 C! S, H! H& d& w, U
case '-=':
% ]0 ~; [ h! W. N$ K9 E
$v = substr($v,2);
/ E9 e, O7 t1 R) j
if (is_numeric($v)) {
0 w* v* W6 L+ b) w
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
) R7 l( z8 a: d& }
} else {
* N$ B) j# k; w3 J; U3 Z
continue;
8 }( q& q2 G& i, O
}
- A% K" u- L; f) _5 Y, D" f
break;
+ Z# |: v" [1 I5 X5 j3 u2 G3 O
default:
2 ?6 u& [; {6 |% A7 l
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
1 b, `/ p2 U2 d! P6 m
}
- b4 _# \! w* }7 C. n) C2 [
}
) j' {4 @8 ^; K& B
$field = implode(',', $fields);
; T* k/ s: ?+ [1 q+ j1 h
} else {
8 w8 h' r/ |6 e+ h
return false;
: Z$ U7 d/ A6 ]/ c4 k: `5 a* G2 t
}
6 R3 v. Z4 g$ {0 P% p, D- @
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
! ?1 i' e! g7 x; b. `/ {2 l4 q7 |
print_r($sql);
& ~5 H% k3 m) M7 \: l
return $this->execute($sql);
c# h& }2 ~: s8 [
}
9 w+ H+ w: F$ i7 ~$ d8 e
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
) V0 S4 X0 a+ V9 X: B8 C. y
* j- Y3 ]" R+ B6 z' o4 A4 N# E
攻击测试:
: h8 q0 R0 q4 n" J0 l/ L3 y
测试地址
http://localhost
9 m3 j; Q* I% h3 d
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
" {4 N+ K8 t1 v, J# R/ d( c; J* Q
2 \8 b3 f. j1 V
[attach]179[/attach]
' S3 k1 h w* G2 r" E+ h+ E
& W* r6 W; m" ]
$ g. d9 }; d# l6 E- v
[attach]180[/attach]
8 P8 G8 I5 _/ d& q( P
欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/)
Powered by Discuz! X3.2