forked from p15670423/monkey
Merge pull request #73 from guardicore/feature/async-scan
Feature/async scan
This commit is contained in:
commit
9f814f0a86
|
@ -13,7 +13,7 @@ from exploit import HostExploiter
|
||||||
from exploit.tools import HTTPTools, get_monkey_depth
|
from exploit.tools import HTTPTools, get_monkey_depth
|
||||||
from exploit.tools import get_target_monkey
|
from exploit.tools import get_target_monkey
|
||||||
from model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS
|
from model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS
|
||||||
from network.tools import check_port_tcp
|
from network.tools import check_tcp_port
|
||||||
from tools import build_monkey_commandline
|
from tools import build_monkey_commandline
|
||||||
|
|
||||||
__author__ = 'hoffer'
|
__author__ = 'hoffer'
|
||||||
|
@ -245,7 +245,7 @@ class RdpExploiter(HostExploiter):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if not self.host.os.get('type'):
|
if not self.host.os.get('type'):
|
||||||
is_open, _ = check_port_tcp(self.host.ip_addr, RDP_PORT)
|
is_open, _ = check_tcp_port(self.host.ip_addr, RDP_PORT)
|
||||||
if is_open:
|
if is_open:
|
||||||
self.host.os['type'] = 'windows'
|
self.host.os['type'] = 'windows'
|
||||||
return True
|
return True
|
||||||
|
@ -254,7 +254,7 @@ class RdpExploiter(HostExploiter):
|
||||||
def exploit_host(self):
|
def exploit_host(self):
|
||||||
global g_reactor
|
global g_reactor
|
||||||
|
|
||||||
is_open, _ = check_port_tcp(self.host.ip_addr, RDP_PORT)
|
is_open, _ = check_tcp_port(self.host.ip_addr, RDP_PORT)
|
||||||
if not is_open:
|
if not is_open:
|
||||||
LOG.info("RDP port is closed on %r, skipping", self.host)
|
LOG.info("RDP port is closed on %r, skipping", self.host)
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -7,7 +7,7 @@ from exploit import HostExploiter
|
||||||
from exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
|
from exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
|
||||||
from model import MONKEY_CMDLINE_DETACHED_WINDOWS, DROPPER_CMDLINE_DETACHED_WINDOWS
|
from model import MONKEY_CMDLINE_DETACHED_WINDOWS, DROPPER_CMDLINE_DETACHED_WINDOWS
|
||||||
from network import SMBFinger
|
from network import SMBFinger
|
||||||
from network.tools import check_port_tcp
|
from network.tools import check_tcp_port
|
||||||
from tools import build_monkey_commandline
|
from tools import build_monkey_commandline
|
||||||
|
|
||||||
LOG = getLogger(__name__)
|
LOG = getLogger(__name__)
|
||||||
|
@ -31,12 +31,12 @@ class SmbExploiter(HostExploiter):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if not self.host.os.get('type'):
|
if not self.host.os.get('type'):
|
||||||
is_smb_open, _ = check_port_tcp(self.host.ip_addr, 445)
|
is_smb_open, _ = check_tcp_port(self.host.ip_addr, 445)
|
||||||
if is_smb_open:
|
if is_smb_open:
|
||||||
smb_finger = SMBFinger()
|
smb_finger = SMBFinger()
|
||||||
smb_finger.get_host_fingerprint(self.host)
|
smb_finger.get_host_fingerprint(self.host)
|
||||||
else:
|
else:
|
||||||
is_nb_open, _ = check_port_tcp(self.host.ip_addr, 139)
|
is_nb_open, _ = check_tcp_port(self.host.ip_addr, 139)
|
||||||
if is_nb_open:
|
if is_nb_open:
|
||||||
self.host.os['type'] = 'windows'
|
self.host.os['type'] = 'windows'
|
||||||
return self.host.os.get('type') in self._TARGET_OS_TYPE
|
return self.host.os.get('type') in self._TARGET_OS_TYPE
|
||||||
|
|
|
@ -7,7 +7,7 @@ import monkeyfs
|
||||||
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 model import MONKEY_ARG
|
from model import MONKEY_ARG
|
||||||
from network.tools import check_port_tcp
|
from network.tools import check_tcp_port
|
||||||
from tools import build_monkey_commandline
|
from tools import build_monkey_commandline
|
||||||
|
|
||||||
__author__ = 'hoffer'
|
__author__ = 'hoffer'
|
||||||
|
@ -41,7 +41,7 @@ class SSHExploiter(HostExploiter):
|
||||||
if servdata.get('name') == 'ssh' and servkey.startswith('tcp-'):
|
if servdata.get('name') == 'ssh' and servkey.startswith('tcp-'):
|
||||||
port = int(servkey.replace('tcp-', ''))
|
port = int(servkey.replace('tcp-', ''))
|
||||||
|
|
||||||
is_open, _ = check_port_tcp(self.host.ip_addr, port)
|
is_open, _ = check_tcp_port(self.host.ip_addr, port)
|
||||||
if not is_open:
|
if not is_open:
|
||||||
LOG.info("SSH port is closed on %r, skipping", self.host)
|
LOG.info("SSH port is closed on %r, skipping", self.host)
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -17,7 +17,7 @@ from impacket.dcerpc.v5 import transport
|
||||||
from exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
|
from exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
|
||||||
from model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
|
from model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
|
||||||
from network import SMBFinger
|
from network import SMBFinger
|
||||||
from network.tools import check_port_tcp
|
from network.tools import check_tcp_port
|
||||||
from tools import build_monkey_commandline
|
from tools import build_monkey_commandline
|
||||||
from . import HostExploiter
|
from . import HostExploiter
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ class Ms08_067_Exploiter(HostExploiter):
|
||||||
|
|
||||||
if not self.host.os.get('type') or (
|
if not self.host.os.get('type') or (
|
||||||
self.host.os.get('type') in self._TARGET_OS_TYPE and not self.host.os.get('version')):
|
self.host.os.get('type') in self._TARGET_OS_TYPE and not self.host.os.get('version')):
|
||||||
is_smb_open, _ = check_port_tcp(self.host.ip_addr, 445)
|
is_smb_open, _ = check_tcp_port(self.host.ip_addr, 445)
|
||||||
if is_smb_open:
|
if is_smb_open:
|
||||||
smb_finger = SMBFinger()
|
smb_finger = SMBFinger()
|
||||||
if smb_finger.get_host_fingerprint(self.host):
|
if smb_finger.get_host_fingerprint(self.host):
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import re
|
import re
|
||||||
from network import HostFinger
|
|
||||||
from network.tools import check_port_tcp
|
|
||||||
from model.host import VictimHost
|
from model.host import VictimHost
|
||||||
|
from network import HostFinger
|
||||||
|
from network.tools import check_tcp_port
|
||||||
|
|
||||||
SSH_PORT = 22
|
SSH_PORT = 22
|
||||||
SSH_SERVICE_DEFAULT = 'tcp-22'
|
SSH_SERVICE_DEFAULT = 'tcp-22'
|
||||||
|
@ -38,7 +39,7 @@ class SSHFinger(HostFinger):
|
||||||
self._banner_match(name, host, banner)
|
self._banner_match(name, host, banner)
|
||||||
return
|
return
|
||||||
|
|
||||||
is_open, banner = check_port_tcp(host.ip_addr, SSH_PORT, TIMEOUT, True)
|
is_open, banner = check_tcp_port(host.ip_addr, SSH_PORT, TIMEOUT, True)
|
||||||
|
|
||||||
if is_open:
|
if is_open:
|
||||||
host.services[SSH_SERVICE_DEFAULT] = {}
|
host.services[SSH_SERVICE_DEFAULT] = {}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import time
|
|
||||||
from random import shuffle
|
from random import shuffle
|
||||||
|
|
||||||
from network import HostScanner, HostFinger
|
from network import HostScanner, HostFinger
|
||||||
from model.host import VictimHost
|
from network.tools import check_tcp_ports
|
||||||
from network.tools import check_port_tcp
|
|
||||||
|
|
||||||
__author__ = 'itamar'
|
__author__ = 'itamar'
|
||||||
|
|
||||||
|
@ -17,29 +16,24 @@ class TcpScanner(HostScanner, HostFinger):
|
||||||
return self.get_host_fingerprint(host, True)
|
return self.get_host_fingerprint(host, True)
|
||||||
|
|
||||||
def get_host_fingerprint(self, host, only_one_port=False):
|
def get_host_fingerprint(self, host, only_one_port=False):
|
||||||
assert isinstance(host, VictimHost)
|
"""
|
||||||
|
Scans a target host to see if it's alive using the tcp_target_ports specified in the configuration.
|
||||||
|
:param host: VictimHost structure
|
||||||
|
:param only_one_port: Currently unused.
|
||||||
|
:return: T/F if there is at least one open port. In addition, the host object is updated to mark those services as alive.
|
||||||
|
"""
|
||||||
|
|
||||||
count = 0
|
|
||||||
# maybe hide under really bad detection systems
|
# maybe hide under really bad detection systems
|
||||||
target_ports = self._config.tcp_target_ports[:]
|
target_ports = self._config.tcp_target_ports[:]
|
||||||
shuffle(target_ports)
|
shuffle(target_ports)
|
||||||
|
|
||||||
for target_port in target_ports:
|
ports, banners = check_tcp_ports(host.ip_addr, target_ports, self._config.tcp_scan_timeout / 1000.0)
|
||||||
|
for target_port, banner in zip(ports, banners):
|
||||||
|
service = 'tcp-' + str(target_port)
|
||||||
|
host.services[service] = {}
|
||||||
|
if banner:
|
||||||
|
host.services[service]['banner'] = banner
|
||||||
|
if only_one_port:
|
||||||
|
break
|
||||||
|
|
||||||
is_open, banner = check_port_tcp(host.ip_addr,
|
return len(ports) != 0
|
||||||
target_port,
|
|
||||||
self._config.tcp_scan_timeout / 1000.0,
|
|
||||||
self._config.tcp_scan_get_banner)
|
|
||||||
|
|
||||||
if is_open:
|
|
||||||
count += 1
|
|
||||||
service = 'tcp-' + str(target_port)
|
|
||||||
host.services[service] = {}
|
|
||||||
if banner:
|
|
||||||
host.services[service]['banner'] = banner
|
|
||||||
if only_one_port:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
time.sleep(self._config.tcp_scan_interval / 1000.0)
|
|
||||||
|
|
||||||
return count != 0
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import socket
|
|
||||||
import select
|
|
||||||
import logging
|
import logging
|
||||||
|
import select
|
||||||
|
import socket
|
||||||
import struct
|
import struct
|
||||||
|
import time
|
||||||
|
|
||||||
DEFAULT_TIMEOUT = 10
|
DEFAULT_TIMEOUT = 10
|
||||||
|
SLEEP_BETWEEN_POLL = 0.5
|
||||||
BANNER_READ = 1024
|
BANNER_READ = 1024
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -32,10 +34,18 @@ def struct_unpack_tracker_string(data, index):
|
||||||
"""
|
"""
|
||||||
ascii_len = data[index:].find('\0')
|
ascii_len = data[index:].find('\0')
|
||||||
fmt = "%ds" % ascii_len
|
fmt = "%ds" % ascii_len
|
||||||
return struct_unpack_tracker(data,index,fmt)
|
return struct_unpack_tracker(data, index, fmt)
|
||||||
|
|
||||||
|
|
||||||
def check_port_tcp(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
|
def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
|
||||||
|
"""
|
||||||
|
Checks if a given TCP port is open
|
||||||
|
:param ip: Target IP
|
||||||
|
:param port: Target Port
|
||||||
|
:param timeout: Timeout for socket connection
|
||||||
|
:param get_banner: if true, pulls first BANNER_READ bytes from the socket.
|
||||||
|
:return: Tuple, T/F + banner if requested.
|
||||||
|
"""
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.settimeout(timeout)
|
sock.settimeout(timeout)
|
||||||
|
|
||||||
|
@ -43,7 +53,7 @@ def check_port_tcp(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
|
||||||
sock.connect((ip, port))
|
sock.connect((ip, port))
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
return False, None
|
return False, None
|
||||||
except socket.error, exc:
|
except socket.error as exc:
|
||||||
LOG.debug("Check port: %s:%s, Exception: %s", ip, port, exc)
|
LOG.debug("Check port: %s:%s, Exception: %s", ip, port, exc)
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
|
@ -54,14 +64,21 @@ def check_port_tcp(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
|
||||||
read_ready, _, _ = select.select([sock], [], [], timeout)
|
read_ready, _, _ = select.select([sock], [], [], timeout)
|
||||||
if len(read_ready) > 0:
|
if len(read_ready) > 0:
|
||||||
banner = sock.recv(BANNER_READ)
|
banner = sock.recv(BANNER_READ)
|
||||||
except:
|
except socket.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
sock.close()
|
sock.close()
|
||||||
return True, banner
|
return True, banner
|
||||||
|
|
||||||
|
|
||||||
def check_port_udp(ip, port, timeout=DEFAULT_TIMEOUT):
|
def check_udp_port(ip, port, timeout=DEFAULT_TIMEOUT):
|
||||||
|
"""
|
||||||
|
Checks if a given UDP port is open by checking if it replies to an empty message
|
||||||
|
:param ip: Target IP
|
||||||
|
:param port: Target port
|
||||||
|
:param timeout: Timeout to wait
|
||||||
|
:return: Tuple, T/F + banner
|
||||||
|
"""
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
sock.settimeout(timeout)
|
sock.settimeout(timeout)
|
||||||
|
|
||||||
|
@ -72,8 +89,71 @@ def check_port_udp(ip, port, timeout=DEFAULT_TIMEOUT):
|
||||||
sock.sendto("-", (ip, port))
|
sock.sendto("-", (ip, port))
|
||||||
data, _ = sock.recvfrom(BANNER_READ)
|
data, _ = sock.recvfrom(BANNER_READ)
|
||||||
is_open = True
|
is_open = True
|
||||||
except:
|
except socket.error:
|
||||||
pass
|
pass
|
||||||
sock.close()
|
sock.close()
|
||||||
|
|
||||||
return is_open, data
|
return is_open, data
|
||||||
|
|
||||||
|
|
||||||
|
def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False):
|
||||||
|
"""
|
||||||
|
Checks whether any of the given ports are open on a target IP.
|
||||||
|
:param ip: IP of host to attack
|
||||||
|
:param ports: List of ports to attack. Must not be empty.
|
||||||
|
:param timeout: Amount of time to wait for connection.
|
||||||
|
:param get_banner: T/F if to get first packets from server
|
||||||
|
:return: list of open ports. If get_banner=True, then a matching list of banners.
|
||||||
|
"""
|
||||||
|
sockets = [socket.socket(socket.AF_INET, socket.SOCK_STREAM) for _ in range(len(ports))]
|
||||||
|
[s.setblocking(0) for s in sockets]
|
||||||
|
port_attempts = []
|
||||||
|
try:
|
||||||
|
for sock, port in zip(sockets, ports):
|
||||||
|
LOG.debug("Connecting to port %d" % port)
|
||||||
|
err = sock.connect_ex((ip, port))
|
||||||
|
if err == 0:
|
||||||
|
port_attempts.append((port, sock))
|
||||||
|
if err == 10035: # WSAEWOULDBLOCK is valid, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
|
||||||
|
port_attempts.append((port, sock))
|
||||||
|
if len(port_attempts) != 0:
|
||||||
|
num_replies = 0
|
||||||
|
timeout = int(round(timeout)) # clamp to integer, to avoid checking input
|
||||||
|
time_left = timeout
|
||||||
|
write_sockets = []
|
||||||
|
read_sockets = []
|
||||||
|
while num_replies != len(port_attempts):
|
||||||
|
# bad ports show up as err_sockets
|
||||||
|
read_sockets, write_sockets, err_sockets = \
|
||||||
|
select.select(
|
||||||
|
[s[1] for s in port_attempts],
|
||||||
|
[s[1] for s in port_attempts],
|
||||||
|
[s[1] for s in port_attempts],
|
||||||
|
time_left)
|
||||||
|
# any read_socket is automatically a writesocket
|
||||||
|
num_replies = len(write_sockets) + len(err_sockets)
|
||||||
|
if num_replies == len(port_attempts) or time_left <= 0:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
time_left -= SLEEP_BETWEEN_POLL
|
||||||
|
time.sleep(SLEEP_BETWEEN_POLL)
|
||||||
|
|
||||||
|
connected_ports_sockets = [x for x in port_attempts if x[1] in write_sockets]
|
||||||
|
LOG.debug(
|
||||||
|
"On host %s discovered the following ports %s" %
|
||||||
|
(str(ip), ",".join([str(x) for x in connected_ports_sockets])))
|
||||||
|
banners = []
|
||||||
|
if get_banner:
|
||||||
|
# read first X bytes
|
||||||
|
banners = [sock.recv(BANNER_READ) if sock in read_sockets else ""
|
||||||
|
for port, sock in connected_ports_sockets]
|
||||||
|
pass
|
||||||
|
# try to cleanup
|
||||||
|
[s[1].close() for s in port_attempts]
|
||||||
|
return [port for port, sock in connected_ports_sockets], banners
|
||||||
|
else:
|
||||||
|
return [], []
|
||||||
|
|
||||||
|
except socket.error as exc:
|
||||||
|
LOG.warning("Exception when checking ports on host %s, Exception: %s", str(ip), exc)
|
||||||
|
return [], []
|
||||||
|
|
|
@ -8,7 +8,7 @@ from threading import Thread
|
||||||
from model import VictimHost
|
from model import VictimHost
|
||||||
from network.firewall import app as firewall
|
from network.firewall import app as firewall
|
||||||
from network.info import local_ips, get_free_tcp_port
|
from network.info import local_ips, get_free_tcp_port
|
||||||
from network.tools import check_port_tcp
|
from network.tools import check_tcp_port
|
||||||
from transport.base import get_last_serve_time
|
from transport.base import get_last_serve_time
|
||||||
|
|
||||||
__author__ = 'hoffer'
|
__author__ = 'hoffer'
|
||||||
|
@ -40,7 +40,7 @@ def _check_tunnel(address, port, existing_sock=None):
|
||||||
sock = existing_sock
|
sock = existing_sock
|
||||||
|
|
||||||
LOG.debug("Checking tunnel %s:%s", address, port)
|
LOG.debug("Checking tunnel %s:%s", address, port)
|
||||||
is_open, _ = check_port_tcp(address, int(port))
|
is_open, _ = check_tcp_port(address, int(port))
|
||||||
if not is_open:
|
if not is_open:
|
||||||
LOG.debug("Could not connect to %s:%s", address, port)
|
LOG.debug("Could not connect to %s:%s", address, port)
|
||||||
if not existing_sock:
|
if not existing_sock:
|
||||||
|
|
Loading…
Reference in New Issue