|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
: {" x0 T% e% A/ z+ g" Q漏洞作者:skysheep) I0 X W- B: W+ A1 S+ a
分析作者:Seay
1 s7 l2 |0 @5 ^0 {博客:http://www.cnseay.com/
& S7 n1 j% `/ ~漏洞分析:! v# o( R1 O: y# w {4 x }! R
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
1 c/ ?! d. `7 P
3 q% E8 {% {4 N$ ~0 _9 M
# W, }9 @; z+ o$ y9 { 1 _9 v' e4 @4 s+ Y. ]& K
public function account_manage_info() {
1 M6 U$ d5 C/ L# w/ H# w: ?: A1 i if(isset($_POST['dosubmit'])) {
( O" W. w4 {! A9 q //更新用户昵称
- h; o1 ~# R3 v9 s, W $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
( n4 r: C% s# C- U if($nickname) { ; C0 V$ z7 A4 }
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
" [5 W: w5 m# N7 i, M if(!isset($cookietime)) { Y0 J! J$ `, ?9 z* l4 K% Z
$get_cookietime = param::get_cookie('cookietime'); $ j8 S1 d+ e4 ^, n
}
. m, w2 I. E! r( ^ $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
1 B: B1 H2 T' a% c6 ~, B $cookietime = $_cookietime ? TIME + $_cookietime : 0;
1 i% C' B! a @, V L2 e) [ param::set_cookie('_nickname', $nickname, $cookietime);
$ e, S; {( K$ ~+ T& w } 9 H! g) C0 e" S) C. I
require_once CACHE_MODEL_PATH.'member_input.class.php';
1 f5 p4 t' d" g) S/ P- G require_once CACHE_MODEL_PATH.'member_update.class.php';
9 k1 c) @! n+ _) X $member_input = new member_input($this->memberinfo['modelid']);
% F0 V) ~1 Q# u9 [ $modelinfo = $member_input->get($_POST['info']); . {7 _0 i! W* }5 J/ C& o8 y; ~
$this->db->set_model($this->memberinfo['modelid']);
" W9 M4 v* b" f0 h6 c. r $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); / @3 F0 D+ \# F& H+ T: M
if(!empty($membermodelinfo)) {
. ^0 P% F1 a& L* N1 M: S $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
- Q8 d) R$ D% V( O% i } else { 0 M, x5 i; S' v+ J+ u9 L
$modelinfo['userid'] = $this->memberinfo['userid'];
: m1 N* f6 @# P $this->db->insert($modelinfo);
" ?! g m6 G" r) B } - \ N/ {7 P4 D) }0 K ~( f
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,& t1 `3 b- t2 n- J" O5 d; C$ A) x- _
在\caches\caches_model\caches_data\ member_input.class.php 文件中:4 F$ w1 p6 j8 h" h+ e& N2 S
: |9 Y- W0 `, J" H
- E/ C( p$ S* a, h8 X. _; w3 T( s ' I, `* _7 Q8 p K. O
function get($data) { 4 _$ B. O, w6 |7 a5 _' j+ ^4 q
$this->data = $data = trim_script($data); 8 n* C1 \7 u0 M3 b0 M. z5 g( V
$model_cache = getcache('member_model', 'commons');
2 M; V9 A. [% e7 Q; p* r( e $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
6 o; O. s/ k5 J0 f2 L $info = array(); ( `9 S+ Y( S2 L* S ^- ~
$debar_filed = array('catid','title','style','thumb','status','islink','description'); ! r! c) k) d9 E4 a2 J% Q _
if(is_array($data)) {
) g& `+ C" n7 X6 \2 q D foreach($data as $field=>$value) { + @% X( O% N2 B6 z6 M
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 8 a+ k4 q5 V+ ^7 \$ `
$name = $this->fields[$field]['name'];
3 N% d/ d( [% K- g% l' X6 ~% V; `4 F $minlength = $this->fields[$field]['minlength']; 3 m) B$ c/ u% x, M1 c9 k1 R }) u( H
$maxlength = $this->fields[$field]['maxlength'];
- n$ o0 o% ~7 z& o5 \ ` $pattern = $this->fields[$field]['pattern']; $ `9 M0 d# r& p8 Y" N( F0 m
$errortips = $this->fields[$field]['errortips'];
) a B. F- r, ~4 p4 f: ` if(empty($errortips)) $errortips = "$name 不符合要求!"; " x1 [& L' f" g, X
$length = empty($value) ? 0 : strlen($value);
# p2 u+ ^6 v# {& z if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ' y9 d. d1 g! x# m4 } K, f1 o6 M
if($maxlength && $length > $maxlength && !$isimport) { 3 `5 s, ^! V* Z, U- Z! u
showmessage("$name 不得超过 $maxlength 个字符!"); ; E/ I" l, U. {
} else {
3 i- S4 O& @+ o$ W& \, V str_cut($value, $maxlength);
3 M0 _1 Q: m7 a } : ^/ p* z, ]* M% ~
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); / z& T) s& ^7 M1 f) n2 f7 y# A
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
' s" u- L5 i# E8 w& F $func = $this->fields[$field]['formtype'];
/ R0 p6 i8 B# J$ O' e if(method_exists($this, $func)) $value = $this->$func($field, $value); ! L3 r% t5 K& v. Y/ ~
$info[$field] = $value; 5 i4 f: ?% v9 A% o5 O
}
$ {$ i4 }3 |6 v7 B' J4 g } 1 S0 h3 g: ?! v; t: S
return $info;
8 v# L) G7 {2 [" B$ ^7 o } : \! ]+ D. {4 N. s' }
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
! B: i4 S" |; Z9 ]5 A- D
+ m4 m2 |( j9 ~% U再到phpcms\modules\member\index.php 文件account_manage_info函数, q( `$ Y8 K2 M" o7 t& O S0 }+ h3 }
过了get()函数之后。
5 U M3 _; c* r' Z4 R, Q+ m5 ^" z7 A( P7 _; i7 t) _( U
* o O3 C- d5 k i5 }
$modelinfo = $member_input->get($_POST['info']); : B' h% d+ T& c, e2 G
$this->db->set_model($this->memberinfo['modelid']);
0 A! ?( Z4 y! y6 Q# {, o& z $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
6 [7 m, v8 u' _; I7 i/ i8 N0 H if(!empty($membermodelinfo)) { 0 A0 d9 v7 A& q
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
; z+ O6 c# ^' l8 `5 n9 F2 k } else {
8 B% E/ b, o3 ^& Q直接带入数据库,update函数我们跟进看看
9 U0 b% v' ~# X A$ o
# r) h2 B9 X. Y9 J( f! w0 u- F' Z
& A8 `! F) y- Y% C5 N& Kpublic function update($data, $table, $where = '') { % z+ t; L6 {, ^
if($table == '' or $where == '') {
* Y* S9 I3 w. z4 ` `2 b; b3 m return false;
; w( K4 q! L; t6 K: X. q } 3 G7 `3 z7 G; Y$ C
$where = ' WHERE '.$where; $ P' s( i$ w- N. n2 Y0 g/ r5 W
$field = '';
& V F3 \7 P$ s# Z4 H if(is_string($data) && $data != '') { ) F) S* V: X }) J; \0 w! n; Q: }8 |
$field = $data; ! @+ j8 Z1 z; o6 S: |8 A2 a" e! g, s7 e* {
} elseif (is_array($data) && count($data) > 0) { 3 b8 h. u- k }5 ?- g
$fields = array();
0 _- B$ e/ I; k9 ~' v1 k foreach($data as $k=>$v) {
% U1 J! ?& y' K c' R. {! w switch (substr($v, 0, 2)) {
4 f" Q5 q' E- m- p& V% j" J case '+=':
' `& q% s( v% W+ k! d $v = substr($v,2); / x, y. F. _$ [ R
if (is_numeric($v)) {
2 B; ?: A+ I9 P8 b $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
6 \; g/ n' `, r% G; K- V8 X } else {
" y$ {0 z2 `' p* c5 I) u continue;
- d. K, }+ q) m/ \' x( E/ `6 U2 U } 5 z* h. ]* } U& e& e4 J9 b& M
break; * G8 k# y# x- r5 u9 `3 D; A# d- h
case '-=':
% o9 P% R# N' m$ N) r% f $v = substr($v,2); ! q U0 v8 |' [0 j w
if (is_numeric($v)) {
+ h8 q/ R4 D" { $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
b. l+ d* x) r$ L* X& T/ P } else { + E' u- @. H0 n# W. J+ r
continue;
1 r! m/ k, |3 x1 P( X! W( V% S- j } " A S' ~ J. ~) Y
break;
( g! |- x" o8 T default: ( s3 o% z& a( Y( R
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); - t6 e9 `( G U- H* ~- z! k* v
}
- f+ c: w$ Y- A" D7 L } 4 r7 e( Y% F1 g% r6 [
$field = implode(',', $fields);
% l n, T1 i' v' ]0 W' D } else { # F( ?* t2 ^* ^6 R1 J4 T7 `* {( t
return false; 8 _0 y) X0 A: c2 A$ G# f4 @/ ]
} 5 b6 l% y: u0 S0 A' i9 s
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; ) K- m/ J* c! n7 C0 `+ U% m& f+ V
print_r($sql);
( c0 t' |) N' I: q: Q6 l return $this->execute($sql); % s" g5 W. W5 n) C! {! y
} 9 m! f) @+ |+ Y' f
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。7 f @# k% S$ b( H: p/ @# w5 `
6 B2 V# I. A) z5 z攻击测试:
7 W& E: B# |( \$ s# V测试地址http://localhost
0 P6 v6 \# }4 b1 y% J$ k 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
6 A3 o- }: p/ R1 s: [$ L8 U
; _- P4 K- |! V+ Y/ H, n
' U, P9 P. h1 V
( b. }4 s% Z. h9 g' P1 S: q/ B4 X3 Y% F
! p) P- W5 [7 K% Q1 Z
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|