|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告$ J' ^6 {; d7 g+ p6 I0 c- a
漏洞作者:skysheep
2 {! D$ u3 @/ b% v7 a分析作者:Seay6 Y3 L9 q Z3 E- _) m9 a; V: M
博客:http://www.cnseay.com/2 v& C+ J; z9 L
漏洞分析:
) b o5 E3 R F5 E1 ] 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。7 z0 m4 O7 z. @3 p0 o) C; u
. ~$ Z4 _$ o* s7 d, Q# i
' g3 S" L' t" K; J 4 J, H5 s0 w& m! K
public function account_manage_info() { ) Q, O; R7 h! d; K
if(isset($_POST['dosubmit'])) { 2 q5 k3 k: _) x1 X/ W+ H
//更新用户昵称 0 {0 p5 c0 u$ ~/ x
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
* z1 p& W( X. K1 U3 J9 M8 a6 ` if($nickname) { " N% i8 Z1 [! D. O5 ^. ^
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 6 p+ H# |1 L) u$ a E
if(!isset($cookietime)) {
' B, H; A7 n7 e $get_cookietime = param::get_cookie('cookietime');
* \1 o+ W3 T8 _ } # ?# G( h& t" k4 l& i3 M2 y
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); / R% R% A. j9 [* G; A4 K3 P
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
* h4 @% y- p* c/ ^0 Q1 Z param::set_cookie('_nickname', $nickname, $cookietime); : c" d9 X. S( l1 z: {
}
) O' O, a8 T5 a2 Q: @ require_once CACHE_MODEL_PATH.'member_input.class.php'; # }7 I8 k! k f% b+ B
require_once CACHE_MODEL_PATH.'member_update.class.php';
% S8 g" U. L0 t B $member_input = new member_input($this->memberinfo['modelid']);
/ |/ W% [7 m: W, q8 M9 L R& s# Q $modelinfo = $member_input->get($_POST['info']); : g2 j( i7 |+ W8 i, e
$this->db->set_model($this->memberinfo['modelid']); - [' Z. W0 a, P, J4 B" D0 {
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 9 v2 I' H! Y. }3 ~: p
if(!empty($membermodelinfo)) { ( Z% W4 r3 J5 V4 O3 j1 }
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
) H9 K3 ?& C: V' D) }( ? } else { $ [, E. D9 ?; C
$modelinfo['userid'] = $this->memberinfo['userid'];
$ o5 H& h+ t! H+ F $this->db->insert($modelinfo); $ s$ {& y V% |! G6 I1 i, f- O
} 0 U1 m7 a. P; E7 k8 s
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
7 {) Z% d4 L, q3 G在\caches\caches_model\caches_data\ member_input.class.php 文件中:5 {) f2 k% g$ i R T# n0 Q
L' i% C/ }, v1 w" a" l' h9 V( n
" H0 F N8 K* D `" {; k . w$ A4 h0 [% B, Z0 c3 N0 I
function get($data) {
4 c7 W8 ^. H/ o, K0 @ $this->data = $data = trim_script($data); ' V4 R. S; Z: X( x: M7 P. e
$model_cache = getcache('member_model', 'commons');
D: x1 l9 O) ?$ } $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 1 ?$ E: b% C B- ^7 ^7 B2 N
$info = array(); * e p% J) T( [5 x, j: J
$debar_filed = array('catid','title','style','thumb','status','islink','description');
3 } u9 ?& j* t+ p6 b% I3 b$ D! i if(is_array($data)) { / s* j, t: w5 h. T. T6 B- ^( H
foreach($data as $field=>$value) {
: }/ B) Y/ @4 k8 t: ?! V if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
* k" h+ H0 J$ o $name = $this->fields[$field]['name']; : q, \/ d1 G: ?( i
$minlength = $this->fields[$field]['minlength'];
2 k4 M/ u/ M$ A9 l4 d $maxlength = $this->fields[$field]['maxlength'];
; H6 q6 N6 ^/ T# I' h, w" m# D $pattern = $this->fields[$field]['pattern']; 6 d+ v7 X# G- k# Q9 Y. P i2 L& l
$errortips = $this->fields[$field]['errortips'];
$ G% B* O/ {1 c$ M6 V% p; u3 M$ @ if(empty($errortips)) $errortips = "$name 不符合要求!"; 5 U* t! o* H- n) S3 |& N. ~
$length = empty($value) ? 0 : strlen($value);
3 ~# g2 |0 o) G" ?5 C4 Q7 A if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ; D& D; B. I4 W0 L
if($maxlength && $length > $maxlength && !$isimport) {
& L3 W% | i* _ showmessage("$name 不得超过 $maxlength 个字符!");
" c- T" u. w9 a0 ?% x } else { , ^2 c2 s6 A( h+ X. l$ G
str_cut($value, $maxlength); 0 c& j( U5 K8 u. t4 Q8 }" z7 c( n
} 4 t. P( y8 \8 } P: Y
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
5 A& L! m# u4 l1 T2 T if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); " V3 |0 y& d# `7 ?/ T
$func = $this->fields[$field]['formtype'];
# m! L, d. n3 l3 ]- m- K( n if(method_exists($this, $func)) $value = $this->$func($field, $value); ! }) V; l% Z8 h/ v' I, @
$info[$field] = $value; # Y) ~; Y4 n9 z
}
* z, D+ i) p; h; l4 U: F }
! P9 J+ v& y/ p: e s return $info; & l9 z3 U% U: S. w! K
} % F2 ?4 l# d& U
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,/ Z! |" n' A% e& ?
7 k. a' }2 W1 R+ i, m g* h再到phpcms\modules\member\index.php 文件account_manage_info函数+ W8 M4 d6 M1 ~. y7 f) {6 w( C
过了get()函数之后。
# j8 Q2 q" e3 k& ^) I% L8 h4 x5 i6 S9 P, x! a, C. [
& q, Y9 U1 ?4 K
$modelinfo = $member_input->get($_POST['info']);
* R {4 U. Q7 }/ [+ X0 l $this->db->set_model($this->memberinfo['modelid']); ' ?- R2 A/ c& I4 w& n( A
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ( g; z' ~- U+ S; d) @ L& {) S, G
if(!empty($membermodelinfo)) { ) s7 Z% {1 b9 P# q9 l* S
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
/ b3 t( t8 Q* J# A: j( Q } else {
2 Y% U* F W5 P' r3 |直接带入数据库,update函数我们跟进看看
, Y' E& f: i+ U; g. u- U: E* t& S. G/ E# I5 y& C
1 y9 l. P: d" s7 k9 ^
public function update($data, $table, $where = '') { - j' N' p$ y% i b7 v: T5 u% L8 M
if($table == '' or $where == '') { ]& V8 E" M8 d
return false;
6 ]) J* r; Z3 \: H8 h9 a }
: T9 Q5 J+ [" | w5 a $where = ' WHERE '.$where; , g: ^2 C6 d, S1 R( k
$field = ''; 1 M9 h7 M+ T$ L( ?6 i: Z
if(is_string($data) && $data != '') {
7 E- l, T3 ]4 `7 R' c $field = $data; 8 z0 G/ T: L1 M+ R/ b# K4 J2 l
} elseif (is_array($data) && count($data) > 0) {
6 m* U* x. C' U# G $fields = array(); & t% a/ t% G8 u$ D# j: j" u3 I% }
foreach($data as $k=>$v) { " |; @( `9 Z- K4 o! o& H# M+ _
switch (substr($v, 0, 2)) { $ {. g0 q! Q7 o9 I4 i/ o% {( f
case '+=': 6 x! @! m) T, d7 K( ^) X
$v = substr($v,2);
$ q$ x) z% R2 j4 M" V, ? if (is_numeric($v)) { " l! L# ^0 @( U' s+ k9 ~+ m" G8 M' s
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
. }# \% V# t0 i$ X( C) H9 o* n h } else { + h" l; l* a5 S3 D
continue;
+ n$ @9 E" x9 z w: G } " Z3 q; r& {5 L
break; b& B% o) @( {# X. E$ d
case '-=': 7 Z1 h* a9 h' [, Z, E- [- E
$v = substr($v,2); ! l* V+ j1 d% Y5 }6 N
if (is_numeric($v)) {
# n# Z7 S8 z$ ^( c% [' n# B $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
" o' [% @- Z1 Q } else {
6 j0 A1 m3 d6 Q% z continue;
: N/ h& k, |6 A5 J" s }
6 ?- w2 N7 r9 p+ m# B" a" a break; 5 K& i9 b6 f! B
default:
- Z5 K" I7 U5 T% C; v $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
0 V2 S4 E5 @$ N9 {* D! ? }
; D# K, Q' k2 k } ' a2 |8 o* w8 ~
$field = implode(',', $fields); 7 E5 @) @/ F A) r7 L+ n5 S
} else { 2 f+ m, U4 ^/ a# I# M( Z
return false; 4 E2 i- U+ B6 w' @, \
}
9 m* H, O; P* |4 z; a& y% q $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; - h# o, _# M/ U0 e+ a6 u
print_r($sql); 3 x: i& x0 q0 [3 f$ I% ^& r3 U
return $this->execute($sql); 9 v3 C; i2 f3 n p9 [8 f( g' ?
} 4 T3 F, d# v7 P! L
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。$ h& S5 \3 q4 R+ a9 \" Q2 i
7 P9 {( g5 c/ Z# y: U1 {
攻击测试:, i* f" ^- |: G# a- ]2 v6 v
测试地址http://localhost9 X- Q* i+ f( {9 p: V# H
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句! g3 ^4 J+ v& Q( |) x
8 u$ ^$ i3 y- j- g# e, p$ ?
6 j% U, H2 b7 I- ~& J* [
) j6 |5 \( H. r; k2 M8 d; V
: ]2 D$ z8 \$ y( z" u
4 ~ @( i) h! T |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|