From c86d973329e1795ceb0be15e70d9411a38b0f60e Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Wed, 11 Sep 2024 15:54:17 +1000 Subject: [PATCH 1/4] Linux page cache plugins pointer verification improvements --- .../framework/plugins/linux/mountinfo.py | 11 +++++--- .../framework/plugins/linux/pagecache.py | 27 ++++++++++++------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/volatility3/framework/plugins/linux/mountinfo.py b/volatility3/framework/plugins/linux/mountinfo.py index 1eaec77bf..2499f009e 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -37,7 +37,7 @@ class MountInfo(plugins.PluginInterface): _required_framework_version = (2, 2, 0) - _version = (1, 2, 0) + _version = (1, 2, 1) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -272,11 +272,16 @@ class MountInfo(plugins.PluginInterface): continue sb_ptr = mnt.get_mnt_sb() - if not sb_ptr or sb_ptr in seen_sb_ptr: + if not (sb_ptr and sb_ptr.is_readable()): + continue + + if sb_ptr in seen_sb_ptr: continue seen_sb_ptr.add(sb_ptr) - yield sb_ptr.dereference(), path_root + superblock = sb_ptr.dereference() + + yield superblock, path_root def run(self): pids = self.config.get("pids") diff --git a/volatility3/framework/plugins/linux/pagecache.py b/volatility3/framework/plugins/linux/pagecache.py index b6c5f7cc0..9b6f05c4d 100644 --- a/volatility3/framework/plugins/linux/pagecache.py +++ b/volatility3/framework/plugins/linux/pagecache.py @@ -104,7 +104,7 @@ class Files(plugins.PluginInterface, timeliner.TimeLinerInterface): _required_framework_version = (2, 0, 0) - _version = (1, 0, 0) + _version = (1, 0, 1) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -186,8 +186,12 @@ class Files(plugins.PluginInterface, timeliner.TimeLinerInterface): seen_dentries.add(dentry_addr) - inode = dentry.d_inode - if not (inode and inode.is_valid()): + inode_ptr = dentry.d_inode + if not (inode_ptr and inode_ptr.is_readable()): + continue + + inode = inode_ptr.dereference() + if not inode.is_valid(): continue # This allows us to have consistent paths @@ -242,8 +246,9 @@ class Files(plugins.PluginInterface, timeliner.TimeLinerInterface): # More dentry/inode sanity checks root_inode_ptr = root_dentry.d_inode - if not root_inode_ptr: + if not (root_inode_ptr and root_inode_ptr.is_readable()): continue + root_inode = root_inode_ptr.dereference() if not root_inode.is_valid(): continue @@ -269,10 +274,12 @@ class Files(plugins.PluginInterface, timeliner.TimeLinerInterface): ): if not file_dentry: continue + # Dentry/inode sanity checks file_inode_ptr = file_dentry.d_inode - if not file_inode_ptr: + if not (file_inode_ptr and file_inode_ptr.is_readable()): continue + file_inode = file_inode_ptr.dereference() if not file_inode.is_valid(): continue @@ -382,7 +389,7 @@ class InodePages(plugins.PluginInterface): _required_framework_version = (2, 0, 0) - _version = (1, 0, 0) + _version = (1, 0, 1) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -482,15 +489,15 @@ class InodePages(plugins.PluginInterface): vollog.error("You must use either --inode or --find") return + if not inode.is_valid(): + vollog.error("Invalid inode at 0x%x", inode.vol.offset) + return + if not inode.is_reg: vollog.error("The inode is not a regular file") return inode_size = inode.i_size - if not inode.is_valid(): - vollog.error("Invalid inode at 0x%x", self.config["inode"]) - return - for page_obj in inode.get_pages(): page_vaddr = page_obj.vol.offset page_paddr = page_obj.to_paddr() From ebb9f0c20163fe215c72d510e9188dbf1d3c318a Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Thu, 12 Sep 2024 17:05:45 +1000 Subject: [PATCH 2/4] Fix typo in timeliner function --- volatility3/framework/plugins/linux/pagecache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/pagecache.py b/volatility3/framework/plugins/linux/pagecache.py index 9b6f05c4d..ad2c3b1be 100644 --- a/volatility3/framework/plugins/linux/pagecache.py +++ b/volatility3/framework/plugins/linux/pagecache.py @@ -342,7 +342,7 @@ class Files(plugins.PluginInterface, timeliner.TimeLinerInterface): description = f"Cached Inode for {inode_out.path}" yield description, timeliner.TimeLinerType.ACCESSED, inode_out.access_time yield description, timeliner.TimeLinerType.MODIFIED, inode_out.modification_time - yield description, timeliner.TimeLinerType.CHANGE, inode_out.change_time + yield description, timeliner.TimeLinerType.CHANGED, inode_out.change_time @staticmethod def format_fields_with_headers(headers, generator): From 7b2e259c6dca79b1d36acdc82bc8d7b76f7aa00b Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Thu, 12 Sep 2024 17:06:29 +1000 Subject: [PATCH 3/4] fix wrong variable names --- volatility3/framework/plugins/linux/pagecache.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/plugins/linux/pagecache.py b/volatility3/framework/plugins/linux/pagecache.py index ad2c3b1be..81e1f3601 100644 --- a/volatility3/framework/plugins/linux/pagecache.py +++ b/volatility3/framework/plugins/linux/pagecache.py @@ -78,8 +78,8 @@ class InodeInternal: cached_pages = int(self.inode.i_mapping.nrpages) file_mode = self.inode.get_file_mode() access_time_dt = self.inode.get_access_time() - modification_time_str = self.inode.get_modification_time() - change_time_str = self.inode.get_change_time() + modification_time_dt = self.inode.get_modification_time() + change_time_dt = self.inode.get_change_time() inode_user = InodeUser( superblock_addr=superblock_addr, @@ -92,8 +92,8 @@ class InodeInternal: cached_pages=cached_pages, file_mode=file_mode, access_time=access_time_dt, - modification_time=modification_time_str, - change_time=change_time_str, + modification_time=modification_time_dt, + change_time=change_time_dt, path=self.path, ) return inode_user From e084086ee5f2d9dad29182bc4616912cbfe53f1e Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 13 Sep 2024 16:18:26 +1000 Subject: [PATCH 4/4] Improve pointer verification in super_block->get_type() --- .../symbols/linux/extensions/__init__.py | 62 ++++++++++++++++--- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index fdd34403a..aa18b8262 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -638,11 +638,23 @@ class super_block(objects.StructType): ] return sb_opts - def get_type(self): - mnt_sb_type = utility.pointer_to_string(self.s_type.name, count=255) - if self.s_subtype: - mnt_sb_subtype = utility.pointer_to_string(self.s_subtype, count=255) + def get_type(self) -> Optional[str]: + """Gets the superblock filesystem type string""" + + s_type_ptr = self.s_type + if not (s_type_ptr and s_type_ptr.is_readable()): + return None + + s_type_name_ptr = s_type_ptr.name + if not (s_type_name_ptr and s_type_name_ptr.is_readable()): + return None + + mnt_sb_type = utility.pointer_to_string(s_type_name_ptr, count=255) + s_subtype_ptr = self.s_subtype + if s_subtype_ptr and s_subtype_ptr.is_readable(): + mnt_sb_subtype = utility.pointer_to_string(s_subtype_ptr, count=255) mnt_sb_type += "." + mnt_sb_subtype + return mnt_sb_type @@ -843,25 +855,55 @@ class dentry(objects.StructType): dentry_type_name = self.get_symbol_table_name() + constants.BANG + "dentry" yield from list_head_member.to_list(dentry_type_name, walk_member) + def get_inode(self) -> interfaces.objects.ObjectInterface: + """Returns the inode associated with this dentry""" + + inode_ptr = self.d_inode + if not (inode_ptr and inode_ptr.is_readable() and inode_ptr.is_valid()): + return None + + return inode_ptr.dereference() + class struct_file(objects.StructType): def get_dentry(self) -> interfaces.objects.ObjectInterface: - if self.has_member("f_dentry"): - return self.f_dentry - elif self.has_member("f_path"): + """Returns a pointer to the dentry associated with this file""" + if self.has_member("f_path"): return self.f_path.dentry + elif self.has_member("f_dentry"): + return self.f_dentry else: raise AttributeError("Unable to find file -> dentry") def get_vfsmnt(self) -> interfaces.objects.ObjectInterface: """Returns the fs (vfsmount) where this file is mounted""" - if self.has_member("f_vfsmnt"): - return self.f_vfsmnt - elif self.has_member("f_path"): + if self.has_member("f_path"): return self.f_path.mnt + elif self.has_member("f_vfsmnt"): + return self.f_vfsmnt else: raise AttributeError("Unable to find file -> vfs mount") + def get_inode(self) -> interfaces.objects.ObjectInterface: + """Returns an inode associated with this file""" + + inode_ptr = None + if self.has_member("f_inode") and self.f_inode and self.f_inode.is_readable(): + # Try first the cached value, kernels +3.9 + inode_ptr = self.f_inode + + if not (inode_ptr and inode_ptr.is_readable() and inode_ptr.is_valid()): + dentry_ptr = self.get_dentry() + if not (dentry_ptr and dentry_ptr.is_readable()): + return None + + inode_ptr = dentry_ptr.d_inode + + if not (inode_ptr and inode_ptr.is_readable() and inode_ptr.is_valid()): + return None + + return inode_ptr.dereference() + class list_head(objects.StructType, collections.abc.Iterable): def to_list(