|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
3 T6 \3 }( p3 o0 V, @, y漏洞作者:skysheep
+ ~. s1 A4 D _. l分析作者:Seay/ [ k$ k" N5 @! f
博客:http://www.cnseay.com/
6 O& y8 y! g* F# J3 S漏洞分析:1 y: [" @1 ]3 [7 N
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
: v' N9 t* N3 Y6 h& g, `" B/ Q
8 g8 x j8 Y2 B7 i& {3 {
. y9 R, J( d0 o+ p( w 2 Y+ T! Z4 n; d% d/ R. I
public function account_manage_info() {
. I6 i8 `) f* O8 @: d if(isset($_POST['dosubmit'])) { 0 y8 a/ \& s3 ]8 V
//更新用户昵称 . P1 }- ?+ h* z! i
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; + y* E% z, l4 j$ B+ F
if($nickname) { X+ S, N" c$ W7 I
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 7 l5 P6 _% X3 ?6 _( p0 k
if(!isset($cookietime)) {
+ E4 U7 E# ]# ] S& t6 w8 ]; I $get_cookietime = param::get_cookie('cookietime');
' k- f1 `" a' ~: E9 k }
- v/ A5 p. S. Y \ $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); ! F" g! m6 J' k* e/ W U1 L
$cookietime = $_cookietime ? TIME + $_cookietime : 0; & q' {8 {: R2 p
param::set_cookie('_nickname', $nickname, $cookietime);
7 _# K! W$ b* X0 Y, G }
% u/ j( ^3 J, w! @ require_once CACHE_MODEL_PATH.'member_input.class.php';
# [/ U$ @8 O: ^, q# r require_once CACHE_MODEL_PATH.'member_update.class.php'; 4 `( r- R- x- D! ~. H5 D5 L) U, V
$member_input = new member_input($this->memberinfo['modelid']);
; m- D. f! d: p. [1 ^/ o4 S ]7 B $modelinfo = $member_input->get($_POST['info']); & E% C/ S0 ], a
$this->db->set_model($this->memberinfo['modelid']); : G' _8 l5 v* |0 D! C( \
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
+ m" N6 e6 W; F1 a8 U3 o2 y if(!empty($membermodelinfo)) { . N6 D& f/ g# s" y s* n, a
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); h4 x8 c6 W, B" o$ T- Y' |: P
} else { 8 F. ]. z+ C* W+ b" m: n
$modelinfo['userid'] = $this->memberinfo['userid'];
: o" z$ G. z4 H }' v $this->db->insert($modelinfo);
$ A' t" Q/ G7 N8 n3 K9 X# W% F }
, Q/ J% I& H9 P; ^/ B4 M/ |代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
$ Z4 T, c# c" B" m2 M; [在\caches\caches_model\caches_data\ member_input.class.php 文件中:2 s3 ]# r$ I2 j$ |
$ M9 V) c( T) x' [' K5 m, D+ T5 u4 o
2 y8 a! e1 X: ]2 Q$ R
function get($data) { - x; N$ P; Y9 N
$this->data = $data = trim_script($data); - m' z8 H! s5 ^, n* o8 ]; `
$model_cache = getcache('member_model', 'commons');
# k& r/ L( d$ p5 X, h# J! _; v3 {" o $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
6 d E8 T# T/ u& o, u) g $info = array();
. @) G. j, G: L( H# L& B $debar_filed = array('catid','title','style','thumb','status','islink','description'); 1 _8 \2 }( Z5 L+ W: T
if(is_array($data)) { . Z+ {$ r$ [& g6 D/ H8 L3 S
foreach($data as $field=>$value) {
5 p( F' Q% u6 I5 O0 k' o if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
1 q. U; G4 Q+ B8 ]5 z- p+ K $name = $this->fields[$field]['name'];
0 V; i1 e! ~ m' j# z5 R* E $minlength = $this->fields[$field]['minlength']; " Q; G2 e: I4 d# O# V, L
$maxlength = $this->fields[$field]['maxlength']; 3 K3 B% M: T4 p! u8 M7 r0 ]( F i
$pattern = $this->fields[$field]['pattern'];
i) G1 }* n% C0 O) D0 S2 w$ y( c $errortips = $this->fields[$field]['errortips']; ( L8 t! J4 t6 V$ f5 {" t' Q
if(empty($errortips)) $errortips = "$name 不符合要求!"; + X [. Y- A P
$length = empty($value) ? 0 : strlen($value);
# o$ H+ o5 l- R if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
z1 ?2 B8 ]6 X4 f( R6 `1 T2 W if($maxlength && $length > $maxlength && !$isimport) {
. n9 B# ?+ l0 k7 }3 _ showmessage("$name 不得超过 $maxlength 个字符!"); ) S; G5 g1 B! A; f3 \: A
} else { 5 X: L( i2 o: B, u! ?! w
str_cut($value, $maxlength); ( r9 ~# _$ S/ ]5 u
} - d- g" D2 p e5 t
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 9 H+ \# z: j1 w" o& t
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
7 O0 U* D7 O. l $func = $this->fields[$field]['formtype']; / V4 g& A; o9 Z, t. A2 t7 B- f
if(method_exists($this, $func)) $value = $this->$func($field, $value);
E7 n3 a% D& e, { $info[$field] = $value;
+ b& i2 N9 ?! u) S! N }
# \, b# X7 V! }" i4 M$ |. B( P& n }
) @6 V' c7 [8 | return $info; $ e0 f) P' p# _
}
: y5 |& N- D% v7 }trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组, l% |4 ?" e) Y' a2 M- }" S
7 ~+ `0 B7 K* S( L& \
再到phpcms\modules\member\index.php 文件account_manage_info函数
: W- H, P5 E) F) i) L过了get()函数之后。
8 n: e- {; _5 ]) V) t4 u7 ]/ Y: @0 |+ X% p" M6 k3 _& | e
1 l: u# I6 z$ E5 |2 E1 V$ z
$modelinfo = $member_input->get($_POST['info']); 0 g" W6 a! F+ g' m2 H
$this->db->set_model($this->memberinfo['modelid']); & {5 I, V- {8 J+ z5 b
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 5 [& G; `; e+ k, o& I
if(!empty($membermodelinfo)) {
S K$ M: W8 U $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ) h9 F. ~ P2 \% z$ E3 W# ~6 J
} else { 7 P' {' x. b* |! J$ k
直接带入数据库,update函数我们跟进看看
! t) c* V2 a4 {* E' Y1 a
. z5 i6 z2 F3 y' }" g: w
" q) B l ~0 X3 Vpublic function update($data, $table, $where = '') {
, f& C/ `. C, X- S: h m if($table == '' or $where == '') {
2 m1 r, Y0 ~; G7 r; r return false; : E. N- W: K; A6 T2 {
} % U2 L4 p& k5 G' ~
$where = ' WHERE '.$where; ( _2 J+ f% {7 b! U0 q( x" _
$field = '';
( a4 C1 `( s: m2 b( F if(is_string($data) && $data != '') { . r7 l- b! s# a' _ e3 N
$field = $data; 5 O4 v. Q" z' t1 M5 S$ V1 x5 d
} elseif (is_array($data) && count($data) > 0) { * b4 A+ F4 J8 n B9 C% b" `
$fields = array(); : P, s- z$ q8 W! v1 A( V
foreach($data as $k=>$v) { {5 ]6 ]1 v* P! O3 X3 [
switch (substr($v, 0, 2)) {
6 Y( B. x# I9 ]; \2 t7 J case '+=':
6 s' X+ [/ ?/ [- r. S $v = substr($v,2); 9 ?% }1 X6 S( O7 z/ g( {
if (is_numeric($v)) {
$ c5 ]! p. D; k+ ^# f$ Z1 L $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); % w1 U& ~/ O% d" b
} else {
; E- s! l# {/ |: v5 p0 \5 N5 ^8 b continue;
& K% M" n+ W) p( q/ G% t } ! ^; `+ b" d; J8 n
break; ' y5 H. a+ I) J5 \3 }' O# X
case '-=':
3 G! Z) c; f4 I; A8 U $v = substr($v,2); * ?! U; ?; A) R8 T, D/ J/ L) g
if (is_numeric($v)) {
% [8 K; ]6 Q0 _7 [( x* i% u! o $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
- s1 i9 z* i7 p, o% H! K/ U } else { * h) m0 T$ X g$ f. ~
continue; " Z( N$ H+ A6 J8 f N1 H
} 9 ^! C; K7 F: _5 X- m
break;
# l) A& r% R* L# F# X9 T default: - R8 O$ G0 I% q3 C
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); $ t* n' C8 O+ ^) O+ Q9 q8 N% ?0 x
}
3 u( ?2 n! ]# j) P& `; q }
) \2 a$ S0 R s: P $field = implode(',', $fields); . d4 J+ m% f% ?0 w0 G
} else {
8 Z4 n; d$ e3 `5 L return false; G, O, `) A: g- [/ z
} 7 }- J& K2 M j+ y
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
. D% H+ S3 E, z" E print_r($sql); # p% j. D( L( ^! _! k/ f
return $this->execute($sql);
! U( s& i5 U/ S! s. ] } $ a3 y( w% K( O9 F
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
0 f% ^6 {+ ?9 h. _4 f* W- z
; \5 R+ p! ^" }5 o6 |- t6 Y9 v攻击测试:, C W: c. F! X& p9 {( w
测试地址http://localhost" s8 `( B' H# {; s& J: N( ~
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
. o; Y- ]6 ~' D, Z3 R
k* b( Y- l' H2 s" Z c, J 5 w- N: q, i& g
8 a/ n9 N$ q9 M+ W: e: c
/ m6 y/ ^. ]: w( B$ J# b% J! ~( A6 w. ]$ S8 n. ? ^
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|