|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
e! b" m6 g) T9 Z/ U漏洞作者:skysheep
3 p9 |( M4 T; _8 i9 x* V4 O9 h分析作者:Seay2 d3 _3 t& b$ ?$ R% ?- U" _
博客:http://www.cnseay.com/* `/ N, p8 N0 n( U2 `' P
漏洞分析:; w' A7 }3 V0 j) X5 _
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
* L" T. D& p: c% F( R) e
* b" I# j% j/ D5 c: z8 u9 R& D' f& ~3 s) }+ l/ Y2 Q
' N; g- T$ g+ G
public function account_manage_info() { y& h4 m% w. e( O- f( Z, x! M, p% S
if(isset($_POST['dosubmit'])) { 5 a) Q5 r2 D6 J0 K: f8 ]
//更新用户昵称 5 _/ B- m. l7 x9 \
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
4 n2 P# ^1 \. ?: X4 b, n" J if($nickname) {
% o1 P* h$ k5 A3 O9 [- l $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); . L1 M4 Q, S- L+ H
if(!isset($cookietime)) { & v; ^% [& D4 `0 M a: v
$get_cookietime = param::get_cookie('cookietime'); 0 U b9 q4 ]- f8 }0 i' Y
}
- g! M" x8 e$ ~7 H0 C% U) [" t $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); & e7 a2 x5 j& h! y4 u+ b; z% K
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 4 g, W* d3 G: D8 d' e
param::set_cookie('_nickname', $nickname, $cookietime);
* U' {- m9 ~! H6 w$ S3 g4 ^& R" `+ { } ; u5 u2 @' @+ [2 d- K; c1 ?
require_once CACHE_MODEL_PATH.'member_input.class.php';
) k+ `* ~' @) J. k. T" ~0 d require_once CACHE_MODEL_PATH.'member_update.class.php'; , d$ B3 n2 l0 ~" [) C
$member_input = new member_input($this->memberinfo['modelid']);
4 G, C( V' H3 O- U- ?- e $modelinfo = $member_input->get($_POST['info']); 1 V2 U6 g1 W% K: p) ^
$this->db->set_model($this->memberinfo['modelid']); 8 a; {* q$ A8 ]. d7 s6 M
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ) O' k1 k6 K( ^+ u" q, o7 w5 w9 H F
if(!empty($membermodelinfo)) {
! u* |1 c$ H) H: s5 V1 ~+ ` $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
& a N e' P" C( L } else { . ?- j. U4 a7 d! I0 d( L9 m, U
$modelinfo['userid'] = $this->memberinfo['userid'];
$ i5 L$ O. u: W' j8 K $this->db->insert($modelinfo); 9 b# t4 D. ]$ g$ [
} * T: }5 n0 I! Q* ~* \1 [3 W6 f% w
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,- N p, _; D) r. G% |6 p
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
, L' f- k6 m/ K- o( ?" Q0 Y4 W+ X- d& [
! D4 }2 P; g4 M0 w0 s/ K. L' \! a
3 J/ v/ d% Z% C! kfunction get($data) { 0 a$ W* y$ m0 K5 U, A
$this->data = $data = trim_script($data);
5 X* z5 S) U) K+ U $model_cache = getcache('member_model', 'commons');
2 C' q/ j3 m2 G5 r1 } $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; ( ?# \$ z7 w9 O5 f+ D
$info = array();
; ^ y# U* \( H4 O) [0 R% S $debar_filed = array('catid','title','style','thumb','status','islink','description');
) d: g2 h6 }0 l4 b) Y if(is_array($data)) { / O5 O0 p( r0 J
foreach($data as $field=>$value) { & @& l s: f. U7 [+ h9 T
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
, A7 V/ X3 m* o1 l- j; ~0 M $name = $this->fields[$field]['name'];
% B- e/ |9 v: J $minlength = $this->fields[$field]['minlength'];
/ i5 }: f( ]$ t& L0 M" q $maxlength = $this->fields[$field]['maxlength']; $ n+ H3 ^: l* g1 i
$pattern = $this->fields[$field]['pattern'];
+ Z& e8 q$ \$ ?( A9 }$ P $errortips = $this->fields[$field]['errortips']; 5 ~+ \; r6 Y& \- w) \$ O- b
if(empty($errortips)) $errortips = "$name 不符合要求!";
" }- W9 m. j! p# u+ P* x $length = empty($value) ? 0 : strlen($value);
; U0 y D( a" ~' V4 [8 ~; n5 E8 b if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
2 J, R$ @% G9 q9 q+ o) Z) Y if($maxlength && $length > $maxlength && !$isimport) { 7 |- o) ^- k+ G1 ]- x6 w
showmessage("$name 不得超过 $maxlength 个字符!"); 4 W: L2 s" t) n6 l1 R3 H1 S
} else { * J9 }4 h' i9 n7 _# W
str_cut($value, $maxlength); 3 b. R( _6 U- k0 X" ?3 z
} 6 w$ L2 U$ Q- Q% L' p
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
. R# [. Z I$ n2 e if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
5 ?- G6 E- m, E $func = $this->fields[$field]['formtype'];
! t2 I5 d- s3 L) P# i if(method_exists($this, $func)) $value = $this->$func($field, $value); 3 T% T% \8 H8 R. }- ~7 z
$info[$field] = $value;
* r* P4 J! t( R- o, q5 V } + N% {9 B7 F% Z1 ^ |
}
* \# Z/ i7 G* N return $info; / W7 w- v- Z4 `" X; h
}
& l, p! r( A& D$ P1 Ktrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,& Z3 M" i- w; ~* p. ` \' ^6 \/ N6 a
3 y; n" z. a( Z. a/ Y h. U再到phpcms\modules\member\index.php 文件account_manage_info函数
# x+ C8 A t. z过了get()函数之后。
1 [ H8 B* k" j3 G' y) @* n8 F! p1 n' x/ S# s2 a
3 J; D. v" m' ^4 N) D" n k9 i$modelinfo = $member_input->get($_POST['info']); $ ]% O1 i m+ t3 n1 Z0 C1 l
$this->db->set_model($this->memberinfo['modelid']);
; m$ i ~! f3 F% F- E4 x) c $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 1 u, n& b8 J+ M
if(!empty($membermodelinfo)) {
+ ~- B% I+ x% Q4 u! }- U $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
! m! b/ l& L: _& ^ ^ } else {
+ @0 `* N% S$ g6 k( m( A) u直接带入数据库,update函数我们跟进看看( n8 G# T2 U8 ] n* R: I
4 W X" }+ C) Y! ]$ G* C2 W! B- c
# i) ?, `: k, F6 e# {public function update($data, $table, $where = '') { # U5 |, s' j l, L! |6 P
if($table == '' or $where == '') { 0 X- A% O& }% _6 D1 d! J
return false; , @ Q: R- s1 B2 f) {6 Z
} ! ]- z8 {3 L' D3 f
$where = ' WHERE '.$where; * g' G1 [& q4 A( u0 J7 ]
$field = ''; ( `% q: n! Q |+ W6 a
if(is_string($data) && $data != '') { 3 Y. {# m6 t8 B% a2 S
$field = $data;
: Z4 P2 n* i- n& M } elseif (is_array($data) && count($data) > 0) {
, n2 W" N- S8 x- l( \ $fields = array(); & ?# K$ Q3 T, N X0 J$ D; w: |. ?
foreach($data as $k=>$v) {
]* Q B$ K) X! _$ s4 h' L$ L switch (substr($v, 0, 2)) { . j3 t% M) P& {. x$ I) ~
case '+=': $ R( P* u0 d9 K( O! f* D* }
$v = substr($v,2);
6 B! A& n8 e$ n8 t! w$ i# F3 h/ q if (is_numeric($v)) {
2 e; o1 v' R6 x3 j M; c6 X- d: w $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); ) V; a( |& F0 g
} else { % G+ p9 `- P% @& Z% K" R, ?
continue;
2 v4 a" }8 }$ y } 3 s% P) J' j# O8 U9 X( d
break;
2 L1 b- t9 K: a! e. ~) r case '-=': ; S8 F* H" Q) I: S/ [+ g- G t
$v = substr($v,2);
) _% T# H+ b' W' {+ w if (is_numeric($v)) { . D7 ]. x5 b) _- a2 H/ v6 X
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 4 {% s+ n5 z& n( r' _& s4 ^9 \ l
} else { + C, U6 s2 H$ U7 K
continue; + X; A' R7 a( E8 I+ n
} ) @3 }3 O f a/ D! B( c
break; ) `( x2 b! v$ O" w B3 J
default:
6 a! q* x# h% m; v $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
# w0 S$ W5 B7 Z& `5 Z/ |9 w } ( v/ H2 S1 a+ M8 n
} " v6 T4 D' X3 [* m' X) z
$field = implode(',', $fields); $ x) ~0 ?6 P4 H) x/ L! C$ {
} else {
1 v, g5 P, D. P, y* b return false; $ J2 ~. C# B* |2 k
}
) g M: r. d7 Z $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
+ z2 U% |# F4 x4 g' G- T% M! _4 y print_r($sql); : _% y9 u# E" ]" N
return $this->execute($sql);
& D9 J+ B, E, |$ x( P: f% [6 d } ( m9 L+ {) I6 Q% W% l; X. R
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
. ?* R: G* u& L# Q# Y
) R2 ~; L3 O9 }% \- m; A攻击测试:( }+ k/ I2 i. m( M4 }& \
测试地址http://localhost4 ]( N& K w7 _/ A
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句' F+ Y2 x' W# k
, p0 s5 T0 i. C! F
: m5 w# w" e/ g5 `, D' D5 _1 k* t5 @
$ z* K% d& N9 f
1 ?# n9 |% E% L( {6 m2 I; f |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|