Skip to content

Check IoC of dirty bit in PTEs from executable VMAs.#995

Merged
ikelos merged 2 commits into
volatilityfoundation:developfrom
xabrouck:dirty
Sep 3, 2023
Merged

Check IoC of dirty bit in PTEs from executable VMAs.#995
ikelos merged 2 commits into
volatilityfoundation:developfrom
xabrouck:dirty

Conversation

@xabrouck
Copy link
Copy Markdown
Contributor

@xabrouck xabrouck commented Aug 14, 2023

Currently, the Malfind plugin in volatility3 does not check the dirty bit in PTEs associated with executable VMAs. When someone modifies running code for example using ptrace (e.g. by writing to /proc/pid/mem), the permissions of the VMA remain r-x (ptrace does not require rwx) and the targeted pages become dirty. This is visible in /proc/pid/smaps where the Private_Dirty of executable VMAs become non zero.

The goal of this patch is to detect such dirty executable pages as it likely means that the target process was debugged at some point or that malicious code was injected.

It could happen that no modification is found when comparing the memory dump of the process with the original executable/libraries. In that case, it can mean that the malware got uninstalled, i.e. the original code was rewritten on top of the malicious code before the memory dump took place. Nevertheless, it is still interesting to know that some code got injected at some point in the past.

The downside of this patch is that it makes Malfind slower.

this can for example detect code injected using ptrace().
this can also detect injected code that was reset to the original code (malware uninstalled before memory dump happened).
Comment thread volatility3/framework/symbols/linux/extensions/__init__.py Fixed
Copy link
Copy Markdown
Member

@ikelos ikelos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a few gotchas, and please apply black to your submission, to make sure the code is all formatted uniformly. You can see on the build list why black believes it's failing if you just want to manually fix it up...

Comment thread volatility3/framework/symbols/linux/extensions/__init__.py Outdated
Comment thread volatility3/framework/symbols/linux/extensions/__init__.py Fixed
Comment thread volatility3/framework/symbols/linux/extensions/__init__.py
@xabrouck
Copy link
Copy Markdown
Contributor Author

@ikelos Any remaining concern?

@xabrouck
Copy link
Copy Markdown
Contributor Author

There's a corner case with some old binaries/libraries that trigger false positives. It's explained in
https://stackoverflow.com/questions/76651605/understanding-a-dt-textrel-warning
If there are absolute addresses in a PIE (unusual), it will have to do runtime fixups, which require writing to an executable region.

Here's a small example to reproduce the corner case:

$ cat dirtyexec.c
int x=0; int main() { scanf("%d",&x); }

$ gcc -w -mcmodel=large -fno-pie dirtyexec.c -o dirtyexec
/usr/bin/ld: /tmp/ccuBN6g5.o: warning: relocation in read-only section `.text'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE

$ file dirtyexec
dirtyexec: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f50788c7e03024bd325756f43eb153d0fb265491, for GNU/Linux 3.2.0, not stripped

$ readelf -r dirtyexec|grep R_X86_64_64
000000001172  000500000001 R_X86_64_64       0000000000001050 scanf@GLIBC_2.2.5 + 0

$ ./dirtyexec
^Z
[1]+  Stopped                 ./dirtyexec

$ egrep "\-|Private_Dirty" /proc/$(pidof dirtyexec)/smaps|head -4|tail -2
55c7a3619000-55c7a361a000 r-xp 00001000 fd:00 6426135                    /home/xabrouck/reloc-test/2/dirtyexec
Private_Dirty:         4 kB 

Copy link
Copy Markdown
Member

@ikelos ikelos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay in reviewing this. I'm quite nervous about adding things into the Intel space, but I believe this is ok so I'm going to merge it in.

except exceptions.InvalidAddressException:
return False

def is_dirty(self, offset: int) -> bool:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My slight concern is that we only add this to the intel layer, and not the underlying TranslationLayerInterface, but I think we've done that in other places. I guess it just requires that any time we use it we ensure we know we're working with an intel layer (at the moment, that's likely to be all we're working with, but in the future it could be an arm layer or something else)...

@ikelos ikelos merged commit c8a85e1 into volatilityfoundation:develop Sep 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants