|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告7 b! k+ p- J7 m0 y" b
漏洞作者:skysheep# ]- v% \3 `4 u5 B0 A& \) d
分析作者:Seay, w, [$ t; D1 w6 d" c5 J( R r
博客:http://www.cnseay.com/
7 ?! {. Y1 W- ^/ B漏洞分析:
6 V) Y) H2 d8 l# F 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。; z$ [* h7 |" e$ O2 Z. S4 @+ e# F
: M+ o/ ] o4 m" d2 @" V; K8 |2 E- E# b$ p+ D0 f
( X0 u% a ` Y8 b# O
public function account_manage_info() {
" i& U' C+ g; b if(isset($_POST['dosubmit'])) {
9 v3 b& l- e# Z; i1 C6 z# F1 B //更新用户昵称 % D6 {; X' ^+ D" e+ I
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 4 D0 U4 w' ^1 K: J
if($nickname) {
( @4 p0 F/ [) {+ `/ [/ n e" ~, M9 @ $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
/ M2 x0 R/ J! c, q% s9 e8 | if(!isset($cookietime)) { 9 {2 @% e; @+ }6 P
$get_cookietime = param::get_cookie('cookietime');
! l$ X( u1 U/ R3 ~; F } 5 E5 o- d0 W. @2 X+ J, y6 D
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); $ h6 s3 H3 g9 C9 }
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
& i! V9 }0 o0 ~ param::set_cookie('_nickname', $nickname, $cookietime); 0 [" q2 Q2 t: o2 j$ |5 j2 ?
} ) O# N" P$ g$ [: R i- w# C
require_once CACHE_MODEL_PATH.'member_input.class.php';
* D7 k/ K9 ]3 n- v1 c require_once CACHE_MODEL_PATH.'member_update.class.php'; 4 k7 X8 O% X" [4 }2 M
$member_input = new member_input($this->memberinfo['modelid']); 6 K; |9 U/ W" v8 U$ T ]5 R) I. K
$modelinfo = $member_input->get($_POST['info']); 7 k. E. N' c5 V8 E' d z
$this->db->set_model($this->memberinfo['modelid']);
# U2 D7 z; a0 U$ l6 t $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
8 Z3 w3 I L8 ~, K6 l8 J: n6 a if(!empty($membermodelinfo)) { # A5 t8 |- u) x4 C; | T6 V
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
' o" U( B7 h: t- i0 a4 O# L) r } else {
; A, M) F* z& ?0 D9 I- ?. X $modelinfo['userid'] = $this->memberinfo['userid'];
9 r+ [& V& v4 Y( u: ?0 l+ _, v6 j _ $this->db->insert($modelinfo);
( r# x0 K/ U' p4 ?' x } 5 S, K" H" |5 |6 n+ N
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
8 S/ F) n1 e! t# _% f在\caches\caches_model\caches_data\ member_input.class.php 文件中:% t3 N( n( |+ a( M
' U9 c/ Z9 i# N, x8 [4 y1 v6 m
_* ~0 D( t' ~# A & z5 l" k0 Y1 u- H) x. p
function get($data) { & _7 S; q& Q; |( {0 t" Y" a. \
$this->data = $data = trim_script($data);
8 F4 _$ I7 @5 c $model_cache = getcache('member_model', 'commons');
5 s' K- \4 b6 A% y. q8 ~* X $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
+ ?/ Y: q5 @. U7 H- w7 u $info = array(); 9 C. b, l% D/ O; y
$debar_filed = array('catid','title','style','thumb','status','islink','description');
# D+ k0 f' g; Z5 \9 x; A if(is_array($data)) { 6 G* T$ H! H7 z/ w% l
foreach($data as $field=>$value) {
! n8 J9 w: b6 y( Z( }. d if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
2 m8 [* B: t- x( E5 }& p: Y: M $name = $this->fields[$field]['name']; 8 U- T' l8 ~$ s' e1 H. ^( [6 m
$minlength = $this->fields[$field]['minlength']; , j2 q, s$ d8 o- q: f! ~" P
$maxlength = $this->fields[$field]['maxlength'];
2 v. F e8 e) A# C; V $pattern = $this->fields[$field]['pattern'];
, D( h: X# f7 t& S $errortips = $this->fields[$field]['errortips']; 1 {, y8 d- x) q6 p
if(empty($errortips)) $errortips = "$name 不符合要求!"; % j& Q: i6 v h+ v& J
$length = empty($value) ? 0 : strlen($value); $ x5 @" O! t" ~' }+ X4 p6 W
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
+ Z& T' Y8 \, e! x! j1 I) F+ p if($maxlength && $length > $maxlength && !$isimport) { 7 r8 r0 k7 ]6 X7 v+ Q3 t
showmessage("$name 不得超过 $maxlength 个字符!"); * Y, _) L) ^, q0 |, E) Y
} else { $ A$ ?3 L7 X) y9 n; a+ Q' _0 ~8 W
str_cut($value, $maxlength); $ M9 x8 B! ^) y* W! K
} $ G9 d/ E+ Q& ~+ b, e, o- f8 n
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 0 X- q5 Z. f% `( k
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
! m' D" c' Z; C1 a6 E2 a# d4 [ $func = $this->fields[$field]['formtype']; - j/ S) V: p8 x- L2 P% ~* N
if(method_exists($this, $func)) $value = $this->$func($field, $value); y/ I# t6 [2 n) E
$info[$field] = $value;
% ~% |; k6 o7 C! B+ T }
6 K6 ?2 d% U* u/ @3 P }
$ A1 w4 w$ G( F& Z+ S return $info; ( P8 Y$ ]$ N* r* a) \
} ; [+ Y4 e7 \* }3 t2 T6 ?
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,% l0 l) t1 Z+ G2 y
' w: J: X, V6 v: {* [3 U% g2 H
再到phpcms\modules\member\index.php 文件account_manage_info函数
! G1 m! D; W3 z, d4 e8 b过了get()函数之后。
2 [3 p+ p: d5 u/ L8 {+ S" f3 j( `% p' ~/ |: y: P& B
# Q: H* E9 A4 A; U$modelinfo = $member_input->get($_POST['info']); ( z+ r8 A* O- ~1 S2 L
$this->db->set_model($this->memberinfo['modelid']); ' z n4 V" |6 b* l0 c
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); . [6 r& k( q6 F4 A: T6 B5 G
if(!empty($membermodelinfo)) { 1 v! w# {5 H( \6 c) b: s7 {
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
6 Z, a4 ~8 \# g) i } else {
+ a( @/ s9 l/ z直接带入数据库,update函数我们跟进看看- P. S) f7 G P3 c: N+ g
4 i O1 J4 F2 r0 {) w6 z
1 ?# ]1 J1 X0 m. A& n! Z
public function update($data, $table, $where = '') {
# a, p% I6 C& O5 v if($table == '' or $where == '') { ' ^7 l0 P+ L+ u) l; S7 N
return false; - a) u: w' c) i) ?& w1 z' |9 O( J
} ! |: H% ~& p8 S# W. d
$where = ' WHERE '.$where; . J8 b8 z9 u8 a& p; P$ v' n, m$ L$ X& V
$field = '';
! l2 ~1 l/ h$ q/ b) k; x2 a& Z% f' ] if(is_string($data) && $data != '') {
( L( s8 u7 b4 R8 p9 q $field = $data; 6 H+ }7 }9 K0 E0 @: I2 p5 ]/ v
} elseif (is_array($data) && count($data) > 0) { 1 \/ W- ^+ K1 d& Y0 I4 J
$fields = array();
, z2 [) E5 _: ~" \0 l foreach($data as $k=>$v) {
' o* z! g v0 c! Q switch (substr($v, 0, 2)) {
5 Z5 { @9 \9 q# t8 l' z* c case '+=': 4 Q! U( l) H- P0 S
$v = substr($v,2);
* v% T- p# L% n+ \! Y. A) ~ if (is_numeric($v)) { , s$ e! x7 {4 W/ \0 I/ [/ E' t; `2 o6 a
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
% V6 G! r9 T& H } else { % {% L2 T$ J2 [* n4 O# p; g* l
continue;
1 z4 t& v+ S0 l# s! ?) e }
0 z2 J& V1 ^3 d! C2 `; K break;
, ?; S5 c9 L& r8 K3 E case '-=':
, p3 D4 C$ E i* z; J. E0 `2 F $v = substr($v,2); # L: I+ p! M/ X% }
if (is_numeric($v)) { # ] v# x( d& L8 v
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
6 k- [. t0 ^5 a+ t2 u, L } else { 4 R, g: u, V1 _. m
continue; 4 c& K$ W7 A5 E( t
}
: P' N& x$ O1 D! i break; ( S7 F7 N$ e- t+ @: P
default:
% P: m. ]3 d( } $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
8 Q3 F" i) r$ S }
V& a: Q. D; i( D: {, [) V. t- V } + r; g8 y( t. j7 |* _) H0 E5 k
$field = implode(',', $fields);
# a0 g7 n: t+ S8 q0 p } else {
" l/ h( u7 N0 F return false;
, e; w+ \% V/ k! |) E- w; d }
& {* \1 i% d b$ T) ]: O! G: Z, ` $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 3 o0 g+ j9 o# z3 q# W
print_r($sql);
) `& k/ e' h) J+ y2 N6 q% H; l return $this->execute($sql);
% z1 r# M- P: {3 n } - i: c8 j2 }$ f4 Q1 h+ M
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。" q+ ?! H9 T4 k3 A/ |, O
: q n5 ]7 d5 t* r# x: ^
攻击测试:
* f2 n1 u& W; Q7 v! l测试地址http://localhost' g$ y9 \, N5 A0 C( ?
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句3 ~% M4 S# \. g. q( N+ F1 f
4 s0 K3 n% t# q- I1 f! _- y7 b
' T9 ~( K; O$ R
. S* u* }+ y$ r+ p3 U
( Q% ~. z5 u! z4 y. D& P6 ]
4 f4 u4 O3 H+ A, P& s
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|