Files

280 lines
9.1 KiB
Python

#!/usr/bin/env python3
"""
macOS Process Injection / Tampering Module
This module demonstrates process injection and tampering techniques on macOS
for EDR telemetry testing. Due to macOS security restrictions, many traditional
injection techniques are limited or require special entitlements.
Techniques demonstrated:
1. DYLD_INSERT_LIBRARIES injection
2. task_for_pid based injection (requires entitlements)
3. Remote thread creation detection
4. Process memory manipulation attempts
Note: Most of these techniques require root privileges or special entitlements
to work on modern macOS versions with SIP enabled.
"""
import os
import sys
import ctypes
import ctypes.util
import signal
import time
import tempfile
# Load libraries
libc = ctypes.CDLL(ctypes.util.find_library('c'))
def dyld_insert_libraries_injection():
"""
Demonstrates DYLD_INSERT_LIBRARIES injection technique.
This is a common macOS code injection method that loads a dynamic library
into a process at launch time.
Note: This technique is blocked for protected binaries and when SIP is enabled.
"""
print(" [*] Testing DYLD_INSERT_LIBRARIES injection...")
try:
# Syscall-only mode: avoid invoking toolchains (clang) or utilities.
# We set DYLD_INSERT_LIBRARIES and exec a child Python process to
# generate process/env telemetry.
env = os.environ.copy()
env['DYLD_INSERT_LIBRARIES'] = '/usr/lib/libSystem.B.dylib'
pid = os.fork()
if pid == 0:
os.execve(
sys.executable,
[sys.executable, '-c', 'import os; print("DYLD test")'],
env,
)
os.waitpid(pid, 0)
print(" [+] DYLD_INSERT_LIBRARIES set and child process executed")
return True
except Exception as e:
print(f" [-] DYLD injection test failed: {e}")
return False
def task_for_pid_injection():
"""
Demonstrates task_for_pid based injection attempt.
This technique requires the com.apple.security.cs.debugger entitlement
or root privileges, and is blocked by SIP for protected processes.
"""
print(" [*] Testing task_for_pid injection technique...")
try:
# Load the Security framework for task_for_pid
security = ctypes.CDLL('/System/Library/Frameworks/Security.framework/Security')
# Define task_for_pid
# kern_return_t task_for_pid(mach_port_t target_tport, int pid, mach_port_t *t);
libc.task_for_pid.argtypes = [ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_uint)]
libc.task_for_pid.restype = ctypes.c_int
# Get mach_task_self
libc.mach_task_self.restype = ctypes.c_uint
self_task = libc.mach_task_self()
# Fork a child process to test against
pid = os.fork()
if pid == 0:
# Child process
time.sleep(10)
os._exit(0)
else:
# Parent process
time.sleep(1) # Let child start
target_task = ctypes.c_uint()
print(f" [*] Attempting task_for_pid on process {pid}...")
result = libc.task_for_pid(self_task, pid, ctypes.byref(target_task))
if result == 0:
print(f" [+] task_for_pid succeeded! Task port: {target_task.value}")
print(" [+] This indicates potential for process injection")
else:
print(f" [!] task_for_pid failed with error: {result}")
print(" [!] This is expected without proper entitlements")
# Cleanup
os.kill(pid, signal.SIGTERM)
os.waitpid(pid, 0)
return True
except Exception as e:
print(f" [-] task_for_pid test failed: {e}")
return False
def mach_inject_attempt():
"""
Demonstrates Mach-based injection attempt.
This uses Mach APIs to attempt thread creation in another process.
"""
print(" [*] Testing Mach-based injection technique...")
try:
# This would require:
# 1. task_for_pid to get target task port
# 2. mach_vm_allocate to allocate memory in target
# 3. mach_vm_write to write shellcode
# 4. thread_create_running to create a thread
# Since we can't actually inject without entitlements,
# we'll demonstrate the API calls that would be used
print(" [*] Mach injection requires:")
print(" - task_for_pid (requires entitlements)")
print(" - mach_vm_allocate (allocate in target)")
print(" - mach_vm_write (write shellcode)")
print(" - thread_create_running (create thread)")
print(" [!] These operations are blocked by SIP and require entitlements")
# Demonstrate mach_task_self which is always available
libc.mach_task_self.restype = ctypes.c_uint
self_task = libc.mach_task_self()
print(f" [+] Current task port: {self_task}")
return True
except Exception as e:
print(f" [-] Mach injection test failed: {e}")
return False
def process_hollowing_attempt():
"""
Demonstrates process hollowing detection.
Process hollowing involves creating a suspended process, unmapping its
memory, and replacing it with malicious code.
On macOS, this is significantly more difficult due to code signing
and SIP protections.
"""
print(" [*] Testing process hollowing technique...")
try:
# On macOS, we can demonstrate the concept by:
# 1. Creating a suspended process
# 2. Attempting to modify its memory
# Create a process in suspended state using posix_spawn
print(" [*] Creating suspended process...")
# Fork and exec with SIGSTOP
pid = os.fork()
if pid == 0:
# Child - stop immediately
os.kill(os.getpid(), signal.SIGSTOP)
# If we get here, we were continued
time.sleep(5)
os._exit(0)
else:
# Parent
time.sleep(1) # Let child stop
print(f" [+] Created suspended process: {pid}")
# On macOS, we cannot easily modify another process's memory
# without proper entitlements
print(" [*] Attempting to access process memory...")
print(" [!] Memory modification blocked without entitlements")
# Resume and cleanup
os.kill(pid, signal.SIGCONT)
time.sleep(0.5)
os.kill(pid, signal.SIGTERM)
os.waitpid(pid, 0)
print(" [+] Suspended process cleaned up")
return True
except Exception as e:
print(f" [-] Process hollowing test failed: {e}")
return False
def thread_injection_attempt():
"""
Demonstrates remote thread creation attempt.
This technique creates a thread in another process to execute code.
"""
print(" [*] Testing remote thread creation...")
try:
# On macOS, thread_create_running requires:
# 1. A valid task port (from task_for_pid)
# 2. Proper entitlements
# We'll demonstrate by creating a thread in our own process
import threading
def injected_thread():
print(" [+] Thread executed (simulating injection)")
time.sleep(1)
print(" [*] Creating thread in current process (simulation)...")
t = threading.Thread(target=injected_thread)
t.start()
t.join()
print(" [!] Remote thread creation in other processes requires entitlements")
return True
except Exception as e:
print(f" [-] Thread injection test failed: {e}")
return False
def process_injection_demo():
"""
Main function to run all process injection/tampering demonstrations.
"""
print("[*] Running Process Injection/Tampering demonstrations...")
print(" Note: Most techniques are blocked by SIP on modern macOS")
print()
results = []
# Run each technique
techniques = [
("DYLD_INSERT_LIBRARIES", dyld_insert_libraries_injection),
("task_for_pid", task_for_pid_injection),
("Mach Injection", mach_inject_attempt),
("Process Hollowing", process_hollowing_attempt),
("Remote Thread Creation", thread_injection_attempt),
]
for name, func in techniques:
print(f"\n === {name} ===")
try:
result = func()
results.append((name, result))
except Exception as e:
print(f" [-] {name} failed: {e}")
results.append((name, False))
# Summary
print("\n === Summary ===")
for name, result in results:
status = "" if result else ""
print(f" [{status}] {name}")
return all(r[1] for r in results)
if __name__ == "__main__":
process_injection_demo()