|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
, L* c( C' E" V G漏洞作者:skysheep
$ Q- M- `' x0 W" y% s; _分析作者:Seay
% J: Y. w) ?% {$ e博客:http://www.cnseay.com/
2 x7 r) Z( n- u _# {( w漏洞分析:& @" Q) V: t. v
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
0 g& q2 ]0 W' q" `$ F" O: J! U5 l! v3 p) H2 R4 O
: l' M6 a p9 I8 Q2 M
* U: I" K6 e% z9 Y# bpublic function account_manage_info() { 5 L$ Z h& J( x
if(isset($_POST['dosubmit'])) { 4 u) d$ p8 K, E8 {
//更新用户昵称
, ~" I( ~, \3 J. j $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
}9 | p( H$ J. G if($nickname) {
3 V# y4 Y& k3 E$ l v+ ?; E7 f! d $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); - H, c! W0 r9 p% g% [, L
if(!isset($cookietime)) { $ }; Y3 w* ^( m* Y2 h$ I, b
$get_cookietime = param::get_cookie('cookietime');
7 q, c9 j4 I% q6 X$ r2 I8 r } " @" l) O5 E' S+ }
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
4 L A2 ]$ s) n! K $cookietime = $_cookietime ? TIME + $_cookietime : 0;
6 H) P' k7 c% |3 U param::set_cookie('_nickname', $nickname, $cookietime); / E( `& g( a5 l+ L4 l6 u# }+ n& C
}
8 v0 o$ y# C0 `1 W" w3 m( D require_once CACHE_MODEL_PATH.'member_input.class.php'; ; \$ q9 |7 a0 A$ l
require_once CACHE_MODEL_PATH.'member_update.class.php'; ' t/ w/ ?, Q8 k0 A8 p
$member_input = new member_input($this->memberinfo['modelid']);
3 ]( @$ b- B6 s+ j( @ $modelinfo = $member_input->get($_POST['info']); 8 Z) w C" |( S) B/ m& R& I
$this->db->set_model($this->memberinfo['modelid']);
2 }; _4 i/ Z8 @6 @" R. r $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
; u0 M V) h4 \; B! v2 h( x0 L if(!empty($membermodelinfo)) { " S4 W V! Q! T) Q8 l
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
7 c; O/ T1 [& d9 C } else {
0 z8 y' C! W- z* q4 X4 O1 f" M $modelinfo['userid'] = $this->memberinfo['userid']; 0 e+ Q+ V; c" x: `
$this->db->insert($modelinfo);
+ K. A, R5 Z4 w }
3 r+ `* m5 Q6 r0 ~! [代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,) P) ` x% M7 `
在\caches\caches_model\caches_data\ member_input.class.php 文件中:) b9 L7 D/ D S$ }/ W1 ^
+ x6 m) x3 C1 M# Z/ V2 `8 o7 B7 c, w; f/ l- F( v( s' I( \
+ p) n0 h" _( F- ~* t! s1 V. \
function get($data) {
$ i5 y; \* ^: g- U5 f$ ~ $this->data = $data = trim_script($data);
, C3 D: g, @( s( S$ P $model_cache = getcache('member_model', 'commons');
- n& a' d, A% }: ^8 |( t5 r $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 4 {7 j* u1 {, Z) R5 }; Q L+ _
$info = array();
/ U% s7 \& ?8 ^$ w( i5 t $debar_filed = array('catid','title','style','thumb','status','islink','description'); 7 l" O; g) [* e8 R t
if(is_array($data)) {
$ a, l& }( X; o& V- L& F7 _ foreach($data as $field=>$value) {
0 G* K8 v# O) D$ J1 k I$ O if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 4 V1 k7 x: N4 h
$name = $this->fields[$field]['name']; 9 c$ [; U) Y2 X9 R5 y$ r( @
$minlength = $this->fields[$field]['minlength'];
+ O: `7 z1 d- H! u8 W' @ $maxlength = $this->fields[$field]['maxlength']; 1 s. R% e+ L3 j/ T2 J
$pattern = $this->fields[$field]['pattern']; / U! t6 L6 Q9 d4 Z0 t
$errortips = $this->fields[$field]['errortips']; 5 ?" Y0 b! }% J/ w: l, Y
if(empty($errortips)) $errortips = "$name 不符合要求!";
6 G. e" J7 w! c1 Z( ^ $length = empty($value) ? 0 : strlen($value);
7 j R6 M' O& p- t7 i. l1 D3 G if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
7 o/ @# c: c4 Z8 G, w. h" m8 m if($maxlength && $length > $maxlength && !$isimport) { ) z$ C5 L2 U7 q+ N% n8 ~
showmessage("$name 不得超过 $maxlength 个字符!"); + a F6 h+ M2 T) D
} else { # i/ C$ Q( A% B- g( [, o, u
str_cut($value, $maxlength); 6 z$ ^% n- s$ J! ~, N1 x
} 7 M4 Q, }8 E" |
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
+ n% M% {6 W9 } g/ o/ [" q! x& w% \ if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
0 ~; p9 T6 q4 N& M! P3 y/ n $func = $this->fields[$field]['formtype'];
3 a. c( s4 L9 x1 p. [8 @ if(method_exists($this, $func)) $value = $this->$func($field, $value); 8 U2 c7 D* L2 Y- y- l3 B! j- R" {6 ?
$info[$field] = $value;
( i$ U* R2 Z( B( j } . V* D+ h& i7 j0 y) |- K6 p( b
}
0 K; B' _+ l5 Q return $info; 6 o- x. e) z0 o& N1 g0 W" @* U# ^/ _: [
}
8 r$ Z! B7 Z- ~: itrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,' R! D2 q% W J2 }3 a
- C2 x. ` } m! @$ s, r; ~" a再到phpcms\modules\member\index.php 文件account_manage_info函数; [: S: l$ H' D: l
过了get()函数之后。1 F2 ^7 |2 A: V8 X/ E' e
q. G% C8 B5 H5 L1 V5 S
: k6 V9 Q8 n3 f- Z0 y0 J% L$modelinfo = $member_input->get($_POST['info']); 0 h$ @- r1 I# F, q S- s
$this->db->set_model($this->memberinfo['modelid']); $ @' D- d+ U, v9 v
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 2 @; l1 b# ]5 f) F
if(!empty($membermodelinfo)) {
: y6 u6 v8 V( a! o u) f% h6 h $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 8 ]) ~; H& g% x5 G# ~ m/ M3 ^
} else {
5 u& L8 G. ^* d7 A' E2 q0 u9 b直接带入数据库,update函数我们跟进看看
! T4 v k* _2 g# P) d8 v7 p. @6 j
; R; b# E; I2 O) j0 U) Y : t/ U0 u8 q f4 \. B m
public function update($data, $table, $where = '') { ( v! M0 K6 ^4 M: w
if($table == '' or $where == '') {
# W% _, f7 O2 O8 l% q$ n+ F* ~ return false;
( c& G" J }/ o8 X, c8 i! ~ } 7 f5 t& h" p! j! h
$where = ' WHERE '.$where; 1 |* }1 J& u8 `8 Y6 {* |
$field = '';
- _, }5 Y; B& I if(is_string($data) && $data != '') { 3 n& y7 e- W# ~
$field = $data;
* j4 J w7 q# z2 Z0 \1 [6 T } elseif (is_array($data) && count($data) > 0) { & _8 m0 k# p @ z7 D
$fields = array();
* R) C* ?& Y( p1 o% b( y; f foreach($data as $k=>$v) { 3 F" }+ p& o5 `+ T5 w
switch (substr($v, 0, 2)) { # P& y" u6 G- U
case '+=':
& v- U5 {5 k, w6 ]" o& |# l $v = substr($v,2);
' @5 l* u0 u1 m$ y# [7 g' N# } if (is_numeric($v)) { 3 x' ~% E+ m4 I& f" H
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
- V8 b) n3 N/ i$ I' x. i } else {
. d5 V; v) b0 z& A7 L! Y continue;
$ n( z# a' a2 k) k } 8 Q: K* {4 F4 ^: z0 U, ~: V
break; / Z" i$ l! o6 c3 d) G# C6 B
case '-=':
4 o8 n- M H7 ^3 Z1 {# d0 R $v = substr($v,2);
: N* {7 Z3 N, K! g! N8 i if (is_numeric($v)) { ( @) B9 f$ {1 I' K% v
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
4 b7 g e0 K# n5 [! j } else {
$ Q: ]5 l5 o. y c# w- R& R continue;
+ j, f( Z3 C6 F }
' c! J2 M# @3 N( e/ S% U* u6 ^8 ] break;
. n" U) @0 O4 w5 U. q default:
j7 ]9 S8 i7 o# L $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
: b8 m, A( W9 ^% } }
% x- i' D: [& Q* j3 _6 l, l+ R } / j1 C3 f+ t" a( y
$field = implode(',', $fields); % y- S, @2 X% E: G' |8 k; y' M
} else {
4 X O* T" A+ J( K return false; 1 f: U6 {# N! R7 |
}
7 v, Y4 w: E9 G8 i $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 0 o$ r, V& N0 W& f) D
print_r($sql);
1 F, r7 r) X8 A8 }9 _. l return $this->execute($sql); # w- k C3 Q k! E
}
# J$ C. G) z: {3 U4 x从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
$ t4 j8 {9 I" p/ ]- l: @7 _# V
! \% x8 x$ n6 I& l7 B" @! K攻击测试:
2 _ Z d$ Z1 K8 e# f6 Z测试地址http://localhost
5 e# i+ G2 m! }) I0 k 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
8 ^0 d g N& f0 g5 U* g
/ {6 B6 @7 W5 k, j ( b; x( e s9 A) c! V5 F" b6 i1 s
) f4 a0 L+ A4 f; ~! V4 F
$ R3 k3 _3 \* }4 G( x* ]8 H0 V H, V5 i
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|