|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告# {5 O( d; Z; m* u U$ [
漏洞作者:skysheep
, u, b5 Q$ O* p' F, i& `分析作者:Seay9 G% ^0 O: l# V; s. p
博客:http://www.cnseay.com/
1 ~7 Z _9 J e5 ~6 K3 K& L漏洞分析:
# }) e( y0 J3 L) u _- h 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
4 V. R& a+ D: o9 Y' {
$ e, I1 a/ B4 Y7 s' L4 [; \ Q- j5 {0 M- b: j( V
2 g U/ E; Z6 Mpublic function account_manage_info() { C$ U1 E& g: m3 y$ t& C
if(isset($_POST['dosubmit'])) { 5 L6 j+ G3 L! Q- g3 I! g
//更新用户昵称
6 L1 J. P. Z# r8 [7 c9 A; ~ $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; , r3 L3 g0 J' A+ Y5 M# e
if($nickname) {
9 {* z; W+ o4 k; `% B $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
+ |5 O: o& k5 }, I9 t if(!isset($cookietime)) {
8 j7 ]* v/ G% Z( N# r6 h5 S7 ?) \ $get_cookietime = param::get_cookie('cookietime');
/ F9 t, ^, Q3 @) [% W3 q8 A4 U } ) j$ _) G8 @# Y, R" W
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
. ^1 I) x" e* D" X $cookietime = $_cookietime ? TIME + $_cookietime : 0; # }& e* T/ }/ X0 C
param::set_cookie('_nickname', $nickname, $cookietime);
/ G0 N- |9 c3 I) ~* { }
3 Z; |0 V' c9 l& z3 ~& O require_once CACHE_MODEL_PATH.'member_input.class.php'; $ h# `$ L# j5 K& F3 i6 l( R) w
require_once CACHE_MODEL_PATH.'member_update.class.php'; & p" f( v% h S% y# I1 q! g2 ?. H
$member_input = new member_input($this->memberinfo['modelid']); 1 [. U& V- N8 t5 L% _
$modelinfo = $member_input->get($_POST['info']);
% C; x2 B1 l$ { $this->db->set_model($this->memberinfo['modelid']); 0 R# o$ i r" d5 [9 |, h
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); % ?* L4 D7 L( i% N0 I
if(!empty($membermodelinfo)) { / |! @3 j3 e) R$ t5 g
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 2 U6 ~% L; P3 G/ i7 {: O
} else { * i5 E' t* r' j a0 X8 G! Q
$modelinfo['userid'] = $this->memberinfo['userid']; 7 {6 c! Q$ a) d8 V" i
$this->db->insert($modelinfo); $ W, i ?/ k( R8 n
} / S# X* D/ b0 ~* }% \
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
: x$ b8 f( C0 J, W; }. P- x/ V在\caches\caches_model\caches_data\ member_input.class.php 文件中:. o/ q* e& X$ f* X9 W, d
7 p0 u/ v% @$ g7 m& @) `" u
$ m m% S8 {. }1 L4 t1 b8 z
, k1 w1 Q, |- w4 L/ M& _# bfunction get($data) {
s$ z* v( S6 b4 \ $this->data = $data = trim_script($data);
& j( S1 O, p0 [ $model_cache = getcache('member_model', 'commons');
3 s3 A; n. z! g9 B: s $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
9 c c. B1 A7 q+ v% H, ~0 z! ` $info = array(); 6 _2 r* _3 r+ I0 N# f- y( B* V
$debar_filed = array('catid','title','style','thumb','status','islink','description'); + d* W6 i2 z/ `! v/ o
if(is_array($data)) {
' V l7 w- ~% _9 N# v# g foreach($data as $field=>$value) {
7 |! I1 p: Y* [) }6 w2 C if($data['islink']==1 && !in_array($field,$debar_filed)) continue; ; }7 e5 j Y7 a8 B" m) x: Z/ g
$name = $this->fields[$field]['name'];
" U9 i! i. Q2 T $minlength = $this->fields[$field]['minlength']; - V6 `3 W9 t I( S3 D" V( ?1 \
$maxlength = $this->fields[$field]['maxlength'];
+ B. R8 M# a3 a8 ~ $pattern = $this->fields[$field]['pattern']; ! |" @3 `0 y! I
$errortips = $this->fields[$field]['errortips'];
' ]' M6 Z$ P# V if(empty($errortips)) $errortips = "$name 不符合要求!";
* H1 x6 w) K8 W) f/ O K $length = empty($value) ? 0 : strlen($value); 8 \1 q& u8 `" t& k9 T
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
% p. U3 s' O, t if($maxlength && $length > $maxlength && !$isimport) {
( S/ k n. ~7 H7 h" E9 V: j showmessage("$name 不得超过 $maxlength 个字符!"); : d* M& R( T4 V8 G
} else { $ e9 t" D7 i$ Y1 O( a9 T
str_cut($value, $maxlength); 6 A! \) @# E5 u8 L- ?% Y6 }
}
1 E* t' V! Q9 k/ r if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
: d" t K" u: W& M9 m9 r6 [ if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); + r+ R% m9 d+ x+ Q" Q
$func = $this->fields[$field]['formtype']; / T( }) N c' }) D- C) E$ s9 S" X
if(method_exists($this, $func)) $value = $this->$func($field, $value);
" X% H9 j2 H) j, Q $info[$field] = $value;
. @$ Z0 n7 h; v' c" \ } - z9 U/ f; _$ G. K; g# p q
}
4 i. n+ \3 m3 N# p' Q return $info; ) r! H$ X3 s& c4 t( o
}
) |/ s9 p- a! w% V Ptrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,( c% B0 q r* z* T% K/ H$ U
$ x) }5 Z) b* m \
再到phpcms\modules\member\index.php 文件account_manage_info函数
6 N0 U6 T' Q( i' l c7 W过了get()函数之后。! P9 x+ }2 s- L' g3 q3 Y
5 O# v2 b" O3 [1 h7 R* f9 \
+ t: r! g" K+ |; y3 F/ L$modelinfo = $member_input->get($_POST['info']);
! R& u# ?" A- G$ m/ X& O! Z $this->db->set_model($this->memberinfo['modelid']); 9 m* ?1 h5 S6 s
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
) V# Q9 |0 R( {& ~ if(!empty($membermodelinfo)) {
4 X8 |. e1 O; w6 ~5 T- s- F $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 9 a# n* j2 K9 n" N: f# k
} else {
0 v, m ~* C7 J- K% }# R直接带入数据库,update函数我们跟进看看
( N8 Y% C4 Z4 d# S1 ]/ Q. l$ I+ ]2 G! `$ Q4 R
: S7 s4 H1 [+ H( E
public function update($data, $table, $where = '') { 2 E/ {. T! |5 i- z. G
if($table == '' or $where == '') {
: X7 m1 `$ [0 g" A% K9 |* j) e return false; % d' O1 G% g9 x5 k, K% Q
}
' z" E* {% U( ~7 F& { $where = ' WHERE '.$where; 7 F4 D6 C& M, c- e, a
$field = ''; ) ]3 D6 {2 d: H: Q9 n6 a
if(is_string($data) && $data != '') {
% C# f6 M# x4 O $field = $data;
% S- S1 @. `$ n } elseif (is_array($data) && count($data) > 0) {
/ [; w# u( ~7 m $fields = array(); 8 P7 O, o: p. F2 T7 ?) X
foreach($data as $k=>$v) { - l$ |( v' R" L+ b" C; }
switch (substr($v, 0, 2)) {
5 ~- g7 |+ D5 ^) n0 g case '+=':
. Y- u3 k1 |# m4 R9 k $v = substr($v,2); 2 G8 p5 c9 f2 E. S# T% b3 _$ G# C
if (is_numeric($v)) {
6 {4 \( J8 M+ N- W. G' [3 N! E $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
5 J- E' p& y) J8 {# _ } else { 3 e' y) [1 [! \3 b' O1 t3 l$ q
continue;
4 ~+ t$ L# f9 x3 x1 ~" l8 c6 p, P- g } - w7 K* b: d5 m) C% z' E: V) L
break;
" t x3 j7 A1 W8 m+ m8 m1 d case '-=':
! t' \% q! L/ |3 [( _0 }8 S $v = substr($v,2); $ l1 o; B J; N$ E. ?1 J
if (is_numeric($v)) {
# B/ v$ T8 H x/ ` $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); " c/ N6 e; x/ G! U* l4 C
} else {
6 C5 c5 Q+ ^$ A" m7 G h$ @ continue; * S7 ^7 Z/ q3 `7 R
} ; q. i- J ^/ v- x
break;
1 n# `' H& }: d- @7 m; {* Z default:
, Y4 N4 W0 L0 x: v- C9 r% x $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); / \9 u5 S- h" N! T6 t5 [% `
} / z" e# {! \: G0 `
}
' r; o; I+ o. j& A7 z $field = implode(',', $fields); 4 z7 _/ |, D( r4 e. L- L) |
} else {
6 J E+ a7 v* T return false; 5 y% S# a8 h1 c" u) [& i
} 3 Y. }+ }" B4 H# B- i$ D
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 7 G. ?' I: x8 ]8 ~6 y
print_r($sql);
" K! v2 p) h) e7 B, L return $this->execute($sql); 4 O3 V3 \0 z# |7 A0 j
} ) g3 V' Y: o( n: c
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。0 ~& t3 ]; Z2 e
9 L6 p c1 e6 b' n3 i. y5 u, q i
攻击测试:) G; I0 C1 q! W" i# c7 Y
测试地址http://localhost' G. q, ~0 c) v6 ]" d1 G
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
8 z }4 Z1 ^# v- e9 h$ u) I
i# f1 q9 E# E7 f8 e0 i , \# k$ Q' f' e! t3 C
3 R: l+ e6 W6 |6 g. i
. L) ^0 s2 F/ J5 ?0 v4 {
$ p0 X/ A6 F7 f+ U' m |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|