|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告; v( Z# ]4 t v3 T' q
漏洞作者:skysheep
# I/ i2 w% X- _2 L. o分析作者:Seay
& h* K- O- E5 `0 F: A) Z, \博客:http://www.cnseay.com/% ?5 l7 W8 Q' m$ {( w
漏洞分析:
7 c0 A4 h8 ]6 m2 ?8 a) d4 d+ ` 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
' L2 Y0 n2 R3 Y) Z4 p' Q v; J, T/ B& s4 B9 [# h9 ^8 v0 q8 t
, V& A# |, `0 E" Z
+ _+ _ N9 h m6 H: dpublic function account_manage_info() {
5 ~, R' y% a5 d# p/ J+ o if(isset($_POST['dosubmit'])) { 4 u# @2 Z1 D$ K
//更新用户昵称 + I w6 ~. n/ }) Y' S
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 2 D3 |5 ~. R+ U% U R7 Z3 f+ D/ p
if($nickname) { 8 g+ n6 o( j1 s. [. E3 X% B) c
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
2 |8 n$ t: I4 |9 i. {) y; h" l if(!isset($cookietime)) {
- D+ x2 I) D$ T $get_cookietime = param::get_cookie('cookietime'); E% ^8 t( }8 z* `. G" l% \5 A! Q
} 4 l0 C+ _+ d i
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
i( v+ F# J* G# s+ j! Y4 C. P $cookietime = $_cookietime ? TIME + $_cookietime : 0; / z, _2 j! s H
param::set_cookie('_nickname', $nickname, $cookietime);
X ?( j& f3 e/ a2 R } 9 @4 X z. |* }+ v" r J
require_once CACHE_MODEL_PATH.'member_input.class.php';
$ z( q% {# F" D" P& c6 e- B require_once CACHE_MODEL_PATH.'member_update.class.php'; 1 [7 U, V& E# x# i# A2 K8 E
$member_input = new member_input($this->memberinfo['modelid']); 8 O8 \) S; W5 m/ \" i9 ]
$modelinfo = $member_input->get($_POST['info']); 1 z2 `) ] h* c( ?3 v, {0 z) q
$this->db->set_model($this->memberinfo['modelid']);
3 Y- i- h; g, [" O ^ $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); & ^6 |4 Y, q" j; j* [# v3 o+ R/ }8 K. c
if(!empty($membermodelinfo)) {
) U. c J4 j2 G- X* L) G $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
% h4 d5 R& G& C } else { . q4 v- {, F; H5 B
$modelinfo['userid'] = $this->memberinfo['userid'];
# `" Q7 g$ T1 \, Z2 q% b $this->db->insert($modelinfo);
8 c1 G8 l% g! i* E0 l- v }
: ?% Z5 ^3 G. Y0 |4 d代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
+ a. o2 D9 S5 q' q在\caches\caches_model\caches_data\ member_input.class.php 文件中: v& g7 L5 o, V9 ], t
6 M' Q9 u: t5 R3 M
5 Q- u- d/ |) K F- I, M % s. D& i4 Y' `
function get($data) { 8 |& j7 V* z, v" Y' I
$this->data = $data = trim_script($data); 4 h, a% ]4 Y0 z: i8 m# N5 [3 p
$model_cache = getcache('member_model', 'commons'); 4 y( K9 t3 k4 F" L: S0 D: R
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; * j2 }6 l& C% X+ C/ o
$info = array();
2 q. `0 X1 t) K6 {, O! U $debar_filed = array('catid','title','style','thumb','status','islink','description'); ' K1 S- K- F* u# Z# j$ J& p3 @. T
if(is_array($data)) {
. I/ s4 {! w X7 ~- o foreach($data as $field=>$value) {
( I3 G$ b* H2 _' R: H: l, ? if($data['islink']==1 && !in_array($field,$debar_filed)) continue; & |) j# X3 J' ]$ j+ R
$name = $this->fields[$field]['name']; 8 \! |* ]7 W" y% Z
$minlength = $this->fields[$field]['minlength'];
0 i) I( w- {6 | $maxlength = $this->fields[$field]['maxlength']; $ H% v }9 p0 r
$pattern = $this->fields[$field]['pattern'];
/ i" d* v+ w$ s: A9 L $errortips = $this->fields[$field]['errortips']; ( l! J7 v6 a4 h' c- F
if(empty($errortips)) $errortips = "$name 不符合要求!";
7 p, _$ o$ t) ]" r( k $length = empty($value) ? 0 : strlen($value);
# f8 E4 Q- V: ]3 d if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
8 u! M8 J1 o* }. G if($maxlength && $length > $maxlength && !$isimport) { 5 [% g1 Y2 F" W
showmessage("$name 不得超过 $maxlength 个字符!");
3 w5 D9 l% \: e } else {
# p8 F- V! Q: Q+ P7 G: J S str_cut($value, $maxlength);
! m: y. r3 A$ V% G( L }
6 P' G8 _- U, Y4 p. Q6 \! H* s if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
( M" [2 ]* Z9 E" H# ^. h4 S7 i if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); " P8 A6 o l1 h5 J3 R2 x* j8 u5 w8 k
$func = $this->fields[$field]['formtype']; 9 v4 b" J$ j/ X: b) ]2 d8 G6 \
if(method_exists($this, $func)) $value = $this->$func($field, $value);
1 M6 y: [. Z. W $info[$field] = $value;
/ y2 J8 l, [1 |% F+ K- P% ?+ c6 i } ( H& E/ d7 A0 h0 x W/ O0 t
}
& B; g/ \% c: o$ a/ V return $info; : d2 h- E; |2 O3 ]* |! |
} 4 p; g8 W/ x* f; w1 `) ]! \
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
& x, U/ z6 _# Z
! c0 d; f0 ], Y8 {再到phpcms\modules\member\index.php 文件account_manage_info函数3 d0 D6 o% g: U2 b+ v( G7 t( U
过了get()函数之后。/ y) m/ N# @, {6 j9 e3 D1 j
3 H/ z# l; C' e6 I
4 c9 y+ U5 G: E* U$ u n$ S" F. b$modelinfo = $member_input->get($_POST['info']);
$ G3 ?$ t' ^" I% _3 j $this->db->set_model($this->memberinfo['modelid']); , b9 E: k/ i/ o( ?
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ; }+ c# @% j) X; i
if(!empty($membermodelinfo)) { - D4 I' I5 N) j3 b0 [5 y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
6 V' y1 I& y0 h% H% z } else {
# W2 Y/ h7 A# M! g直接带入数据库,update函数我们跟进看看5 a) h) z: D+ v/ t$ u
9 }" c# d# ]: n! K5 z! {
- B& G* V) E: E& k$ m& u+ M
public function update($data, $table, $where = '') {
) ], h( y9 @+ V; R if($table == '' or $where == '') {
0 x5 r! y c1 }, X6 f# U return false; 9 u2 f) |! k1 E* y# e
}
' B* s5 | F1 ]* {3 {6 V5 f $where = ' WHERE '.$where; 4 n7 \5 x! G6 R2 C/ p+ R
$field = '';
# d( L6 U% ^3 R7 Q4 e if(is_string($data) && $data != '') {
/ S; |; S3 `, b# m1 G $field = $data; 0 y& ^0 {4 F, _& a+ L
} elseif (is_array($data) && count($data) > 0) {
2 \0 R9 O5 @4 E9 g6 v( \ $fields = array();
, T, l1 c! \$ ?( [" L foreach($data as $k=>$v) {
' x: Q6 H' X2 T3 n3 y7 Z/ b; ~ switch (substr($v, 0, 2)) {
$ ~& L+ B) y% g case '+=':
* |1 b J1 G" u4 Y $v = substr($v,2);
' M+ [8 s, ~, C/ V if (is_numeric($v)) {
3 K0 h6 P. K' f' z/ U9 L/ t. a- T $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 4 T- f8 v& d1 k
} else {
8 M4 D0 N) W. g) y( N2 [* B* D continue;
6 G& ^1 S1 d- \ } 2 n; z1 F: r8 j; _( V
break;
, s. [$ J* ], U/ K) W case '-=':
! c3 |4 N1 w; O7 u" H $v = substr($v,2); , h0 }9 N5 s$ f
if (is_numeric($v)) { " K. S" j+ ?3 s8 Q2 v. x% @
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 6 v$ V& O% v0 l
} else { 2 n! p9 C ^# Q$ I% _: M
continue; * ?9 \( c3 l \2 Y
} ! r( S* _( C% u7 S7 Q
break; # x" {9 v# r5 p' ~
default: & _' U* e, g2 ~! O( _! v
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); , @) H- z! {' ~) J4 e
}
0 ~7 U8 x+ J: E U } * O# Y: b2 _3 {3 r# N- ^7 f
$field = implode(',', $fields);
6 z$ U t" `1 t" J. h9 H! | } else {
$ ]3 E$ S9 v, N1 ?0 s" T' U return false;
/ q1 w" N# P- L7 |$ M" X } - t. G) p7 ^6 A4 R
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 1 q+ t5 f0 G4 O/ D% c* ?9 z% O9 d y
print_r($sql); # l0 w: v3 x) ~0 Z4 O! s0 w
return $this->execute($sql); 3 i& J' b; o2 V5 I( z$ s: P
} 0 N9 s4 j9 A6 r0 P7 s5 w# m
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
) r, Y, N$ Y2 ?3 j+ d7 @0 J5 F7 Z; U" a' {
攻击测试:* X! [, ~# r9 n* z( y0 P. v$ H
测试地址http://localhost
% d# L; L: o; R6 o, V/ P 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句; |# ^% N! ?: a# T) K1 u( U, Y
) |( s8 K. b3 k- n0 [ d8 N+ q$ w/ A/ P9 K1 M; p
- h: Q; |- w5 r0 A: C! {; w+ t- G9 _7 z
& M5 o& M& ~6 [ t: l |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|