|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告) e& \2 |! Y$ h/ g; W7 O0 `8 D
漏洞作者:skysheep
) ?; E7 C3 n8 b' x1 [2 ~1 x" _) J" H分析作者:Seay0 K3 v5 L. K" S. @
博客:http://www.cnseay.com/
# A Z1 l5 I& X/ m, W0 _& \漏洞分析:
- @' H( I Q$ ^7 F4 J' a9 D( s 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
* Q* C _0 r0 v$ n7 L& |2 M
2 M$ f3 e% v6 V% K
I! @ `9 Y6 h! w5 b% ?0 L n* ^& M6 a' R/ a8 w" k! \
public function account_manage_info() { : G# w, G M% d, n
if(isset($_POST['dosubmit'])) {
4 @4 R9 z% G) L% Q) e6 B //更新用户昵称 / _4 |- X3 u) B2 m9 a
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
3 V8 n9 k; s& _ if($nickname) { 6 h7 ?* C* O4 e3 Z
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
) Y3 O' m8 l3 M, C! c! ~ if(!isset($cookietime)) { 7 j7 R$ b7 T. K/ P/ q
$get_cookietime = param::get_cookie('cookietime');
7 x; y( `/ q, S" L- I1 D& Y( [ }
5 _7 z: D7 Y# i1 U $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 8 e6 i' `' N' H& U
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
7 Y. K0 K( x' Y9 z: z4 V param::set_cookie('_nickname', $nickname, $cookietime); 4 X @. g# O4 H+ i1 V% b
} 0 P# E( d# j3 y- F" @# |
require_once CACHE_MODEL_PATH.'member_input.class.php'; 7 {4 ?3 | ~/ h) J0 _ a
require_once CACHE_MODEL_PATH.'member_update.class.php';
3 D4 e7 ]6 w4 T7 t9 v! ~- ~ $member_input = new member_input($this->memberinfo['modelid']);
9 l2 |9 t4 N; e7 C $modelinfo = $member_input->get($_POST['info']); Z& y/ I, h; B
$this->db->set_model($this->memberinfo['modelid']); 8 g; W, U0 F) q6 h* W2 L F i
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
/ S2 H" H+ j7 ?( O: z- k* `* ]& I& j- N if(!empty($membermodelinfo)) { ' t" y$ k! ~$ l* I: O- y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); - V' l% o5 y m; ]8 N
} else {
, d% w: J0 i( { $modelinfo['userid'] = $this->memberinfo['userid']; 4 c* s* F4 ^# |( O! J; L
$this->db->insert($modelinfo); 3 o/ U0 F, O3 G* `
}
1 ~$ |8 G# S' C3 D' U% Z代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,2 x+ V. x% W: M, x( i( R
在\caches\caches_model\caches_data\ member_input.class.php 文件中:7 |; X+ f: c# |# ?. Z G5 P
: Z5 y# ?; ^. d k. D; [1 f& U
+ J; p- e- @& R* l$ a- y8 J
# M' c2 j$ N9 X% H9 q) }# _
function get($data) {
. T7 |. _% k- H# [' s* b $this->data = $data = trim_script($data);
% M" l& m0 X: ~' s7 k/ t $model_cache = getcache('member_model', 'commons');
/ @3 O% y( i+ O9 R* _' e6 R $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
& U7 V* [; o) x, G1 S5 R $info = array(); U6 h! b; G; E9 [( _* l9 A" K
$debar_filed = array('catid','title','style','thumb','status','islink','description');
6 B6 C, B7 O+ p" t; O3 k1 W, p if(is_array($data)) { " ]( E" z$ x! _) Y3 Z M A2 }4 z
foreach($data as $field=>$value) { 0 F7 b1 Z% Q. x5 ~$ g
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; # z9 N# s# m: Q9 ~, g& u0 q M+ ? U
$name = $this->fields[$field]['name'];
% X6 l9 I# L, F6 H $minlength = $this->fields[$field]['minlength']; 4 z0 v# a% O$ s5 Z5 G0 k: ~1 w
$maxlength = $this->fields[$field]['maxlength']; 7 G3 u4 f8 I4 s; b/ ]
$pattern = $this->fields[$field]['pattern']; ' Y4 ~0 u* t# L$ P- R! Y7 ^4 V v
$errortips = $this->fields[$field]['errortips'];
. {6 }* T, L' J6 Q7 A* m if(empty($errortips)) $errortips = "$name 不符合要求!";
7 U: ]! N* z; _: O; R' ? $length = empty($value) ? 0 : strlen($value); 8 X: V/ I( x. F. N6 `
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
% Q7 Q/ u$ m7 W6 \% ]7 ?; C/ S1 `' w if($maxlength && $length > $maxlength && !$isimport) { 1 _! I& h! ]9 T3 g Q6 n( b. w
showmessage("$name 不得超过 $maxlength 个字符!");
/ l) L( o; q9 i3 s9 U1 J7 q } else { 3 W. f" t, v3 e1 Y7 c" t/ }
str_cut($value, $maxlength); ( H8 ~5 K* Z: B0 m A8 ]" w; @
}
1 t6 P3 I0 J* q: z. p- t3 ? if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ; ]5 }: J. U* n
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 3 A: e1 j6 [9 p! T+ P
$func = $this->fields[$field]['formtype']; " d2 z, |$ m3 x3 Z! A l+ K5 K6 N3 L
if(method_exists($this, $func)) $value = $this->$func($field, $value); 2 u' ^9 [3 C$ t- @! k
$info[$field] = $value;
5 n9 h! I* R( o8 E+ o% u }
8 z9 E+ y7 z( N; ?: X# p }
" q0 I. T' p6 q8 @ return $info;
3 ], |- p, u/ |: P6 X- T } * U/ \$ j7 ~1 d8 x `& n) T- g
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
" x( u3 T7 L6 Q8 K6 z6 r3 x( S$ c& O2 f
再到phpcms\modules\member\index.php 文件account_manage_info函数" p0 l5 B5 A: i. E
过了get()函数之后。7 N c% B3 R" [; n' K, J3 X5 C L
. p( T. ]1 e5 `) Z/ P# V
5 Q. z1 ^& F3 E+ ]5 I' p: P$ V
$modelinfo = $member_input->get($_POST['info']); 2 T/ p' z8 H6 i
$this->db->set_model($this->memberinfo['modelid']);
' V) ~& E' ]7 E9 X- ~- c0 u $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
% F% Q% [7 r4 g6 [& P+ j8 h if(!empty($membermodelinfo)) { 0 d& U g8 p; v) f) j' ?& r
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
5 q" \ j( J. N- k } else { + V8 y5 ?5 x( u$ X( A* Y0 n
直接带入数据库,update函数我们跟进看看
4 n( X7 E6 b8 Q. ^' C. V- |: | T& |
2 f* }* _# O s, u) t6 ]# Xpublic function update($data, $table, $where = '') { . c* o& v, d6 J4 Q3 j6 M4 u
if($table == '' or $where == '') { 3 V: C `; Z }- b/ S0 e! o+ m
return false;
* Q1 i5 {; ?7 a3 Q }
: Q- u/ q1 G! y! Z3 K $where = ' WHERE '.$where; # q9 e. {& x2 o m3 ~* |/ @
$field = ''; ; P% x' G Y4 C6 C% j
if(is_string($data) && $data != '') { % e$ @8 |$ G' B* R/ |8 u
$field = $data; & O. ~; B! s5 h: x
} elseif (is_array($data) && count($data) > 0) { 5 r! J( p) Y7 f6 {; y
$fields = array();
; s& i' B1 W5 z" b p. a foreach($data as $k=>$v) {
5 @" s# U7 P: O switch (substr($v, 0, 2)) {
+ x5 D7 _; \6 L6 E+ I `3 V. ^! H case '+=': / U5 k4 D$ w( K. d/ a
$v = substr($v,2);
! T9 e) Z ]7 S9 p if (is_numeric($v)) {
! k1 v" _5 n# d$ \; W6 L3 ~ $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 4 h6 i6 N7 q% k: B: }) U
} else {
; K# G7 y: F" h" U" m5 M5 w continue;
/ q% S5 W$ o' w }
* z7 v" F) K% J$ Z F* w break; . q2 O* y0 c; B/ g1 `
case '-=':
8 F' p# M& l# d) y L5 g7 Y- b4 p $v = substr($v,2); * h- f4 S+ X3 c' D6 \: {" [
if (is_numeric($v)) {
$ s1 Z& P4 [9 @ $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
9 d5 k' ?: x; F } else {
6 U" W% g- b1 r( o. S continue; 0 x3 T0 A& ^* c+ b$ w% w) o6 h
} , j$ `+ U' h$ c* o1 E( B
break;
. O, f" ^5 g6 V4 L8 v% e# {6 e default: 9 P! U) _$ l0 ]4 \8 \5 r9 C6 [( O
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 9 X7 X) n1 y$ \4 W. k0 O3 R0 T
} 4 ~' I$ N$ j0 ^' N& B* E* [/ |
} ) q+ q7 O( R4 k9 ~1 E2 |( ?
$field = implode(',', $fields); $ x3 `* K4 `! @' i1 v! `6 g- M& o
} else { " \4 ~- r2 t# W( j. Q1 ?
return false; ( y2 @1 z1 Y8 Y% G3 v+ W
} # T" P3 K0 d/ F# L
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
3 C4 Q3 x1 X* a6 k+ n print_r($sql);
6 |# B% C. A/ ^9 t/ w$ O. q2 g return $this->execute($sql); 6 c \+ H/ R* {( k: y
} 6 d$ J# F. d0 G4 v
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。) k+ x j% k, B9 @& h* u: k
% D. h" P) _* F4 M8 {3 ?
攻击测试:
% k& i8 Z9 ?0 X$ z ?) d测试地址http://localhost
8 g5 U6 Y9 I1 W. v9 b% w, j5 r 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
7 j$ O: H6 b) R& I% J, m$ V5 |. w1 v+ _: K4 f
6 P8 V( A* { H* j% B! R9 ]
, S( o' Z& a; j. [0 j: |
6 l6 a6 `' x1 c3 _+ T
8 L( E4 U" r6 M1 Q0 }2 q4 W+ a
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|