monkey/infection_monkey/system_info/__init__.py

134 lines
4.6 KiB
Python
Raw Normal View History

import logging
2015-12-09 22:33:44 +08:00
import socket
import sys
2015-12-09 22:33:44 +08:00
import psutil
from enum import IntEnum
from network.info import get_host_subnets
from azure_cred_collector import AzureCollector
LOG = logging.getLogger(__name__)
2017-09-28 22:56:34 +08:00
# Linux doesn't have WindowsError
try:
WindowsError
except NameError:
WindowsError = None
__author__ = 'uri'
class OperatingSystem(IntEnum):
Windows = 0
Linux = 1
class SystemInfoCollector(object):
"""
A class that checks the current operating system and calls system information collecting modules accordingly
"""
def __init__(self):
self.os = SystemInfoCollector.get_os()
if OperatingSystem.Windows == self.os:
from windows_info_collector import WindowsInfoCollector
self.collector = WindowsInfoCollector()
else:
from linux_info_collector import LinuxInfoCollector
self.collector = LinuxInfoCollector()
def get_info(self):
return self.collector.get_info()
@staticmethod
def get_os():
if sys.platform.startswith("win"):
return OperatingSystem.Windows
else:
return OperatingSystem.Linux
2015-12-09 22:33:44 +08:00
class InfoCollector(object):
"""
Generic Info Collection module
"""
2016-08-20 22:58:59 +08:00
2015-12-09 22:33:44 +08:00
def __init__(self):
self.info = {}
def get_hostname(self):
"""
2017-09-12 00:24:18 +08:00
Adds the fully qualified computer hostname to the system information.
:return: None. Updates class information
"""
LOG.debug("Reading hostname")
self.info['hostname'] = socket.getfqdn()
2015-12-09 22:33:44 +08:00
def get_process_list(self):
2017-09-12 00:24:18 +08:00
"""
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: None. Updates class information
2017-09-12 00:24:18 +08:00
"""
LOG.debug("Reading process list")
2015-12-09 22:33:44 +08:00
processes = {}
for process in psutil.process_iter():
try:
processes[process.pid] = {"name": process.name(),
2016-08-20 22:58:59 +08:00
"pid": process.pid,
"ppid": process.ppid(),
"cmdline": " ".join(process.cmdline()),
"full_image_path": process.exe(),
}
except (psutil.AccessDenied, WindowsError):
# we may be running as non root
# and some processes are impossible to acquire in Windows/Linux
# in this case we'll just add what we can
processes[process.pid] = {"name": "null",
"pid": process.pid,
"ppid": process.ppid(),
"cmdline": "ACCESS DENIED",
"full_image_path": "null",
}
continue
2015-12-09 22:33:44 +08:00
self.info['process_list'] = processes
2017-09-10 18:11:51 +08:00
def get_network_info(self):
"""
Adds network information from the host to the system information.
2017-09-12 00:24:18 +08:00
Currently updates with a list of networks accessible from host,
containing host ip and the subnet range.
:return: None. Updates class information
"""
LOG.debug("Reading subnets")
self.info['network_info'] = {'networks': get_host_subnets()}
def get_azure_info(self):
"""
Adds credentials possibly stolen from an Azure VM instance (if we're on one)
Updates the credentials structure, creating it if neccesary (compat with mimikatz)
:return: None. Updates class information
"""
from config import WormConfiguration
if not WormConfiguration.extract_azure_creds:
return
LOG.debug("Harvesting creds if on an Azure machine")
azure_collector = AzureCollector()
if 'credentials' not in self.info:
self.info["credentials"] = {}
azure_creds = azure_collector.extract_stored_credentials()
for cred in azure_creds:
username = cred[0]
password = cred[1]
if username not in self.info["credentials"]:
self.info["credentials"][username] = {}
# we might be losing passwords in case of multiple reset attempts on same username
# or in case another collector already filled in a password for this user
self.info["credentials"][username]['password'] = password
if len(azure_creds) != 0:
self.info["Azure"] = {}
self.info["Azure"]['usernames'] = [cred[0] for cred in azure_creds]