|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
# I1 n0 B& t0 Z: y3 f* i漏洞作者:skysheep+ t4 {1 {3 I3 B' P6 ? n5 i. N- {1 W3 }
分析作者:Seay
0 A% d* Y$ D0 D2 J" v博客:http://www.cnseay.com/9 H4 o8 b b: }- r9 n5 Y2 H4 O, P
漏洞分析:
5 r; k- w. P7 F 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。! @2 E1 E. g- B& A8 {
1 W/ q' d' ]( o
7 X: Y3 b) u$ g* k; P' `. [8 t
5 h* J* i l( C$ Z' M& D2 M5 N
public function account_manage_info() { ! M+ v. L# ?' E, K5 p% ^0 y: h
if(isset($_POST['dosubmit'])) { 2 Y5 ^- X* s; d u' k
//更新用户昵称
5 J2 I0 m i; Y8 u( D $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 8 E3 ]7 Y" F& K' |; o. E% ^6 H3 q# b
if($nickname) { 9 S# }7 G: ?7 B* h
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); # K( R( ~4 G7 r+ X4 r# Z
if(!isset($cookietime)) { $ k/ c) `- E) E' b" s7 j
$get_cookietime = param::get_cookie('cookietime');
% j0 ^# o6 s1 t }
9 s7 ]! ~9 w' i% { $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
; b) J) C' m P- p( j; _ $cookietime = $_cookietime ? TIME + $_cookietime : 0;
7 S& m& o/ |6 z4 H; ? param::set_cookie('_nickname', $nickname, $cookietime); ! E& r+ L! i% A
} 7 h V k; T( E
require_once CACHE_MODEL_PATH.'member_input.class.php'; ) u' w+ I: A3 ~7 N
require_once CACHE_MODEL_PATH.'member_update.class.php'; 2 Q/ n, m5 i( J
$member_input = new member_input($this->memberinfo['modelid']); ( ?# ^' ]. f }4 G3 Q
$modelinfo = $member_input->get($_POST['info']); & a5 [/ x1 E- P' O! ?
$this->db->set_model($this->memberinfo['modelid']);
: F! T- q2 y7 e- ^% | $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
9 G3 w% s0 F+ q' F if(!empty($membermodelinfo)) { ' a. m; K& A- v @6 j5 y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
* e, [9 q+ L7 i4 Z# B } else { x6 p1 u; S! O% p! b
$modelinfo['userid'] = $this->memberinfo['userid'];
" ~7 f, x w. E" x8 X8 j$ ?' N $this->db->insert($modelinfo);
3 A3 B }: u! b }
$ |- S! H/ c3 O! `! ^# R1 a代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,; O. J& \- l8 x- |, Z! H3 T
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
3 {& O* I( j: n0 ~4 B
7 F2 b! C+ H) z0 p" {
/ F' q6 I+ I+ G$ d, u& ]" p+ U
; E% l) c" z V6 M. ~6 \function get($data) { ' @% c( r0 U' N" A# u
$this->data = $data = trim_script($data);
- t+ X# a( G+ d- R: f; I0 j $model_cache = getcache('member_model', 'commons'); & W- c: \3 r% `. p1 `! g, J
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
. f" E# ]" \; C# ]$ k4 U$ T $info = array(); : j* A7 n* M" w1 o5 W$ g' p# S
$debar_filed = array('catid','title','style','thumb','status','islink','description');
/ ~: z* i" k7 p9 @% ^5 L if(is_array($data)) {
4 O% x/ m/ x u+ p8 s8 ?( @ foreach($data as $field=>$value) {
* V3 U! [7 C- U/ P: h if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
& L7 g K8 X3 b7 [% v1 K2 C $name = $this->fields[$field]['name'];
9 S4 @- P2 C1 l $minlength = $this->fields[$field]['minlength'];
4 f/ j$ F: Y' S. ^ $maxlength = $this->fields[$field]['maxlength']; 0 I3 l% U; n7 H
$pattern = $this->fields[$field]['pattern'];
% y. {4 t/ N' a$ h6 C0 X $errortips = $this->fields[$field]['errortips'];
' Y1 y0 G1 K7 {! |" q7 h if(empty($errortips)) $errortips = "$name 不符合要求!";
' d% z7 \+ X* ^4 t, w" T) { $length = empty($value) ? 0 : strlen($value);
# U$ z# I3 O0 N0 S) t! T- Q9 A if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
! V, F$ E( y" P/ E | if($maxlength && $length > $maxlength && !$isimport) {
2 t- _- X, S, v9 `7 s5 p( W showmessage("$name 不得超过 $maxlength 个字符!");
/ U1 m/ F' w5 n } else {
) V5 ^$ w6 r6 A str_cut($value, $maxlength); / z3 @- n3 t+ V+ b
} 8 M# s: P& x. D7 N7 W* z& Y; Z9 i( r# b9 L
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 1 s; x4 p5 p+ z4 P
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
E- g9 h: z" { $func = $this->fields[$field]['formtype'];
& Z$ {* D' c w if(method_exists($this, $func)) $value = $this->$func($field, $value);
& E% ^& F3 h2 N2 x0 X5 l $info[$field] = $value; 3 Y% h% `; I2 m% Z% C# F
} . X6 U2 [& h2 C* T9 m( P
}
8 V$ ?& c5 h- F5 ^$ X$ ~ return $info; , H; i3 S7 F# }) V
} / j$ j0 Q! _* X
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
6 K7 N Z+ B( K. S8 m8 q) k; y9 H& Y1 ?' s+ Q' i3 w4 [
再到phpcms\modules\member\index.php 文件account_manage_info函数
$ ?: K9 V* i$ N) Q4 |& m+ N" k) N# B过了get()函数之后。
~7 ^) k& }1 `2 N3 H/ h: p) d
/ ^5 j' K- y% t: A7 R# r + b2 g6 M" Q) M6 }
$modelinfo = $member_input->get($_POST['info']); ( I& ~0 c$ t: t% F" d
$this->db->set_model($this->memberinfo['modelid']);
; \0 z N Z3 d% s0 |. G; s; u5 C+ ` $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
3 B% S7 | f9 Y5 m8 q. R `! I if(!empty($membermodelinfo)) {
3 T: ^# U3 d# |# K& A0 K& N2 ` $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
' x+ ?% R) X$ O& y } else {
( B+ e6 B) G' _ j6 P* x直接带入数据库,update函数我们跟进看看
9 O7 N9 B+ K1 x o$ }# L r0 n" b2 A9 f4 k- h2 H+ l) J1 t/ a
4 Q9 I0 Q3 |; p0 N4 vpublic function update($data, $table, $where = '') {
2 C' b$ I1 `/ ~; \ if($table == '' or $where == '') {
8 `8 j" N% ]1 m8 y. Z return false; 0 s6 F4 c# x% b& N# l
} # r6 H( `) N" Q! J. B
$where = ' WHERE '.$where;
: g1 q5 t5 T5 `0 C% s3 Z $field = '';
" K3 y$ D4 n3 C" }' q3 Y if(is_string($data) && $data != '') { 9 x% x7 g7 C# F; T( e! ^
$field = $data; 3 }: z7 q( t, y9 f! V
} elseif (is_array($data) && count($data) > 0) {
2 ?, y5 g. _: j* y. W; ]1 G $fields = array(); 2 R" f8 S. t( N* Z: M+ S6 A! J! e2 o
foreach($data as $k=>$v) { 6 R) x' Q5 }) G
switch (substr($v, 0, 2)) {
- J; U6 S$ \7 F% q, E3 F! c8 \ case '+=': 8 Y, c( ~! w3 H" |3 H
$v = substr($v,2); * P7 Q7 _) U/ L. u7 W
if (is_numeric($v)) {
, b" Y+ C; j2 w, _0 E6 p $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
$ g6 _8 k: L' O% M1 t1 E6 K! C } else { + f! l$ `' \4 C
continue;
. I1 j u+ q1 i5 S } , S( I' D# F' P: Q
break;
5 V; y# h! |) `0 v3 }7 [ case '-=': ) s7 n4 H- ~& D a& P- b7 w$ B. K" V
$v = substr($v,2);
( a+ E6 ~! W' e if (is_numeric($v)) { ' t. T$ }9 x5 m7 M' u* ~- y9 i- q
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); * w4 T; g8 i* C, i- M
} else {
+ R; l$ T& l3 \, d7 } continue; % b* |; a) C( a1 W9 H
} 9 x) _& k, V8 w! x8 x" L
break; # F, u& S" ~5 B+ Y/ z- H
default: ; R o, x. L9 q8 N
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
* R4 O, B8 H! \' l }
6 a5 r! n" k& Z4 q! {! F) } } 4 c/ i5 m$ Y4 Z, ], |
$field = implode(',', $fields); 0 D# m1 J( W/ u( b7 Q
} else { $ j& [8 d" ?" R% b* y+ Q
return false;
1 W. D V4 U; i- j, J) t }
: G. w8 |2 r: W $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; ' h: c1 ?7 \- y, g4 m
print_r($sql);
6 Q1 C( o4 J% S: P. p1 C return $this->execute($sql); ( z8 o6 F! J) P) N
} 7 ~) {# q$ m; t; }
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
) A) I2 g, z, A; A: K& L8 e9 A' `9 N% N* @5 n0 _1 \8 e* A$ x
攻击测试:
& @! O# f( G) m; |: v/ `测试地址http://localhost
1 l( O1 S3 e5 u, G- e1 `* l# B+ a 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
) k3 c- s/ C2 f- e, l D; ^1 A4 v! ~
I4 Y( ?! w) f# ?
9 y) T; L7 e6 e" A
" R% c! p0 Z2 e' ?+ F
$ c+ T P0 Y( p0 @+ z5 V |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|