|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
# a& r% O7 J8 l/ h/ X( P5 P漏洞作者:skysheep
9 O9 G A$ J. y分析作者:Seay
, u9 p! h! a8 [' d6 _$ E博客:http://www.cnseay.com/1 Q$ W# k7 S. [5 l
漏洞分析:3 D9 W3 O* C7 T& _/ U& K2 S
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。 ? _7 V( X+ K
+ G5 a0 ^% x# Z S6 ~
0 x: m/ W+ u/ A' k# X' K 7 H- @8 O+ J* [' d; B$ @' |" I4 @
public function account_manage_info() { : Z, A7 I5 C5 N5 g
if(isset($_POST['dosubmit'])) {
1 G7 S" O3 n0 Q, K //更新用户昵称 2 ^5 Q9 B1 D2 m$ r2 o, q p- J
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; - p2 |# J$ n! t
if($nickname) { ' ?2 v' Y0 ^) L0 d
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ! K* K7 g7 {" m% B2 f: Z; L
if(!isset($cookietime)) { 6 X3 _' M' I0 ]3 P4 s4 A) M- p
$get_cookietime = param::get_cookie('cookietime');
# s# a. ?! }( E! V3 m$ S- F" E } 5 [" S8 o6 T' R6 L
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); * s4 w: X o) H- I
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
* c1 N6 [; Z- f, ^5 h4 U$ y param::set_cookie('_nickname', $nickname, $cookietime);
+ V# m% W1 Q% ~! X }
2 ?) v( W$ `7 F7 d require_once CACHE_MODEL_PATH.'member_input.class.php';
; p- g. L4 `& Z9 L& W require_once CACHE_MODEL_PATH.'member_update.class.php';
0 s4 B" [) @# E) h& p6 m) S- ~$ t $member_input = new member_input($this->memberinfo['modelid']); 9 _/ v. H; a# s; C# z
$modelinfo = $member_input->get($_POST['info']);
. y) ]$ n' i8 ~1 Z& L $this->db->set_model($this->memberinfo['modelid']); + F4 V# {8 k" s
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
l* U. h+ m4 y( n if(!empty($membermodelinfo)) {
2 K1 K/ I4 J8 S/ J7 M: ?) e4 G $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 4 {/ t4 [ g0 ?% X: v) t- F8 M
} else {
; [' _( c7 b4 e $modelinfo['userid'] = $this->memberinfo['userid']; 7 M+ X' {( h" F- u6 M. z6 X
$this->db->insert($modelinfo); 6 v3 O3 z3 j2 A
}
; V g; _) f! v4 d5 k1 `1 X- W代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
' [- q9 G5 Q6 b# ]在\caches\caches_model\caches_data\ member_input.class.php 文件中:; a6 f0 e& k! y, E0 v% N5 f- A- J
0 v. L+ V9 X0 y& }# i9 K3 ]% C# C+ _$ v& Y) J
! O+ n: G) Z. E5 P! P6 k: P
function get($data) {
3 f/ Q6 H$ \6 w$ ^ $this->data = $data = trim_script($data);
) ]( M' U. O) [0 s9 D+ e& A6 i8 y" k $model_cache = getcache('member_model', 'commons'); $ U$ \/ ^, p) y6 f4 y8 N
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
$ j. a5 y% [; F$ u- }: }5 e $info = array(); 6 g. z, h' a$ T
$debar_filed = array('catid','title','style','thumb','status','islink','description');
; B: V, z6 F; `& V if(is_array($data)) { + L: t& E5 ?) |" |/ o/ f
foreach($data as $field=>$value) {
: [/ z2 b$ x9 H0 K if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
6 {0 J. p& ?$ E. R- F6 z $name = $this->fields[$field]['name'];
/ i/ C' C$ F. Z- d7 Y $minlength = $this->fields[$field]['minlength'];
5 l1 }) B- |7 a' u$ S $maxlength = $this->fields[$field]['maxlength']; $ p2 v! V# F9 D' I1 |
$pattern = $this->fields[$field]['pattern'];
& Z9 W1 X5 w3 W2 \ $errortips = $this->fields[$field]['errortips'];
5 ?$ ^3 E: V! i% C2 W( w- Q, g if(empty($errortips)) $errortips = "$name 不符合要求!";
: b6 \# V& C9 z$ [% K& Z3 J $length = empty($value) ? 0 : strlen($value); 3 f, ]0 u$ Q8 E) F" e" \1 ^
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 9 n1 ] ~4 x4 ]# c: d2 T
if($maxlength && $length > $maxlength && !$isimport) {
6 O+ Z0 v% g: E showmessage("$name 不得超过 $maxlength 个字符!");
2 C5 g, [* x" U! p6 K' L } else { + M' l* p/ ~4 D/ k
str_cut($value, $maxlength);
7 B$ \1 ^/ B. G, _' }6 x }
& l1 m, }; S U' N) Q7 K5 i1 Q9 U if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 8 A2 H0 ^- F4 \+ d. R _
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 4 ]2 J+ T- l1 z: e
$func = $this->fields[$field]['formtype'];
+ y! ^+ V" r7 q" ?" X1 D if(method_exists($this, $func)) $value = $this->$func($field, $value); * y* d4 `( Z, w. C4 F$ T& S3 r
$info[$field] = $value;
$ S: M# r! L5 o$ u: b1 f } 8 c6 x# D2 A3 [6 O: c3 l, L
} ! [6 z7 f) f$ Y
return $info; $ B* G2 m G) }# b7 ?4 u/ o- O
}
; w& ~5 K+ L) d K3 M3 {trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,8 S3 X* Q) O8 p2 z$ N/ H* c
9 d0 G0 o) r3 ^. d( W- J9 h
再到phpcms\modules\member\index.php 文件account_manage_info函数
! J, i% `% d5 c" W, v. |7 {过了get()函数之后。 x, Q" @( C* p2 U* v
. W- z3 Z' B ]7 X$ H 5 `; m6 a6 P8 e) ^
$modelinfo = $member_input->get($_POST['info']);
/ F* V, a7 ~5 W; n $this->db->set_model($this->memberinfo['modelid']);
: i. g! E3 }6 W2 R- e, c $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 9 m# g; \4 ], G4 ~ ?' |
if(!empty($membermodelinfo)) {
: n- \5 u2 ^8 Z( c; B/ a $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ) N$ F( [' u5 G6 | q/ I) w3 E) _
} else {
8 D1 \! n: }$ Q' k5 G( n$ _* h直接带入数据库,update函数我们跟进看看. `* u9 r: w/ _9 u
8 F" z7 u/ L. C " a' Q- w( n4 s+ n* [) X& E& S
public function update($data, $table, $where = '') { , C, ^# X8 \( z* h; U0 O5 j
if($table == '' or $where == '') {
8 {5 u, p+ N/ s7 D return false; . u# \0 |0 F% C, i
}
, h7 _; _+ x: K7 y4 I $where = ' WHERE '.$where; 9 u3 l5 A% I y* W% @- h
$field = '';
3 g# ~3 i9 h3 Q, B% b8 n( N if(is_string($data) && $data != '') {
; \: L+ D# s- ]$ c $field = $data; " u: d3 ^2 e( c; Q$ ^" L8 [8 q- o
} elseif (is_array($data) && count($data) > 0) { 9 i1 N9 k& e2 j# O+ f
$fields = array();
& z- [& P9 {8 Q9 O foreach($data as $k=>$v) { # G5 t8 @3 Z7 l* M5 J
switch (substr($v, 0, 2)) { 0 ]0 H. p- @" H* h3 {: g
case '+=': 5 X1 n" Y+ C# q# N
$v = substr($v,2); , x# J% }/ X% _) |
if (is_numeric($v)) {
! z9 `& g8 G& a; c+ B $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
* f+ A& s1 _) ]1 k9 H } else {
- J; m! z" s9 o( I& I* n) Y continue; # L5 \& S' B6 o: a1 R
} ; O1 W; u! S9 p" _" T; |
break;
4 A6 u# E9 O" K# S2 @. T case '-=':
% `# a1 Y( d* m+ w* M $v = substr($v,2);
" x9 o) S2 C: P( C) L if (is_numeric($v)) {
: Z! I2 k% x; |$ \. N. W $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
+ p' c8 A+ y1 O, M } else { ) }: m$ H+ d3 S; m2 x3 z: _
continue; ! s$ U s/ R. i
}
3 z/ g3 A% B4 n0 E7 Y& \" b6 z break; # K# o4 T! I' U0 A) r% v
default: @3 U* [7 P; A
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
6 K8 }7 {3 {0 D } 0 w1 h4 `; p5 g) D+ B
} 9 b2 Y8 A9 k! _) L
$field = implode(',', $fields);
& s2 n, g7 ?3 E; U1 S% b! Z } else {
s% e4 A6 C* y8 I5 x3 K6 c/ y return false;
) G. Y' O% e( B } - q( R3 A: Z) `5 Z) H) X
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; & P, c$ Y: d1 d( A' m6 H
print_r($sql); 7 G! ]3 \5 ]8 j! _& v6 ]. r
return $this->execute($sql);
+ O) I- S: |4 o) u+ B* D8 U } $ D, l; [1 H9 v7 I9 N
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。) l4 D N0 A6 f
) }) S- v7 f9 u, W7 B# N攻击测试:4 Y1 s, S2 A8 ]- b/ |- B* B8 b' Q& m
测试地址http://localhost6 v, b/ P/ d- @" y# r
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
+ r* u& O. H U, W. t- m8 x: n Y* I; `" L
6 \& ~# X j1 ]! C
! S y& ?0 P% u+ s
2 i- h9 J: W, ]& r8 n. [' T$ M" {& ?- D8 n% T4 s0 r
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|