|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告' }5 B3 J; A1 \1 u
漏洞作者:skysheep
8 U( ^: s9 k! x. E: i; V分析作者:Seay
3 d/ O( m% m- u7 h5 Q) a% f& Z博客:http://www.cnseay.com/
2 L) j4 N5 B1 h5 z% M/ l$ x漏洞分析:( ~. X; l( u L Z' S! p1 j* s6 \
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。" l& D0 i" O j% }
( M4 s" j; e* o+ x0 N: c: _1 g- G- z1 z$ N& {, [) f
: F! M7 T/ \# Tpublic function account_manage_info() { ( G: u8 y- ^5 A+ c$ z+ c( V- y" l# f
if(isset($_POST['dosubmit'])) { 8 p' {! B/ @/ G3 c% h2 J y/ y
//更新用户昵称
; ]- f0 t8 d& r6 ?. ?& G7 z $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
9 E9 ^6 T1 J5 W* _9 W/ { if($nickname) { # ~% E0 l: ^, T! f9 {! c
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); # G! _; s$ m6 @
if(!isset($cookietime)) { & r2 E( Q3 Q/ D) Q
$get_cookietime = param::get_cookie('cookietime');
# T3 J6 Q2 T I2 S4 _% H. d }
1 D, h$ v2 a2 V $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); * j1 Z9 \% H3 i& ^( C( s( b
$cookietime = $_cookietime ? TIME + $_cookietime : 0; " m) i* `. [# M; B; Q: f: z5 d' f$ M
param::set_cookie('_nickname', $nickname, $cookietime);
- G# C5 Q) X0 [' E. e }
; r0 `+ ?( s; W6 C; V require_once CACHE_MODEL_PATH.'member_input.class.php';
* W; j( y |0 v1 J( m9 ^ f require_once CACHE_MODEL_PATH.'member_update.class.php';
6 @+ s6 b \' U: k3 r $member_input = new member_input($this->memberinfo['modelid']);
. R9 x# {, Z4 K5 {& ]. | $modelinfo = $member_input->get($_POST['info']);
! E0 D3 z1 x4 ~0 v/ Q( H8 j1 G3 J. b $this->db->set_model($this->memberinfo['modelid']);
8 c y7 q# N7 s $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
6 {# X4 N1 F9 P- C if(!empty($membermodelinfo)) { " C( [: I1 g( O' \) m5 Q
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ; `( z& K' L/ W% d' P/ T; r; p, q
} else {
' _1 ^$ p* X9 w% g, v $modelinfo['userid'] = $this->memberinfo['userid'];
/ U- B* ]! \1 v $this->db->insert($modelinfo);
1 R* f$ o' S" u ~+ I }
: L, ^- _: } `/ D4 y0 K1 V代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,! p" B! h( \/ R3 x
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
9 v# R) _& [" S$ `$ A6 F% O0 w! C/ {' ~5 B- p- U1 ^, K
1 J' I, f! v2 z8 Y2 e9 T3 E
- I8 E& q% _9 S/ v! Q y4 `
function get($data) {
7 q( c. ^: c# n7 Z $this->data = $data = trim_script($data);
8 V: i) t! K8 L2 `( p1 S $model_cache = getcache('member_model', 'commons'); - \7 }5 {* D( E1 |/ ^& t
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
1 f3 c% M9 Q0 h4 m B $info = array(); ( f* O* R, D0 I- M
$debar_filed = array('catid','title','style','thumb','status','islink','description'); 6 V' M5 A7 n8 O) t8 O4 p& s
if(is_array($data)) { ! `; }1 y8 r( o9 a& d" |4 `
foreach($data as $field=>$value) { % o) g" W/ |3 |. F9 s; K
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; % q0 N- P3 s' U
$name = $this->fields[$field]['name']; & C+ X, K% D' ?6 J- [ g* `" }
$minlength = $this->fields[$field]['minlength']; - I6 c' _' j9 W5 _
$maxlength = $this->fields[$field]['maxlength']; ; M5 W8 |8 d6 L9 n
$pattern = $this->fields[$field]['pattern']; 5 B# i/ I- e, S+ K. f$ n
$errortips = $this->fields[$field]['errortips'];
& H; W& I$ N8 D' G& B8 L$ U7 K if(empty($errortips)) $errortips = "$name 不符合要求!";
# P2 Q# a f/ j $length = empty($value) ? 0 : strlen($value);
8 w9 O* H+ o/ V6 {) A if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
7 z8 f0 e" ~! s1 O2 ?: t$ l6 z$ T if($maxlength && $length > $maxlength && !$isimport) {
3 _3 g Q3 \$ ?% q H# u w showmessage("$name 不得超过 $maxlength 个字符!");
5 v4 C, R/ P# w# S7 n- ? } else {
, J1 A( j& \* Z5 y) z# O str_cut($value, $maxlength); " Y b6 a# _8 p, k: R
} - X4 Y2 N- n+ N, z9 J& j4 D
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); * B. u; M: Y; o9 E0 s% b
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); ( A1 o, o8 {! g
$func = $this->fields[$field]['formtype']; - J- ^) m; t/ L( O
if(method_exists($this, $func)) $value = $this->$func($field, $value);
9 y; S* }5 g1 o, x( ~3 R0 ] $info[$field] = $value;
6 t# J/ T+ T' t: D }
6 _3 {) [: ]8 Q0 w }
" D7 U# u7 M4 t: W+ c. M return $info;
1 e" T( F0 z6 q }
" B+ f5 p+ q$ N* [; D6 B) A9 utrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
5 ^. n3 m3 \: |6 W" j. B0 E; ~6 X
再到phpcms\modules\member\index.php 文件account_manage_info函数1 s# \0 |& U% A d' _, N! F$ R j
过了get()函数之后。8 ?; g- s# z9 t
- z" Z y- l q$ r
3 n- K6 G' V% m: x5 z3 ~2 S0 t; G4 i. S
$modelinfo = $member_input->get($_POST['info']); / W' {* ?6 f5 q' u, s4 d
$this->db->set_model($this->memberinfo['modelid']); 9 k6 {: P& f* t( g2 G, g$ z
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 4 K b7 ~2 p2 C( S7 t$ e- Z
if(!empty($membermodelinfo)) {
$ R* h2 L$ F/ r$ O $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); # F! G& T' a2 F: j' P8 y; Z# f
} else {
9 B% d( C4 L" Z. V* V7 J直接带入数据库,update函数我们跟进看看
; |+ f9 ~, d8 @# R& {1 O+ z# e4 ?0 g9 r
+ {9 v8 c4 D. s8 i
public function update($data, $table, $where = '') { - I, a; N7 s! w' `3 N
if($table == '' or $where == '') { + p j# K8 n2 J4 X7 \3 s
return false;
* t3 W0 E9 @5 y3 o6 p% }$ L8 H }
]7 [8 y: V) G6 K5 `$ ` $where = ' WHERE '.$where;
. K2 I4 | u U# c $field = '';
0 s7 X' v" \! r$ y# { if(is_string($data) && $data != '') {
& w4 L, B- X& S2 Z; } ^$ h$ @ $field = $data;
7 a, y; Z B9 a( s } elseif (is_array($data) && count($data) > 0) {
0 W+ N# z& }) X$ G: q1 q: B- | $fields = array(); ! ]- i. [- v4 p% ]6 Z2 h/ o
foreach($data as $k=>$v) { 2 H& k2 C! D7 a8 b9 y
switch (substr($v, 0, 2)) { - Z+ I) B$ |- B
case '+=':
, W/ O2 h& X4 H( _; A) W& { $v = substr($v,2);
5 Z5 U8 Y. b! u# X6 O if (is_numeric($v)) {
; ]3 o: y$ J) F6 ~% O3 P% i" U" I $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); # M- f1 p# }& O8 f& J
} else {
' U" F9 c, C+ r+ d4 @ continue; $ ] T/ h: ~* L% Z% j4 A( Q: I+ X
} , M' h: {8 {* I! L2 u! E& G$ u3 p
break; 1 e1 l/ _) J! k$ f3 s
case '-=':
# d+ W; j4 a" A. w2 L; [; x8 F $v = substr($v,2); 4 a2 t6 o8 `- @2 Q# H( i
if (is_numeric($v)) {
, Y0 E& A# [. y, I+ j* W $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
4 `, ^' V8 ?" P M } else {
2 Y5 ] X3 |7 K9 ^, N$ X6 W+ Z continue; 2 Q) B2 w( t- H$ Z3 J
} $ [2 [" A. m( r& D
break; $ a' V9 F& b! a- q! M
default: 0 Z u% ?' I/ i, p
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
* A3 V. y% o3 ?- E& | } % f( p+ k/ k: Z! p. u) r: ~, h5 F
} 8 _+ |2 i, h3 I3 I/ G% t
$field = implode(',', $fields);
9 [3 ~) F+ N+ a$ Q4 m8 Q* U& p' d1 v } else { L+ \- ?1 E: }3 g0 R7 `8 i( g
return false;
' |: P' o: q: w# m }
5 E |+ w ?# H* G: A) o) B $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
7 w. z) M% n4 c! { print_r($sql);
B8 w; G& ?( |/ g! {' B return $this->execute($sql);
8 f3 p& f8 |* [ } - @3 u" W% ?$ G3 m; |+ j& z& e
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
7 a6 z2 r, H3 C {2 Y
5 Z# t1 i8 s" m; e攻击测试:
, W5 O: H# I0 ]- I# A测试地址http://localhost
9 Q" a4 n1 ~2 h t 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句( ]/ A4 q e% \" f( j/ t5 S! [# y
, O* _& C/ r( `0 T1 F2 q( n; e
9 J3 q! a5 q, ~! f+ p: j4 ~* ~% x: B: }$ I5 ^
' p$ u9 Y( K9 a- e& m6 n6 Q" X4 u7 {8 w: {9 ^5 g! g
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|