中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
# X2 O5 c' t$ [! E% W: b
漏洞作者:skysheep
% B* v% x, @5 Q% e/ b% Q, G1 q n3 y
分析作者:Seay
: ?& A- T& U! w3 N+ s
博客:
http://www.cnseay.com/
" G1 _: [, q9 ^: K& i
漏洞分析:
& J+ i0 G. a$ ?5 t* G2 }
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
+ E- m3 F0 @: Y' R, g4 r+ ^
2 \# R1 H( ?* ^2 Y
; S/ v0 v; q. V9 j$ y8 Z4 p
/ c% J( L0 d$ ?* M6 B
public function account_manage_info() {
9 D7 }6 I" t9 B
if(isset($_POST['dosubmit'])) {
l/ T) R' ~% v# {
//更新用户昵称
- G4 b/ o7 m! ^7 a" h0 y9 D7 l
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
/ g$ z! A9 N' K) g% p
if($nickname) {
a$ P& d1 c6 E+ Y4 Y' E4 V9 `
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
2 x9 l- @' F9 E0 V
if(!isset($cookietime)) {
4 \' m A. \' K; t
$get_cookietime = param::get_cookie('cookietime');
# D8 e) _/ C# M. p3 K
}
7 q8 K8 Q* M g3 ^9 B! S
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
/ O4 X, y, l- y2 S% e
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
, B7 `2 k9 w2 _
param::set_cookie('_nickname', $nickname, $cookietime);
' b+ k/ B6 N, r" R, A
}
: _. U$ H( i+ d# ~
require_once CACHE_MODEL_PATH.'member_input.class.php';
4 z# T8 I/ b0 q f8 X) |
require_once CACHE_MODEL_PATH.'member_update.class.php';
/ y6 ~" i2 K# i9 `' l
$member_input = new member_input($this->memberinfo['modelid']);
* X/ n t/ O/ r' R9 t
$modelinfo = $member_input->get($_POST['info']);
/ g* @0 H: r# {+ x
$this->db->set_model($this->memberinfo['modelid']);
9 U# _1 h$ [- V& X
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
9 y( i" o' _7 x4 o! S1 Q4 O5 J4 ~
if(!empty($membermodelinfo)) {
' L1 b7 n- p7 A- C3 P! c
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
1 x; T2 d" b9 O; ^* Z
} else {
7 g4 z# E! N' n3 \ T
$modelinfo['userid'] = $this->memberinfo['userid'];
& t4 N$ `% d; u8 w' B1 Z
$this->db->insert($modelinfo);
2 S" S9 J7 |2 p
}
0 M. T0 z- P) \! ?2 ~
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
U" W/ Y( o+ A1 N+ ?
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
$ D: g% g' n: g- Z/ }6 G* \9 f
3 I: a0 I# J, {- C
' t9 Y9 i \, m! H, f+ F. M& p
6 p: K; U7 l* a
function get($data) {
4 U3 T! T: ^- o p# v
$this->data = $data = trim_script($data);
0 g; b1 p: ~( E' l9 U& g9 J6 ]3 P
$model_cache = getcache('member_model', 'commons');
; H) I( X8 h2 n. e
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
7 o% \2 _2 _7 Z/ U3 R6 u
$info = array();
1 O$ g9 @( j) k
$debar_filed = array('catid','title','style','thumb','status','islink','description');
3 s9 I A# |- e) L6 n
if(is_array($data)) {
3 q* Q: J# X2 j& x" [' O. ]& Z
foreach($data as $field=>$value) {
) ]" C9 G- Q. [$ P) p; n
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
+ I6 v: `6 K7 e2 O. O
$name = $this->fields[$field]['name'];
7 p7 t8 u: p! n% k. i2 P% x
$minlength = $this->fields[$field]['minlength'];
6 d: L2 \! t- d/ ]' W9 k
$maxlength = $this->fields[$field]['maxlength'];
P; w: j1 l# p7 Y. h: w
$pattern = $this->fields[$field]['pattern'];
. g! T( C* _3 C# q( q& x1 Z
$errortips = $this->fields[$field]['errortips'];
0 t" ^( i3 h" V
if(empty($errortips)) $errortips = "$name 不符合要求!";
5 @* E7 w: F8 V D8 B( K
$length = empty($value) ? 0 : strlen($value);
! d p" {7 H% o1 X3 i
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
- o' R0 g$ p! I5 `4 M
if($maxlength && $length > $maxlength && !$isimport) {
! [, ?- |; K# V
showmessage("$name 不得超过 $maxlength 个字符!");
3 e# H# t. `7 B" {) s
} else {
5 S$ _% y! j" v; a Y7 v
str_cut($value, $maxlength);
; i ]; d" u0 Q" d
}
7 P) K% p5 l/ _0 u$ I, y8 C
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
3 x9 j, ^. h. F" i
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
2 _% @$ y7 H7 W4 A9 R0 p
$func = $this->fields[$field]['formtype'];
5 {& V. a. A4 @5 |
if(method_exists($this, $func)) $value = $this->$func($field, $value);
5 b+ y. f% Z! I6 p& w# b8 j N
$info[$field] = $value;
/ r9 e0 U9 \+ U5 O! g+ y
}
4 W6 R. P$ p7 f ~; P
}
+ o: F# O- x c4 ~) j' m& I
return $info;
, Z2 F h: g9 m5 G' A* H- @ f
}
. J. t1 s C8 k$ L7 s
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
) y& @8 ?- ~3 U* V4 E! c8 r- ^5 b
& P: c; |) c9 i! x, @: y
再到phpcms\modules\member\index.php 文件account_manage_info函数
3 Q' w$ B: d n. S
过了get()函数之后。
) ]7 f, z2 z+ L2 u2 d) D$ L
; W0 b, Y |! F
5 c7 r4 @2 l5 F8 r; [ j1 V
$modelinfo = $member_input->get($_POST['info']);
+ S' y) ]2 r- r. J+ B
$this->db->set_model($this->memberinfo['modelid']);
1 p1 ~- i" b% `7 f2 k
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
F% c" k. D: j0 Y. f2 M1 q; q
if(!empty($membermodelinfo)) {
2 q& B+ o& T$ i$ A
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
1 Q7 U8 d$ @! O' Y7 O- x9 c) l' v
} else {
* O. @5 ]. ]' {8 B3 U8 k
直接带入数据库,update函数我们跟进看看
8 O, a% P8 @- ^1 j$ L1 J% ^
8 c9 z; I) F+ H# @
! h- d0 L* _8 w, P9 q' w
public function update($data, $table, $where = '') {
. \( I" b- w8 r* G ~; t
if($table == '' or $where == '') {
^0 T/ g8 J) I m% q6 \3 a
return false;
# A5 k$ M( c2 }- S6 v9 s
}
! }/ [5 L3 [2 Y4 ~% r! X
$where = ' WHERE '.$where;
" s' Z9 r" f8 S
$field = '';
* }6 Q8 w8 F, X
if(is_string($data) && $data != '') {
, ?5 J" c* g% D
$field = $data;
) S0 A6 b0 y2 [& ?9 {9 A: E* `
} elseif (is_array($data) && count($data) > 0) {
" g; V0 g4 a9 a. m1 q
$fields = array();
$ s0 t8 T `7 c+ v5 x9 j6 m1 V/ ?
foreach($data as $k=>$v) {
: G0 L3 W2 C2 q
switch (substr($v, 0, 2)) {
& n) W* y( Y+ x: C9 X
case '+=':
. I g0 q6 i# y+ ?# q2 K5 x
$v = substr($v,2);
. Z& ~6 _- g }& ]* y2 g1 ^
if (is_numeric($v)) {
- A6 T, d4 w! z9 b, x! H# s0 @
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
$ V/ e p6 M6 m8 D: t3 L
} else {
z/ {" U, x+ l, [6 E/ i( z! L
continue;
( S8 M0 F5 y0 z
}
9 v8 d" r2 }* @+ I4 E& Q& h3 }6 N
break;
9 D5 H8 z( M) f# N2 ?! Y
case '-=':
: K& y, N }# u K, I& ^
$v = substr($v,2);
9 l/ m. Q Y7 s+ g) G$ |$ A
if (is_numeric($v)) {
- t; q1 ^" U; s5 w, R
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
9 z( V4 D1 I' z. m- B9 `! r
} else {
0 a' h2 y, o6 W4 u
continue;
}$ v: d* `' i* W* c8 C1 m9 G
}
, e. n" Q4 k5 h6 Y7 ? H' C1 R6 S
break;
* z1 ]. q H9 w: u+ w D4 J' y
default:
8 ?$ i; ~7 }! \) y* q( Q
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
# N3 A: a0 M2 F: u% A0 n4 N1 J# a
}
3 \3 r% h! r& m( y' r
}
0 Q; ?' f* B3 T7 s" w7 B
$field = implode(',', $fields);
9 V, V3 Y* u: s( [7 j
} else {
- D! Q0 ?2 l& Q: a7 m9 v
return false;
# p3 w) R7 r4 s4 u. Y
}
! l# w, |/ c' y, S7 A5 b6 F5 o
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
- Y6 b: y1 ~) `0 [$ J# G8 a# z
print_r($sql);
; s X5 | Q* j2 i7 ]
return $this->execute($sql);
8 W3 ]2 g/ Q; K3 V9 ~
}
9 a. t# n& ~6 |' Y2 X* [$ y
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
3 v' ]. b3 g b
% W' P+ x5 d% O6 e: v# I1 g) |
攻击测试:
( G/ \ d5 b" U& W [7 a. Z) b
测试地址
http://localhost
8 F5 `9 V5 ^6 l8 F6 j2 O
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
5 U. d) J0 h3 D( m3 ^
* w& Z1 y: c. _$ L: \( p
[attach]179[/attach]
: T% N3 B+ s8 ` n' Y
! s9 ^* J& K1 @4 F0 @! b
. i9 W1 k4 c6 @5 t3 O" D
[attach]180[/attach]
# |0 a5 M. i8 j: G. T
欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/)
Powered by Discuz! X3.2