|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
! ] J3 K% i# ^/ R- N8 r+ L" e漏洞作者:skysheep
# I) r7 S% \% B& l$ C7 P分析作者:Seay
5 O( f8 v* f3 T# v0 u博客:http://www.cnseay.com/
1 f" K# t5 U0 j8 z漏洞分析:! z; F. T8 V; [4 Y" e
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。0 ^3 _) t# M' O1 G6 k
% v2 e. U, _; f% Q6 v. b
. f8 _" f1 c4 o' ?8 P
$ O( k5 ?( b, G2 A( g! E7 dpublic function account_manage_info() { # j" M5 M! _& H N. X
if(isset($_POST['dosubmit'])) {
3 B# Z0 J0 F, D p0 s( a //更新用户昵称 ; I$ Q5 p$ {5 N' S' D5 w! R; I
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; ) z0 i4 Y: X+ o& N" i; u
if($nickname) {
0 ?7 t) \ R/ n u" Y$ S8 N $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); & K. ]4 z* P- v" O% |: f
if(!isset($cookietime)) { & j1 D% m3 _2 } N, m6 R
$get_cookietime = param::get_cookie('cookietime'); ( i0 K2 F9 s* F; b
}
d' t- P; A. t* B9 | $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
! Q4 F: E' D2 K* _) K: P5 q" s $cookietime = $_cookietime ? TIME + $_cookietime : 0;
' e. v: r, y* s param::set_cookie('_nickname', $nickname, $cookietime); ! n1 t8 D: T7 y) v, _
} , y- q" J( Z" o9 C5 s8 _( O
require_once CACHE_MODEL_PATH.'member_input.class.php';
! T( }( O# G4 _ require_once CACHE_MODEL_PATH.'member_update.class.php'; ; j5 K' w- @; b! @6 M8 U2 u
$member_input = new member_input($this->memberinfo['modelid']);
7 r/ k, Y8 U5 C" u* C4 t $modelinfo = $member_input->get($_POST['info']);
0 |5 X( D7 [! E2 @) Z $this->db->set_model($this->memberinfo['modelid']); , S- J- B7 x t
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ) W( [3 E5 Z& Z- |- n! d. k
if(!empty($membermodelinfo)) { 8 A5 `9 N; h m/ Y0 t
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); $ g+ t1 H) s: [7 F
} else {
W r4 w, M( O% N8 q; I- m $modelinfo['userid'] = $this->memberinfo['userid']; 2 P! q+ x: G" x. `6 `! m
$this->db->insert($modelinfo); ' N* W8 i+ n. V! z5 T
}
+ U$ K" l8 A7 }% f* N2 ?) E代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,3 H" d6 x( u" z, i" x
在\caches\caches_model\caches_data\ member_input.class.php 文件中:5 s3 P* f- E$ d! ]7 P9 w" b
p- Z4 B( x1 d* L" m7 t+ x' u
: A- T* \: D$ p7 {7 ? ! v k0 T7 [, P
function get($data) {
6 _( t. d& l4 p- I! [7 m7 L $this->data = $data = trim_script($data); 0 @( K# o7 C& S
$model_cache = getcache('member_model', 'commons');
0 h2 a" N+ x3 r $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
% `& N8 Q! C& S# b/ g( k $info = array();
. [. R7 f1 L9 I, Q $debar_filed = array('catid','title','style','thumb','status','islink','description'); / T- B5 S4 ?" Y( Q7 r# E9 \" f
if(is_array($data)) { 8 V5 K) d' z# N4 F8 o9 }8 Q3 V5 n+ b/ `$ K
foreach($data as $field=>$value) {
D6 n* h8 f) q# ?# |# A; _ if($data['islink']==1 && !in_array($field,$debar_filed)) continue; ! q* a# N; V. T5 \" V9 X
$name = $this->fields[$field]['name']; . y. t6 X9 U# L' u- Z
$minlength = $this->fields[$field]['minlength'];
* i# _. l* A0 r: V $maxlength = $this->fields[$field]['maxlength'];
2 w4 z L5 t. J2 B% \7 p $pattern = $this->fields[$field]['pattern']; ! Z4 P4 ~/ k- L E/ z
$errortips = $this->fields[$field]['errortips'];
' C! j5 Y: ]5 @ if(empty($errortips)) $errortips = "$name 不符合要求!";
( W) h" O7 U& W7 ^+ p $length = empty($value) ? 0 : strlen($value); & E/ s/ Z9 c! m/ q5 S
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
' Z) H4 ~4 A" \+ z2 Z9 r' Q if($maxlength && $length > $maxlength && !$isimport) {
$ T* w6 q7 J$ x) }2 z8 V" E& q% g% j showmessage("$name 不得超过 $maxlength 个字符!"); - C* C% U9 L2 ?% [6 A( F
} else {
, u# j' c8 k" L) J str_cut($value, $maxlength);
7 I& `3 A! e* S } 3 ~+ ?0 U. [4 }
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); % O' \& }5 J3 K4 D
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); , i6 o g+ n* W; q* B5 G) @/ n
$func = $this->fields[$field]['formtype'];
7 }6 ^ Q2 a5 J& P7 ?! k/ t3 f if(method_exists($this, $func)) $value = $this->$func($field, $value); 5 i: `9 ]+ f' u4 B' A! V8 b/ d9 B- t* p
$info[$field] = $value; # O6 j( p, K1 I2 i
}
' X% i. s6 ]2 @; u } ' H8 T5 U9 M5 y
return $info; 6 g) M6 A) [! v) t* f
} u! E: W" r8 B6 r3 G
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,4 Z! k; k9 c; @/ F% S) t: i, m
, h( O% B% q- N, D+ m0 I0 r再到phpcms\modules\member\index.php 文件account_manage_info函数 X1 l9 r, _0 Q8 ?0 Y, Q0 }# a0 J/ v
过了get()函数之后。
8 f' e" z( s) n' l5 ~& H/ H( N
$ b( p! C( J3 {# M, r, k- H. J
- F' b0 w. o$ R. y$modelinfo = $member_input->get($_POST['info']);
2 N& K6 V3 m3 l) M $this->db->set_model($this->memberinfo['modelid']); * ^ s1 C" W. B" a N& U! P1 w v
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
4 D; t9 z- j0 I' x if(!empty($membermodelinfo)) { ( n' a8 r0 |8 R+ O
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
' D7 L4 ]! k6 p, t' h H, V+ Z } else { ; A* s/ y: v4 g" x% p( y
直接带入数据库,update函数我们跟进看看
( X$ y! r* n" G X& W6 N/ P. [+ E/ D! O/ K( K# v) F
6 p. T) k9 z8 h
public function update($data, $table, $where = '') { ' {3 k4 k2 X, P6 ?0 d0 B
if($table == '' or $where == '') { / F% i$ E- g# B2 Z9 L# D
return false; # D! |! v% R4 c( ~6 h% x
}
( v3 J. s" V# F* [ $where = ' WHERE '.$where; 0 m/ Z* C/ R6 S. ~1 _/ D. y
$field = '';
) U# J/ i. r: |% C( V' ] if(is_string($data) && $data != '') { / Z( K! U5 I# w* T" D/ D
$field = $data;
! N5 m: S. {0 q+ n } elseif (is_array($data) && count($data) > 0) {
$ W8 N; w1 N6 c! X# l- M! H% y3 f $fields = array();
6 F% i0 j; c0 E foreach($data as $k=>$v) {
+ K6 v& t9 a2 j$ t switch (substr($v, 0, 2)) {
+ k0 l$ E5 E& |! Y% {2 P" S8 D case '+=':
& ]0 z" c0 E. U- J9 B2 |7 y7 V $v = substr($v,2);
; }8 ]" K+ @4 g& A, |( S5 R) W if (is_numeric($v)) {
. @" i# ]: X$ P5 W7 q $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
+ v' ~2 b- F3 y. a3 [8 f } else { 8 t4 S$ n9 F3 d9 F) t( z
continue; & W. v N- y' o0 o7 @! i, {
} % c8 W- v7 v4 M6 M0 ~( L! ^
break; + k; ~, q% M' B, O; Z) G
case '-=': ! d3 X8 v7 [. x: e
$v = substr($v,2); 2 O( ^' i/ T# v \" T ?
if (is_numeric($v)) { 6 Y, ^5 j* h/ ?) V" K
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
4 p o, S6 z1 l+ ? } else {
6 s& w! X. p1 f4 s j5 W continue; , E+ r4 M' ?- G! {
} . L; C+ w1 I6 n
break;
- I! A, Z% z4 g5 _* d. f* p7 i2 A default: ! k8 |& t5 r* @) g+ }5 ^+ @
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
! C& ^& n. J. a7 |! h" R9 s) G }
- u7 j7 V3 l( v9 b* ` }
* o+ {. ]/ d; j% G $field = implode(',', $fields); ( \- w* ^. g. _5 v9 t9 n9 N
} else {
. {$ l6 D3 z- r& ~ return false; # n) L0 F. i8 I5 c: a# \$ O: X
} 5 G5 T4 Q; E5 K
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
6 o) {! P/ p2 f print_r($sql); 1 X2 [6 B4 R- B8 D
return $this->execute($sql);
; ~# j( j7 A8 Q7 B: U8 i( x } : B1 x) w& I, S3 i1 y( r
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
5 l, J4 t& L2 d# H
3 C$ a: w! s4 e: B攻击测试:
; ~ q, s' o6 p7 _2 c$ e+ H测试地址http://localhost _ K4 q* T9 z& U0 {
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
0 {4 k- @% L3 g: k1 n
' w$ S5 U9 a3 v3 _3 D& l
8 r6 {9 H# x. d( s3 Z& k* {* q ^+ z4 U1 N
# k9 g% A& P$ N" P, u
l1 o& u+ m2 |2 P3 P
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|