
Código fonte e arquivos de configuração relacionados ao nosso artigo na MISC96
Este repositório é um complemento ao artigo publicado na MISC Magazine #96.
Conseguimos elevar nossos privilégios de forma confiável, em nossa máquina virtual com SMEP / SMAP e KASLR ativados. Deve-se notar, no entanto, que o sistema fica em um estado instável e que um oops é muito provável de ocorrer.
Na pasta configs/, você encontrará arquivos de configuração para o kernel Linux
e Busybox, cada um sendo ligeiramente diferente dos padrões.
A pasta binaries/ contém todos os binários pré-construídos que você precisará
para reproduzir nosso ambiente de teste, como um rootfs completo pronto para ser usado com
QEMU.
linux-stable é um submódulo git apontando para uma revisão vulnerável do kernel
Linux. Use git submodule update --recursive para obtê-lo (se você tiver pelo menos
1 GB livre no seu sistema...). O mesmo vale para busybox.
Nosso ambiente é baseado em uma máquina virtual QEMU x86 com uma pasta compartilhada
com o host via 9P. Uma versão vulnerável do kernel (ff33952e4d23) é
compilada junto com uma compilação estaticamente ligada do Busybox.
Nossa configuração é bem simples, já que não precisamos suportar nenhum tipo
de arquitetura ou hardware exótico. Foi gerada executando make defconfig e alguns recursos foram habilitados para suportar rede e compartilhamento de pastas do QEMU:
CONFIG_BLK_MQ_VIRTIO=y
CONFIG_MEMORY_BALLOON=y
CONFIG_BALLOON_COMPACTION=y
CONFIG_NET_9P=y
CONFIG_NET_9P_VIRTIO=y
CONFIG_NET_9P_DEBUG=y
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_BLK_SCSI=y
CONFIG_VIRTIO_NET=y
CONFIG_HVC_DRIVER=y
CONFIG_VIRTIO_CONSOLE=y
CONFIG_HW_RANDOM_VIRTIO=y
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_INPUT=y
CONFIG_VIRTIO_MMIO=y
CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
CONFIG_9P_FS=y
CONFIG_9P_FS_POSIX_ACL=y
CONFIG_9P_FS_SECURITY=y
Scripts GDB e símbolos de depuração também foram adicionados para facilitar o desenvolvimento dos primeiros PoCs:
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF4=y
CONFIG_GDB_SCRIPTS=y
Observe que nesta versão, o KASLR está habilitado por padrão no x86. Como o
kernel é mapeado em endereços diferentes após cada reinicialização, o GDB não conseguirá
corresponder o arquivo de símbolos com ele. Você tem então duas opções: realizar testes
sem KASLR ou passar o endereço base do kernel para symbol-file ao
carregar os símbolos no GDB.
Após compilá-lo, você encontrará o bzImage em linux-stable/arch/x86/boot/bzImage.
Este arquivo está presente em binaries/bzImage e nosso arquivo de configuração em
confifs/kernel.config. Para usá-lo, basta copiá-lo como .config
em linux-stable.
Usamos a última versão estável do Busybox, 1.28. A única configuração a alterar é
CONFIG_STATIC, defina-a como y. A compilação não deve causar problemas.
Nosso arquivo de configuração está disponível em configs/busybox.config e um
binário compilado estaticamente em binaries/busybox. Assim como no Linux, basta copiá-lo
como .config na pasta de fontes do Busybox.
O Busybox já implementa um processo init que tentará executar /etc/init.d/rcS.
Normalmente, ele é fornecido pela sua distribuição (potencialmente com um nome diferente)
mas teremos que fazer isso por nós mesmos aqui! Este script criará e montará
várias pastas obrigatórias do sistema (proc, sys, dev), nossa pasta compartilhada com
o host e nos colocará em um shell sem privilégios:
for i in $(seq 1 9); do mknod /dev/tty$i c 4 1; done
mknod -m 0666 /dev/null c 1 3
mknod -m 0660 /dev/ttyS0 c 4 64
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
mkdir -p /mnt/share
mount -t 9p -o trans=virtio share /mnt/share/ -oversion=9p2000.L,posixacl,sync
chmod 777 /mnt/share/
export ENV=/etc/profile
setsid cttyhack setuidgid 1000 sh
umount /proc
umount /sys
umount /dev
poweroff -f
O arquivo /etc/profile não é obrigatório, mas é útil durante nossos testes,
especialmente quando o exploit não era confiável e levou várias tentativas para
obter privilégios de root.
O processo por trás da escrita do exploit é razoavelmente documentado na MISC 96: usamos
unsafe_put_user para sondar a memória até encontrarmos o endereço base do heap.
Então, milhares de chamadas a clone nos permitem espalhar inúmeras estruturas cred
na memória. Durante nossos testes, a posição delas na memória era muito mais "constante"
do que ao usar fork, pois aloca menos estruturas para a nova tarefa.
A saída do processo filho causará um oops no kernel, devido a uma solicitação de paginação defeituosa, isso precisa ser tratado corretamente.
Se você quiser melhorar a confiabilidade deste exploit ou adicionar documentação, contribuições são bem-vindas! Também podemos ter cometido erros ou imprecisões em alguns conceitos, não hesite em abrir uma issue se achar que algo está errado.
Kernel Linux
Exploração