|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
% S: z3 ?+ s8 R) e漏洞作者:skysheep6 Y' u: @2 K( ?1 t
分析作者:Seay
* a+ E: ~1 n5 A0 D3 A" {' j+ f- m博客:http://www.cnseay.com/; m8 y' e J( V
漏洞分析:& U7 s% G9 n" O/ B) U0 d3 A% ?- G
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
' [9 y% n0 U( g9 g9 G2 U; f& m) }5 F
: n: r8 y ? E, W4 f9 G
. A7 e7 ^- `& R7 ~9 l" k- lpublic function account_manage_info() {
% p, s. G1 u& H3 k9 @ i X if(isset($_POST['dosubmit'])) {
* F1 r' r9 |8 X) y) h- |3 c //更新用户昵称 7 M. u( h5 b" a2 Q0 W; |
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
: N, A3 ~0 H) T8 `7 m$ _, ~ if($nickname) {
4 T! I% g; V% S, K4 ? $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
) V. z! J! ^ ? if(!isset($cookietime)) { 4 K1 C3 `, c( F+ ?0 q4 Y
$get_cookietime = param::get_cookie('cookietime'); ) ]9 z. L0 {, I" `+ P- U" F
}
* H2 Z: d6 H, n# z9 g $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
6 C" G% H8 ~# v% s' W. G8 } $cookietime = $_cookietime ? TIME + $_cookietime : 0;
4 u' M+ |, C* k) s- B; c param::set_cookie('_nickname', $nickname, $cookietime); % c; q% q6 E/ w: r* L+ n6 b) \
}
! z1 x5 g3 K2 O$ w" P" f5 N require_once CACHE_MODEL_PATH.'member_input.class.php'; % ^# B0 k' r! j2 t
require_once CACHE_MODEL_PATH.'member_update.class.php';
1 A, d- ^! N$ c0 S $member_input = new member_input($this->memberinfo['modelid']);
2 {: b) a8 M/ Y, P3 f! ` $modelinfo = $member_input->get($_POST['info']); 4 _/ L. a% Y( b- h6 a
$this->db->set_model($this->memberinfo['modelid']); ' Z" Q7 @- t2 T% V
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
& l% y6 C I. | |! o- ? if(!empty($membermodelinfo)) { ! J: h: K4 _, I2 B, m3 o. \
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
' p N# @* q6 p } else { + t* H+ o0 u( Q9 D: [
$modelinfo['userid'] = $this->memberinfo['userid'];
" f6 t" {) Y. P. g $this->db->insert($modelinfo);
9 \9 @" m8 E' v2 `- o& |3 J, Z. B: a* j } + v: i) G0 k. U: a3 y" m, D5 o+ B# f% x
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
5 J( K/ Q6 r0 x( Y7 b! h5 U在\caches\caches_model\caches_data\ member_input.class.php 文件中:' M" N, c! {: ?0 V/ U% k
0 o( h. `7 F* m% N8 Z4 ~7 _
( b; x5 B2 Q7 G7 B# h
% s* g8 B$ o* t8 O5 v
function get($data) { # m. `3 }1 T: z* K
$this->data = $data = trim_script($data);
* a) v$ l8 _, f ^" q8 T $model_cache = getcache('member_model', 'commons');
1 a) c; ?) r6 w: M8 N) ^; ~ $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; & H% W! B; \/ M3 X+ e' M8 D5 a
$info = array();
% d7 x( k! R7 D. _# u. _ B $debar_filed = array('catid','title','style','thumb','status','islink','description');
* a- V$ @9 f, G if(is_array($data)) { 7 ~1 y0 \% `- ^, F) e
foreach($data as $field=>$value) { # ?4 N {9 [' j9 v- N
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
2 F* S7 r8 ~; u$ q $name = $this->fields[$field]['name'];
7 ?6 p2 }' H# ^5 ~ $minlength = $this->fields[$field]['minlength']; ; o" ?6 d) o- y5 m. w
$maxlength = $this->fields[$field]['maxlength']; " u a8 ]3 B2 U$ W" [
$pattern = $this->fields[$field]['pattern']; & Z( f! J: {0 k- F
$errortips = $this->fields[$field]['errortips']; * o- N& @; m3 G* r) g+ P4 X
if(empty($errortips)) $errortips = "$name 不符合要求!"; 7 D! m3 Z/ j( N; `7 x
$length = empty($value) ? 0 : strlen($value); " }' Y* X& l- M' u) ?
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
T% B5 f3 ^5 Q4 o if($maxlength && $length > $maxlength && !$isimport) { 4 ~* H, Q, | H0 A: W, F; U' y( U
showmessage("$name 不得超过 $maxlength 个字符!"); 2 U! j! e7 g2 R& r
} else {
" |- l) p! t7 [ str_cut($value, $maxlength);
: y' ?& I0 q, q o3 B% H } + W; j: I$ a* x% u9 j0 {
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
2 I1 x4 @8 C# O3 @' T if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
2 [5 V/ k2 p' P. O5 e% i $func = $this->fields[$field]['formtype']; 6 E8 O& f L! i" n! h
if(method_exists($this, $func)) $value = $this->$func($field, $value); # V6 z) B9 ^0 d& X0 E" I! F @3 }
$info[$field] = $value;
: n9 Z3 M! T' m) Z; d1 `/ ] }
" y( k5 D; m2 U0 c ?2 D4 S" z+ A } # `3 N! g( o1 |: l% |
return $info; ( [/ F% ^) _5 r% c
}
! P% M1 t9 X; |6 _$ i# E; _6 Htrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
% o6 a4 q# M: g* J
) {$ o7 F& F3 x- l- F; E再到phpcms\modules\member\index.php 文件account_manage_info函数( k, w" s. S* d h# I
过了get()函数之后。
8 y! x* M3 [0 `0 B$ |6 w, a! {* s
. W6 u$ m4 c' S8 U- i. E
- A* M. d( k& Y( [8 a$modelinfo = $member_input->get($_POST['info']); 1 H3 M5 _4 T7 k* A9 _! q
$this->db->set_model($this->memberinfo['modelid']);
& k4 M- z3 D" [; j; t $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 7 m- W/ Z" N) C; _
if(!empty($membermodelinfo)) {
; h2 H# o1 @# P- _3 y& Y; Y $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
9 X+ ]1 W+ d8 f% K7 `5 D } else { 3 B9 I3 B+ Q- x p
直接带入数据库,update函数我们跟进看看/ i/ b; ~/ x, ^+ P# |' ]4 `
# K: k7 d8 Y* F1 i, [5 \% B - O, f% H2 v& d$ W7 p
public function update($data, $table, $where = '') { $ P. a$ u" L P4 v8 x
if($table == '' or $where == '') {
2 ^' S0 G: h; o7 Z" B. F4 ]: n' C return false; 8 i% f" u- N! @; `3 P$ M+ @3 Q
}
' _( c2 _/ J8 \, z/ b $where = ' WHERE '.$where; : R# {: ]' o: d: R
$field = '';
$ I5 i8 ~' |) g- b3 I$ m, C if(is_string($data) && $data != '') { - J- y2 B+ r. v1 K* |+ e
$field = $data; $ N2 \9 Q" Z$ |
} elseif (is_array($data) && count($data) > 0) {
- |* c) Q" [0 |7 b8 ~, z1 J $fields = array();
7 i3 N. X! A4 \3 p foreach($data as $k=>$v) {
( e4 z/ u X0 P. Q( W, k) z+ g0 Y2 y switch (substr($v, 0, 2)) {
6 L. W& y2 X4 V* j% R' D case '+=':
- F& Z0 C( M; J* | $v = substr($v,2);
+ y$ m1 |+ {3 J( {+ T if (is_numeric($v)) { & o6 v0 u* d, l# g* T o
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
4 p$ f; S6 D* @' ~( S. } } else {
2 o* d2 `" N. u2 f- ^3 a continue;
" ~$ \6 J- K5 \, A. _ }
! X/ c$ _9 I0 f( Z break; ; Q# T5 i. Z2 V$ r8 A, D+ B
case '-=':
8 r0 A$ k: s1 [+ L $v = substr($v,2); ; m' e: r( t$ T
if (is_numeric($v)) {
1 ~: s2 r1 f/ O5 B1 P9 Q $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
! Q# ^( Q9 q& s4 N } else { $ Q+ E, P' S( ^' L2 u! X
continue;
9 n) Q- }: S6 Q }
! \7 c2 ?2 Q* T1 @3 C break;
) t4 h- ]" {6 c( U default: : |7 t. i, N5 F) q5 i0 n+ i
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
& m2 J& \1 y5 q; t7 ^) P! D }
( _1 L7 o8 \1 F n- _ }
) S( Y0 [ n F3 J* O $field = implode(',', $fields); 0 r, f- A) H, a- O5 Y/ j9 {5 G4 C
} else { - @8 s' b- x7 i$ |0 y3 n& H7 K
return false;
* }* n( U. x+ Z. r } / f! O6 {! t7 K$ d7 F; ~9 H
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
6 f5 a* }5 O- T9 n print_r($sql);
9 f' J! [" [1 s) ^7 l return $this->execute($sql);
2 v- D5 ]0 ^8 p! g! E9 ~3 @9 z, i }
! d* R0 K5 W: @0 M5 I2 v& d从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。, `+ u' s" a4 J+ J6 R. M0 q3 W; n
& ^, }) i8 _0 e1 _( j; V" [
攻击测试:7 z, m% t! a2 P1 C
测试地址http://localhost+ ]% m6 D, ~1 T- @, R, ~8 L B( b
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句5 n1 V$ F1 t/ L4 m- h; X5 M7 M5 V
. H' N! e0 R, Q8 A& L5 t7 B
% E* ^! D8 f$ d6 d: K
' }& f; F5 K0 ~3 `. r8 K4 s; V
B2 d0 u( L) o& [; {
% z* {8 a9 O4 F; ?$ [ |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|