|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
/ F! t, H6 L- j( i0 b+ c漏洞作者:skysheep
w8 s, | @; H3 p& l0 C5 z$ h0 K分析作者:Seay
: M' V% a& S1 d& W' o: o博客:http://www.cnseay.com/, O% P% U d; _8 ?& e# f
漏洞分析:' T$ y6 @* x. ]+ K5 T8 i8 g
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
g- T: {5 D0 _
% Q# N, l& l9 H9 U' }( T- j/ v! Q: U7 v) \
* g: S4 r3 O- a) j3 V" ~- dpublic function account_manage_info() { 1 V+ Z. _. c" h( y% p* ]
if(isset($_POST['dosubmit'])) {
, f# U2 A$ y( L% _3 \" w2 w //更新用户昵称 ' e. f9 ~* d, j1 ~+ R* ^1 Z
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; ) d2 e& J: q* F* I, l" v i5 H; e
if($nickname) { ) h' l* ^% y& g! x
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); + u, Z# R5 F5 T4 v* @+ g
if(!isset($cookietime)) {
; S+ e4 C- Y* j $get_cookietime = param::get_cookie('cookietime');
& P# M9 r2 a- A3 Z# P* u } $ O/ Z: [, C. ]. _
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
6 t; I) L/ [' U1 V7 l" X, s V $cookietime = $_cookietime ? TIME + $_cookietime : 0;
6 n, D# b# e# \( A param::set_cookie('_nickname', $nickname, $cookietime);
2 M$ S. \ P3 N" G2 c; a0 Q } ) K: w& H2 M9 h# q3 _
require_once CACHE_MODEL_PATH.'member_input.class.php'; * j' t1 U$ `& l x
require_once CACHE_MODEL_PATH.'member_update.class.php'; 1 L8 Y" l( S2 i! I) ~
$member_input = new member_input($this->memberinfo['modelid']); 6 g g# h8 c1 n8 ], {
$modelinfo = $member_input->get($_POST['info']); ! Z }. B% ^% R1 R9 d) d/ `
$this->db->set_model($this->memberinfo['modelid']);
3 m, \5 t1 M+ E6 p" L $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 5 f0 o) I" R% \( z
if(!empty($membermodelinfo)) { 6 c! [8 f B1 |4 U' T6 \) e! ^
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
- i1 N% _& l: ^ } else { * p# z5 I2 j6 m1 p7 z X
$modelinfo['userid'] = $this->memberinfo['userid'];
# b. m( |& z! D! P $this->db->insert($modelinfo); 6 ?( u+ j. E3 ~
}
K$ k! u H+ w% y代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,/ \: E) ?/ h6 h/ e9 M0 Y+ ]$ {$ M
在\caches\caches_model\caches_data\ member_input.class.php 文件中:4 S& y8 A* c! b7 g: Y% u
3 S! r" I- c' b X2 Y8 _0 o
/ z7 k/ O8 F+ Y
0 a* m2 M* h' e$ G6 ]function get($data) { : b/ |3 N+ }/ I
$this->data = $data = trim_script($data);
* S% T; z% x, g8 H4 ^7 P $model_cache = getcache('member_model', 'commons');
5 A' V$ x% s0 R3 c) x& c $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 8 }0 v$ g- v! p; ]: Q
$info = array(); 7 y9 ]& }, r5 {0 m
$debar_filed = array('catid','title','style','thumb','status','islink','description'); : c8 P6 m) d- M6 Q
if(is_array($data)) { * `; u# ~$ C7 \5 |7 o8 r3 y' J4 I
foreach($data as $field=>$value) { 9 W+ K7 }- X7 A, x |4 S7 N
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
1 I3 l, I1 G: o x. ~+ v- a- k9 l $name = $this->fields[$field]['name']; ! ]; r7 n/ Y+ ~: b: Q5 a
$minlength = $this->fields[$field]['minlength']; : P" L& w% s+ h
$maxlength = $this->fields[$field]['maxlength']; 7 S8 k9 k# n0 j1 v9 [& M
$pattern = $this->fields[$field]['pattern'];
( q6 {% F0 i: t- n $errortips = $this->fields[$field]['errortips']; ; A. d1 K$ J1 `) {' `: j
if(empty($errortips)) $errortips = "$name 不符合要求!";
) s0 }. j' T& I1 K& e $length = empty($value) ? 0 : strlen($value);
8 X+ W! q6 B$ _- H, v% f8 C& |/ g if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
# i% |/ [- z! C6 T$ o+ e6 u if($maxlength && $length > $maxlength && !$isimport) { - F! b% _+ b2 O2 V$ @
showmessage("$name 不得超过 $maxlength 个字符!"); ' o3 H. K) o0 j! w6 q( T5 J7 u
} else { ! O, V- m( W4 \
str_cut($value, $maxlength);
( r% a0 M. ]5 L) D6 B }
9 `! e5 k z a: \ if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); # N& G# j' `- u) r' h
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
6 Z9 o! f6 ^8 ]% w, N/ ]) e $func = $this->fields[$field]['formtype'];
3 O& O$ g2 H. w8 C/ d* _ if(method_exists($this, $func)) $value = $this->$func($field, $value); . @! N3 l: p6 z
$info[$field] = $value; # ~8 Z6 [6 \; q/ a& _# y
} 2 r: ~- ]' o( i8 }4 S5 p
} * W% f: P- T {+ ?; a6 f
return $info; # X8 R& c+ s! I* A1 G
}
) @- L$ x: I0 \2 Atrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
, n! F# y6 k+ N, \9 j
/ J f, |5 O3 m6 W* D6 N2 i再到phpcms\modules\member\index.php 文件account_manage_info函数5 D! L# `. o0 `: p' J+ i6 ]2 @
过了get()函数之后。, h0 Y6 b" D$ D
; B; M' `% b3 N! U: Y+ a, t
; G! h" P- M" b" U7 `6 ]( P' K; y$modelinfo = $member_input->get($_POST['info']); ; E& Q. z3 K6 Q$ E4 Q9 o) w# f
$this->db->set_model($this->memberinfo['modelid']);
- V( o( A: a7 G: C $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 7 K8 v# ~5 }) g+ l: q& Z! r
if(!empty($membermodelinfo)) { T0 U( f$ v; T' ]
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 9 L: V; i2 D. q* Q2 L. ]' m* i& H
} else {
: y5 Y% Q$ l1 f5 e$ Q1 r% Q! A直接带入数据库,update函数我们跟进看看/ W/ E$ Z( R7 T, s/ {
5 r& C8 e1 M- U & `: Y N7 @8 l w
public function update($data, $table, $where = '') {
3 e: O2 T, x& j8 K; n if($table == '' or $where == '') {
+ d4 \7 \: p4 x return false;
T2 D6 F& k# ?$ n5 E4 q, K) X }
9 e& ~& k1 B% W- N( P+ M& T $where = ' WHERE '.$where; - ]& ~* l, d& i: U
$field = '';
1 I0 q' a* n8 W if(is_string($data) && $data != '') { ! T: L$ ~+ r& U, C. b
$field = $data; # E2 ?1 x- r) C7 W& w! f
} elseif (is_array($data) && count($data) > 0) { z, r5 P, G" f/ A. u
$fields = array();
8 z3 t5 K6 r$ s- i1 w3 Y C foreach($data as $k=>$v) { , N0 E" V" Z3 q, v/ p
switch (substr($v, 0, 2)) {
1 Q/ s/ D2 U7 E; i2 d. O case '+=':
5 E6 u4 h: w9 l+ a $v = substr($v,2);
" E+ w, |+ I8 y7 }" j2 [" ?3 K" O if (is_numeric($v)) {
2 }$ c' @) g6 D& C, n $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); " ~. J R0 x) z1 G* W. c
} else { % s) G/ w6 U& j1 V# L; R
continue; 7 ^0 p" g6 n9 Y1 f8 u, a
} ' x+ O: a1 k# Z& c4 R
break;
9 r5 t8 X" n( S; [" b0 L! c case '-=':
( u* h. S1 L1 {0 t $v = substr($v,2); * F$ l, ^4 F3 E
if (is_numeric($v)) {
# o5 `2 j: u/ G' n/ Y5 Y1 ? $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 9 o8 m5 }2 @$ J3 K9 v4 L( _
} else { 3 z( v2 Y x1 M$ @% v; x( w
continue;
" A" o0 {8 G& I( ~. x8 M } p+ p0 g' _! X& o3 B
break; + U. S: Z [* c0 K
default: ( m( l) |% X: M5 Z4 p0 g
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
6 J- q1 `1 u2 l# k5 o$ r% @ } . v6 c! _8 P) d$ e6 _, [+ W" c% {# C4 T
}
7 |/ `" ^ {$ z7 _/ D $field = implode(',', $fields); $ n; q! |# ^1 x* b( d- Q$ {
} else { % A) O' ], O/ N, ^4 U' ^- b9 w
return false; ; I, T' H. M" n
}
- `6 g' w4 R2 s* I* W0 ?: S/ x $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 1 r0 g$ S0 Y7 k2 a" t
print_r($sql);
9 g8 s) e$ c1 S( Z8 H1 c return $this->execute($sql);
: F/ b0 K4 L5 K; P; F; q3 p) E }
* V9 s. [" o3 U7 o- |; s从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
2 _: x% r/ ?. c5 d( R' p
) l7 v' o% W4 [1 q攻击测试:
/ D. u- q; S4 ^5 C- z1 F/ e测试地址http://localhost
* R$ t9 R S E l/ { 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句+ E9 F9 D1 B7 a% u5 ]. |+ |. c
& L! I0 X. ?' j0 ?
3 ]2 Q9 x$ o6 {1 G5 R; m$ s8 r1 C- d+ A7 I5 W. ?6 P8 C
9 I! ]/ d% y# H5 G) f5 y- @$ Q( s- u, `) n$ Y% B- P
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|