|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
' L9 h, e$ R) d漏洞作者:skysheep4 ]- t+ h" k4 f/ M/ t8 ^
分析作者:Seay5 d& S. G: o* _! P) z, O
博客:http://www.cnseay.com/
; l& X/ Q+ ~: r0 B' s1 S& Q; q0 P漏洞分析:
6 M# L+ f [+ x' K) a 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。# e2 I% O* `+ y9 K+ V/ K, k
4 r8 h% ]7 D; s' U r' i4 O. H# p3 r! @( w
* s( A) ~' l0 F# |( q
public function account_manage_info() {
; \. l* W+ R8 A if(isset($_POST['dosubmit'])) {
/ E# N" p2 W/ {% G; P ] p //更新用户昵称 " G5 [8 l1 |, d @$ n+ }
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; + |/ b H8 h6 k" ^' w" C
if($nickname) {
& Y! D0 \5 @5 |2 t Q $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ; r8 e: d+ k7 T6 A
if(!isset($cookietime)) { * t+ v' g' G" s0 N$ x7 R
$get_cookietime = param::get_cookie('cookietime'); 1 I0 d+ o( U! y9 Q, S' N
}
5 ]* G; R! C2 _) c1 l1 z $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 8 e- y& g9 P8 A L/ d
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
/ k' ~, e, G% s9 Y! L m% C/ j param::set_cookie('_nickname', $nickname, $cookietime); # X" ^* @6 v* Q" E' c7 D
}
) |6 `5 W% L( @6 d* N1 s# N% n require_once CACHE_MODEL_PATH.'member_input.class.php';
4 v- l p& B; k2 P' I9 a require_once CACHE_MODEL_PATH.'member_update.class.php'; ' K; v1 t0 W" d. T
$member_input = new member_input($this->memberinfo['modelid']); ; k) b1 y0 n; p6 `
$modelinfo = $member_input->get($_POST['info']);
- @0 `$ O( T) L$ r3 J( M1 d' M $this->db->set_model($this->memberinfo['modelid']);
8 P$ |. X1 z; R/ ~ $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 2 E+ u( T2 L- _# a7 u3 b9 \
if(!empty($membermodelinfo)) {
5 V! a. M/ E J $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 2 ?# |2 F3 ~; L4 w; S7 Q6 r3 i
} else { 1 Y* b1 ] X7 F" }- F9 f
$modelinfo['userid'] = $this->memberinfo['userid']; ) ?- N: I4 M9 G9 v8 `8 _
$this->db->insert($modelinfo);
7 w- s9 I. I3 ^ }
9 z2 E- D9 n( i* I0 _代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
9 f; T: H8 m x1 L3 }; V: s在\caches\caches_model\caches_data\ member_input.class.php 文件中:
9 w- Q# e* N1 D' y* s; R" Y& T: P% w" c- H7 Y9 ^5 v) y# Y
$ u4 F8 A( @, X1 l
& w, p4 E) F" `. e
function get($data) {
# o7 k. W: O0 z9 W e' u $this->data = $data = trim_script($data);
9 B+ b" v& Y: s9 F7 h $model_cache = getcache('member_model', 'commons');
& Z7 |5 G; u; G0 F; v' v9 N $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; , J. Y% j$ U5 P1 I* T& i
$info = array();
* ^4 T* P. U( A $debar_filed = array('catid','title','style','thumb','status','islink','description'); 0 D/ C+ V4 E2 S. c* B$ p/ z% `4 J
if(is_array($data)) { 5 {0 s; N- ~ A1 w! x3 T/ g
foreach($data as $field=>$value) { / t1 @0 {9 A a# @ _2 v
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
. u% t2 ^1 B% t7 x' f7 e $name = $this->fields[$field]['name']; : H; C% `8 o9 S* N' H8 b; K
$minlength = $this->fields[$field]['minlength'];
- \; d" q! s& |1 ~ $maxlength = $this->fields[$field]['maxlength'];
, }1 D" u% a, f' L, W7 g $pattern = $this->fields[$field]['pattern']; ! t/ E# Q6 g. W
$errortips = $this->fields[$field]['errortips']; % J- L" [6 Z( C: m, i6 V
if(empty($errortips)) $errortips = "$name 不符合要求!"; . M1 \# i' F. I4 L6 F1 }
$length = empty($value) ? 0 : strlen($value); % z% d- ^! W1 O1 P
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 4 [$ q/ D4 y( j2 h* }8 F3 d
if($maxlength && $length > $maxlength && !$isimport) { / r& D% K% [( _
showmessage("$name 不得超过 $maxlength 个字符!"); / F; h6 S+ R/ j h+ i w
} else { . }( y3 F+ M) y0 r7 r
str_cut($value, $maxlength);
! x* a$ f! D/ W1 x5 t } * A( J& P% ~, b1 h* G% C
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ( A1 B0 F: A& O8 `) W- G% ^
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
* s4 k: D! U; X* a c9 f) J* q) s" ? $func = $this->fields[$field]['formtype'];
, {) ^$ g* G7 u: T7 j if(method_exists($this, $func)) $value = $this->$func($field, $value); 3 f) X2 w7 b& n2 O9 i
$info[$field] = $value; 5 Y& Y9 I7 T$ Z: r' V) _! p" f
}
( M8 R( L& }& D; x }
/ T/ Y! `1 D/ D0 g% N O6 [7 C$ Q& C return $info;
* b* G5 p6 x D2 z) }* G7 H } # Z2 x! F2 e8 }
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
' i% i: t' W) b$ t" |2 F# p; S" m8 c2 n% ~% E4 U
再到phpcms\modules\member\index.php 文件account_manage_info函数 |3 x' q" q3 L& g M4 p
过了get()函数之后。: V! q4 J* I$ d) J
: @) t& F5 J% a! j0 ^* p
& C( k# }& L% G/ g' d$modelinfo = $member_input->get($_POST['info']);
& a! T0 `& M9 z) l. y $this->db->set_model($this->memberinfo['modelid']); 1 e t/ H- J/ b1 V
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); * }8 s1 S; S$ Z* x' D
if(!empty($membermodelinfo)) { ' c% e4 m9 L! H/ F
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 3 z: W& I( v+ Q
} else {
& @" f; ]8 ?( J1 Z* J/ |直接带入数据库,update函数我们跟进看看% ^) Q; ]& s! ]! t/ f
, |# I% I0 |; {: M
0 m3 A: p5 E0 f* W4 a! [4 ~. ]- {& qpublic function update($data, $table, $where = '') {
2 | Q/ x8 u4 f) R/ ]9 a. Q! K" B/ _ if($table == '' or $where == '') { + I& t, W3 q( \3 S
return false; - K E3 o1 Q' ^" ~; L/ w" v. X3 |
}
8 G9 ^" ~' i2 q) \3 g0 M% Y6 {4 Z $where = ' WHERE '.$where;
) ]3 \# f7 m7 T- H, S' j $field = ''; ( P& m1 ^6 P |5 A4 A! s
if(is_string($data) && $data != '') { % }' a* F* S1 X$ t1 p1 r. P
$field = $data; 4 V+ W0 l% [2 ]1 _
} elseif (is_array($data) && count($data) > 0) {
" u) b) | l/ N* \ $fields = array(); , P2 E5 W9 J I- L5 Q
foreach($data as $k=>$v) {
& b8 J5 l& b% P, M- e5 C switch (substr($v, 0, 2)) { ( ?! }0 E7 [0 R: e
case '+=':
C2 Q) K3 j7 @3 B# } $v = substr($v,2); $ B' b; ?2 f2 h) a5 q4 t
if (is_numeric($v)) {
% F l# n2 J! ~# W+ g, \$ E $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
) q' p0 k+ r! D1 U7 \ } else { 7 w% H G7 A% ?! J5 o
continue; . Y! I! W/ Q9 E/ j1 O/ `
}
c4 f5 y3 \2 A7 L( C. P break;
/ x; g# p1 z: P* T# o2 g2 C4 ? case '-=':
@3 _9 y( K2 l, a8 k $v = substr($v,2);
2 `% p; O. n, R+ {6 ], e if (is_numeric($v)) {
2 V# |/ t3 R+ F- L! ~ $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 3 y7 {: o3 E3 } F7 D, o1 n6 \
} else { 5 S+ J0 F! l9 F% f+ l4 W# v/ `5 }
continue; q5 B% }0 f2 `7 }% m
} # { G+ L \5 R( E3 Q$ S
break; |# T+ n" l, E( l0 l$ t
default:
6 _# `# m1 M2 ]6 E- C7 R0 a $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); - G4 X. a7 h W* L- o, W$ U5 |
}
1 ?8 A6 i7 U# n f* r }
4 P" t6 [% g+ w" d1 K5 _ $field = implode(',', $fields);
: q/ E: o' I( z3 Z } else { 9 q- y J6 ]" M; O8 u& [
return false; ; b' t8 L# r, J; P% a( t
} . L1 g# {4 m# y5 Z |& N* |
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
( b z7 b$ K- l2 Y8 U! f print_r($sql);
9 L+ `4 c9 }6 x return $this->execute($sql); 9 U* ^+ B) `& a" Z0 s9 y
}
9 u i, d4 p) _' s从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
- \: b' P) E! h8 V0 `/ l$ ^$ g3 f: O4 I8 A" o p, `6 t3 s: U' p% V
攻击测试:& ?; n8 Z- C2 W( B( l3 b
测试地址http://localhost, H6 o: M6 b; O. y
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句/ E7 N8 _, E3 d) C9 z- O4 n
3 b) u2 J# c; \% ?! E I% m4 J7 j' O2 |8 l* ~3 ?
( M: j9 `0 Z5 X; K& F$ \5 X
, v: `+ I: {4 U0 M; ^- q2 @" D) y1 P
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|