|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告% x8 h7 e, K& Y( P0 ^2 p
漏洞作者:skysheep, V/ C5 t* y1 d7 \. A
分析作者:Seay
3 }2 O b" c' \8 A r: K博客:http://www.cnseay.com/
3 M$ r6 _/ d2 L4 r漏洞分析:; D1 C$ z/ X' k; f% n4 G% A& y6 L/ J
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。1 z- h8 f' ~! ]. q/ h6 X
! |3 R5 s9 d, E, W1 I' k; U/ e
) v: d* N$ b4 v* ?$ ]8 A
5 G7 r# E D7 _* ]public function account_manage_info() {
O) K1 p0 I7 n7 ~3 E9 o' B- { if(isset($_POST['dosubmit'])) { " ?7 x0 a& C5 o# R( e
//更新用户昵称
1 G b6 m3 E) w& F. ? $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
- j$ E# m- L9 p8 x7 g6 Z+ } if($nickname) {
& v8 o) m3 V" h7 X $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
; x* C. C2 F1 K& [9 M if(!isset($cookietime)) { ! [* r! H9 Z& t" @+ {% g
$get_cookietime = param::get_cookie('cookietime');
7 W' ^5 ]1 ?$ `( ~ } . S3 j% [5 f! [8 k
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
0 E; r* J' T+ S/ \ $cookietime = $_cookietime ? TIME + $_cookietime : 0; ) Z- |) |& E$ `- i. i4 p
param::set_cookie('_nickname', $nickname, $cookietime);
/ t5 D5 m: Z! E! h/ P7 H } 5 D$ P) i3 ]3 n5 R$ R; B/ P1 l, l
require_once CACHE_MODEL_PATH.'member_input.class.php'; 0 x2 u3 u* |: l! L# {
require_once CACHE_MODEL_PATH.'member_update.class.php'; 8 V. d/ C9 M5 P' L! N. l
$member_input = new member_input($this->memberinfo['modelid']); ! e1 ^1 I8 |0 F- h
$modelinfo = $member_input->get($_POST['info']); & b' C( } p& L6 X
$this->db->set_model($this->memberinfo['modelid']); # N9 T8 A7 I4 P' W% } S7 P- f Y
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
2 w3 M; h4 H6 l+ O* Q if(!empty($membermodelinfo)) {
[) Q" s0 l2 W/ Y $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
( s8 R5 L& j: m5 f" C7 G+ e } else {
! X6 u. O- i" Y; L3 w( i- ~, @ $modelinfo['userid'] = $this->memberinfo['userid']; % l/ j% T M$ i2 Y u3 f
$this->db->insert($modelinfo);
$ G7 D- e% p. }3 E' K; @* E6 V } : H' V7 k/ G+ a) j7 E% q* u8 z4 H
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
7 V _" O+ G4 {1 |$ J+ M在\caches\caches_model\caches_data\ member_input.class.php 文件中:
% P, {. \& C6 l( t/ J3 w4 {! _6 W
, Z- B/ h9 J- \% _" R
7 x* i" C7 ~* a
function get($data) {
- f6 A5 p% M2 r6 }% j1 H* I $this->data = $data = trim_script($data);
8 H. D. W; t( a $model_cache = getcache('member_model', 'commons'); 5 C- E- a9 x( Z% J4 Q: ^
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
+ Z( ~4 X" h& ~. @' |$ X! W# P* o $info = array();
3 [. K7 W2 ]1 {5 _* \; u $debar_filed = array('catid','title','style','thumb','status','islink','description');
+ {; A& L+ @6 [/ Z7 K$ x5 r/ | if(is_array($data)) {
* d" B) f8 b$ a foreach($data as $field=>$value) { ; W1 {* T9 S; c( Y8 @% }
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; , D- S6 u3 P1 t# P8 i
$name = $this->fields[$field]['name']; % Z, v: p, A+ b1 ^7 ~4 v5 o2 N; t
$minlength = $this->fields[$field]['minlength']; & Q0 E0 n# |( @# J+ \) Y9 \# P
$maxlength = $this->fields[$field]['maxlength'];
( z2 I E* H e3 E" l $pattern = $this->fields[$field]['pattern']; # M" n3 _8 G8 b" e
$errortips = $this->fields[$field]['errortips']; & F$ f: N; n* c3 `
if(empty($errortips)) $errortips = "$name 不符合要求!"; 7 w7 T) p7 O0 E6 t/ J& [4 \
$length = empty($value) ? 0 : strlen($value); 7 V" n: l$ p% j# F
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
3 ]! Q) g2 N) Y9 q; m8 ] if($maxlength && $length > $maxlength && !$isimport) {
! F2 _" Z5 W/ z/ P8 {* G+ s showmessage("$name 不得超过 $maxlength 个字符!");
" A7 }$ z1 L" o2 w/ b2 h0 p } else { k; ^0 J$ o: A3 U% d
str_cut($value, $maxlength);
2 g, Q1 \4 r' Q# u }
% I- T( n2 c/ g$ X3 d0 _8 I if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
" Q8 m" M x$ g if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
( H) J4 l. C4 b4 x/ g" w $func = $this->fields[$field]['formtype'];
0 H1 r" Y' Q L9 i- S if(method_exists($this, $func)) $value = $this->$func($field, $value); ' O7 w/ a! X) s& _6 J1 E
$info[$field] = $value; $ I& e1 K8 C l
} 9 r- d: H U& F a) Y4 C
}
$ y7 W3 u) e# L9 f3 { return $info;
. |# F3 `: O- p } 2 [6 v8 U+ X9 n: p& O
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,: r& }! n% j* |4 X7 G; F
M' k+ t* L' t: x2 j再到phpcms\modules\member\index.php 文件account_manage_info函数
6 L% \ t/ C1 A/ j- z过了get()函数之后。/ Z# ~* l7 w. n; V
) _$ l; i0 m; h
: b3 n9 @; o/ {1 c: ~9 j$modelinfo = $member_input->get($_POST['info']); ' y) a% V6 P" f" k" G, T
$this->db->set_model($this->memberinfo['modelid']);
Y3 m: }/ I1 ?& s* h $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
2 g" v4 ?! \$ U" q7 C if(!empty($membermodelinfo)) { 0 @7 H4 c: V7 k/ q
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
1 y8 k% y, V/ k4 u- d } else { . l3 C2 p/ l* |* S
直接带入数据库,update函数我们跟进看看" W$ |3 g9 X+ m5 k$ l* r- R
) ]0 V$ i& R3 }% s( L
7 T' [$ C9 q* P6 [4 ~6 i2 B2 J, Bpublic function update($data, $table, $where = '') { 5 F* \! q1 `- k1 T+ p" V1 H0 K4 L" `
if($table == '' or $where == '') { + b3 X8 b; V: P# O, H" ?
return false; : V! D+ t' ]3 r5 p
}
0 J* g- h1 T5 d: M J. |' r $where = ' WHERE '.$where;
: z# U) R. [1 \; j- N9 F, V $field = '';
- i& e' n3 ^2 Y% W% H. U/ } if(is_string($data) && $data != '') {
8 w% {% u! S0 [4 p& ~5 J% L $field = $data; 3 N! f0 {5 I% L A7 Q
} elseif (is_array($data) && count($data) > 0) { 4 _3 u D7 H1 Q5 ? q& O2 E
$fields = array();
( M2 V. W1 T. C9 k foreach($data as $k=>$v) {
. l; u D0 z9 v switch (substr($v, 0, 2)) { - B- }: }& Q3 Q# j! \, U
case '+=':
& r5 U5 T$ H4 o5 Q% }8 [7 c1 O: v- d $v = substr($v,2);
& z4 h1 @! @7 h+ S ]! ^# [' i; n if (is_numeric($v)) {
u! y+ A, U; M $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 7 r( b% B. o4 A/ K! A6 O6 a$ \) \
} else { % U9 S) W6 l. S5 n( h: f; z
continue; " G" l2 g0 S3 {
} % w) r+ R" m" W, `1 {6 z
break;
$ z* w' C- {$ ?6 N+ {, I0 u case '-=':
: y: a! J) M- w9 K% s9 n+ q $v = substr($v,2);
+ O$ {9 t4 D: }5 Q7 q+ @8 n if (is_numeric($v)) { ( n( f2 r; e/ }% b
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); : B4 e9 u# g% z
} else {
Y0 q4 M$ g+ Y0 p* C continue;
+ A" D9 I+ `4 I6 p! s* [8 ~8 q( p }
: F4 H# F! y- d; D& T! k% } break; / S, p& K% ?4 R. j8 @
default:
X! O7 } V# T2 i $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); : ] \* q3 C/ ^% V! B
} * d% A9 \9 K( @% w5 q
}
8 l' f) r; v& |! X $field = implode(',', $fields);
& S1 R5 Y1 ?( h, A. _ } else { ' g& ~4 t2 e8 O& \' z5 U: y! m
return false; 6 F/ p' H( m8 Q4 X8 S
}
$ I7 c+ k2 G: ?6 C$ X $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; . t' D* B5 I9 ~% u4 T P
print_r($sql); ' u/ V3 O& F5 s- u
return $this->execute($sql);
' J) B. m- y2 F9 u4 t } 5 E w" | n2 v, X( f" R3 z
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
+ _. L+ o/ Y( ]* e
8 Q5 T" Y" k/ z' f% U$ [1 m6 k攻击测试:, Q2 _: G( Y7 E8 Z Q8 f3 O7 q
测试地址http://localhost* W9 r% m* l) d0 |* e! L' S0 e. `
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
+ U4 \1 ^* {9 A( {; o: R' a( T- h- Y# x& a' F+ ]8 p1 H
, C) V u! V: f5 e4 w, d. O \1 y7 ` R: Y) `
& ^2 g T' [" R& t
7 y; w6 t, [/ a$ G& j5 m) i! C& c" z
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|