|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
* D% d4 ~; W4 k5 {, Z漏洞作者:skysheep
6 @/ y1 v' B$ ~ i. M分析作者:Seay" q( E b; w \8 |
博客:http://www.cnseay.com/" _6 F4 x2 T! {3 N- k1 x
漏洞分析:' M- O9 ~. D' F/ S; k
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。4 G/ z" b8 C+ M* B: a; K
& I. p! P3 I& Z/ B, u' C9 j+ @2 ^5 k( y+ q6 _6 U$ v
$ @% J0 o8 Q$ {" M+ n- Tpublic function account_manage_info() {
: S& b7 W$ s8 m3 n' z- \ }9 m; k if(isset($_POST['dosubmit'])) { 1 |3 O5 j( C) f0 j
//更新用户昵称 ' E# k4 E6 {/ G0 c; i* ]
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 2 p. e5 N' s1 C! O6 `, r# J1 q7 u
if($nickname) {
; O0 z4 A( g# c$ q5 ^! r3 D $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
4 j; o3 u: i2 d! y if(!isset($cookietime)) {
4 z% k, l8 a4 D% `8 e% K" J $get_cookietime = param::get_cookie('cookietime');
$ ?0 O; H# a+ I; a. p. m }
% s2 M- `6 O; c* E$ f $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
! H* C# [: S6 k8 h0 h $cookietime = $_cookietime ? TIME + $_cookietime : 0;
) L0 {* \! ]9 T( Q& ]0 W$ h param::set_cookie('_nickname', $nickname, $cookietime); 0 p R- f" v& ~0 `0 h1 I2 \- P
}
; a/ g5 i' _: s require_once CACHE_MODEL_PATH.'member_input.class.php';
. l9 C; r1 e1 S0 k( u require_once CACHE_MODEL_PATH.'member_update.class.php'; / e# t3 u T6 [- ?/ `
$member_input = new member_input($this->memberinfo['modelid']); 3 a: k. Z% B' g; E7 \0 v
$modelinfo = $member_input->get($_POST['info']);
" @, j, U' ]. O) E6 } G $this->db->set_model($this->memberinfo['modelid']); # s! {. K F I+ N4 E
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
; }3 z3 v1 L+ W; M& F5 z if(!empty($membermodelinfo)) {
: K+ O; @. t* \" Y# ~ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); # m7 v2 A- U+ F% W7 C4 X
} else {
* H% a$ I0 ]+ V3 S, w: e+ y $modelinfo['userid'] = $this->memberinfo['userid']; 6 l' B9 A0 {% { ~; v/ C: c5 H- c
$this->db->insert($modelinfo); / }9 r! k$ ^% [6 Z, O* ]
}
! \9 c+ v. C0 P6 ?代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
7 S% T# d" P( Z在\caches\caches_model\caches_data\ member_input.class.php 文件中:% H K. N# J5 W. m
- |4 l# ]: N1 F+ o9 G! g! `/ c1 a7 A7 L5 G! {) N" B. Z4 w2 A- V/ X, |
2 i' @: o9 `2 j$ {
function get($data) { . M7 i; C, L1 L
$this->data = $data = trim_script($data);
7 [1 K0 @# z+ C% n5 s( I $model_cache = getcache('member_model', 'commons'); ' n7 k G7 C" w9 J
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
. g+ {! | M- n $info = array();
5 U1 R. v( [9 }+ F/ C) t6 T# u $debar_filed = array('catid','title','style','thumb','status','islink','description'); * r* r: z# H+ k* N% z
if(is_array($data)) { ! h0 g0 U7 ?# g7 ~
foreach($data as $field=>$value) {
# N! b" `4 [9 I! D: K0 ^# d" R if($data['islink']==1 && !in_array($field,$debar_filed)) continue; J3 b9 u2 ~* J7 F
$name = $this->fields[$field]['name']; ) U! E$ e' X3 T0 d1 z! x6 G; J4 G
$minlength = $this->fields[$field]['minlength'];
2 @. g8 h% N$ U6 l% o" W $maxlength = $this->fields[$field]['maxlength']; ! Q$ g% q" H) V+ p% [# t
$pattern = $this->fields[$field]['pattern']; 6 n/ f; ^* s; R) R
$errortips = $this->fields[$field]['errortips']; : u+ d# }. y" r e4 q
if(empty($errortips)) $errortips = "$name 不符合要求!";
- G/ b$ q+ Y7 p. x( l6 s/ X $length = empty($value) ? 0 : strlen($value); B( R7 d' m/ x: C6 k. X
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); + A4 s" P* g$ y+ {, F+ k
if($maxlength && $length > $maxlength && !$isimport) {
3 S9 O+ S8 a! i( _+ L/ M showmessage("$name 不得超过 $maxlength 个字符!"); ! @1 \( s* _) m" n
} else {
. r! v8 I+ `/ i1 h9 V str_cut($value, $maxlength); 5 f% G8 W2 ]# K$ n5 ^9 c0 A
}
. r$ ?8 d! u- n+ B if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
$ ]7 x% `/ }) w9 ]4 w6 S if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
3 v# X3 U0 O+ s0 g |7 G $func = $this->fields[$field]['formtype']; : f# s7 X& ?$ n+ X" L
if(method_exists($this, $func)) $value = $this->$func($field, $value);
/ E! }5 {5 i8 j$ u% i: p& c $info[$field] = $value;
; I: q8 I1 t! ?5 S }
0 I: G: ~7 F( t+ H, y; x! D& C } 3 D) y' i; ?* E+ b. p6 G( e" M$ |
return $info; , @& K/ `. y% W4 m1 \
} / |4 V7 B( Q7 ?3 z% [' j, `6 h
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,5 _& J, I: w( G3 h- N
/ G- N4 i/ ~" t6 F再到phpcms\modules\member\index.php 文件account_manage_info函数
1 @( G/ S8 x; {3 i过了get()函数之后。
- G, c P4 \- V; w) J5 A
7 N3 ]7 c4 Z5 `( k7 E 6 D% e) x( |2 H2 M% ]% `
$modelinfo = $member_input->get($_POST['info']);
- T, G$ l/ ]/ @: w: t7 s $this->db->set_model($this->memberinfo['modelid']); 0 F z5 H5 h8 p8 |) F: b% L( a
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
% _; T8 S6 s0 T+ V if(!empty($membermodelinfo)) { N0 |4 H" H9 p- V( X0 ^
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 0 [' \3 l. B$ o! R1 R
} else { , K( L( n( f: q* X e4 D8 f8 T* ]
直接带入数据库,update函数我们跟进看看% p. m' _/ [1 u1 t. E; p
* ]# S& f* g9 Z$ l' G8 h2 I 9 [: i* t" _7 ~1 f9 v
public function update($data, $table, $where = '') { 4 v! m9 ^* E3 B6 x, d
if($table == '' or $where == '') { - H) h0 J3 O3 I5 d. P
return false;
, ]& D/ D$ p/ d0 Q } 9 r- ]6 l! k- B. a
$where = ' WHERE '.$where; 6 M1 {5 C) ]& P' t5 W
$field = ''; , b. Y4 k" w. ~" V
if(is_string($data) && $data != '') {
( S" C9 H8 `8 s, i6 C9 g5 T9 Q: m* | $field = $data; " ]% q% M( R; n
} elseif (is_array($data) && count($data) > 0) { : ]9 U! G- k# D; U3 R: W
$fields = array(); % J7 o' x( N0 L
foreach($data as $k=>$v) {
4 E2 u6 s0 X% G" p* l switch (substr($v, 0, 2)) {
" r: y7 D9 n5 C( }5 B) Q' \ case '+=':
: ^9 J; [ b' U I/ c $v = substr($v,2); 7 V9 E( R6 G- B" Y
if (is_numeric($v)) {
, I" l) Q$ c; K* L4 e6 X $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
' ^! h* ]) n- ~ } else { $ r; [5 q+ g5 o. l
continue;
5 Z1 b4 }# u, B }
! B/ _1 \" N' w3 [; v break;
0 P$ D# a% T. x% T case '-=': * Z6 O8 \3 K8 S
$v = substr($v,2);
# m* ^% s8 w+ M3 I( G- G- L if (is_numeric($v)) {
2 y# b" X& B! F1 ?1 f" M2 E $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 6 Q% q+ Z. ~7 |! M
} else { . g+ ?# Q% R# g3 U# @5 I- e6 E
continue;
% X+ I/ i( V+ l }
( K% X0 \( d X/ @7 A9 h break;
, n8 |) f& f! j8 @: M- ~9 W default:
& j& A; z: z! Q. u $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
8 W3 \- j; m4 D4 E) ~2 g" c }
8 { B. s% I" j Z+ Q) X6 }: K0 J3 d }
7 p. Z1 n8 F& |: n4 P $field = implode(',', $fields);
% \- [- P" c) R. O } else { * b5 U$ I' T1 c8 q4 _0 h! W
return false; 5 V% Y. P! Y$ f! M! m% j( k
} * Q# z7 o: ]- y) H) ^5 N2 }
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
# G+ a7 H+ W W6 w( v9 L0 \3 x$ J print_r($sql);
# ?4 ?1 D6 [! F$ w( ^0 ~ return $this->execute($sql);
* c" }8 M' F- b } / Q2 R7 y7 {' h) Y. \
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。. Y6 j2 ^, \0 m+ M& v! u. F- m8 J
) p2 P* ^! P! M8 ?1 m, w" Y0 G: j
攻击测试:1 C, A! S# m" r$ y% l
测试地址http://localhost
: j, ?9 y! N: C ]3 x4 N, L* X" U 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句1 B$ j' U: a% R; j! ?% U4 ^
! m9 g, o/ A! n& A
$ @$ N% j j' E4 ]1 x3 D
( Y, f) ?- W7 S: A6 P5 ^' i! Z6 h, E% u) y- E
9 y8 p* {2 ` R8 P+ f
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|