|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
5 ?, S1 P) L) z. ^1 f( m漏洞作者:skysheep
! M) s' {- p' c分析作者:Seay4 n0 V% K9 p, W" I) f
博客:http://www.cnseay.com/
" J6 N5 e( l; J1 s漏洞分析:) J6 c1 b& B, J3 w9 s B$ B
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
5 E) K4 C8 }. L0 A
0 J6 `! X+ z! x. M: s
1 G1 M; q% f9 C+ G; V' E I9 P X7 v; r5 ^" f
public function account_manage_info() { & z/ w0 B. I3 H3 K
if(isset($_POST['dosubmit'])) { ( g" ]: t/ V, l) @8 G9 X
//更新用户昵称
/ j' d7 V* P2 ]4 ], C" q $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
8 `% ]. {, D( I if($nickname) { ) F1 S+ o* O" Y
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
0 h6 X$ @! ^& D: q+ j if(!isset($cookietime)) {
0 w* J* \1 Y/ {8 t! p& x7 y- \5 j6 Z $get_cookietime = param::get_cookie('cookietime'); / L- } N9 S; x) E5 O$ v
} - @5 k7 ^2 t* X& L) X
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); D) |7 n( g" }
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 0 O8 Z3 \; V- e0 P5 W
param::set_cookie('_nickname', $nickname, $cookietime);
* C, K$ |) A% \: q- k, u } , T# ^+ Y& t7 F4 N+ v
require_once CACHE_MODEL_PATH.'member_input.class.php'; U) q6 q* M& _0 z; a
require_once CACHE_MODEL_PATH.'member_update.class.php'; 0 }5 ?8 x" h: W
$member_input = new member_input($this->memberinfo['modelid']); : P9 q. e" t& g
$modelinfo = $member_input->get($_POST['info']);
9 n7 `. X$ K w, ]4 w" \) @ $this->db->set_model($this->memberinfo['modelid']); + O/ {& {; Q5 k1 h% u
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
" z7 o" W, G* Y8 d if(!empty($membermodelinfo)) { / X4 C. J0 S, Z# N: ]2 t* a6 j) A
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
; n6 ?& K; @/ ^6 ? } else { / q T3 M+ X$ K
$modelinfo['userid'] = $this->memberinfo['userid']; $ V9 X# Z9 b, P+ ]$ |7 x$ T! Z
$this->db->insert($modelinfo);
7 ^" O$ C" _/ N6 [* _ } ! j! l5 Z b" v/ N+ }0 o' p
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
/ Q0 L6 o$ E! M \& [" ]3 \& U# e在\caches\caches_model\caches_data\ member_input.class.php 文件中:
% }: M) K/ P& k5 z" a- n* M0 P: [4 g4 f* L+ F; }* F
) W7 W/ l4 y0 J3 n, }- ?* j. ` 4 u/ ~5 g0 K7 @# U
function get($data) { 3 _- |8 ?. g7 s# t; A# Z
$this->data = $data = trim_script($data);
/ l8 t1 G) {! E; y* d $model_cache = getcache('member_model', 'commons'); ; t$ ]- X4 A! L) c1 G4 ?! F
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
- D& T! G! d5 U( |3 c $info = array(); B/ h) a6 J$ r/ ]9 i, ^
$debar_filed = array('catid','title','style','thumb','status','islink','description');
. Q: [4 G' @* b3 u v# b' N if(is_array($data)) {
# Y9 ]( k+ b7 d+ L# x5 u; A foreach($data as $field=>$value) { # `' L% c! T8 {+ ? m. u5 u
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
0 A9 y! j) w1 U- n3 k# T, y, ~0 ? $name = $this->fields[$field]['name']; / v; @) @& f# V5 e& m; X
$minlength = $this->fields[$field]['minlength']; # f8 u# i( p& u* x( U# R! Z Y
$maxlength = $this->fields[$field]['maxlength'];
* J! c4 ]* I) ?; x* w; R6 | $pattern = $this->fields[$field]['pattern'];
: R! L% @$ j7 L: ?* b( k0 v $errortips = $this->fields[$field]['errortips'];
9 t! K5 }6 b$ q. O- ? if(empty($errortips)) $errortips = "$name 不符合要求!";
9 T/ M$ F- l* V1 Y; f $length = empty($value) ? 0 : strlen($value); 9 w6 B: U9 O/ a7 j- c$ ?. I
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 0 k: n: m+ X! m' D5 o
if($maxlength && $length > $maxlength && !$isimport) { 0 b7 I" `- M" }
showmessage("$name 不得超过 $maxlength 个字符!");
, d/ a' q) Z: Y, C/ c+ ]7 E" [+ z } else { + W, X g$ E2 @- E
str_cut($value, $maxlength); 7 a) P! z, C4 v0 @( C& k+ h- t
} ! R/ e- D1 L, X3 d* s! N2 x. C
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
- B+ z8 F$ J6 A2 `) i/ [) @7 m if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
) g# f0 Q/ E$ f; n4 N $func = $this->fields[$field]['formtype']; / v: Z) V% A1 M" K
if(method_exists($this, $func)) $value = $this->$func($field, $value);
' h2 }& G; w* c. c7 a2 a5 ?, R $info[$field] = $value;
1 O( d' x7 t$ W. m4 b* L# s }
% ]+ ?$ w2 ]- o3 c' ^/ a }
$ _1 ~* v& I3 A- X- Y5 ?' D return $info; ) B6 }. t0 ]7 j8 l
}
: c$ k8 I m/ ^+ j1 D0 }trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
2 b& a& y1 Z7 g8 Q: e4 W; y6 Z* M% h5 n5 G) D, s
再到phpcms\modules\member\index.php 文件account_manage_info函数5 v9 k' A& `2 |! \+ [1 G
过了get()函数之后。" w( I2 _" K, ?- b! k
, h$ F( X& f, R& M" l0 m& K
5 k5 U# M9 j( I' | U! O- X6 \
$modelinfo = $member_input->get($_POST['info']); / C2 ?* e- b6 N! n. j
$this->db->set_model($this->memberinfo['modelid']); 8 {2 i1 D# j: m* j: N1 z8 u
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 7 p1 I3 w' J# G m8 Q
if(!empty($membermodelinfo)) { * R" s- y3 b+ S; D/ O+ y0 y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ) Y1 r. E( c% f' D" A) t6 v9 K
} else {
- W: N% r. ~0 `; A2 [直接带入数据库,update函数我们跟进看看# C6 t8 ~& i2 j5 N3 A
: p) N5 u4 D, N! { 0 @- F7 W4 v/ q
public function update($data, $table, $where = '') {
# f+ M, ]4 h4 c& w' H if($table == '' or $where == '') {
5 d0 t- b5 t7 g2 ]) o, O return false;
) T8 G; l5 s# R4 A1 X }
+ z3 }3 W/ d! H" ~6 @ $where = ' WHERE '.$where;
A* L, z( M4 s2 x. h0 n+ D $field = ''; & O- @( H9 f8 g: Y$ w) w; s
if(is_string($data) && $data != '') {
9 ^5 S) U, B- r: _7 L $field = $data;
/ G% G3 _3 ]0 {. u$ @ } elseif (is_array($data) && count($data) > 0) {
7 U0 y7 B- i0 }1 ? $fields = array(); / W( q9 Q u+ @ |- Q+ W9 l
foreach($data as $k=>$v) { * A1 G& r# ~' \- X9 a
switch (substr($v, 0, 2)) {
- R$ V. ^' F4 ?; g0 y+ @% ^- L9 i case '+=':
8 i1 O; [) i8 g, S0 _ $v = substr($v,2); 6 S: j) i9 F& r6 q% ]2 w# d8 F3 W
if (is_numeric($v)) { 8 |5 Z7 n7 w4 \* e
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
5 m6 k' d" W* [1 L8 R$ S } else { / p. D1 L4 } h: P) ?
continue; / [* @, n6 m3 {6 w# ~- W9 j- G; r. w
} . e4 S2 [+ P) W! t9 I$ f
break; ! Z! }% K( C' E7 ~ v" G* r! C
case '-=': # T$ B$ m9 W9 V8 a
$v = substr($v,2);
1 X1 |, |; b1 B3 n( ^ if (is_numeric($v)) {
; b# h# A, z( W |) Z5 |! s7 \. ? $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
9 s6 T( i9 ]* m; M4 T } else {
; p3 r# Y4 p) I' C continue;
' N, X# P! G' Z2 L$ @! d w }
* k# P& `6 h5 M0 m5 U0 M break;
2 F4 n+ }2 x& t' K I# B. C default: 9 @9 E) e" C* u: K
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); + B6 |% _/ U7 @& l) u" \& d9 \
}
7 I! |5 w! d3 L" X } 1 z, @3 R/ ]8 V. |4 D7 C
$field = implode(',', $fields); s) \0 _$ F0 t# e7 y% S
} else { : Y& }! T {5 Y2 |
return false;
" O8 j+ ~# p; T2 v }
3 j5 d' ^% {+ Q; O $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
8 S2 K- g B0 ~/ }, P S print_r($sql);
" c% M. B/ p: @4 c+ v" ^ return $this->execute($sql);
t- d7 S4 R( x) M B, V9 Y3 V }
% A9 K# y& N; g6 u从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
2 ]3 s8 Y5 q) N% p. _) J% B* [& O5 `! ~/ `( N$ l4 ?+ o
攻击测试:9 B; [. ^) Y0 G
测试地址http://localhost
' [% q2 e3 L2 l7 n' f 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句2 f3 g0 V+ ?$ f
7 { h) b% C( e! j6 b! L. ^
& z7 a1 |# v. b+ a- @4 n% }+ V, k' Y1 |
! i: y4 G+ e! I! \' E j+ [5 `1 C5 V- p; N( T
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|