line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package CGI::IDS; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
#------------------------- Notes ----------------------------------------------- |
4
|
|
|
|
|
|
|
# This source code is documented in both POD and ROBODoc format. |
5
|
|
|
|
|
|
|
# Please find additional POD documentation at the end of this file |
6
|
|
|
|
|
|
|
# (search for "__END__"). |
7
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
#****c* IDS |
10
|
|
|
|
|
|
|
# NAME |
11
|
|
|
|
|
|
|
# PerlIDS (CGI::IDS) |
12
|
|
|
|
|
|
|
# DESCRIPTION |
13
|
|
|
|
|
|
|
# Website Intrusion Detection System based on PHPIDS https://phpids.org rev. 1409 |
14
|
|
|
|
|
|
|
# AUTHOR |
15
|
|
|
|
|
|
|
# Hinnerk Altenburg |
16
|
|
|
|
|
|
|
# CREATION DATE |
17
|
|
|
|
|
|
|
# 2008-06-03 |
18
|
|
|
|
|
|
|
# COPYRIGHT |
19
|
|
|
|
|
|
|
# Copyright (C) 2008-2014 Hinnerk Altenburg |
20
|
|
|
|
|
|
|
# |
21
|
|
|
|
|
|
|
# This file is part of PerlIDS. |
22
|
|
|
|
|
|
|
# |
23
|
|
|
|
|
|
|
# PerlIDS is free software: you can redistribute it and/or modify |
24
|
|
|
|
|
|
|
# it under the terms of the GNU Lesser General Public License as published by |
25
|
|
|
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or |
26
|
|
|
|
|
|
|
# (at your option) any later version. |
27
|
|
|
|
|
|
|
# |
28
|
|
|
|
|
|
|
# PerlIDS is distributed in the hope that it will be useful, |
29
|
|
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
30
|
|
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
31
|
|
|
|
|
|
|
# GNU Lesser General Public License for more details. |
32
|
|
|
|
|
|
|
# |
33
|
|
|
|
|
|
|
# You should have received a copy of the GNU Lesser General Public License |
34
|
|
|
|
|
|
|
# along with PerlIDS. If not, see . |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
#**** |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head1 NAME |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
CGI::IDS - PerlIDS - Perl Website Intrusion Detection System (XSS, CSRF, SQLI, LFI etc.) |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head1 VERSION |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
Version 1.0217 - based on and tested against the filter tests of PHPIDS https://phpids.org rev. 1409 |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=cut |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
our $VERSION = '1.0217'; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=head1 DESCRIPTION |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
PerlIDS (CGI::IDS) is a website intrusion detection system based on PHPIDS L to detect possible attacks in website requests, e.g. Cross-Site Scripting (XSS), Cross-Site Request Forgery (CSRF), SQL Injections (SQLI) etc. |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
It parses any hashref for possible attacks, so it does not depend on CGI.pm. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
The intrusion detection is based on a set of converters that convert the request according to common techniques that are used to hide attacks. These converted strings are checked for attacks by running a filter set of currently 68 regular expressions and a generic attack detector to find obfuscated attacks. For easily keeping the filter set up-to-date, PerlIDS is compatible to the original XML filter set of PHPIDS, which is frequently updated. |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Each matching regular expression has it's own impact value that increases the tested string's total attack impact. Using these total impacts, a threshold can be defined by the calling application to log the suspicious requests to database and send out warnings via e-mail or even SMS on high impacts that indicate critical attack activity. These impacts can be summed per IP address, session or user to identify attackers who are testing the website with small impact attacks over a time. |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
You can improve the speed and the accurancy (reduce false positives) of the IDS by specifying an L. This whitelist check can also be processed separately by using L if you want to pre-check the parameters on your application servers before you send only the suspicious requests over to worker servers that do the complete CGI::IDS check. |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
Download and install via CPAN: L |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
Report issues and contribute to PerlIDS on GitHub: L |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=head1 SYNOPSIS |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
use CGI; |
69
|
|
|
|
|
|
|
use CGI::IDS; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
$cgi = new CGI; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# instantiate the IDS object; |
74
|
|
|
|
|
|
|
# do not scan keys, values only; don't scan PHP code injection filters (IDs 58,59,60); |
75
|
|
|
|
|
|
|
# whitelist the parameters as per given XML whitelist file; |
76
|
|
|
|
|
|
|
# All arguments are optional, 'my $ids = new CGI::IDS();' is also working correctly, |
77
|
|
|
|
|
|
|
# loading the entire shipped filter set and not scanning the keys. |
78
|
|
|
|
|
|
|
# See new() for all possible arguments. |
79
|
|
|
|
|
|
|
my $ids = new CGI::IDS( |
80
|
|
|
|
|
|
|
whitelist_file => '/home/hinnerk/ids/param_whitelist.xml', |
81
|
|
|
|
|
|
|
disable_filters => [58,59,60], |
82
|
|
|
|
|
|
|
); |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# start detection |
85
|
|
|
|
|
|
|
my %params = $cgi->Vars; |
86
|
|
|
|
|
|
|
my $impact = $ids->detect_attacks( request => \%params ); |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
if ($impact > 0) { |
89
|
|
|
|
|
|
|
my_log( $ids->get_attacks() ); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
if ($impact > 30) { |
92
|
|
|
|
|
|
|
my_warn_user(); |
93
|
|
|
|
|
|
|
my_email( $ids->get_attacks() ); |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
if ($impact > 50) { |
96
|
|
|
|
|
|
|
my_deactivate_user(); |
97
|
|
|
|
|
|
|
my_sms( $ids->get_attacks() ); |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# now with scanning the hash keys |
101
|
|
|
|
|
|
|
$ids->set_scan_keys(scan_keys => 1); |
102
|
|
|
|
|
|
|
$impact = $ids->detect_attacks( request => \%params ); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
See F in CGI::IDS module package for a running demo. |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
You might want to build your own 'session impact counter' that increases during multiple suspicious requests by one single user, session or IP address. |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=head1 METHODS |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=cut |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
#------------------------- Pragmas --------------------------------------------- |
113
|
1
|
|
|
1
|
|
59831
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
42
|
|
114
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
33
|
|
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
#------------------------- Libs ------------------------------------------------ |
117
|
1
|
|
|
1
|
|
554
|
use XML::Simple qw(:strict); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
use HTML::Entities; |
119
|
|
|
|
|
|
|
use MIME::Base64; |
120
|
|
|
|
|
|
|
use Encode qw(decode); |
121
|
|
|
|
|
|
|
use Carp; |
122
|
|
|
|
|
|
|
use Time::HiRes; |
123
|
|
|
|
|
|
|
use FindBin qw($Bin); |
124
|
|
|
|
|
|
|
use CGI::IDS::Whitelist; |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
#------------------------- Settings -------------------------------------------- |
127
|
|
|
|
|
|
|
$XML::Simple::PREFERRED_PARSER = "XML::Parser"; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
#------------------------- Debugging ------------------------------------------- |
130
|
|
|
|
|
|
|
# debug modes (binary): |
131
|
|
|
|
|
|
|
use constant DEBUG_KEY_VALUES => (1 << 0); # print each key value pair |
132
|
|
|
|
|
|
|
use constant DEBUG_IMPACTS => (1 << 1); # print impact per key value pair |
133
|
|
|
|
|
|
|
use constant DEBUG_ARRAY_INFO => (1 << 2); # print attack info arrays |
134
|
|
|
|
|
|
|
use constant DEBUG_CONVERTERS => (1 << 3); # print output of each converter |
135
|
|
|
|
|
|
|
use constant DEBUG_SORT_KEYS_NUM => (1 << 4); # sort request by keys numerically |
136
|
|
|
|
|
|
|
use constant DEBUG_SORT_KEYS_ALPHA => (1 << 5); # sort request by keys alphabetically |
137
|
|
|
|
|
|
|
use constant DEBUG_WHITELIST => (1 << 6); # dumps loaded whitelist hash |
138
|
|
|
|
|
|
|
use constant DEBUG_MATCHED_FILTERS => (1 << 7); # print IDs of matched filters |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
#use constant DEBUG_MODE => DEBUG_KEY_VALUES | |
141
|
|
|
|
|
|
|
# DEBUG_IMPACTS | |
142
|
|
|
|
|
|
|
# DEBUG_WHITELIST | |
143
|
|
|
|
|
|
|
# DEBUG_ARRAY_INFO | |
144
|
|
|
|
|
|
|
# DEBUG_CONVERTERS | |
145
|
|
|
|
|
|
|
# DEBUG_MATCHED_FILTERS | |
146
|
|
|
|
|
|
|
# DEBUG_SORT_KEYS_NUM; |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# simply comment this line out to switch debugging mode on (also uncomment above declaration) |
149
|
|
|
|
|
|
|
use constant DEBUG_MODE => 0; |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
#------------------------- Constants ------------------------------------------- |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# converter functions, processed in this order |
154
|
|
|
|
|
|
|
my @CONVERTERS = qw/ |
155
|
|
|
|
|
|
|
stripslashes |
156
|
|
|
|
|
|
|
_convert_from_repetition |
157
|
|
|
|
|
|
|
_convert_from_commented |
158
|
|
|
|
|
|
|
_convert_from_whitespace |
159
|
|
|
|
|
|
|
_convert_from_js_charcode |
160
|
|
|
|
|
|
|
_convert_js_regex_modifiers |
161
|
|
|
|
|
|
|
_convert_entities |
162
|
|
|
|
|
|
|
_convert_quotes |
163
|
|
|
|
|
|
|
_convert_from_sql_hex |
164
|
|
|
|
|
|
|
_convert_from_sql_keywords |
165
|
|
|
|
|
|
|
_convert_from_control_chars |
166
|
|
|
|
|
|
|
_convert_from_nested_base64 |
167
|
|
|
|
|
|
|
_convert_from_out_of_range_chars |
168
|
|
|
|
|
|
|
_convert_from_xml |
169
|
|
|
|
|
|
|
_convert_from_js_unicode |
170
|
|
|
|
|
|
|
_convert_from_utf7 |
171
|
|
|
|
|
|
|
_convert_from_concatenated |
172
|
|
|
|
|
|
|
_convert_from_proprietary_encodings |
173
|
|
|
|
|
|
|
_run_centrifuge |
174
|
|
|
|
|
|
|
/; |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
#------------------------- Subs ------------------------------------------------ |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
#****m* IDS/new |
179
|
|
|
|
|
|
|
# NAME |
180
|
|
|
|
|
|
|
# Constructor |
181
|
|
|
|
|
|
|
# DESCRIPTION |
182
|
|
|
|
|
|
|
# Creates an IDS object. |
183
|
|
|
|
|
|
|
# The filter set and whitelist will stay loaded during the lifetime of the object. |
184
|
|
|
|
|
|
|
# You may call detect_attacks() multiple times, the attack array ( get_attacks() ) |
185
|
|
|
|
|
|
|
# will be emptied at the start of each run of detect_attacks(). |
186
|
|
|
|
|
|
|
# INPUT |
187
|
|
|
|
|
|
|
# HASH |
188
|
|
|
|
|
|
|
# filters_file STRING The path to the filters XML file (defaults to shipped IDS.xml) |
189
|
|
|
|
|
|
|
# whitelist_file STRING The path to the whitelist XML file |
190
|
|
|
|
|
|
|
# scan_keys INT 1 to scan also the keys, 0 if not (default: 0) |
191
|
|
|
|
|
|
|
# disable_filters ARRAYREF[INT,INT,...] if given, these filter ids will be disabled |
192
|
|
|
|
|
|
|
# OUTPUT |
193
|
|
|
|
|
|
|
# IDS object, dies (croaks) if no filter rule could be loaded |
194
|
|
|
|
|
|
|
# EXAMPLE |
195
|
|
|
|
|
|
|
# # instantiate object; do not scan keys, values only |
196
|
|
|
|
|
|
|
# my $ids = new CGI::IDS( |
197
|
|
|
|
|
|
|
# filters_file => '/home/hinnerk/sandbox/ids/cgi-bin/default_filter.xml', |
198
|
|
|
|
|
|
|
# whitelist_file => '/home/hinnerk/sandbox/ids/cgi-bin/param_whitelist.xml', |
199
|
|
|
|
|
|
|
# scan_keys => 0, |
200
|
|
|
|
|
|
|
# disable_filters => [58,59,60], |
201
|
|
|
|
|
|
|
# ); |
202
|
|
|
|
|
|
|
#**** |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head2 new() |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
Constructor. Can optionally take a hash of settings. If I is not given, |
207
|
|
|
|
|
|
|
the shipped filter set will be loaded, I defaults to 0. |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
The filter set and whitelist will stay loaded during the lifetime of the object. |
210
|
|
|
|
|
|
|
You may call C multiple times, the attack array (C) |
211
|
|
|
|
|
|
|
will be emptied at the start of each run of C. |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
For example, the following is a valid constructor: |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
my $ids = new CGI::IDS( |
216
|
|
|
|
|
|
|
filters_file => '/home/hinnerk/ids/default_filter.xml', |
217
|
|
|
|
|
|
|
whitelist_file => '/home/hinnerk/ids/param_whitelist.xml', |
218
|
|
|
|
|
|
|
scan_keys => 0, |
219
|
|
|
|
|
|
|
disable_filters => [58,59,60], |
220
|
|
|
|
|
|
|
); |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
The Constructor dies (croaks) if no filter rule could be loaded. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=cut |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub new { |
227
|
|
|
|
|
|
|
my ($package, %args) = @_; |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# defaults |
230
|
|
|
|
|
|
|
$args{scan_keys} = $args{scan_keys} ? 1 : 0; |
231
|
|
|
|
|
|
|
my $filters_file_default = __FILE__; |
232
|
|
|
|
|
|
|
$filters_file_default =~ s/IDS.pm/IDS.xml/; |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# self member variables |
235
|
|
|
|
|
|
|
my $self = { |
236
|
|
|
|
|
|
|
filters_file => $args{filters_file} || $filters_file_default, |
237
|
|
|
|
|
|
|
whitelist => CGI::IDS::Whitelist->new(whitelist_file => $args{whitelist_file}), |
238
|
|
|
|
|
|
|
scan_keys => $args{scan_keys}, |
239
|
|
|
|
|
|
|
impact => 0, |
240
|
|
|
|
|
|
|
attacks => undef, # [] |
241
|
|
|
|
|
|
|
filters => [], |
242
|
|
|
|
|
|
|
filter_disabled => { map { $_ => 1} @{$args{disable_filters} || []} }, |
243
|
|
|
|
|
|
|
}; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
if (DEBUG_MODE & DEBUG_WHITELIST) { |
246
|
|
|
|
|
|
|
use Data::Dumper; print Dumper($self->{whitelist}->{whitelist}); |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# create object |
250
|
|
|
|
|
|
|
bless $self, $package; |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# read & parse filter XML |
253
|
|
|
|
|
|
|
if (!$self->_load_filters_from_xml($self->{filters_file})) { |
254
|
|
|
|
|
|
|
croak "No IDS filter rules loaded!"; |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
return $self; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
#****m* IDS/detect_attacks |
261
|
|
|
|
|
|
|
# NAME |
262
|
|
|
|
|
|
|
# detect_attacks |
263
|
|
|
|
|
|
|
# DESCRIPTION |
264
|
|
|
|
|
|
|
# Parses a hashref (e.g. $query->Vars) for detection of possible attacks. |
265
|
|
|
|
|
|
|
# The attack array is emptied at the start of each run. |
266
|
|
|
|
|
|
|
# INPUT |
267
|
|
|
|
|
|
|
# +request hashref to be parsed |
268
|
|
|
|
|
|
|
# OUTPUT |
269
|
|
|
|
|
|
|
# Impact if filter matched, 0 otherwise |
270
|
|
|
|
|
|
|
# SYNOPSIS |
271
|
|
|
|
|
|
|
# $ids->detect_attacks(request => $query->Vars); |
272
|
|
|
|
|
|
|
#**** |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=head2 detect_attacks() |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
DESCRIPTION |
277
|
|
|
|
|
|
|
Parses a hashref (e.g. $query->Vars) for detection of possible attacks. |
278
|
|
|
|
|
|
|
The attack array is emptied at the start of each run. |
279
|
|
|
|
|
|
|
INPUT |
280
|
|
|
|
|
|
|
+request hashref to be parsed |
281
|
|
|
|
|
|
|
OUTPUT |
282
|
|
|
|
|
|
|
Impact if filter matched, 0 otherwise |
283
|
|
|
|
|
|
|
SYNOPSIS |
284
|
|
|
|
|
|
|
$ids->detect_attacks(request => $query->Vars); |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=cut |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub detect_attacks { |
289
|
|
|
|
|
|
|
my ($self, %args) = @_; |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
return 0 unless ($args{request}); |
292
|
|
|
|
|
|
|
my $request = $args{request}; |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# reset last detection data |
295
|
|
|
|
|
|
|
$self->{impact} = 0; |
296
|
|
|
|
|
|
|
$self->{attacks} = []; |
297
|
|
|
|
|
|
|
$self->{filtered_keys} = []; |
298
|
|
|
|
|
|
|
$self->{non_filtered_keys} = []; |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
my @request_keys = keys %$request; |
301
|
|
|
|
|
|
|
# sorting for filter debugging only |
302
|
|
|
|
|
|
|
if (DEBUG_MODE & DEBUG_SORT_KEYS_ALPHA) { |
303
|
|
|
|
|
|
|
@request_keys = sort {$a cmp $b} @request_keys; |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
elsif (DEBUG_MODE & DEBUG_SORT_KEYS_NUM) { |
306
|
|
|
|
|
|
|
@request_keys = sort {$a <=> $b} @request_keys; |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
foreach my $key (@request_keys) { |
310
|
|
|
|
|
|
|
my $filter_impact = 0; |
311
|
|
|
|
|
|
|
my $key_converted = ''; |
312
|
|
|
|
|
|
|
my $value_converted = ''; |
313
|
|
|
|
|
|
|
my $time_ms = 0; |
314
|
|
|
|
|
|
|
my @matched_filters = (); |
315
|
|
|
|
|
|
|
my @matched_tags = (); |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
my $request_value = defined $request->{$key} ? $request->{$key} : ''; |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
if (DEBUG_MODE & DEBUG_KEY_VALUES) { |
320
|
|
|
|
|
|
|
print "\n\n\n******************************************\n". |
321
|
|
|
|
|
|
|
"Key : $key\nValue : $request_value\n"; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
if ($self->{whitelist}->is_suspicious(key => $key, request => $request)) { |
325
|
|
|
|
|
|
|
$request_value = $self->{whitelist}->convert_if_marked_encoded(key => $key, value => $request_value); |
326
|
|
|
|
|
|
|
my $attacks = $self->_apply_filters($request_value); |
327
|
|
|
|
|
|
|
if ($attacks->{impact}) { |
328
|
|
|
|
|
|
|
$filter_impact += $attacks->{impact}; |
329
|
|
|
|
|
|
|
$time_ms += $attacks->{time_ms}; |
330
|
|
|
|
|
|
|
$value_converted = $attacks->{string_converted}; |
331
|
|
|
|
|
|
|
push (@matched_filters, @{$attacks->{filters}}); |
332
|
|
|
|
|
|
|
push (@matched_tags, @{$attacks->{tags}}); |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# scan key only if desired |
337
|
|
|
|
|
|
|
if ($self->{scan_keys}) { |
338
|
|
|
|
|
|
|
# scan only if value is not harmless |
339
|
|
|
|
|
|
|
if ( !$self->{whitelist}->is_harmless_string($key) ) { |
340
|
|
|
|
|
|
|
# apply filters to key |
341
|
|
|
|
|
|
|
my $attacks = $self->_apply_filters($key); |
342
|
|
|
|
|
|
|
$filter_impact += $attacks->{impact}; |
343
|
|
|
|
|
|
|
$time_ms += $attacks->{time_ms}; |
344
|
|
|
|
|
|
|
$key_converted = $attacks->{string_converted}; |
345
|
|
|
|
|
|
|
push (@matched_filters, @{$attacks->{filters}}); |
346
|
|
|
|
|
|
|
push (@matched_tags, @{$attacks->{tags}}); |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
else { |
349
|
|
|
|
|
|
|
# skipped, alphanumeric key only |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
# add attack to log |
354
|
|
|
|
|
|
|
my %attack = (); |
355
|
|
|
|
|
|
|
if ($filter_impact) { |
356
|
|
|
|
|
|
|
# make arrays unique and sorted |
357
|
|
|
|
|
|
|
my %seen = (); |
358
|
|
|
|
|
|
|
@matched_filters = sort grep { ! $seen{$_} ++ } @matched_filters; |
359
|
|
|
|
|
|
|
%seen = (); |
360
|
|
|
|
|
|
|
@matched_tags = sort grep { ! $seen{$_} ++ } @matched_tags; |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
%attack = ( |
363
|
|
|
|
|
|
|
key => $key, |
364
|
|
|
|
|
|
|
key_converted => $key_converted, |
365
|
|
|
|
|
|
|
value => $request_value, |
366
|
|
|
|
|
|
|
value_converted => $value_converted, |
367
|
|
|
|
|
|
|
time_ms => $time_ms, |
368
|
|
|
|
|
|
|
impact => $filter_impact, |
369
|
|
|
|
|
|
|
matched_filters => \@matched_filters, |
370
|
|
|
|
|
|
|
matched_tags => \@matched_tags, |
371
|
|
|
|
|
|
|
); |
372
|
|
|
|
|
|
|
push (@{$self->{attacks}}, \%attack); |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
$self->{impact} += $filter_impact; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
if (DEBUG_MODE & DEBUG_ARRAY_INFO && %attack) { |
377
|
|
|
|
|
|
|
use Data::Dumper; |
378
|
|
|
|
|
|
|
print "------------------------------------------\n". |
379
|
|
|
|
|
|
|
Dumper(\%attack) . |
380
|
|
|
|
|
|
|
"\n\n"; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
if (DEBUG_MODE & DEBUG_MATCHED_FILTERS && @matched_filters) { |
384
|
|
|
|
|
|
|
my $filters_concat = join ", ", @matched_filters; |
385
|
|
|
|
|
|
|
print "Filters: $filters_concat\n"; |
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
if (DEBUG_MODE & DEBUG_IMPACTS) { |
389
|
|
|
|
|
|
|
print "Impact : $filter_impact\n"; |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
} # end of foreach key |
393
|
|
|
|
|
|
|
push (@{$self->{filtered_keys}}, @{$self->{whitelist}->suspicious_keys()}); |
394
|
|
|
|
|
|
|
push (@{$self->{non_filtered_keys}}, @{$self->{whitelist}->non_suspicious_keys()}); |
395
|
|
|
|
|
|
|
# reset filtered_keys and non_filtered_keys |
396
|
|
|
|
|
|
|
$self->{whitelist}->reset(); |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
if ($self->{impact} > 0) { |
399
|
|
|
|
|
|
|
return $self->{impact}; |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
else { |
402
|
|
|
|
|
|
|
return 0; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
#****m* IDS/set_scan_keys |
407
|
|
|
|
|
|
|
# NAME |
408
|
|
|
|
|
|
|
# set_scan_keys |
409
|
|
|
|
|
|
|
# DESCRIPTION |
410
|
|
|
|
|
|
|
# Sets key scanning option |
411
|
|
|
|
|
|
|
# INPUT |
412
|
|
|
|
|
|
|
# +scan_keys 1 to scan keys, 0 to switch off scanning keys, defaults to 0 |
413
|
|
|
|
|
|
|
# OUTPUT |
414
|
|
|
|
|
|
|
# none |
415
|
|
|
|
|
|
|
# SYNOPSIS |
416
|
|
|
|
|
|
|
# $ids->set_scan_keys(scan_keys => 1); |
417
|
|
|
|
|
|
|
#**** |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
=head2 set_scan_keys() |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
DESCRIPTION |
422
|
|
|
|
|
|
|
Sets key scanning option |
423
|
|
|
|
|
|
|
INPUT |
424
|
|
|
|
|
|
|
+scan_keys 1 to scan keys, 0 to switch off scanning keys, defaults to 0 |
425
|
|
|
|
|
|
|
OUTPUT |
426
|
|
|
|
|
|
|
none |
427
|
|
|
|
|
|
|
SYNOPSIS |
428
|
|
|
|
|
|
|
$ids->set_scan_keys(scan_keys => 1); |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=cut |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
sub set_scan_keys { |
433
|
|
|
|
|
|
|
my ($self, %args) = @_; |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
$self->{scan_keys} = $args{scan_keys} ? 1 : 0; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
#****m* IDS/get_attacks |
439
|
|
|
|
|
|
|
# NAME |
440
|
|
|
|
|
|
|
# get_attacks |
441
|
|
|
|
|
|
|
# DESCRIPTION |
442
|
|
|
|
|
|
|
# Get an key/value/impact array of all detected attacks. |
443
|
|
|
|
|
|
|
# The array is emptied at the start of each run of detect_attacks(). |
444
|
|
|
|
|
|
|
# INPUT |
445
|
|
|
|
|
|
|
# none |
446
|
|
|
|
|
|
|
# OUTPUT |
447
|
|
|
|
|
|
|
# HASHREF ( |
448
|
|
|
|
|
|
|
# key => '', |
449
|
|
|
|
|
|
|
# value => '', |
450
|
|
|
|
|
|
|
# impact => n, |
451
|
|
|
|
|
|
|
# filters => (n, n, n, n, ...), |
452
|
|
|
|
|
|
|
# tags => ('', '', '', '', ...), |
453
|
|
|
|
|
|
|
# ) |
454
|
|
|
|
|
|
|
# SYNOPSIS |
455
|
|
|
|
|
|
|
# $ids->get_attacks(); |
456
|
|
|
|
|
|
|
#**** |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=head2 get_attacks() |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
DESCRIPTION |
461
|
|
|
|
|
|
|
Get an key/value/impact array of all detected attacks. |
462
|
|
|
|
|
|
|
The array is emptied at the start of each run of C. |
463
|
|
|
|
|
|
|
INPUT |
464
|
|
|
|
|
|
|
none |
465
|
|
|
|
|
|
|
OUTPUT |
466
|
|
|
|
|
|
|
ARRAY ( |
467
|
|
|
|
|
|
|
key => '', |
468
|
|
|
|
|
|
|
value => '', |
469
|
|
|
|
|
|
|
impact => n, |
470
|
|
|
|
|
|
|
filters => (n, n, n, n, ...), |
471
|
|
|
|
|
|
|
tags => ('', '', '', '', ...), |
472
|
|
|
|
|
|
|
) |
473
|
|
|
|
|
|
|
SYNOPSIS |
474
|
|
|
|
|
|
|
$ids->get_attacks(); |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
=cut |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
sub get_attacks { |
479
|
|
|
|
|
|
|
my ($self) = @_; |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
return $self->{attacks}; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
#****m* IDS/get_rule_description |
485
|
|
|
|
|
|
|
# NAME |
486
|
|
|
|
|
|
|
# get_rule_description |
487
|
|
|
|
|
|
|
# DESCRIPTION |
488
|
|
|
|
|
|
|
# This sub returns the rule description for a given rule id. This can be used for logging purposes. |
489
|
|
|
|
|
|
|
# INPUT |
490
|
|
|
|
|
|
|
# HASH |
491
|
|
|
|
|
|
|
# + rule_id id of rule |
492
|
|
|
|
|
|
|
# OUTPUT |
493
|
|
|
|
|
|
|
# SCALAR description |
494
|
|
|
|
|
|
|
# EXAMPLE |
495
|
|
|
|
|
|
|
# $ids->get_rule_description( rule_id => $rule_id ); |
496
|
|
|
|
|
|
|
#**** |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=head2 get_rule_description() |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
DESCRIPTION |
501
|
|
|
|
|
|
|
Returns the rule description for a given rule id. This can be used for logging purposes. |
502
|
|
|
|
|
|
|
INPUT |
503
|
|
|
|
|
|
|
HASH |
504
|
|
|
|
|
|
|
+ rule_id id of rule |
505
|
|
|
|
|
|
|
OUTPUT |
506
|
|
|
|
|
|
|
SCALAR description |
507
|
|
|
|
|
|
|
EXAMPLE |
508
|
|
|
|
|
|
|
$ids->get_rule_description( rule_id => $rule_id ); |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
=cut |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
sub get_rule_description { |
513
|
|
|
|
|
|
|
my ($self, %args) = @_; |
514
|
|
|
|
|
|
|
return $self->{rule_descriptions}{$args{rule_id}}; |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
#****im* IDS/_apply_filters |
518
|
|
|
|
|
|
|
# NAME |
519
|
|
|
|
|
|
|
# _apply_filters |
520
|
|
|
|
|
|
|
# DESCRIPTION |
521
|
|
|
|
|
|
|
# Applies filter rules to a string to detect attacks |
522
|
|
|
|
|
|
|
# INPUT |
523
|
|
|
|
|
|
|
# + $string string to be checked for possible attacks |
524
|
|
|
|
|
|
|
# OUTPUT |
525
|
|
|
|
|
|
|
# attack hashref: |
526
|
|
|
|
|
|
|
# ( |
527
|
|
|
|
|
|
|
# impact => n, |
528
|
|
|
|
|
|
|
# filters => (n, n, n, ...), |
529
|
|
|
|
|
|
|
# tags => ('', '', '', ...), |
530
|
|
|
|
|
|
|
# string_converted => string |
531
|
|
|
|
|
|
|
# ) |
532
|
|
|
|
|
|
|
# SYNOPSIS |
533
|
|
|
|
|
|
|
# IDS::_apply_filters($string); |
534
|
|
|
|
|
|
|
#**** |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
sub _apply_filters { |
537
|
|
|
|
|
|
|
my ($self, $string) = @_; |
538
|
|
|
|
|
|
|
my %attack = ( |
539
|
|
|
|
|
|
|
filters => [], |
540
|
|
|
|
|
|
|
tags => [], |
541
|
|
|
|
|
|
|
impact => 0, |
542
|
|
|
|
|
|
|
string_converted => '', |
543
|
|
|
|
|
|
|
); |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
# benchmark |
546
|
|
|
|
|
|
|
my $start_time = Time::HiRes::time(); |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# make UTF-8 and sanitize from malformated UTF-8, if necessary |
549
|
|
|
|
|
|
|
$string = $self->{whitelist}->make_utf_8($string); |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
# run all string converters |
552
|
|
|
|
|
|
|
$attack{string_converted} = _run_all_converters($string); |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
# apply filters |
555
|
|
|
|
|
|
|
foreach my $filter (@{$self->{filters}}) { |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
# skip disabled filters |
558
|
|
|
|
|
|
|
next if ($self->{filter_disabled}{$filter->{id}}); |
559
|
|
|
|
|
|
|
my $string_converted_lc = lc($attack{string_converted}); |
560
|
|
|
|
|
|
|
if ($string_converted_lc =~ $filter->{rule}) { |
561
|
|
|
|
|
|
|
$attack{impact} += $filter->{impact}; |
562
|
|
|
|
|
|
|
push (@{$attack{filters}}, $filter->{id}); |
563
|
|
|
|
|
|
|
push (@{$attack{tags}}, @{$filter->{tags}}); |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
# benchmark |
568
|
|
|
|
|
|
|
my $end_time = Time::HiRes::time(); |
569
|
|
|
|
|
|
|
$attack{time_ms} = int(($end_time-$start_time)*1000); |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
return \%attack; |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
#****im* IDS/_load_filters_from_xml |
575
|
|
|
|
|
|
|
# NAME |
576
|
|
|
|
|
|
|
# _load_filters_from_xml |
577
|
|
|
|
|
|
|
# DESCRIPTION |
578
|
|
|
|
|
|
|
# loads the filters from PHPIDS filter XML file |
579
|
|
|
|
|
|
|
# INPUT |
580
|
|
|
|
|
|
|
# filterfile path + name of the XML filter file |
581
|
|
|
|
|
|
|
# OUTPUT |
582
|
|
|
|
|
|
|
# filtercount number of loaded filters |
583
|
|
|
|
|
|
|
# SYNOPSIS |
584
|
|
|
|
|
|
|
# IDS::_load_filters_from_xml('/home/xyz/default_filter.xml'); |
585
|
|
|
|
|
|
|
#**** |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
sub _load_filters_from_xml { |
588
|
|
|
|
|
|
|
my ($self, $filterfile) = @_; |
589
|
|
|
|
|
|
|
my $filtercnt = 0; |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
if ($filterfile) { |
592
|
|
|
|
|
|
|
# read & parse filter XML |
593
|
|
|
|
|
|
|
my $filterxml; |
594
|
|
|
|
|
|
|
eval { |
595
|
|
|
|
|
|
|
$filterxml = XML::Simple::XMLin($filterfile, |
596
|
|
|
|
|
|
|
forcearray => [ qw(rule description tags tag impact filter filters)], |
597
|
|
|
|
|
|
|
keyattr => [], |
598
|
|
|
|
|
|
|
); |
599
|
|
|
|
|
|
|
}; |
600
|
|
|
|
|
|
|
if ($@) { |
601
|
|
|
|
|
|
|
croak "Error in _load_filters_from_xml while parsing $filterfile: $@"; |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
# convert XML structure into handy data structure |
605
|
|
|
|
|
|
|
foreach my $filterobj (@{$filterxml->{filter}}) { |
606
|
|
|
|
|
|
|
my @taglist = (); |
607
|
|
|
|
|
|
|
foreach my $tag (@{$filterobj->{tags}[0]->{tag}}) { |
608
|
|
|
|
|
|
|
push(@taglist, $tag); |
609
|
|
|
|
|
|
|
} |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
my $rule = ''; |
612
|
|
|
|
|
|
|
eval { |
613
|
|
|
|
|
|
|
$rule = qr/$filterobj->{rule}[0]/ms; |
614
|
|
|
|
|
|
|
}; |
615
|
|
|
|
|
|
|
if ($@) { |
616
|
|
|
|
|
|
|
croak 'Error in filter rule #' . $filterobj->{id} . ': ' . $filterobj->{rule}[0] . ' Message: ' . $@; |
617
|
|
|
|
|
|
|
} |
618
|
|
|
|
|
|
|
my %filterhash = ( |
619
|
|
|
|
|
|
|
rule => $rule, |
620
|
|
|
|
|
|
|
impact => $filterobj->{impact}[0], |
621
|
|
|
|
|
|
|
id => $filterobj->{id}, |
622
|
|
|
|
|
|
|
tags => \@taglist, |
623
|
|
|
|
|
|
|
); |
624
|
|
|
|
|
|
|
push (@{$self->{filters}}, \%filterhash); |
625
|
|
|
|
|
|
|
$self->{rule_descriptions}{$filterobj->{id}} = $filterobj->{description}[0]; |
626
|
|
|
|
|
|
|
$filtercnt++ |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
return $filtercnt; |
630
|
|
|
|
|
|
|
} |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
#****if* IDS/_run_all_converters |
633
|
|
|
|
|
|
|
# NAME |
634
|
|
|
|
|
|
|
# _run_all_converters |
635
|
|
|
|
|
|
|
# DESCRIPTION |
636
|
|
|
|
|
|
|
# Runs all converter functions |
637
|
|
|
|
|
|
|
# INPUT |
638
|
|
|
|
|
|
|
# value the string to convert |
639
|
|
|
|
|
|
|
# OUTPUT |
640
|
|
|
|
|
|
|
# value converted string |
641
|
|
|
|
|
|
|
# SYNOPSIS |
642
|
|
|
|
|
|
|
# IDS::_run_all_converters($value); |
643
|
|
|
|
|
|
|
#**** |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
sub _run_all_converters { |
646
|
|
|
|
|
|
|
my ($value) = @_; |
647
|
|
|
|
|
|
|
if (DEBUG_MODE & DEBUG_CONVERTERS) { |
648
|
|
|
|
|
|
|
print "------------------------------------------\n\n"; |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
foreach my $converter (@CONVERTERS) { |
652
|
|
|
|
|
|
|
no strict 'refs'; |
653
|
|
|
|
|
|
|
$value = $converter->($value); |
654
|
|
|
|
|
|
|
if (DEBUG_MODE & DEBUG_CONVERTERS) { |
655
|
|
|
|
|
|
|
print "$converter output:\n$value\n\n"; |
656
|
|
|
|
|
|
|
} |
657
|
|
|
|
|
|
|
} |
658
|
|
|
|
|
|
|
return $value; |
659
|
|
|
|
|
|
|
} |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
#****if* IDS/_convert_from_repetition |
662
|
|
|
|
|
|
|
# NAME |
663
|
|
|
|
|
|
|
# _convert_from_repetition |
664
|
|
|
|
|
|
|
# DESCRIPTION |
665
|
|
|
|
|
|
|
# Make sure the value to normalize and monitor doesn't contain |
666
|
|
|
|
|
|
|
# possibilities for a regex DoS. |
667
|
|
|
|
|
|
|
# INPUT |
668
|
|
|
|
|
|
|
# value the value to pre-sanitize |
669
|
|
|
|
|
|
|
# OUTPUT |
670
|
|
|
|
|
|
|
# value converted string |
671
|
|
|
|
|
|
|
# SYNOPSIS |
672
|
|
|
|
|
|
|
# IDS::_convert_from_repetition($value); |
673
|
|
|
|
|
|
|
#**** |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
sub _convert_from_repetition { |
676
|
|
|
|
|
|
|
my ($value) = @_; |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
# remove obvios repetition patterns |
679
|
|
|
|
|
|
|
$value = preg_replace( |
680
|
|
|
|
|
|
|
qr/(?:(.{2,})\1{32,})|(?:[+=|\-@\s]{128,})/, |
681
|
|
|
|
|
|
|
'x', |
682
|
|
|
|
|
|
|
$value |
683
|
|
|
|
|
|
|
); |
684
|
|
|
|
|
|
|
return $value; |
685
|
|
|
|
|
|
|
} |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
#****if* IDS/_convert_from_commented |
688
|
|
|
|
|
|
|
# NAME |
689
|
|
|
|
|
|
|
# _convert_from_commented |
690
|
|
|
|
|
|
|
# DESCRIPTION |
691
|
|
|
|
|
|
|
# Check for comments and erases them if available |
692
|
|
|
|
|
|
|
# INPUT |
693
|
|
|
|
|
|
|
# value the string to convert |
694
|
|
|
|
|
|
|
# OUTPUT |
695
|
|
|
|
|
|
|
# value converted string |
696
|
|
|
|
|
|
|
# SYNOPSIS |
697
|
|
|
|
|
|
|
# IDS::_convert_from_commented($value); |
698
|
|
|
|
|
|
|
#**** |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
sub _convert_from_commented { |
701
|
|
|
|
|
|
|
my ($value) = @_; |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
# check for existing comments |
704
|
|
|
|
|
|
|
if (preg_match(qr/(?:\|\/\*|\*\/|\/\/\W*\w+\s*$)|(?:--[^-]*-)/ms, $value)) { #/ |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
my @pattern = ( |
707
|
|
|
|
|
|
|
qr/(?:(?:))/ms, |
708
|
|
|
|
|
|
|
qr/(?:(?:\/\*\/*[^\/\*]*)+\*\/)/ms, |
709
|
|
|
|
|
|
|
qr/(?:--[^-]*-)/ms, |
710
|
|
|
|
|
|
|
); |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
my $converted = preg_replace(\@pattern, ';', $value); |
713
|
|
|
|
|
|
|
$value .= "\n" . $converted; |
714
|
|
|
|
|
|
|
} |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
# make sure inline comments are detected and converted correctly |
717
|
|
|
|
|
|
|
$value = preg_replace(qr/(<\w+)\/+(\w+=?)/m, '$1/$2', $value); |
718
|
|
|
|
|
|
|
$value = preg_replace(qr/[^\\:]\/\/(.*)$/m, '/**/$1', $value); |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
return $value; |
721
|
|
|
|
|
|
|
} |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
#****if* IDS/_convert_from_whitespace |
724
|
|
|
|
|
|
|
# NAME |
725
|
|
|
|
|
|
|
# _convert_from_whitespace |
726
|
|
|
|
|
|
|
# DESCRIPTION |
727
|
|
|
|
|
|
|
# Strip newlines |
728
|
|
|
|
|
|
|
# INPUT |
729
|
|
|
|
|
|
|
# value the string to convert |
730
|
|
|
|
|
|
|
# OUTPUT |
731
|
|
|
|
|
|
|
# value converted string |
732
|
|
|
|
|
|
|
# SYNOPSIS |
733
|
|
|
|
|
|
|
# IDS::_convert_from_whitespace($value); |
734
|
|
|
|
|
|
|
#**** |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
sub _convert_from_whitespace { |
737
|
|
|
|
|
|
|
my ($value) = @_; |
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
# check for inline linebreaks |
740
|
|
|
|
|
|
|
my @search = ('\r', '\n', '\f', '\t', '\v'); |
741
|
|
|
|
|
|
|
$value = str_replace(\@search, ';', $value); |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
# replace replacement characters regular spaces |
744
|
|
|
|
|
|
|
$value = str_replace('�', ' ', $value); |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
# convert real linebreaks (\013 in Perl instead of \v in PHP et al.) |
747
|
|
|
|
|
|
|
return preg_replace(qr/(?:\n|\r|\013)/m, ' ', $value); |
748
|
|
|
|
|
|
|
} |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
#****if* IDS/_convert_from_js_charcode |
751
|
|
|
|
|
|
|
# NAME |
752
|
|
|
|
|
|
|
# _convert_from_js_charcode |
753
|
|
|
|
|
|
|
# DESCRIPTION |
754
|
|
|
|
|
|
|
# Checks for common charcode pattern and decodes them |
755
|
|
|
|
|
|
|
# INPUT |
756
|
|
|
|
|
|
|
# value the string to convert |
757
|
|
|
|
|
|
|
# OUTPUT |
758
|
|
|
|
|
|
|
# value converted string |
759
|
|
|
|
|
|
|
# SYNOPSIS |
760
|
|
|
|
|
|
|
# IDS::_convert_from_js_charcode($value); |
761
|
|
|
|
|
|
|
#**** |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
sub _convert_from_js_charcode { |
764
|
|
|
|
|
|
|
my ($value) = @_; |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
my @matches = (); |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
# check if value matches typical charCode pattern |
769
|
|
|
|
|
|
|
# PHP to Perl note: additional parenthesis around RegEx for getting PHP's $matches[0] |
770
|
|
|
|
|
|
|
if (preg_match_all(qr/(?:[\d+-=\/\* ]+(?:\s?,\s?[\d+-=\/\* ]+)){4,}/ms, |
771
|
|
|
|
|
|
|
$value, \@matches)) { |
772
|
|
|
|
|
|
|
my $converted = ''; |
773
|
|
|
|
|
|
|
my $string = implode(',', $matches[0]); |
774
|
|
|
|
|
|
|
$string = preg_replace(qr/\s/, '', $string); |
775
|
|
|
|
|
|
|
$string = preg_replace(qr/\w+=/, '', $string); |
776
|
|
|
|
|
|
|
my @charcode = explode(',', $string); |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
foreach my $char (@charcode) { |
779
|
|
|
|
|
|
|
$char = preg_replace(qr/\W0/s, '', $char); |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
my @matches = (); |
782
|
|
|
|
|
|
|
# PHP to Perl note: additional parenthesis around RegEx for getting PHP's $matches[0] |
783
|
|
|
|
|
|
|
if (preg_match_all(qr/(\d*[+-\/\* ]\d+)/, $char, \@matches)) { |
784
|
|
|
|
|
|
|
my @match = split(qr/(\W?\d+)/, |
785
|
|
|
|
|
|
|
(implode('', $matches[0])), |
786
|
|
|
|
|
|
|
# null, |
787
|
|
|
|
|
|
|
# PREG_SPLIT_DELIM_CAPTURE |
788
|
|
|
|
|
|
|
); |
789
|
|
|
|
|
|
|
# 3rd argument null, 4th argument PREG_SPLIT_DELIM_CAPTURE is default in Perl and not there |
790
|
|
|
|
|
|
|
my $test = implode('', $matches[0]); |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
if (array_sum(@match) >= 20 && array_sum(@match) <= 127) { |
793
|
|
|
|
|
|
|
$converted .= chr(array_sum(@match)); |
794
|
|
|
|
|
|
|
} |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
} |
797
|
|
|
|
|
|
|
elsif ($char && $char >= 20 && $char <= 127) { |
798
|
|
|
|
|
|
|
$converted .= chr($char); |
799
|
|
|
|
|
|
|
} |
800
|
|
|
|
|
|
|
} |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
$value .= "\n" . $converted; |
803
|
|
|
|
|
|
|
} |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
# check for octal charcode pattern |
806
|
|
|
|
|
|
|
# PHP to Perl note: \\ in Perl instead of \\\ in PHP |
807
|
|
|
|
|
|
|
# PHP to Perl note: additional parenthesis around RegEx for getting PHP's $matches[0] |
808
|
|
|
|
|
|
|
if (preg_match_all(qr/((?:(?:[\\]+\d+\s*){8,}))/ms, $value, \@matches)) { |
809
|
|
|
|
|
|
|
my $converted = ''; |
810
|
|
|
|
|
|
|
my @charcode = explode('\\', preg_replace(qr/\s/, '', implode(',', |
811
|
|
|
|
|
|
|
$matches[0]))); |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
foreach my $char (@charcode) { |
814
|
|
|
|
|
|
|
if ($char) { |
815
|
|
|
|
|
|
|
if (oct($char) >= 20 && oct($char) <= 127) { |
816
|
|
|
|
|
|
|
$converted .= chr(oct($char)); |
817
|
|
|
|
|
|
|
} |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
} |
820
|
|
|
|
|
|
|
$value .= "\n" . $converted; |
821
|
|
|
|
|
|
|
} |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
# check for hexadecimal charcode pattern |
824
|
|
|
|
|
|
|
# PHP to Perl note: \\ in Perl instead of \\\ in PHP |
825
|
|
|
|
|
|
|
# PHP to Perl note: additional parenthesis around RegEx for getting PHP's $matches[0] |
826
|
|
|
|
|
|
|
if (preg_match_all(qr/((?:(?:[\\]+\w+[ \t]*){8,}))/ms, $value, \@matches)) { |
827
|
|
|
|
|
|
|
my $converted = ''; |
828
|
|
|
|
|
|
|
my @charcode = explode('\\', preg_replace(qr/[ux]/, '', implode(',', |
829
|
|
|
|
|
|
|
$matches[0]))); |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
foreach my $char (@charcode) { |
832
|
|
|
|
|
|
|
if ($char) { |
833
|
|
|
|
|
|
|
if (hex($char) >= 20 && hex($char) <= 127) { |
834
|
|
|
|
|
|
|
$converted .= chr(hex($char)); |
835
|
|
|
|
|
|
|
} |
836
|
|
|
|
|
|
|
} |
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
$value .= "\n" . $converted; |
839
|
|
|
|
|
|
|
} |
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
return $value; |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
} |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
#****if* IDS/_convert_js_regex_modifiers |
846
|
|
|
|
|
|
|
# NAME |
847
|
|
|
|
|
|
|
# _convert_js_regex_modifiers |
848
|
|
|
|
|
|
|
# DESCRIPTION |
849
|
|
|
|
|
|
|
# Eliminate JS regex modifiers |
850
|
|
|
|
|
|
|
# INPUT |
851
|
|
|
|
|
|
|
# value the string to convert |
852
|
|
|
|
|
|
|
# OUTPUT |
853
|
|
|
|
|
|
|
# value converted string |
854
|
|
|
|
|
|
|
# SYNOPSIS |
855
|
|
|
|
|
|
|
# IDS::_convert_js_regex_modifiers($value); |
856
|
|
|
|
|
|
|
#**** |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
sub _convert_js_regex_modifiers { |
859
|
|
|
|
|
|
|
my ($value) = @_; |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
$value = preg_replace(qr/\/[gim]+/, '/', $value); |
862
|
|
|
|
|
|
|
return $value; |
863
|
|
|
|
|
|
|
} |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
#****if* IDS/_convert_quotes |
866
|
|
|
|
|
|
|
# NAME |
867
|
|
|
|
|
|
|
# _convert_quotes |
868
|
|
|
|
|
|
|
# DESCRIPTION |
869
|
|
|
|
|
|
|
# Normalize quotes |
870
|
|
|
|
|
|
|
# INPUT |
871
|
|
|
|
|
|
|
# value the string to convert |
872
|
|
|
|
|
|
|
# OUTPUT |
873
|
|
|
|
|
|
|
# value converted string |
874
|
|
|
|
|
|
|
# SYNOPSIS |
875
|
|
|
|
|
|
|
# IDS::_convert_quotes($value); |
876
|
|
|
|
|
|
|
#**** |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
sub _convert_quotes { |
879
|
|
|
|
|
|
|
my ($value) = @_; |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
# normalize different quotes to " |
882
|
|
|
|
|
|
|
my @pattern = ('\'', '`', '´', '’', '‘'); |
883
|
|
|
|
|
|
|
$value = str_replace(\@pattern, '"', $value); |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
# make sure harmless quoted strings don't generate false alerts |
886
|
|
|
|
|
|
|
$value = preg_replace(qr/^"([^"=\\!><~]+)"$/, '$1', $value); |
887
|
|
|
|
|
|
|
return $value; |
888
|
|
|
|
|
|
|
} |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
#****if* IDS/_convert_from_sql_hex |
891
|
|
|
|
|
|
|
# NAME |
892
|
|
|
|
|
|
|
# _convert_from_sql_hex |
893
|
|
|
|
|
|
|
# DESCRIPTION |
894
|
|
|
|
|
|
|
# Converts SQLHEX to plain text |
895
|
|
|
|
|
|
|
# INPUT |
896
|
|
|
|
|
|
|
# value the string to convert |
897
|
|
|
|
|
|
|
# OUTPUT |
898
|
|
|
|
|
|
|
# value converted string |
899
|
|
|
|
|
|
|
# SYNOPSIS |
900
|
|
|
|
|
|
|
# IDS::_convert_from_sql_hex($value); |
901
|
|
|
|
|
|
|
#**** |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
sub _convert_from_sql_hex { |
904
|
|
|
|
|
|
|
my ($value) = @_; |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
my @matches = (); |
907
|
|
|
|
|
|
|
# PHP to Perl note: additional parenthesis around RegEx for getting PHP's $matches[0] |
908
|
|
|
|
|
|
|
if(preg_match_all(qr/((?:0x[a-f\d]{2,}[a-f\d]*)+)/im, $value, \@matches)) { |
909
|
|
|
|
|
|
|
foreach my $match ($matches[0]) { |
910
|
|
|
|
|
|
|
my $converted = ''; |
911
|
|
|
|
|
|
|
foreach my $hex_index (str_split($match, 2)) { |
912
|
|
|
|
|
|
|
if(preg_match(qr/[a-f\d]{2,3}/i, $hex_index)) { |
913
|
|
|
|
|
|
|
$converted .= chr(hex($hex_index)); |
914
|
|
|
|
|
|
|
} |
915
|
|
|
|
|
|
|
} |
916
|
|
|
|
|
|
|
$value = str_replace($match, $converted, $value); |
917
|
|
|
|
|
|
|
} |
918
|
|
|
|
|
|
|
} |
919
|
|
|
|
|
|
|
# take care of hex encoded ctrl chars |
920
|
|
|
|
|
|
|
$value = preg_replace('/0x\d+/m', 1, $value); |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
return $value; |
923
|
|
|
|
|
|
|
} |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
#****if* IDS/_convert_from_sql_keywords |
926
|
|
|
|
|
|
|
# NAME |
927
|
|
|
|
|
|
|
# _convert_from_sql_keywords |
928
|
|
|
|
|
|
|
# DESCRIPTION |
929
|
|
|
|
|
|
|
# Converts basic SQL keywords and obfuscations |
930
|
|
|
|
|
|
|
# INPUT |
931
|
|
|
|
|
|
|
# value the string to convert |
932
|
|
|
|
|
|
|
# OUTPUT |
933
|
|
|
|
|
|
|
# value converted string |
934
|
|
|
|
|
|
|
# SYNOPSIS |
935
|
|
|
|
|
|
|
# IDS::_convert_from_sql_keywords($value); |
936
|
|
|
|
|
|
|
#**** |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
sub _convert_from_sql_keywords { |
939
|
|
|
|
|
|
|
my ($value) = @_; |
940
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
my $pattern = qr/(?:IS\s+null)|(LIKE\s+null)|(?:(?:^|\W)IN[+\s]*\([\s\d"]+[^()]*\))/ims; |
942
|
|
|
|
|
|
|
$value = preg_replace($pattern, '"=0', $value); |
943
|
|
|
|
|
|
|
$value = preg_replace(qr/\W+\s*like\s*\W+/ims, '1" OR "1"', $value); |
944
|
|
|
|
|
|
|
$value = preg_replace(qr/null[,"\s]/ims, ',0', $value); |
945
|
|
|
|
|
|
|
$value = preg_replace(qr/\d+\./ims, ' 1', $value); |
946
|
|
|
|
|
|
|
$value = preg_replace(qr/,null/ims, ',0', $value); |
947
|
|
|
|
|
|
|
$value = preg_replace(qr/(?:between|mod)/ims, 'or', $value); |
948
|
|
|
|
|
|
|
$value = preg_replace(qr/(?:and\s+\d+\.?\d*)/ims, '', $value); |
949
|
|
|
|
|
|
|
$value = preg_replace(qr/(?:\s+and\s+)/ims, ' or ', $value); |
950
|
|
|
|
|
|
|
# \\N instead of PHP's \\\N |
951
|
|
|
|
|
|
|
$pattern = qr/[^\w,\(]NULL|\\N|TRUE|FALSE|UTC_TIME|LOCALTIME(?:STAMP)?|CURRENT_\w+|BINARY|(?:(?:ASCII|SOUNDEX|FIND_IN_SET|MD5|R?LIKE)[+\s]*\([^()]+\))|(?:-+\d)/ims; |
952
|
|
|
|
|
|
|
$value = preg_replace($pattern, 0, $value); |
953
|
|
|
|
|
|
|
$pattern = qr/(?:NOT\s+BETWEEN)|(?:IS\s+NOT)|(?:NOT\s+IN)|(?:XOR|\WDIV\W|\WNOT\W|<>|RLIKE(?:\s+BINARY)?)|(?:REGEXP\s+BINARY)|(?:SOUNDS\s+LIKE)/ims; |
954
|
|
|
|
|
|
|
$value = preg_replace($pattern, '!', $value); |
955
|
|
|
|
|
|
|
$value = preg_replace(qr/"\s+\d/, '"', $value); |
956
|
|
|
|
|
|
|
$value = preg_replace(qr/\/(?:\d+|null)/, '', $value); |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
return $value; |
959
|
|
|
|
|
|
|
} |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
#****if* IDS/_convert_entities |
962
|
|
|
|
|
|
|
# NAME |
963
|
|
|
|
|
|
|
# _convert_entities |
964
|
|
|
|
|
|
|
# DESCRIPTION |
965
|
|
|
|
|
|
|
# Converts from hex/dec entities (use HTML::Entities;) |
966
|
|
|
|
|
|
|
# INPUT |
967
|
|
|
|
|
|
|
# value the string to convert |
968
|
|
|
|
|
|
|
# OUTPUT |
969
|
|
|
|
|
|
|
# value converted string |
970
|
|
|
|
|
|
|
# SYNOPSIS |
971
|
|
|
|
|
|
|
# IDS::_convert_entities($value); |
972
|
|
|
|
|
|
|
#**** |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
sub _convert_entities { |
975
|
|
|
|
|
|
|
my ($value) = @_; |
976
|
|
|
|
|
|
|
my $converted = ''; |
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
# deal with double encoded payload |
979
|
|
|
|
|
|
|
$value = preg_replace(qr/&/, '&', $value); |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
if (preg_match(qr/?[\w]+/ms, $value)) { |
982
|
|
|
|
|
|
|
$converted = preg_replace(qr/(?[\w]{2}\d?);?/ms, '$1;', $value); |
983
|
|
|
|
|
|
|
$converted = HTML::Entities::decode_entities($converted); |
984
|
|
|
|
|
|
|
$value .= "\n" . str_replace(';;', ';', $converted); |
985
|
|
|
|
|
|
|
} |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
# normalize obfuscated protocol handlers |
988
|
|
|
|
|
|
|
$value = preg_replace( |
989
|
|
|
|
|
|
|
'/(?:j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\s*)|(d\s*a\s*t\s*a\s*)/ms', |
990
|
|
|
|
|
|
|
'javascript', $value |
991
|
|
|
|
|
|
|
); |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
return $value; |
994
|
|
|
|
|
|
|
} |
995
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
#****if* IDS/_convert_from_control_chars |
997
|
|
|
|
|
|
|
# NAME |
998
|
|
|
|
|
|
|
# _convert_from_control_chars |
999
|
|
|
|
|
|
|
# DESCRIPTION |
1000
|
|
|
|
|
|
|
# Detects nullbytes and controls chars via ord() |
1001
|
|
|
|
|
|
|
# INPUT |
1002
|
|
|
|
|
|
|
# value the string to convert |
1003
|
|
|
|
|
|
|
# OUTPUT |
1004
|
|
|
|
|
|
|
# value converted string |
1005
|
|
|
|
|
|
|
# SYNOPSIS |
1006
|
|
|
|
|
|
|
# IDS::_convert_from_control_chars($value); |
1007
|
|
|
|
|
|
|
#**** |
1008
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
sub _convert_from_control_chars { |
1010
|
|
|
|
|
|
|
my ($value) = @_; |
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
# critical ctrl values |
1013
|
|
|
|
|
|
|
my @search = ( |
1014
|
|
|
|
|
|
|
chr(0), chr(1), chr(2), chr(3), chr(4), chr(5), |
1015
|
|
|
|
|
|
|
chr(6), chr(7), chr(8), chr(11), chr(12), chr(14), |
1016
|
|
|
|
|
|
|
chr(15), chr(16), chr(17), chr(18), chr(19), chr(24), |
1017
|
|
|
|
|
|
|
chr(25), chr(192), chr(193), chr(238), chr(255) |
1018
|
|
|
|
|
|
|
); |
1019
|
|
|
|
|
|
|
$value = str_replace(\@search, '%00', $value); |
1020
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
# take care for malicious unicode characters |
1022
|
|
|
|
|
|
|
$value = urldecode(preg_replace(qr/(?:%E(?:2|3)%8(?:0|1)%(?:A|8|9)\w|%EF%BB%BF|%EF%BF%BD)|(?:(?:65|8)\d{3};?)/i, '', |
1023
|
|
|
|
|
|
|
urlencode($value))); |
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
$value = urldecode( |
1026
|
|
|
|
|
|
|
preg_replace(qr/(?:%F0%80%BE)/i, '>', urlencode($value))); |
1027
|
|
|
|
|
|
|
$value = urldecode( |
1028
|
|
|
|
|
|
|
preg_replace(qr/(?:%F0%80%BC)/i, '<', urlencode($value))); |
1029
|
|
|
|
|
|
|
$value = urldecode( |
1030
|
|
|
|
|
|
|
preg_replace(qr/(?:%F0%80%A2)/i, '"', urlencode($value))); |
1031
|
|
|
|
|
|
|
$value = urldecode( |
1032
|
|
|
|
|
|
|
preg_replace(qr/(?:%F0%80%A7)/i, '\'', urlencode($value))); |
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
$value = preg_replace(qr/(?:%ff1c)/, '<', $value); |
1035
|
|
|
|
|
|
|
$value = preg_replace( |
1036
|
|
|
|
|
|
|
qr/(?:&[#x]*(200|820|200|820|zwn?j|lrm|rlm)\w?;?)/i, '', $value |
1037
|
|
|
|
|
|
|
); |
1038
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
$value = preg_replace(qr/(?:(?:65|8)\d{3};?)|(?:(?:56|7)3\d{2};?)|(?:(?:fe|20)\w{2};?)|(?:(?:d[c-f])\w{2};?)/i, '', |
1040
|
|
|
|
|
|
|
$value); |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
$value = str_replace( |
1043
|
|
|
|
|
|
|
["\x{ab}", "\x{3008}", "\x{ff1c}", "\x{2039}", "\x{2329}", "\x{27e8}"], '<', $value |
1044
|
|
|
|
|
|
|
); |
1045
|
|
|
|
|
|
|
$value = str_replace( |
1046
|
|
|
|
|
|
|
["\x{bb}", "\x{3009}", "\x{ff1e}", "\x{203a}", "\x{232a}", "\x{27e9}"], '>', $value |
1047
|
|
|
|
|
|
|
); |
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
return $value; |
1050
|
|
|
|
|
|
|
} |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
#****if* IDS/_convert_from_nested_base64 |
1053
|
|
|
|
|
|
|
# NAME |
1054
|
|
|
|
|
|
|
# _convert_from_nested_base64 |
1055
|
|
|
|
|
|
|
# DESCRIPTION |
1056
|
|
|
|
|
|
|
# Matches and translates base64 strings and fragments used in data URIs (use MIME::Base64;) |
1057
|
|
|
|
|
|
|
# INPUT |
1058
|
|
|
|
|
|
|
# value the string to convert |
1059
|
|
|
|
|
|
|
# OUTPUT |
1060
|
|
|
|
|
|
|
# value converted string |
1061
|
|
|
|
|
|
|
# SYNOPSIS |
1062
|
|
|
|
|
|
|
# IDS::_convert_from_nested_base64($value); |
1063
|
|
|
|
|
|
|
#**** |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
sub _convert_from_nested_base64 { |
1066
|
|
|
|
|
|
|
my ($value) = @_; |
1067
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
my @matches = (); |
1069
|
|
|
|
|
|
|
preg_match_all(qr/(?:^|[,&?])\s*([a-z0-9]{30,}=*)(?:\W|$)/im, #)/ |
1070
|
|
|
|
|
|
|
$value, |
1071
|
|
|
|
|
|
|
\@matches, |
1072
|
|
|
|
|
|
|
); |
1073
|
|
|
|
|
|
|
# PHP to Perl note: PHP's $matches[1] is Perl's default ($matches[0] is the entire RegEx match) |
1074
|
|
|
|
|
|
|
foreach my $item (@matches) { |
1075
|
|
|
|
|
|
|
if ($item && !preg_match(qr/[a-f0-9]{32}/i, $item)) { |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
# fill up the string with zero bytes if too short for base64 blocks |
1078
|
|
|
|
|
|
|
my $item_original = $item; |
1079
|
|
|
|
|
|
|
if (my $missing_bytes = length($item) % 4) { |
1080
|
|
|
|
|
|
|
for (1..$missing_bytes) { |
1081
|
|
|
|
|
|
|
$item .= "="; |
1082
|
|
|
|
|
|
|
} |
1083
|
|
|
|
|
|
|
} |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
my $base64_item = MIME::Base64::decode_base64($item); |
1086
|
|
|
|
|
|
|
$value = str_replace($item_original, $base64_item, $value); |
1087
|
|
|
|
|
|
|
} |
1088
|
|
|
|
|
|
|
} |
1089
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
return $value; |
1091
|
|
|
|
|
|
|
} |
1092
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
#****if* IDS/_convert_from_out_of_range_chars |
1094
|
|
|
|
|
|
|
# NAME |
1095
|
|
|
|
|
|
|
# _convert_from_out_of_range_chars |
1096
|
|
|
|
|
|
|
# DESCRIPTION |
1097
|
|
|
|
|
|
|
# Detects nullbytes and controls chars via ord() |
1098
|
|
|
|
|
|
|
# INPUT |
1099
|
|
|
|
|
|
|
# value the string to convert |
1100
|
|
|
|
|
|
|
# OUTPUT |
1101
|
|
|
|
|
|
|
# value converted string |
1102
|
|
|
|
|
|
|
# SYNOPSIS |
1103
|
|
|
|
|
|
|
# IDS::_convert_from_out_of_range_chars($value); |
1104
|
|
|
|
|
|
|
#**** |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
sub _convert_from_out_of_range_chars { |
1107
|
|
|
|
|
|
|
my ($value) = @_; |
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
my @values = str_split($value); |
1110
|
|
|
|
|
|
|
foreach my $item (@values) { |
1111
|
|
|
|
|
|
|
if (ord($item) >= 127) { |
1112
|
|
|
|
|
|
|
$value = str_replace($item, ' ', $value); |
1113
|
|
|
|
|
|
|
} |
1114
|
|
|
|
|
|
|
} |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
return $value; |
1117
|
|
|
|
|
|
|
} |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
#****if* IDS/_convert_from_xml |
1120
|
|
|
|
|
|
|
# NAME |
1121
|
|
|
|
|
|
|
# _convert_from_xml |
1122
|
|
|
|
|
|
|
# DESCRIPTION |
1123
|
|
|
|
|
|
|
# Strip XML patterns |
1124
|
|
|
|
|
|
|
# INPUT |
1125
|
|
|
|
|
|
|
# value the string to convert |
1126
|
|
|
|
|
|
|
# OUTPUT |
1127
|
|
|
|
|
|
|
# value converted string |
1128
|
|
|
|
|
|
|
# SYNOPSIS |
1129
|
|
|
|
|
|
|
# IDS::_convert_from_xml($value); |
1130
|
|
|
|
|
|
|
#**** |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
sub _convert_from_xml { |
1133
|
|
|
|
|
|
|
my ($value) = @_; |
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
my $converted = strip_tags($value); |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
if ($converted && ($converted ne $value)) { |
1138
|
|
|
|
|
|
|
return $value . "\n" . $converted; |
1139
|
|
|
|
|
|
|
} |
1140
|
|
|
|
|
|
|
return $value; |
1141
|
|
|
|
|
|
|
} |
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
#****if* IDS/_convert_from_js_unicode |
1144
|
|
|
|
|
|
|
# NAME |
1145
|
|
|
|
|
|
|
# _convert_from_js_unicode |
1146
|
|
|
|
|
|
|
# DESCRIPTION |
1147
|
|
|
|
|
|
|
# Converts JS unicode code points to regular characters |
1148
|
|
|
|
|
|
|
# INPUT |
1149
|
|
|
|
|
|
|
# value the string to convert |
1150
|
|
|
|
|
|
|
# OUTPUT |
1151
|
|
|
|
|
|
|
# value converted string |
1152
|
|
|
|
|
|
|
# SYNOPSIS |
1153
|
|
|
|
|
|
|
# IDS::_convert_from_js_unicode($value); |
1154
|
|
|
|
|
|
|
#**** |
1155
|
|
|
|
|
|
|
|
1156
|
|
|
|
|
|
|
sub _convert_from_js_unicode { |
1157
|
|
|
|
|
|
|
my ($value) = @_; |
1158
|
|
|
|
|
|
|
my @matches = (); |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
# \\u instead of PHP's \\\u |
1161
|
|
|
|
|
|
|
# PHP to Perl note: additional parenthesis around RegEx for getting PHP's $matches[0] |
1162
|
|
|
|
|
|
|
preg_match_all(qr/(\\u[0-9a-f]{4})/ims, $value, \@matches); |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
if ($matches[0]) { |
1165
|
|
|
|
|
|
|
foreach my $match ($matches[0]) { |
1166
|
|
|
|
|
|
|
my $chr = chr(hex(substr($match, 2, 4))); |
1167
|
|
|
|
|
|
|
$value = str_replace($match, $chr, $value); |
1168
|
|
|
|
|
|
|
} |
1169
|
|
|
|
|
|
|
$value .= "\n".'\u0001'; |
1170
|
|
|
|
|
|
|
} |
1171
|
|
|
|
|
|
|
return $value; |
1172
|
|
|
|
|
|
|
} |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
#****if* IDS/_convert_from_utf7 |
1175
|
|
|
|
|
|
|
# NAME |
1176
|
|
|
|
|
|
|
# _convert_from_utf7 |
1177
|
|
|
|
|
|
|
# DESCRIPTION |
1178
|
|
|
|
|
|
|
# Converts relevant UTF-7 tags to UTF-8 (use Encode qw/decode/;) |
1179
|
|
|
|
|
|
|
# INPUT |
1180
|
|
|
|
|
|
|
# value the string to convert |
1181
|
|
|
|
|
|
|
# OUTPUT |
1182
|
|
|
|
|
|
|
# value converted string |
1183
|
|
|
|
|
|
|
# SYNOPSIS |
1184
|
|
|
|
|
|
|
# IDS::_convert_from_utf7($value); |
1185
|
|
|
|
|
|
|
#**** |
1186
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
sub _convert_from_utf7 { |
1188
|
|
|
|
|
|
|
my ($value) = @_; |
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
if (preg_match(qr/\+A\w+-/m, $value)) { |
1191
|
|
|
|
|
|
|
$value .= "\n" . decode("UTF-7", $value); |
1192
|
|
|
|
|
|
|
} |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
return $value; |
1195
|
|
|
|
|
|
|
} |
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
#****if* IDS/_convert_from_concatenated |
1198
|
|
|
|
|
|
|
# NAME |
1199
|
|
|
|
|
|
|
# _convert_from_concatenated |
1200
|
|
|
|
|
|
|
# DESCRIPTION |
1201
|
|
|
|
|
|
|
# Converts basic concatenations |
1202
|
|
|
|
|
|
|
# INPUT |
1203
|
|
|
|
|
|
|
# value the string to convert |
1204
|
|
|
|
|
|
|
# OUTPUT |
1205
|
|
|
|
|
|
|
# value converted string |
1206
|
|
|
|
|
|
|
# SYNOPSIS |
1207
|
|
|
|
|
|
|
# IDS::_convert_from_concatenated($value); |
1208
|
|
|
|
|
|
|
#**** |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
sub _convert_from_concatenated { |
1211
|
|
|
|
|
|
|
my ($value) = @_; |
1212
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
# normalize remaining backslashes |
1214
|
|
|
|
|
|
|
# Perl's \\ should be equivalent to PHP's \\\ |
1215
|
|
|
|
|
|
|
if ($value ne preg_replace(qr/(?:(\w)\\)/, '$1', $value)) { |
1216
|
|
|
|
|
|
|
$value .= preg_replace(qr/(?:(\w)\\)/, '$1', $value); |
1217
|
|
|
|
|
|
|
} |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
my $compare = stripslashes($value); |
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
my @pattern = ( |
1222
|
|
|
|
|
|
|
qr/(?:<\/\w+>\+<\w+>)/s, |
1223
|
|
|
|
|
|
|
qr/(?:":\d+[^"[]+")/s, |
1224
|
|
|
|
|
|
|
qr/(?:"?"\+\w+\+")/s, |
1225
|
|
|
|
|
|
|
qr/(?:"\s*;[^"]+")|(?:";[^"]+:\s*")/s, |
1226
|
|
|
|
|
|
|
qr/(?:"\s*(?:;|\+).{8,18}:\s*")/s, |
1227
|
|
|
|
|
|
|
qr/(?:";\w+=)|(?:!""&&")|(?:~)/s, |
1228
|
|
|
|
|
|
|
qr/(?:"?"\+""?\+?"?)|(?:;\w+=")|(?:"[|&]{2,})/s, |
1229
|
|
|
|
|
|
|
qr/(?:"\s*\W+")/s, |
1230
|
|
|
|
|
|
|
qr/(?:";\w\s*\+=\s*\w?\s*")/s, |
1231
|
|
|
|
|
|
|
qr/(?:"[|&;]+\s*[^|&\n]*[|&]+\s*"?)/s, |
1232
|
|
|
|
|
|
|
qr/(?:";\s*\w+\W+\w*\s*[|&]*")/s, |
1233
|
|
|
|
|
|
|
qr/(?:"\s*"\s*\.)/s, |
1234
|
|
|
|
|
|
|
qr/(?:\s*new\s+\w+\s*[+",])/, |
1235
|
|
|
|
|
|
|
qr/(?:(?:^|\s+)(?:do|else)\s+)/, |
1236
|
|
|
|
|
|
|
qr/(?:[{(]\s*new\s+\w+\s*[)}])/, |
1237
|
|
|
|
|
|
|
qr/(?:(this|self)\.)/, |
1238
|
|
|
|
|
|
|
qr/(?:undefined)/, |
1239
|
|
|
|
|
|
|
qr/(?:in\s+)/, |
1240
|
|
|
|
|
|
|
); |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
# strip out concatenations |
1243
|
|
|
|
|
|
|
my $converted = preg_replace(\@pattern, '', $compare); |
1244
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
# strip object traversal |
1246
|
|
|
|
|
|
|
$converted = preg_replace(qr/\w(\.\w\()/, '$1', $converted); |
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
# normalize obfuscated method calls |
1249
|
|
|
|
|
|
|
$converted = preg_replace(qr/\)\s*\+/, ')', $converted); |
1250
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
# convert JS special numbers |
1252
|
|
|
|
|
|
|
$converted = preg_replace(qr/(?:\(*[.\d]e[+-]*[^a-z\W]+\)*)|(?:NaN|Infinity)\W/ims, 1, $converted); |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
if ($converted && ($compare ne $converted)) { |
1255
|
|
|
|
|
|
|
$value .= "\n" . $converted; |
1256
|
|
|
|
|
|
|
} |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
return $value; |
1259
|
|
|
|
|
|
|
} |
1260
|
|
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
#****if* IDS/_convert_from_proprietary_encodings |
1262
|
|
|
|
|
|
|
# NAME |
1263
|
|
|
|
|
|
|
# _convert_from_proprietary_encodings |
1264
|
|
|
|
|
|
|
# DESCRIPTION |
1265
|
|
|
|
|
|
|
# Collects and decodes proprietary encoding types |
1266
|
|
|
|
|
|
|
# INPUT |
1267
|
|
|
|
|
|
|
# value the string to convert |
1268
|
|
|
|
|
|
|
# OUTPUT |
1269
|
|
|
|
|
|
|
# value converted string |
1270
|
|
|
|
|
|
|
# SYNOPSIS |
1271
|
|
|
|
|
|
|
# IDS::_convert_from_proprietary_encodings($value); |
1272
|
|
|
|
|
|
|
#**** |
1273
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
sub _convert_from_proprietary_encodings { |
1275
|
|
|
|
|
|
|
my ($value) = @_; |
1276
|
|
|
|
|
|
|
|
1277
|
|
|
|
|
|
|
# Xajax error reportings |
1278
|
|
|
|
|
|
|
$value = preg_replace(qr//im, '$1', $value); |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
# strip false alert triggering apostrophes |
1281
|
|
|
|
|
|
|
$value = preg_replace(qr/(\w)\"(s)/m, '$1$2', $value); |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
# strip quotes within typical search patterns |
1284
|
|
|
|
|
|
|
$value = preg_replace(qr/^"([^"=\\!><~]+)"$/, '$1', $value); |
1285
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
# OpenID login tokens |
1287
|
|
|
|
|
|
|
$value = preg_replace(qr/{[\w-]{8,9}\}(?:\{[\w=]{8}\}){2}/, '', $value); |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
# convert Content to null to avoid false alerts |
1290
|
|
|
|
|
|
|
$value = preg_replace(qr/Content|\Wdo\s/, '', $value); |
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
# strip emoticons |
1293
|
|
|
|
|
|
|
$value = preg_replace(qr/(?:\s[:;]-[)\/PD]+)|(?:\s;[)PD]+)|(?:\s:[)PD]+)|-\.-|\^\^/m, '', $value); |
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
# normalize separation char repetition |
1296
|
|
|
|
|
|
|
$value = preg_replace(qr/([.+~=*_\-;])\1{2,}/m, '$1', $value); |
1297
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
# normalize multiple single quotes |
1299
|
|
|
|
|
|
|
$value = preg_replace(qr/"{2,}/m, '"', $value); |
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
# normalize quoted numerical values and asterisks |
1302
|
|
|
|
|
|
|
$value = preg_replace(qr/"(\d+)"/m, '$1', $value); |
1303
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
# normalize pipe separated request parameters |
1305
|
|
|
|
|
|
|
$value = preg_replace(qr/\|(\w+=\w+)/m, '&$1', $value); |
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
# normalize ampersand listings |
1308
|
|
|
|
|
|
|
$value = preg_replace(qr/(\w\s)&\s(\w)/, '$1$2', $value); |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
return $value; |
1311
|
|
|
|
|
|
|
} |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
#****if* IDS/_run_centrifuge |
1314
|
|
|
|
|
|
|
# NAME |
1315
|
|
|
|
|
|
|
# _run_centrifuge |
1316
|
|
|
|
|
|
|
# DESCRIPTION |
1317
|
|
|
|
|
|
|
# The centrifuge prototype |
1318
|
|
|
|
|
|
|
# INPUT |
1319
|
|
|
|
|
|
|
# value the string to convert |
1320
|
|
|
|
|
|
|
# OUTPUT |
1321
|
|
|
|
|
|
|
# value converted string |
1322
|
|
|
|
|
|
|
# SYNOPSIS |
1323
|
|
|
|
|
|
|
# IDS::_run_centrifuge($value); |
1324
|
|
|
|
|
|
|
#**** |
1325
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
sub _run_centrifuge { |
1327
|
|
|
|
|
|
|
my ($value) = @_; |
1328
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
my $threshold = 3.49; |
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
if (strlen($value) > 25) { |
1332
|
|
|
|
|
|
|
# strip padding |
1333
|
|
|
|
|
|
|
my $tmp_value = preg_replace(qr/\s{4}|==$/m, '', $value); |
1334
|
|
|
|
|
|
|
$tmp_value = preg_replace( |
1335
|
|
|
|
|
|
|
qr/\s{4}|[\p{L}\d\+\-=,.%()]{8,}/m, |
1336
|
|
|
|
|
|
|
'aaa', |
1337
|
|
|
|
|
|
|
$tmp_value |
1338
|
|
|
|
|
|
|
); |
1339
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
# Check for the attack char ratio |
1341
|
|
|
|
|
|
|
$tmp_value = preg_replace(qr/([*.!?+-])\1{1,}/m, '$1', $tmp_value); |
1342
|
|
|
|
|
|
|
$tmp_value = preg_replace(qr/"[\p{L}\d\s]+"/m, '', $tmp_value); |
1343
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
my $stripped_length = strlen( |
1345
|
|
|
|
|
|
|
preg_replace(qr/[\d\s\p{L}\.:,%&\/><\-)!]+/m, |
1346
|
|
|
|
|
|
|
'', |
1347
|
|
|
|
|
|
|
$tmp_value) |
1348
|
|
|
|
|
|
|
); |
1349
|
|
|
|
|
|
|
my $overall_length = strlen( |
1350
|
|
|
|
|
|
|
preg_replace( |
1351
|
|
|
|
|
|
|
qr/([\d\s\p{L}:,\.]{3,})+/m, |
1352
|
|
|
|
|
|
|
'aaa', |
1353
|
|
|
|
|
|
|
preg_replace( |
1354
|
|
|
|
|
|
|
qr/\s{2,}/ms, |
1355
|
|
|
|
|
|
|
'', |
1356
|
|
|
|
|
|
|
$tmp_value |
1357
|
|
|
|
|
|
|
) |
1358
|
|
|
|
|
|
|
) |
1359
|
|
|
|
|
|
|
); |
1360
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
if ($stripped_length != 0 && |
1362
|
|
|
|
|
|
|
$overall_length/$stripped_length <= $threshold |
1363
|
|
|
|
|
|
|
) { |
1364
|
|
|
|
|
|
|
$value .= "\n".'$[!!!]'; |
1365
|
|
|
|
|
|
|
} |
1366
|
|
|
|
|
|
|
} |
1367
|
|
|
|
|
|
|
|
1368
|
|
|
|
|
|
|
if (strlen($value) > 40) { |
1369
|
|
|
|
|
|
|
# Replace all non-special chars |
1370
|
|
|
|
|
|
|
my $converted = preg_replace(qr/[\w\s\p{L},.:!]/, '', $value); |
1371
|
|
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
# Split string into an array, unify and sort |
1373
|
|
|
|
|
|
|
my @array = str_split($converted); |
1374
|
|
|
|
|
|
|
my %seen = (); |
1375
|
|
|
|
|
|
|
my @unique = grep { ! $seen{$_} ++ } @array; |
1376
|
|
|
|
|
|
|
@unique = sort @unique; |
1377
|
|
|
|
|
|
|
|
1378
|
|
|
|
|
|
|
# Normalize certain tokens |
1379
|
|
|
|
|
|
|
my %schemes = ( |
1380
|
|
|
|
|
|
|
'~' => '+', |
1381
|
|
|
|
|
|
|
'^' => '+', |
1382
|
|
|
|
|
|
|
'|' => '+', |
1383
|
|
|
|
|
|
|
'*' => '+', |
1384
|
|
|
|
|
|
|
'%' => '+', |
1385
|
|
|
|
|
|
|
'&' => '+', |
1386
|
|
|
|
|
|
|
'/' => '+', |
1387
|
|
|
|
|
|
|
); |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
$converted = implode('', @unique); |
1390
|
|
|
|
|
|
|
$converted = str_replace([keys %schemes], [values %schemes], $converted); |
1391
|
|
|
|
|
|
|
$converted = preg_replace(qr/[+-]\s*\d+/, '+', $converted); |
1392
|
|
|
|
|
|
|
$converted = preg_replace(qr/[()[\]{}]/, '(', $converted); |
1393
|
|
|
|
|
|
|
$converted = preg_replace(qr/[!?:=]/, ':', $converted); |
1394
|
|
|
|
|
|
|
$converted = preg_replace(qr/[^:(+]/, '', stripslashes($converted)); #/ |
1395
|
|
|
|
|
|
|
|
1396
|
|
|
|
|
|
|
# Sort again and implode |
1397
|
|
|
|
|
|
|
@array = str_split($converted); |
1398
|
|
|
|
|
|
|
@array = sort @array; |
1399
|
|
|
|
|
|
|
$converted = implode('', @array); |
1400
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
if (preg_match(qr/(?:\({2,}\+{2,}:{2,})|(?:\({2,}\+{2,}:+)|(?:\({3,}\++:{2,})/, $converted)) { |
1402
|
|
|
|
|
|
|
return $value . "\n" . $converted; |
1403
|
|
|
|
|
|
|
} |
1404
|
|
|
|
|
|
|
} |
1405
|
|
|
|
|
|
|
|
1406
|
|
|
|
|
|
|
return $value; |
1407
|
|
|
|
|
|
|
} |
1408
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
#------------------------- PHP functions --------------------------------------- |
1410
|
|
|
|
|
|
|
|
1411
|
|
|
|
|
|
|
#****if* IDS/array_sum |
1412
|
|
|
|
|
|
|
# NAME |
1413
|
|
|
|
|
|
|
# array_sum |
1414
|
|
|
|
|
|
|
# DESCRIPTION |
1415
|
|
|
|
|
|
|
# Equivalent to PHP's array_sum, sums all array values |
1416
|
|
|
|
|
|
|
# INPUT |
1417
|
|
|
|
|
|
|
# array the string to convert |
1418
|
|
|
|
|
|
|
# OUTPUT |
1419
|
|
|
|
|
|
|
# sum sum of all array values |
1420
|
|
|
|
|
|
|
# SYNOPSIS |
1421
|
|
|
|
|
|
|
# IDS::array_sum(@array); |
1422
|
|
|
|
|
|
|
#**** |
1423
|
|
|
|
|
|
|
|
1424
|
|
|
|
|
|
|
sub array_sum { |
1425
|
|
|
|
|
|
|
(my @array) = @_; |
1426
|
|
|
|
|
|
|
|
1427
|
|
|
|
|
|
|
my $sum = 0; |
1428
|
|
|
|
|
|
|
foreach my $value (@array) { |
1429
|
|
|
|
|
|
|
if ($value) { |
1430
|
|
|
|
|
|
|
$sum += $value; |
1431
|
|
|
|
|
|
|
} |
1432
|
|
|
|
|
|
|
} |
1433
|
|
|
|
|
|
|
return $sum; |
1434
|
|
|
|
|
|
|
} |
1435
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
#****if* IDS/preg_match |
1437
|
|
|
|
|
|
|
# NAME |
1438
|
|
|
|
|
|
|
# preg_match |
1439
|
|
|
|
|
|
|
# DESCRIPTION |
1440
|
|
|
|
|
|
|
# Equivalent to PHP's preg_match, but with two arguments only |
1441
|
|
|
|
|
|
|
# INPUT |
1442
|
|
|
|
|
|
|
# pattern the pattern to match |
1443
|
|
|
|
|
|
|
# string the string |
1444
|
|
|
|
|
|
|
# OUTPUT |
1445
|
|
|
|
|
|
|
# boolean 1 if pattern matches string, 0 otherwise |
1446
|
|
|
|
|
|
|
# SYNOPSIS |
1447
|
|
|
|
|
|
|
# IDS::preg_match($pattern, $string); |
1448
|
|
|
|
|
|
|
#**** |
1449
|
|
|
|
|
|
|
|
1450
|
|
|
|
|
|
|
sub preg_match { |
1451
|
|
|
|
|
|
|
(my $pattern, my $string) = @_; |
1452
|
|
|
|
|
|
|
return ($string =~ $pattern); |
1453
|
|
|
|
|
|
|
} |
1454
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
#****if* IDS/preg_match_all |
1456
|
|
|
|
|
|
|
# NAME |
1457
|
|
|
|
|
|
|
# preg_match_all |
1458
|
|
|
|
|
|
|
# DESCRIPTION |
1459
|
|
|
|
|
|
|
# Equivalent to PHP's preg_match_all, but with three arguments only. |
1460
|
|
|
|
|
|
|
# Does not return nested arrays like PHP. |
1461
|
|
|
|
|
|
|
# Does not automatically match entire RegEx in $matches[0] like PHP does - |
1462
|
|
|
|
|
|
|
# Use brackets around your entire RegEx instead: preg_match_all(qr/(your(\d)(R|r)egex)/. |
1463
|
|
|
|
|
|
|
# INPUT |
1464
|
|
|
|
|
|
|
# pattern the pattern to match |
1465
|
|
|
|
|
|
|
# string the string |
1466
|
|
|
|
|
|
|
# arrayref the array to store the matches in |
1467
|
|
|
|
|
|
|
# OUTPUT |
1468
|
|
|
|
|
|
|
# array same content as written into arrayref |
1469
|
|
|
|
|
|
|
# SYNOPSIS |
1470
|
|
|
|
|
|
|
# IDS::preg_match_all(qr/(?:[\d+-=\/\* ]+(?:\s?,\s?[\d+-=\/\* ]+)+){4,}/ms, $value, \@matches) |
1471
|
|
|
|
|
|
|
# if (IDS::preg_match_all(qr/(?:[\d+-=\/\* ]+(?:\s?,\s?[\d+-=\/\* ]+)+){4,}/ms, $value, \@matches)) { |
1472
|
|
|
|
|
|
|
# print 'match'; |
1473
|
|
|
|
|
|
|
# } |
1474
|
|
|
|
|
|
|
#**** |
1475
|
|
|
|
|
|
|
|
1476
|
|
|
|
|
|
|
sub preg_match_all { |
1477
|
|
|
|
|
|
|
(my $pattern, my $string, my $matches) = @_; |
1478
|
|
|
|
|
|
|
return (@$matches = ($string =~ /$pattern/g)); |
1479
|
|
|
|
|
|
|
} |
1480
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
#****if* IDS/preg_replace |
1482
|
|
|
|
|
|
|
# NAME |
1483
|
|
|
|
|
|
|
# preg_replace |
1484
|
|
|
|
|
|
|
# DESCRIPTION |
1485
|
|
|
|
|
|
|
# Equivalent to PHP's preg_replace, but with three arguments only |
1486
|
|
|
|
|
|
|
# INPUT |
1487
|
|
|
|
|
|
|
# + pattern the pattern(s) to match |
1488
|
|
|
|
|
|
|
# replacement the replacement(s) |
1489
|
|
|
|
|
|
|
# + string the string(s) |
1490
|
|
|
|
|
|
|
# OUTPUT |
1491
|
|
|
|
|
|
|
# string the string(s) with all replacements done |
1492
|
|
|
|
|
|
|
# SYNOPSIS |
1493
|
|
|
|
|
|
|
# IDS::preg_replace(\@patterns, $replacement, $string); |
1494
|
|
|
|
|
|
|
# IDS::preg_replace(qr/^f.*ck/i, 'censored', $string); |
1495
|
|
|
|
|
|
|
# IDS::preg_replace(['badword', 'badword2', 'badword3'], ['censored1', 'censored2', 'censored3'], $string); |
1496
|
|
|
|
|
|
|
#**** |
1497
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
sub preg_replace { |
1499
|
|
|
|
|
|
|
(my $patterns, my $replacements, my $strings) = @_; |
1500
|
|
|
|
|
|
|
|
1501
|
|
|
|
|
|
|
# check input |
1502
|
|
|
|
|
|
|
if (!defined($strings) || !$strings || |
1503
|
|
|
|
|
|
|
!defined($patterns) || !$patterns ) { |
1504
|
|
|
|
|
|
|
return ''; |
1505
|
|
|
|
|
|
|
} |
1506
|
|
|
|
|
|
|
|
1507
|
|
|
|
|
|
|
my $return_string = ''; |
1508
|
|
|
|
|
|
|
if (ref($strings) ne 'ARRAY') { |
1509
|
|
|
|
|
|
|
$return_string = $strings; |
1510
|
|
|
|
|
|
|
} |
1511
|
|
|
|
|
|
|
|
1512
|
|
|
|
|
|
|
if (ref($strings) eq 'ARRAY') { |
1513
|
|
|
|
|
|
|
my @replaced_strings = map { |
1514
|
|
|
|
|
|
|
preg_replace($patterns, $replacements, $_); |
1515
|
|
|
|
|
|
|
} @$strings; |
1516
|
|
|
|
|
|
|
return \@replaced_strings; |
1517
|
|
|
|
|
|
|
} |
1518
|
|
|
|
|
|
|
elsif (ref($patterns) eq 'ARRAY') { |
1519
|
|
|
|
|
|
|
my $pattern_no = 0; |
1520
|
|
|
|
|
|
|
foreach my $pattern (@$patterns) { |
1521
|
|
|
|
|
|
|
if (ref($replacements) eq 'ARRAY') { |
1522
|
|
|
|
|
|
|
$return_string = preg_replace($pattern, @$replacements[$pattern_no++], $return_string); |
1523
|
|
|
|
|
|
|
} |
1524
|
|
|
|
|
|
|
else { |
1525
|
|
|
|
|
|
|
$return_string = preg_replace($pattern, $replacements, $return_string); |
1526
|
|
|
|
|
|
|
} |
1527
|
|
|
|
|
|
|
} |
1528
|
|
|
|
|
|
|
} |
1529
|
|
|
|
|
|
|
else { |
1530
|
|
|
|
|
|
|
my $repl = ''; |
1531
|
|
|
|
|
|
|
|
1532
|
|
|
|
|
|
|
if (ref($replacements) eq 'ARRAY') { |
1533
|
|
|
|
|
|
|
$repl = @$replacements[0]; |
1534
|
|
|
|
|
|
|
} |
1535
|
|
|
|
|
|
|
else { |
1536
|
|
|
|
|
|
|
if (!defined($replacements)) { |
1537
|
|
|
|
|
|
|
$repl = ''; |
1538
|
|
|
|
|
|
|
} |
1539
|
|
|
|
|
|
|
else { |
1540
|
|
|
|
|
|
|
$repl = $replacements; |
1541
|
|
|
|
|
|
|
} |
1542
|
|
|
|
|
|
|
} |
1543
|
|
|
|
|
|
|
$repl =~ s/\\/\\\\/g; |
1544
|
|
|
|
|
|
|
$repl =~ s/\"/\\"/g; |
1545
|
|
|
|
|
|
|
$repl =~ s/\@/\\@/g; |
1546
|
|
|
|
|
|
|
$repl =~ s/\$(?!\d)/\\\$/g; # escape $ if not substitution variable like $1 |
1547
|
|
|
|
|
|
|
$repl = qq{"$repl"}; |
1548
|
|
|
|
|
|
|
$return_string =~ s/$patterns/defined $repl ? $repl : ''/eeg; |
1549
|
|
|
|
|
|
|
} |
1550
|
|
|
|
|
|
|
return $return_string; |
1551
|
|
|
|
|
|
|
} |
1552
|
|
|
|
|
|
|
|
1553
|
|
|
|
|
|
|
#****if* IDS/str_replace |
1554
|
|
|
|
|
|
|
# NAME |
1555
|
|
|
|
|
|
|
# str_replace |
1556
|
|
|
|
|
|
|
# DESCRIPTION |
1557
|
|
|
|
|
|
|
# Equivalent to PHP's str_replace, but with three arguments only (simply a wrapper for preg_replace, but escapes pattern meta characters) |
1558
|
|
|
|
|
|
|
# INPUT |
1559
|
|
|
|
|
|
|
# pattern the pattern(s) to match |
1560
|
|
|
|
|
|
|
# replacement the replacement(s) |
1561
|
|
|
|
|
|
|
# string the string(s) |
1562
|
|
|
|
|
|
|
# OUTPUT |
1563
|
|
|
|
|
|
|
# string the string(s) with all replacements done |
1564
|
|
|
|
|
|
|
# SYNOPSIS |
1565
|
|
|
|
|
|
|
# IDS::str_replace(\@patterns, $replacement, $string); |
1566
|
|
|
|
|
|
|
# IDS::str_replace('bad\tword', 'censored', $string); # replaces 'bad\tword' but not 'bad word' or "bad\tword" |
1567
|
|
|
|
|
|
|
# IDS::str_replace(['badword', 'badword2', 'badword3'], ['censored1', 'censored2', 'censored3'], $string); |
1568
|
|
|
|
|
|
|
#**** |
1569
|
|
|
|
|
|
|
|
1570
|
|
|
|
|
|
|
sub str_replace { |
1571
|
|
|
|
|
|
|
(my $patterns, my $replacements, my $strings) = @_; |
1572
|
|
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
my @escapedpatterns = (); |
1574
|
|
|
|
|
|
|
|
1575
|
|
|
|
|
|
|
if (ref($patterns) eq 'ARRAY') { |
1576
|
|
|
|
|
|
|
@escapedpatterns = map {quotemeta($_)} @$patterns; |
1577
|
|
|
|
|
|
|
return preg_replace(\@escapedpatterns, $replacements, $strings); |
1578
|
|
|
|
|
|
|
} |
1579
|
|
|
|
|
|
|
else { |
1580
|
|
|
|
|
|
|
return preg_replace(quotemeta($patterns), $replacements, $strings); |
1581
|
|
|
|
|
|
|
} |
1582
|
|
|
|
|
|
|
} |
1583
|
|
|
|
|
|
|
|
1584
|
|
|
|
|
|
|
#****if* IDS/str_split |
1585
|
|
|
|
|
|
|
# NAME |
1586
|
|
|
|
|
|
|
# str_split |
1587
|
|
|
|
|
|
|
# DESCRIPTION |
1588
|
|
|
|
|
|
|
# Equivalent to PHP's str_split |
1589
|
|
|
|
|
|
|
# INPUT |
1590
|
|
|
|
|
|
|
# string the string to split |
1591
|
|
|
|
|
|
|
# OUTPUT |
1592
|
|
|
|
|
|
|
# array the split string |
1593
|
|
|
|
|
|
|
# SYNOPSIS |
1594
|
|
|
|
|
|
|
# IDS::str_split($string); |
1595
|
|
|
|
|
|
|
#**** |
1596
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
sub str_split { |
1598
|
|
|
|
|
|
|
(my $string, my $limit) = @_; |
1599
|
|
|
|
|
|
|
if (defined($limit)) { |
1600
|
|
|
|
|
|
|
return ($string =~ /(.{1,$limit})/g); |
1601
|
|
|
|
|
|
|
} |
1602
|
|
|
|
|
|
|
else { |
1603
|
|
|
|
|
|
|
return split(//, $string); |
1604
|
|
|
|
|
|
|
} |
1605
|
|
|
|
|
|
|
} |
1606
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
#****if* IDS/strlen |
1608
|
|
|
|
|
|
|
# NAME |
1609
|
|
|
|
|
|
|
# strlen |
1610
|
|
|
|
|
|
|
# DESCRIPTION |
1611
|
|
|
|
|
|
|
# Equivalent to PHP's strlen, wrapper for Perl's length() |
1612
|
|
|
|
|
|
|
# INPUT |
1613
|
|
|
|
|
|
|
# string the string |
1614
|
|
|
|
|
|
|
# OUTPUT |
1615
|
|
|
|
|
|
|
# string the string's length |
1616
|
|
|
|
|
|
|
# SYNOPSIS |
1617
|
|
|
|
|
|
|
# IDS::strlen($url); |
1618
|
|
|
|
|
|
|
#**** |
1619
|
|
|
|
|
|
|
|
1620
|
|
|
|
|
|
|
sub strlen { |
1621
|
|
|
|
|
|
|
(my $string) = @_; |
1622
|
|
|
|
|
|
|
return length($string); |
1623
|
|
|
|
|
|
|
} |
1624
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
#****if* IDS/urldecode |
1626
|
|
|
|
|
|
|
# NAME |
1627
|
|
|
|
|
|
|
# urldecode |
1628
|
|
|
|
|
|
|
# DESCRIPTION |
1629
|
|
|
|
|
|
|
# Equivalent to PHP's urldecode |
1630
|
|
|
|
|
|
|
# INPUT |
1631
|
|
|
|
|
|
|
# string the URL to decode |
1632
|
|
|
|
|
|
|
# OUTPUT |
1633
|
|
|
|
|
|
|
# string the decoded URL |
1634
|
|
|
|
|
|
|
# SYNOPSIS |
1635
|
|
|
|
|
|
|
# IDS::urldecode($url); |
1636
|
|
|
|
|
|
|
#**** |
1637
|
|
|
|
|
|
|
|
1638
|
|
|
|
|
|
|
sub urldecode { |
1639
|
|
|
|
|
|
|
(my $theURL) = @_; |
1640
|
|
|
|
|
|
|
$theURL =~ tr/+/ /; |
1641
|
|
|
|
|
|
|
$theURL =~ s/%([a-fA-F0-9]{2,2})/chr(hex($1))/eg; |
1642
|
|
|
|
|
|
|
$theURL =~ s///g; |
1643
|
|
|
|
|
|
|
utf8::decode($theURL); |
1644
|
|
|
|
|
|
|
return $theURL; |
1645
|
|
|
|
|
|
|
} |
1646
|
|
|
|
|
|
|
|
1647
|
|
|
|
|
|
|
#****if* IDS/urlencode |
1648
|
|
|
|
|
|
|
# NAME |
1649
|
|
|
|
|
|
|
# urlencode |
1650
|
|
|
|
|
|
|
# DESCRIPTION |
1651
|
|
|
|
|
|
|
# Equivalent to PHP's urlencode |
1652
|
|
|
|
|
|
|
# INPUT |
1653
|
|
|
|
|
|
|
# string the URL to encode |
1654
|
|
|
|
|
|
|
# OUTPUT |
1655
|
|
|
|
|
|
|
# string the encoded URL |
1656
|
|
|
|
|
|
|
# SYNOPSIS |
1657
|
|
|
|
|
|
|
# IDS::urlencode($url); |
1658
|
|
|
|
|
|
|
#**** |
1659
|
|
|
|
|
|
|
|
1660
|
|
|
|
|
|
|
sub urlencode { |
1661
|
|
|
|
|
|
|
(my $theURL) = @_; |
1662
|
|
|
|
|
|
|
$theURL =~ s/([\W])/sprintf("%%%02X",ord($1))/eg; |
1663
|
|
|
|
|
|
|
utf8::encode($theURL); |
1664
|
|
|
|
|
|
|
return $theURL; |
1665
|
|
|
|
|
|
|
} |
1666
|
|
|
|
|
|
|
|
1667
|
|
|
|
|
|
|
#****if* IDS/implode |
1668
|
|
|
|
|
|
|
# NAME |
1669
|
|
|
|
|
|
|
# implode |
1670
|
|
|
|
|
|
|
# DESCRIPTION |
1671
|
|
|
|
|
|
|
# Equivalent to PHP's implode (simply wrapper of join) |
1672
|
|
|
|
|
|
|
# INPUT |
1673
|
|
|
|
|
|
|
# string glue the glue to put between the pieces |
1674
|
|
|
|
|
|
|
# array pieces the pieces to be put together |
1675
|
|
|
|
|
|
|
# OUTPUT |
1676
|
|
|
|
|
|
|
# string the imploded string |
1677
|
|
|
|
|
|
|
# SYNOPSIS |
1678
|
|
|
|
|
|
|
# IDS::implode(';', @pieces); |
1679
|
|
|
|
|
|
|
#**** |
1680
|
|
|
|
|
|
|
|
1681
|
|
|
|
|
|
|
sub implode { |
1682
|
|
|
|
|
|
|
(my $glue, my @pieces) = @_; |
1683
|
|
|
|
|
|
|
return join($glue, @pieces); |
1684
|
|
|
|
|
|
|
} |
1685
|
|
|
|
|
|
|
|
1686
|
|
|
|
|
|
|
#****if* IDS/explode |
1687
|
|
|
|
|
|
|
# NAME |
1688
|
|
|
|
|
|
|
# explode |
1689
|
|
|
|
|
|
|
# DESCRIPTION |
1690
|
|
|
|
|
|
|
# Equivalent to PHP's explode (simply wrapper of split, but escapes met characters) |
1691
|
|
|
|
|
|
|
# INPUT |
1692
|
|
|
|
|
|
|
# string glue the glue to put between the pieces |
1693
|
|
|
|
|
|
|
# string string the string to split |
1694
|
|
|
|
|
|
|
# OUTPUT |
1695
|
|
|
|
|
|
|
# array the exploded string |
1696
|
|
|
|
|
|
|
# SYNOPSIS |
1697
|
|
|
|
|
|
|
# IDS::explode(';', $string); |
1698
|
|
|
|
|
|
|
#**** |
1699
|
|
|
|
|
|
|
|
1700
|
|
|
|
|
|
|
sub explode { |
1701
|
|
|
|
|
|
|
(my $glue, my $string) = @_; |
1702
|
|
|
|
|
|
|
return split(quotemeta($glue), $string); |
1703
|
|
|
|
|
|
|
} |
1704
|
|
|
|
|
|
|
|
1705
|
|
|
|
|
|
|
#****if* IDS/stripslashes |
1706
|
|
|
|
|
|
|
# NAME |
1707
|
|
|
|
|
|
|
# stripslashes |
1708
|
|
|
|
|
|
|
# DESCRIPTION |
1709
|
|
|
|
|
|
|
# Equivalent to PHP's stripslashes |
1710
|
|
|
|
|
|
|
# INPUT |
1711
|
|
|
|
|
|
|
# string string the string |
1712
|
|
|
|
|
|
|
# OUTPUT |
1713
|
|
|
|
|
|
|
# string the stripped string |
1714
|
|
|
|
|
|
|
# SYNOPSIS |
1715
|
|
|
|
|
|
|
# IDS::stripslashes($string); |
1716
|
|
|
|
|
|
|
#**** |
1717
|
|
|
|
|
|
|
|
1718
|
|
|
|
|
|
|
sub stripslashes { |
1719
|
|
|
|
|
|
|
(my $string) = @_; |
1720
|
|
|
|
|
|
|
# $string =~ s/(?:\\(\'|\"|\\|\0|N))/$1/g; |
1721
|
|
|
|
|
|
|
$string =~ s/\\([^\\])/$1/g; |
1722
|
|
|
|
|
|
|
return $string; |
1723
|
|
|
|
|
|
|
} |
1724
|
|
|
|
|
|
|
|
1725
|
|
|
|
|
|
|
#****if* IDS/strip_tags |
1726
|
|
|
|
|
|
|
# NAME |
1727
|
|
|
|
|
|
|
# strip_tags |
1728
|
|
|
|
|
|
|
# DESCRIPTION |
1729
|
|
|
|
|
|
|
# Equivalent to PHP's strip_tags, but without 'allowable_tags' parameter |
1730
|
|
|
|
|
|
|
# INPUT |
1731
|
|
|
|
|
|
|
# string string the string |
1732
|
|
|
|
|
|
|
# OUTPUT |
1733
|
|
|
|
|
|
|
# string the stripped string |
1734
|
|
|
|
|
|
|
# SYNOPSIS |
1735
|
|
|
|
|
|
|
# IDS::strip_tags($string); |
1736
|
|
|
|
|
|
|
#**** |
1737
|
|
|
|
|
|
|
|
1738
|
|
|
|
|
|
|
sub strip_tags { |
1739
|
|
|
|
|
|
|
(my $string) = @_; |
1740
|
|
|
|
|
|
|
|
1741
|
|
|
|
|
|
|
while ($string =~ s/<\S[^<>]*(?:>|$)//gs) {}; |
1742
|
|
|
|
|
|
|
|
1743
|
|
|
|
|
|
|
return $string; |
1744
|
|
|
|
|
|
|
} |
1745
|
|
|
|
|
|
|
|
1746
|
|
|
|
|
|
|
1; |
1747
|
|
|
|
|
|
|
|
1748
|
|
|
|
|
|
|
__END__ |