mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-17 20:35:40 +02:00
215 lines
7.0 KiB
Python
215 lines
7.0 KiB
Python
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
|
|
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
|
#
|
|
"""A module containing a plugin that verifies the operation function
|
|
pointers of network protocols."""
|
|
import logging
|
|
from typing import List, Tuple, Generator
|
|
|
|
from volatility3.framework import exceptions, interfaces
|
|
from volatility3.framework import renderers
|
|
from volatility3.framework.configuration import requirements
|
|
from volatility3.framework.interfaces import plugins
|
|
from volatility3.framework.renderers import format_hints
|
|
|
|
vollog = logging.getLogger(__name__)
|
|
|
|
|
|
class Check_afinfo(plugins.PluginInterface):
|
|
"""Verifies the operation function pointers of network protocols."""
|
|
|
|
_version = (1, 0, 0)
|
|
_required_framework_version = (2, 0, 0)
|
|
|
|
@classmethod
|
|
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
|
return [
|
|
requirements.ModuleRequirement(
|
|
name="kernel",
|
|
description="Linux kernel",
|
|
architectures=["Intel32", "Intel64"],
|
|
),
|
|
]
|
|
|
|
@classmethod
|
|
def _check_members(
|
|
cls,
|
|
context: interfaces.context.ContextInterface,
|
|
vmlinux_name: str,
|
|
var_ops: interfaces.objects.ObjectInterface,
|
|
var_name: str,
|
|
members: List[str],
|
|
) -> Generator[Tuple[str, str, int], None, None]:
|
|
"""
|
|
Yields any members that are not pointing inside the kernel
|
|
"""
|
|
|
|
vmlinux = context.modules[vmlinux_name]
|
|
|
|
for check in members:
|
|
# redhat-specific garbage
|
|
if check.startswith("__UNIQUE_ID_rh_kabi_hide"):
|
|
continue
|
|
|
|
# These structures have members like `write` and `next`, which are built in Python functions
|
|
addr = var_ops.member(attr=check)
|
|
|
|
# Unimplemented handlers are set to 0
|
|
if not addr:
|
|
continue
|
|
|
|
if len(vmlinux.get_symbols_by_absolute_location(addr)) == 0:
|
|
yield var_name, check, addr
|
|
|
|
@classmethod
|
|
def _check_pre_4_18_ops(
|
|
cls,
|
|
context: interfaces.context.ContextInterface,
|
|
vmlinux_name: str,
|
|
var_name: str,
|
|
var: interfaces.objects.ObjectInterface,
|
|
op_members: List[str],
|
|
seq_members: List[str],
|
|
):
|
|
"""
|
|
Finds the correct way to reference `op_members`
|
|
"""
|
|
vmlinux = context.modules[vmlinux_name]
|
|
|
|
if var.has_member("seq_fops"):
|
|
yield from cls._check_members(
|
|
context, vmlinux_name, var.seq_fops, var_name, op_members
|
|
)
|
|
# newer kernels
|
|
if var.has_member("seq_ops"):
|
|
yield from cls._check_members(
|
|
context, vmlinux_name, var.seq_ops, var_name, seq_members
|
|
)
|
|
|
|
# this is the most commonly hooked member by rootkits, so a force a check on it
|
|
elif var.has_member("seq_show"):
|
|
if len(vmlinux.get_symbols_by_location(var.seq_show)) == 0:
|
|
yield var_name, "show", var.seq_show
|
|
else:
|
|
raise exceptions.VolatilityException(
|
|
"_check_afinfo_pre_4_18: Unable to find sequence operations members for checking."
|
|
)
|
|
|
|
@classmethod
|
|
def _check_afinfo_pre_4_18(
|
|
cls,
|
|
context: interfaces.context.ContextInterface,
|
|
vmlinux_name: str,
|
|
seq_members: str,
|
|
) -> Generator[Tuple[str, str, int], None, None]:
|
|
"""
|
|
Checks the operations structures for network protocols of < 4.18 systems
|
|
"""
|
|
tcp = ("tcp_seq_afinfo", ["tcp6_seq_afinfo", "tcp4_seq_afinfo"])
|
|
udp = (
|
|
"udp_seq_afinfo",
|
|
[
|
|
"udplite6_seq_afinfo",
|
|
"udp6_seq_afinfo",
|
|
"udplite4_seq_afinfo",
|
|
"udp4_seq_afinfo",
|
|
],
|
|
)
|
|
protocols = [tcp, udp]
|
|
|
|
vmlinux = context.modules[vmlinux_name]
|
|
|
|
op_members = vmlinux.get_type("file_operations").members
|
|
|
|
# loop through all symbols
|
|
for struct_type, global_vars in protocols:
|
|
for global_var_name in global_vars:
|
|
# this will lookup fail for the IPv6 protocols on kernels without IPv6 support
|
|
try:
|
|
global_var = vmlinux.object_from_symbol(global_var_name)
|
|
except exceptions.SymbolError:
|
|
continue
|
|
|
|
yield from cls._check_pre_4_18_ops(
|
|
context,
|
|
vmlinux_name,
|
|
global_var_name,
|
|
global_var,
|
|
op_members,
|
|
seq_members,
|
|
)
|
|
|
|
@classmethod
|
|
def _check_afinfo_post_4_18(
|
|
cls,
|
|
context: interfaces.context.ContextInterface,
|
|
vmlinux_name: str,
|
|
seq_members: str,
|
|
) -> Generator[Tuple[str, str, int], None, None]:
|
|
"""
|
|
Checks the operations structures for network protocols of >= 4.18 systems
|
|
"""
|
|
vmlinux = context.modules[vmlinux_name]
|
|
|
|
ops_structs = [
|
|
"raw_seq_ops",
|
|
"udp_seq_ops",
|
|
"arp_seq_ops",
|
|
"unix_seq_ops",
|
|
"udp6_seq_ops" "raw6_seq_ops",
|
|
"tcp_seq_ops",
|
|
"tcp4_seq_ops",
|
|
"tcp6_seq_ops",
|
|
"packet_seq_ops",
|
|
]
|
|
|
|
for protocol_ops_var in ops_structs:
|
|
# These will fail if the particular kernel doesn't have support for a protocol like IPv6
|
|
try:
|
|
protocol_ops = vmlinux.object_from_symbol(protocol_ops_var)
|
|
except exceptions.SymbolError:
|
|
continue
|
|
|
|
yield from cls._check_members(
|
|
context, vmlinux_name, protocol_ops, protocol_ops_var, seq_members
|
|
)
|
|
|
|
@classmethod
|
|
def check_afinfo(
|
|
cls, context: interfaces.context.ContextInterface, vmlinux_name
|
|
) -> Generator[Tuple[str, str, int], None, None]:
|
|
"""
|
|
Walks the network protocol operations structures for common network protocols.
|
|
Reports any initialized operations members that do not point inside the kernel.
|
|
"""
|
|
vmlinux = context.modules[vmlinux_name]
|
|
|
|
type_check = vmlinux.get_type("tcp_seq_afinfo")
|
|
if type_check.has_member("seq_fops"):
|
|
checker = cls._check_afinfo_pre_4_18
|
|
else:
|
|
checker = cls._check_afinfo_post_4_18
|
|
|
|
seq_members = vmlinux.get_type("seq_operations").members
|
|
|
|
yield from checker(context, vmlinux_name, seq_members)
|
|
|
|
def _generator(self):
|
|
"""
|
|
A simple wrapper around `check_afino`
|
|
"""
|
|
for name, member, address in self.check_afinfo(
|
|
self.context, self.config["kernel"]
|
|
):
|
|
yield 0, (name, member, format_hints.Hex(address))
|
|
|
|
def run(self):
|
|
return renderers.TreeGrid(
|
|
[
|
|
("Symbol Name", str),
|
|
("Member", str),
|
|
("Handler Address", format_hints.Hex),
|
|
],
|
|
self._generator(),
|
|
)
|