|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告6 l2 G) |; E. x. f' e8 r3 c
漏洞作者:skysheep
2 q: T/ w7 F: _! _5 F分析作者:Seay
4 C. h) L- m1 }& _6 h, |$ i, L5 [博客:http://www.cnseay.com/, [: P" @: V: |, j+ i
漏洞分析:9 b9 C9 s5 {" W- @3 N
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。- t+ R$ Q* |" a# a/ k
- G+ O0 g2 D* a, D/ P% k
$ o- R( m R0 d- z: q
. A' S7 p2 z/ a% Q5 U G4 z, Vpublic function account_manage_info() { ; d( A# p ]4 `$ [2 ~6 ?& B, H
if(isset($_POST['dosubmit'])) { 0 J+ o7 U6 t+ i& a1 u: G5 U
//更新用户昵称
6 K7 A* D5 b( b $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 0 u) u3 ^9 b) R0 p g
if($nickname) {
9 ]) t) I, j$ M9 x6 I5 c% ] $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); " ]& U8 V8 |1 O/ c1 V/ s- v! }0 L3 |* \
if(!isset($cookietime)) { 1 z' b+ j+ ?" ~, \% u! o
$get_cookietime = param::get_cookie('cookietime');
$ ? \: A+ @& z3 d }
, ~! B! ?0 B8 f $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
$ \# s( `% I) l. U2 C- O/ e $cookietime = $_cookietime ? TIME + $_cookietime : 0; # u$ j `. B; ~
param::set_cookie('_nickname', $nickname, $cookietime); 0 f F P- v* P- G" e
}
# B2 [2 W% R* [ require_once CACHE_MODEL_PATH.'member_input.class.php'; " V; k7 T4 [! k2 ?/ r- ~! p( ~+ B
require_once CACHE_MODEL_PATH.'member_update.class.php';
- I* I! O( c+ d C0 f O $member_input = new member_input($this->memberinfo['modelid']); \! n" P; m7 O, u+ Y
$modelinfo = $member_input->get($_POST['info']); $ Y, V4 s; |/ w! [) x) Y
$this->db->set_model($this->memberinfo['modelid']);
4 P. j) W) `4 L) z+ ~$ m( e4 ? $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ' k8 e5 A/ I7 B' X/ ?
if(!empty($membermodelinfo)) {
/ ]4 ] S. |- }; Y$ g $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
( z I$ P; @9 j ~ } else {
! H6 n' t# N0 N) |6 F: ]* x. b9 y $modelinfo['userid'] = $this->memberinfo['userid']; ' l0 {( [2 w/ N
$this->db->insert($modelinfo); 3 V+ c4 r2 @% d) _4 S' k
} & z1 d/ G+ a# p) X( {! z
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
& @9 z- r: |9 r6 `, a6 Y4 d在\caches\caches_model\caches_data\ member_input.class.php 文件中:! q1 Z7 ^% S9 \' ^7 K" R/ Q9 o+ M
" Y1 P3 P* {3 d$ y/ t0 P) p/ M, |
# x, D. S* X+ b. @( s: B; S
' D6 {- |% B1 T0 nfunction get($data) {
4 k) T1 h+ e5 r9 l8 z1 \" f; ^ $this->data = $data = trim_script($data); ( ~4 w& |9 q) p' b m4 s$ d3 n' G
$model_cache = getcache('member_model', 'commons'); - ?8 u" C0 U; _1 C
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
) m" N+ e" N0 K- c6 r. m $info = array(); / G9 |& `( V6 {+ }, f# M, K
$debar_filed = array('catid','title','style','thumb','status','islink','description');
- g1 }6 t; Y& A$ g- K/ Q* ^ if(is_array($data)) { 2 G9 T( y6 y8 o/ o8 p
foreach($data as $field=>$value) { 4 ^! L% Z$ P' T: X3 n
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; " {9 w6 ]! P5 i3 m# d7 M7 U4 e' y
$name = $this->fields[$field]['name'];
+ {- I1 s" D3 p/ [7 E% t% m $minlength = $this->fields[$field]['minlength'];
( I9 P& q" a( l' p, J $maxlength = $this->fields[$field]['maxlength'];
' h8 P" x) I H. ^' Z6 z $pattern = $this->fields[$field]['pattern']; 8 K& j: X; \6 ^/ _" Q
$errortips = $this->fields[$field]['errortips'];
# c6 Z8 B) v3 H% F# E8 D( G) G if(empty($errortips)) $errortips = "$name 不符合要求!"; 3 Q+ Q, y6 ]& I- a0 T- @- k
$length = empty($value) ? 0 : strlen($value); 6 ]; G3 ^+ ^, ?/ j' T
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); * O8 X! m1 W. V2 L8 t
if($maxlength && $length > $maxlength && !$isimport) {
0 J) P5 q5 Z$ M5 N, Q showmessage("$name 不得超过 $maxlength 个字符!");
: p8 J5 \7 W; |0 t8 I- f } else { 4 Y; d9 u4 [9 i5 j8 g+ ^( H
str_cut($value, $maxlength);
0 m/ U0 d+ K/ n! l, s }
. h: l. g6 U. i( e3 e: k0 j if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ! @+ Z8 L/ E4 z% F3 l
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 1 p- B0 @) N' g
$func = $this->fields[$field]['formtype'];
2 _8 M$ s/ s8 K# } if(method_exists($this, $func)) $value = $this->$func($field, $value);
0 F* t1 C: M. }% ` $info[$field] = $value; 7 v) ^4 E+ `. w$ Q, { `% f* F
} . a* n% S7 A9 x: X6 `
}
" E; w1 d6 j' A- D return $info; . p7 Z3 R7 e- u4 c: q
}
9 \4 h: n) U5 [trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
3 }( | V, e9 R+ t
8 R# n6 B+ \5 Y, j T9 w r再到phpcms\modules\member\index.php 文件account_manage_info函数
, u1 T/ ?$ `# g3 b, [, T. u! r过了get()函数之后。 _) Z u; e5 Q7 f
2 A/ a4 `2 l& ~9 k0 `, A + ~3 \9 d7 \# [; O! V
$modelinfo = $member_input->get($_POST['info']); . W2 Q1 \; j% }7 |" C9 n) X. J
$this->db->set_model($this->memberinfo['modelid']); 5 a% k% d O6 x R7 S, K; T$ d! ?
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
6 Q& P' V5 I* t0 d t if(!empty($membermodelinfo)) {
& U0 k, o _; ]* n' G* N+ s. p$ ?( g $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ) \% c8 Y; w$ t6 D: ^% `' z2 i0 r1 p3 Q
} else {
5 ~6 S8 Z' M/ o3 V, _+ q; [直接带入数据库,update函数我们跟进看看- p1 d& ~+ v" p9 L& H
- J L- |! l0 Z3 C8 u " a8 m9 b T3 v- U$ b0 B
public function update($data, $table, $where = '') { / C$ d# }9 G* E. N8 z3 k
if($table == '' or $where == '') {
3 Y5 F. l7 v- _7 E5 S3 L% t return false;
' B( N6 m2 D5 d: M3 \; H }
, X6 d/ L& E2 t2 W$ ~9 O $where = ' WHERE '.$where;
0 w5 d5 k6 D6 T; o $field = ''; # V S- b; c. B7 f9 _# O" D
if(is_string($data) && $data != '') {
) |; }2 K7 G+ F/ U# ]0 j $field = $data; " j' M7 }5 C; |+ P/ }
} elseif (is_array($data) && count($data) > 0) { ' n3 {7 c5 h, v! e$ n7 T
$fields = array();
/ g5 [$ p! {. w- X' d foreach($data as $k=>$v) { " O6 n) m* X# `0 E/ G: s6 H
switch (substr($v, 0, 2)) {
+ C( s; Q# Y- ]6 f case '+=': 6 m# w1 C- {" A/ ^- h
$v = substr($v,2); " m" E H1 C7 A4 F8 |
if (is_numeric($v)) { ) c7 X3 B3 p2 T/ H5 |* [
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
/ h" V, d( b0 j } else {
/ K* X, c, |7 J$ X) Q continue;
7 r( v4 H( ]7 K4 ?2 c% l8 S2 A }
4 b0 w6 ?9 M% a3 P. \( J break;
' V7 B" o! g9 p9 N& S' V case '-=': 3 t1 _: ~, i, J% U% n
$v = substr($v,2);
, B8 O' Y7 q% k if (is_numeric($v)) {
; R& j% c0 H5 s& [7 Z6 Y5 N $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
- Z+ v) N4 I1 L9 m1 j } else {
( S1 u9 ]+ r2 f/ y+ r4 B! q7 S continue;
9 i6 I6 A+ \: ^; k0 Y' { }
0 o+ L; N" M7 ^% ^ D break; ; D* {2 X4 _5 |# h* m1 ^* d
default: 8 G: s, G% \2 f t! f5 h, X* x& b
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
8 ^4 J. Y* D( S# x- g0 V% p9 W }
7 ~& k0 O+ K+ h* P, |- i$ S0 l } 9 f3 t7 M7 r* n( A, ?
$field = implode(',', $fields);
/ s# @0 ~- f1 M& Y } else {
5 R0 b6 u( ^- D+ p& F/ v' E0 ? return false;
6 H' o( [3 I8 o# W, ^& r+ j }
* G$ s# ?8 \6 x $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
9 W" A- I; b5 N print_r($sql);
) Z, {/ | d* R( h) N return $this->execute($sql);
5 c8 K" w, B+ | }
6 G2 F9 |. n) H& a+ \从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
$ L- s" t% j5 W( a4 l9 b$ b- E4 v, C8 p/ j
攻击测试:
( }& j. ~: h, k5 p$ B/ y- @# Z7 Y测试地址http://localhost: C; J/ b% z! e: P3 L
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句' O# n3 W, T' C6 P: C
* {) p2 \+ |% F. C+ [! e
2 o' ]2 L0 u/ ~$ R$ A
. ]& r4 `/ e+ Y
* [2 U9 @8 p, b( d0 E/ H/ j" M) `4 H" f2 M2 g6 T1 }, }* f% }
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|