|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告5 b W6 I. c& c! q; F) i& l" W
漏洞作者:skysheep
( p3 H" d" g2 Y- s; _; A( D分析作者:Seay
3 P- p6 a1 ~0 X: b- z" ~) p- j博客:http://www.cnseay.com/
7 Y9 C* Z2 \: A# V. `' h7 M3 Y漏洞分析:
$ S' \7 {% w5 O 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
+ [: J) ^( L6 }3 _5 }! y
$ s9 k4 }. U) k3 F m9 P) L0 w: e! D) ~
8 M+ ]) ?( a% M% e8 b5 opublic function account_manage_info() {
( d/ ~2 r- _- c4 N; j9 a: n if(isset($_POST['dosubmit'])) {
" N h8 I1 q8 }1 {9 I //更新用户昵称 / N9 u# [7 r- B A- n% U
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 2 _. A7 R9 s* C- ~% o$ O) [
if($nickname) {
) ~- c2 P/ x: D7 v" o$ T9 K $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
6 H* z2 T# e3 i% a if(!isset($cookietime)) { ( \9 y0 s( O; P! P
$get_cookietime = param::get_cookie('cookietime');
2 t- }4 k" P; r3 w0 J } ]) x$ F0 D) f9 Z: [# c
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
0 q* o3 Q% w: S3 k/ j+ C0 ^ $cookietime = $_cookietime ? TIME + $_cookietime : 0;
2 u) x( S* V9 h: `& d& S/ r param::set_cookie('_nickname', $nickname, $cookietime);
" G& m7 G& r9 c9 @' G4 P }
, |+ t9 e/ F4 }% Q6 { require_once CACHE_MODEL_PATH.'member_input.class.php'; 3 b8 g+ n0 Y5 ~( N2 h$ v
require_once CACHE_MODEL_PATH.'member_update.class.php'; 1 Z+ m3 h$ L% Q L- X
$member_input = new member_input($this->memberinfo['modelid']);
. O2 {5 I: M( q3 ` $modelinfo = $member_input->get($_POST['info']); 5 e" E0 H$ E6 s1 Y0 C
$this->db->set_model($this->memberinfo['modelid']); y$ @7 G% S' k
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 3 \! M9 u: N7 l6 q6 Q
if(!empty($membermodelinfo)) { 6 V" f9 f- Y: O( L% D& I
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 6 o# V2 ]3 |7 s5 b1 k: Y# J2 z
} else { 0 d4 }" U( q& B; d4 Q8 \: u
$modelinfo['userid'] = $this->memberinfo['userid'];
1 I4 y& I) f5 q& A: [* |" A $this->db->insert($modelinfo); ) x) A: b5 T! S" O+ P! G4 {0 @
}
' m% O2 l! h3 G2 s$ z代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
5 ?8 n# J3 I* S, h- g; z在\caches\caches_model\caches_data\ member_input.class.php 文件中:: T z# T8 X8 t8 o
- s# q/ \3 X: x6 I1 Q
. p2 o: ~9 x2 x 2 Z- W; U! F' C5 C6 @( B! w
function get($data) { / T, A' m% ^4 a) s( f4 O
$this->data = $data = trim_script($data); + ~- C% z( L2 M" ~
$model_cache = getcache('member_model', 'commons'); ' d/ k! O; y# R8 k
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
/ v( {9 F7 ], w- {7 N. Z/ n $info = array(); . B: u$ S" q7 B5 |& T' F
$debar_filed = array('catid','title','style','thumb','status','islink','description'); # t! T( j Z1 b2 m9 S
if(is_array($data)) {
) s. W: s+ g5 n5 S/ [% R$ x foreach($data as $field=>$value) { 1 y5 _' O- V6 ^+ f' s
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
9 B' _5 m; Z+ a0 W* I $name = $this->fields[$field]['name']; 8 A# @) V1 C: F+ N
$minlength = $this->fields[$field]['minlength'];
0 O9 H: \+ Q! `% y $maxlength = $this->fields[$field]['maxlength']; , Z: Q7 ~" \( {4 l- s) l4 t
$pattern = $this->fields[$field]['pattern'];
3 \* q+ S3 a! m5 {9 H ^ $errortips = $this->fields[$field]['errortips'];
1 h2 v4 h, M, l4 a# c if(empty($errortips)) $errortips = "$name 不符合要求!";
6 l" w% Y) y5 w! O% Q $length = empty($value) ? 0 : strlen($value); 9 l0 @& f: _ k/ z
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
I) {: j8 v7 X- W- } if($maxlength && $length > $maxlength && !$isimport) {
: W- e6 |+ F4 p7 b showmessage("$name 不得超过 $maxlength 个字符!"); / W: f2 {! p3 E: l. @4 k
} else { & ^/ ]) s A4 X8 r5 J- G7 }
str_cut($value, $maxlength);
) {& d' g& E$ B6 t } - a. j+ L5 }" m! W1 U& t" W: B
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 3 S- V! B1 o7 `$ x
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 5 I, h- h2 Y4 U% H- }, C: I
$func = $this->fields[$field]['formtype'];
; K9 m+ k2 q& L if(method_exists($this, $func)) $value = $this->$func($field, $value); / a* z2 N3 Z# t* v
$info[$field] = $value; 8 P; W2 b7 P/ `- N5 W& j8 J( ?( r
}
- Y' U; M/ S: r6 q7 b# O }
" ~7 d* s$ m, k) w/ H6 @ return $info;
2 Y J ^/ I5 x$ [ }
) ?$ K& [: q9 E* L: V. p0 s' G7 Xtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,* V8 K& f& V& X: D+ F1 E: |/ d( s
- N5 R r* ^- {( e
再到phpcms\modules\member\index.php 文件account_manage_info函数
\ o0 q) y5 e过了get()函数之后。
* O: X+ A7 o0 m3 ~! Q' d, z% `& _5 x8 }
7 d! m9 {* V; ^' ~4 M$modelinfo = $member_input->get($_POST['info']);
3 ]# D% L# D: r9 b& W8 ^' L $this->db->set_model($this->memberinfo['modelid']); 4 v; o: V6 R* o' O7 C
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 1 J! N+ |; [% r( C8 s
if(!empty($membermodelinfo)) {
% i$ n" z+ J) O $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
0 W8 ]. q) |4 H } else {
% G% i" d0 [5 w* B7 u+ Y! t直接带入数据库,update函数我们跟进看看
% U+ E7 O+ R/ b% }# y( c& E. R, d W9 x. F% t
0 L2 D1 h. r9 _1 Q
public function update($data, $table, $where = '') { ; M$ E' k) P& G/ ^ J
if($table == '' or $where == '') { # n# @* F( i& E3 g5 s8 Q$ x
return false; ( g5 `5 s( ^. s
} ' {! q$ @ r; I0 P7 G. J
$where = ' WHERE '.$where;
1 L/ z' ~9 ?9 T/ s4 B $field = '';
, a4 V; Q9 Q1 }1 ?2 W7 _/ J if(is_string($data) && $data != '') { ; Q0 u; j5 L- g0 e- L. `" X
$field = $data;
6 U+ ] H# z7 C. H6 j& P } elseif (is_array($data) && count($data) > 0) {
! R) c2 h; Q: r! } Y" P $fields = array();
# B% P. w `) u8 U foreach($data as $k=>$v) {
' o! }/ r( |. X! y g6 b switch (substr($v, 0, 2)) {
9 Z% E# i! k6 Z2 b; ]- G1 C5 `4 } case '+=': / p4 w1 f) @$ G/ z, x3 W9 X3 {
$v = substr($v,2);
0 j* P' T. D4 M' W if (is_numeric($v)) {
8 j% ?0 W8 [; x% W) @ $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); ) h1 s( ?6 B% N- i8 F
} else { # y4 k" T" t& m1 ]" P7 w. p) V5 V
continue; 3 o7 n' d e3 J
} / {( r# J8 V3 t0 ?* G) w8 q
break;
6 _( F, r# i; Q# @3 } case '-=':
* j G1 Q( L8 v5 @5 V/ V8 o7 [1 O $v = substr($v,2); $ _$ G$ s7 s/ l1 | N; o }
if (is_numeric($v)) {
; N' y5 R, z& R0 Q& b8 f $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
& Y% H; F2 P! O/ t* c$ l } else { " z4 @- D: Q2 @! ^" q5 w
continue; ! t3 t9 [% u" x: ^1 K" {
} ! z' v- K: W* c+ L
break; 9 T& C& U' X3 K; \
default: $ h1 s8 [) ~9 H
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 0 m. N0 d' S: V; ~& J+ m% I
}
' B: e2 O4 }7 ^" ^2 ^- j ]. e' ] } 5 }# X3 c8 ^" }( w" S% P+ T
$field = implode(',', $fields);
0 I7 h' z2 H c) r$ U7 L3 Y2 A } else { 2 Q* ^' Z% o/ m! C3 P' _0 b! w
return false; F+ E+ X/ @+ E* ?3 K: [% k4 Y& Y
}
2 y" s2 C3 D- j' W $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 6 |. Y( g' ~0 ^/ l! h& l
print_r($sql); 5 V5 o: D- Q) i0 @' c0 O% z
return $this->execute($sql); 0 E3 i5 E: x" k1 V8 n
}
( S, l+ Z9 I% L# x t2 |- Q从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
, |2 ^; K C# U, Z' R) A1 y
3 Z" Z. Z4 y$ ^5 O攻击测试:7 q' y9 o! q0 e# N b8 a
测试地址http://localhost
! X2 \0 D; ^: y+ H3 s5 ] 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句# A; O) J, \- U" R& B' Q6 B
: ]. Y, |9 g" `* t- ]1 ~" Y1 y 0 s8 {& w4 a6 _0 F! w8 k* J
( h" X2 R; O k1 z
3 u1 j5 G E$ U! U: i
9 \8 r) G8 c/ U6 {8 d9 M |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|