Agent: Add connect() method to IPowerShellClient

This commit is contained in:
Mike Salvatore 2022-03-16 11:55:46 -04:00 committed by Ilija Lazoroski
parent 55f969b44f
commit f9936fe65d
3 changed files with 51 additions and 22 deletions

View File

@ -127,9 +127,10 @@ class PowerShellExploiter(HostExploiter):
encryption=ENCRYPTION_AUTO, encryption=ENCRYPTION_AUTO,
ssl=use_ssl, ssl=use_ssl,
) )
# TODO: Report login attempt or find a better way of detecting if SSL is enabled
PowerShellClient(self.host.ip_addr, credentials, auth_options) # TODO: Report login attempt or find a better way of detecting if SSL is enabled
client = PowerShellClient(self.host.ip_addr, credentials, auth_options)
client.connect()
def _authenticate_via_brute_force( def _authenticate_via_brute_force(
self, credentials: List[Credentials], auth_options: List[AuthOptions] self, credentials: List[Credentials], auth_options: List[AuthOptions]
@ -137,6 +138,7 @@ class PowerShellExploiter(HostExploiter):
for (creds, opts) in zip(credentials, auth_options): for (creds, opts) in zip(credentials, auth_options):
try: try:
client = PowerShellClient(self.host.ip_addr, creds, opts) client = PowerShellClient(self.host.ip_addr, creds, opts)
client.connect()
logger.info( logger.info(
f"Successfully logged into {self.host.ip_addr} using Powershell. User: " f"Successfully logged into {self.host.ip_addr} using Powershell. User: "
f"{creds.username}, Secret Type: {creds.secret_type.name}" f"{creds.username}, Secret Type: {creds.secret_type.name}"

View File

@ -54,6 +54,10 @@ def format_password(credentials: Credentials) -> Optional[str]:
class IPowerShellClient(Protocol, metaclass=abc.ABCMeta): class IPowerShellClient(Protocol, metaclass=abc.ABCMeta):
@abc.abstractmethod
def connect(self) -> str:
pass
@abc.abstractmethod @abc.abstractmethod
def execute_cmd(self, cmd: str) -> str: def execute_cmd(self, cmd: str) -> str:
pass pass
@ -72,14 +76,19 @@ class PowerShellClient(IPowerShellClient):
_set_sensitive_packages_log_level_to_error() _set_sensitive_packages_log_level_to_error()
self._ip_addr = ip_addr self._ip_addr = ip_addr
self._credentials = credentials
self._auth_options = auth_options
self._client = None
def connect(self):
self._client = Client( self._client = Client(
ip_addr, self._ip_addr,
username=credentials.username, username=self._credentials.username,
password=format_password(credentials), password=format_password(self._credentials),
cert_validation=False, cert_validation=False,
auth=auth_options.auth_type, auth=self._auth_options.auth_type,
encryption=auth_options.encryption, encryption=self._auth_options.encryption,
ssl=auth_options.ssl, ssl=self._auth_options.ssl,
connection_timeout=CONNECTION_TIMEOUT, connection_timeout=CONNECTION_TIMEOUT,
) )

View File

@ -58,8 +58,11 @@ def powershell_exploiter(monkeypatch):
def test_powershell_disabled(monkeypatch, powershell_exploiter, powershell_arguments): def test_powershell_disabled(monkeypatch, powershell_exploiter, powershell_arguments):
mock_powershell_client = MagicMock(side_effect=Exception) mock_powershell_client = MagicMock()
monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) mock_powershell_client.connect = MagicMock(side_effect=Exception)
monkeypatch.setattr(
powershell, "PowerShellClient", MagicMock(return_value=mock_powershell_client)
)
exploit_result = powershell_exploiter.exploit_host(**powershell_arguments) exploit_result = powershell_exploiter.exploit_host(**powershell_arguments)
assert not exploit_result.exploitation_success assert not exploit_result.exploitation_success
@ -74,8 +77,12 @@ def test_powershell_http(monkeypatch, powershell_exploiter, powershell_arguments
else: else:
raise Exception raise Exception
mock_powershell_client = MagicMock(side_effect=allow_http) mock_powershell_client = MagicMock()
monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) mock_powershell_client.connect = MagicMock(side_effect=allow_http)
monkeypatch.setattr(
powershell, "PowerShellClient", MagicMock(return_value=mock_powershell_client)
)
powershell_exploiter.exploit_host(**powershell_arguments) powershell_exploiter.exploit_host(**powershell_arguments)
for call_args in mock_powershell_client.call_args_list: for call_args in mock_powershell_client.call_args_list:
@ -89,8 +96,12 @@ def test_powershell_https(monkeypatch, powershell_exploiter, powershell_argument
else: else:
raise Exception raise Exception
mock_powershell_client = MagicMock(side_effect=allow_https) mock_powershell_client = MagicMock()
monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) mock_powershell_client.connect = MagicMock(side_effect=allow_https)
monkeypatch.setattr(
powershell, "PowerShellClient", MagicMock(return_value=mock_powershell_client)
)
powershell_exploiter.exploit_host(**powershell_arguments) powershell_exploiter.exploit_host(**powershell_arguments)
for call_args in mock_powershell_client.call_args_list: for call_args in mock_powershell_client.call_args_list:
@ -99,8 +110,11 @@ def test_powershell_https(monkeypatch, powershell_exploiter, powershell_argument
def test_no_valid_credentials(monkeypatch, powershell_exploiter, powershell_arguments): def test_no_valid_credentials(monkeypatch, powershell_exploiter, powershell_arguments):
mock_powershell_client = MagicMock(side_effect=AuthenticationErrorForTests) mock_powershell_client = MagicMock()
monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) mock_powershell_client.connect = MagicMock(side_effect=AuthenticationErrorForTests)
monkeypatch.setattr(
powershell, "PowerShellClient", MagicMock(return_value=mock_powershell_client)
)
exploit_result = powershell_exploiter.exploit_host(**powershell_arguments) exploit_result = powershell_exploiter.exploit_host(**powershell_arguments)
assert not exploit_result.exploitation_success assert not exploit_result.exploitation_success
@ -142,13 +156,14 @@ def test_failed_copy(monkeypatch, powershell_exploiter, powershell_arguments):
def test_failed_monkey_execution(monkeypatch, powershell_exploiter, powershell_arguments): def test_failed_monkey_execution(monkeypatch, powershell_exploiter, powershell_arguments):
mock_client = MagicMock() mock_powershell_client = MagicMock()
mock_client.execute_cmd_as_detached_process = MagicMock( mock_powershell_client.execute_cmd_as_detached_process = MagicMock(
side_effect=Exception("EXECUTION FAILED") side_effect=Exception("EXECUTION FAILED")
) )
mock_powershell_client = MagicMock(side_effect=authenticate(mock_client)) monkeypatch.setattr(
monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) powershell, "PowerShellClient", MagicMock(return_value=mock_powershell_client)
)
exploit_result = powershell_exploiter.exploit_host(**powershell_arguments) exploit_result = powershell_exploiter.exploit_host(**powershell_arguments)
assert exploit_result.exploitation_success is True assert exploit_result.exploitation_success is True
@ -172,8 +187,11 @@ def test_login_attempts_correctly_reported(monkeypatch, powershell_exploiter, po
# login attempt doesn't throw an exception, signifying that login with credentials was # login attempt doesn't throw an exception, signifying that login with credentials was
# successful. # successful.
connection_attempts = [True, Exception, Exception, Exception, Exception, Exception, True] connection_attempts = [True, Exception, Exception, Exception, Exception, Exception, True]
mock_client = MagicMock(side_effect=connection_attempts) mock_powershell_client = MagicMock(side_effect=connection_attempts)
monkeypatch.setattr(powershell, "PowerShellClient", mock_client) mock_powershell_client.connect = MagicMock(side_effect=connection_attempts)
monkeypatch.setattr(
powershell, "PowerShellClient", MagicMock(return_value=mock_powershell_client)
)
exploit_result = powershell_exploiter.exploit_host(**powershell_arguments) exploit_result = powershell_exploiter.exploit_host(**powershell_arguments)