|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告4 B1 n$ X! h {
漏洞作者:skysheep
; T1 p3 N I' I5 t分析作者:Seay
6 Z& }( M5 a: D- r) M博客:http://www.cnseay.com/. S @/ d+ f9 ^) v5 I$ E
漏洞分析:' C/ l- S; @7 }7 i4 Z
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。8 D( p6 `1 p, Q ^
a6 _+ E1 D* I/ X4 Y* o2 ]
4 K% i( N, ]0 i1 G" q! q
" B* Q) L& b- l8 O3 u* n- Bpublic function account_manage_info() {
9 L1 m% A; l7 S& x8 E, l2 ^ if(isset($_POST['dosubmit'])) {
! ]! g, m& `$ h' i& v //更新用户昵称 ) Z8 c- O4 y6 e% N3 @# B
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; * c7 h$ U0 |- A& U
if($nickname) { 3 C8 }& j1 o# _" ], B$ [* B
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
- e+ o" \7 r" s if(!isset($cookietime)) {
" J$ W2 P2 M; p8 U3 Y $get_cookietime = param::get_cookie('cookietime'); 2 `, v9 Z% e6 i1 E
}
* X5 ]( I8 N/ I. N% E1 s $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 4 ?6 G0 T7 a& L: o5 a
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
' }% j# y' ?+ n param::set_cookie('_nickname', $nickname, $cookietime);
8 f$ @, ]% g" Q# }6 y% I }
6 c# E- m/ h1 B# p' R require_once CACHE_MODEL_PATH.'member_input.class.php'; ' I! h2 {* c9 L5 i# q& T: T' _
require_once CACHE_MODEL_PATH.'member_update.class.php';
- `4 x0 N8 n! Q- u: w, w& t* w- E $member_input = new member_input($this->memberinfo['modelid']);
! F, {1 a* U, ~6 v& H& b $modelinfo = $member_input->get($_POST['info']);
# T8 [, {1 r+ G3 @5 n6 E* c+ P* u $this->db->set_model($this->memberinfo['modelid']); * J. `+ s( \$ ]
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
7 _" e5 I `2 j2 \5 ~4 ~! i7 v- v" E: A if(!empty($membermodelinfo)) { & V8 B; [1 e, ^' z+ s
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
( R4 x( i w! F$ V1 N- D( _ } else { 0 x8 H6 d5 p: ]9 V! O5 m) P
$modelinfo['userid'] = $this->memberinfo['userid'];
5 C& |2 @6 j8 B( i" J $this->db->insert($modelinfo); # F* B, n+ a, ? Y8 B
} & P$ ^ L I; u3 T9 r; Y/ U& D
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
, F; T0 [1 [" N, Z2 D$ b2 ?. k+ ]在\caches\caches_model\caches_data\ member_input.class.php 文件中:
& j! t( Y% S, r0 ~: W( r' F/ @% M) q: m1 @7 _; \+ g6 C; Y9 _
( p( r# k! Y1 x. \5 u+ o
; S5 h9 d4 O& P* I. F2 _8 f# w# y/ zfunction get($data) { . _3 c" \ p5 a3 \! w8 t% A, ]' D
$this->data = $data = trim_script($data); : h) f5 l. o3 q. M* W
$model_cache = getcache('member_model', 'commons'); ! ~/ p2 G& ~& Z" k& Y
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; $ V9 f& q# L) j% _6 m
$info = array();
. c D( f! H7 H2 i, L6 a $debar_filed = array('catid','title','style','thumb','status','islink','description'); 5 z& f8 J) b# s
if(is_array($data)) { 5 X, M9 o, n9 F. P, X) v
foreach($data as $field=>$value) {
' h9 y q& H/ ^0 u8 B3 Z if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 1 E% [1 I( {5 ? j; K
$name = $this->fields[$field]['name']; 1 U' h) H+ N- j4 K
$minlength = $this->fields[$field]['minlength'];
: q. Y! v; ^/ e4 @: P$ b( a* x $maxlength = $this->fields[$field]['maxlength'];
- |1 C8 e, }, Q1 f% n- c- p) S3 m $pattern = $this->fields[$field]['pattern']; + v0 T5 o- |1 q) L3 S7 Y: r( C
$errortips = $this->fields[$field]['errortips']; / t. O- Q o3 @# j* B# V5 ?: p
if(empty($errortips)) $errortips = "$name 不符合要求!"; : X- b7 {1 o j6 Y4 P
$length = empty($value) ? 0 : strlen($value);
# N0 I7 g r1 w7 N: [$ A if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); / X A4 h5 j% k4 {4 m
if($maxlength && $length > $maxlength && !$isimport) {
$ x, _2 u, e( u3 Z" H# `& a showmessage("$name 不得超过 $maxlength 个字符!"); ; {: I2 E, k# `$ X8 u; f3 w p
} else {
o9 `$ t5 F$ q8 Q9 G0 D str_cut($value, $maxlength);
5 t; Z X: P. w$ f" R8 P } # o* b6 u: r* O- K1 a% y
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
$ K% {, r; x( a. k) X if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 4 ^, i! F/ z* q! j
$func = $this->fields[$field]['formtype']; ) b( X! `" V9 ]3 |. Q
if(method_exists($this, $func)) $value = $this->$func($field, $value);
7 N5 S2 `6 `8 d3 H& D $info[$field] = $value; : l& C# C+ R. b$ T- m m
} + P% }! X6 o: f2 C' t$ H
} 3 D" D' u+ M$ l0 u1 c
return $info;
- F& f6 W, p6 H& b4 o0 X3 O } 3 x6 x# c4 t" l7 `1 D4 P5 P# [6 u
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
: L% }- y) _3 E" i; V
4 P( w# U. p2 M8 O. Z' Z% t再到phpcms\modules\member\index.php 文件account_manage_info函数; k# r- ~# h% ?: S( V
过了get()函数之后。5 C3 s- s+ j: u# D
3 f% c' K+ r9 y; {0 e4 Q5 U }0 h& F + o1 D" B( }0 D1 d* v: c) U9 m7 _- n
$modelinfo = $member_input->get($_POST['info']); & q+ b( E2 g2 R6 [
$this->db->set_model($this->memberinfo['modelid']);
" y8 r' w9 E# C, m1 S $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); . {6 w2 @7 d( A2 U
if(!empty($membermodelinfo)) {
* {# D4 V* u9 U- V% A* g $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
- N; H' |( L, G$ y8 v. ` } else {
- x; ?4 c+ W; W* [直接带入数据库,update函数我们跟进看看
" ~; o: f3 a8 t' ^+ X. X
, X( o+ [! A/ w, w( i8 h
% Q% ~. X$ Q. u7 u7 e7 I+ ~+ Z1 |public function update($data, $table, $where = '') { ) a8 O- @2 Q0 \! N" W5 N
if($table == '' or $where == '') { ! q' W# @) f9 C. t/ H
return false;
. z9 _$ @3 T1 D# A& q( E, [3 g } 2 T% H2 r2 z# t' T- ~, o8 }. X1 e
$where = ' WHERE '.$where;
% e0 l7 d. F9 J: Z" m, a, }: |5 I8 C+ D $field = ''; / o- V" T' S( C; G! V. {
if(is_string($data) && $data != '') { % Z; w: i4 P. x0 ^) l
$field = $data; 3 |% t/ F% I- w1 H4 h
} elseif (is_array($data) && count($data) > 0) {
0 h) m! U* a8 o $fields = array();
g9 ?! k5 q* z; I- D6 P/ Z6 B& a foreach($data as $k=>$v) { 3 w9 F8 h1 \0 n% q' ?
switch (substr($v, 0, 2)) {
7 T8 Z, f( G* _" b case '+=': & C5 L+ i2 B8 L, v8 N/ T& o
$v = substr($v,2); . J' `: q, T* e# q3 A# Q
if (is_numeric($v)) {
2 }( B8 K8 c @4 ]* V" r $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
2 K$ Y* g7 [' i3 |- R4 x% g" A } else {
4 f( a% q1 w: ^& U( E. b continue;
* I# X. U; I- e& n9 Y9 G3 L* b }
% j8 e0 K2 X2 U( r" Q- V; ]( o break; ; A, u2 C& [6 i$ a
case '-=': , [9 O: `2 h8 J) R3 V
$v = substr($v,2); ! ]1 P) j% |' I# {* a+ A
if (is_numeric($v)) { ! g, ]" A& f# g4 `
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
3 N" U. f: v6 g0 _: `) a } else {
; H' _1 C! x$ a# \6 b continue; 1 q, N O" ?0 C) c$ ?* u6 h
}
; G4 D8 F+ j' v1 B) ` break; 9 U" Y3 L: z, r4 @& ~* [( b, z; ]
default:
5 a! c) ?) k4 m" p9 |' {4 Q $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
' n" o* S, }6 W B% { N } & l& q* A- i. c5 R" E( P% h, g
} : u) X7 ~) w9 M8 h( R
$field = implode(',', $fields);
: u& M5 k( N8 j } else { - F+ Q: u! \0 G2 q% i/ C& t$ d! O7 b
return false; 3 q( B. ?7 w" H R, N& b, g
} : _ }* c/ }- R6 b1 B- h6 @: u
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 7 x+ |) `! r+ c& N4 |, D
print_r($sql);
2 B9 D4 C& n! ^2 l! y2 R return $this->execute($sql); ! ]! S7 F. i" U7 Q2 u. H
} 9 _/ `7 Y( a" x* {; ]
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。5 O) n5 U; R8 m, k
$ C; x) [- l& `! P攻击测试:
/ B* h+ o+ Y9 \; F; ^1 T测试地址http://localhost
4 T3 \: O6 e+ Z' X8 ]/ u. S 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
. O* c0 k, v& g$ n k
9 z& O& L3 ^5 C( C+ B) V/ K : B7 K: `' ?( Y
# B+ x; c$ T9 w: j; B% p
6 f; ?0 c6 u0 ~2 [; ?5 c) H ~+ q5 q2 \' l' t2 x# D
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|