|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告8 S( c0 y) b% P% `6 w/ b
漏洞作者:skysheep
& P' J8 p. M/ ? Y) A分析作者:Seay
2 t6 z/ g. @& N+ b& K, q# N. k$ w: G博客:http://www.cnseay.com/
8 s/ D* J: F" u- }7 a$ o. _漏洞分析:) ]$ q2 V6 F1 O! Y! i6 X/ W0 s1 i
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。% \# S* O7 ~& t* f4 Q
( h' v4 {; {# l* B; |
/ S3 `4 N7 p E( g
$ q4 x- c9 k7 k) q1 Cpublic function account_manage_info() { 8 D- j' q& R t0 b/ r0 S' L5 {
if(isset($_POST['dosubmit'])) {
! E. M$ P9 I3 ]7 Q% |! ^ //更新用户昵称
. B! L b/ \7 O: N5 N $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
+ T9 y- H! W6 t# d; u7 g- M if($nickname) {
/ _6 j1 K" J- l $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
$ c- e6 R! ^3 E+ e+ q( F/ _# q7 R7 _9 m if(!isset($cookietime)) {
8 `" ?+ v& W' j2 F5 M o, o. a $get_cookietime = param::get_cookie('cookietime'); / [% \5 C+ G& g& n
} & Q( @* k% N) M* A5 a3 x2 e" G& c
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); # A* ` r8 _/ K/ ?& T
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
: X6 L/ I, M" q( e param::set_cookie('_nickname', $nickname, $cookietime);
* r, y. G- q# k } 4 T- V8 d3 N( E# H, b [5 J! m& w
require_once CACHE_MODEL_PATH.'member_input.class.php';
4 f2 W4 [; c) L2 Q require_once CACHE_MODEL_PATH.'member_update.class.php'; ; |% i/ c4 }5 A8 ^
$member_input = new member_input($this->memberinfo['modelid']); 4 F# e; `, D0 q/ n
$modelinfo = $member_input->get($_POST['info']);
+ q; l: N$ _( v $this->db->set_model($this->memberinfo['modelid']); , _0 Z( A$ C! g- T5 I0 p
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
+ S5 l- N$ b3 J5 l- c' | if(!empty($membermodelinfo)) { 2 Q! R$ @7 `) _. ^9 p/ d$ K( E+ `# D
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
6 q9 s; l% A/ j; I! q q } else { 7 Q) K, U( E0 g# J
$modelinfo['userid'] = $this->memberinfo['userid'];
+ g% k5 C; Z0 y M $this->db->insert($modelinfo);
, F/ t) @6 X; ^9 V, O8 A: T } ' O4 i- N: y7 A, g# z$ f+ [
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
! ]7 P' J4 T5 D6 ~4 O* h在\caches\caches_model\caches_data\ member_input.class.php 文件中:( a' `4 ^! q" G* O e/ T+ `
* E& o+ l) a. b/ q
/ ?3 j8 {% v+ Q% k! u f8 m+ ~) q+ I4 D, g
function get($data) { - ]' D* r% u& u
$this->data = $data = trim_script($data);
: Y) w, U* Y% e& o7 A $model_cache = getcache('member_model', 'commons');
; g3 R0 {3 Y6 d $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 1 S* h( {$ _4 ]* V8 r) a
$info = array(); q2 M$ D5 e7 N- ]& w
$debar_filed = array('catid','title','style','thumb','status','islink','description'); ! J$ z$ A$ U; p' n9 E, P* C. q$ }
if(is_array($data)) {
. z' w5 k# ~9 h6 O( b O foreach($data as $field=>$value) {
1 G. z# U9 ]2 o2 \, G, @2 U if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
. ^3 y z+ I# e' n! c M $name = $this->fields[$field]['name']; 5 |( K2 P3 r& s3 j
$minlength = $this->fields[$field]['minlength'];
5 w7 |) `, h- {# i6 {9 z $maxlength = $this->fields[$field]['maxlength']; ; p, l9 r, _; V0 z- w3 w! o
$pattern = $this->fields[$field]['pattern'];
& x7 M5 g. q" ^: A, g $errortips = $this->fields[$field]['errortips'];
: J) N+ M6 l$ t" K- K; I7 n if(empty($errortips)) $errortips = "$name 不符合要求!";
^+ _# b; g0 x $length = empty($value) ? 0 : strlen($value);
/ l- @ y0 U' B! t! ` if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 6 A ^; h, Y! v- O
if($maxlength && $length > $maxlength && !$isimport) {
6 X" Q1 l/ G) p8 m" a showmessage("$name 不得超过 $maxlength 个字符!");
1 ]; Q% S3 u# ` } else { 3 Y/ Y' L& G) K: {5 Z6 C4 j9 W
str_cut($value, $maxlength); - Y- Z# z* f& K. ^0 s
} / Q2 c% ^, R# x! x2 Q2 c6 J2 L
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ! }( {% h- W* V, g5 K3 ]- Z
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
/ K( s: B6 J. W# a+ T j2 I" Q $func = $this->fields[$field]['formtype'];
" n3 r0 ]7 O' U* x: _; J if(method_exists($this, $func)) $value = $this->$func($field, $value);
2 ^% x# s7 l& O $info[$field] = $value;
! Z& K D# z" q) C! x: @2 Y }
9 o3 H- T( P* N D1 e }
/ A: f# d+ j0 N/ B" N return $info; * Q% F( R+ L6 P
}
' E) M7 D2 \% n3 O" Wtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,: n; }& a; b/ v: M
/ F z& \) i6 S. Q' S6 s* ?8 |再到phpcms\modules\member\index.php 文件account_manage_info函数; ]4 c. j% m2 j: \
过了get()函数之后。 [" J% E9 p1 e q
/ U' {, U( b. D# n" n O* @- r
) U U- [* V% m6 h+ z$modelinfo = $member_input->get($_POST['info']); ) o# [7 f3 V4 _4 v
$this->db->set_model($this->memberinfo['modelid']); & s" U4 I; Z2 p
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 9 v1 w/ T) `% \5 w% M2 B! c1 s# |
if(!empty($membermodelinfo)) { 6 O8 K0 ?* _0 @& W; j
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ' M9 U) c0 r' k' }- ~" S2 n
} else { - v1 L, X) X$ z8 h
直接带入数据库,update函数我们跟进看看. P9 ^8 r6 y! ?- u8 Y1 L
( o3 ~! Q. |) ?3 k( L
- Q% o- S5 K3 v% z+ I6 D2 Qpublic function update($data, $table, $where = '') { $ g: s4 U, X% ^3 I* E7 B% ~
if($table == '' or $where == '') { & m- H3 M; `, Z
return false; : E7 a# U: K, x. J# M, U$ @/ ?
}
4 s, O/ _9 r, X: u6 K" N $where = ' WHERE '.$where; 5 k7 v9 r6 ~, Q5 R' I/ _1 I9 ~- x6 {
$field = ''; ) ~) p4 l0 q5 ?) O
if(is_string($data) && $data != '') {
: ^6 L: Z9 c+ c $field = $data; 6 I9 C$ v5 n& t
} elseif (is_array($data) && count($data) > 0) { 7 J7 d. n; E) K7 B# b
$fields = array(); , P3 K( N( ^ a' r
foreach($data as $k=>$v) { : ~ s! F! _# z1 u
switch (substr($v, 0, 2)) {
% |7 M" _5 c2 K+ S: G* D, X case '+=': 7 ~/ S# i; j D$ c* h
$v = substr($v,2);
. M. `* b( v! m2 G, A if (is_numeric($v)) { " Q1 `: ~+ }9 r8 p$ @: W7 }- t# j
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); $ ]. T- N# j. }+ o' L
} else { 8 R+ R4 P7 Y2 ?# Q: |+ F( e6 H
continue;
' L5 o2 R0 T4 N: i% A. s }
# S! {7 j B7 l& k7 f break; 5 m9 i! y5 U5 T7 v7 z `6 _
case '-=': 9 [2 C: D' B/ c* y: d
$v = substr($v,2);
) q/ v- t, y8 G& s p0 V3 r if (is_numeric($v)) { / m, _) T/ H' |1 c
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
) O2 V8 d" v% h5 Q0 f: @# P } else {
% s" V. ^4 t+ g2 y2 c continue; 4 n# X. E. G, \5 C' i5 P
}
9 V8 x$ b% B% t( ]; o! Y7 ?( @ break; 0 L7 k5 x4 e. z" l
default: 6 W N: a8 R! t a# v) u v- m
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); " |0 ]& n' |; A- a5 T$ ~
} , x1 N7 ?8 @8 @8 L; ~) c( f7 X
} ) _% k" P7 S6 q* N1 t. z# h
$field = implode(',', $fields);
& a4 b! i/ N5 s" N4 { } else { % ~+ d0 {8 k, S+ K6 {& ?7 w
return false;
2 r5 H1 ^$ U# @6 p4 n& Z: @% l } # Q p" |/ G4 z) b8 u' \
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; ; y- M7 O& g9 h9 I9 L5 A4 I
print_r($sql); " c9 T3 U' l- ]; ~6 ]
return $this->execute($sql); $ D! q5 E7 ]9 T/ {& w
}
* ~+ n( [0 E0 K9 ?" o从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
+ Y L& d6 H2 A; ]4 Z5 ~ E
' m2 a+ q, D7 B7 `: c2 V- |攻击测试:, X$ ~: Z& T& R4 W* P# @9 j- Q9 ]
测试地址http://localhost
e( j2 I" U: ?6 J- U1 x) I 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
6 q" B$ M) G$ {( _5 r: Q! `7 |
8 \ Y; G% N' \& F# e8 ^ / A4 E! E( Z) B& s
" [( h* F5 x9 p+ d3 C, F# Q, z
. |2 G( G# S: O) C. g* M: A" l/ u* S0 {) M" V+ o! `' ?
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|