|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告0 z8 w/ e& b2 Z/ Y# `
漏洞作者:skysheep5 D- x) G% S9 l0 p& H+ X; {+ J; |
分析作者:Seay$ L. n* ~' Z1 o6 O$ K
博客:http://www.cnseay.com/
; H1 B) V0 ]6 g9 b漏洞分析:
( F1 C, }* {9 g) p/ E S( o; m 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。) \! y- O6 j2 m2 @
# s; X$ _+ X2 }: f0 y' F) K" h% j$ B+ l8 ~! v0 d4 R
: e. w6 E( E1 z5 a7 ?& Lpublic function account_manage_info() {
- r" i& T, S+ q6 b0 L if(isset($_POST['dosubmit'])) {
* W( `9 \! r, E% D6 o- P5 _, d) N //更新用户昵称 , Y. n. I' S% n+ U8 v' b0 S: @
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
1 w; p+ x. g/ q" O& D if($nickname) {
4 Y B4 w Z! Q' y m $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 9 o) o2 h: d# D# C8 J
if(!isset($cookietime)) {
% r5 Z( {# A6 T/ u7 B $get_cookietime = param::get_cookie('cookietime');
" B/ e' [ u v }
+ O4 e$ O, V& _: f0 m $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
/ p' O4 N: P* \$ M7 r* m $cookietime = $_cookietime ? TIME + $_cookietime : 0;
& b8 I V. i! p. n param::set_cookie('_nickname', $nickname, $cookietime);
i* |+ z) ` a# n' f } 2 Q. k) B- u9 w6 R5 t( C. H* k0 f w- C
require_once CACHE_MODEL_PATH.'member_input.class.php'; & Y8 R" s7 G6 a" J+ C
require_once CACHE_MODEL_PATH.'member_update.class.php'; * Y' K# {( O/ A/ |$ Y1 Q+ V
$member_input = new member_input($this->memberinfo['modelid']); 2 J X+ X9 d* d8 _8 w
$modelinfo = $member_input->get($_POST['info']);
) n9 b0 P z4 A6 S $this->db->set_model($this->memberinfo['modelid']); * v, T, h" u) u
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); * }8 \' l) I3 H0 Z( H
if(!empty($membermodelinfo)) {
& F1 u; e! |1 V $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
4 J. q9 A9 S/ t% c5 E+ g } else {
7 l) N/ n9 ` T' y" o $modelinfo['userid'] = $this->memberinfo['userid']; - j3 g. j' B: f5 g+ M
$this->db->insert($modelinfo);
. B& k# t7 z5 u" D } ) \1 Y/ W4 m! z7 F' a" Q% ~9 q' ~
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,& a( @# t( U+ w/ J9 l9 V
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
( m* o2 S2 N/ O7 O
2 ^2 R& n; T% R9 P% h: q
" s3 ?5 Z. `1 _& E
9 i5 m1 F& b9 R H. Qfunction get($data) { + t% X+ [: S& F0 e: [
$this->data = $data = trim_script($data);
, `9 s! w% \* y8 w5 \ $model_cache = getcache('member_model', 'commons'); * l* J; ]6 J# _; K. j# |
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
+ @ o$ W$ ?5 x/ s$ J9 Y' a $info = array();
3 m" y" e; [. ? $debar_filed = array('catid','title','style','thumb','status','islink','description');
; K7 e: n- f, D/ t5 }, m if(is_array($data)) { * b4 G, \2 P' `- y9 \! f
foreach($data as $field=>$value) { 2 S0 I( G/ Q3 E1 P
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; + A4 ~# }3 z" |& N: I
$name = $this->fields[$field]['name'];
& |$ r/ J: G3 N& N $minlength = $this->fields[$field]['minlength'];
i, x9 A4 Y- G, A3 A8 y $maxlength = $this->fields[$field]['maxlength']; % L+ q4 N7 h) O+ x) W I% P- l/ C
$pattern = $this->fields[$field]['pattern'];
5 _6 o+ x* |3 d: B $errortips = $this->fields[$field]['errortips']; + v q1 O' V9 B5 N
if(empty($errortips)) $errortips = "$name 不符合要求!"; - a3 r3 a W J6 d" v- y' ^
$length = empty($value) ? 0 : strlen($value); : y% ]% k {+ Z" x
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); . X+ m8 G2 }/ p- N( I8 N6 b, Q, o
if($maxlength && $length > $maxlength && !$isimport) { : A6 a1 ~5 x1 p/ y; |1 ` h
showmessage("$name 不得超过 $maxlength 个字符!");
2 c0 a; n' O6 n) l* z. M _' C } else { 5 C2 Q5 a. `( J) ~( j
str_cut($value, $maxlength); ' r4 a9 q6 ]# O/ k1 i
}
0 ^; N" j6 Y4 [+ r& ` if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); : ]4 `# ^& t0 m9 m% z: N6 [
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
8 T) i, [( T1 [7 ^: P% _ $func = $this->fields[$field]['formtype'];
; M, k! [# T- O5 B" U( P0 \7 ]) ` if(method_exists($this, $func)) $value = $this->$func($field, $value);
$ d( e4 P# m3 z $info[$field] = $value;
x, p$ y' g9 A9 N& f9 J, w2 | } : M" G7 o/ t# i7 z' J: y- `: ]2 M$ {
}
# m" f& Z& T& U+ k return $info;
! q" n2 {5 V8 Z+ I2 w } % M2 G% n- B5 k! P: o+ e
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,5 j# y7 x7 Q6 x( H! z X
+ [& B/ C D( K" S4 i6 ~9 }
再到phpcms\modules\member\index.php 文件account_manage_info函数! h: P2 {0 N- T. R) ^/ o
过了get()函数之后。
1 M3 R5 v+ W: y7 R7 o+ \! M7 S
; k Z$ Z0 x0 _
8 m3 ~& v& D% ?) {$modelinfo = $member_input->get($_POST['info']); + K& \$ P3 t$ \8 Z4 v6 I
$this->db->set_model($this->memberinfo['modelid']); 4 P2 i9 ^; x1 N' d
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 8 l+ a! b7 l) j" m8 ]
if(!empty($membermodelinfo)) {
5 c# ~6 \2 |6 b, u $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 2 a7 B% A% d9 n. h% K+ c; x* b5 l
} else {
) T, _! L4 m7 Z直接带入数据库,update函数我们跟进看看
/ _" F5 N! G+ O
( L& ?& v" K: S- C0 g+ H
) e7 \+ j; t- o" opublic function update($data, $table, $where = '') {
. }% d5 y. x% m4 J if($table == '' or $where == '') {
) I- ~2 b7 p. _$ n: W$ a5 u0 j' n return false;
9 I9 C. Z$ u3 R4 ~6 @5 [ }
5 z0 G6 q F+ J' s& g' V8 V) l: J $where = ' WHERE '.$where; " ^1 K' D" }& e1 W* I* {& ^
$field = ''; 9 P1 n' ^3 N# i. [( M: U, Q
if(is_string($data) && $data != '') { 5 j3 |0 J0 Y4 V; B- d; s! {8 }8 v
$field = $data; ! r# s. B" Y# U. `' v
} elseif (is_array($data) && count($data) > 0) { % j; Z u& ]/ L& p0 v$ t
$fields = array(); - g. B6 }* Z! B1 P' G
foreach($data as $k=>$v) { " V. k1 ?! s2 b4 R
switch (substr($v, 0, 2)) { ) N }, _# l3 L+ {" I/ j6 L
case '+=':
9 X& r9 `5 ?$ F& P* r+ K: ~ $v = substr($v,2); 9 y2 s$ ^3 F* O1 Y4 K/ v. S* D }
if (is_numeric($v)) { K( r7 a0 r( C. G
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 1 F+ t, g' c1 E) j( t! Q7 s# W
} else { 1 L# q+ T$ P0 W7 c
continue; 4 R' s. X7 U' G n2 Q7 k9 j$ P" q
}
2 O8 V2 ]5 G* }9 O' v break;
) r- \7 p, ]+ h/ l2 |/ | case '-=':
7 u" |0 H% }. o3 ?7 a0 y& k $v = substr($v,2);
! q7 M! o+ ^! ^2 O0 O, u6 [- ?/ Z if (is_numeric($v)) {
9 a, {7 | Y% W$ R' L $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
- i/ D {! |( s } else { ' e# q4 M4 `7 n! K3 Q
continue; ; l1 G4 m2 `( p
} + b# h6 F: I7 S" O0 }7 q: m) s, N
break; ) [, \4 s0 T$ M& e$ i I
default:
$ l& @/ b+ F9 C. R) ^! I7 v; j $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 5 l& Z @" t/ B# z' z/ Q. X
} , |6 Y" D( B" B7 l( q
}
& o5 f( ?/ J4 x& T! s $field = implode(',', $fields);
2 M$ ^: R4 i1 B. b: D% I5 v% _% } } else {
- H) o, S! ]" A, G1 [ return false;
7 r! T6 I* C- a' o }
. N: `2 q( x! |* v1 U $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
4 k+ Z0 ?1 ^. b& H0 v; K print_r($sql);
5 e u: Z9 X0 w) |6 C* J return $this->execute($sql); 9 S9 E& Q8 Y& [( [- D
}
% s7 s3 g0 ?" q3 T9 G3 x1 K; k从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
+ d9 }6 S! s5 x* I0 k& V. O( b, A+ ?
攻击测试:- [2 {7 N! u$ N% v$ y5 u
测试地址http://localhost
( w8 T% f+ z; b3 P; G5 M2 Q 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句. o7 v- U: c1 Z" H. U
+ {# k5 j) o' K6 R2 U" F 9 E/ P7 C& l: J" l# f* ~
+ H# t, Y* a6 N2 O' j) R7 ]/ u# E/ E ~, V* I0 Q# l
; P2 N, O# S" F0 M
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|