|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告! b. B1 r2 N7 f6 I9 t$ [
漏洞作者:skysheep; p5 y* T1 c: m: ~; f. r
分析作者:Seay8 a }4 `; N; g2 \+ S- X
博客:http://www.cnseay.com/
! e" X- P% }; _# x漏洞分析:0 B6 w) N/ j! \. W* {! ?( V
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
; J& E% H+ N7 f) D w
. K$ y! J0 R! s' \
/ ]! r% G6 s$ h( _5 ` ! d5 P( G' l4 V6 t, {! t) i
public function account_manage_info() { 3 M3 ]( @' J- f3 m0 I" N
if(isset($_POST['dosubmit'])) { K3 D% }' V6 E8 Q6 T" N. i
//更新用户昵称 , ?( _6 r9 `' f
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
9 m1 V' K) T' P+ p9 t if($nickname) { & l; T. [4 f$ h' }. i5 m- x0 D
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
1 i" W! {& K% B if(!isset($cookietime)) {
6 o2 n; E4 w' H4 Z, \/ j) [) ~% k7 G $get_cookietime = param::get_cookie('cookietime'); - S. L* H8 i2 v5 r
} + \' z3 `7 s% T# [
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); - @% J* Y- k6 `. O4 `4 D
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 2 L& w5 R( l1 b/ v$ }' U, P
param::set_cookie('_nickname', $nickname, $cookietime);
7 f$ y: L |& f7 j; F) [$ @/ { } * V0 V" z, Z% z+ ?6 R. e
require_once CACHE_MODEL_PATH.'member_input.class.php';
: v( r' ~; @! p require_once CACHE_MODEL_PATH.'member_update.class.php';
1 b% J) ~4 @8 T, ~1 J $member_input = new member_input($this->memberinfo['modelid']); ! x) m) x' v5 e3 F% H1 D$ R' y7 u
$modelinfo = $member_input->get($_POST['info']); $ s# v( J% Z- ~/ P; j) O* R+ Y# d
$this->db->set_model($this->memberinfo['modelid']);
4 ^: {0 ^! ~ \' j) B8 ?7 n $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
3 `9 m r$ Q8 h$ ?; W M* Z if(!empty($membermodelinfo)) { ! r8 ^" ] a# _2 W
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
# i, E T& r; x1 A. c& I } else { , s* g" y( e" S3 e
$modelinfo['userid'] = $this->memberinfo['userid']; , l; D% T9 K* Z" }3 C' z N" V
$this->db->insert($modelinfo); 1 V+ n/ c$ y/ G( N$ z$ K" r- t
}
7 k! u% Z! q6 i i. ]代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
$ j" m5 R' u- W% Z6 o5 j, K3 j$ g+ L在\caches\caches_model\caches_data\ member_input.class.php 文件中:
* u, l; C) G/ Q, m
- G4 E: O) _0 @! V+ B# k& C# K( g' L2 N8 D* c3 i% p
8 z& F- i. C. q. Q! Z( P- mfunction get($data) { u5 n( d7 l- e# ?! Z: [
$this->data = $data = trim_script($data); # [/ p& s7 c7 s; S) E/ `
$model_cache = getcache('member_model', 'commons');
. T u E3 S) |7 n* D% u8 | $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; ( G$ ~: c1 j! J& a% J2 C5 s; l# x
$info = array(); 7 ^( Y& K6 T+ p3 y( v
$debar_filed = array('catid','title','style','thumb','status','islink','description'); ; l0 u l" S1 j3 ^
if(is_array($data)) {
. {2 \. Y5 l$ e G foreach($data as $field=>$value) { " \0 R" k# m- V2 U' h; ~
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; U" h5 { j6 q# u+ I5 Q; L) M% p3 `
$name = $this->fields[$field]['name'];
8 p, Z! b+ {' ]" X9 h $minlength = $this->fields[$field]['minlength'];
# p/ Y7 ^* [/ `- P# g9 Z $maxlength = $this->fields[$field]['maxlength'];
4 }: |/ I3 ] e& u6 N5 b, \' Q $pattern = $this->fields[$field]['pattern'];
6 h t6 M. k, M3 n $errortips = $this->fields[$field]['errortips']; ; u% U K4 h. I9 _ d* o" S
if(empty($errortips)) $errortips = "$name 不符合要求!";
" A0 B) J3 A& F, G! s $length = empty($value) ? 0 : strlen($value); : r) F D+ {5 z# X) b
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
5 O/ P; M, F; C6 E9 j6 A if($maxlength && $length > $maxlength && !$isimport) {
" B. o% r( |4 Q1 A+ F showmessage("$name 不得超过 $maxlength 个字符!");
9 Q) o- p* f. \# V1 L } else { " I/ N6 Y- w6 D9 @% [& Z; L
str_cut($value, $maxlength);
) G- F; m- z9 n" J. G } + K! C$ C9 p1 N2 S- _
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
2 E, H f( [' Y& Y4 m if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 7 ?& s, R: ~5 a" B. c
$func = $this->fields[$field]['formtype']; : O2 e+ k2 G1 |: ?6 f: G& b% k
if(method_exists($this, $func)) $value = $this->$func($field, $value);
3 O& P1 H) y) U4 S8 J $info[$field] = $value; 0 j& l4 G- n# u
} + a$ ?3 I* p" w; {2 k! ~5 u& K, {
} 4 W' I5 b9 z$ r5 o" J7 f c+ E
return $info; + D0 C3 j' }1 x' f f5 `& d
}
6 Q/ N7 h* P: e1 f2 R, ztrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
* ^/ `. P. Q) m$ P( {% t6 N! D
9 v; g+ \% q0 N5 X( x1 t再到phpcms\modules\member\index.php 文件account_manage_info函数 j6 \5 I5 V1 ^
过了get()函数之后。
! T. k+ l$ b' f8 Z6 y. [ q0 H! W
0 p3 Z& G$ I- x3 ^$ m8 `" ]# W
+ B/ J, T8 o- N' M- O$modelinfo = $member_input->get($_POST['info']); 6 `9 Y% T( Z4 t8 l6 o
$this->db->set_model($this->memberinfo['modelid']);
2 Q7 ]6 m. f9 K1 c $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); , p+ n2 [+ D0 j
if(!empty($membermodelinfo)) { ; v' P0 \& \3 G; C/ e# K1 g
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
( L5 I4 v% j+ p9 D } else {
* P/ ^: Q* ?. I% n; x! O8 m直接带入数据库,update函数我们跟进看看% X2 d3 Z6 W% `3 e5 M: }9 _: l
2 s7 V# i4 V& H# Q' e; g
7 F9 `) I5 U6 e% H0 q5 ]
public function update($data, $table, $where = '') {
8 H/ p9 d* x' Z if($table == '' or $where == '') {
1 F% b* X; v- _/ P7 c: i) T* O return false;
5 Z( N7 z' I9 `3 x/ v }
" y6 u0 Y% H" _" J+ }! [ $where = ' WHERE '.$where; ' m0 R" D, l/ P( [- Z2 u
$field = ''; * E" A# b u; Q3 N
if(is_string($data) && $data != '') {
) n. p, N' e* ^; h2 P/ H $field = $data;
s+ h+ O6 T2 r* U$ z) g( h } elseif (is_array($data) && count($data) > 0) { & x. B/ l+ A, \0 V' r7 J- A
$fields = array();
' z. [$ \" Z& M \ foreach($data as $k=>$v) {
- M+ [9 J4 N$ _6 P+ ?* m switch (substr($v, 0, 2)) {
( R( b& }! l' X( Y case '+=':
; q4 n# `6 E3 } $v = substr($v,2); ( v; u% T+ _ k" Z
if (is_numeric($v)) { , A0 ]( l" u: I% _
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); % }, d$ H' ? A O. n+ ^
} else { 9 P+ y9 u& ~' [: Z: i
continue; $ x8 p, E2 q$ f
}
+ r7 r E1 S+ i [% p5 P } break;
- O4 K0 m8 B# y' x; L case '-=':
5 V- f3 K2 }: M $v = substr($v,2);
: G; P: \& L' m4 Y/ `! }: W* A if (is_numeric($v)) { 1 Y8 h2 e8 r- A6 C6 N5 k
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); . D# Y0 T, {) n% A8 L* c
} else { 4 E. D3 u' y& V' }' a' k8 J
continue;
& M& g8 a/ u4 k9 F3 v6 O } 8 K& M: [. k) `: l0 t
break;
- \+ f8 k O: V2 W, N default:
# E4 X1 @0 }3 t1 Z- p0 Y $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
) I5 m, P, t! [6 A8 _, V } 4 s$ A. R. t' E" d2 p3 D: j6 b
}
4 {' g1 o4 x" W" O1 ? $field = implode(',', $fields); % Z2 n, f* @# s& n
} else {
. z- v1 P5 x3 `3 d* W5 o return false;
3 @7 F# t; I7 s3 n. t+ o }
( V0 q! R i& T1 b $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 9 h/ ?9 _$ s5 j3 R) N0 F- n
print_r($sql); : t7 k+ `) b- {
return $this->execute($sql); 2 |0 E3 q6 \2 M8 X B3 p
} ) y. V/ Y8 I5 p% U) Z6 A
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
: T" L7 g, }, ?' s. m8 q% Y6 _5 j5 M4 Q" g7 N+ B1 D/ c S' A; e5 X$ e
攻击测试:
! }9 P; [' e% x; e( S/ y# N% ]测试地址http://localhost
( e q. }8 `3 D 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
k! U# [3 ^( [: l# Z- X( |- B
5 J) O0 _# j8 C2 w" l4 i$ H
* \9 \8 ~* X8 u- V4 N
/ }1 C1 V8 Y4 _) @+ A, m: z
9 g7 a: F# j- r+ y! {. |3 Q5 t/ o5 i
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|