|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告) X; \- x$ y$ L
漏洞作者:skysheep
1 \2 e: H2 G" e8 E/ I) d分析作者:Seay" N! k" @, f( X
博客:http://www.cnseay.com/
) s; R$ [# n5 q4 f' e6 v漏洞分析:( y5 [; u k* Y) S+ f8 W& H( Z# K
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。: m# L" w% `0 C2 O9 K# A' E2 u
. K4 p) C" |( r! M/ n8 E
) E$ Y2 I: Z! U% w8 i( x- b) q
9 Z! b) S P7 `( @ e3 {- n4 w
public function account_manage_info() {
, R/ a' {- @6 G* k% `) |% C if(isset($_POST['dosubmit'])) {
9 h8 V& g$ R6 M! J* ^' v, _9 q% M //更新用户昵称 / J0 Z& _& M8 T# A! s
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; $ l" O' z+ t" G% ~/ L" a+ N
if($nickname) {
% }8 o% ?9 t; t% Q- L3 Z2 Z) v% s $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); , b6 k( Y7 o- a0 n- O. N
if(!isset($cookietime)) {
% N' a4 s# q( L( E8 Y6 u0 t! Y $get_cookietime = param::get_cookie('cookietime');
! C8 }! @! q) R2 h* B5 \3 F } & \/ j Z3 \4 N
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
4 x, ^5 J. O4 M7 x $cookietime = $_cookietime ? TIME + $_cookietime : 0; 6 P A/ Y3 B- J3 u, z' q
param::set_cookie('_nickname', $nickname, $cookietime); 6 T* l1 o5 S6 o2 D5 z2 M# M! _
} 9 `3 J4 Z" k! `; [8 ^
require_once CACHE_MODEL_PATH.'member_input.class.php';
% Y9 T, e! L3 O9 n( b4 t require_once CACHE_MODEL_PATH.'member_update.class.php'; + c' _# U" T4 Z# g( x, D
$member_input = new member_input($this->memberinfo['modelid']); . \) g3 t. {2 L7 ^0 m) C/ {2 O
$modelinfo = $member_input->get($_POST['info']);
0 z5 I6 q0 M, _- w" { $this->db->set_model($this->memberinfo['modelid']);
4 O3 G; p6 K3 { $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
( Y! g- H/ x) C' s% ~" m d9 G- z if(!empty($membermodelinfo)) { 0 s% c/ K4 s- b# x( c
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ! R3 H+ P. p* {* d1 }
} else {
, b$ k4 c% _" H: d5 W9 b $modelinfo['userid'] = $this->memberinfo['userid']; D: G8 c1 |: d) H
$this->db->insert($modelinfo);
8 y2 J. U. {$ ~ }
; e& \2 _3 O. f; i* T代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
/ o) R0 C7 [% D) c在\caches\caches_model\caches_data\ member_input.class.php 文件中:
# T, [- h8 J) ~9 w7 @' ?6 B Y) H! k" a, L
3 ^5 M! D% i' P$ h7 w
8 d# W# K( Q1 _2 `% z. q" e' i+ yfunction get($data) {
$ j/ d' ?& T' n& t7 r0 }9 ]1 N $this->data = $data = trim_script($data);
7 ?4 _& L% B5 E% h/ p( m( s $model_cache = getcache('member_model', 'commons');
8 c* F3 J3 `) \7 ], W1 r! Q% e $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
5 }5 ?6 \. g2 k, W0 d0 L' @ $info = array();
! _6 B' M! W1 _) n% _6 K6 M $debar_filed = array('catid','title','style','thumb','status','islink','description');
4 ^& Q4 K5 S& v8 C, ] if(is_array($data)) {
' @, x6 E$ J# J3 S" X6 C foreach($data as $field=>$value) {
8 }5 b& G8 o y1 T0 e7 ?. Y+ s+ O7 R if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 2 Z0 l- I* u9 g9 o. v) F- L
$name = $this->fields[$field]['name'];
9 q% Q6 y6 U- r" E/ A1 J+ | $minlength = $this->fields[$field]['minlength'];
5 r j0 g G$ A/ J: Z $maxlength = $this->fields[$field]['maxlength']; 6 g0 j2 n2 R+ |/ R8 H' l
$pattern = $this->fields[$field]['pattern'];
6 D4 l; ~$ C/ Q, A6 W; E% |+ |' w( { $errortips = $this->fields[$field]['errortips']; 8 j+ {6 \8 m: ~& O
if(empty($errortips)) $errortips = "$name 不符合要求!"; + o5 n% H( |0 @ B1 g
$length = empty($value) ? 0 : strlen($value);
0 A) c/ a: B2 c+ C% M+ W* g5 y0 \, R if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
5 c% K2 Z) D3 C. s% l1 j2 L if($maxlength && $length > $maxlength && !$isimport) { # T; ?1 p+ B) o
showmessage("$name 不得超过 $maxlength 个字符!"); ; h/ C# b0 i" Y! f8 n
} else { }" q, S# P' [0 j {) G/ A
str_cut($value, $maxlength);
% F: f) @- U4 A, O+ r } f4 u* t! Q$ t7 u* Q! m% |$ L6 x
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ! w, G; x3 a% i- S3 [
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
/ O" l7 d( B& S $func = $this->fields[$field]['formtype']; & R: s% x2 ^3 N; L
if(method_exists($this, $func)) $value = $this->$func($field, $value); 3 I& o. v; R( `! m- [
$info[$field] = $value; 7 d- w4 R( p2 Y) p: y
} # d7 H& Q& J/ @/ m4 @9 O! F
} 8 K, F1 d' a* y' r1 K
return $info;
2 T+ ?2 a9 @7 f# B } . {# _7 \+ {) W0 f x
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
. r- M5 \ {9 u8 z
4 T$ Y( U- z8 {" x+ d2 Y再到phpcms\modules\member\index.php 文件account_manage_info函数
) s' V# N! E. K过了get()函数之后。0 g' N% t$ e% a; r$ N1 a# ^, j1 t4 t
# ?7 W9 K: h" W" D) t5 Z
/ b( f1 a. B" E4 k6 i
$modelinfo = $member_input->get($_POST['info']); $ r0 t4 K+ S" O( D
$this->db->set_model($this->memberinfo['modelid']);
( J( J3 V/ b- ? $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
. V+ s6 E6 f* K- g$ v" j7 w if(!empty($membermodelinfo)) { W6 U9 A7 {1 s- Q$ Z T, \) |4 u
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
2 g7 K# I1 G! I9 E: h# h } else {
0 z2 c3 l4 W+ y1 P7 H5 X0 F直接带入数据库,update函数我们跟进看看
) G L9 ?3 _3 ~- n7 c4 h& Y) v* p2 z# L: w
0 W7 b$ ^7 ^8 u' u1 k4 M1 _6 Spublic function update($data, $table, $where = '') {
! V' F) M1 w+ S if($table == '' or $where == '') { ' y7 D, E2 Y3 o9 W
return false;
$ D) Y- i, L5 Y0 m: B }
2 _, t$ c+ ?& ? $where = ' WHERE '.$where;
H" Y: U8 C1 K8 j# j $field = '';
) A* |- ^* e5 u8 t8 c if(is_string($data) && $data != '') { ' w+ A' D) E$ h( D6 J' I
$field = $data; ! }" J9 F( M0 _9 m% K5 `- K" U
} elseif (is_array($data) && count($data) > 0) {
4 ^, N0 \, U7 R; o' v $fields = array();
) l/ i6 m( @& ~* F foreach($data as $k=>$v) {
: _; K1 u6 G l0 B switch (substr($v, 0, 2)) { 1 A, @+ b' t, L
case '+=': # p. q: Q# T/ V j2 A7 l/ W
$v = substr($v,2);
\* N' k+ g# e% D0 l$ w4 f; J if (is_numeric($v)) {
- {+ R0 S3 B# i7 s $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); B6 q( z1 L4 B& m8 a5 \% ]2 P; ^
} else { 0 U+ \3 }; L( i
continue; 1 l$ _8 }4 T0 W
}
! d( ]5 |1 V% h% s5 [3 G+ `$ S1 w break;
6 i# [& g0 F4 L case '-=': $ N! z" _: `8 E
$v = substr($v,2); , y& y# B7 e1 t) p) m9 u. e
if (is_numeric($v)) {
6 T( K- W! |( ` $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 0 |, E7 v4 }! T# o# u P
} else {
$ i: B% [+ U o- l continue;
) a, n, M7 c* I7 x }
* E: c; F. x5 B; X) S break; 2 A8 M! F- k, O+ H0 z. V1 ?
default:
1 J( L: E d9 i* K( i+ S6 F $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
9 J9 j8 ~/ b" i } $ @8 [5 n- x) } J
} + I9 h/ Z* |- P& a5 ]9 j& i
$field = implode(',', $fields);
7 ]& R; s( g5 i1 X: t3 \* B: V } else {
5 y$ z: e) c5 @9 M* ]- w return false; ! R, Q- Z5 r5 ?6 d
}
+ M9 {3 L, |" |2 K9 S+ f $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 1 @7 y4 N& r% t
print_r($sql); ; ^+ z! ]2 [& w6 n7 Z) q
return $this->execute($sql);
. ?* a; {- [ d. `, A }
9 n: n! r2 \' k; Q- Z从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。 N0 J" Q$ k: R6 r. M" b. z
* ], g8 P% U' x: j攻击测试:$ A- \! c- B2 y: P% L9 Z; w, [
测试地址http://localhost6 k+ C* N6 r% }$ E: ~$ N( v
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
' r: h. B' `3 F, J( t2 N" Q% t( ~! @# U# g8 t- E, \
; t3 ?( \# i/ f5 n( j* h
, v5 ^1 c" @) G8 l) d7 e
& a6 m S& H- |/ X7 u. E2 K
! a4 G( `$ x' u0 V$ j/ u: H
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|