|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
$ m4 p M: ?- j+ j" g" ?, F漏洞作者:skysheep
* Y( u4 _; E( B7 i s% I* `分析作者:Seay
0 [) t8 d/ E- v; q5 a6 d7 k4 X; r1 v博客:http://www.cnseay.com/. g/ @% }7 Q% i
漏洞分析:
/ n! i. \: W9 o 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。% V8 f+ T6 Z' }
, |0 X4 P# s2 v; {3 D* z/ ~0 a- R
% g) B) f v K2 I# D& Q3 C$ {0 }! V
" Y% ~6 A7 ~" u6 ]3 p1 q& [, ]4 Dpublic function account_manage_info() { % O2 \; B$ l1 c% e
if(isset($_POST['dosubmit'])) { 1 k* U4 `6 r3 `& ?; u% c( F% V
//更新用户昵称
3 }3 A4 k, e. [! h% h5 T% q- q $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; / P* t/ m* p9 H$ z. Z, C5 e
if($nickname) {
. E# M, C$ K9 z+ t $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
( ?) q7 U, o" M. L9 _ if(!isset($cookietime)) { 2 {8 _1 v0 E& \3 R
$get_cookietime = param::get_cookie('cookietime');
; Z! X( c2 C5 v! U- _* y }
9 i4 } d1 U M7 e: D. G+ P6 K $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); & I& E q1 p& `- [) z
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
6 L j$ m+ S! K4 { param::set_cookie('_nickname', $nickname, $cookietime); ' Z3 @# O& h6 d
}
0 S: j" K6 @( H- x: q/ u; O( m, J0 [ require_once CACHE_MODEL_PATH.'member_input.class.php'; 3 Z4 j+ P1 Q1 x% \1 H9 U, n8 B
require_once CACHE_MODEL_PATH.'member_update.class.php';
; S" W% S& Y; L) g, \. z/ k $member_input = new member_input($this->memberinfo['modelid']);
% a0 A; ]3 ^. H* ^ $modelinfo = $member_input->get($_POST['info']);
. f8 ~4 \0 S1 u; }# X3 J! ?& g $this->db->set_model($this->memberinfo['modelid']);
& B( V2 {! H- ]! C: v- x) C $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
5 a( x [& B4 t2 L5 F if(!empty($membermodelinfo)) { / l8 k' _2 P) T. A. N) k
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 9 Q% _( o9 y1 B$ v& A
} else {
) P3 g/ o' J' Y: ~ $modelinfo['userid'] = $this->memberinfo['userid'];
# F) G& M+ ]2 Z: P# m) V7 f $this->db->insert($modelinfo); & a5 r: |: y2 c' B5 c0 l2 y% c
} 1 C+ B9 g7 I( H
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,* M% o! |$ \( j& f7 c6 K
在\caches\caches_model\caches_data\ member_input.class.php 文件中:' B* w9 m0 A) `' m
# A# O: a! Z5 u$ E
: {, U2 x# a4 B$ A, X1 p/ e ( A! A( K5 [0 ]
function get($data) { 5 f7 F; x$ B, e+ ^1 T
$this->data = $data = trim_script($data); 2 v4 ~. f! \) o3 [
$model_cache = getcache('member_model', 'commons');
( L2 e! ?8 [7 g6 A1 \1 ?1 _7 R $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
2 j! l& M# @' [4 }) {2 s- t7 O {' c* ^ $info = array();
, `- ?2 k% w' V $debar_filed = array('catid','title','style','thumb','status','islink','description');
9 X5 |6 }* y4 {! u: C4 G5 ~ if(is_array($data)) { ( H2 s6 d$ L, n L+ x% s
foreach($data as $field=>$value) {
Z; s% Y r' {& G3 J0 j8 l8 l if($data['islink']==1 && !in_array($field,$debar_filed)) continue; $ H$ h2 C+ |0 r. N F3 C$ L" l
$name = $this->fields[$field]['name']; ' b% z7 y5 X" b; i+ J A3 N4 `& ]; P
$minlength = $this->fields[$field]['minlength'];
+ I2 d+ v$ U2 k9 g; O- _" h) a $maxlength = $this->fields[$field]['maxlength'];
/ j" D! T# K5 B4 H: |# f $pattern = $this->fields[$field]['pattern']; / D+ z8 P( `* A1 @
$errortips = $this->fields[$field]['errortips'];
' }, M8 e2 o0 l6 _) _" m if(empty($errortips)) $errortips = "$name 不符合要求!";
. i5 c' o" X4 @7 F n$ X3 D $length = empty($value) ? 0 : strlen($value); - w& ^5 Z& x. b* F0 G$ u
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
, _8 h9 @1 X8 R if($maxlength && $length > $maxlength && !$isimport) {
$ C) n$ ~2 [( G/ N2 E2 I showmessage("$name 不得超过 $maxlength 个字符!");
3 M) n8 R- `8 ]+ n, O( Q } else {
9 a( K- p( U* F0 w, L+ O str_cut($value, $maxlength);
' n" i3 V% u2 l }
- H, R W( a8 K- h if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 3 e) y1 X J/ A1 l0 E
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
8 Z# K( ~6 I' `, L# w! f, @ $func = $this->fields[$field]['formtype'];
8 w& m; c3 V% l% [1 z5 B/ S if(method_exists($this, $func)) $value = $this->$func($field, $value); ) }$ c( v( j+ ]5 h5 L3 `; w7 D
$info[$field] = $value; . N( x2 n7 a. r6 i/ p& E* z [$ D
} 5 `$ G3 ~. n7 ^
}
+ t6 B" D" a7 K+ E6 b# O return $info; ( |- T% R3 V& G3 R* F l0 X) l
} 9 U! C- W0 e9 U
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
) ?) C9 l& y- y% |7 p9 V) g1 P; N" A0 d9 P
再到phpcms\modules\member\index.php 文件account_manage_info函数
5 Q9 x2 O3 M8 G& i3 m过了get()函数之后。
% d, A" ]( z4 v! [2 f8 F* u0 S$ q( i6 ~' v$ F
' G4 [$ c. U4 F7 D
$modelinfo = $member_input->get($_POST['info']);
! R; L% M# m7 E; g6 J2 j $this->db->set_model($this->memberinfo['modelid']);
+ n+ f( f- `8 ? $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
% k( [% S( S3 z2 D5 }6 _' Q3 ^ if(!empty($membermodelinfo)) { ! L" [& ^* v5 Y. `
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); : i2 ]/ ?. T# U) j' h. A: Q1 m- I
} else { , t; g0 @* }$ m( k( K `/ q3 x
直接带入数据库,update函数我们跟进看看' U; E, \) \7 R
4 w" q0 M# Y" c6 O% _8 Q+ o , I: ]1 l! {$ @7 ^$ P+ U+ @
public function update($data, $table, $where = '') { 1 F* f9 H: a: G% K5 O' _
if($table == '' or $where == '') {
2 L$ b( Y6 u0 [( e- s$ T7 U8 @ return false;
: J0 S* h4 a# @' M3 O }
5 q, }! \& @: I, X $where = ' WHERE '.$where;
4 }4 T6 f+ f: {& }; R* Z4 e6 a- e" p $field = '';
7 g7 j7 R0 S1 d* k9 U if(is_string($data) && $data != '') {
& P$ V/ j- @+ _8 g3 V6 M( s" L $field = $data; 1 u( _4 g1 A1 @+ i w" _6 ]5 T3 [: w
} elseif (is_array($data) && count($data) > 0) {
( }/ I& h2 I0 o( ?+ ] $fields = array();
1 r3 H9 m* l; T foreach($data as $k=>$v) {
2 E! s/ m: I3 y" S& L2 p switch (substr($v, 0, 2)) { ' x( Y8 @5 k0 Y9 d) O" ]
case '+=': 4 W; K9 P2 y5 g- u2 O8 M& N
$v = substr($v,2); 6 Y* q( `3 y3 F. |; o
if (is_numeric($v)) {
8 ?/ c7 p, k% r6 R; i $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
2 f! ^6 _! x4 R0 p } else { # @7 ^: D f8 |8 v; `) ?
continue;
6 g, S: ?1 }' w4 {. Y }
% \: M4 d8 f8 K% l8 U/ W. T8 r break; 2 j. P% y+ u7 W2 B1 c# m& x! W
case '-=':
+ _. ^1 H/ A0 t1 O0 I% V& | $v = substr($v,2); 5 c, x$ T" p5 V/ B5 z# Y( @/ B
if (is_numeric($v)) {
3 _# E% w! h R$ D7 J6 f c2 b3 P $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
* i" r- d0 P1 _5 j# \7 _6 d } else {
8 v, j# u% Z- ]7 U continue;
9 d$ G+ m5 m9 g3 A% ]- z }
/ R% m$ ^( `( h/ U3 p" i" w+ X2 B break;
4 C2 I; Z9 X; B default:
" b# U8 w. {& |. J0 I& [' M% L( t5 @ $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
/ |3 X9 y# h, w. i- A# j# i } 3 q4 n! Y$ Q+ R% [8 E
} : P+ \2 |) Q/ u# q. J4 O& k& R
$field = implode(',', $fields); + Z; o3 m2 K- |, R% q8 \0 Y
} else { ! _9 H7 v% A/ \: n7 C( f
return false; $ [( E7 ^4 X0 p7 M9 n+ J
}
2 \8 J1 L& {) z7 q $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 9 N M! q9 G, c
print_r($sql); * W0 w6 W9 m! Q8 V0 A: U
return $this->execute($sql);
" B2 [/ U: y# q- p! q! d/ f3 p }
6 @' X" e9 a) i9 @- R从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。; V* W8 z# H0 b& t" h
2 T8 H6 g2 I" P' B1 ^攻击测试:
2 M+ J* Z1 c' q. v9 m测试地址http://localhost' V! {, k( p0 y! G3 I2 N
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句" a' f. A+ U |) r, a5 ^; p
+ D; a9 o) c9 a" n& {
8 s3 U: ]# }+ O! h
3 S. S6 }( i; [3 v7 ~: x! @! x9 [* O6 k
2 E/ x1 c) t6 [0 N+ l& x( k$ K1 W
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|