|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
* M, F! t( F( `8 H7 q, a漏洞作者:skysheep
: Y/ m7 ~# r/ K- K. _# E: s& L9 A分析作者:Seay9 }! c6 R. u2 w8 ^
博客:http://www.cnseay.com/9 ~' Z3 u: w2 o
漏洞分析:
. n% [: X* }2 Q5 @6 j; P 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。7 b& B8 W% d s1 ~3 T: j
5 @% s% b; H) P3 J4 n& x% A4 t" E' Q$ y. F
' J5 k- W3 s" m/ D9 z
public function account_manage_info() {
/ Y% B# B, ~7 {- U! b' o3 M3 a- Q if(isset($_POST['dosubmit'])) { & A/ T; }* n! U4 q4 y
//更新用户昵称 & j5 Q8 a O$ z1 h- d
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; / o$ y. H5 i( W; g# q/ K
if($nickname) {
7 g% U5 x: i3 B% v $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); " m! f( Z% S2 W/ W y7 }
if(!isset($cookietime)) {
4 u1 N( J: b* K $get_cookietime = param::get_cookie('cookietime'); + ^ u2 q3 J) c; r9 t8 G( m
}
- R5 N0 R. P" z $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
" H4 ?: I* {0 \, d- V" \8 A $cookietime = $_cookietime ? TIME + $_cookietime : 0;
! L: t2 }6 N( s param::set_cookie('_nickname', $nickname, $cookietime); * Y+ F) L/ |6 e5 ^9 }* I
} 1 ^9 b, T# O0 B# ]# W
require_once CACHE_MODEL_PATH.'member_input.class.php'; C3 }2 n2 A: S+ @
require_once CACHE_MODEL_PATH.'member_update.class.php'; " N' s3 O. T: |# X" d. h+ ]
$member_input = new member_input($this->memberinfo['modelid']); 8 |9 L4 |9 Y; G& O# c" d" C
$modelinfo = $member_input->get($_POST['info']);
( l1 K) G2 c( @# ~4 u' w! n" ^9 m $this->db->set_model($this->memberinfo['modelid']); , E3 E4 [4 j& T* ]
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
, Y m t: G. _ if(!empty($membermodelinfo)) { " L T; Z7 @5 h; X
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
* s8 k( g' ?" R! V1 f } else { , \% ~% W' J# X1 w5 ^& `
$modelinfo['userid'] = $this->memberinfo['userid'];
- Z7 d- ?7 c) ~" q) \) t $this->db->insert($modelinfo);
$ H& s7 s; m4 T) p: A } 6 Y; Y0 p _; l7 h5 ~" D7 v+ P# d
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,* G7 t: z6 s( Q4 S
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
( {& n2 ?5 x5 @8 @ R, l8 a" s
) O, O, c, n' `, U; d$ {
% l9 s' n2 e; N5 F 2 b/ c# ]9 m% n2 M
function get($data) {
9 L1 f" n6 H+ X8 N- U2 ~ $this->data = $data = trim_script($data); 4 a F0 ^2 P* y) S# n; N# ^
$model_cache = getcache('member_model', 'commons');
) g5 k2 J2 d5 A& p5 |6 Z. Z $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 0 p. Z) \3 c. {: `* f
$info = array();
" r" p* l! m7 [ $debar_filed = array('catid','title','style','thumb','status','islink','description'); * y/ J7 r$ D% `7 z) `' X
if(is_array($data)) { - P+ c- p0 p" x5 Z" n/ _! L
foreach($data as $field=>$value) {
; d/ U% j1 y9 u& x" u( r4 k if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
?9 o, u4 ^! M $name = $this->fields[$field]['name'];
. d$ P" V8 }+ H# ]/ p $minlength = $this->fields[$field]['minlength'];
( A0 T. _, s7 A" M2 a# u $maxlength = $this->fields[$field]['maxlength']; 2 L3 m: m6 T0 h$ u$ A8 g* ?
$pattern = $this->fields[$field]['pattern']; 5 E! Z/ J* C- }+ X7 Y0 E) ]. F
$errortips = $this->fields[$field]['errortips']; 6 r) H# Z9 z( y# E" _) N4 M
if(empty($errortips)) $errortips = "$name 不符合要求!";
, ]% |$ D8 ~5 ~$ q& ]3 R" N; y' h s $length = empty($value) ? 0 : strlen($value);
- L) _1 f1 A& F/ {& j$ F# T if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 7 o: R6 O0 p' y/ N5 H4 ~$ N5 E6 P
if($maxlength && $length > $maxlength && !$isimport) { - J' m) |. l9 R) O3 b/ ^; ^$ h
showmessage("$name 不得超过 $maxlength 个字符!");
" `' l) k. q$ E/ a, V6 S } else { - c. k/ d1 f$ f2 G. ]5 |
str_cut($value, $maxlength);
8 M, z6 a. R5 A V* I } : y* ?; {' z- N7 `' D: m" Q
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); & _ ?2 W/ J- @: y1 c/ \
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); + R: o; o5 w& h) ~5 V- l
$func = $this->fields[$field]['formtype'];
5 d7 Z, p7 r; H9 L# b: @1 o6 \ if(method_exists($this, $func)) $value = $this->$func($field, $value); H: I- @9 O# G9 C
$info[$field] = $value;
7 H3 X7 G) S* T; @# S }
/ D, h) ]% ?+ z' a } $ o& i" R9 s3 L4 v7 W, ~3 R9 [# ~9 R+ u
return $info; 1 M( ~. X) j7 B) `
} * j. o. t J$ q& K& s6 f
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
- H9 ^; Z7 o& ~3 ]) F) M
* f- K" z5 Y, d7 _' \2 e& e再到phpcms\modules\member\index.php 文件account_manage_info函数
* H& b! H7 }; ~" ]过了get()函数之后。/ v* p. W& y: W, Z- m# l
' {. @& m2 q% m5 @
- L& i* q5 x9 J0 I
$modelinfo = $member_input->get($_POST['info']);
. ~1 U& J- }1 O $this->db->set_model($this->memberinfo['modelid']);
* h% R+ l! u, G. F" Y3 W% n j# [9 r $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
) M4 n0 H' x" L% Q5 z4 T0 _- ^1 G* C* [ if(!empty($membermodelinfo)) { , X% t2 s+ Z% F8 w* n5 d, j
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
" _ O9 d5 t% i/ }9 g2 Q0 m3 C } else { ' ]& H- q' d0 H9 d2 k8 T: L7 ]
直接带入数据库,update函数我们跟进看看' {. z3 Z' _6 {1 r+ r
! a' S# {6 {" B% L/ I3 I# f& w/ M' w
+ b0 W, ~- F# epublic function update($data, $table, $where = '') { % n% Z, r1 i1 D1 B0 D
if($table == '' or $where == '') { ' v4 T4 ^% _& i7 E4 ]" G9 [+ D
return false; 3 U$ R/ c; j% u$ z
} 0 ~: [9 \) Z x* \
$where = ' WHERE '.$where;
5 \1 h2 X& a) {, G& S2 K5 I, h $field = '';
( C+ T1 Y$ A V8 r& Y. n if(is_string($data) && $data != '') {
, q4 F5 b& \ c. U0 a) b $field = $data;
( D! c) K' B7 O9 h' u( C* C3 [& j } elseif (is_array($data) && count($data) > 0) { ; }. @# k5 T1 O* Y
$fields = array();
' a- j& p" @' J; c! t6 s7 _ foreach($data as $k=>$v) {
% j6 S' P0 d3 p- B8 I% J switch (substr($v, 0, 2)) { 0 w% Q+ q7 v9 r; T6 X3 V6 t
case '+=':
! m2 l; e, [5 Z- A $v = substr($v,2);
0 d; v9 Z; `3 G) _! }& I if (is_numeric($v)) {
8 o5 ]% A7 k* c5 o H $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); ( n7 j8 d$ p: A; {) q
} else {
0 _$ r: \7 T: k1 y$ X continue; * I# n% W' O4 Z! I
}
7 e# f: B1 A6 L2 ^ break; 4 n" \8 L: C0 o! z4 x% w, |2 N& L
case '-=':
3 `; }, F8 _9 M/ N( c5 O $v = substr($v,2);
! V# ], M6 m. g* T. Q$ T* Z if (is_numeric($v)) { 0 r/ P0 G( T4 \9 _, @9 {
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
& ~' l* ]3 Q+ X( ^7 N6 ] } else { & G0 L% `0 o4 H& K5 A0 V- Y
continue;
9 r$ L7 L6 E8 D( w% W# N }
* n* ~( |1 E# p1 h8 z7 c3 \ break;
$ _1 k/ [6 ^7 a( ?; G default:
7 B0 \' X6 t1 a. G/ {$ ] $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 2 f( f0 ]1 x; S0 w
} 2 R4 u0 h0 y* f9 x
}
" K, j" A, P3 y1 A$ J $field = implode(',', $fields);
' H4 i' m" q! z3 e1 K } else { " j& k# X& G8 O, v; G+ i0 s
return false; * g2 d4 M, t1 Z z7 V1 P0 p8 T; a0 k2 |
} 9 Z, a3 z# k9 j6 ]$ [
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; , P( R+ T* ?' a6 |, j( N
print_r($sql); ' @6 v# O9 ^ S( W, z% m
return $this->execute($sql); ) ^# q2 ]* b$ c% Z8 h) g: u. @
}
1 a4 n5 z8 y. y从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。8 Y0 U$ X4 D8 h6 I' _& Z
9 ?1 A8 b' o, O' N& d+ R9 `/ w攻击测试:! j) k: ]2 X% M6 {6 t' _" e0 x
测试地址http://localhost5 k* |: f* p0 k
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句 d1 ~ [% t3 I3 c3 _* m
R9 t2 ~' d6 C# B & d; J E( j1 F2 f0 U* d
' b8 E/ Z* v( `# g& o9 A: B
2 t* F6 B( J* f" m, k& Q3 k4 k6 T3 }2 b3 f6 D; M8 w; @- Y" C) z
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|