|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告( S' j7 w3 D O6 D9 ?
漏洞作者:skysheep
, {) |) W* ^9 [1 K' T4 D5 C分析作者:Seay
; x0 ^; \4 y7 c6 q% |, }博客:http://www.cnseay.com/, d" g' A4 I: F) E. A
漏洞分析:( O' z, @- p) p/ T
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。* p# u* R) r& v# r9 j" {. y
. x1 f9 J6 l) P8 u7 ~# Q6 r* y/ t) Z" Z }
7 b. h/ o _% H/ }; d" k c" V$ V
public function account_manage_info() { ( z% E" h n/ D" l) m+ {2 ~
if(isset($_POST['dosubmit'])) {
) T4 |8 i2 l, |$ ` //更新用户昵称
/ H4 y, a: z' q $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; : `: j ?* D2 M/ T1 Y+ n
if($nickname) { / n" j' h$ {! r/ f, \" g
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 7 K2 k' s8 j+ j& \: K+ H# n3 Q
if(!isset($cookietime)) {
3 L8 {/ G/ ?/ H; I $get_cookietime = param::get_cookie('cookietime'); 1 n! c4 y6 g4 }+ @; L
} ! F- u9 o3 y5 m) W' H0 M, H
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
1 Y& W# L" }& \" ? $cookietime = $_cookietime ? TIME + $_cookietime : 0; : i' n8 a% f4 e% B- c3 Z. w( j' _! [
param::set_cookie('_nickname', $nickname, $cookietime); ! v8 j, W4 c* ~* l$ t
} , ^; m; o* l' d" |
require_once CACHE_MODEL_PATH.'member_input.class.php'; * O$ C4 m4 |$ |7 c$ b
require_once CACHE_MODEL_PATH.'member_update.class.php'; ; u( p, I( {' P
$member_input = new member_input($this->memberinfo['modelid']);
" p# i6 ~6 h3 `" S& k' F; b $modelinfo = $member_input->get($_POST['info']);
: Y3 _9 d" M8 O( y7 v $this->db->set_model($this->memberinfo['modelid']);
0 o" [) o- p, ^1 t $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
% ?) A1 d, k: l- U. T( l& F if(!empty($membermodelinfo)) { & `6 p' c+ ]+ b$ G' E4 x1 H9 o
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); / J2 M; V6 E$ W5 H% i
} else {
( ^* r( Q- r; F! n2 N6 [ $modelinfo['userid'] = $this->memberinfo['userid'];
" Z* Q) c% ?; T, E! {% w8 [ $this->db->insert($modelinfo); / I# l5 n% M1 i+ t+ j' H; x3 k
}
- t( G% b8 z6 |. A% Y* X代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
' p) c4 J5 p8 P在\caches\caches_model\caches_data\ member_input.class.php 文件中:* N; {" g a( R# w5 m+ ^
2 E H( m# e" w9 l
( k6 u; E" J" X; `0 n: Z ) [9 P0 V; l5 m P) m
function get($data) { $ v5 ?+ t6 v5 J% r9 U
$this->data = $data = trim_script($data); 3 U P6 r/ {7 I, U; C7 x
$model_cache = getcache('member_model', 'commons');
. m% [' V3 t! {- m $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 9 V# x6 v, |5 ?' }8 b% t6 K" K6 R
$info = array(); . m4 L' o2 n: f, y4 O. C
$debar_filed = array('catid','title','style','thumb','status','islink','description'); 6 U: C# P1 s3 u) i$ j% C2 K9 O
if(is_array($data)) {
; `+ P, z% U0 i) s4 p* | foreach($data as $field=>$value) { % O5 I; T o% X. A
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 1 I Z" w0 | D E3 `% [
$name = $this->fields[$field]['name'];
7 C% s; I6 R9 N- d& |# k1 c $minlength = $this->fields[$field]['minlength']; 3 p! X u$ `/ W% r
$maxlength = $this->fields[$field]['maxlength'];
/ m- R) `; g7 `' F7 L) {) T $pattern = $this->fields[$field]['pattern']; 6 V( \& Z5 m2 e: J V' A' [% t+ W, V
$errortips = $this->fields[$field]['errortips']; ( ~( u7 Z: t& q. ?
if(empty($errortips)) $errortips = "$name 不符合要求!";
0 T! V! l5 {$ S5 J $length = empty($value) ? 0 : strlen($value); , q# d1 K y; A6 \; h b. s
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); & I0 N9 l8 l2 ~% C( R
if($maxlength && $length > $maxlength && !$isimport) { 8 n: L# c. [, Q& v4 o
showmessage("$name 不得超过 $maxlength 个字符!");
$ D: F6 S! R3 p5 Y* c. K0 V } else {
# Y9 E8 f: c1 c3 C/ y str_cut($value, $maxlength); 6 i& K: N) \( G* _
}
|0 ]6 R# ]8 G- Q0 P if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
1 T: |/ M- H! X; [2 E" k% @- k' Z if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); & R" `+ n, i/ p8 f( R1 I) I6 q
$func = $this->fields[$field]['formtype'];
7 s4 E, C5 L; `2 w" x# c$ P if(method_exists($this, $func)) $value = $this->$func($field, $value); : q3 O9 t5 u* ~$ S& `, P
$info[$field] = $value; . O4 J4 g* @2 \0 \* s1 o: W5 e
} - H7 `# q; f" _2 u
} , T- D2 A& M7 F1 @ ?
return $info;
3 y- k9 Z8 G& r9 R }
1 L5 }) L9 H1 h. U+ I$ Ftrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
! a. B! b. {' H/ e
0 {. t0 h5 `0 w) {5 v0 @再到phpcms\modules\member\index.php 文件account_manage_info函数
* h" h9 }: s& M3 V; k过了get()函数之后。
% k, D( d3 ?6 c( S$ |# e* x" G6 b0 D" V: ^( l5 ] k5 P
3 ?( c4 t. x* h3 l6 a$modelinfo = $member_input->get($_POST['info']); % a! N# |6 g8 I1 ^
$this->db->set_model($this->memberinfo['modelid']); ( k! X; w2 X# t* w1 T; L, P3 w6 Z
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); U/ ]# U' w. M/ L/ T' j+ f
if(!empty($membermodelinfo)) { 8 y9 r7 Y9 }, f4 K
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); * D+ T1 B, n" Q6 A
} else { 1 Q, v$ j7 ^. l/ V. E1 S6 ~/ b- t: Z
直接带入数据库,update函数我们跟进看看" q" D" x1 I. v9 K+ g3 }% Y y* q
+ k+ H4 v/ s5 a6 f& @ u
/ U& i ~7 S' m- Dpublic function update($data, $table, $where = '') {
+ h2 Z, N1 |8 k! E7 d4 q* D if($table == '' or $where == '') {
& s# _. Y! W) @9 p+ {7 `4 |. R return false; 4 Z: O/ t6 F# y6 a! A
} ' {: n, K7 r9 p( _3 t
$where = ' WHERE '.$where; ) N' ?+ `9 G0 g$ a C5 c _8 @
$field = '';
! J1 n1 Q7 u N+ i if(is_string($data) && $data != '') { & ]' g$ O# s: u' i5 q1 O1 u* e& h- _+ Q
$field = $data;
# |- `9 M6 @. x } elseif (is_array($data) && count($data) > 0) { & I* W( N2 i$ f, h/ v2 r. C. o( N
$fields = array();
8 m9 ^9 T4 V3 W* z* \, x foreach($data as $k=>$v) {
; Q" D/ Y- r) q. H5 F8 o# o switch (substr($v, 0, 2)) { . M5 e, m+ ]) B+ y
case '+=': $ }, b$ u5 I8 L7 M( M
$v = substr($v,2); + l; K" g: \ m+ j9 o- o( L$ N
if (is_numeric($v)) { + ]% R( L W1 j! f
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); & B' G) ~9 u5 G' \; e
} else { 3 b2 E+ d3 P6 q4 c
continue;
* }9 [& A2 I/ P: M4 a* v }
: b- a1 Z V; U: Q* m+ P/ V break;
6 g, ], V+ X+ E8 s2 z3 g2 g+ } case '-=': ) k, ?( o; T. D) n2 [# Q
$v = substr($v,2); & k/ _! a+ b. k& M
if (is_numeric($v)) { 3 ]2 F; b* N) Y1 O4 L7 [: _
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
' [# ?2 O- j- h* }, b% } } else { " w( Z+ [0 {3 F" Q0 O$ I' P: C8 H5 T
continue; ' A$ H( g. U8 n0 r5 }; X& z9 J
}
G6 H% K h% H0 N" | break;
) D: M2 h# h% ^* ^0 Y9 B default: ; `3 s# C B! \ M' g. ~* [
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
& T A+ A9 r" N0 ~$ R' s, x4 E } S: W3 d1 z, ?% {5 {' f; P* N5 t) U
} 7 u f) u y) ~% h ~$ K1 _
$field = implode(',', $fields); 2 f6 _" K) v* C3 T0 D
} else {
# S3 m' P% b! { return false;
O6 P0 P l3 T; w! R- K$ N }
: h( _. J8 E" P) T( B# ^/ L# o $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
2 B, ?2 \# E' X2 g M+ l print_r($sql);
* U6 e% Y7 a& v$ n return $this->execute($sql);
5 l1 [% K! m' a }
r5 F& [4 V; K! t: x, b# d# M* R从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
& O: C# G; l4 h
& i8 D' h4 U' M攻击测试:
' O. J3 @: s2 G测试地址http://localhost- @- ]4 T2 N3 X( E B1 V" ]* N7 q+ j
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句5 @2 ^/ P; i9 t# w
/ p2 T" I9 ]9 r. T4 a& V
# V5 t2 L. K+ l0 }
! g4 s+ D, e" n$ X9 E y2 O3 J: U! U5 C6 q0 M: f+ E9 L
! e8 ^& p) a0 _6 V! g* k1 o: | |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|