|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
! x; ~8 \( B5 L( n, {% u+ B9 A漏洞作者:skysheep
7 C' g6 j0 R# {% z( q9 T分析作者:Seay
8 p( {" {6 w6 d) [( F博客:http://www.cnseay.com/$ b* Z/ S" Q$ T, Y
漏洞分析:( w, _( k d C& P0 f+ @3 U' D
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
6 a4 P2 d$ ?" ]+ A6 D( z, R) C( U5 g8 {* N% A+ h% r- W$ p
3 w$ l6 S* u3 a% d$ B
5 A* O& u/ e, p' N" o3 Y' J0 n
public function account_manage_info() {
V( A+ @9 Z7 L& h8 f! z; \* t if(isset($_POST['dosubmit'])) { ; X& Y# B# L ^5 T1 H$ ~' ]
//更新用户昵称 % e5 v4 o" @2 a) q8 G
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
: P! v( Y" D3 ?& Z4 H- ^ if($nickname) { ( G% b" _& s8 m
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
4 y0 g6 p' P6 t; [9 T; w2 ? if(!isset($cookietime)) { 9 v) O# w9 _' H) r8 x, c
$get_cookietime = param::get_cookie('cookietime'); " h( ~3 y+ D* ] }2 `1 T5 y/ R
} ; F9 f: c6 U" B9 w8 z
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); ' ?- V' w$ E0 z; o3 V% n5 l
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
8 I, `1 |8 W3 B1 n% Q8 l4 V/ p param::set_cookie('_nickname', $nickname, $cookietime);
& o% U) g" y0 s* L2 i }
6 T! H& C* j/ g& f4 `1 n* w require_once CACHE_MODEL_PATH.'member_input.class.php';
% ~1 T/ ^/ t, Z require_once CACHE_MODEL_PATH.'member_update.class.php'; * y& t4 I0 X" [* Z1 d- e! Z( V
$member_input = new member_input($this->memberinfo['modelid']); , j9 E5 A# V% I( H8 `
$modelinfo = $member_input->get($_POST['info']);
/ u7 h6 R) T" O8 P2 A" K2 D: y, w5 g $this->db->set_model($this->memberinfo['modelid']);
5 q. @1 T! y$ ~+ ~ $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); , n' W4 b8 |) \! F6 i
if(!empty($membermodelinfo)) { / j" f2 i+ K' M- r' ]; D- p7 L
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 5 L8 H2 X* T; L: l7 Q6 @: k
} else { : T: J7 o0 C" u
$modelinfo['userid'] = $this->memberinfo['userid'];
- a- x8 h" o; Y5 S+ p" v $this->db->insert($modelinfo);
/ r! x+ Q. ^8 P }
2 k' B3 c; v3 \2 w代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
1 x$ y4 Z" h0 b6 K+ ]在\caches\caches_model\caches_data\ member_input.class.php 文件中:
& g4 `& X& X6 e" l0 K" T
$ J$ s' N- U4 S6 Y# K" [ E$ F# f
, T6 G; a- X7 o& G" f; x% ^
* P6 t, }0 k- Z, ^) @8 Rfunction get($data) {
9 ~; Y5 T5 z d' H& T( q- Z2 [& y $this->data = $data = trim_script($data); " Q# t; z- c) r
$model_cache = getcache('member_model', 'commons'); / x! ?0 r' {/ j2 B9 ? @
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
H) g, ~, V" n7 Z& p $info = array();
3 n0 {) c0 L3 X0 f0 M- n% n; } $debar_filed = array('catid','title','style','thumb','status','islink','description'); . _" |; L: x. x- P1 C% l9 S
if(is_array($data)) { ; b* ]/ Z8 C$ I
foreach($data as $field=>$value) {
5 }) m8 }* H4 }1 G5 o5 u9 A: z. e if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
. C+ R6 o* T. D' l( {* [4 k $name = $this->fields[$field]['name'];
3 K4 g' N% B$ \2 b. O $minlength = $this->fields[$field]['minlength'];
u# k! a4 @+ G% j0 d $maxlength = $this->fields[$field]['maxlength'];
& R6 L- D8 R0 n' o+ E, p $pattern = $this->fields[$field]['pattern']; 7 A7 f6 [; Z0 a: Y9 Q! O- C
$errortips = $this->fields[$field]['errortips']; ! M* C' h1 D; x
if(empty($errortips)) $errortips = "$name 不符合要求!"; ( _) ~5 b9 L6 D" r4 d5 q: ]
$length = empty($value) ? 0 : strlen($value);
6 u" }# u" ^2 ?- ~8 o! L: i. m if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ( W0 z. x3 E0 B7 B# Y) \
if($maxlength && $length > $maxlength && !$isimport) {
! N( l+ x6 |0 e; F' a. b showmessage("$name 不得超过 $maxlength 个字符!");
9 e2 C4 u( M+ p8 k9 F" F } else { + R/ v3 K8 L4 |0 G3 r
str_cut($value, $maxlength);
6 S$ B8 [! W, d( t3 Y7 Y# @ } 0 \* A/ [8 o. o( ]9 y
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
! R: Q& F1 `- X6 ]4 l1 m* j if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); % Z4 j& n9 O1 {9 U- P) c: v
$func = $this->fields[$field]['formtype'];
. v7 C1 _/ g, z# [! P; U if(method_exists($this, $func)) $value = $this->$func($field, $value);
: F4 `! j3 K( }0 m $info[$field] = $value;
2 b3 B, {" O; H }
3 A6 Z# e2 r0 M$ ^! G! \ }
) O; h4 @0 o5 X return $info; 9 [6 {* {, {, t' y* c
}
4 o9 p6 P4 P" l7 N/ G+ K) o1 Strim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
2 J3 Z4 N1 `3 O7 j, `7 s5 d! j: e* w- ~& ^! l; M# I
再到phpcms\modules\member\index.php 文件account_manage_info函数
' C- r( }9 T. I0 x1 g) S- n过了get()函数之后。9 `/ D m9 w* w$ `' q5 z+ r
6 \( r' T; h. m
% H) _ |/ w. g: D# ~3 M. O$modelinfo = $member_input->get($_POST['info']);
; K. F$ r- L! M' ~; c $this->db->set_model($this->memberinfo['modelid']); . j: e/ U9 ~2 c1 n; d- A! `
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); & Q/ }( ` F) z& d4 x+ X4 j2 c
if(!empty($membermodelinfo)) { : j$ [' X- B3 z9 a+ ~/ Q# Y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
: H% ~# d) w8 K% A; h1 P) D } else {
' Z; V ~, p p% X直接带入数据库,update函数我们跟进看看% p) I* W# `5 x+ |! D8 w
/ K3 n' b# f' E: C: ?# }+ a$ E % M9 n7 { P! s4 @
public function update($data, $table, $where = '') { + Q8 K3 y& h1 \* s8 @
if($table == '' or $where == '') {
. L i7 [, C6 t- i# } return false;
+ q! u( U- r* F. X; C7 ~4 g. x } # p: Y8 Z' a; e: w7 L
$where = ' WHERE '.$where; 1 k5 J1 ^% m0 H
$field = ''; ' p% b& H/ @# W" b9 g
if(is_string($data) && $data != '') { * u* R* e# a# T3 D* D$ \
$field = $data; % `/ k" T& t" \3 a0 w* T
} elseif (is_array($data) && count($data) > 0) {
0 S$ o2 P" r: | $fields = array();
9 ]' w0 `, w9 B4 R. y$ e, ~ foreach($data as $k=>$v) {
; E9 S. {" F5 L; E h switch (substr($v, 0, 2)) {
) q0 a! t" q+ E l% w4 V2 W case '+=': ; p9 b: E. ], H. S* R `) g* W; e
$v = substr($v,2); 7 @5 a K. `; u) m; K, H$ @
if (is_numeric($v)) {
4 l$ q% C6 {4 b $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
9 d7 t4 y9 p0 S2 X, E( g } else {
! V; N9 F2 `" _3 ~$ ` continue;
# k$ u9 l' D' Q- z, q. J* Q& K }
3 O- M4 s+ U8 Z4 ~1 @ break; ) ?- t4 g8 [0 ]" V0 n( w1 @5 z! C
case '-=':
! i8 s) k) t! K $v = substr($v,2);
1 v4 ?$ o9 u/ ? if (is_numeric($v)) {
8 \, I' }3 ` j4 d0 u( L/ W$ z $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
. N1 U" {8 ], L$ e/ J' k } else {
0 P2 j2 u. X H( K continue; 2 ~9 Z1 Y# Y# z6 r1 v7 Z
}
; M2 h) a( m; V6 t break; . G2 i, ~5 v3 q$ ^# M' |
default:
2 V" r4 l p$ l' | $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 1 H; ?' I# {' H# J2 g
} # [: N8 k# N- H
}
! O, o9 e& \* Q; o8 L2 E $field = implode(',', $fields); : P- ~. C4 B X! f4 I
} else {
' U$ G$ @5 E Y7 F: O( x# v return false; ) `5 D* _! B3 o f
}
- |2 A: Q) }0 E; _) Z8 ?1 T $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
" u# y; P8 ?2 h9 \4 |* G; c9 w- R1 A print_r($sql);
6 q+ w% Y9 z5 q3 T& {& s return $this->execute($sql); # }) b. r% L/ s7 Z9 W
}
" x% _9 u9 ~" }/ j J! A l6 m从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
: J! }' R7 f) r9 H1 i C4 ~- z+ [6 w+ Y. |5 v) l6 j# n
攻击测试:; q) F; i. y4 g1 _+ b8 C @1 z, r
测试地址http://localhost9 B) C' m( f) ]9 M
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句" H: d* Y2 v, w% S) {! M7 `
' r9 i; b$ P e* ~6 W3 ^/ z % M: j( |0 _7 a' V1 U& M" o( Q
! h" F9 X8 r0 k9 b3 w( `$ W+ B0 B
9 e! v3 N5 Y0 i% z, y
' x- M' o$ D, J+ ~2 v5 J |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|