|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
$ C% Q! ~) @* E* S$ q3 e漏洞作者:skysheep/ n2 F3 ~5 V3 F0 L; ^) F" v# X Q0 A
分析作者:Seay
* t$ z! ]4 X. A+ c3 ]/ a4 i, a博客:http://www.cnseay.com/( O6 @+ S4 @# j( J
漏洞分析:9 I% I( K! J/ Z; S. C$ q6 C& S
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
' x! ?/ c% c/ p$ O' v$ Q% S7 n0 q1 C( v: o
9 D! g/ H# A, @4 o9 u/ w8 V C0 Z
9 ?9 u" c9 X4 Npublic function account_manage_info() { ) L. h* A1 N+ t" X
if(isset($_POST['dosubmit'])) {
; P1 J2 ]. N4 B //更新用户昵称 1 d2 H; T9 _# o
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
/ |! \9 j$ H- M8 H if($nickname) { 9 B, `$ ^ Q' L' l% ]. y9 F4 j
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
- _; B3 W- z4 X4 A" d if(!isset($cookietime)) { 3 n+ V, I; h2 o: v
$get_cookietime = param::get_cookie('cookietime'); 2 K9 ~: Z+ Q6 n4 J
}
: |3 h% U5 T. e2 w8 s+ X5 u o $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); : W& s& Y1 D) @; V0 S
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
! N% y/ S0 q7 v param::set_cookie('_nickname', $nickname, $cookietime);
; o) \$ r/ x4 O) J& \0 e$ L }
. j- t! y, w& W) `6 r( |$ I' j" z1 O require_once CACHE_MODEL_PATH.'member_input.class.php'; 4 ~1 u+ v0 ~! W
require_once CACHE_MODEL_PATH.'member_update.class.php';
$ J- G) g. Q" P! U: T) @, y6 M) | $member_input = new member_input($this->memberinfo['modelid']); & V E o6 a6 z- K3 i1 R: A6 g# J
$modelinfo = $member_input->get($_POST['info']);
6 k0 }* o! ]8 R f: ? $this->db->set_model($this->memberinfo['modelid']);
# N0 V- i5 @3 X/ h0 r# G* | $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
4 s! U- ~' s! E if(!empty($membermodelinfo)) {
' G0 p! f6 v. B4 u: _, s7 N$ W $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); % S+ ]8 G8 m; K, k* @' b" Q
} else { ! M L! W+ N* z0 q2 [& b5 L$ W
$modelinfo['userid'] = $this->memberinfo['userid']; 1 O: T& [! x" }3 U0 i
$this->db->insert($modelinfo);
& F( P. S1 B( L6 |7 \; [% D } - c' V1 `) W7 }3 X/ _) l
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
- u- E3 t- f) @3 k在\caches\caches_model\caches_data\ member_input.class.php 文件中:
+ F) a, ^! U, J+ l b2 N" Z( ] R+ z+ C* f3 w* G: M9 c# O6 g/ K1 ^9 [
. A8 d; [' K7 {: K/ |2 o
! k+ W8 T0 G+ G/ G0 X8 Q1 afunction get($data) {
9 F, K- A, g" H6 t! H& ~* ^- p $this->data = $data = trim_script($data);
: m$ S ^( s) r3 g $model_cache = getcache('member_model', 'commons'); ! B" \1 k& _/ L
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
1 S2 e3 _; B! I9 K% @ $info = array(); . z$ Z4 z/ [: A4 E* P+ r( r. f
$debar_filed = array('catid','title','style','thumb','status','islink','description');
2 a+ Z2 m* o7 V& x7 x9 s8 U0 I if(is_array($data)) { ( K1 ?4 ~- P0 ]3 Q2 A B& x
foreach($data as $field=>$value) {
, L& K2 G5 s5 M3 x& d! W if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
9 }0 t) L1 y7 H1 d $name = $this->fields[$field]['name']; 9 W3 r9 t M; E1 ?8 d! u" J c
$minlength = $this->fields[$field]['minlength'];
4 p7 ?3 E4 [+ ]0 ~* b6 ^ $maxlength = $this->fields[$field]['maxlength'];
% I2 a* w/ \+ |4 A1 Y7 ^5 U1 S $pattern = $this->fields[$field]['pattern']; 4 J7 @) h- A/ n- [7 p
$errortips = $this->fields[$field]['errortips'];
0 A. h% o4 F3 ^) l! F if(empty($errortips)) $errortips = "$name 不符合要求!"; & b, G# d6 I( L1 z( G
$length = empty($value) ? 0 : strlen($value); 2 Z' I0 [7 r. z# S. B
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); + ?$ k+ f) n( V" N; a1 t7 |
if($maxlength && $length > $maxlength && !$isimport) {
" f6 T7 }# N1 I' L showmessage("$name 不得超过 $maxlength 个字符!");
: v$ X ^% }4 }" G* d8 X: r } else { $ P/ \4 M$ `' A! [& {) ]' z9 Z4 _
str_cut($value, $maxlength);
% n. c8 d7 C, G! @4 @) S" u } " m7 Y+ @* D/ V3 A
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 6 E9 [* Q- O) C' R, r* K* m& e
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
0 R( s0 H( @ A( z( ]+ `6 ^ $func = $this->fields[$field]['formtype']; ' n4 m" N0 P% s) g: p* M- O1 F
if(method_exists($this, $func)) $value = $this->$func($field, $value); 4 C1 W, B- p* g2 K
$info[$field] = $value; / U, x$ ^0 j) W- M
} " x2 C) b& G% F( o; @3 |
}
& e6 y( m- `- |) m" X% j* o" g return $info; + k5 {9 [) H$ t6 v# W1 e& z
}
* c6 v2 |" }: h! ]7 P- w7 ^trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,$ M9 u6 P8 K4 j3 e# q6 z% ~
2 a* W# h2 H3 H
再到phpcms\modules\member\index.php 文件account_manage_info函数
: c! L, h4 h2 Y3 g: s5 k0 p; h过了get()函数之后。
7 Y' h1 D8 V, N$ `' P1 o- x: O7 ~; T5 }9 r l/ W+ K; n. W
+ ^+ s' h1 z( C" Y+ m6 ^# v% E" j
$modelinfo = $member_input->get($_POST['info']); ' [, g& J. T# m/ s/ u, x$ L
$this->db->set_model($this->memberinfo['modelid']);
8 y$ e2 d; K. L $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
% `! p+ S# ?7 L- } if(!empty($membermodelinfo)) { $ v+ U* [, _0 i4 |+ ]' f) @
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); + R5 [) \ q( B/ {
} else {
- A! w- K8 u$ _3 ]5 P直接带入数据库,update函数我们跟进看看+ h: H2 ^. H7 I( K
( I7 u5 d. R& _2 H& P. q4 o/ G 7 |& Q5 B Y! D
public function update($data, $table, $where = '') { & ~# J3 n3 E& g7 y. e% b) G
if($table == '' or $where == '') {
1 b# J- D8 y* \: s& [3 M return false;
. b) {6 h+ \" X4 ]: X } 4 W- G* l+ j0 K" e: T9 O
$where = ' WHERE '.$where;
& y- ^( `0 z5 }6 b+ E) { $field = ''; 2 M# x3 L" t' B6 Y" a1 ?9 z; v5 v
if(is_string($data) && $data != '') {
/ v* M: ]5 t H! J' [9 g# l $field = $data; - v0 b! H+ {+ @) Q* g& V0 J
} elseif (is_array($data) && count($data) > 0) {
" o$ E1 N: g9 Q1 a* T6 T- t $fields = array();
+ ~" U/ L: S3 W foreach($data as $k=>$v) {
; Y4 V' t5 ~# k switch (substr($v, 0, 2)) {
% F, d& h+ | Q0 D7 q2 U case '+=': 1 I7 _/ g6 f4 d' e) f" A/ a. z
$v = substr($v,2);
! Y, E' B0 P* Q; w. f/ O if (is_numeric($v)) {
& q% v8 S/ U% A) z9 g0 d. _ $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
& P1 ~; F1 `2 a9 q- w" v } else {
$ X R w: J" o0 F9 Y) M continue; ) N1 P6 ?3 R; k! N- T
} k( K! F5 a* K
break;
% v8 a& r0 ^ i case '-=': ( J* \. O* l5 ~
$v = substr($v,2);
: k* P6 Q: X) M8 G# L9 V if (is_numeric($v)) { ! {2 [- r# r7 R
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
% l6 O, h) I9 F5 m0 ]1 A$ B( E0 ^ } else {
: @. f2 q0 }/ n+ ^( d+ X2 `1 e3 Q continue;
& d- Y1 l2 H- J8 d$ t; w( x }
# Q1 X$ e9 v! _" \ break;
% L- @# P2 f% I) }1 ?) w) {% v# a default:
9 C4 t P0 y) @) U $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); % |4 X$ t5 K5 ?
} . c9 p8 y/ x0 ~+ ~
}
q7 w! v! K; P4 `+ D+ q% G" V $field = implode(',', $fields); 6 W# q; \- h, S- Y4 i! c" |) v
} else {
( R. i1 k7 G8 l return false; 8 ]- k q( U9 H1 W
} , n4 f( n R1 G9 J! N
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; d$ g L5 M k" w; J
print_r($sql);
6 k' `2 i2 X% K return $this->execute($sql);
4 n L3 _: i- w F& i: T8 f* z }
$ k2 q0 U$ t. G9 |从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。' f N5 {- M# B7 m* u8 C
9 P0 H% d: a4 \& I6 J4 [
攻击测试:; Z }% n/ U7 h) t0 z7 N2 p
测试地址http://localhost
# N7 t7 j. @( A9 f0 R 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
$ Q/ |2 a/ m) {
! [3 N/ u/ e' _9 J* B
0 P. }5 I: [3 j$ J
7 l- \9 i1 V/ N$ t' l# d' D+ _+ b4 k
5 R. \. F2 ~4 [& M* w3 | |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|