-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Sandfly's Linux stealth rootkit decloaking utility version 1.0 released.
- Loading branch information
Craig H. Rowland
committed
Nov 15, 2022
1 parent
feb583d
commit 1672e18
Showing
3 changed files
with
263 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
(<https://github.com/sandflysecurity/sandfly-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 | ||
#<lkm_tag> | ||
malicious_module | ||
#</lkm_tag> | ||
``` | ||
|
||
Anything between the *<lkm_tag>* and *</lkm_tag>* 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 <file_to_investigate> | ||
``` | ||
|
||
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 | ||
#<reptile> | ||
malicious_module | ||
#</reptile> | ||
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: | ||
|
||
<https://www.sandflysecurity.com> | ||
|
||
## 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: | ||
|
||
<https://www.sandflysecurity.com> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
|
||
|