|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告* g' s: t9 l) v1 G' [' J
漏洞作者:skysheep
' J# }! O# }) L8 I分析作者:Seay
& ~- \9 O0 L2 H/ o5 ~1 u- a博客:http://www.cnseay.com/
! F6 W5 k7 }# G1 H1 X! L8 G漏洞分析:
& z/ y$ E- R0 t: g! e6 X% O( R+ s3 c1 K 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。! }% C% x$ c: Z. I' q
9 b2 J9 b( Z" D w' t( Z" Z
2 c2 c2 z0 ?. |
, c F- V3 a, p7 Bpublic function account_manage_info() { 2 R# J4 H& Z0 ]9 |
if(isset($_POST['dosubmit'])) { ( W. w, y1 ?% x
//更新用户昵称
7 y% T/ R+ b# E, o $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
$ S1 B9 ?- Y0 q! W/ s if($nickname) {
( h8 k+ n( i8 K7 k $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); % o3 h- d, i2 A! @4 U9 Q% A
if(!isset($cookietime)) {
. `) I" v7 d3 I- A' A4 c $get_cookietime = param::get_cookie('cookietime');
0 j% R6 K1 T! M5 z8 p L2 o% ~, ? } ( Q0 t. m3 Y9 u+ y, e2 P2 o3 f
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); ' u! R7 ]* U- W8 J1 w& W
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 9 N+ p9 _+ z5 w$ s8 j& C
param::set_cookie('_nickname', $nickname, $cookietime);
" c* O1 l5 V' \1 W }
0 O" k/ Z. \: y4 w require_once CACHE_MODEL_PATH.'member_input.class.php'; 2 ]% Q( h( x3 d" u
require_once CACHE_MODEL_PATH.'member_update.class.php';
. k9 ]! o- N/ ^3 a" x. n $member_input = new member_input($this->memberinfo['modelid']);
" \* Y! ^: e5 G $modelinfo = $member_input->get($_POST['info']);
9 T1 b/ T: ?4 W3 A $this->db->set_model($this->memberinfo['modelid']); 5 L1 Q+ b* Q$ t& x& u G
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); * Q, h. ]5 C# u1 Y
if(!empty($membermodelinfo)) { 4 H: c7 _+ a6 ]: G, [7 b
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 6 t8 y" Q) O9 N, f3 [* j
} else { # G( q$ @+ n" h
$modelinfo['userid'] = $this->memberinfo['userid']; ( X, V0 [) K+ W0 y; T! L- R
$this->db->insert($modelinfo);
1 {2 I* b9 H4 w0 y1 Q4 ]# i6 s } - k$ R$ h8 A0 _1 ?) P5 e, @
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,2 h! T# k& g9 V
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
3 V; H2 q+ U5 [/ ]% `# Z; M2 |& A
& e1 h- E$ v+ U; S7 @/ b: v3 |
. T$ t r K' o: R
function get($data) { ! s4 R @# }- I" J" k2 z+ D
$this->data = $data = trim_script($data);
0 W; _( \; O2 U8 d% k( w $model_cache = getcache('member_model', 'commons');
8 O1 @+ H2 b3 I' p2 O0 X' l $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
- u; D% a& \& E W9 Q [ q2 p $info = array();
6 ]8 h: \* Q4 E' e- R: b $debar_filed = array('catid','title','style','thumb','status','islink','description'); 3 g6 F6 t5 ]. u7 I& \
if(is_array($data)) {
* u8 o. D# M. K1 l- B9 {$ M) O foreach($data as $field=>$value) {
7 I) k& s* o! t" O% M if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 5 ~( f2 `9 ^( W8 h
$name = $this->fields[$field]['name'];
7 V. o2 f. @ }: S: c $minlength = $this->fields[$field]['minlength']; M# ^* C' i# l2 E; E; \
$maxlength = $this->fields[$field]['maxlength'];
# @6 R) k2 T; q' m" i: [ $pattern = $this->fields[$field]['pattern'];
3 l5 n8 N& d4 ?6 o5 ?6 P $errortips = $this->fields[$field]['errortips']; 0 Q& i" [0 l) y: e2 @
if(empty($errortips)) $errortips = "$name 不符合要求!";
) C; M h/ L9 k $length = empty($value) ? 0 : strlen($value);
9 R8 g5 q7 F( c% l3 T9 A if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
0 |8 `4 R: s" R+ a) \4 r if($maxlength && $length > $maxlength && !$isimport) { & G# ~4 e4 {( w3 T* C
showmessage("$name 不得超过 $maxlength 个字符!");
k; m4 V1 D4 H( q4 A+ R3 U' S5 x } else {
/ R! t8 ^7 B) F6 h6 Z6 P str_cut($value, $maxlength); 9 n$ D6 L0 s' d. @" S$ L
} " f8 R: }5 y5 Y# q
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); & W# P8 I+ K! T
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); & ^6 J% [) q D. G$ P: y7 N( Y
$func = $this->fields[$field]['formtype'];
4 d/ ]$ |# ^/ \ if(method_exists($this, $func)) $value = $this->$func($field, $value);
( \% J+ }' L, b* x $info[$field] = $value;
3 ]1 F2 Q, f$ Z; e4 C8 j }
) r+ f( @, H: L- m) q, \/ R } 8 H6 F+ H" w7 y
return $info;
5 C) p& R2 Q8 Y& {7 t a } # u' i" x" V& p( |7 E% V$ r
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,1 S, H: _3 k8 G3 i5 @$ ]
. C1 x! Y6 p6 a0 d# l再到phpcms\modules\member\index.php 文件account_manage_info函数' P0 t: M0 V# K5 x
过了get()函数之后。# t2 }4 _( x1 T) y5 M% Y
& j- Y' D+ D8 ~5 x" ?5 v& H
# P" i+ A" Y7 }: K5 |
$modelinfo = $member_input->get($_POST['info']); 2 Z1 U) _% ]* c# W! K
$this->db->set_model($this->memberinfo['modelid']);
# B8 W }, Z4 ` $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
( X+ \1 U# T% w8 D# P6 g" } if(!empty($membermodelinfo)) {
K0 Q/ G6 x8 L v( K% \ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
; S& R w# W( e/ ~* E1 b } else { 3 z8 n$ z. c2 p1 u7 J. c( y% @( {3 @
直接带入数据库,update函数我们跟进看看
7 ?/ q7 ~& z: p0 U8 \
0 n, h* ~7 N& X0 y& h) q; \
" U: u% j+ n) s) s k9 q2 W$ v1 o+ Gpublic function update($data, $table, $where = '') { " V* z$ K* Z& l, W5 n: ~
if($table == '' or $where == '') { % ]! [& n* `5 |6 B
return false;
- w" J# o( d% w: g% c% H* }! j! n } 4 ~9 @4 T* H% V0 [& w
$where = ' WHERE '.$where;
) O8 c) r8 t) ^! x W3 S! j $field = '';
' l! v6 p9 h& w# |9 ]$ ^1 f if(is_string($data) && $data != '') { / V0 Y- X' Q7 z+ Y( e- L5 X+ G( E7 [
$field = $data;
z9 ]7 o4 y5 K6 d6 W b+ o } elseif (is_array($data) && count($data) > 0) {
( s% n; W+ c. m! C2 p6 V $fields = array();
7 t8 T$ ^6 x: x/ E M$ @- Z' L foreach($data as $k=>$v) { 1 f% O' B) d e* B
switch (substr($v, 0, 2)) {
2 Z7 N8 E/ L* Y case '+=': % ^2 @* ?5 _+ k# {
$v = substr($v,2);
- f: k3 T4 w$ M" m9 e6 z/ L if (is_numeric($v)) {
1 y F# V- q% z$ v $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); O F; B5 x5 p5 _) b+ K5 M
} else {
# h0 Z7 K* D6 k: C continue;
8 t* z4 y& |4 h, ` }
' I6 Z" ^! T: S' c7 W$ X break; / y) @3 }3 a% G6 Q% J8 P( V
case '-=':
, P$ A. I" _2 S. R" L9 E+ { | $v = substr($v,2);
7 M+ [ J- e& e% Q if (is_numeric($v)) { 0 U7 J( P- `5 i, Q* w a' k* W5 L. T
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
% k5 g% u, m, R$ i9 Z: d } else { ; N# T& i' S3 _) s1 u
continue;
6 L& L0 J' }% ]6 ]% O& b6 t } ; d# h$ |1 Z$ \6 W5 V
break; 0 P0 z6 f& `% V3 p
default:
; h4 |$ L5 w5 ~8 W- k) g. o, e $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
6 C7 B( i: @) d0 [# K } ) ?7 ^. ^) s! t. M; Z0 \
}
4 g% |, v. u) T6 e9 w $field = implode(',', $fields); : b$ i3 k2 S: {7 q7 k0 E* H9 k5 M
} else { 2 J+ s: e3 X" ~' j
return false; % v4 s4 k9 c) V2 W5 C1 _
}
" ^ Y, Q3 \: B $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
" A6 F- T, i2 j, L7 I print_r($sql);
2 ~- \' G- c- {' U9 K return $this->execute($sql);
5 ^/ U- ~; V. y4 n } * p' M1 D! P4 J6 b' q
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
9 n Q* @) M( B, M7 p; ~8 A
1 V3 q% r B O# ^' }0 G. [攻击测试:
$ u& [+ F S( A测试地址http://localhost1 Q: s; E, N+ ~5 R# N
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
# y1 ~) Z+ Y! M5 b; n2 q# [# O; O4 s+ S# s) o& d
3 C2 Z3 O7 W4 G, }' P, c4 E: |) M' h: B7 S V
1 G6 D+ o5 E) r0 A
* c- ^+ z9 k" E* c
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|