|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告) }; Y5 r; X5 F$ v% V( y+ ~
漏洞作者:skysheep
# r6 v6 {" {, A7 Y+ h0 ]* {2 f分析作者:Seay
n) S9 m6 \) W1 h: ?博客:http://www.cnseay.com/6 _9 a2 s! s7 Q: ^
漏洞分析:' U! Z. @3 _. p, j. e
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。$ d( I# |$ q5 X- L, E \
/ P* G2 a. M' q h7 U. `) J! l; D& q, q- T
) @, f4 c ~2 R- L" `7 lpublic function account_manage_info() { " ~4 j k6 b9 S5 X$ J
if(isset($_POST['dosubmit'])) { & s; i- i6 `5 i0 E
//更新用户昵称 & [1 i3 ^/ P0 e# Z& h) j! \, i
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; ' S# w1 ]: }# k+ q) i4 v% v/ x
if($nickname) {
# X: y- L, D% N, d) f $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); " ~! S7 o& W, {: T
if(!isset($cookietime)) { 4 E1 M; ^" c+ S8 u% e
$get_cookietime = param::get_cookie('cookietime'); , l' r; G/ O& V: S8 |
}
% U: a# _& z( u( \4 V9 ^ $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
& p ^0 S; e$ L, U! n3 p $cookietime = $_cookietime ? TIME + $_cookietime : 0;
1 x, e9 c6 K* j( n! A# U: g param::set_cookie('_nickname', $nickname, $cookietime);
0 E" f: ]' |, L0 m } * g5 t5 v3 I. s' r
require_once CACHE_MODEL_PATH.'member_input.class.php'; $ u, o$ w3 u% D: _- T$ o
require_once CACHE_MODEL_PATH.'member_update.class.php';
/ L" n( o( w- T9 n0 I; } $member_input = new member_input($this->memberinfo['modelid']); 1 ^" S& R7 Q8 i+ C/ I
$modelinfo = $member_input->get($_POST['info']);
8 b# x/ M- v5 Q6 n $this->db->set_model($this->memberinfo['modelid']);
7 l" @: m! `$ D' i: S $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
) M3 }0 \9 t+ _ K5 j E. B if(!empty($membermodelinfo)) {
/ P0 W' F% q7 @# ~/ M $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
. q& w1 Q2 a g' n0 p0 C3 A, O I } else {
1 k0 K3 E! J! W $modelinfo['userid'] = $this->memberinfo['userid'];
4 s$ ]+ ~" N; C0 Q, |+ l $this->db->insert($modelinfo);
: Q- _) D- k- M6 [0 \- p i* q4 W }
0 R* G( y. f. j9 O4 s$ J2 U) A代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
7 ^) M! i4 U* a在\caches\caches_model\caches_data\ member_input.class.php 文件中:
% A* B' e( ]4 N; W' d) C. R: {* ?
3 b H. P; |1 h" j
( o) x4 J: Q* Pfunction get($data) { - V; f% m" ^6 g. k9 p. w
$this->data = $data = trim_script($data);
8 J1 s3 D, q5 y- y2 H5 z' U $model_cache = getcache('member_model', 'commons'); / S7 f9 C% L; T
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
6 U6 B6 A( w& V9 U4 m1 ` $info = array(); * u4 K) \3 ?% b6 [& K
$debar_filed = array('catid','title','style','thumb','status','islink','description');
+ n5 n0 H& n G K6 Z, u- L if(is_array($data)) { 4 V9 J; o8 n" ^7 I/ ?+ A, o4 C
foreach($data as $field=>$value) {
8 u2 Q1 F9 t+ T# j9 L if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
' f. T9 V2 V a0 w; j9 B7 i $name = $this->fields[$field]['name']; 8 m, g u1 d% c" y
$minlength = $this->fields[$field]['minlength'];
! \3 I$ |9 p6 q $maxlength = $this->fields[$field]['maxlength'];
2 b$ G, C2 X3 Y0 p5 I $pattern = $this->fields[$field]['pattern']; 1 X3 i+ h# c* \! q L9 _3 a7 z& h
$errortips = $this->fields[$field]['errortips']; 2 M6 t, y0 o$ z& ?* V
if(empty($errortips)) $errortips = "$name 不符合要求!";
[* b2 M- W3 R i( P $length = empty($value) ? 0 : strlen($value); " W5 r3 H3 [7 _# y
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 3 t# d8 y6 K% t+ A+ A) x5 ?- r5 }
if($maxlength && $length > $maxlength && !$isimport) { - [! _3 ?4 y0 F \! [. R
showmessage("$name 不得超过 $maxlength 个字符!"); * h' B' E+ \% J6 W+ {& s
} else {
8 d+ V1 q. u" I str_cut($value, $maxlength);
, X( ?3 ?6 S0 u- F) k/ U- w } 4 C o" j# g2 O: F5 S d
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
% a2 ?0 f* U# C' V% w6 U9 U if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); ) L8 m$ @; p5 \* c0 v) v
$func = $this->fields[$field]['formtype'];
1 o2 i9 |6 s1 N' F3 Z% a) E+ u if(method_exists($this, $func)) $value = $this->$func($field, $value);
& D2 _3 P0 B4 _; b: l: o* K $info[$field] = $value; 8 w$ y$ g' h' {2 r
} 5 L; ~( O/ Y$ B f2 V( N
}
+ k0 }' p; p' @& z% l% K return $info; : u+ ^2 x6 K: P; q. a. c
} ; c2 V5 X* C. @1 P+ S7 `
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
0 u6 A2 {- w) S- h9 |, J& P; M. K- w r( Q5 L5 |9 l i+ M" x
再到phpcms\modules\member\index.php 文件account_manage_info函数
! V: v; Z* q6 Q/ K& K s过了get()函数之后。
; u! Z; [& m/ c6 A! J/ O: Y0 l' e" d! u9 R5 c. R
: d: W# I2 `- W* ]1 W
$modelinfo = $member_input->get($_POST['info']);
( M0 F0 {6 E2 ^; T7 L $this->db->set_model($this->memberinfo['modelid']);
5 d0 E! u$ Z# l( C3 \ $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ' T6 d; {8 _" T, F7 v
if(!empty($membermodelinfo)) { ' @$ C" l; G- s; _
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
+ I* j/ f6 m0 `; ]; n; z3 q) T( k } else {
( a3 c7 [+ n) a/ d( ]0 D8 }$ `直接带入数据库,update函数我们跟进看看
+ w o" G ^% l& f& N/ r7 d5 o) T
% Y: ~! L! `- D @ $ e. v+ L4 c& O$ V: v( x* K
public function update($data, $table, $where = '') { ! B# r$ X3 q5 h' m
if($table == '' or $where == '') { 7 Z2 `& K" s( x
return false;
) a/ _- {; m1 ^* [0 v) l; z } 9 b1 x! |% L% O1 b% v2 O+ S5 C
$where = ' WHERE '.$where;
/ z! w. r5 F2 s3 e $field = '';
& M3 O' L3 h9 _. z9 X F if(is_string($data) && $data != '') { ) ^/ w; E, L: S/ c3 k' N3 x
$field = $data; ! a7 T: n) \2 x6 j( G
} elseif (is_array($data) && count($data) > 0) { ( U! l; m. q; _# x- C6 v
$fields = array();
9 n: J; _% b% r foreach($data as $k=>$v) {
7 E$ t6 R& i e d6 b& z9 E6 H switch (substr($v, 0, 2)) {
. l/ V- f& e4 [5 p2 H- E; w" Y case '+=':
t o! c. c8 n% X9 u& t7 N! G% y; e $v = substr($v,2);
% l+ A$ O1 P, q9 U if (is_numeric($v)) {
2 s- P; M7 a; O! V $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 7 M | O7 L" x' j X$ @7 I' N7 M$ T
} else {
1 X% u" _. z: q u, I! m continue;
I7 S! E6 B, ? }
3 A9 u. K* l& b9 j8 q4 c/ ?8 s" O" @ break; 5 \, u. q2 e! y" x1 k
case '-=':
1 o! S, A; E# d. ~6 c $v = substr($v,2);
& k8 o. J4 N: E E, \ if (is_numeric($v)) {
" f. F8 ^( d% ~. U8 ^& e $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
0 c: u4 H" L2 p7 l- X } else {
, T8 U- T. H: p$ b. V* K continue;
8 q. K! O( Q: l) p' ` }
" H7 Q1 J4 t. f break; ) J9 z: [0 l; |% B: k6 q' e! Y
default: ( I& W, [* m u9 D; ^& ?
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); ; t. g% ]' I5 n' f2 T( e3 z$ x
}
7 H+ ^9 |* n# k& o7 s9 [; d' o5 q }
/ q+ O$ z+ @$ V+ b $field = implode(',', $fields);
/ G$ E s4 x) m; z6 e! z) d( L* l } else {
7 P8 k6 w2 F9 C+ W return false;
1 L: u& D: C" A" t9 Z* ?1 m8 \' y" u } _# G+ i* @+ @2 m- W' y
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
( M( }* X' M4 G. v9 k' D* L print_r($sql); ) [# h3 v$ p* Q" |' f! O
return $this->execute($sql);
) b' M/ c2 p+ I5 N7 v* j }
. g! k" @9 t9 I5 }从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
3 V6 R; `) T) k/ \# @: a) Y! E4 g* G1 y$ \7 B$ b) s
攻击测试:
+ w0 U* l0 r% \ W8 G/ C测试地址http://localhost
# Y: l6 N/ m+ U% `8 R! b 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句# J- R; D8 h$ D/ y$ F
/ c- W& K4 `6 [$ V# N/ o$ i1 N8 e
% ^6 |- u4 y$ `+ C% D, O( i, X- G' ~) r
& X+ A% i; g: T0 Y) d W
{ }$ r9 ~& O- [" K |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|