中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
) L" G, d6 }1 X* D- G u& [8 I& n5 c
漏洞作者:skysheep
7 s, ^; t1 f0 \3 b: j P
分析作者:Seay
5 r6 W9 t0 O/ ]5 r( ]; [
博客:
http://www.cnseay.com/
1 |, a0 ?2 Y* b: W/ H$ }. b
漏洞分析:
9 X# [; W. R: V$ J4 S# k( l
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
8 D" _& ~7 E' c% o/ T' Y5 f
8 d1 ]: W/ R5 U
, G/ Q$ @, }; K
1 b# [) z |0 `) t
public function account_manage_info() {
. O* V0 S7 d( z/ k
if(isset($_POST['dosubmit'])) {
, n9 b+ n& P" s, t$ B: J8 T+ |
//更新用户昵称
~- t) y9 }6 n0 K: E
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
$ [0 ]9 s. M! x0 _7 ^
if($nickname) {
) F$ h6 t& j' D3 t2 o, ~
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
% ?/ U e0 w; G2 W& `% b) u' E5 i
if(!isset($cookietime)) {
- M( Y( c) y, ]
$get_cookietime = param::get_cookie('cookietime');
S( }) e% `3 |2 z% G; H
}
0 m! b$ R" W8 ?+ B0 I; O! p- C
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
" w3 B' F! h L B' Q1 B
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
: F4 N b# v+ I/ Q1 |
param::set_cookie('_nickname', $nickname, $cookietime);
9 M& n5 }# \9 n( h3 Z
}
3 |$ g2 ?0 k6 f" R( H9 _, f D* c x
require_once CACHE_MODEL_PATH.'member_input.class.php';
% f* j, L0 W! G( i6 z& L3 F9 k
require_once CACHE_MODEL_PATH.'member_update.class.php';
, Y5 j' l& p0 [3 D8 A8 b# D3 ~
$member_input = new member_input($this->memberinfo['modelid']);
0 t; `6 I, D1 V0 g: U- V+ h: h+ X
$modelinfo = $member_input->get($_POST['info']);
# j( i$ U( i( l$ ~3 B' W
$this->db->set_model($this->memberinfo['modelid']);
8 H) p: h# P0 A
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
3 u5 r: Y a1 L. I4 B2 Q
if(!empty($membermodelinfo)) {
4 }9 B' W4 D2 X
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
" z+ x; G# s- ~
} else {
, [2 K7 `9 _5 F' ?
$modelinfo['userid'] = $this->memberinfo['userid'];
D6 \+ Y* k* N+ m% \7 v
$this->db->insert($modelinfo);
5 B- |0 r$ a5 H2 @4 u- n
}
, V( I4 n" P2 c
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
; ~ n4 S( [' F! T2 H, Y
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
" e9 t$ D5 y" x5 u' a6 m( k
! k+ m' Z0 Z: L9 \) G* z0 _
& W1 L* x6 _% t
& b, P" X h& z* z2 R
function get($data) {
/ |; q p" z$ m4 ~3 U: l" p2 R! S
$this->data = $data = trim_script($data);
" C5 b4 X3 F6 ]6 x" D
$model_cache = getcache('member_model', 'commons');
+ O6 _1 y3 y f. G. h2 @4 O
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
- M' ^, l# p% v- M
$info = array();
. U/ m( j: B4 h' z, ^
$debar_filed = array('catid','title','style','thumb','status','islink','description');
" |% r; p) n% l, s* `- Z
if(is_array($data)) {
4 {& R' c% B q
foreach($data as $field=>$value) {
+ h7 T0 u8 \& m) x2 x
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
: P4 \! i% f' x7 j7 t
$name = $this->fields[$field]['name'];
/ O5 L' \$ P5 y& d& p
$minlength = $this->fields[$field]['minlength'];
1 K# i: e# O8 z, _
$maxlength = $this->fields[$field]['maxlength'];
}; m, o2 X3 |' a l# }
$pattern = $this->fields[$field]['pattern'];
" I5 m% q0 r1 F$ B
$errortips = $this->fields[$field]['errortips'];
% p% l( E! P$ l/ P( Z+ t1 B
if(empty($errortips)) $errortips = "$name 不符合要求!";
) ^" t! n' C' L: X/ R$ @% T/ B
$length = empty($value) ? 0 : strlen($value);
+ Y: c4 l6 o$ {
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
! z" f; Q$ P) ^8 a5 r
if($maxlength && $length > $maxlength && !$isimport) {
( b! `# j: J' _0 ?: c4 `
showmessage("$name 不得超过 $maxlength 个字符!");
0 a4 E% H6 b/ x$ T) X
} else {
+ \- g+ O. M8 I. q' D
str_cut($value, $maxlength);
% B$ A e8 N/ r9 L
}
3 T; Z, D. U+ t# Q4 P" |6 R
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
/ J; L; E" `/ d% H3 `4 `# s' Z% L: j
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
4 [% g& h% o3 d9 P0 E6 H! M* \7 C
$func = $this->fields[$field]['formtype'];
7 }& {/ S1 ~* U3 X' v/ _
if(method_exists($this, $func)) $value = $this->$func($field, $value);
6 ~* u) ]. @) C9 e. Q
$info[$field] = $value;
9 e0 q( J4 j1 x
}
$ e$ w. U/ I9 y/ d
}
% i7 p2 u; s! G& C
return $info;
$ u% o$ {+ V0 L$ s, i* v: ], a9 q
}
: p& |9 j' }' f- [
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
- U: V9 e% |$ @$ A- L4 k5 v
" k) J7 h) a6 Y! i
再到phpcms\modules\member\index.php 文件account_manage_info函数
$ {# I% \8 a$ w( a T( y& K* A
过了get()函数之后。
' B% _) T' l& k: |/ y
9 j( Q) P7 }2 \8 }0 |
4 j0 v' h' E% |3 ~8 n
$modelinfo = $member_input->get($_POST['info']);
& D5 a# F8 W/ ?6 I) @! N
$this->db->set_model($this->memberinfo['modelid']);
1 U( N' Z9 {9 y& y6 U
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
( T) ?" k- v+ Q1 f! f1 F7 Y
if(!empty($membermodelinfo)) {
3 _4 @& S( i2 B% W
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
4 A$ Y4 q" j7 c0 M
} else {
; j d' u: ^5 j. s5 U+ `1 s# o
直接带入数据库,update函数我们跟进看看
! O4 i" b2 ]; M( V8 G
/ g. s( A3 E/ O8 N
3 }; J; F$ e$ \5 q
public function update($data, $table, $where = '') {
! m9 @% W# M) Y. Q2 C2 ^) j
if($table == '' or $where == '') {
3 m* v! c4 u0 ^9 Y
return false;
" D, T, t% L* J7 f+ \
}
' Y1 `4 L) O" U; I+ T
$where = ' WHERE '.$where;
5 n# p" f' J* k3 K
$field = '';
% a* O+ f+ l" S0 s
if(is_string($data) && $data != '') {
# e( |& m5 x) e2 N- N
$field = $data;
4 a, \/ v3 Q( g) @0 }2 n
} elseif (is_array($data) && count($data) > 0) {
; @; V* l5 S* o# O, r' W; c
$fields = array();
1 {7 c6 t; s$ ]0 j j
foreach($data as $k=>$v) {
' ?$ {7 i' A) r4 E3 V5 X8 M; Z* D
switch (substr($v, 0, 2)) {
( A) Q {8 O' U1 v$ r
case '+=':
, e ^- l" H o( j
$v = substr($v,2);
5 c4 ~- ?/ A+ y1 {2 q% `: {) K
if (is_numeric($v)) {
. F7 ?9 u! D* [
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
& [) e9 N6 S( E: T7 v2 N8 L5 w# ^' ^- N
} else {
$ X# j/ s7 I7 F& t$ B& O# q& n
continue;
: V) O9 Z8 s; f: T
}
. X/ M! P2 d: l1 ?
break;
' f- B- h6 B1 V
case '-=':
& r' a0 k8 y6 n( ~7 R* F' {
$v = substr($v,2);
& @+ n5 Y; a0 N" E3 V
if (is_numeric($v)) {
1 N% \' C6 S3 d
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
% u7 x& k- ~! H4 w' ?8 o
} else {
7 |3 L. I- {3 I6 u9 {8 B
continue;
, q3 L0 _' l( ~+ _- x0 X
}
8 \$ R5 I- b8 I0 `0 J7 s# ]( [
break;
) _5 E4 K) T3 R, m& ^
default:
. ?6 n* m$ _& t' r4 t
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
& w# i1 t+ F% i2 f' S/ M
}
* S# ^6 J/ r3 @9 {
}
' N* f1 _) P. n0 V6 l% h! `
$field = implode(',', $fields);
; K2 m Z4 B/ T+ |4 q
} else {
" _, B# H6 o) |- q- ?' v
return false;
8 Z7 i3 i) f2 q/ v) M# P6 R
}
7 K3 n, c2 ^: r1 a; J" s
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
+ S) ]1 W5 [' |7 ]
print_r($sql);
* R$ z9 b: M# v% `" O _- \2 n
return $this->execute($sql);
% f, i5 _, K, j. a' S
}
; N9 b, I b2 ~ K( f) h+ n
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
8 S9 R$ O% c' d- C4 I- G
# L# t* Q3 j4 W# }4 N
攻击测试:
7 |' ?6 a) r, q% @: V
测试地址
http://localhost
2 Q2 S% }4 ^6 _. K: V" e. a
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
8 A. X7 A* p* S& ?
$ e. r9 ` ^8 b" N) J" w. z
[attach]179[/attach]
- W+ F/ n' a, _4 t
# {2 C( @" V/ L o& d
2 f! A: q* O4 G3 K. E2 n
[attach]180[/attach]
. b- m" ]* R- S0 M# k) Q6 i& K
欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/)
Powered by Discuz! X3.2