|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告( i. S$ g% B* y" g
漏洞作者:skysheep$ E5 X+ R2 u \6 E' d
分析作者:Seay
" m, v0 S8 O! P0 y" ^博客:http://www.cnseay.com/
) E0 O$ o: \- @9 V8 R, d漏洞分析:6 [ E6 C1 b. k5 L9 f: ~
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
1 h2 d8 w' h) |. d# m$ s$ D: O/ [0 H+ b$ U5 Y$ R2 e( x( D- c
' }6 |4 ^+ |* f+ O
$ j, s3 O* ` y3 F1 N' Opublic function account_manage_info() {
4 _ e' V% L1 }, q if(isset($_POST['dosubmit'])) {
+ i" ]- l) C B0 A, y4 v& | //更新用户昵称
7 Q+ p$ n( k, M1 a7 O $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
2 P4 Y7 ~2 k# l1 J, q/ v+ s if($nickname) {
; |' q1 y5 [6 @1 D/ ` $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 8 M5 W+ B! O2 q6 O9 D0 _( k
if(!isset($cookietime)) { 3 p; V# T. d# |( A& `6 p
$get_cookietime = param::get_cookie('cookietime');
# p/ _! N8 a! [6 h7 x& ?$ Q1 L+ F } 1 F% V, b C! i, |+ v
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 1 `# Q+ M. O* k F( x# L; k8 V
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
f5 [, k7 G; ^- R7 e param::set_cookie('_nickname', $nickname, $cookietime); ! v0 {; j2 R4 W! Z- a* ^( @# P2 q
}
- U! S7 y, e) } x! y require_once CACHE_MODEL_PATH.'member_input.class.php'; ; o- D; g( p( F" Z0 T! D( R. N
require_once CACHE_MODEL_PATH.'member_update.class.php'; 5 a( }) O! j2 _1 e. z- {
$member_input = new member_input($this->memberinfo['modelid']); ; d, u6 H5 R5 e, Z
$modelinfo = $member_input->get($_POST['info']); + o0 A# i) M2 l* g
$this->db->set_model($this->memberinfo['modelid']); # s% b) \! r/ D, E. U5 Q8 d
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
/ d1 k. H9 F) C if(!empty($membermodelinfo)) { * D8 d; N- Z$ q/ o! k
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
& {' M& h, B. ` v' h9 @. A& D8 A } else { \% F( R R$ z8 r1 s0 W
$modelinfo['userid'] = $this->memberinfo['userid']; $ r9 r6 x7 V |* | I$ X+ F/ H
$this->db->insert($modelinfo); $ i' J% k) O. N" M0 {1 w' p g
}
% n" M9 t: u& M1 u代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,) j( S8 }% ?. i h7 k @
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
6 Y5 w* F. S0 B, l; N5 ~7 d) a B0 ]& d. g2 R b
" n" s1 K# \7 `" |: e6 X+ d
: T$ j! p; C4 ^6 s- C [' f& q
function get($data) { 0 |" q9 f" u/ M
$this->data = $data = trim_script($data); 5 {6 L3 S* h5 f8 R
$model_cache = getcache('member_model', 'commons');
9 Y- @' u# b' d6 c6 X( y% @( H $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
, z" F r" K' d $info = array();
6 a) A" A H& {; J N $debar_filed = array('catid','title','style','thumb','status','islink','description'); S. v* p5 U0 r1 H
if(is_array($data)) {
0 ~, g E; p7 O& }- I foreach($data as $field=>$value) {
1 ]1 S8 Y; L9 o) F: A# i( T- r if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
) s3 |* o4 U! }( U8 x1 H $name = $this->fields[$field]['name']; " N: r/ d, K8 o* d
$minlength = $this->fields[$field]['minlength']; 2 b# [2 _/ n7 k6 b" D9 A
$maxlength = $this->fields[$field]['maxlength']; ( V7 M( r- U o" j9 r& b
$pattern = $this->fields[$field]['pattern'];
% z& o9 s& V" u. x' J$ s+ f $errortips = $this->fields[$field]['errortips']; ; J5 d% X9 b2 P5 z3 x- l
if(empty($errortips)) $errortips = "$name 不符合要求!"; 8 D# M* I# A" r* H- p A
$length = empty($value) ? 0 : strlen($value); . E( o3 O8 f/ [
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
! J" b. q7 h3 V if($maxlength && $length > $maxlength && !$isimport) {
l w' _5 s2 B% ? showmessage("$name 不得超过 $maxlength 个字符!"); ! f- L" b4 K8 Y% s! X) q
} else {
5 d$ m# F. F" t4 F' B/ Y str_cut($value, $maxlength); 6 m) L8 x" r3 w. _/ ?2 E: @% I
}
. ~; i) n2 D& m# ?: { if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 9 ^1 {, ^8 f6 A& Z8 Z
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
8 ~& ? g/ q K1 A- h4 [ $func = $this->fields[$field]['formtype'];
% J, {: [9 c' ~2 V if(method_exists($this, $func)) $value = $this->$func($field, $value);
9 d7 |7 R0 w9 a' Z) G $info[$field] = $value;
: k1 |/ ~& L: [% X } ( l( p1 J0 Y {* j
} : G7 [( \- @9 {
return $info; # }/ N3 N/ y) P% ]
}
' m6 T% G3 m3 H6 ]4 B6 ptrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,5 n0 G Y3 K L8 L( v* |
, N8 C" B4 E' ]7 B- {8 P$ L4 v1 u- S" ^
再到phpcms\modules\member\index.php 文件account_manage_info函数
1 x* h R z ^/ M过了get()函数之后。7 j9 Q- Z- @3 }
* k* W. _+ B9 b" w
& n! v' P4 ]. q$modelinfo = $member_input->get($_POST['info']);
7 s5 G: n- n: G0 v; L $this->db->set_model($this->memberinfo['modelid']);
3 k1 X' C4 @ N8 ?$ p* ` $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
- _) J) X7 ^! S' v9 x) @0 w if(!empty($membermodelinfo)) { 6 m) G3 ^4 Q+ y/ M( a
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
b5 J$ ~# X! C } else { ! d/ k) K- w/ N8 e, g6 V
直接带入数据库,update函数我们跟进看看- l; [( U3 ~2 G0 h% F
0 H& X8 N8 {$ \ . g2 E: c& S6 M2 C2 z+ w8 b
public function update($data, $table, $where = '') { 2 {' L3 {+ {1 S: ]2 r; M; U' l
if($table == '' or $where == '') {
& _/ i3 ?6 `5 T) n( U' u5 I+ v) M s return false; , `* M+ c9 X6 c# f6 S' _, M
} 4 R$ o5 m: v: C' G' ~- L& J2 R
$where = ' WHERE '.$where; " D4 f0 }/ h" B& A( n. [' G
$field = ''; & } r" J/ f ?* q+ F' n0 X
if(is_string($data) && $data != '') {
+ j6 V' S2 f b3 s" U $field = $data; 2 L+ H, i% V% P4 z/ ?; l+ C
} elseif (is_array($data) && count($data) > 0) { / {, z. a- v1 i- I/ e* d
$fields = array(); : @" m2 ^6 l, P0 U8 T
foreach($data as $k=>$v) { " c( q$ \4 k) m) a+ r3 l6 r& @( Q" k
switch (substr($v, 0, 2)) { ( ~' c/ D2 I) _% y% m' ~
case '+=': 3 S& r( O( k8 v4 v) u
$v = substr($v,2);
" B# l3 d+ p* R2 }1 z if (is_numeric($v)) { # ^/ I1 s5 H3 k' W% j0 X' ?3 t! X" H
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); ; x% d/ e4 W1 G, R7 M- i0 l
} else { ; j2 Q. x. ^* W" q
continue;
( ?3 p% S, t; a6 P) a/ @! w8 M } ! L7 ^4 f% o2 u. Y7 W0 }
break;
- `) p9 S* ^' a& p: ^ case '-=':
" L* z# U& O1 R3 E) c2 Z. H $v = substr($v,2);
% m" v3 |% d& t if (is_numeric($v)) {
1 d, X; y# H% ~+ t3 E) ?& |6 {1 _ $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
! X k' I$ b; v. M# E# x' U } else {
% [+ E. |: M$ _ continue; + S, z! [" Z# D7 T* K+ g& Z" W
} " B0 Y/ U) Z8 r2 c2 `( i
break;
7 f6 p1 R$ s3 p q5 m% [& R0 I default:
& Q5 z2 @5 w: d( ] L $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
( z8 q/ e; r) n m9 i, a } ' ]+ _/ {( ]9 m8 Q
} 6 L# y! L# G0 ~
$field = implode(',', $fields); / p1 ^) z: X9 B
} else {
( B! m8 W, A1 d) ^1 [6 a5 d return false;
7 O5 Q! |2 D& l4 t Y } 0 S1 o$ O2 z. A X' v. V2 ]
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
7 h$ f q# l7 ~* n print_r($sql); 6 C) a, n2 C8 u2 m6 a3 }
return $this->execute($sql); ; V7 ?, A3 \; Y. A6 [$ H
} 2 H2 s" b7 `* Z4 ^3 a# l
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
8 E; ?' n2 d( }! g9 u! L
" ^! V7 n. H! d5 P$ a8 r攻击测试:
9 [$ t7 F" ^7 |5 ^1 W测试地址http://localhost
2 \5 U0 v6 @ r: C8 ?3 d. ]6 h 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句+ k: @# l0 W; [/ o9 Z* d
F) b3 s. U9 O6 J+ }0 }
( [6 }( t) m* J
# `2 d* c" G1 P
' b' S7 h+ p. m
4 a+ m! B3 ^9 S; L) d7 K |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|