|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
# ~! p5 a ]# |. h+ F4 `( J* n漏洞作者:skysheep
! ` K6 a6 t2 s* `8 Z. n分析作者:Seay( `. J% d/ u+ K/ G
博客:http://www.cnseay.com/6 `8 x+ R1 t1 Q. e) M* G: s
漏洞分析:, A, r" z) {0 C4 C
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
3 E" k0 U9 p$ ^7 I* O5 J* X
; U" p6 s. I1 R" I& t R+ n" ?! i( j0 M3 F* o
. v$ W% y: \; l8 U
public function account_manage_info() { ) V/ {" C8 a$ M7 y9 |2 e+ L) S
if(isset($_POST['dosubmit'])) { ; s2 b: R r; x! z' {% ~
//更新用户昵称 / H. O& P) x, @
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; . z6 H7 R* h6 o8 v
if($nickname) {
: _, J: y+ k: {6 V; Y $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 6 z' ~0 M# j4 v; U( L2 R+ N
if(!isset($cookietime)) {
3 b+ `- l3 F& }( }" Z& z. M9 W d $get_cookietime = param::get_cookie('cookietime'); & J0 [/ L0 o( m* F: O1 n4 [% L
} % r: P; K1 J- _ P, t1 n
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); " f2 q& E+ Z" [: k
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
$ t$ _. D3 P7 q param::set_cookie('_nickname', $nickname, $cookietime);
! c0 T. P9 o$ h, {6 |, T }
) F5 \. M/ P! N require_once CACHE_MODEL_PATH.'member_input.class.php';
7 s+ V) q: s* ?( Z& P* r require_once CACHE_MODEL_PATH.'member_update.class.php';
( Z1 F6 L7 \, ]& W Y A5 [" z $member_input = new member_input($this->memberinfo['modelid']);
5 S, p# F Q+ i9 M/ d4 W8 T9 u $modelinfo = $member_input->get($_POST['info']);
1 J# c% r/ {( w $this->db->set_model($this->memberinfo['modelid']); 7 @0 q1 k/ v6 y6 Y9 h
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
, N4 _) `. t1 }- O: |7 M$ {! ? if(!empty($membermodelinfo)) { 3 C& l2 B3 Y4 f% I& B
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
6 _6 ~: O4 }# z( M3 B } else { # Y( e8 D }, A4 L0 J
$modelinfo['userid'] = $this->memberinfo['userid'];
6 D" z8 g5 F7 h' t $this->db->insert($modelinfo);
* {. m2 v( ]6 D# S6 p; F( X } & |0 N9 Y: C; j$ X" O) B
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
; @1 S- \9 d, a( W在\caches\caches_model\caches_data\ member_input.class.php 文件中:
, D7 s3 m% G7 s/ x
8 l, C$ D, E4 ?7 ~3 t f. J$ M& w( |1 D7 Z- Q
: j. c& v/ D0 X, z) jfunction get($data) { ' ^# ]. J: [/ Q: \! j
$this->data = $data = trim_script($data);
' r: _2 x6 f0 S e+ X/ Y1 U $model_cache = getcache('member_model', 'commons');
- _; e- K* v7 n+ b% ^/ m( @5 P$ o! e $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
8 m+ A0 R2 I9 A2 p+ j5 K $info = array(); # V7 H' f9 M b5 z1 [5 q
$debar_filed = array('catid','title','style','thumb','status','islink','description');
3 u- j) s- x. P0 X9 I% Y if(is_array($data)) {
$ q+ B: T4 B: N3 Z4 B foreach($data as $field=>$value) { * H$ [. {0 X1 z
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
. _% Y8 w( X) Y( r $name = $this->fields[$field]['name']; % Y6 @/ e0 K& O8 ^; s3 Y! `
$minlength = $this->fields[$field]['minlength'];
" ~" A: e$ ~- k $maxlength = $this->fields[$field]['maxlength']; ! I" Q; C) W( B: L; e( R1 v$ ]
$pattern = $this->fields[$field]['pattern']; # _" f" [7 ]4 P. s/ u6 q: a% a
$errortips = $this->fields[$field]['errortips']; & P( g. Q# k$ I7 W
if(empty($errortips)) $errortips = "$name 不符合要求!"; / z" p, Z# [, J' \# l
$length = empty($value) ? 0 : strlen($value);
7 K4 }- q6 I3 z# z% [4 @! q if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 4 l6 J, a" c0 X3 M1 [% X
if($maxlength && $length > $maxlength && !$isimport) { + c3 G# v$ Y8 t k6 i6 \$ ?/ D
showmessage("$name 不得超过 $maxlength 个字符!"); $ u/ C& W% {# J# g
} else { 4 B# E8 P. P4 o$ X
str_cut($value, $maxlength); ! f' Y8 j9 C! _
}
/ i8 G2 G( Z h6 E* b( o if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ! z1 g _+ G8 p$ @* Y% l
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 0 j% p# V! f! U4 Z7 F
$func = $this->fields[$field]['formtype']; + i% U3 ]$ r1 T- y4 S6 H
if(method_exists($this, $func)) $value = $this->$func($field, $value);
4 I7 K. d" U. i% {2 k4 L $info[$field] = $value; & Z% t$ q9 y, D% _
} , }) h" ^1 p* v4 _. k, ?' y
} 2 u' E* A1 p0 n9 c
return $info;
* H2 i/ K2 Z$ K } % Q6 Y% b0 [0 {) O" Z5 D
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
: q! j% B; r3 M2 k5 ~" p& M5 Z/ ]; x7 W' E
再到phpcms\modules\member\index.php 文件account_manage_info函数
! c; H! H g4 c/ [ B6 y1 Q过了get()函数之后。* G4 C/ x' r+ h2 T
4 V, d/ Z& W* B$ M; s) h1 H9 u 0 _$ l1 l3 d% B% ~4 W
$modelinfo = $member_input->get($_POST['info']); . l) I5 b% f s' b& c
$this->db->set_model($this->memberinfo['modelid']);
& }0 m7 p# g% l& O, |2 S. n $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); # e' I% `5 d2 j2 e- I
if(!empty($membermodelinfo)) {
3 y, ^( C5 q1 u& \& R $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 6 ?( K6 Q0 n& t+ D4 V
} else {
: X5 {5 _% k4 n5 ~直接带入数据库,update函数我们跟进看看7 ]& R/ q4 E7 j5 m
7 P+ x1 u! J. L' r8 [! H9 R
; b" F* W/ g5 u: a3 `' ^public function update($data, $table, $where = '') {
4 U* W1 E9 h- n7 u) N4 H if($table == '' or $where == '') {
2 y& K2 B5 [* N# ~6 D return false;
5 X0 y7 l0 C1 S2 m5 b, K } ( i0 I" b* H& k6 I0 g) A
$where = ' WHERE '.$where;
, V9 z4 n- ]3 f. A8 ]) J* | $field = '';
7 t. p& r( r1 m u9 Y5 n, s# j if(is_string($data) && $data != '') {
) j! g3 ?- e ` $field = $data;
/ s3 T+ O9 ~( G6 i8 |! a1 N& F } elseif (is_array($data) && count($data) > 0) {
- ?' Z3 q& F/ w, }1 r $fields = array(); 0 \+ d# D" u& A2 H }# A9 m$ ]& y
foreach($data as $k=>$v) { 2 R; F: H6 m: h4 N1 h2 p' v/ l) o
switch (substr($v, 0, 2)) { 8 c& ~4 | X: E5 U' H
case '+=': 3 s' \4 {* H2 X0 L# @; ^" j; j- N
$v = substr($v,2);
" K* r Q: j% F8 v) b if (is_numeric($v)) { . M0 X8 O) O& }& G+ }4 Z. Y$ q" _
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
8 D; M6 {# \( f' h } else {
# e0 p& O, x- V1 P0 u6 h continue;
9 e; R& U' |) m" N& M7 [# n) e" S } ) c5 r6 Y9 K' ^: F
break; ( Z' r! Q" H$ [9 x2 E$ @; w
case '-=': # {7 p" y- G B2 r1 b* A
$v = substr($v,2);
) W5 E, U0 c8 ?! c: ~ if (is_numeric($v)) {
& U/ v& c% `# q# | $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
5 A/ Y, Y u* t4 d& x7 @+ H } else { " r# i: ]- D7 F- A9 q! ~
continue;
# `/ S/ x3 z) z- b- u6 g9 J( F* l }
# d3 g6 Z6 L6 G- O break; ( k+ A; s" z6 k' P( U! U$ F5 ?
default: " b# s5 ~+ v* {
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); $ a* D% t/ T4 W4 P7 m
}
7 _2 P$ a$ S$ q' `9 x5 `) l, ?8 i, \: n } % I' d$ ], I p- G# {, }
$field = implode(',', $fields);
9 S" {# K1 Y( j* I3 S* j } else {
+ `! Z4 k6 z! g, ~1 Q return false;
' r' L* ~- v5 ?9 H0 H1 | } $ D$ F4 y2 P6 L& {
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
3 {: \# I# H* _ print_r($sql);
& T0 H7 Y( j1 X return $this->execute($sql); $ s8 o1 K) P) P3 Q
}
" I- D# W$ G& r8 y9 J, b从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。: L ~% `' {$ w4 \
0 L2 v* H& o+ O2 t: @攻击测试:
. b& }9 d! y3 h测试地址http://localhost/ V* ]9 n9 }9 E) s$ W+ h6 [8 O
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句* n) K& p$ _- R& Y( \! d( T
. D: `8 ] s4 T$ l& B% F' F- s! X
# h$ q. n+ i! O# S1 N" l$ ^
( N! R) ^- C! z7 M9 p
) M. b! R) G+ C$ `7 z! z) @- E/ p. C b' ?1 {- f6 k
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|