Skip to content

Commit

Permalink
Merge pull request #98 from fkie-cad/dev
Browse files Browse the repository at this point in the history
Merge dev into main
  • Loading branch information
ru37z authored Dec 9, 2024
2 parents a41f8ef + a24ecbb commit 1e4fce2
Show file tree
Hide file tree
Showing 17 changed files with 173 additions and 152 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ The numbers above are valid for small simulations with 1-3 clients.

## Installation

The installation instructions below were tested on a fresh Ubuntu 20.04 LTS system.
The installation instructions below were tested on a fresh Ubuntu 24.04 LTS system.
Please adhere strictly to the instructions as different software versions might not work as expected.

```sh
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def setup_logging():
logging.Formatter.converter = time.localtime
logging.Formatter.default_time_format = "%Y-%m-%dT%H:%M:%S"
is_dst = time.daylight and time.localtime().tm_isdst > 0
gmt_offset_secs = - (time.altzone if is_dst else time.timezone)
gmt_offset_secs = -(time.altzone if is_dst else time.timezone)
gmt_offset_string = "{0:+03d}:00".format(gmt_offset_secs // 3600)
log_handler = WatchedFileHandler(filename="/var/log/breach/external_mail_handler.log")
logging.basicConfig(
Expand All @@ -53,13 +53,13 @@ def __init__(self, smtp_out):
async def handle_DATA(self, server, session, envelope):
# peer, mail_from, mail_to, rcpt_tos and data are now all encapsulated in 'envelope'
# keep in mind that envelope.data contains raw bytes which first have to be decoded
mail = mime_string_to_text_mail(envelope.data.decode("utf-8"))
mail = mime_string_to_text_mail(envelope.content.decode("utf-8"))
logger.info("Received mail from " + str(mail.sender) + " addressed to " + str(mail.receiver))
self.swap_sender_receiver(mail)
self.modify_text(mail)
self.send_mail(mail)
# A return message is mandatory
return '250 OK'
return "250 OK"

@staticmethod
def swap_sender_receiver(mail):
Expand All @@ -84,19 +84,17 @@ class Responder:
def __init__(self):
self.smtp_out = Server("172.18.0.2", 25)
self.smtp_in = Server("0.0.0.0", 25)
self.controller: Controller|None = None
self.controller: Controller | None = None

def run(self):
logger.info("Starting Mail Responder listening at " +
str(self.smtp_in.server_ip) +
":" + str(self.smtp_in.server_port))
logger.info("Sending responses to " +
str(self.smtp_out.server_ip) +
":" + str(self.smtp_out.server_port))
logger.info(
"Starting Mail Responder listening at " + str(self.smtp_in.server_ip) + ":" + str(self.smtp_in.server_port)
)
logger.info("Sending responses to " + str(self.smtp_out.server_ip) + ":" + str(self.smtp_out.server_port))
self.init_controller()
self.controller.start() # detaches from current thread
input('SMTP server running. Press Return to stop server and exit.')
self.controller.stop()
while True:
time.sleep(1)

def init_controller(self):
handler = CustomHandler(self.smtp_out)
Expand Down Expand Up @@ -146,4 +144,4 @@ def __init__(self, server_ip=None, server_port=None):
if __name__ == "__main__":
setup_logging()
responder = Responder()
responder.run()
responder.run()
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ async def test_handler(self, handler: CustomHandler):
envelope.peer = "127.0.0.1"
envelope.mail_to = mail.sender
envelope.rcpt_to = [mail.receiver]
envelope.data = mail.to_mime_text().as_string().encode('utf-8')
envelope.content = mail.to_mime_text().as_string().encode("utf-8")
await handler.handle_DATA(None, None, envelope)
assert handler.swap_sender_receiver.called
assert handler.send_mail.called
Expand All @@ -98,15 +98,19 @@ def test_init_controller(self, responder: Responder):

def test_run(self, responder: Responder):
responder.init_controller = Mock()
responder.controller = Mock() # Mock the controller object itself
responder.controller = Mock()
responder.controller.start = Mock()
responder.controller.stop = Mock()

with patch("builtins.input", return_value=""):
responder.run()
with patch("time.sleep", side_effect=[KeyboardInterrupt]):
try:
responder.run()
except KeyboardInterrupt:
pass

assert responder.init_controller.called
assert responder.controller.start.called
assert responder.controller.stop.called
assert not responder.controller.stop.called


@pytest.fixture()
Expand Down
12 changes: 12 additions & 0 deletions provisioning/ansible/roles/external_mail_handler/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@
# Additionally, creates a logrotate config file for logs
#

- name: Ensure compatible version of binutils
apt:
name: binutils=2.43.1-5
state: present
update_cache: yes

- name: Install aiosmtpd (requirement) globally
apt:
name: python3-aiosmtpd=1.4.6-1
update_cache: yes
state: present

- name: "Create script directory {{ script_dir }}"
file:
path: "{{ script_dir }}"
Expand Down
9 changes: 4 additions & 5 deletions provisioning/packer/attacker.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"initrd=/install.amd/initrd.gz ",
"auto-install/enable=true ",
"debconf/priority=critical ",
"preseed/url=http://{{user `host_ip_addr`}}:{{.HTTPPort}}/attacker_preseed.cfg ", "<wait5s>",
"preseed/url=http://{{user `host_ip_addr`}}:{{.HTTPPort}}/attacker_preseed.cfg ",
"<wait5s>",
"netcfg/choose_interface=enp0s8 ",
"netcfg/disable_autoconfig=true ",
"netcfg/get_ipaddress=192.168.56.31 ",
Expand Down Expand Up @@ -104,7 +105,7 @@
"-i",
"../ansible/hosts"
],
"pause_before": "5m",
"pause_before": "2m",
"playbook_file": "../ansible/attacker_playbook.yml",
"type": "ansible"
}
Expand All @@ -117,6 +118,4 @@
"vm_output": "./exports/attacker",
"vm_hostonlyif": "{{env `HOSTONLYIF`}}"
}
}


}
5 changes: 2 additions & 3 deletions provisioning/packer/companyrouter.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"guest_os_type": "Linux_64",
"vm_name": "Company Router",
"guest_additions_mode": "disable",
"headless": "true",
"headless": "true",
"iso_checksum": "sha256:7793981fbe39cb1fa3cb2f89dd1472751d502823b2fc333449171af1bc225f8f",
"iso_urls": [
"https://downloads.ipfire.org/releases/ipfire-2.x/2.25-core141/ipfire-2.25.x86_64-full-core141.iso"
Expand Down Expand Up @@ -163,5 +163,4 @@
"type": "ansible"
}
]
}

}
3 changes: 1 addition & 2 deletions provisioning/packer/http/attacker_preseed.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,5 @@ d-i preseed/late_command string \
in-target sh -c "systemctl start systemd-networkd"; \
in-target sh -c "systemctl start systemd-resolved"; \
in-target sh -c "systemctl enable ssh.service"; \
in-target sh -c "ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf"; \
in-target sh -c "echo 'nameserver 172.18.0.1' > /etc/resolv.conf"; \
in-target sh -c "echo 'deb http://http.kali.org/kali kali-rolling main non-free contrib' >> /etc/apt/sources.list"

18 changes: 8 additions & 10 deletions provisioning/packer/internetrouter.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
"vm_name": "Internet Router",
"guest_additions_mode": "disable",
"headless": "true",
"iso_checksum": "sha256:7793981fbe39cb1fa3cb2f89dd1472751d502823b2fc333449171af1bc225f8f",
"iso_checksum": "sha256:831ee9a6197e0f351fa477b81aab510df6874e7f0e6d16fe1683768b60ff9dc0",
"iso_urls": [
"https://downloads.ipfire.org/releases/ipfire-2.x/2.25-core141/ipfire-2.25.x86_64-full-core141.iso"
"https://downloads.ipfire.org/releases/ipfire-2.x/2.25-core157/ipfire-2.25.x86_64-full-core157.iso"
],
"ssh_host": "{{user `ssh_host_addr`}}",
"ssh_password": "breach",
Expand Down Expand Up @@ -58,8 +58,8 @@
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>internetrouter<tab><spacebar>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>localdomain<tab><spacebar>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>breach<tab>breach<tab><spacebar>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>breach<tab>breach<tab><spacebar>",
"<wait10s><tab><spacebar>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>breach<tab>breach<tab><spacebar>",
"<wait10s><tab><spacebar>",
"<wait><down><down><tab><spacebar>",
"<wait><down><tab><spacebar>",
"<wait><tab><spacebar>",
Expand All @@ -82,7 +82,7 @@
"<wait><tab><tab><spacebar>",
"<wait><down><tab><tab><spacebar>",
"<wait><tab><spacebar>",
"<wait><spacebar>",
"<wait><spacebar>",
"<wait60s>",
"root<wait><enter>breach<wait><enter>",
"<wait20s>iptables -I INPUT -p tcp --dport 444 -j ACCEPT<enter>",
Expand All @@ -96,9 +96,10 @@
"<wait>sed -i 's/Port 22/Port 222/g' /etc/ssh/sshd_config<enter>",
"<wait>/etc/rc.d/init.d/sshd restart<enter>",
"<wait10s>/etc/init.d/sshd restart<enter>",
"<wait>echo '3,8.8.8.8,,enabled,' > /var/ipfire/dns/servers<enter>",
"<wait10s>/etc/init.d/unbound restart<enter>",
"<wait10s>"
],

"export_opts": [
"--manifest",
"--vsys",
Expand All @@ -111,9 +112,7 @@
"output_directory": "{{user `vm_output`}}",
"keep_registered": "true",
"skip_export": "true",

"shutdown_command": "echo 'packer' | shutdown -h -P now",

"vboxmanage_post": [
[
"modifyvm",
Expand Down Expand Up @@ -145,5 +144,4 @@
"type": "ansible"
}
]
}

}
15 changes: 11 additions & 4 deletions provisioning/packer/post_install/attacker_setup.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
#!/usr/bin/env bash

# Enable root account, set password and reboot
touch /tmp/runasroot.sh

echo "wget https://archive.kali.org/archive-key.asc -O /etc/apt/trusted.gpg.d/kali-archive-keyring.asc" > /tmp/runasroot.sh
echo "apt update" >> /tmp/runasroot.sh
echo "apt install kali-root-login" >> /tmp/runasroot.sh
# Set up DNS
# echo "apt-get purge resolvconf -y" >> /tmp/runasroot.sh
echo "echo 'nameserver 172.18.0.1' > /etc/resolv.conf" > /tmp/runasroot.sh

# Update certificates
echo "wget http://http.kali.org/kali/pool/main/k/kali-archive-keyring/kali-archive-keyring_2024.1_all.deb -O /tmp/kali-archive-keyring_2024.1_all.deb" >> /tmp/runasroot.sh
echo "dpkg -i /tmp/kali-archive-keyring_2024.1_all.deb" >> /tmp/runasroot.sh
echo "apt-get update" >> /tmp/runasroot.sh

# Enable root account, set password and reboot
echo "apt-get install -y kali-root-login" >> /tmp/runasroot.sh
echo "echo 'root:breach' | chpasswd" >> /tmp/runasroot.sh
echo "reboot" >> /tmp/runasroot.sh

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ ansible==8.7.0
colorama==0.4.6
paramiko==3.4.1
pytest==8.3.3
pyvmomi==6.7.1.2018.12
pyvmomi==8.0.3.0.1
pywinrm==0.4.1
selenium==4.3.0
tox==4.18.1
Expand Down
37 changes: 23 additions & 14 deletions src/vmcontrol/sessionhandler/sessionhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class SessionConfig(DictNamespace):
"Company Router",
"Log Server",
"Internal Server",
"DMZ Server"]
"DMZ Server",
]
client_vm = "Client"
number_of_clones = 3
vm_start_timeout = 0
Expand All @@ -54,14 +55,18 @@ class Clone(DictNamespace):
user = None
password = None
domain = None
vrde_port = None


class CloneCreator:
def __init__(self, father_vm, base_snapshot, number_of_clones, vmm_controller: VMMController):
self.father_vm = father_vm
def __init__(
self, parent_vm, base_snapshot, number_of_clones, vmm_controller: VMMController, vrde_port_start=5000
):
self.parent_vm = parent_vm
self.base_snapshot = base_snapshot
self.number_of_clones = number_of_clones
self.vmmc = vmm_controller
self.vrde_port_start = vrde_port_start
self._current_vms = self.vmmc.get_vms()
self._clones = None

Expand All @@ -77,9 +82,10 @@ def set_data(self, clone):
self.set_management_mac(clone)
self.set_internal_mac(clone)
self.set_credentials(clone)
self.set_vrde_port(clone)

def set_vm_name(self, clone):
clone.vm = self.father_vm + "Clone" + str(clone.id)
clone.vm = self.parent_vm + "Clone" + str(clone.id)
while clone.vm in self._current_vms:
clone.vm += "a"

Expand All @@ -94,10 +100,14 @@ def set_credentials(self, clone):
clone.password = "breach"
clone.domain = "BREACH"

def set_vrde_port(self, clone):
clone.vrde_port = self.vrde_port_start + clone.id - 1

def create_vm(self, clone):
self.vmmc.clone(self.father_vm, self.base_snapshot, clone.vm)
self.vmmc.clone(self.parent_vm, self.base_snapshot, clone.vm)
self.vmmc.set_mac(clone.vm, clone.management_mac, if_id=2)
self.vmmc.set_mac(clone.vm, clone.internal_mac, if_id=1)
self.vmmc.set_vrde_port(clone.vm, clone.vrde_port)


class SessionHandler:
Expand Down Expand Up @@ -192,10 +202,9 @@ def create_backup_snapshots(self, vms):

def create_clones(self):
logger.info("Creating clones")
father_vm = self.config.client_vm
base_snapshot = self.backup_snapshots[father_vm]
clone_creator = self.clone_creator_class(
father_vm, base_snapshot, self.config.number_of_clones, self.vmmc)
parent_vm = self.config.client_vm
base_snapshot = self.backup_snapshots[parent_vm]
clone_creator = self.clone_creator_class(parent_vm, base_snapshot, self.config.number_of_clones, self.vmmc)
self.clones = clone_creator.create()

def start_all_vms(self):
Expand Down Expand Up @@ -256,8 +265,8 @@ def restore_delete_snapshots(self, snapshots):
time.sleep(timeout)
if snaps:
raise SessionHandlerException(
"Could not restore and delete all snapshots. Failing: {snaps}"
.format(snaps=snaps))
"Could not restore and delete all snapshots. Failing: {snaps}".format(snaps=snaps)
)

def poweroff_vms(self, vms):
fails, max_fails, timeout = 0, 100, 0.01
Expand All @@ -272,7 +281,8 @@ def poweroff_vms(self, vms):
time.sleep(timeout)
if running_vms:
raise SessionHandlerException(
"Could not poweroff all machines. Still alive: {vms}".format(vms=running_vms))
"Could not poweroff all machines. Still alive: {vms}".format(vms=running_vms)
)

def delete_vms(self, vms):
fails, max_fails, timeout = 0, 100, 0.01
Expand All @@ -287,8 +297,7 @@ def delete_vms(self, vms):
existing_vms.append(vm)
time.sleep(timeout)
if existing_vms:
raise SessionHandlerException(
"Could not delete all machines. Still there: {vms}".format(vms=existing_vms))
raise SessionHandlerException("Could not delete all machines. Still there: {vms}".format(vms=existing_vms))

def take_vm_start_timeout(self):
time.sleep(self.config.vm_start_timeout)
Expand Down
Loading

0 comments on commit 1e4fce2

Please sign in to comment.