|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
- W4 ~. O% C3 d漏洞作者:skysheep" r0 E( ~8 Z6 |! C
分析作者:Seay
& {1 G: k8 [# d7 l" y4 r. d博客:http://www.cnseay.com/ b! x9 g7 y$ \, p
漏洞分析:
* ?' `* ]" T9 H9 f! r$ q 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。; c4 i7 X/ e) N6 P( l
, Y" Q6 @1 g7 e+ ?
1 z# Z6 ^2 b% Q, @4 W9 z & U" {" X" S, S7 K' ]
public function account_manage_info() {
" m$ u, s! F# l2 V2 M if(isset($_POST['dosubmit'])) { / n4 ]$ S2 x2 R9 f* i
//更新用户昵称
' _6 J$ G) z* x8 i% j* y $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; * D7 \9 O, C$ F, i6 a5 x# a& m
if($nickname) { % [. e L% q2 h" R3 k
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ' A5 w! f! m( J, v- ]. z( o
if(!isset($cookietime)) { / y% }3 G& P9 V
$get_cookietime = param::get_cookie('cookietime');
# d! U3 n3 r' {2 Q; K } 6 `2 K; J9 T/ q! n
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
* f X- F3 z( {: ]. a* T+ ~, k $cookietime = $_cookietime ? TIME + $_cookietime : 0;
& n6 D3 t$ w- s& h8 u; x param::set_cookie('_nickname', $nickname, $cookietime); ' E4 ~' n& L) c
}
& S: Q$ D1 c7 N( h* S8 g. J* a require_once CACHE_MODEL_PATH.'member_input.class.php'; - C9 u+ C* k* V5 L
require_once CACHE_MODEL_PATH.'member_update.class.php';
" u7 C- S3 P5 q: ` $member_input = new member_input($this->memberinfo['modelid']); ) n$ P, X4 W* y7 n* \. i( U
$modelinfo = $member_input->get($_POST['info']); ! Y# y! |1 t* U/ e! B
$this->db->set_model($this->memberinfo['modelid']);
4 E: ~& y+ k1 j3 t* H7 { $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ' C5 s6 I* T4 A' r$ h+ B' w( N3 ^. L
if(!empty($membermodelinfo)) { & G p) s, r5 I* i3 @* N* c2 o
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
) ?& s9 @- y3 m: D/ l4 _: \) T } else {
6 F' k$ B5 ~$ b/ P $modelinfo['userid'] = $this->memberinfo['userid']; ( y" m4 t* J4 N( x+ ?/ T$ U2 i
$this->db->insert($modelinfo); ?$ }3 m, L! T8 }( j8 P
}
3 ?7 C' J5 X" z! N: Y9 Z* t代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
2 z! G* W$ o D/ ^在\caches\caches_model\caches_data\ member_input.class.php 文件中:
% a; l7 s' ~$ i4 ^+ K
8 ]3 R' N' d" R) T, c% U8 r2 _2 i) f( A2 S
& ]1 Q1 y! \- k/ Z5 w* M
function get($data) { & Q% ]) J/ Q; \* B' E+ M
$this->data = $data = trim_script($data);
! [2 k$ t) \8 V $model_cache = getcache('member_model', 'commons'); 7 g+ S* V' P; a- y# E% W* P: m/ w
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; : O4 X# H6 S( c0 y5 v
$info = array(); 3 ^/ ?7 i' y! I$ G
$debar_filed = array('catid','title','style','thumb','status','islink','description');
" l% v/ I* T6 i9 U/ D if(is_array($data)) { 3 N& X3 A# @. R1 P
foreach($data as $field=>$value) { % [; T+ k* K, R0 H# T* V# z
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 6 t; N7 @% p# \ O4 X
$name = $this->fields[$field]['name'];
/ Q- c3 w' d' z $minlength = $this->fields[$field]['minlength'];
$ S) H) ?. X% |5 [- n+ ^8 q $maxlength = $this->fields[$field]['maxlength'];
9 @# R, O/ R: i& C9 e $pattern = $this->fields[$field]['pattern'];
9 s/ y. [( }6 Z1 j1 e8 S2 \8 ` $errortips = $this->fields[$field]['errortips'];
- E/ [/ ~) ^5 o' o. C7 { if(empty($errortips)) $errortips = "$name 不符合要求!";
5 h) s% O% T! R! T: x( N $length = empty($value) ? 0 : strlen($value);
8 G d. i2 p8 L: V if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
$ d- z3 \: T0 W; U if($maxlength && $length > $maxlength && !$isimport) {
+ [, y$ G+ V/ X% h1 Z showmessage("$name 不得超过 $maxlength 个字符!"); $ }1 Z( c; z/ @0 Y5 p4 V# ^' I
} else {
( |" |+ c+ l8 q1 d6 i' z str_cut($value, $maxlength);
5 y5 ^, w& Q5 O# v/ h- a! s }
/ Q0 X9 d, z1 i5 H' c& w [ if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); : o" B. H4 O5 U- U* N5 [
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); : l* F* c& s1 [; |* q% b- U
$func = $this->fields[$field]['formtype']; 3 x" ?# ^% W3 h* f" u
if(method_exists($this, $func)) $value = $this->$func($field, $value); 7 m# J% `. b5 `/ \
$info[$field] = $value;
) b* r# s* k$ ^5 a: Q0 j3 g* y } + k: f$ @& {9 V! L% [7 c. n9 y
} . l2 O% Z3 ^" ?9 h3 s2 U
return $info;
! c7 a) n2 d) K4 ?8 {' Q5 F, V4 W" k }
* o4 {7 a6 X5 r1 A5 utrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
& n- a1 v& |4 j& G
/ I" P4 v6 {# n& }. K; i再到phpcms\modules\member\index.php 文件account_manage_info函数! [1 W2 N3 ?% ~3 v& {
过了get()函数之后。) H: K I/ m( I) Q" X, H- e
: V. R% l) R1 |6 c; M5 w + H6 H+ X- m) ~. G* E6 Z6 q
$modelinfo = $member_input->get($_POST['info']); : C7 w: d; |: j& y! r
$this->db->set_model($this->memberinfo['modelid']);
* y3 ^* ?; R& ^- a- t0 S! \) q $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
, e, T0 p3 k* o4 U if(!empty($membermodelinfo)) { + _, Q9 C& Q- F
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); $ u. e3 q- k2 _7 V8 u
} else { & m4 Y r& q! H4 D
直接带入数据库,update函数我们跟进看看
. j, F s* x* H0 w) P
0 g8 l7 e; R0 o3 k 7 J$ n5 k/ l+ e/ [
public function update($data, $table, $where = '') {
& m; ]. R; w% e( L) F: E- D if($table == '' or $where == '') {
6 ^ n3 _; K! O8 l% |, I6 _/ T return false;
# r x- w. j# H. _8 i3 j }
% T, g! j6 r4 i+ E3 j* ^- J) e $where = ' WHERE '.$where;
: r7 _; {5 d7 B6 _ $field = ''; 4 k7 Z5 I, J* L& O t
if(is_string($data) && $data != '') {
) j9 |; {# h3 b# h9 X $field = $data; * q/ T: s8 m9 x% p4 D
} elseif (is_array($data) && count($data) > 0) {
& X7 A1 }3 Z$ }$ V, T7 ~' | $fields = array();
5 ]! c7 G* I7 p; t9 K* l6 |& M foreach($data as $k=>$v) {
2 _# O/ w* ~; O$ B3 o6 _7 R8 ?0 r" \( g: F switch (substr($v, 0, 2)) {
* a& d) h5 c$ H/ U case '+=': , M C' a# v1 U0 }8 A
$v = substr($v,2); : P& ?2 ~) I6 C* q, G6 z4 |+ q
if (is_numeric($v)) { # S) w$ {2 E N. d) E1 o" V
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); ' a; c8 D8 F8 }. p. ~8 y
} else { 1 t' _; Q: |1 l, \3 O0 A
continue;
, N* q& Q0 C; k1 r0 w! }- m. x } ) z' e: M! F% L# i" H6 F
break;
' ^" X( j0 i! }9 s/ i case '-=': d C4 ]7 r4 k5 q( G% q& A( M/ p
$v = substr($v,2); & u8 R9 T0 V' `
if (is_numeric($v)) {
9 R. o' ^ ^8 ^' I $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
. z. f; r5 P1 A z ~ } else {
3 R& Q0 m8 N' W) L# p: y, s1 X continue; ! v H+ ]- z. q
} 4 V/ `; A4 j' k8 p
break; ) ?/ T# D6 ^: v
default:
( c! m, u0 t6 O, y3 M% k# Q: s $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
+ }% E: E( x7 p; a$ Q } * A# a6 r$ w8 W3 W" e) G4 C
}
0 G* b, {6 Q- {6 \ $field = implode(',', $fields);
/ D- C! j7 W& | } else { o& A( F! P) Y9 N
return false; / e( c, Q0 _* U8 h
} * o; X: Z* x. T B
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 0 v4 d! U8 q+ v2 [! f( E& D8 }
print_r($sql);
?5 Z% [ N/ z) r$ y) } return $this->execute($sql); ! h, q2 q1 P: D
} 2 V" ~* r2 S% E+ }
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。+ K+ V3 U$ r* u1 l S* V
5 k5 F6 B& K) e攻击测试:: v, R( `8 b3 Z( }
测试地址http://localhost+ k1 a, N) d1 \/ ? ]0 K# W
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
% j; m" o' k, t! ]) c( m3 |: h. J! g3 R" _- c' W
% V- u9 z6 d9 P7 ~5 t# N/ W. R8 B% A3 |- E- k! i
2 L- Q1 X' Y/ V* e# n5 ]) N* { v, s1 w7 X4 s6 G+ b) q
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|