|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告* z0 _9 E, j% q1 c1 ~5 R, o Q, x
漏洞作者:skysheep+ d% I( w- e) Z; h3 C
分析作者:Seay
4 l7 \& z3 ~( T1 e& z, b( o' O博客:http://www.cnseay.com/+ ^$ K3 O- Z; V9 a' r5 A
漏洞分析:
# r8 j, B `( H: ~ 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。* |, U5 E- }3 H* S
" w9 D3 I5 U m P9 M3 N6 P( i
3 p6 }9 |2 g+ q# Z0 s
$ J. e0 _% X1 @/ T. cpublic function account_manage_info() {
2 Z Z8 T @1 p- B' z if(isset($_POST['dosubmit'])) {
" b7 ?1 m- ~5 F1 y: O0 o //更新用户昵称 2 I. l @9 J1 N" D0 z/ d( T
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; ' Y( r2 Z5 |% [2 G; z( E% d
if($nickname) {
, c4 B9 a9 B1 K $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 0 x% s: o2 z/ N# e% s3 M
if(!isset($cookietime)) { . g3 n' i+ x9 q* t% N/ n
$get_cookietime = param::get_cookie('cookietime');
1 }' L, l% o/ l } , Y) E! |! G5 f# U3 o# C3 j; a
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); / J6 K4 K: U6 n2 Z9 Z9 n) Z" O
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
4 o. Y7 R7 M) K3 B/ \: [6 C param::set_cookie('_nickname', $nickname, $cookietime);
. K; U2 v4 e0 C$ X) c% ~; J }
6 N5 j3 b T" h7 a* e8 R9 ^: K! l require_once CACHE_MODEL_PATH.'member_input.class.php';
' Q' k+ r1 E. T9 V require_once CACHE_MODEL_PATH.'member_update.class.php'; / K4 b1 X. W$ ~9 p( u0 }; d
$member_input = new member_input($this->memberinfo['modelid']);
% u o" h, |7 V* j7 y; { $modelinfo = $member_input->get($_POST['info']);
* X1 \ ]/ t- {# u% @! L4 W( E $this->db->set_model($this->memberinfo['modelid']); 6 d: f+ b$ S$ q5 t
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); : Q# j% o: W4 C6 I# N
if(!empty($membermodelinfo)) { , [8 w& y: s# D6 T3 \+ H
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ! x* G! _/ h$ B/ m
} else {
8 a; m. Z' Y# k% ]! u8 [2 U $modelinfo['userid'] = $this->memberinfo['userid']; ( u$ n) [$ w3 J. C1 B2 _: n8 e7 X
$this->db->insert($modelinfo);
. k/ M6 A/ j0 E# R } 1 ?( o0 Y. K' `
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看," \! h% _ W* ^
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
6 m% R0 ?* u U0 y
7 F a! Y. O; I5 r" `
* u% O% ]% g2 y3 P
* o, \( Q/ F& a hfunction get($data) {
1 K3 I' \9 J. N' v* @* c $this->data = $data = trim_script($data); 7 ?9 B; b" B' M8 c8 y8 M# a
$model_cache = getcache('member_model', 'commons'); ( o$ u: g. |2 W1 ^; z
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; % w& Z; P! a& J! T; q
$info = array();
' j- y0 U9 W( n" T $debar_filed = array('catid','title','style','thumb','status','islink','description'); - }4 ` N' d9 E4 n7 W( w; v. c
if(is_array($data)) {
: t! {- K+ j. r4 Q" H Q foreach($data as $field=>$value) { + v. q5 L; |$ Y+ Q5 G) z
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
3 ~* I9 r" Q3 o $name = $this->fields[$field]['name']; c1 ?- `7 Q V+ N5 V
$minlength = $this->fields[$field]['minlength']; 3 b( D: q/ j8 R5 W* [# W! `2 ^0 x: N
$maxlength = $this->fields[$field]['maxlength']; & v4 v" u4 X4 r- K% y
$pattern = $this->fields[$field]['pattern']; - v" s* R1 w) S0 S7 z h" l( d
$errortips = $this->fields[$field]['errortips'];
3 H: Q9 T" _; M# {1 Q- y8 z if(empty($errortips)) $errortips = "$name 不符合要求!";
' J! Y) R- c7 y5 J. q $length = empty($value) ? 0 : strlen($value);
; D5 {- {1 j& H1 _ M if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); / m3 s6 h; B; a& y$ z# ]5 t( ?3 b
if($maxlength && $length > $maxlength && !$isimport) { " B9 i' a$ g9 v( b5 \9 {/ [ b
showmessage("$name 不得超过 $maxlength 个字符!");
9 V; C4 Q1 E% }9 ~- a+ F% N } else {
3 i& ~7 r3 `% y1 e str_cut($value, $maxlength);
6 O5 U5 o& y+ S7 T }
5 g# x1 Z3 _ v" T k% h$ \1 g if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); " S9 R* F& W& ?" C# U/ X- Z
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
' H! m/ r& N" T/ U! b* A+ S D b $func = $this->fields[$field]['formtype'];
: q) J! r* [* T! x4 [ if(method_exists($this, $func)) $value = $this->$func($field, $value);
& u1 `# }2 b. O4 T% O $info[$field] = $value;
$ w: p6 b9 |8 S: r. v$ D$ Q1 h5 ? }
* [- y; o$ p2 Z5 A: n! Q# ? }
; A- V/ t% B7 r% T% `8 C" K( Y return $info; ; c1 ?( j2 M/ |) h+ V$ b8 k1 ]8 m
} 1 z& Y* `+ v1 [8 z- |
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组, @0 k; u2 E4 Q1 y$ g! g \
, s0 J& U7 I. x' d' H再到phpcms\modules\member\index.php 文件account_manage_info函数
" J% m( {! X& v ^过了get()函数之后。( P6 S# O; y- f
- A: H6 p7 |' @; R
3 s1 _: g, \$ V7 X$modelinfo = $member_input->get($_POST['info']);
) X8 S/ C( }) U1 A7 h9 n6 ] $this->db->set_model($this->memberinfo['modelid']);
$ N9 _ r+ L# s& L- j+ X $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); * M/ H* N+ U+ B2 \) w; w: N5 q
if(!empty($membermodelinfo)) {
1 q$ Z5 L6 F7 J. A& | $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); , n9 P9 j5 K) L
} else { $ z; N4 P2 M5 @5 u
直接带入数据库,update函数我们跟进看看" k4 ~* X2 w# ~
, V- S6 T4 j" @, A" K* N
! ?. k8 s& s: k% Upublic function update($data, $table, $where = '') { - Q$ @5 ]6 R$ g1 w% b
if($table == '' or $where == '') { 1 O, \( j- n: m) b3 G% I4 a
return false; 1 b- \& O# E" Q C8 Q/ H
}
9 i0 Y, a6 N3 m9 P! l2 q" J+ j $where = ' WHERE '.$where; 7 ]6 k# f# V# {1 [, u K; F6 j
$field = '';
* c' ~) {- U+ J4 A& ]( |# l6 F if(is_string($data) && $data != '') { . E9 I: S# W' p4 D
$field = $data;
, M/ P. W4 L9 {5 H6 m- V0 F/ G } elseif (is_array($data) && count($data) > 0) { , \ B/ p! p9 z" w7 P0 q
$fields = array(); 7 }" H q) O* V/ l; j! V/ a
foreach($data as $k=>$v) { 9 D; x- M7 _ X5 [. ?9 m3 D
switch (substr($v, 0, 2)) { . K: u9 O$ Z! d; M3 ]: Q! L
case '+=': + C, \ X' b, l8 ^0 r
$v = substr($v,2); ' |! Z2 l4 K& A7 X% M
if (is_numeric($v)) {
# d) m0 S& M% O# l' }6 H $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
7 m# }' b0 u( N0 o1 C } else { ! Z( o0 E6 Z" K$ N$ F
continue; . t( Q" }6 v3 i7 O- U6 Q
} ! {1 F- s8 V4 f! Z
break;
0 ?1 \$ [ C5 R$ g- \$ l g. B. w; u case '-=': 7 x( M6 S I P. ~ r; E6 `$ d
$v = substr($v,2); , ~8 H/ I. J; R* y2 m% D; s6 ^
if (is_numeric($v)) {
$ [% a3 H2 m2 J u- o $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
; |2 ^5 e, D$ ^8 V } else {
, W! X" F* x6 k. D) T- T8 ?8 o continue; . n$ D0 n1 g4 }1 s. Z; r# E7 u
}
' k. }+ Q/ y* B2 k$ r; P break;
8 v4 U# ^& R4 R1 z7 }$ N, U default: 3 Z: d0 b& n# f6 g! _9 s! J( E0 N
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); % Z$ `; j+ n4 r! Z/ H7 K- S
} 2 N2 s" ]) D: ` b) P
}
0 p3 ?- P \/ Q* G) j \4 H o $field = implode(',', $fields);
( W- q/ c# M4 h! [9 |1 j' Y6 u1 j } else { 0 L, v' C/ e" a& V, B* u2 O
return false;
- {7 d) a5 P' q5 {- x }
6 d8 H1 M9 u7 P $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; : b* G6 P0 i+ f+ g! \8 k5 F1 u) f
print_r($sql);
& _$ n" i c7 Z& h/ O7 s2 \ return $this->execute($sql);
" ?9 J/ r& Z& D0 Z: q } 3 ?' q+ F" V( Y8 ]
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
/ n/ ]* O0 |/ W* y3 b1 }6 I' _: t! a. z3 ?2 S& }% ?6 f+ S; E) }3 n
攻击测试:
3 e6 m" |. b# F" G8 H0 v0 {6 s/ G测试地址http://localhost
5 F. H3 z% g U" C; T 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句8 H/ S$ Q0 q9 y8 i) W9 n
$ b' m/ o8 E3 R/ w
4 T8 e+ w4 L$ ~, D5 `2 p% r* n9 U" u0 b* O
; E; [! J3 R) T9 y7 V- B! c+ i; n6 \3 b; I
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|