|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
; U! {. {- Z+ | l# }漏洞作者:skysheep" E8 F) R" t* [. J5 M" y
分析作者:Seay0 R) e8 y4 w0 }9 c+ F
博客:http://www.cnseay.com/
1 e8 H3 H6 k* X6 D漏洞分析:
O: N+ b* H& T5 }4 v7 C 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
H* ], v1 k! _5 a. Q7 S$ A
. c8 G5 O- D/ G! V# r' _; x; z$ g+ `9 x* o; n: B& T3 Z
. T8 f$ |' a* [9 F! apublic function account_manage_info() {
# S9 l. D( b! f! ]( B if(isset($_POST['dosubmit'])) {
+ L @0 l/ e0 q6 f1 S# X h //更新用户昵称
6 E1 o9 p( w8 R' j( c- K# {% h# l $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
- H$ F6 \& P/ i: I* \) O. f0 { if($nickname) { ( |) t1 V' \# F* k$ ^( B. a
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ) ?. ?, ~1 g" L+ Q/ D; y3 c) L4 B
if(!isset($cookietime)) { 4 @, x% k, D. U
$get_cookietime = param::get_cookie('cookietime');
; k2 ^2 h3 ]# c4 D0 R }
4 r+ T3 \! Q) h9 h8 \ $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
! E6 H. O' m A. k# ]' |9 C+ k $cookietime = $_cookietime ? TIME + $_cookietime : 0; 4 v0 y+ h8 n$ c1 W) ]
param::set_cookie('_nickname', $nickname, $cookietime); 6 B. ]5 j5 E, E5 K* G, H
} 4 d! ~% {+ Z2 O# m
require_once CACHE_MODEL_PATH.'member_input.class.php';
) t% P/ O, J6 L; I, `2 W5 Q2 G require_once CACHE_MODEL_PATH.'member_update.class.php'; ! R: m ~4 D) Y6 j$ u; s. f$ }
$member_input = new member_input($this->memberinfo['modelid']); 7 J. K$ w4 w6 h% ]( y
$modelinfo = $member_input->get($_POST['info']); : `, E7 E. p: ^
$this->db->set_model($this->memberinfo['modelid']); W% u' }* ~3 I/ V
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
( i- R( A% ^+ S+ J; p$ M5 h if(!empty($membermodelinfo)) { $ u4 K) s$ [- r6 W
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 9 U4 {; X6 H2 t7 E1 Z
} else {
F0 l- e# _# t! F* \ $modelinfo['userid'] = $this->memberinfo['userid']; 8 w. U. a# p, |5 x
$this->db->insert($modelinfo);
4 A& X% `9 ?& N4 r( W2 p4 _* N } ( K# q0 V6 i, m$ P6 ]3 [" |( b
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,# f! Z$ @; \& `' x
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
7 e0 x/ P. |' `2 F% c1 ^( z
7 c- [0 \ C% E) w( v
% y, Z/ c6 W2 d2 B6 l 2 i: w* o: j/ P8 q S- {& G
function get($data) { ! J/ N8 n; r* R% I
$this->data = $data = trim_script($data); ( v- Z" g4 V! k3 ?% L: |
$model_cache = getcache('member_model', 'commons'); ' ~$ G2 f! i8 W4 g; y& {
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
& l$ x& _- k1 U/ b7 }4 \* v $info = array();
" D1 n# B2 q# X& h# k& B0 z" d+ Q $debar_filed = array('catid','title','style','thumb','status','islink','description');
* r) f8 v' }7 N, k1 s! H9 r if(is_array($data)) { 5 v6 I4 K2 u+ e7 C( j$ j" e
foreach($data as $field=>$value) { % y% |& V/ T' I! ]7 n n
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
0 |1 T; T- k9 R( c8 K3 h8 r- H $name = $this->fields[$field]['name'];
( y% m) v, s% ]9 B f5 n# ~ $minlength = $this->fields[$field]['minlength'];
2 c* C: s1 A7 @- W/ V $maxlength = $this->fields[$field]['maxlength'];
6 O! ]8 a [+ D2 j" h( Q: j& y $pattern = $this->fields[$field]['pattern']; 4 b& M% |3 y7 {# N& b
$errortips = $this->fields[$field]['errortips'];
9 P! E8 W1 i& }. `/ }( a if(empty($errortips)) $errortips = "$name 不符合要求!"; + _7 A- x2 O7 T- p) K/ P
$length = empty($value) ? 0 : strlen($value);
T: s7 s7 C5 u" d9 h9 E5 u7 n/ }& ] if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
& N1 N2 W' T2 k: p if($maxlength && $length > $maxlength && !$isimport) {
0 b* b+ c8 A7 W W$ g3 I$ I showmessage("$name 不得超过 $maxlength 个字符!"); 6 S# d* F2 F0 K/ M; E
} else { ) X% V5 w6 e) w8 B3 B
str_cut($value, $maxlength);
9 K. T7 J7 \! M } ' m5 i+ p' y3 {7 F7 U) Y
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
6 |( E: O8 V: Y- r8 O: ` if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
: y: {* b5 E, H+ d% h4 `: F$ _ $func = $this->fields[$field]['formtype'];
6 o- f% n* J' u3 i1 R% X W if(method_exists($this, $func)) $value = $this->$func($field, $value); ! j* V- r j* A, b; t! g
$info[$field] = $value;
$ G3 S/ P5 B5 _; i5 E } - v1 _% q' _7 m/ d# J: f0 c7 ?
}
) T b, e. P( `8 p, \0 e/ x: \: V return $info; " z0 _7 u( q `4 [ v- W! ?. [
} 2 }, ~. N. C' A2 R; \$ M
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
$ K: Y6 y, x ^- V/ Q# K! S! ~
. [( n) o4 f0 |* l( b/ T/ _再到phpcms\modules\member\index.php 文件account_manage_info函数/ I) _5 g% H& ^7 n+ G: c3 Y
过了get()函数之后。0 f8 D2 j' y* _5 C
; [) T8 X9 ~6 N; G( f
* L. M5 F5 Y9 B+ ~6 `$ t: {
$modelinfo = $member_input->get($_POST['info']);
( B; m* d- x8 F/ h2 V $this->db->set_model($this->memberinfo['modelid']); & j1 q# P* T# R+ q( E
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ' d; r' W0 m% l. L6 ~8 A
if(!empty($membermodelinfo)) { " o1 ] h$ H$ r! G# T% Y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
" h# Y# U: t L( o# p% @# g } else {
8 ~* [& D! m6 x% G! C/ l直接带入数据库,update函数我们跟进看看
0 P: C0 q7 Y# E2 F/ g/ K. M" U/ v! a! @$ H% A
V) d- R) p6 E: } K. Q9 O' E; R
public function update($data, $table, $where = '') { 0 e4 G- T4 I3 B# G
if($table == '' or $where == '') {
8 }( \% v/ R/ d- R% b7 x: j! ^ return false; & a; a, ~1 F, R# m6 R* i
}
' N/ S* |9 ^6 V9 V $where = ' WHERE '.$where;
& a B4 G" R" r* U7 M5 f( R $field = '';
" Y0 R+ X% E: I) d- X+ g9 O8 E if(is_string($data) && $data != '') { 2 M4 k& y# i* {4 w4 C
$field = $data; 7 l# ~3 t* d+ O
} elseif (is_array($data) && count($data) > 0) {
# A! s7 r% ^2 V3 k/ W7 @ $fields = array(); / i g `# ^0 I8 H: I+ P, h
foreach($data as $k=>$v) { " b5 G3 E. l- I. i& Y
switch (substr($v, 0, 2)) { ! S* d% z" M0 o* l% M
case '+=': , w% N: R8 l1 f- ?
$v = substr($v,2);
( V* M' c0 x' L% ^3 v R6 g6 E if (is_numeric($v)) { - C( ]6 J$ F/ E$ W$ `
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
5 p9 p* i+ u7 x* F' k- a3 {9 c: Z } else { ( F: M8 m& \2 F" z" ^4 s
continue;
- g6 y4 V3 L' s5 I# k2 r }
+ f* E, Z1 D5 y1 z% C break;
% C$ g! r$ M- |8 T/ t case '-=': ) }/ s% M. y: S6 n/ R7 N
$v = substr($v,2); 0 H6 O4 _ @" }7 z2 D% q( L, v- w% q
if (is_numeric($v)) { . B7 }4 g3 w! l" b! k, q/ `
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
( z6 J- b; h |0 L9 y0 H } else {
8 g5 M4 }) o/ q7 |9 l continue;
6 u5 h* T4 r! A: L! w3 M }
3 ?$ ^" [) m0 W; C7 `9 b break; A k; y" R8 s1 {' h
default: / M" T% Z8 p% E( o& z- ?; k
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 4 r6 Q6 a: ?, U1 o, n1 ]
} ( { H" i( g* B" v- a. } v7 R3 k
} / Z4 R7 j$ ~2 O, i
$field = implode(',', $fields); & V# ?% p) {; ~8 q
} else { . R; t P1 M7 } I6 I2 n/ E4 Y+ Z
return false; 1 T; ?9 Z. b. g: r
}
9 Z8 \3 k/ k: @3 A. T- ], V $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
1 T J8 G5 A5 o print_r($sql);
, k* p8 V( u. x; r, v2 O5 H return $this->execute($sql);
# q2 r& v# T) e ]& K }
: n1 S: }! X' U: [5 |5 A) x1 Z/ t# |. E从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
8 S* u$ A9 X, z# {) X7 U- L' j6 h( b3 t
攻击测试:
9 w8 h" }- H) S" n$ P) o7 M& }测试地址http://localhost
) ~6 h8 s" I: A& o' A0 d* ~( Y 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
2 @ L1 h t# l% V, K& m$ W1 ~' M' P0 z- Z) ~6 t0 |: Y& G
& g! b5 l" K- P( b
& P }1 Q' t# B$ @9 n
7 G- A3 r5 J1 r; R" i0 W
. ~; B; G2 `& L+ R5 V! H
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|