|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告! ~' C2 w# [& l
漏洞作者:skysheep2 I; J, G# ~; d* U! s" W
分析作者:Seay% Z0 Q4 u$ r8 F8 M9 M3 q0 L
博客:http://www.cnseay.com/2 M; r' [0 |6 U. C9 b
漏洞分析:
( N X4 I8 h0 n- v3 M: Z+ o+ E4 F, X 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。# T( P5 d6 U, D
* |: @1 ~/ j# _' F7 }+ U4 z4 m+ R. N2 {# ^1 e! X1 I! \9 t9 k7 c
/ J/ o* ~8 \) M
public function account_manage_info() {
/ _8 u# j7 P {- p8 ` if(isset($_POST['dosubmit'])) { 6 V6 Z6 C7 N4 [. l- u* o& p1 y. K
//更新用户昵称
4 Z3 H4 b1 t: f& ~ $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
) w" h- U, A( e# D if($nickname) {
& C; E+ ], I+ }$ T* s! z$ r3 ?6 u $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
3 s- {. `( v6 Q4 k if(!isset($cookietime)) { ! v' t1 X6 J1 Y8 u
$get_cookietime = param::get_cookie('cookietime');
* f1 I2 Q, F, ~) Q }
6 H" _( f4 t( o" ]$ H9 h% K $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); : I+ m4 g0 c# H8 P, L7 R6 S
$cookietime = $_cookietime ? TIME + $_cookietime : 0; . A% M3 P- b, i, p f* ?4 f
param::set_cookie('_nickname', $nickname, $cookietime); . m" X7 ~0 {, V- y
} 0 J6 s8 J" Y* p- ?
require_once CACHE_MODEL_PATH.'member_input.class.php';
) v* T: v- c/ ^" T& t- S require_once CACHE_MODEL_PATH.'member_update.class.php';
0 o4 x! X# }+ z N9 a $member_input = new member_input($this->memberinfo['modelid']); - c: m; s) w$ u0 U! q
$modelinfo = $member_input->get($_POST['info']);
. O6 j6 n) f; l s l, q5 ~/ X $this->db->set_model($this->memberinfo['modelid']);
3 i, d3 O( ^1 e $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
+ T `7 n3 g6 M, J+ G if(!empty($membermodelinfo)) {
3 x$ E4 K/ q/ Q9 r3 c/ Z $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
0 J) b" ^) }) R8 r, X6 Q! [1 c) a } else {
7 H, ]1 o' z) e$ ~6 k1 { $modelinfo['userid'] = $this->memberinfo['userid']; 1 {0 o1 D; O0 k5 ^
$this->db->insert($modelinfo);
% F1 R* A" q' Z9 S0 `5 d }
. g( x- k# f; y代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,9 H1 x0 B3 X1 S# b# Q% K! l
在\caches\caches_model\caches_data\ member_input.class.php 文件中:4 T! ~; y* Q- S4 Z
1 b Q- P: M* G3 U( V" N+ @$ b6 S% t* _' |
\/ ?) I; `7 w- y
function get($data) {
8 m Z5 w9 f, M3 n' d $this->data = $data = trim_script($data); $ w5 A6 i$ _$ J. W* F
$model_cache = getcache('member_model', 'commons'); + ^/ E4 V; ^. G8 X' }; z
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 6 q8 b% W0 [' a q! ]1 l. J
$info = array(); 3 }$ e% @0 x, p
$debar_filed = array('catid','title','style','thumb','status','islink','description');
. L% I' [7 c- s8 j% t& P2 C if(is_array($data)) { 1 M S" g( W% F" U' s
foreach($data as $field=>$value) { 4 Y; x4 v3 `! T( u
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; & J6 X$ a9 h4 } m. E4 J
$name = $this->fields[$field]['name'];
/ f% V5 S- O4 x+ e $minlength = $this->fields[$field]['minlength'];
: r: `% j( F- ^4 Z $maxlength = $this->fields[$field]['maxlength']; 3 q% ^8 Q, }* K- D* j- U
$pattern = $this->fields[$field]['pattern'];
' `: y$ G. c: b2 v( t' j; i# S1 | $errortips = $this->fields[$field]['errortips'];
) K' s# e A! s- Y( Q$ t. L if(empty($errortips)) $errortips = "$name 不符合要求!";
) }! Q2 U% o* e $length = empty($value) ? 0 : strlen($value); \& |( R; z+ x: J
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 2 c- |" V( e2 X' ^
if($maxlength && $length > $maxlength && !$isimport) {
/ r: K% R4 v$ I, Q3 |0 C showmessage("$name 不得超过 $maxlength 个字符!"); ) T& w, K' P9 S
} else {
. C5 ^2 X& I) G# h j( n: Z str_cut($value, $maxlength);
$ [$ s+ ~4 j- u& `1 } }
; X# G/ U3 V- O1 K: A: J/ [# \0 V if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
9 x* a k3 _3 N8 P$ n `2 k; u if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
' T3 {, i9 y. J) z $func = $this->fields[$field]['formtype'];
: z& n/ q" } D if(method_exists($this, $func)) $value = $this->$func($field, $value);
: K# b' v9 Y, y, x8 ?/ W9 I, @ $info[$field] = $value; 8 W% B/ a2 Z, n( a
} 0 x. v8 V% \* t
} ( f5 U( u7 J$ I
return $info;
; p6 E8 y& j4 j; _" y3 i) G }
2 E; k/ {# K6 O' F% K' H xtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
; I& s7 D' v# r1 ^; Q, a8 i" O) \) X" V6 h h
再到phpcms\modules\member\index.php 文件account_manage_info函数7 ]8 F% a& n+ ^7 T) ?* w' l
过了get()函数之后。
4 P0 r5 S6 ]- X* M) \& @/ ^& k) q- {, r7 H8 H. k2 C/ E& b
6 Q6 d( ^# _9 j6 y! x# ?" o$modelinfo = $member_input->get($_POST['info']); 4 d* ~ H" o- e. q
$this->db->set_model($this->memberinfo['modelid']);
& A; e8 t* U& @# h $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
% Q" H! o8 { o if(!empty($membermodelinfo)) {
F2 j: p1 |/ ?7 q7 z8 C. f $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); " V4 f o7 p, n/ o( D5 j2 K2 p. ]
} else { 2 O9 ]+ }. ^: d6 N" W
直接带入数据库,update函数我们跟进看看
0 u9 i0 O, l7 l5 c9 W* |. y: R& _; ^0 v! |/ Q$ h) W" C) i2 _; e& L, T% j
. z2 ^9 |; n$ h( {
public function update($data, $table, $where = '') {
7 ~, \( w- S! i0 D. b: p+ R1 P0 u& ~ if($table == '' or $where == '') {
6 }$ t' N4 H, }' G4 X8 Y$ C6 Z return false;
" W! ^$ V$ \3 d: O7 B. U; F } 8 c2 w' H2 W8 S- y# q: i7 k' ~
$where = ' WHERE '.$where;
$ M- S' S& o& M $field = ''; 4 T( C0 r" X1 i1 C2 G$ }- a
if(is_string($data) && $data != '') {
6 Q% y9 D) D. f: Q" ?- ]/ ]4 R+ @4 ? $field = $data; ) \$ b$ F+ N4 @2 k% S, L3 E9 W
} elseif (is_array($data) && count($data) > 0) { ~3 a7 O3 Z# i! n5 K1 I- C
$fields = array();
6 K, \# T! {, K) G$ @7 v7 x foreach($data as $k=>$v) { # h2 f# f& U$ |) V) ^3 Y: a7 h
switch (substr($v, 0, 2)) { 4 U9 U; u4 T/ G7 c6 U w: y7 }
case '+=':
+ @0 h, n1 g" c $v = substr($v,2); 9 J5 @" i* F9 \. h0 Y* j
if (is_numeric($v)) { ' y) }% R | I8 @4 G1 {- [* W
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); $ j% |) j: u0 B
} else {
( P0 v) I& Z- q+ W& y5 g continue; 3 F' n9 ^5 G& e N: I
}
1 q8 D! x }6 r; C7 | break;
5 a# T4 ?0 e$ r [8 H" A3 N9 P case '-=': ' ^9 ]4 g* B* C4 P" ^6 p
$v = substr($v,2);
" t- W8 u9 |: J/ P' I; Y if (is_numeric($v)) { : [1 D: d" q; e; S. b
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 2 C! q5 A) W: h" `- K
} else { * }% Y: J" ?% @! b- j3 p
continue;
* l2 H1 `7 s& O1 m } ' {3 q! X _: V* q; \5 F( Q
break; ! O' a/ t# f8 _8 d( S
default:
+ c1 _3 |8 A, Z& L3 M $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
; n0 s2 j# F* m9 F# b% e1 P3 c }
8 T5 S4 E' j9 {" w4 M }
0 L( T w% U+ M7 w' O9 h $field = implode(',', $fields); , F0 E: ~. _' e6 Q( t4 q9 Y" `
} else {
- z% P0 I9 V' \5 l, Y# D% Q$ ~& S return false;
- h% E( i. S6 s4 i }
; E3 x- H: i% \$ m" ^: A7 V; E $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
# |: @2 Z J5 M% t6 c3 Y6 ] print_r($sql);
. U- \1 ?2 g* I; L6 O0 W0 c( y return $this->execute($sql); & V% {& N. X. j* @, f
}
4 N: v* l s5 M- c2 T; l# x4 C/ Y从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
6 K5 I9 r0 m7 u. }5 J
' B5 Z7 @3 p% B! i- V攻击测试:4 K6 w0 Z! p2 M1 T7 d8 o+ u
测试地址http://localhost
' m$ ^2 ^# I7 Z* L8 K2 K 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
6 H( D/ p9 g" t7 R' B
6 b" s V# w4 D% p5 U. |6 G6 \ 4 }8 f7 f# I3 W- U& C3 y7 K; o
9 R3 `& {2 |! U, _# k( v& }
2 A# d2 j7 k% L5 p- i0 V1 [* D; f' ~
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|