Skip to content

Commit

Permalink
Plugin renamed. Added net iface status and other improvements from @eve
Browse files Browse the repository at this point in the history
volatilityfoundation#1029

* IP address conversion via renderers.coversion.*
* Use MAC address internal size instead of hardcoded.
* Read NET_DEVICE_FLAGS from enumeration
  • Loading branch information
gcmoreira committed Jan 27, 2024
1 parent 6b3bbad commit 6e0ffc9
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 37 deletions.
36 changes: 34 additions & 2 deletions volatility3/framework/constants/linux/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,5 +291,37 @@
IFA_LINK = IPV6_ADDR_LINKLOCAL
IFA_SITE = IPV6_ADDR_SITELOCAL

# Promiscous mode
IFF_PROMISC = 0x100
# Only for kernels < 3.15 when the net_device_flags enum didn't exist
# ref include/uapi/linux/if.h
NET_DEVICE_FLAGS = {
"IFF_UP": 0x1,
"IFF_BROADCAST": 0x2,
"IFF_DEBUG": 0x4,
"IFF_LOOPBACK": 0x8,
"IFF_POINTOPOINT": 0x10,
"IFF_NOTRAILERS": 0x20,
"IFF_RUNNING": 0x40,
"IFF_NOARP": 0x80,
"IFF_PROMISC": 0x100,
"IFF_ALLMULTI": 0x200,
"IFF_MASTER": 0x400,
"IFF_SLAVE": 0x800,
"IFF_MULTICAST": 0x1000,
"IFF_PORTSEL": 0x2000,
"IFF_AUTOMEDIA": 0x4000,
"IFF_DYNAMIC": 0x8000,
"IFF_LOWER_UP": 0x10000,
"IFF_DORMANT": 0x20000,
"IFF_ECHO": 0x40000,
}

# RFC 2863 operational status. Kernels >= 2.6.17. See IF_OPER_* in include/uapi/linux/if.h
IF_OPER_STATES = (
"UNKNOWN",
"NOTPRESENT",
"DOWN",
"LOWERLAYERDOWN",
"TESTING",
"DORMANT",
"UP",
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from volatility3.framework.objects import utility


class Ifconfig(plugins.PluginInterface):
class Addr(plugins.PluginInterface):
"""Lists network interface information for all devices"""

_required_framework_version = (2, 0, 0)
Expand All @@ -29,6 +29,7 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
def _gather_net_dev_info(self, net_dev):
mac_addr = net_dev.get_mac_address()
promisc = net_dev.promisc
operational_state = net_dev.get_operational_state()
iface_name = utility.array_to_string(net_dev.name)
iface_ifindex = net_dev.ifindex
try:
Expand All @@ -42,15 +43,15 @@ def _gather_net_dev_info(self, net_dev):
prefix_len = in_ifaddr.get_prefix_len()
scope_type = in_ifaddr.get_scope_type()
ip_addr = in_ifaddr.get_address()
yield net_ns_id, iface_ifindex, iface_name, mac_addr, promisc, ip_addr, prefix_len, scope_type
yield net_ns_id, iface_ifindex, iface_name, mac_addr, promisc, ip_addr, prefix_len, scope_type, operational_state

# Interface IPv6 Addresses
ip6_ptr = net_dev.ip6_ptr.dereference().cast("inet6_dev")
for inet6_ifaddr in ip6_ptr.get_addresses():
inet6_dev = net_dev.ip6_ptr.dereference().cast("inet6_dev")
for inet6_ifaddr in inet6_dev.get_addresses():
prefix_len = inet6_ifaddr.get_prefix_len()
scope_type = inet6_ifaddr.get_scope_type()
ip6_addr = inet6_ifaddr.get_address()
yield net_ns_id, iface_ifindex, iface_name, mac_addr, promisc, ip6_addr, prefix_len, scope_type
yield net_ns_id, iface_ifindex, iface_name, mac_addr, promisc, ip6_addr, prefix_len, scope_type, operational_state

def _generator(self):
vmlinux = self.context.modules[self.config["kernel"]]
Expand All @@ -75,6 +76,7 @@ def run(self):
("IP", str),
("Prefix", int),
("Scope Type", str),
("State", str),
]

return renderers.TreeGrid(headers, self._generator())
83 changes: 53 additions & 30 deletions volatility3/framework/symbols/linux/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@

import collections.abc
import logging
import struct
import socket as socket_module
from typing import Generator, Iterable, Iterator, Optional, Tuple, List
from typing import Generator, Iterable, Iterator, Optional, Tuple, List, Dict

from volatility3.framework import constants
from volatility3.framework.constants.linux import SOCK_TYPES, SOCK_FAMILY
from volatility3.framework.constants.linux import IP_PROTOCOLS, IPV6_PROTOCOLS
from volatility3.framework.constants.linux import TCP_STATES, NETLINK_PROTOCOLS
from volatility3.framework.constants.linux import ETH_PROTOCOLS, BLUETOOTH_STATES
from volatility3.framework.constants.linux import BLUETOOTH_PROTOCOLS, SOCKET_STATES
from volatility3.framework.constants.linux import CAPABILITIES, IFF_PROMISC
from volatility3.framework.constants.linux import CAPABILITIES, NET_DEVICE_FLAGS
from volatility3.framework.constants.linux import IFA_HOST, IFA_LINK, IFA_SITE
from volatility3.framework.constants.linux import IF_OPER_STATES
from volatility3.framework.renderers import conversion
from volatility3.framework import exceptions, objects, interfaces, symbols
from volatility3.framework.layers import linear
from volatility3.framework.objects import utility
Expand Down Expand Up @@ -1236,24 +1237,25 @@ def get_inode(self):


class net_device(objects.StructType):
@staticmethod
def _format_as_mac_address(hwaddr):
return ":".join([f"{x:02x}" for x in hwaddr[:6]])
def _format_as_mac_address(self, hwaddr):
return ":".join([f"{x:02x}" for x in hwaddr[: self.addr_len]])

def get_mac_address(self):
def get_mac_address(self) -> str:
"""Get the MAC address of this network interface.
Returns:
str: the MAC address of this network interface.
"""
if self.has_member("perm_addr"):
null_mac_addr_bytes = b"\x00" * self.addr_len
null_mac_addr = self._format_as_mac_address(null_mac_addr_bytes)
mac_addr = self._format_as_mac_address(self.perm_addr)
if mac_addr != "00:00:00:00:00:00":
if mac_addr != null_mac_addr:
return mac_addr

parent_layer = self._context.layers[self.vol.layer_name]
try:
hwaddr = parent_layer.read(self.dev_addr, 6)
hwaddr = parent_layer.read(self.dev_addr, self.addr_len, pad=True)
except exceptions.InvalidAddressException:
vollog.debug(
f"Unable to read network inteface mac address from {self.dev_addr:#x}"
Expand All @@ -1262,16 +1264,41 @@ def get_mac_address(self):

return self._format_as_mac_address(hwaddr)

def _get_flag_choices(self) -> Dict:
"""Return the net_deivce flags as a list of strings"""
vmlinux = linux.LinuxUtilities.get_module_from_volobj_type(self._context, self)
try:
# kernels >= 3.15
net_device_flags_enum = vmlinux.get_enumeration("net_device_flags")
choices = net_device_flags_enum.choices
except exceptions.SymbolError:
# kernels < 3.15
choices = NET_DEVICE_FLAGS

return choices

def _get_net_device_flag_value(self, name):
"""Return the net_deivce flag value based on the flag name"""
return self._get_flag_choices()[name]

def get_flag_names(self) -> List[str]:
"""Return the net_deivce flags as a list of strings
Returns:
List[str]: A list of flag names
"""
return list(self._get_flag_choices())

@property
def promisc(self):
"""Return if this network interface is in promiscuous mode.
Returns:
bool: True if this network interface is in promiscuous mode. Otherwise, False
"""
return self.flags & IFF_PROMISC == IFF_PROMISC
return self.flags & self._get_net_device_flag_value("IFF_PROMISC") != 0

def get_net_namespace_id(self):
def get_net_namespace_id(self) -> int:
"""Return the network namespace id for this network interface.
Returns:
Expand All @@ -1288,6 +1315,17 @@ def get_net_namespace_id(self):

return net_ns_id

def get_operational_state(self) -> str:
"""Return the netwok device oprational state (RFC 2863) string
Returns:
str: A string with the operational state
"""
if self.operstate >= len(IF_OPER_STATES):
vollog.warning(f"Invalid net_device operational state '{self.operstate}'")
return "INVALID"

return IF_OPER_STATES[self.operstate]

class in_device(objects.StructType):
def get_addresses(self):
Expand Down Expand Up @@ -1320,7 +1358,7 @@ def get_addresses(self):
return

symbol_space = self._context.symbol_space
table_name = self.vol.type_name.split(constants.BANG)[0]
table_name = self.get_symbol_table_name()
inet6_ifaddr_symname = table_name + constants.BANG + "inet6_ifaddr"
if not symbol_space.has_type(inet6_ifaddr_symname) or not symbol_space.get_type(
inet6_ifaddr_symname
Expand Down Expand Up @@ -1351,7 +1389,7 @@ def get_scope_type(self):
Returns:
str: the IPv4 scope type.
"""
table_name = self.vol.type_name.split(constants.BANG)[0]
table_name = self.get_symbol_table_name()
rt_scope_enum = self._context.symbol_space.get_enumeration(
table_name + constants.BANG + "rt_scope_t"
)
Expand All @@ -1368,8 +1406,7 @@ def get_address(self):
Returns:
str: the IPv4 address
"""
ipv4_bytes = struct.pack("<I", self.ifa_address)
return socket_module.inet_ntop(socket_module.AF_INET, ipv4_bytes)
return conversion.convert_ipv4(self.ifa_address)

def get_prefix_len(self):
"""Get the IPv4 address prefix len
Expand Down Expand Up @@ -1402,21 +1439,7 @@ def get_address(self):
Returns:
str: the IPv6 address
"""
symbol_space = self._context.symbol_space
table_name = self.vol.type_name.split(constants.BANG)[0]
in6_addr_symname = table_name + constants.BANG + "in6_addr"
in6_addr_size = symbol_space.get_type(in6_addr_symname).size

parent_layer = self._context.layers[self.vol.layer_name]
try:
ip6_addr_bytes = parent_layer.read(self.addr.vol.offset, in6_addr_size)
except exceptions.InvalidAddressException:
vollog.debug(
f"Unable to read network IPv6 address from {self.addr.vol.offset:#x}"
)
return

return socket_module.inet_ntop(socket_module.AF_INET6, ip6_addr_bytes)
return conversion.convert_ipv6(self.addr.in6_u.u6_addr32)

def get_prefix_len(self):
"""Get the IPv6 address prefix len
Expand Down

0 comments on commit 6e0ffc9

Please sign in to comment.