|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
- C4 @) P1 S1 V" d" ~4 Z9 ^/ a漏洞作者:skysheep1 ?4 K: [# W, Y" S* L
分析作者:Seay
: o$ \, u) j" o博客:http://www.cnseay.com/
; ^: P, ` ]& I/ B* L1 |' ?漏洞分析:) e+ O1 E& s# \+ T
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。8 _8 Y4 J' ^. ]) Q: q
' L( x1 H+ T# a! o: Z- j' @1 N! T9 q3 C
$ X" f. ~( s$ m9 U$ a0 @, D8 L8 a
public function account_manage_info() {
+ S7 u& h3 y8 @* k if(isset($_POST['dosubmit'])) {
$ T- M, A( D2 b+ k9 z1 r //更新用户昵称
& w1 L9 _9 M% c! ^! Z $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
$ l& r+ n" S! O/ c' ~2 E) X if($nickname) {
, k4 M# D1 L" x $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
0 h9 V+ s8 {9 S( w$ ]+ j g; ? if(!isset($cookietime)) {
( A: X- U2 u# y- }& _ $get_cookietime = param::get_cookie('cookietime'); " z9 l& P! }9 W5 \8 R% y/ s
}
|8 h/ z: |7 L; ~ $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); / H9 Q: Q& u& \/ a
$cookietime = $_cookietime ? TIME + $_cookietime : 0; / ?( v& o# r0 Z
param::set_cookie('_nickname', $nickname, $cookietime);
4 L2 E" r0 q' a- o0 p' f } 1 f( Y. j% W; e! T. _6 i
require_once CACHE_MODEL_PATH.'member_input.class.php'; 5 S! V! \$ E/ ^2 P
require_once CACHE_MODEL_PATH.'member_update.class.php'; / x8 V/ @7 \' ?$ ]3 b/ L
$member_input = new member_input($this->memberinfo['modelid']); 5 K# r& Q3 s5 ~( H2 q8 ?
$modelinfo = $member_input->get($_POST['info']);
: J: I5 U% j: F4 o0 ?* ^; Z $this->db->set_model($this->memberinfo['modelid']); 3 F: R& h" p; i! _
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 2 w2 z3 d! `7 l
if(!empty($membermodelinfo)) {
1 S% g& Z; J" C$ F* W; c $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); . S0 o7 v+ B0 t7 s- U0 _9 R4 m
} else { ! N+ ?7 U& _+ ?+ k& F
$modelinfo['userid'] = $this->memberinfo['userid'];
. z+ n/ }4 R& Q& F K. M% ^ $this->db->insert($modelinfo);
! P* |) D1 e- y! T6 G2 \ } 5 F! i7 g2 J, }& j& b
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,9 |% A7 U# ^# w0 @7 z3 u
在\caches\caches_model\caches_data\ member_input.class.php 文件中:% N& n9 E: W* r
7 }4 r: K0 f: f6 I
$ I- d# \4 b! X* ]* `
b! y6 R( `0 @! M
function get($data) { 0 h0 G5 a7 J R, b' c
$this->data = $data = trim_script($data);
/ v& l% F/ Q: A: \/ g% c $model_cache = getcache('member_model', 'commons');
( D& t T6 ^' d" V $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; + C6 G2 g( Z: U9 n
$info = array(); ' f2 F' R4 v1 G% U3 w/ U9 G. k# Z
$debar_filed = array('catid','title','style','thumb','status','islink','description');
9 _- R# O4 J* w2 d* {7 S: A+ P if(is_array($data)) {
4 y! h' i/ F0 g1 h foreach($data as $field=>$value) { 4 ^ Q3 Z: u4 z6 g5 Q, k
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; : v8 o/ w* m. t, D
$name = $this->fields[$field]['name']; / O" R: U$ {% p! x* B$ L. p% y( }6 q
$minlength = $this->fields[$field]['minlength'];
W8 P2 n1 _- D3 e5 P $maxlength = $this->fields[$field]['maxlength'];
. Y% O: A3 h y# Y $pattern = $this->fields[$field]['pattern'];
% d( Q9 Q" p' V& I9 u9 n3 E. _4 m $errortips = $this->fields[$field]['errortips'];
4 J% [6 t! i" K M if(empty($errortips)) $errortips = "$name 不符合要求!";
; K# n* ]2 J* s4 f1 G6 J $length = empty($value) ? 0 : strlen($value);
: C, y1 d8 r( K# I' s0 ] if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
0 }7 L* q, E4 N& A3 Y$ e, R if($maxlength && $length > $maxlength && !$isimport) {
4 Z1 k6 \8 J* N+ j showmessage("$name 不得超过 $maxlength 个字符!"); w- N4 w5 A* r! d# M w
} else {
[& J( ?/ W! w: R( b2 o4 z- E str_cut($value, $maxlength);
. h4 o& C- n+ Z } * J0 d6 K1 u: j+ Z! O0 i! y
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
. @' N! `8 w1 W+ Z, N0 z6 r if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
& ^+ x t( k) p8 h $func = $this->fields[$field]['formtype'];
; r& J: h! O) b4 K if(method_exists($this, $func)) $value = $this->$func($field, $value);
, f2 s) S1 a. L9 W5 x1 v $info[$field] = $value;
; N, k7 d. ]1 H } 7 x9 g" o$ l: }7 ?% j
} 6 K$ x2 n$ l+ m
return $info;
; Y9 q/ V; ~! r O r# b F }
G- d6 b K' Ctrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
/ v5 t: L2 q& N# }2 }" g! a- p
% `5 a: ^9 o, j% C再到phpcms\modules\member\index.php 文件account_manage_info函数$ A, ~3 F( F* Q, y6 f" a4 w
过了get()函数之后。
& N5 A! z* ^" }: @# h, s# x
% n& Q3 h: C! e- S c4 x 7 j) F) {2 A) d5 o! I M
$modelinfo = $member_input->get($_POST['info']);
+ A) x* a1 a e B" g $this->db->set_model($this->memberinfo['modelid']);
# J' F0 k8 m1 N$ S2 M4 o, i$ i $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
. @7 d7 F' r5 d" E, R- ? if(!empty($membermodelinfo)) { 7 ?! u4 _ e! P c5 C d3 A& y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ' R+ s9 A3 M: E7 o' M3 v
} else { 4 Q/ D* p! D0 `# F
直接带入数据库,update函数我们跟进看看! p8 d( Q0 m* S, j9 z
8 V6 k' l7 U3 Q9 ` " J1 \( q0 Y0 P
public function update($data, $table, $where = '') { ; B1 ?- }. f) C2 c4 u ~; O4 d
if($table == '' or $where == '') {
' j# A/ r3 A1 H% f9 b2 P return false;
) T, z3 Q5 p9 H! m( N+ w } - x' @( p8 [* }8 h6 ?' `0 A# A
$where = ' WHERE '.$where; % a+ @: L5 W% I! L! k
$field = ''; 8 v. M5 ^4 m$ r2 b- T9 N5 r( [) p
if(is_string($data) && $data != '') {
5 r- ?# z }7 [4 j" T+ k8 t) B [ $field = $data;
! n. E1 t* Y* E0 f4 @9 b } elseif (is_array($data) && count($data) > 0) {
4 R+ [, d# i L! P( Y. ^5 _ $fields = array();
: ?$ Y) u7 c2 ?5 w. d foreach($data as $k=>$v) {
* C5 O0 J! v K3 g+ a0 }% W switch (substr($v, 0, 2)) { $ S' @, g6 x& L2 Y4 R+ F. T
case '+=':
/ A& C" L$ P+ h6 a% V1 P $v = substr($v,2); + Q" U; @, i t# [5 t' O
if (is_numeric($v)) {
( }3 x! ]# C0 [ $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
) R6 [' T- z( W3 u f } else {
6 ~/ {) w, \6 J' Q: M# q( N continue; 8 \9 j1 A5 C) p2 b0 C& X# C" W! |
}
: Y; H7 q% r9 c. m k h3 Z1 u9 @2 M break; % Y6 u5 N+ K' C1 Q
case '-=':
9 y: L$ I6 a, } $v = substr($v,2); ' v! m- L: ~8 [7 I. z1 V0 n
if (is_numeric($v)) { : L6 }* ~8 L6 p
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
- f$ F% c, C# f% j6 E. Z } else { 1 t+ p3 K, p6 j: [
continue; + P: k8 K$ `: c S* _$ L+ S) I
}
; O+ u+ P9 y* r/ j4 a break;
4 Q. F4 U: g, M4 V default:
, j$ C0 x7 p! H; f $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 3 y$ ^, F: |9 \ B( e6 D
}
# Q* K2 [- C/ v! F3 f } & v( Z" E; H" g- ^+ p) J
$field = implode(',', $fields);
$ v. R! C6 l6 b } else { 1 X# N+ i; e7 N. r
return false; # o; m! l4 v* }4 Q# e
} " w: q) C9 S1 J1 U8 N# M8 U. [; V, g' e
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
) C. ~7 g+ s5 J: d print_r($sql);
3 l- `+ K" H4 @' d( ~ return $this->execute($sql); 2 G5 p' O' a+ m) I M6 T- V, B, C
} ' X" @0 V, G! `4 o. F0 j" n
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。! y, k9 X0 s9 l% x
" R; \ k; e/ V4 E6 x* e攻击测试:
) n o# o7 L; |$ e测试地址http://localhost3 i) N) c0 m1 o; N
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
' R* z- N- ]: k# a, y. x5 r/ f z" _$ E
! Y2 v; o y1 J& w
% y6 a! s1 o; k* k2 {6 P
) `" T6 o' ^8 K0 k
9 O) c% Y; e( r- j r
4 u8 ]: W, z9 w; h6 u9 U |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|