|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
4 O% @) T9 K i _( q) v$ r漏洞作者:skysheep
& U4 J1 w2 c7 v6 T分析作者:Seay% [) k; }1 d; M( b- R9 d) K' V4 b% S
博客:http://www.cnseay.com/
# k7 @7 {8 I. ~, ~漏洞分析:+ u$ H. _8 d5 L
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。9 g0 c3 m. i- c0 c/ N
; U4 @0 Y4 J( V
% k' S9 j% s! K$ e5 D
- c' `& D$ N9 Z4 v7 Xpublic function account_manage_info() {
G; V4 X. B* G+ l if(isset($_POST['dosubmit'])) {
1 x2 w" T# W# V, H& x) I //更新用户昵称 ' _; G3 {- {/ G% X9 [
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; # C- x% l2 I' C; N9 N
if($nickname) {
# b: z) ?7 t2 a: a. v$ p $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
s- N! d& e( }9 s4 H if(!isset($cookietime)) {
5 w5 h: j! V9 _* K $get_cookietime = param::get_cookie('cookietime');
: M8 J1 J9 F6 S" \: b } 8 d' Z, l; j0 B$ Y7 v6 A2 K# Y
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); ; N* ?& h! _) ^2 e. n8 b
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
) j# X K" P H4 b param::set_cookie('_nickname', $nickname, $cookietime); ; a# ^' P0 {5 \& C* z. H; i& y
} * R6 a* {) ?& U' l, p
require_once CACHE_MODEL_PATH.'member_input.class.php';
- |: Q3 _7 E! @* o { require_once CACHE_MODEL_PATH.'member_update.class.php';
2 C/ q( g& A! ^. B $member_input = new member_input($this->memberinfo['modelid']); ; a( {/ [; H& k& W0 v! p
$modelinfo = $member_input->get($_POST['info']); 8 \; \- u1 U6 h( R0 E; F
$this->db->set_model($this->memberinfo['modelid']);
- _) w S* c- F" V/ z; N $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); + E, o; L/ [6 n
if(!empty($membermodelinfo)) { * j$ u/ N7 b# Z
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); % t0 c( e4 x* `6 D
} else {
$ {* K; r! q5 o5 |8 D" y1 i- v $modelinfo['userid'] = $this->memberinfo['userid']; * J4 a7 n D, }' u5 s+ g
$this->db->insert($modelinfo);
+ B, D4 G' B5 S3 ?4 g" B }
; X9 Y5 F( I" d7 k代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,6 l8 g# V3 S/ _2 W( m. d- {" h, @
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ j- e# ~5 X3 P [. ?- ^# c' D( m$ O* \! N' m# |
6 |1 v! f) d }% w
9 Z2 H1 E" ]' C$ F# H- D
function get($data) {
2 i3 ]/ I9 Q: b) x! V $this->data = $data = trim_script($data);
# k2 \4 X1 |2 K, r t0 a7 B $model_cache = getcache('member_model', 'commons');
/ L' t% S) P, P0 C& g $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; : t( a+ w8 p" J
$info = array();
6 G, ^3 [8 o5 G7 ~, { $debar_filed = array('catid','title','style','thumb','status','islink','description'); 6 w2 g ^6 l& D0 N1 X
if(is_array($data)) { 1 W9 H" m' I! c, ?/ s9 y
foreach($data as $field=>$value) { # n1 n6 }" @! \5 |' [, B
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
9 G7 i) I' D/ N6 [ $name = $this->fields[$field]['name']; ) |- `* c1 L Y$ ?) b2 j' u# t
$minlength = $this->fields[$field]['minlength']; / a! D" {" t6 v0 R
$maxlength = $this->fields[$field]['maxlength'];
{/ i5 `" X/ P+ w ^ $pattern = $this->fields[$field]['pattern']; g* d' P: A' D
$errortips = $this->fields[$field]['errortips']; ; {. K0 [4 n8 x6 U; F; q' ]9 o0 P9 Q
if(empty($errortips)) $errortips = "$name 不符合要求!";
; L. o0 v" e% N, I $length = empty($value) ? 0 : strlen($value); * R* R# Y' a4 \5 E# `; Z H; F
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
6 l" `/ A9 F! _; Z+ i% J7 N* i if($maxlength && $length > $maxlength && !$isimport) { ; n: J. m8 n8 Q% M4 W
showmessage("$name 不得超过 $maxlength 个字符!");
1 R6 T9 h( u* D% S } else {
7 o' e, t: s7 h, @* t% | str_cut($value, $maxlength); ! m v# X8 ^9 N0 P
}
1 d$ s% Q, ^: U# K# Q9 U- X if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
* e! ]+ ^8 x# k9 C% y4 e if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); ; k! `% y0 |/ v9 M5 u* c
$func = $this->fields[$field]['formtype'];
U/ p3 z. P3 g( Q! e if(method_exists($this, $func)) $value = $this->$func($field, $value);
; C2 O8 d- J1 h $info[$field] = $value; * C: t* k0 J: T+ C9 f
}
2 y5 \# P1 j3 P1 Y) |! q0 Z( ~ }
6 T9 i6 A5 F% R: L return $info;
, t8 r0 |( s# Y& \# @& G/ e7 M" m7 @ }
4 A) U5 y7 Y/ }( E$ \# `trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
" Q h+ k- F9 J
. R' R. g1 i6 d6 T7 S1 y再到phpcms\modules\member\index.php 文件account_manage_info函数! n5 W# I# ]/ e. m* a' i0 m
过了get()函数之后。! i$ y$ v% e$ L% d
0 L j. S$ w7 R: j$ l2 D
6 M$ |( B6 y8 h) t4 N1 a$modelinfo = $member_input->get($_POST['info']);
% \$ |$ V3 @( F* y# c $this->db->set_model($this->memberinfo['modelid']); 7 n# x C x3 h: p2 {
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); / a7 o l+ H- _! y/ x
if(!empty($membermodelinfo)) {
. v0 G; N/ E* w+ m. k( r8 S $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
, i3 p8 G8 W$ M# E } else {
$ H# w7 K" J1 ]1 E直接带入数据库,update函数我们跟进看看) O; M% u7 _9 i
r- b* F) e7 A/ j
( _: L1 b: A* f. S; N/ i; x! X% _5 npublic function update($data, $table, $where = '') { ( {3 h/ r! J0 D3 e
if($table == '' or $where == '') { 9 \, a1 G. q. e5 L( {
return false; 6 _/ G {3 U+ A: P" x
} & F- q5 H6 F1 J* M, k1 K
$where = ' WHERE '.$where;
) z" w8 m" [4 ?5 h7 E0 A) Y $field = '';
6 K# ^" s0 `" y1 f9 r1 ~% j if(is_string($data) && $data != '') { : G& Y4 n7 n& b
$field = $data;
4 g$ J3 X+ [' M6 b! K- i } elseif (is_array($data) && count($data) > 0) {
% c& l* ^; [ h. b: D $fields = array(); ! @: R/ F: P8 h$ h- E/ u) ~/ o
foreach($data as $k=>$v) {
3 b' Y2 h2 p8 Q) ?/ Z5 k switch (substr($v, 0, 2)) {
0 T3 E- v+ |: V case '+=':
; I9 [; p3 k0 Q) s1 F4 l $v = substr($v,2);
+ C8 h) m1 `) g) R0 \. M3 r* f if (is_numeric($v)) {
# D5 q0 r' r6 c& [+ ^2 g $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
2 {. i% m, y4 Y& ]2 L5 n } else { 1 l5 G: T4 E O6 ]& V" ^0 _
continue; ; Y. a; A, [ X/ s2 `1 D9 v
}
1 e6 X a/ v# v# x9 e break; . }# O# ]3 N+ x2 |
case '-=': / P5 B5 m: R# S1 F; ^" {9 j
$v = substr($v,2);
' a. y) { W0 n if (is_numeric($v)) {
4 h" O: X/ }( J- A1 L& L $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); ; h- L4 |3 i9 r6 p9 G _
} else {
( p" |4 U! q g) u2 ` continue;
5 [* @8 }. V3 P, A4 u5 x7 m5 R$ \: d }
, @/ v* r. l# X, {" q break;
! ]3 [9 R& N. \- F$ s$ g+ U0 v default: 0 r4 J* M/ i9 e, k; p8 d5 l7 r
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
0 o1 S% d0 B- S+ o }
6 V* g% U3 ]6 n7 _ }
" @5 N" p8 `# T: i0 m $field = implode(',', $fields);
( G' P, ?9 S! ]# V6 h# m } else {
5 q. `5 {" M8 m' r return false;
8 Y/ G( |* r9 }, d' O } + \% Z# k' B. s: O
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
2 x; r+ z1 W2 Q/ W) ]" j. K print_r($sql);
' d) _8 N: w/ n0 K( d return $this->execute($sql);
7 p3 B8 ?7 x$ W8 K } C ]; d# N% y: J" j& t/ |& o$ v
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。5 ~$ j- n% T0 X( Y# t- _7 ?: J
- s/ I* `. n1 x ]6 i! {* g3 A9 B+ A& k攻击测试:
, l8 g' K+ z O测试地址http://localhost9 ]+ U$ r2 B' }$ }5 T! B( d
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
# L2 \2 R* u3 ^8 f4 T' |
' }/ a; A: C/ M4 h& s0 i" H& j m
) M9 v5 `/ ~, q3 n/ X" n( q# F/ X( p! g [
3 M* D" G; u p. T+ b1 y' p" l
$ k& G- J# i4 o( m' V, Q& x1 y$ U |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|