中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
/ W. \2 y" E5 ~+ L' F
漏洞作者:skysheep
$ v; D7 e0 z+ v5 r5 }* G1 L3 x. U
分析作者:Seay
5 |$ l* d$ `' |2 _" Q) l- Y
博客:
http://www.cnseay.com/
& k! ?' I: m2 T2 V0 t( L6 V7 C
漏洞分析:
* p# }' q% S' w
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
; W" N: {/ @. a: L4 l" Z s% r0 z
% l6 j! C/ S0 h+ d/ b
% d0 B8 U9 a1 o" Z1 W/ z
3 M- w6 x5 a2 ]! ]7 g% t
public function account_manage_info() {
5 d% b& {* [9 M! y' X
if(isset($_POST['dosubmit'])) {
* L% Q4 t! n; K" i
//更新用户昵称
7 v4 x; g3 }+ k$ ?! y2 ?
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
0 d' S0 P6 H5 Y$ Y5 _, i7 ~
if($nickname) {
. K: M" B% I! s6 y
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
$ n1 k1 B5 b/ h9 O1 E0 j1 }3 S8 O5 W/ o
if(!isset($cookietime)) {
5 D0 \9 a' q$ z& O' B
$get_cookietime = param::get_cookie('cookietime');
) Q/ A! _# T8 I
}
7 A# `: l& l9 `6 i* v
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
8 T# n+ L$ @+ M- N6 \
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
4 M! Z5 ?/ [7 N0 ]* J9 W( Q+ J" t* D
param::set_cookie('_nickname', $nickname, $cookietime);
1 Q6 w; Z, \1 n0 Q) h! `$ i3 m
}
/ d. n8 S$ m" h+ @( S
require_once CACHE_MODEL_PATH.'member_input.class.php';
; V9 a5 k" ]9 X' }9 m
require_once CACHE_MODEL_PATH.'member_update.class.php';
; @! _+ A; Z q4 Q" `6 J( p
$member_input = new member_input($this->memberinfo['modelid']);
$ c4 P( a! N; V) e* ]- A
$modelinfo = $member_input->get($_POST['info']);
: P5 A2 T! q1 _) P- ~; ~
$this->db->set_model($this->memberinfo['modelid']);
. C% N. L. T7 F; E* W
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
7 a' M3 ^2 x* P4 ^0 I7 u5 h5 ^
if(!empty($membermodelinfo)) {
6 K1 ]+ d0 E7 {$ ]- ?
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
0 Z) I' J g& T
} else {
3 O' w' o8 z5 p3 `9 U5 p" Y+ C [ O8 L* n
$modelinfo['userid'] = $this->memberinfo['userid'];
* W) t6 U7 R1 f
$this->db->insert($modelinfo);
9 G& i( J9 G1 c, w* y/ B0 H& F
}
" n9 v7 J) O5 x2 d- S
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
3 Z, c5 s( o6 z$ t' W3 x
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
: Z Z/ D: b$ A% [1 H0 m1 Y3 t
# E. A& P1 F( [/ H! C4 A2 I: z" R
* Q9 K9 {2 h" S
. ^4 I7 a6 W; \8 t. Q8 q/ B# i; |
function get($data) {
- T/ H+ n( N, W- m& `3 `
$this->data = $data = trim_script($data);
7 V7 j# V/ i! X _* i l5 @
$model_cache = getcache('member_model', 'commons');
9 G9 ^! ^1 h( }* p7 I& r! j% s
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
- C0 \$ ?, A8 v2 G b8 p
$info = array();
r. O3 s6 T' e9 @- J( i
$debar_filed = array('catid','title','style','thumb','status','islink','description');
7 V8 `! o# W0 J/ a
if(is_array($data)) {
/ ^7 q" ~) u8 g; d7 f7 O: q
foreach($data as $field=>$value) {
9 N! ~; i9 U2 s3 t
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
k: L8 g9 p' @' D6 t
$name = $this->fields[$field]['name'];
e9 K9 D% T- p
$minlength = $this->fields[$field]['minlength'];
/ B1 N( e# A; y4 y
$maxlength = $this->fields[$field]['maxlength'];
% W# W' `8 E t! b3 ~
$pattern = $this->fields[$field]['pattern'];
: {4 n; M" }2 A0 H" w
$errortips = $this->fields[$field]['errortips'];
- ~" L a3 Z3 v
if(empty($errortips)) $errortips = "$name 不符合要求!";
8 @1 I; ~7 |: t+ ]) p
$length = empty($value) ? 0 : strlen($value);
# L1 m6 Q4 K/ y3 w2 x- T
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
* v, a4 M e& i* ]6 p1 U8 B0 Z \
if($maxlength && $length > $maxlength && !$isimport) {
3 z: G+ x ^ [9 x
showmessage("$name 不得超过 $maxlength 个字符!");
! V4 P( `, o7 s# O3 _0 ~: `
} else {
1 c! u4 _ o" K! p; C9 n
str_cut($value, $maxlength);
0 K( {( Q3 n4 o9 j- j' h
}
3 ^2 u: ~( M2 S) f# i
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
, |. B: r+ l# X: ]: s' u7 M4 L- `1 @
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
- o5 T1 r! F' T9 g& d$ d1 n# O
$func = $this->fields[$field]['formtype'];
" _( `7 _ d6 }" C% L
if(method_exists($this, $func)) $value = $this->$func($field, $value);
7 t5 p8 T4 l8 y8 p7 i% a
$info[$field] = $value;
% s8 N6 d. }* l
}
7 N! j( p, t1 y
}
4 I6 D) v3 H- a7 {9 Z$ L* G6 w
return $info;
+ V9 `) ]. t0 F; J @
}
! q: p. q) M" D# p
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
) p/ H# z% n3 B) \0 Z" b6 M$ K: W6 u
: G- Q. M1 E# T) N' P
再到phpcms\modules\member\index.php 文件account_manage_info函数
V& n+ U: O+ w2 n+ x' `
过了get()函数之后。
( k0 E; f% s8 O* h3 `! b q
0 F7 s% j/ V+ V+ r$ |
( M2 l( v5 b f6 D3 J R8 S5 O
$modelinfo = $member_input->get($_POST['info']);
2 d$ ]- D2 L, S4 O8 q4 B1 f
$this->db->set_model($this->memberinfo['modelid']);
+ D1 R+ s) W2 K% l _. z
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
" Q* S3 P. `$ e4 [
if(!empty($membermodelinfo)) {
9 ? k; F, ?! d; {, o6 h$ X) ]8 }
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
5 L$ e8 R& z) R( h+ e: P5 [' @
} else {
! v; ~ N" x2 N3 _! u7 R- n+ s
直接带入数据库,update函数我们跟进看看
& J& y( T/ t) P3 s! I4 l3 ?
3 j) a7 U- L3 o2 i
4 R1 v# l: b9 {$ x
public function update($data, $table, $where = '') {
6 q( I- X- ?4 ]% e
if($table == '' or $where == '') {
7 b0 I* w% |( Q) s) u! W0 T
return false;
& p4 S' ]+ @0 w4 e% d* F
}
( V% J3 `% p- ~1 J1 g
$where = ' WHERE '.$where;
* P7 @% K5 c9 Y) K" y
$field = '';
; p1 W% _: I/ h5 A
if(is_string($data) && $data != '') {
9 X4 |1 C7 D9 T' @ n& S) q7 ^5 U# Z
$field = $data;
) `" K; _( ~$ D! i
} elseif (is_array($data) && count($data) > 0) {
G! b/ d) v$ F/ V$ j- F
$fields = array();
e& h9 i# e* x
foreach($data as $k=>$v) {
, Y1 D& h6 c' a
switch (substr($v, 0, 2)) {
* |! i9 U" x+ }( k3 ~- ]. L5 ^
case '+=':
6 N$ |; y) f' o4 {: R r
$v = substr($v,2);
$ c) Q# E, d, H
if (is_numeric($v)) {
: D) O0 x' i( P
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
; }* q2 i0 k$ L) Z4 U( u
} else {
. n; E6 d* {# [) {: p3 ?' l% F5 [
continue;
) y1 t; U* Z9 X& N1 N4 B
}
( J9 C" j' v6 t/ z, L
break;
: O5 Z l: T( a/ _$ ]) I3 H
case '-=':
, N0 n$ \7 O) c! x( u6 A
$v = substr($v,2);
7 o$ }8 i5 ]& {( \) C
if (is_numeric($v)) {
( c3 W' | ^& ~5 Z
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
- J7 r/ n7 U* u6 V- Z; x) t# Q
} else {
3 a7 q" F0 x X' y f! o+ G7 k
continue;
- T0 d: n. J! `) p5 O l$ w) ]
}
0 X8 V. i5 S0 Y0 \/ a" r
break;
8 U0 g& U/ C! w# A9 b" @' ?
default:
* Q: W6 B8 v6 L' d& \
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
! ]1 v. `1 P$ T/ k: c
}
4 _# j- T- ~% K) i; \& Z+ g
}
# P( U2 B4 K" L' S1 A
$field = implode(',', $fields);
9 ?5 |/ s6 Q0 R' T
} else {
' c* q4 U' `& |5 Z6 y$ G2 o
return false;
) n' `9 T2 D. `
}
2 g" V2 Z) U) e' M8 b9 o8 b. [
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
/ j2 g' Q% K6 H% F6 B" {$ V1 Q
print_r($sql);
( U" c( M4 s0 _! u- _, K* Q. B
return $this->execute($sql);
- t3 p4 X% v, h9 U9 ]
}
+ D- k* }% b! l% E2 k% S
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
; H) ?# `1 W* P$ B+ S `8 H9 f
; Z9 y4 ]+ x" b$ a! S2 J6 k7 F. d6 N. s
攻击测试:
% ~4 x. D5 p" O/ R2 O
测试地址
http://localhost
+ B1 o6 y7 a5 N; K* B \( n- n
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
" G# @9 _# p! N- v
' k" L, U' @# t' R O5 r! F1 U( D: p
[attach]179[/attach]
1 H$ p0 j9 {6 y" r0 M9 g7 x
, b& {: Q$ t3 A1 `1 q4 }: i
) E' z' v# I0 { j1 M! M. r
[attach]180[/attach]
9 G% H6 I* S& v
欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/)
Powered by Discuz! X3.2