|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告9 C3 ?, J8 v/ ~* c" h% F2 x% D
漏洞作者:skysheep
3 g' N8 K4 p! h8 t$ B5 C6 E分析作者:Seay( E# b+ P8 O& e) y. _- }# P
博客:http://www.cnseay.com/* u' C4 G6 q8 {
漏洞分析:
7 _) ^8 I! M& M 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。) F) R- k! _: x* {! Z2 ]0 b
; L; ?( n& T3 |/ U- i2 t/ e$ b& Y
0 ^" c5 a F7 O" L 3 z, V! C( E- N2 p
public function account_manage_info() {
4 p* Y& X* U( F8 W if(isset($_POST['dosubmit'])) {
5 X, F( R. v1 {* T- a' ? //更新用户昵称 ) f% W) K% d2 X; Y4 t8 C
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; & I9 Q2 {! f; P- f: h6 \7 V
if($nickname) { 2 H" ?; B" |! j: e+ _
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); " c6 Y3 p7 x+ g4 X1 G
if(!isset($cookietime)) { - e) J) v% J3 k$ S2 y3 D- J6 {
$get_cookietime = param::get_cookie('cookietime');
, X) v6 x& C5 W: `. ?) \9 | } 1 g& ?2 A: A# \/ i+ ?9 ]
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
: ?3 v0 y: Z, ]8 T4 D) g$ j $cookietime = $_cookietime ? TIME + $_cookietime : 0; 2 j' k% O& Q/ m* I0 J i
param::set_cookie('_nickname', $nickname, $cookietime);
4 P' h* v: O( B5 V' e4 y } - @" w; z6 F- z, M
require_once CACHE_MODEL_PATH.'member_input.class.php'; 5 P! `2 ]5 U0 j
require_once CACHE_MODEL_PATH.'member_update.class.php'; 6 }" j' M. x6 j; @
$member_input = new member_input($this->memberinfo['modelid']); % d+ H% c* a0 _! L- I
$modelinfo = $member_input->get($_POST['info']); 7 X5 f0 V" A- O% x& _
$this->db->set_model($this->memberinfo['modelid']);
' s$ G( h$ d- S1 W# U( K $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 9 u" d% G/ J+ q& z- [
if(!empty($membermodelinfo)) {
7 u2 b6 A# _# }5 L) t1 a $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
- ~+ ]- {. J7 f/ m6 A } else { ) v/ q( K: W! f" a# J- F; X
$modelinfo['userid'] = $this->memberinfo['userid']; 8 h, @) ], Y) I) ?4 {: W( I3 o4 z
$this->db->insert($modelinfo);
0 a' U9 i9 H3 x6 r8 k, r% J }
7 m9 e+ M7 A$ z8 W6 K O代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
c8 _* j& S5 w7 |4 U在\caches\caches_model\caches_data\ member_input.class.php 文件中:
B L; C! @8 P2 X8 |
' ^7 T/ o/ ~8 h2 D
/ R' M$ l9 x- ^& _' D/ w1 M; h2 n ( _" z7 Z" I, ^
function get($data) {
5 d6 `, D9 l: b I! S $this->data = $data = trim_script($data); * ]1 o- K$ O+ S6 Z1 d( ~0 a
$model_cache = getcache('member_model', 'commons'); # V+ n$ W9 R1 _& L7 M
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
3 | Y5 M5 E4 ~3 T' W, J $info = array(); . r7 r$ q Y* S# l; @& |: a0 j/ w9 u
$debar_filed = array('catid','title','style','thumb','status','islink','description');
/ B1 R) t! }8 h0 [" I. y6 c/ W* M if(is_array($data)) { 7 D: i( T7 \1 K- J- a$ D# u
foreach($data as $field=>$value) {
! u: e" h0 W; K( E if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
+ D' I! I. y0 w& a; ?5 \ $name = $this->fields[$field]['name'];
* a" B9 {- M' W' I $minlength = $this->fields[$field]['minlength'];
1 p6 u( w- T# F $maxlength = $this->fields[$field]['maxlength']; 5 | Z( V+ l; R6 V& d$ l
$pattern = $this->fields[$field]['pattern'];
1 {( N6 M U7 K+ I# ~8 a: \) v# L $errortips = $this->fields[$field]['errortips'];
* q( z5 y+ J! D$ v# {4 j' F9 \& H if(empty($errortips)) $errortips = "$name 不符合要求!"; % W- i, k2 r- U2 i* |/ l* F
$length = empty($value) ? 0 : strlen($value);
6 p/ O. i2 b! I: \: T if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
* I9 i7 e# ^+ |/ o, x if($maxlength && $length > $maxlength && !$isimport) { 0 M) x3 F0 N2 W3 p- P
showmessage("$name 不得超过 $maxlength 个字符!"); ' a& X8 y. x I+ X, ^- }
} else { 8 n1 O6 f3 \5 j
str_cut($value, $maxlength);
/ |* W! K0 [" C }
- u+ Z0 ]7 i8 t* S& x if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
, e! F3 c( T; N if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
4 Y, Z$ T- {4 D# F $func = $this->fields[$field]['formtype']; 5 e0 c8 z% l1 ]4 E* _+ K: l! ~7 L
if(method_exists($this, $func)) $value = $this->$func($field, $value); 2 b) [) d: I; W, R" N8 C
$info[$field] = $value; ! ~( I8 o0 s7 C+ i! E/ l; g0 c
} , `- J# ?8 t" i4 D8 ]4 \. Q
} 8 m# I& O; |* Z
return $info; % d: g: P# C+ S- J* F r
}
6 _+ R- z- ?/ Z& Atrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,3 }8 M/ C4 V$ ^
* V) n+ o3 j3 Z8 t: C6 l( p
再到phpcms\modules\member\index.php 文件account_manage_info函数; f0 I. }, h' G+ ^5 Z
过了get()函数之后。
! i* z5 V4 I( }4 L* _. w
' Q. H. k! C+ d M
}$ N* m1 X6 _7 q, w) F( ]$modelinfo = $member_input->get($_POST['info']); : N; [" d, o# K% E f- t* O1 u0 _6 v
$this->db->set_model($this->memberinfo['modelid']); / x, h# E; l0 y( H! c8 }" L
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); # D% M0 ^6 [- _' ]9 x2 F- ^
if(!empty($membermodelinfo)) { & n5 R; F3 x, q0 f1 j) e
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 3 A, O; B8 n) y/ P4 ?0 U1 Q: i: L
} else { 0 @$ p, u9 ~4 t$ e. j
直接带入数据库,update函数我们跟进看看
: E2 K" m) V. H4 J4 O. _3 E
( \- t& Q( |& B, B; t% I9 p" V ' R b3 l" I9 H, r' i: v7 v
public function update($data, $table, $where = '') { $ ?" Z5 b6 d, f/ l- c& }7 ?8 s6 O& C
if($table == '' or $where == '') {
( z9 ^4 s& l) K }8 t return false;
" F- t: T' W# T+ n4 U# g' p } 3 \+ \+ ^% c! d4 G4 [( C
$where = ' WHERE '.$where;
G9 A" H' X* t _/ k( k $field = '';
, \/ N. O) Z% y( z& v+ `7 k [ if(is_string($data) && $data != '') { ! K0 i4 R: W- ~2 }) u( g
$field = $data;
7 C+ o% s+ q7 s& ?) S8 N5 q } elseif (is_array($data) && count($data) > 0) {
4 x# k- j7 `4 U/ p5 { $fields = array(); 0 O% R) ~: N: T" g0 o' E) F @
foreach($data as $k=>$v) {
) t; U. U+ T7 E6 A& V4 F/ h6 ]/ U1 ]8 [ switch (substr($v, 0, 2)) {
2 ^" s, P1 r2 a, b( k case '+=': / I7 X8 I$ x1 c( M. j% D& a6 R9 J
$v = substr($v,2);
" Z4 _6 w$ ~& Z- x n if (is_numeric($v)) {
4 m3 Q/ Z. d0 e5 M( X+ u* U $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
; m6 O/ U* Y2 V& ^5 a" \ } else {
% l( v4 }" K* Y9 Q' Y/ g( J continue;
9 Z2 g# N3 B5 q) `: E8 R; A' c) E }
' o* C. e- I2 G# L4 f break; ) E+ {/ E0 d0 E6 l
case '-=': - N' y2 ?5 e* t/ G+ J! s$ P
$v = substr($v,2);
6 r+ s; p% T4 ]+ H% l2 C& j" b if (is_numeric($v)) { 8 _7 X/ K- e4 x8 E$ V
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
6 p3 Z+ _( {% C: ^ } else {
W9 Q$ N, Y; r' I continue;
6 {9 }+ m; ]$ s/ Y } 4 F+ R8 ~, Y( m9 f+ n
break; ' E; W2 w$ b( T3 V
default:
8 N [6 [! l Y4 R/ S6 t $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
6 u j% h( [" i* g' L8 L. | }
4 O8 B' l, |. r' [ } B# }5 U% t% S8 S$ E3 B6 P
$field = implode(',', $fields);
+ \2 e$ N( U ^ } else { " z* r/ v0 |& O
return false;
4 l4 N: {% X% C: Q1 A& t1 T6 } }
& [( f& S0 I$ d. w0 t7 ~& }0 r2 K $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; # i0 w" t' ~4 u# N
print_r($sql); : R3 U& ~0 C3 j
return $this->execute($sql); * X% q. |0 b" O* h9 C; V! I
} 8 A I( C8 _: y, f( A; Z3 I
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。& j1 K4 V2 A8 `0 P
. I# w6 b7 l( h M: G% V
攻击测试:
& A; I5 y; t/ y$ y测试地址http://localhost
/ q( E! _( P8 q% T0 s 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句! S' L+ L( S ?" n- ?
) I' y. X( M, e R- Z$ P
" w: Y5 H9 X3 d- Z3 ?" I- s7 q3 G. F( l
P7 ^/ G0 d3 g* x1 j3 ]" y. J
e" _% S) J6 {: j: b( ~/ v |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|