|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告6 e& U+ }, [" I" j; h
漏洞作者:skysheep3 P4 {, F, F3 I3 h9 F m6 H( ~
分析作者:Seay
8 U' ?) ]. } j& s" d C* a: B: W! O5 a博客:http://www.cnseay.com/' P1 G# K7 H# S" b( q
漏洞分析:9 H: x+ m, ], X7 }' \# I, a# z7 Z, e ~
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
7 p0 m. m3 x5 S! m [+ v! Z0 f+ v9 Q3 p) y. f3 d
% T3 O" y) [7 |. a$ c1 c - l# O. }$ F$ B8 f7 l+ p& q1 i
public function account_manage_info() { ; ] T, `- M2 J0 ~% K) ?
if(isset($_POST['dosubmit'])) {
- ^ X" J7 F$ v; h( [+ M //更新用户昵称 ' x+ _, K9 T7 A2 g u* H4 e
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
" {. |6 \+ N3 x& }, _ if($nickname) {
$ i) U8 [7 n- a1 |6 {4 P2 E: \. i5 S $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 7 C; V) W" J. W5 s" ^! l* y
if(!isset($cookietime)) { 4 c( r/ ~9 V' u1 F" J9 J! J ^: B
$get_cookietime = param::get_cookie('cookietime');
- v5 q+ x9 ?% l5 q1 a% o) } }
3 A* [- l* b! S9 x; C $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
- U8 @) ]7 a$ h $cookietime = $_cookietime ? TIME + $_cookietime : 0;
$ ^1 Y4 m8 S. r* C8 v/ a1 h" V) G) S param::set_cookie('_nickname', $nickname, $cookietime);
1 Q( h$ P; G% t/ @! Z } $ }# X# `- t, r9 Y8 W7 j9 k+ \! x+ Q
require_once CACHE_MODEL_PATH.'member_input.class.php';
, K" h6 u O( Z3 V require_once CACHE_MODEL_PATH.'member_update.class.php';
" y) c- Q8 J S, j9 h( w% Y1 w $member_input = new member_input($this->memberinfo['modelid']); 1 J0 X+ ]+ F5 A/ ]
$modelinfo = $member_input->get($_POST['info']);
3 v* m' ]$ s b7 b" M $this->db->set_model($this->memberinfo['modelid']);
$ r4 e0 w& ]1 u+ h/ I8 y $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 3 q1 ~5 e, }" H% v5 e) g
if(!empty($membermodelinfo)) {
3 H, R8 H1 ~% x, n6 S' I7 G $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 1 F% Q# m4 B! e# f3 e
} else { ' t* `! E6 M# ^. d2 |; W* _) }4 h
$modelinfo['userid'] = $this->memberinfo['userid'];
! y& f2 N& i- c1 V a5 V $this->db->insert($modelinfo); ) d4 ^: E) {- {; V! F3 Q4 b" `: Q
} * ?, \9 f9 ~2 D! ~) Z/ o& \- c% w0 u
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
" x( R! O0 t" T* ^$ X# {+ F$ F在\caches\caches_model\caches_data\ member_input.class.php 文件中:
2 j) r: M$ H; j. q& z2 e
; q8 Z8 ~! l* h+ }8 ?2 N9 n# V
; w" c1 r. a0 H+ k# j( |' M7 P( h
% G1 c& l* l( T) T5 Y( wfunction get($data) {
4 d, f: |+ u. Y! N* q8 T $this->data = $data = trim_script($data);
- b- Q: ?$ S* ^4 ]1 Y $model_cache = getcache('member_model', 'commons'); 0 n. A4 _5 i! f O1 p U1 ~& W; n0 w
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
$ U- t# g1 A1 u& A; V/ T $info = array(); " X8 A, l& z% X5 t
$debar_filed = array('catid','title','style','thumb','status','islink','description');
' A/ `8 m+ F! L9 x if(is_array($data)) {
, ]7 W( k- q5 Y5 k7 p foreach($data as $field=>$value) { 5 @1 [' L, \' I3 D' Q) |- \
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
* {( n+ o+ F: N' Q P/ H- t- M $name = $this->fields[$field]['name']; * d; C+ E# R) l; @6 u5 j4 V) h+ e" r$ _
$minlength = $this->fields[$field]['minlength']; 1 { c1 G$ @* _2 l
$maxlength = $this->fields[$field]['maxlength'];
+ T9 N/ j: u: R! \9 ^; H$ _ $pattern = $this->fields[$field]['pattern'];
$ J( B3 c1 W e& P! V" O $errortips = $this->fields[$field]['errortips'];
9 w& [5 f+ b! e: p. e% ?9 [ if(empty($errortips)) $errortips = "$name 不符合要求!"; # \& w' l+ I: P- ^
$length = empty($value) ? 0 : strlen($value); 4 c9 T% u% `- K+ B* k8 }
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ' T- [5 _. z( ]
if($maxlength && $length > $maxlength && !$isimport) {
: c' O1 ~+ C7 w showmessage("$name 不得超过 $maxlength 个字符!"); / d) T1 h) C2 [" l8 c/ s) i
} else { 5 R, Z0 a% b t/ ]# X, z
str_cut($value, $maxlength); ! v8 t( Z# ~9 {5 |) p
} % ?3 p( U0 \# r* v3 j: ?/ Q
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
1 ~ ~0 c" _# o4 l1 |) z if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
. P, l" Y H' v. {$ H: Z $func = $this->fields[$field]['formtype'];
" U" ?3 p8 V" | if(method_exists($this, $func)) $value = $this->$func($field, $value);
( ]; h3 ?) u: }1 W I! O7 |: }& h; [ $info[$field] = $value;
& }; V+ n. C( W) w: ]" ]6 U1 } }
+ k. Z- Q# D# [" c: y: ] } 8 K v: V/ ]3 a5 p4 R
return $info;
, ~* Y' u" @( U( v' `* U# p }
3 R+ }3 X+ Z( \+ w) Y, j) Qtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
3 ?* c" l/ V* a* h% \9 V
: Z. v9 D- l% v. K: U( g/ t再到phpcms\modules\member\index.php 文件account_manage_info函数
+ M* Z8 `) j, ^* V过了get()函数之后。
2 L6 p1 I. d- `* X2 U, H! h6 V4 Y! k) _1 o
5 c, t/ L4 W! |$ [- ~$modelinfo = $member_input->get($_POST['info']);
, z9 ]$ _) ~$ f2 F. }/ k& H' V $this->db->set_model($this->memberinfo['modelid']);
5 b. F5 ` w+ k- f $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 4 l5 h2 J! {; }7 C& |4 H; d
if(!empty($membermodelinfo)) { " B/ J) k7 B& ~/ I2 y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
5 Z9 b9 Z& ?5 Q% A* T! Y } else {
% j7 h+ n% M2 M4 ^( v4 H8 G4 x- O直接带入数据库,update函数我们跟进看看
1 {2 N( X# G1 z+ H, W/ M9 K! e: ~
+ E% i @1 N$ A5 ?! b
public function update($data, $table, $where = '') { * e9 i- h! ]; g0 V
if($table == '' or $where == '') { ; a# }% N! s5 z4 ~
return false;
# V: B0 X: y& ]0 q1 M }
. b" A/ G6 C" `. y $where = ' WHERE '.$where;
/ g5 T# P3 c) {, V' I $field = '';
4 ?- x. K- y% D; N! `4 @0 ^ if(is_string($data) && $data != '') {
5 Z0 A l% h, ~+ t! p! X $field = $data; ) b9 S9 V: _& A: h
} elseif (is_array($data) && count($data) > 0) { 4 b& D& }. E! P/ v
$fields = array();
9 O7 O r4 i7 K& B* D& ~* l; ~) v foreach($data as $k=>$v) {
5 ]/ }& X% ~0 d switch (substr($v, 0, 2)) { A: w+ `- S" d! J" I7 T. h
case '+=':
$ d8 T- l5 W% a7 ~; h/ g8 Y( N9 _ $v = substr($v,2);
1 i+ O' H u3 R h! W1 ~ if (is_numeric($v)) {
/ T' b2 U. I; L9 F7 U! U& { $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 1 S1 X4 g; y4 j& d+ Q
} else {
8 N( x; R5 _% s L! c4 v% J8 l continue;
% ^& C/ b2 V, ~; W: p; Q% N }
/ V1 W' x% B: B O& ^6 U6 X$ V break;
0 K2 c, m1 m j0 R3 [ case '-=': 1 J1 V% w. e: \0 s1 U8 f
$v = substr($v,2);
' [+ d1 o7 t) y+ {5 t if (is_numeric($v)) { 3 V9 b+ @* P# [: c% G' L
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); , l9 w4 j% K' {
} else { / D4 f' V- k4 M! w
continue; " K& j/ p, l$ V' H/ _
} ' t" K0 a3 S* L
break;
' ~& n. E/ |% d default:
4 w( N( @6 d: [) w $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 0 D2 [# R1 A! x, E, \3 {
} 9 `0 U* Z6 o k/ b' I1 e/ D
}
" g# [/ t; y% W $field = implode(',', $fields); " F$ X; @. m) W1 i2 L& W
} else { $ P6 h4 X9 w& ^
return false; % y' u* Q+ A! A" Q1 W' Q( O, l* ]
} 5 H; p' x) m3 b) s
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; * y' t% ?8 v* T: K8 ~
print_r($sql);
5 j9 F! L% ]; K' d8 e0 W* N return $this->execute($sql); 9 m! m0 W j2 Z- j; K& U
} ) n0 [! t. E/ z4 D! G- B
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。# f- s2 x5 ^* U( u8 i" ?0 i
& \( }# ?- A. g: U" E s. H
攻击测试:
6 N f# ^0 k+ G0 P! E5 c测试地址http://localhost
" ^8 K" @/ t7 N- J& x 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
+ l8 ~0 s3 ^# q/ Y7 q
+ @& \, `( ^5 U/ q2 U5 ?+ G 5 h9 f7 V1 x+ g) ^* c
8 c; c+ `' U1 W/ ?) n: _
" E7 ]7 Z3 ~% A1 z! g/ I! P8 ?$ N* l8 ], l. Y {" F
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|