|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
6 z z1 j/ ^. ^! V& {9 }. g7 H漏洞作者:skysheep$ C8 n# Q M. g8 y1 d
分析作者:Seay( a# W. x. e r V2 X& K1 V( C# K
博客:http://www.cnseay.com/# [& Y8 {) g3 C9 W4 Y$ S& M8 y, V
漏洞分析:4 U0 H2 Z" {3 i
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。- \' ]& D& N) Q% ?/ w) t
$ ]% k* x# K) P9 K8 i) g# V3 ~
; D( E9 r, H7 Q8 P( }' S/ a9 m) Y& x ( Y- p+ s+ x& e, E F# |
public function account_manage_info() {
$ R% k4 S; D1 U) ` if(isset($_POST['dosubmit'])) {
5 ] c3 T& T" L9 I5 k6 _ w //更新用户昵称
0 ~5 h# k X( t" \2 e $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; : U8 Z/ O- b* q
if($nickname) {
2 c# O, D3 V3 h% l$ N $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
2 r/ K9 F) R6 _2 U5 z. G. N if(!isset($cookietime)) { , Q8 ^5 i3 U3 D! p, V
$get_cookietime = param::get_cookie('cookietime'); + a6 a) y3 V8 s' K3 K: {2 H
}
& d) F) S C0 _7 @& T $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); / v/ i# h. C! V
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
0 H- r [0 |* g' ?1 d o param::set_cookie('_nickname', $nickname, $cookietime);
' f1 ~7 q) {* z( ]$ O2 U }
( f4 o% U7 s. I5 F: i require_once CACHE_MODEL_PATH.'member_input.class.php';
) \- M; T3 p0 w+ G require_once CACHE_MODEL_PATH.'member_update.class.php'; - @5 P* i5 k W( M f: z# I5 }
$member_input = new member_input($this->memberinfo['modelid']); # k8 g% T1 O7 B9 }1 S0 ]( j
$modelinfo = $member_input->get($_POST['info']); 1 h6 m" z. X, _9 m: f
$this->db->set_model($this->memberinfo['modelid']);
2 H1 {# Q6 y% w $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 5 ?( k1 G" R( u) G( v* ]8 k
if(!empty($membermodelinfo)) {
# |7 H5 \1 e; K' o, q0 C $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
) E% f1 D7 |3 z/ i; E } else { . K) k: Q& p4 M: V0 Z! U
$modelinfo['userid'] = $this->memberinfo['userid']; # O$ _) \$ L+ Y
$this->db->insert($modelinfo);
0 p+ T% n' R+ x& S$ T$ @ } - L0 m f- I8 E1 x
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
& @' v+ U, u& X2 Z在\caches\caches_model\caches_data\ member_input.class.php 文件中:
6 ?6 A1 H( x" d+ y- \2 n: N8 r: q) z( ^3 [# P- o( |
6 O% ^& Y: Y" J
" k5 @& Q" Q) ]8 [% Y, V* ofunction get($data) {
) x* _' F9 X5 y: X2 ` $this->data = $data = trim_script($data);
9 z6 `) A* E6 X A% X6 n $model_cache = getcache('member_model', 'commons');
/ ~3 v0 z+ }8 s; j) n8 w $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; + N+ |* _6 ~5 F8 z
$info = array();
4 R6 r$ Q- x) p* L3 G $debar_filed = array('catid','title','style','thumb','status','islink','description'); - D2 P& M* S/ [- {, M$ G5 b/ o
if(is_array($data)) { 4 k/ R% w' }. O( p5 s
foreach($data as $field=>$value) {
3 U) D; x' W8 c% t if($data['islink']==1 && !in_array($field,$debar_filed)) continue; * y/ X' V* h* N# s& I, L
$name = $this->fields[$field]['name']; 7 P. @0 l6 o# D
$minlength = $this->fields[$field]['minlength']; ! t5 |: ]" |) `! l) V
$maxlength = $this->fields[$field]['maxlength']; % {" L5 y! V+ q o% a! J. C, a
$pattern = $this->fields[$field]['pattern'];
! |% N, s y5 q. R& L $errortips = $this->fields[$field]['errortips'];
+ o4 N; o$ l2 i, I: s if(empty($errortips)) $errortips = "$name 不符合要求!"; ' X4 D, W2 e6 j! k0 W. K6 E
$length = empty($value) ? 0 : strlen($value); 4 S* m/ G/ Q1 b5 x5 R
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 3 `) h. M* l1 ^8 O& T. g) \
if($maxlength && $length > $maxlength && !$isimport) {
: E- R4 w1 `* z9 Q6 c showmessage("$name 不得超过 $maxlength 个字符!");
7 _. ?1 H1 t' p/ f6 A } else { 1 _0 s) ^: ` C1 O7 { o; d( ?
str_cut($value, $maxlength); 6 s2 C# c, h' Y- i! w
}
% Y- L d# b9 `9 T" k# F X if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
" W( x& \3 w3 y! u: I if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); . i/ U* P% U2 d" T( H
$func = $this->fields[$field]['formtype'];
. t, E$ Z/ s+ X! V1 P9 c if(method_exists($this, $func)) $value = $this->$func($field, $value);
$ Y1 u- r2 J4 H* g6 ]. _4 e9 C $info[$field] = $value; & z7 U2 A) M/ n: U
}
% t# }6 O6 t$ W$ U }
9 R9 _( {4 @- S% O$ ~1 K6 E9 } return $info; 7 M8 L, R' u, z, \( O
} 0 ?2 T: N, W( K
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
' N$ x, X7 ~6 A! S" ?
4 @% m/ o& R+ c0 L0 _% s8 Z再到phpcms\modules\member\index.php 文件account_manage_info函数
) @' ^/ k6 k3 E. v过了get()函数之后。" |) _+ x6 y6 E
9 c% K3 j3 W4 H; g
+ S1 t3 F" J- E$modelinfo = $member_input->get($_POST['info']);
; c' v, O( g3 R! \7 c $this->db->set_model($this->memberinfo['modelid']); : G5 S( `9 w& S) W- H0 V N5 Q
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
; y, p6 _; i- q R q if(!empty($membermodelinfo)) {
+ @9 p3 J' R O% b: p$ i) _ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
. |! f7 Y7 Q' k7 @ S- X" c- Q1 ^. G } else {
/ C, H0 M! J8 M1 C: j( o' S8 i直接带入数据库,update函数我们跟进看看" b5 d! l& Y! L/ S9 [* P
6 Z7 q L0 H% L6 C v! E; P" R1 `
8 U" X: u, ^4 s: l/ w/ n: d, e `public function update($data, $table, $where = '') {
& s' ~8 X! W0 i3 Y) C, \* @ if($table == '' or $where == '') { R+ K5 @) V) a/ }
return false; * Q) b/ b2 R0 I, w
}
5 P+ w" u4 j( p$ T8 \5 J& e. ? $where = ' WHERE '.$where; . d6 s* a" Z3 t$ c
$field = ''; * I) ]; s1 T' }% y4 m# _! x+ v
if(is_string($data) && $data != '') {
& a( T' k* h. x4 p $field = $data;
5 j( A7 P0 J7 a3 y0 n) R2 o } elseif (is_array($data) && count($data) > 0) {
% A& ^! ]6 Y/ h, X $fields = array();
* x! U2 K. \4 B9 C, G7 H1 r foreach($data as $k=>$v) {
2 M- ]2 m: s! ], Y switch (substr($v, 0, 2)) { 2 d' e! b" D+ `
case '+=':
4 i3 G, R1 B f4 j$ ?# s $v = substr($v,2);
7 Q; d @3 B. v; y: n* ] if (is_numeric($v)) {
7 C( c( p( g6 ^ c9 w3 ^) A5 Y" q, S $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); ; F3 P" W+ G" w9 C4 B! T
} else {
# V+ p3 ?' S1 ?, X9 j! [ continue; 1 x7 |2 t! R7 ^4 R" S
} 1 {) u" a. ?( z2 J4 X+ Z5 ?! C! \
break; / m$ B& ?) Y) H9 Y( P: D: Z
case '-=':
. q/ p8 N% v* Y) ]* E$ f1 G+ \0 i $v = substr($v,2); % @$ ?2 _* h6 N; ~, i. g& o
if (is_numeric($v)) { 4 `" @# E2 g! V( g5 F
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
7 v; m+ w5 t- M- P# F& T } else { - Z+ N: O: U I
continue; # V. G) b; B6 e- |! Y) Y( l
}
/ g! l+ Z* I6 F& V0 ~* y& p break; + w$ E! {- Z6 B- R& H V6 D
default: # @) i% p a+ p2 J! U) x' C3 k
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); ! w0 l2 f: v* _+ n0 {) {5 t, r
}
# E w( w' n+ E' C8 [ } 4 e) ] m$ c" A6 E
$field = implode(',', $fields);
+ U6 }+ q) b; J% D3 p6 w } else {
. ]& F: d' z" c' f: k& O' b return false;
" d+ q. P- ~6 W( u2 h }
' [! ~* T! B" A2 h $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
5 L" i4 W' z; Q d) T& F, ~ print_r($sql);
6 G8 y& l4 k6 C9 L return $this->execute($sql); ; l3 T* G; ]4 T2 @
} 7 a% `3 \, h6 F5 y2 D2 k' [8 i
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。" b; y: |# c! |' W5 g
" ^ D5 S8 E- @) n7 A2 ^( d
攻击测试:
6 } g4 `& B5 l测试地址http://localhost
7 s8 ]. b3 S) U9 g1 ^. z 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句- r7 y. s. _1 t! W% ?+ m8 p
* J, A7 {" m* q
! U" m; O3 z. o6 C8 K; e7 G
4 r0 a/ r# d" q9 f" e
% v# {- i) Y3 T+ v7 f7 k8 B3 C4 Z( K' u+ j" Q2 x& M4 r: D7 X& S9 q
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|