|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
9 s9 L' Q# ?, \: i- [- y; O' a$ h# Z2 O漏洞作者:skysheep
( f+ G5 p: _. L- P- L分析作者:Seay5 ~' b6 C [9 R8 C7 S" j; C" v
博客:http://www.cnseay.com/
/ c0 M: a) t) l' B漏洞分析:
1 X- k4 }' b+ u9 F5 F: u 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。5 L! t9 c% s7 I7 X5 @ k
5 G+ \6 r- p1 ~" e
8 t! h P, s6 t
3 T7 S8 u7 e( S$ w. n5 bpublic function account_manage_info() {
5 Z2 D1 a" m2 J& Z if(isset($_POST['dosubmit'])) {
* b' d3 p* }& n3 D //更新用户昵称
3 P) z; \: c3 N: M: W& _: U $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; W# R6 I: ^- ~% v( H6 O2 ]7 g
if($nickname) { 8 f& b* V2 p( Y' _
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ( x6 m6 h2 ]1 m. q7 t: c% `3 w+ }. i
if(!isset($cookietime)) {
5 [' H. z) D4 ^ $get_cookietime = param::get_cookie('cookietime'); ' l; \" q0 C9 ~. J6 f; i% `# c
}
: P i6 f3 a" S9 B $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); ) b3 y) I8 r3 k4 S1 d& [
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
1 q% x" y O' }; p2 C+ U, u param::set_cookie('_nickname', $nickname, $cookietime);
# L6 P( \' G9 T: q2 ]: j0 v }
' x5 _# m" ]; e; b1 K& L" H# d require_once CACHE_MODEL_PATH.'member_input.class.php';
3 z, w# ?/ y) m1 K, _% | require_once CACHE_MODEL_PATH.'member_update.class.php';
5 y6 l; v' G% W- o8 V: L" ~ $member_input = new member_input($this->memberinfo['modelid']); 7 v$ D" ~+ M( o/ D' s8 S8 U- Q
$modelinfo = $member_input->get($_POST['info']); - L5 Y" D: N: k. U8 W e; a. y! _
$this->db->set_model($this->memberinfo['modelid']); - D c" }" F) G6 p& V
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
# B5 m/ c* }( G( O$ S0 ^2 v if(!empty($membermodelinfo)) {
# ] q+ T. M& D $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
# H Y( [9 ?: I) Z# V } else { ) V- B) ~& l; L6 q+ [. o$ p6 D
$modelinfo['userid'] = $this->memberinfo['userid'];
' E3 W- \8 U' S, ^$ n $this->db->insert($modelinfo);
, d/ K5 ^' C% [; T: q' h }
; N) r2 h* ?. c/ z! B( j代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
* C8 u% R0 z0 Q$ h( s: K) h在\caches\caches_model\caches_data\ member_input.class.php 文件中: X3 }- ~3 Z1 f- t! i1 X( i
) x- l$ y! N6 A3 U$ E2 A2 {& w
' b- |' e% A6 I) V 6 x3 ?0 |( X' X
function get($data) { * r+ f' x( O# p, P+ A
$this->data = $data = trim_script($data); : C# w3 {8 O+ ]; \% B Z
$model_cache = getcache('member_model', 'commons'); 2 f9 r. |8 y5 c: y# [2 X
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; ) q3 h4 t4 ~+ \1 f: N% e* ]2 J/ P
$info = array(); 7 o" l4 N7 T4 e5 M+ n9 d
$debar_filed = array('catid','title','style','thumb','status','islink','description'); % o1 U% O' ?6 Q+ O; ]2 \& F
if(is_array($data)) { 7 Z- Z" b0 i, }& Q) L
foreach($data as $field=>$value) {
8 x# ?) T( }0 t8 K! Z: l% I- _0 |- c if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
! y3 X) ?6 E( G' A& E $name = $this->fields[$field]['name']; 5 ~1 C" ?* x) O' [2 N! B! ^
$minlength = $this->fields[$field]['minlength']; % B- Q& c8 G6 F; W7 |
$maxlength = $this->fields[$field]['maxlength'];
, o7 i9 M$ [; X) U2 Z $pattern = $this->fields[$field]['pattern'];
$ F; S1 `9 [. B9 `! @1 R& G, l $errortips = $this->fields[$field]['errortips'];
9 g, E6 y- {7 w" n9 q: c. _2 s if(empty($errortips)) $errortips = "$name 不符合要求!"; 1 k H- v# V6 r) [
$length = empty($value) ? 0 : strlen($value);
0 A4 n" w, V, U9 H( ^ if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 8 `. N( H5 V# L- j/ c
if($maxlength && $length > $maxlength && !$isimport) {
1 ]' a6 p# D- |8 [ showmessage("$name 不得超过 $maxlength 个字符!");
+ ?5 {7 N0 @. L. X) C, n$ B k/ _ } else {
8 I X6 n7 M- i! j$ O8 M+ j' y6 Q# O str_cut($value, $maxlength); : W& s+ o" M# b6 L7 @; S
}
# n9 p# O: i j if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
5 ^+ u# z' b0 h: n$ t if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
" {, F. r x' g# g: f $func = $this->fields[$field]['formtype'];
: }% F6 I+ ? M6 ~: ^ if(method_exists($this, $func)) $value = $this->$func($field, $value); 7 O) @3 G- H$ u
$info[$field] = $value; ! u' ?$ L) L: V8 f6 ^# f
}
0 F% ]6 o9 `( L8 ~3 r: W* {( l E } 0 G E5 A5 C4 T9 M
return $info; & }" t& {9 G9 Q) a& X4 o$ v
}
, K! V" U6 @( D9 @* G/ B: }trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
& ^7 C6 v4 ]: h$ U$ I0 A& h2 p6 S8 a
再到phpcms\modules\member\index.php 文件account_manage_info函数 s! f' _+ c/ y* i* I; [1 q$ v
过了get()函数之后。
4 ~" o/ @* J- t) v- m- B
; c' w3 n" G- d, t ! H" g# S! T- E# g) Q) Y- D6 n
$modelinfo = $member_input->get($_POST['info']);
' M( I$ S3 ~, v F) G $this->db->set_model($this->memberinfo['modelid']); - z/ V! r. ~. V( s9 r
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ! |, b5 J5 [4 |' M8 b: a
if(!empty($membermodelinfo)) {
4 ] y6 K- L X7 q7 k$ D7 ] $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
- @, ^! w$ L) m3 B2 W* `; d' O } else {
$ v# t5 l/ w* i( K! p$ e5 |$ v直接带入数据库,update函数我们跟进看看
. w& \7 a/ R: T8 A3 r* c, h) [' q) t& t0 H. @: M
2 R4 C1 Y- B+ E; S' v/ Y; @
public function update($data, $table, $where = '') { 1 z4 H, _5 ^) a- n" Q+ j$ m
if($table == '' or $where == '') { 5 k& J- L5 J, R: l% W
return false; " r" t5 z3 z1 ]* x0 }7 J
} 4 z/ b6 W( Z6 a4 z. R2 r! i
$where = ' WHERE '.$where; ; ]/ { d2 j3 A, ]. N8 ]; U
$field = '';
# g3 E+ x/ U1 C+ y if(is_string($data) && $data != '') { 7 k X% x( W0 X4 W
$field = $data;
+ u- J5 ]6 `! D/ k } elseif (is_array($data) && count($data) > 0) { 3 o: r# |2 N. J; m" ~* R, S' k- e& W
$fields = array(); ; k5 S9 r" D# S! v3 e
foreach($data as $k=>$v) {
& Y" m. z/ u1 p, L% o& P switch (substr($v, 0, 2)) { : W6 j+ g8 s# J' ~; @
case '+=':
* n8 N: }3 D3 M9 m' ?/ _ $v = substr($v,2);
* j. u' V0 L8 a2 o' n1 {6 W6 \6 q if (is_numeric($v)) {
5 {3 k- ]# r0 Y" |4 r $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); J* N/ `; t1 v; G8 `9 f
} else {
* u+ A& E+ I5 ?# p W+ e. ` continue;
4 G( p; [9 y( {1 j( _/ ? }
, r6 q1 L; }1 W) ^7 o- o k4 X$ g break;
, E1 E3 T3 a _ case '-=': 0 M8 u" ~ F# s* K9 W
$v = substr($v,2);
, F4 I4 Z: X" l if (is_numeric($v)) {
. l8 ?8 I0 N3 j$ d4 L2 S( X $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
, `/ s$ q& Y# N! q9 v7 x } else { 0 F3 ?4 \7 A* u6 s5 V
continue;
6 A" ^& F! m: s3 Z8 d( S/ i# b5 p } 2 n' J7 ?. t" u& x, W
break; 2 o$ X) y2 F- I0 _% z
default: + l5 g/ i' |* {: l5 {
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 2 Z) W0 |' M& A S# R* ^
} , ?- @( p5 Q4 R4 S
}
2 b* S2 H+ o* P( Q, ]) Q7 d $field = implode(',', $fields);
X- z" U' C$ J# n } else {
8 ^' |2 ?3 O7 p4 f return false; # I* `( N6 q! X
}
4 G/ k0 A; b @ $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
& D3 Z2 |0 C, O, ]) [( ]6 G print_r($sql);
2 E* a; G, G1 J/ x/ @ return $this->execute($sql); 7 n6 @6 s; A/ ]
} 6 d0 v' K; E+ B
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
& q7 J0 u9 E$ Q5 I( f) e2 P
4 N: n( K- }* E+ X, b) R9 z攻击测试:7 _7 g. I2 T# a7 S$ H z& T4 x7 O3 }
测试地址http://localhost! Q( f2 |6 Y$ v8 A* k7 Z6 j
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句, g/ A& B* J/ \& F: D3 A' q& V* [
0 \2 H- C& y4 z: @2 B* A* Z0 r; @
8 _9 E* G- n" [" ^& |1 q8 L1 z8 @" U
! P" T& j$ o6 o, W% \
: S l. f3 H5 Z8 v' ~$ | |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|