|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告5 J: z& \7 k. m A* ]' ^, ~
漏洞作者:skysheep
+ Q5 G/ `. C& a7 O( W: `9 W分析作者:Seay* [( X9 |4 u" y4 z2 d: K. H( Q7 m
博客:http://www.cnseay.com/
: F" T: Z; o3 d2 A# E漏洞分析:
) o7 L- [4 X7 a 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。& y, i' U6 j. M- {5 z
" j. n% @$ b" @: i! R) b E
3 E6 x! h- a, I3 h" l, ~7 Y8 N
: g1 S) I( R4 B7 B* apublic function account_manage_info() {
% ^; D! w1 {& M7 T$ e" O' Z if(isset($_POST['dosubmit'])) { . J% \8 E' b/ ~1 Y0 u
//更新用户昵称 : z9 ?+ ?6 R2 a4 q) j. r; x! U5 ^
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
" y# C6 {, M6 }6 a% a' e3 ^8 ] if($nickname) { M! [3 L% ]3 a1 `- m" r$ W
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ' H% b1 M' f5 s9 V- d, x; F
if(!isset($cookietime)) {
% P( v. G* y5 q0 S $get_cookietime = param::get_cookie('cookietime'); / L( x2 T+ i) X+ I0 b
} 1 t+ V7 p: w0 }1 ?5 E
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 0 |9 C; `# n% i" x
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
9 _+ t/ T. U9 b7 {( l; M6 o param::set_cookie('_nickname', $nickname, $cookietime);
" a7 J8 `; H8 m6 l8 ` }
$ W& g6 E6 Y- {" T require_once CACHE_MODEL_PATH.'member_input.class.php';
; C& b5 x- N/ b: y6 p0 N require_once CACHE_MODEL_PATH.'member_update.class.php';
8 g5 O* E1 ^0 L7 c8 K5 |: x $member_input = new member_input($this->memberinfo['modelid']);
( C4 Z O& g; A/ p9 H $modelinfo = $member_input->get($_POST['info']);
) b P$ B( W* }2 ? h $this->db->set_model($this->memberinfo['modelid']);
; H0 ?' D) Q3 f% b0 ] $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); % h% ]+ v1 r. z0 x
if(!empty($membermodelinfo)) { 9 V1 ^1 d" i% q A5 h" e
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
+ R1 c R7 d5 e* x } else {
0 H: W i, u* n( g! D $modelinfo['userid'] = $this->memberinfo['userid']; - P" H% t; V2 r7 g; `
$this->db->insert($modelinfo);
+ {& f9 r7 N$ } } 9 C% J6 C( i, k& ^
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,/ P! M. K3 O1 y( s7 F2 d
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
3 J8 g. ? j3 W, `6 `- ^
1 y8 y: `. w( y+ s1 w# R' t* {5 \+ ~! j& J7 n
9 @$ [$ Z. ?0 m; I
function get($data) { . Y8 M7 }( Q, I. \; }
$this->data = $data = trim_script($data);
6 j& X1 Q1 A/ g/ l. `5 y+ t $model_cache = getcache('member_model', 'commons'); 8 v: H+ J9 P" @: U5 Q
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
; K0 [5 @/ ?& u $info = array();
& W, }% I4 M+ K $debar_filed = array('catid','title','style','thumb','status','islink','description'); H8 k. p& l9 q8 d2 w4 O) I
if(is_array($data)) { ! J4 I# ^: F5 h( }# u$ k0 y+ t( N
foreach($data as $field=>$value) { 7 ~$ ]! r) H p2 x
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; ' s) x8 F4 h2 J2 n3 i0 v
$name = $this->fields[$field]['name'];
! }) m5 D L) S6 L: p' w $minlength = $this->fields[$field]['minlength']; 5 m& w R* k1 |1 w* w
$maxlength = $this->fields[$field]['maxlength']; 5 [4 Y" f: a8 M1 y6 I K) U
$pattern = $this->fields[$field]['pattern']; , B$ E5 y+ g2 c$ u
$errortips = $this->fields[$field]['errortips'];
6 s: u9 P/ A' U* ^& C if(empty($errortips)) $errortips = "$name 不符合要求!"; 2 t4 q- F) Q: U
$length = empty($value) ? 0 : strlen($value);
& ~6 O9 U i/ w5 J: |# A if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); " f+ G& g8 T. i5 [7 F8 U
if($maxlength && $length > $maxlength && !$isimport) { 6 u; v% k" m4 `4 J8 ^' o0 R
showmessage("$name 不得超过 $maxlength 个字符!");
8 Q: i8 G l3 L" _ } else { k, C' J0 r# T* c
str_cut($value, $maxlength); ' p/ i4 ]1 L, s" P u8 j7 X
}
/ [* }' W& @6 P7 m. u' | if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
+ K* N4 F: u* S! P" h) s* u if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
& A& E' [( _ s/ z7 O7 [ $func = $this->fields[$field]['formtype'];
. Q( n3 k3 V3 d' n if(method_exists($this, $func)) $value = $this->$func($field, $value); 5 N0 m% t& \- Z4 V/ \
$info[$field] = $value; " a3 l' `4 ^: D+ d3 `6 l
} ) H4 b% E' p" N% s
}
. j- w% O( _ L, Q/ b# P return $info;
* u( F2 a N7 D. y" O, ~( v } - K% k1 a; I8 K# @& i& d
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,5 l4 p* F3 P0 B: \$ U$ Y, w% U
% s) N# E3 H h' E( C再到phpcms\modules\member\index.php 文件account_manage_info函数7 p% N+ \) ~5 x7 @$ B; h3 z9 m
过了get()函数之后。
! K& p" U- O' e6 R
" |9 _" u( v& I) N5 P5 r
1 x7 z6 c! Q3 D$modelinfo = $member_input->get($_POST['info']);
/ J* `5 Q9 v! I# K5 ?# R $this->db->set_model($this->memberinfo['modelid']);
& ~; p( Y6 P$ z3 a $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 0 ? o9 ~9 P* p. u: p9 b$ w% p
if(!empty($membermodelinfo)) {
+ @0 z% R+ f6 E7 f $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ' v8 X3 v | `
} else {
6 L7 r& ]8 s# L8 A直接带入数据库,update函数我们跟进看看5 |! N+ @* q0 p4 w' j! Y" Y. I
( ?/ \+ Z# v$ d2 g+ Z6 e0 W 8 c. g0 X1 E$ ]. C" ^
public function update($data, $table, $where = '') {
. b. r e) \2 v/ D# c if($table == '' or $where == '') {
' B+ x. f% B1 U. m$ Z return false; i8 X( N7 u! Y/ l7 k* L* v# D) h
}
5 g+ @ Y* X: `9 ~9 w $where = ' WHERE '.$where; ; b) n3 z" T$ P$ Z4 F
$field = ''; # j" ]: x9 R2 _+ L
if(is_string($data) && $data != '') { % M+ _, p9 l" b6 C K
$field = $data; 7 Z) ]! k7 Y2 g$ N+ j2 }
} elseif (is_array($data) && count($data) > 0) {
4 I, n" H# X' N $fields = array();
1 Q/ [7 ~+ L, O9 B% Y# h foreach($data as $k=>$v) { 1 k' l8 y) k" b' f: p7 Y
switch (substr($v, 0, 2)) {
1 R# j9 _( @- |, Z# \' l: F case '+=': 6 c W1 w" s \1 \
$v = substr($v,2); # F) p8 Y/ @+ @. V: K4 S, f0 m
if (is_numeric($v)) {
, Y6 X% f2 W3 n! P7 A $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
& g$ X7 D; y9 g7 S } else {
. q- @4 v1 H' H& u continue;
4 j# A$ i1 z- X& K9 { } 6 r5 x+ {5 u7 O1 v$ o5 c/ u6 U
break;
+ S. `6 _4 f5 N! f: g case '-=':
! r5 j: |2 T5 v/ W, {- o* U0 l. { $v = substr($v,2);
0 n* R+ t% y$ R if (is_numeric($v)) { 5 l# w) m6 r+ P& E; \( _
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); * S/ K! g) F1 R Y/ r% n( S
} else {
# E4 @9 p0 Q: e! p: S5 j continue; 8 y) i5 v- G4 l
} + B" h9 M; N4 B) N: p
break; ' K, u/ B2 H6 D) ], K1 y$ F
default: ) H$ u; W. ~ x
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
/ Z; y* B/ g, O& L } 3 T+ n& i$ {- i' L9 z
}
- E6 q' h' \1 U8 i' I3 E9 ~ $field = implode(',', $fields);
% b# z, d% c8 j* N1 D } else {
4 ?# `0 X8 a5 @# _7 } return false; 7 m, {! M2 h; w7 h/ \8 L ~
} , G$ K2 y0 Y2 Y1 K6 ]1 N) r+ j
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
4 D- E, Q7 f8 p! N% t print_r($sql);
- ~, x( W1 |/ }' B) o; T w return $this->execute($sql);
9 w- k8 D/ n* J4 P; O, E# @1 v- Z }
5 a1 ?0 L- L8 D; O, [/ {3 p从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
, u' w* w) n5 f) d+ ^8 i% j' O" g/ B( p: I& p! S
攻击测试:
7 }) D. }5 [2 p5 R( H- b' w0 r' ?* I测试地址http://localhost% {/ w2 Q- O* G" {7 j% O, Y
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
/ [. b) i! M8 C9 w
1 B$ E3 ^. D, |- P" j1 |1 V
% c, J2 v9 j$ n9 d- m4 l4 F$ \# e0 ~& y' c) s I* J0 I$ n) q
: p9 c2 q9 X, L* R. i
; p4 t# n& i( v5 F
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|