|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告, A- R- m1 D' V, f# j4 A
漏洞作者:skysheep! p# s3 H& r# W
分析作者:Seay Z; u! v( R0 O. q- Y7 g# L
博客:http://www.cnseay.com/
! `& q. G% ?" E" X5 b漏洞分析:& D# x( M* y' P) w! t; E4 U2 \; u
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
, d( r9 U. u1 ~1 L r4 }& E2 } v1 M0 Z7 B
& X! ^8 E R& q
$ S& Y* Y5 f) |( A6 x/ i
public function account_manage_info() { ' `- \' t" L- \& W( }
if(isset($_POST['dosubmit'])) {
g$ R3 N& d" Q5 V q! s3 [ //更新用户昵称 ) Y- `! @' q2 T m5 K
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
7 b9 Q8 j, a* ^9 S; R+ b if($nickname) {
' p) ]# \( w% f# w* d3 r* z! c $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
! d0 R% c& I. |2 s if(!isset($cookietime)) { , b+ c1 [" I, B/ I) p4 b' ?' }
$get_cookietime = param::get_cookie('cookietime'); ) O4 G. x& E9 A" s
}
, ?9 N2 c) J, t7 g* `* w $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); # r4 A0 R5 H+ q
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 8 h, n0 w9 W/ P& v3 ]& E6 o
param::set_cookie('_nickname', $nickname, $cookietime); & d9 B3 u( P+ @1 J
} ' g1 d8 d4 y9 L& \; R
require_once CACHE_MODEL_PATH.'member_input.class.php'; $ W& F! U7 _' Z, t# Z7 T
require_once CACHE_MODEL_PATH.'member_update.class.php'; " O1 D1 b/ p% {' L
$member_input = new member_input($this->memberinfo['modelid']); 5 k% `/ Q" \3 A# x* U
$modelinfo = $member_input->get($_POST['info']);
- l- h2 Q7 x5 z6 q+ v $this->db->set_model($this->memberinfo['modelid']);
/ O' n1 m: p" P2 d$ G. d" C7 O $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); $ S1 ] P4 w2 h/ V8 w# ~
if(!empty($membermodelinfo)) { ; h$ n9 G% e0 \5 t' L/ ^: V
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 8 e% ^2 Q( I5 W: e6 Y
} else {
+ F) V0 e }' C. [3 O% Y $modelinfo['userid'] = $this->memberinfo['userid'];
, s& A5 @' Q+ B $this->db->insert($modelinfo);
) C% H1 ?% I0 u) U$ f4 X" x, S$ { } X9 P. {6 n; x5 D% k
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,: D+ a; Y, C7 W0 q7 ~
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
) J- a0 U8 R0 H3 E# C# h2 h# I. g& R
% ]. Y! Y* P) h
; p* _5 V+ k9 G" Ufunction get($data) {
$ b( b0 Z- R5 z# ~ $this->data = $data = trim_script($data);
% g1 ^$ s' p! K7 z* B& ^ $model_cache = getcache('member_model', 'commons');
" d0 j4 u) Q2 q, z7 O6 o! o. a7 f) h7 r $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
5 B; ]% e8 S) g6 o0 M) w* ~ $info = array();
1 L+ i! I3 a7 o0 x ] $debar_filed = array('catid','title','style','thumb','status','islink','description'); % S+ B9 n7 j! _+ L* u
if(is_array($data)) {
' \2 {6 ~, t$ b+ ?! J foreach($data as $field=>$value) {
7 ]; e& J8 E$ j, x2 S+ p: u x if($data['islink']==1 && !in_array($field,$debar_filed)) continue; - T4 f. T" J& A9 ^! X2 z6 [: D
$name = $this->fields[$field]['name'];
. B8 }6 J( u+ M" f5 R $minlength = $this->fields[$field]['minlength'];
- H7 M/ c# N+ G$ |1 g $maxlength = $this->fields[$field]['maxlength'];
6 {" r& B S7 \9 A8 z $pattern = $this->fields[$field]['pattern'];
& U; v+ o& v! a" `# h! f3 ^ $errortips = $this->fields[$field]['errortips'];
; o/ O S8 `* T! P if(empty($errortips)) $errortips = "$name 不符合要求!";
4 ?' L7 g2 D" N" ] $length = empty($value) ? 0 : strlen($value); & L8 Z& @/ w4 v7 F6 U' F0 C
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
6 O8 n/ o9 N2 V8 \/ _+ L if($maxlength && $length > $maxlength && !$isimport) {
* B# `( J- k3 P, {/ Q$ W3 z showmessage("$name 不得超过 $maxlength 个字符!");
2 j/ c V$ \ i, X) [1 n } else { 2 b' |) B' u1 |7 a- L
str_cut($value, $maxlength); & K* J( A; X% x3 D; m
}
4 k6 Z( ]) h d0 \. l7 m# W% m9 z3 d if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); j" Z ~3 f' \4 t+ T
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); - E3 I3 u4 a! S% {
$func = $this->fields[$field]['formtype'];
& k% o u* g# l0 h7 B: u if(method_exists($this, $func)) $value = $this->$func($field, $value);
* D1 h1 s+ b6 h( Z& R+ } $info[$field] = $value; 5 d+ G- O6 ?8 ~+ m- O6 \ L& U( ]
} 2 R+ Z& w& d( }' Q5 F4 |
} * V* ` P. J3 U8 E/ N
return $info;
+ T z( @8 H! k' U5 Z& ` } 2 i" L$ f8 X; s% T
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
' U8 P2 D5 o( C& x2 s
* _0 D5 E/ i( n r再到phpcms\modules\member\index.php 文件account_manage_info函数
9 m" Z+ c, m, a0 L/ D4 s过了get()函数之后。' E2 C0 b, n* S- F: Q" e6 q
& L* [1 G* a! L b! |$ V% d5 n" \
- J5 Y$ E9 f, [$modelinfo = $member_input->get($_POST['info']); : ~3 H" X0 |2 y8 H5 m3 m' s& T
$this->db->set_model($this->memberinfo['modelid']);
4 }2 ?9 ^% i; K r $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
+ b; C2 I7 C) b" X1 b4 O& i) f8 x# w if(!empty($membermodelinfo)) {
4 a3 |7 q b2 \1 l $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); / Y W: c4 p- ~( ^# Y: H
} else { 7 O! d1 h7 G2 x' s" m4 W
直接带入数据库,update函数我们跟进看看8 `. E8 v; u4 @3 W
, F2 y: I# o/ s5 Y5 w, f
- y& F7 b8 y+ U8 N3 e9 K$ Apublic function update($data, $table, $where = '') { 6 k' w9 t1 t/ D' P2 {3 z* q
if($table == '' or $where == '') {
* {8 _4 K0 v" s( d4 ?# o6 _ return false; ! w; B# F4 p, i; y- S% x
} 4 _" a- k5 W7 ?" ^' b p; @! |
$where = ' WHERE '.$where;
# R! O9 j& M" h) j4 K $field = ''; : r0 Y8 @+ R6 q2 w' p" L3 M
if(is_string($data) && $data != '') {
6 c8 G) \4 F2 M* @2 o$ h0 i $field = $data;
. S: ]7 X" Y: w" C" K& z } elseif (is_array($data) && count($data) > 0) { 3 n3 g# {7 `0 Q" Q' b
$fields = array(); 1 ~' k8 O; \) R9 q% k& \8 H
foreach($data as $k=>$v) { 4 D# m! P7 ^4 g5 a$ Q9 ^7 H
switch (substr($v, 0, 2)) {
" W% n \# Q- h7 {5 J case '+=':
; G6 [2 T) c$ B* Q1 k5 U $v = substr($v,2);
+ V8 R( O+ R4 K2 r, l, A if (is_numeric($v)) {
/ F' u" ? B8 l$ T $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); . O" y! ` g+ P! E( ]# G
} else { 2 k$ b! m; [# P/ D1 v7 b- }9 J
continue; - z& \: h1 j! y4 Z3 E
} 9 D3 B( A( ?) N0 g1 z: h: r
break; 6 X9 l) b; b+ Q+ |' m
case '-=': # h9 e6 G) e% \0 p; Q
$v = substr($v,2); % G p2 {$ e5 K7 V1 b
if (is_numeric($v)) {
% C* M5 H% f+ Z$ o $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
0 z/ ~/ z( K* C; c8 D; v0 d7 S' o0 D } else { 5 e0 o. H. Q" h8 A: w6 L, e% t. ]9 |
continue;
2 x9 P* s) E' v% k/ b- g }
: q' z+ n$ g4 X& v F! K7 ]5 \ break; 2 ~. B! y$ M7 G3 I
default:
' d0 \; p8 G0 O% Y. t: M $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
2 }% p; x" D5 Z$ R }
8 }# n% G+ i. |$ t$ h- k" s } ) W' y5 D& I& V
$field = implode(',', $fields); * u3 ^$ m, _2 Q- Z
} else {
) t' e3 [2 N: P. t) J# p/ U return false; 8 h! q8 Z6 z( v4 r
} ) i2 ?+ Q2 c9 u, r7 x6 r$ q$ ^
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
8 E# X. N. k& {2 s print_r($sql);
! I ~7 f. h2 T8 k. ^( I( k. _" [ return $this->execute($sql);
5 O3 J: z; ]# y$ `5 X4 i5 Q }
! I5 f1 E$ S& Y3 _从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
8 V) A4 Z3 n" Z& Q$ ` i5 l! p
. ?$ E# u1 V+ E8 X攻击测试:4 q) s; L3 n; L0 n; j( N, u
测试地址http://localhost+ p$ Y- k, E* y: A& N* b, y
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
8 g# t1 ~5 b2 Z `
8 b$ P: M: Q* u, m: Z% G ; F2 q! g y# h3 v: \
7 T/ f( i4 H) T# ]; p! g) O9 \
8 v& c9 j0 m2 |/ d
' t1 W {# ? F( ?0 ^8 _ |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|