Merge pull request #45 from guardicore/feature/network_info

Feature/network info
This commit is contained in:
itaymmguardicore 2017-09-12 13:55:13 +03:00 committed by GitHub
commit 1fe9fddd7f
4 changed files with 72 additions and 65 deletions

View File

@ -1,81 +1,56 @@
import os import os
import sys import sys
import array
import socket import socket
import struct import struct
import psutil import psutil
import ipaddress import ipaddress
import itertools
import netifaces
from subprocess import check_output from subprocess import check_output
from random import randint from random import randint
if sys.platform == "win32":
import netifaces
def get_host_subnets():
"""
Returns a list of subnets visible to host (omitting loopback and auto conf networks)
Each subnet item contains the host IP in that network + the subnet.
:return: List of dict, keys are "addr" and "subnet"
"""
ipv4_nets = [netifaces.ifaddresses(interface)[netifaces.AF_INET]
for interface in netifaces.interfaces()
if netifaces.AF_INET in netifaces.ifaddresses(interface)
]
# flatten
ipv4_nets = itertools.chain.from_iterable(ipv4_nets)
# remove loopback
ipv4_nets = [network for network in ipv4_nets if network['addr'] != '127.0.0.1']
# remove auto conf
ipv4_nets = [network for network in ipv4_nets if not network['addr'].startswith('169.254')]
for network in ipv4_nets:
if 'broadcast' in network:
network.pop('broadcast')
return ipv4_nets
if sys.platform == "win32":
def local_ips(): def local_ips():
local_hostname = socket.gethostname() local_hostname = socket.gethostname()
return socket.gethostbyname_ex(local_hostname)[2] return socket.gethostbyname_ex(local_hostname)[2]
def get_host_subnets(only_ips=False):
network_adapters = []
valid_ips = local_ips()
if only_ips:
return valid_ips
interfaces = [netifaces.ifaddresses(x) for x in netifaces.interfaces()]
for inte in interfaces:
if netifaces.AF_INET in inte:
for add in inte[netifaces.AF_INET]:
if "netmask" in add and add["addr"] in valid_ips:
network_adapters.append((add["addr"], add["netmask"]))
return network_adapters
def get_routes(): def get_routes():
raise NotImplementedError() raise NotImplementedError()
else: else:
from fcntl import ioctl from fcntl import ioctl
def get_host_subnets(only_ips=False):
"""Get the list of Linux network adapters."""
max_bytes = 8096
is_64bits = sys.maxsize > 2 ** 32
if is_64bits:
offset1 = 16
offset2 = 40
else:
offset1 = 32
offset2 = 32
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
names = array.array('B', '\0' * max_bytes)
outbytes = struct.unpack('iL', ioctl(
sock.fileno(),
0x8912,
struct.pack('iL', max_bytes, names.buffer_info()[0])))[0]
adapter_names = [names.tostring()[n_cnt:n_cnt + offset1].split('\0', 1)[0]
for n_cnt in xrange(0, outbytes, offset2)]
network_adapters = []
for adapter_name in adapter_names:
ip_address = socket.inet_ntoa(ioctl(
sock.fileno(),
0x8915,
struct.pack('256s', adapter_name))[20:24])
if ip_address.startswith('127'):
continue
subnet_mask = socket.inet_ntoa(ioctl(
sock.fileno(),
0x891b,
struct.pack('256s', adapter_name))[20:24])
if only_ips:
network_adapters.append(ip_address)
else:
network_adapters.append((ip_address, subnet_mask))
return network_adapters
def local_ips(): def local_ips():
return get_host_subnets(only_ips=True) ipv4_nets = get_host_subnets()
valid_ips = [network['addr'] for network in ipv4_nets]
return valid_ips
def get_routes(): # based on scapy implementation for route parsing def get_routes(): # based on scapy implementation for route parsing
LOOPBACK_NAME = "lo" LOOPBACK_NAME = "lo"
@ -141,6 +116,11 @@ def get_free_tcp_port(min_range=1000, max_range=65535):
def check_internet_access(services): def check_internet_access(services):
"""
Checks if any of the services are accessible, over ICMP
:param services: List of IPs/hostnames
:return: boolean depending on internet access
"""
ping_str = "-n 1" if sys.platform.startswith("win") else "-c 1" ping_str = "-n 1" if sys.platform.startswith("win") else "-c 1"
for host in services: for host in services:
if os.system("ping " + ping_str + " " + host) == 0: if os.system("ping " + ping_str + " " + host) == 0:
@ -149,19 +129,24 @@ def check_internet_access(services):
def get_ips_from_interfaces(): def get_ips_from_interfaces():
"""
Returns a list of IPs accessible in the host in each network interface, in the subnet.
Limits to a single class C if the network is larger
:return: List of IPs, marked as strings.
"""
res = [] res = []
ifs = get_host_subnets() ifs = get_host_subnets()
for interface in ifs: for net_interface in ifs:
ipint = ipaddress.ip_interface(u"%s/%s" % interface) host_addr = ipaddress.ip_address(net_interface['addr'])
ip_interface = ipaddress.ip_interface(u"%s/%s" % (net_interface['addr'], net_interface['netmask']))
# limit subnet scans to class C only # limit subnet scans to class C only
if ipint.network.num_addresses > 255: if ip_interface.network.num_addresses > 255:
ipint = ipaddress.ip_interface(u"%s/24" % interface[0]) ip_interface = ipaddress.ip_interface(u"%s/24" % net_interface['addr'])
for addr in ipint.network.hosts(): addrs = [str(addr) for addr in ip_interface.network.hosts() if addr != host_addr]
if str(addr) == interface[0]: res.extend(addrs)
continue
res.append(str(addr))
return res return res
if sys.platform == "win32": if sys.platform == "win32":
def get_ip_for_connection(target_ip): def get_ip_for_connection(target_ip):
return None return None
@ -171,7 +156,7 @@ else:
query_str = 'ip route get %s' % target_ip query_str = 'ip route get %s' % target_ip
resp = check_output(query_str.split()) resp = check_output(query_str.split())
substr = resp.split() substr = resp.split()
src = substr[substr.index('src')+1] src = substr[substr.index('src') + 1]
return src return src
except Exception: except Exception:
return None return None

View File

@ -2,6 +2,7 @@ import sys
import socket import socket
import psutil import psutil
from enum import IntEnum from enum import IntEnum
from network.info import get_host_subnets, local_ips
__author__ = 'uri' __author__ = 'uri'
@ -45,9 +46,19 @@ class InfoCollector(object):
self.info = {} self.info = {}
def get_hostname(self): def get_hostname(self):
self.info['hostname'] = socket.gethostname() """
Adds the fully qualified computer hostname to the system information.
:return: Nothing
"""
self.info['hostname'] = socket.getfqdn()
def get_process_list(self): def get_process_list(self):
"""
Adds process information from the host to the system information.
Currently lists process name, ID, parent ID, command line
and the full image path of each process.
:return: Nothing
"""
processes = {} processes = {}
for process in psutil.process_iter(): for process in psutil.process_iter():
try: try:
@ -69,3 +80,12 @@ class InfoCollector(object):
} }
pass pass
self.info['process_list'] = processes self.info['process_list'] = processes
def get_network_info(self):
"""
Adds network information from the host to the system information.
Currently updates with a list of networks accessible from host,
containing host ip and the subnet range.
:return: None
"""
self.info['network_info'] = {'networks': get_host_subnets()}

View File

@ -14,4 +14,5 @@ class LinuxInfoCollector(InfoCollector):
def get_info(self): def get_info(self):
self.get_hostname() self.get_hostname()
self.get_process_list() self.get_process_list()
self.get_network_info()
return self.info return self.info

View File

@ -14,6 +14,7 @@ class WindowsInfoCollector(InfoCollector):
def get_info(self): def get_info(self):
self.get_hostname() self.get_hostname()
self.get_process_list() self.get_process_list()
self.get_network_info()
mimikatz_collector = MimikatzCollector() mimikatz_collector = MimikatzCollector()
self.info["credentials"] = mimikatz_collector.get_logon_info() self.info["credentials"] = mimikatz_collector.get_logon_info()
return self.info return self.info