diff --git a/LICENSE b/LICENSE index 8adf104..9cb3e8a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,20 @@ MIT License -Copyright (c) 2022 Sandfly Security +Copyright © 2018-2022 Sandfly Security +Agentless Security for Linux (www.sandflysecurity.com) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the “Software”), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c311d68 --- /dev/null +++ b/README.md @@ -0,0 +1,152 @@ +# Sandfly File Decloak - Decloak data hidden by a Linux stealth rootkit + +This utility helps investigate a host for signs of an active Linux stealth rootkit that may +be hiding data in critical files. It does this by reading in a file using standard file I/O +operations and then doing the same using memory mapped I/O to see if the number of bytes +read are identical. + +Any differences detected will generate an alert. Plus, you will see the hidden data +decloaked to instantly see if it is suspicious or not. This utility will work against many +common Loadable Kernel Module (LKM) stealth rootkits that use data hiding techniques. + +## Linux Loadable Kernel Module Stealth Rootkit File Hiding Tactics + +Loadable Kernel Module rootkits on Linux use a variety of tactics to hide. One method is to +hide processes which can be decloaked using our utility sandlfy-processdecloak +(). The other is to hide data +inside critical start-up scripts so it can maintain persistence between reboots but not be +seen by investigators when running. + +For instance the files below are commonly targeted to hide data as they are used to insert +kernel modules upon system boot. Or, these files can be used to insert malicious libraries +to intercept system calls in libc, etc. to alter data to callers: + +```text +/etc/modules +/etc/ld.so.conf +``` + +Also directories for module loading on boot such as: + +```text +/etc/modules-load.d +/etc/init.d +/etc/rc*.d +/etc/systemd +``` + +Many more files can also be used for this purpose and often are hidden under /etc as part +of system init scripts. + +## Stealth Rootkit Hiding Method + +Most LKM rootkits generally accomplish data hiding by hooking common system calls for file +read operations. They will use a special set of tags to mark data to hide. Between the tags +the malicious data will be inserted. When the rootkit sees the start tag it simply does not +show any data present until the end tag is read. By doing this the rootkit can effectively +hide from discovery using common command line tools and even editors on Linux. + +For instance, a modified file may have tags inserted like this: + +```bash +# malicious content below +# +malicious_module +# +``` + +Anything between the ** and ** will be masked (along with the tags +themselves) when you use tools like cat, echo, vi and so on. It simply won't be shown. + +## Detecting LKM rootkits + +It is one thing to convince the kernel to hide data, but something else entirely to get +the file system to agree with it. In fact we know the data is there on the file system +and we just need to bypass the hooked calls to see if we can get it to reveal itself. We'll +accomplish this using memory mapped (mmap) file I/O instead of standard file I/O. LKM +rootkits generally do not intercept mmap file I/O which is a significantly harder and +riskier thing to do. + +We will read the file using standard system calls, then we read the file using mmap system +calls in a simple Python script. We then compare the two results. If the results show the +same number of bytes the system is likely clean. However if the two results do not match +then data is being hidden and we will use the mmap I/O to show the data difference and +decloak the data it saw that was different. + +## Usage + +Simply execute the python script on the system in question and the answer will come forth. + +```bash +python3 ./sandfly-file-decloak.py -f +``` + +Below we find a system with cloaked data under /etc/modules. + +```text +root@sandflysecurity:/root # python3 ./sandfly-file-decloak.py -f /etc/modules + +Sandfly File Decloaking Utility - Version 1.0 +Copyright (c) 2018-2022 Sandfly Security +Agentless Security for Linux - https://www.sandflysecurity.com + + +************************************** +File contents with standard I/O +************************************** + + +# /etc/modules: kernel modules to load at boot time. +# +# This file contains the names of kernel modules that should be loaded +# at boot time, one per line. Lines beginning with "#" are ignored. + +# malicious content below + + + +************************************** +File contents with memory mapped I/O +************************************** + + +# /etc/modules: kernel modules to load at boot time. +# +# This file contains the names of kernel modules that should be loaded +# at boot time, one per line. Lines beginning with "#" are ignored. + +# malicious content below +# +malicious_module +# + + + +Standard IO file size bytes: 222 +MMAP IO file size bytes: 260 + +******************************************************************************************** +ALERT: File sizes do not match. File has cloaked data. Check contents above for hidden data. +******************************************************************************************** +``` + +## Automating with Agentless Linux Endpoint Detection and Response (EDR) + +This tool can be built into an automated script to check hosts for signs of compromise. +However, a much easier way is to use Sandfly to do it agentlessly for all your Linux systems +continuously. + +We have modules to detect and decloak stealth rootkit activity instantly across all your +Linux systems. Even better, we can do it without the risk of loading any agents on your +endpoints. + +You can find out more information and get a free license to use on your Linux systems below: + + + +## More Linux Forensics + +If you liked this utility, please check out our blog where we go into many other Linux +forensic techniques using command line tools and procedures at our website: + + diff --git a/sandfly-file-decloak.py b/sandfly-file-decloak.py new file mode 100644 index 0000000..de40235 --- /dev/null +++ b/sandfly-file-decloak.py @@ -0,0 +1,96 @@ +# Copyright © 2018-2022 Sandfly Security - Agentless Security for Linux (www.sandflysecurity.com) +# +# A utility to test if a file is hiding cloaked data due to a Loadable Kernel Module (LKM) or +# LD_PRELOAD stealth rootkit on Linux. Simply call the command as follows against a suspect file: +# +# python3 ./sandfly-file-decloak.py -f /etc/modules +# +# Any cloaked data will be reported and decloaked so you can see the contents and investigate if +# it is malicious. +# +# Sandfly produces an agentless Linux endpoint detection and incident response platform (EDR) that can detect +# thousands of compromise tactics against Linux, including stealth rootkits. Sandfly hunts for threats +# against your Linux systems without loading any agents on your endpoints and works against most distributions +# and architectures. +# +# Please see our website for more information or a free trial. +# +# MIT Licensed +# https://www.sandflysecurity.com +# @SandflySecurity +# +# Please see our blog for more detailed information on this tool and other Linux forensics articles and +# techniques. + +import getopt +import mmap +import sys +import binascii + +VERSION="1.0" + +def main(): + filename = None + + try: + opts, args = getopt.getopt(sys.argv[1:], "f:") + except getopt.GetoptError: + print("Illegal option. Valid option is -f") + sys.exit(-1) + + if len(opts) > 0: + for opt, arg in opts: + if opt == '-f': + filename = arg + + if not filename: + print("Need to supply filename with -f") + sys.exit(-1) + + + print("\nSandfly File Decloaking Utility - Version {0}".format(VERSION)) + print("Copyright (c) 2018-2022 Sandfly Security") + print("Agentless Security for Linux - https://www.sandflysecurity.com") + + print("\n\n**************************************") + print("File contents with standard I/O") + print("**************************************\n\n") + with open(filename, "r+b") as f: + file_size_standard_io = 0 + for line in f: + output = line + try: + print(output.decode('utf-8').rstrip()) + except UnicodeDecodeError: + print("hex: ", binascii.hexlify(output)) + file_size_standard_io += len(output) + + print("\n\n**************************************") + print("File contents with memory mapped I/O") + print("**************************************\n\n") + with open(filename, "r+b") as f: + map = mmap.mmap(f.fileno(), 0, access=mmap.PROT_READ) + file_size_mmap = map.size() + file_seek = 0 + while file_seek < file_size_mmap: + output = map.readline() + try: + print(output.decode('utf-8').rstrip()) + except UnicodeDecodeError: + print("hex: ", binascii.hexlify(output)) + file_seek += len(output) + + print("\n\n") + print("Standard IO file size bytes: ", file_size_standard_io) + print("MMAP IO file size bytes: ", file_size_mmap) + if file_size_standard_io != file_size_mmap: + print("\n********************************************************************************************") + print("ALERT: File sizes do not match. File has cloaked data. Check contents above for hidden data.") + print("********************************************************************************************\n\n") + else: + print("\nOK: File sizes are same so they are not cloaked.\n\n") + +if __name__ == '__main__': + main() + +