Skip to content

Commit

Permalink
Initial 1.0 Commit
Browse files Browse the repository at this point in the history
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
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 16 deletions.
31 changes: 15 additions & 16 deletions LICENSE
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.
152 changes: 152 additions & 0 deletions README.md
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>
96 changes: 96 additions & 0 deletions sandfly-file-decloak.py
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()


0 comments on commit 1672e18

Please sign in to comment.