add support for simple fingerprinting by: ping, smb, ssh and open ports

This commit is contained in:
Barak Hoffer 2015-09-29 17:55:54 +03:00
parent bea2d5e3d4
commit 7697f5fce9
7 changed files with 392 additions and 36 deletions

View File

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

View File

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

View File

@ -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 smbfinger import SMBFinger
from sshfinger import SSHFinger
from info import local_ips

View File

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

View File

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

View File

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

View File

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