|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告+ `2 S8 J* A; h+ x2 g
漏洞作者:skysheep
! {% g2 t0 _* r3 F8 f! T, H分析作者:Seay
& C, l0 k$ E {' e博客:http://www.cnseay.com/
' r" y8 o1 g" \# L漏洞分析:
+ X2 s& z5 u' i 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。# S$ q, J4 Q8 y6 d2 U" Q$ [8 a! T0 _
' ]3 Z4 n* E! d# y/ V* Q$ O
0 R; [' M6 m0 Z: D' F- l/ j 9 O" a$ F- j, s; M* a+ V; j
public function account_manage_info() {
7 b( c" q5 x! H& | o% C if(isset($_POST['dosubmit'])) {
# v# J8 b' F( g& x* F2 N- M6 \ //更新用户昵称
% _# G& {1 i, `. u& _ $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; : r/ j1 i$ t/ s+ Y5 q# a0 d' C8 l% ?
if($nickname) { . M% H ~9 z, R# M, Q8 q/ g* K: L
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
7 R5 S6 p F* m4 e. a if(!isset($cookietime)) { 1 }; S! i" E" g2 H1 F; `6 z5 q0 t
$get_cookietime = param::get_cookie('cookietime');
I# W# h# z8 D7 E } + {' I/ V- i7 U6 l7 ^$ I
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
. g, ? A+ {. F, M+ r& y8 B1 Y $cookietime = $_cookietime ? TIME + $_cookietime : 0;
! A6 Q: ^3 }5 \ param::set_cookie('_nickname', $nickname, $cookietime); - |' f+ I+ y+ [4 N k
}
1 @: p$ f I$ }% X# n/ h l require_once CACHE_MODEL_PATH.'member_input.class.php';
& O. Z8 `" U P/ }& P; X+ ] require_once CACHE_MODEL_PATH.'member_update.class.php';
. k: r# D+ D# ]+ y7 d! E/ a $member_input = new member_input($this->memberinfo['modelid']);
; e& R4 Q1 o9 i2 d $modelinfo = $member_input->get($_POST['info']); + o( b$ c2 t+ P! G) z
$this->db->set_model($this->memberinfo['modelid']);
; y4 z# T V' ] P4 T $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
) s% O! Q6 z9 b6 k; @ if(!empty($membermodelinfo)) {
" m) u7 z# S( m+ ]# C9 L# V $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
2 J, ~5 Q% Y* @7 O! m } else {
# g9 O7 i/ W5 l+ @7 ? $modelinfo['userid'] = $this->memberinfo['userid']; ( X, a. R5 ?* Y" z, r/ Z4 r
$this->db->insert($modelinfo);
! Z3 u" T; i, X9 [, v }
! T) O3 ]6 y! M& `( z2 b* r/ F代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
& [7 A/ T0 T) I$ `在\caches\caches_model\caches_data\ member_input.class.php 文件中:$ M U# ?+ h: D2 [3 c
, n! ]6 ~. I% R! v9 N; t
" n. O( B8 k0 y! S+ X4 q; V
" v& T' |( Y4 N: g4 L
function get($data) { 9 L2 ~! f, @1 S: [) G
$this->data = $data = trim_script($data); ' K# }) L0 }. G3 o6 P: I# c+ Y+ x
$model_cache = getcache('member_model', 'commons');
2 J. S" C' R) e! }- |8 P $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
7 z- i6 y( `9 d ? $info = array();
1 b8 `1 I' X- t5 | H $debar_filed = array('catid','title','style','thumb','status','islink','description'); * e4 Z# M6 {4 T1 Z
if(is_array($data)) {
/ T4 m9 {. ^! L0 F% W foreach($data as $field=>$value) { 6 B& Q, Y7 \% @' t. r
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; ) e% ?" R8 w6 f; F$ C8 I# b9 o
$name = $this->fields[$field]['name']; ( j6 H ^( j1 @; }, i0 G0 p4 K0 {
$minlength = $this->fields[$field]['minlength'];
: p/ o6 r7 m& j3 q $maxlength = $this->fields[$field]['maxlength'];
( c/ J- Y7 j, F0 [7 H- @ $pattern = $this->fields[$field]['pattern']; / Q6 b7 L, \) q6 @" }6 E- T8 \
$errortips = $this->fields[$field]['errortips'];
3 f/ C8 c# P: K1 @' V- K0 k if(empty($errortips)) $errortips = "$name 不符合要求!";
- ~, c/ n) [, r/ Y3 P2 ]5 `4 E $length = empty($value) ? 0 : strlen($value);
2 Q0 ?# o' s2 r5 f& D; O2 h% @ if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
6 Y- _- x4 Z2 F1 l' Y if($maxlength && $length > $maxlength && !$isimport) { % y4 z$ \3 P& E7 e
showmessage("$name 不得超过 $maxlength 个字符!"); . J+ K: ?- b9 h. T! O
} else {
8 [( B3 r+ Y5 T; k str_cut($value, $maxlength); : E) `. g( r8 ~* ^3 p: U" h3 y
} ( C) X! k1 ^ `& |
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
7 @* P- m, a0 N' A% N7 H+ B- o if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
& B! s( j1 [" p/ j9 ?5 k $func = $this->fields[$field]['formtype'];
. F3 V1 R& x/ i' r, |' L if(method_exists($this, $func)) $value = $this->$func($field, $value);
, n& C8 `' m4 {* e% r $info[$field] = $value;
; n7 N, e: Y$ W0 n3 g) G }
2 u0 T! g/ |, r }
" A6 s( N1 z) A4 h- F: F4 K4 z return $info;
. s% B2 C9 ?4 U6 Y4 N+ q }
$ {3 e u5 h, Q! i4 Itrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
3 g% r. x8 q% M- p# t: X" J
0 J' j d: O$ A% { S再到phpcms\modules\member\index.php 文件account_manage_info函数
! S+ ^/ u0 w$ K# f过了get()函数之后。0 Q/ m3 S& I1 [ C
# p, D1 O) E% T1 E' z
% r5 e" l. ~0 y& v1 C" |7 y
$modelinfo = $member_input->get($_POST['info']);
/ x. R. p1 l& u- h6 U8 @8 l. l $this->db->set_model($this->memberinfo['modelid']);
8 \0 ?+ i2 `8 M7 g+ O# C0 b $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); % i! C1 j0 z H' {
if(!empty($membermodelinfo)) {
% `) {) t- t& E+ M$ y) S, S $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
4 h( [. p* V, l- P9 B* X } else { ' E4 Z# e0 s& E9 }
直接带入数据库,update函数我们跟进看看
& r3 }2 w2 ?- }8 f. w* I0 r3 r6 u; M' U h" |( G
! D) k( b6 P" b0 }
public function update($data, $table, $where = '') {
9 _* I: |7 Q6 w5 ] if($table == '' or $where == '') {
* }7 G; [+ E% R4 h( s- j return false;
/ L# D( L* E. z" e } - p( T: X* k- `: r( ~8 c! A B0 p
$where = ' WHERE '.$where;
5 F2 C: r. K; V, V- p $field = '';
6 ~! P9 i, h+ s' Y$ W if(is_string($data) && $data != '') {
0 C8 h4 c' T- J ]5 G' V2 S& q $field = $data;
; {8 s' Y; H* G; u0 ?$ Z } elseif (is_array($data) && count($data) > 0) {
5 w* O- r$ l4 F6 | $fields = array(); : A+ R# u5 V8 [
foreach($data as $k=>$v) { # M4 |& d, {7 s- V0 ~! U [% O( f# M# _
switch (substr($v, 0, 2)) {
) Z$ V/ s: E+ F& r case '+=':
8 X$ B3 c* I2 v4 n. g3 S $v = substr($v,2); + E5 |) j+ R# i5 ?$ P
if (is_numeric($v)) {
, K& f" b( I; A. v $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
! L- j% B1 s) g1 G2 v# w } else { 7 L6 c) O1 C* z- Y) q* P. c
continue; - J0 E( V; ^: W0 T/ ~2 T& ^7 z; X
}
0 |, D+ K: O) E+ Y7 F break; 5 Q3 i/ R- }* ^
case '-=': ( ?( e( {6 r- v& X' a) v9 V
$v = substr($v,2);
1 T% {1 l6 E0 f7 ? if (is_numeric($v)) { ) h4 c V. m) V
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
$ F) ^: Z. _; ?, d) t' c } else {
# u9 N% {7 @. q! n continue;
) b- U& e3 d- \% h& V }
5 z1 r8 P1 i* @0 ] break; 0 P4 M8 U3 W' S1 ^, W& S9 L
default:
' ]8 K! o7 R: E6 q2 @: x0 ] $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
{8 G( H w3 z. o/ v1 X) m }
2 U6 e! i. e( x& C# E: Z }
9 G8 W0 _0 c6 y v: L $field = implode(',', $fields); 8 U8 j" e' G9 O A3 `' Z: b1 A
} else {
* l# [9 S6 y4 B, V! g return false; - }6 F- f. L" t$ ~- C% {+ l) C! F1 e
}
6 O( Y3 ?. W) E7 }1 H' } $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
' F) M0 G; B; c print_r($sql); * Q4 n7 B- b+ A
return $this->execute($sql);
5 g) j) b7 t) X2 |$ ?9 M } ) q# P8 J3 A4 R6 j) D3 L# l
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
+ f, o6 @1 H" p& X1 [
2 \ I( z5 e; Y攻击测试: W& \6 [5 |, p9 a
测试地址http://localhost* _/ w7 l% d6 \* Z/ }4 u
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句! m7 R% H* \) W0 m1 }
0 Y5 }! l( o( [
]% X x k0 p/ Z1 w$ {) |
9 V3 {9 m, o5 D: P5 M
8 o$ u" c/ s5 ~! N: v; C9 s m2 E# M% T8 C- C; g* [, |7 E
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|