Introdução
Quem já precisou transformar a saída do objdump em uma shellcode, sabe do trabalho que dá. É muito chato pegar byte por byte para montar a shellcode, e em alguns casos, acontece de esquecermos alguns bytes, fazendo a shellcode não funcionar.
Hoje irei apresentar um método usando somente comandos padrões do linux para transformar a saída do objdump em uma shellcode.
Shellcode?
Shellcode é um pequeno pedaço de código usado para explorar uma aplicação vulnerável e na maioria das vezes seu objetivo é rodar uma shell (daí o nome shellcode). Como as shellcodes realizam funções específicas, é muito fácil encontrá-las prontas na Internet. Sites como [2], mantém muitas shellcodes em seus bancos de dados.
Em geral, um programa em C ou em assembly é escrito e em seguida compilado, e à partir dos códigos de máquina desse programa, a shellcode é gerada. Esse processo, feito de forma manual é muito cansativo, principalmente quando a shellcode é grande, e também muito passível de erros.
objdump
objdump, como o nome já sugere, mostra (“dump“) informações de arquivos binários. Essas informações são as mais diversas possíveis, e para esse post a mais importante é a capacidade de mostrar o código de máquina do binário assim como as instruções em assembly correspondentes.
Obtendo a shellcode através do objdump
Esse post é baseado totalmente na ideia apresenta por gunslinger_ em [1]. O que irei fazer aqui é um passo a passo de como a sequência de comandos funciona.
Bem, como exemplo irei usar o seguinte programa em assembly para obter uma shellcode:
.text .globl _start _start: push $0xb pop %eax cltd push %edx push $0x68732f2f push $0x6e69622f movl %esp, %ebx xorl %ecx, %ecx int $0x80
O que esse programa faz é executar a syscall execve para executar uma shell (/bin/sh). Vamos compilá-lo:
$ as -gstabs -o prog.o prog.s $ ld -o prog prog.o
O executável prog foi criado. Agora vamos ver a saída do objdump em cima desse arquivo:
$ objdump -d prog prog: file format elf32-i386 Disassembly of section .text: 08048054 <_start>: 8048054: 6a 0b push $0xb 8048056: 58 pop %eax 8048057: 99 cltd 8048058: 52 push %edx 8048059: 68 2f 2f 73 68 push $0x68732f2f 804805e: 68 2f 62 69 6e push $0x6e69622f 8048063: 89 e3 mov %esp,%ebx 8048065: 31 c9 xor %ecx,%ecx 8048067: cd 80 int $0x80
A sequência de bytes “6a 0b 58 … cd 80” é a nossa shellcode. Vamos usar o grep para selecionar somente as linhas da shellcode:
$ objdump -d prog | grep '[0-9a-f]:' | grep -v file 8048054: 6a 0b push $0xb 8048056: 58 pop %eax 8048057: 99 cltd 8048058: 52 push %edx 8048059: 68 2f 2f 73 68 push $0x68732f2f 804805e: 68 2f 62 69 6e push $0x6e69622f 8048063: 89 e3 mov %esp,%ebx 8048065: 31 c9 xor %ecx,%ecx 8048067: cd 80 int $0x80
Agora vamos deletar os endereços que estão antes do ‘:’:
$ objdump -d prog | grep '[0-9a-f]:' | grep -v file | cut -f2 -d ':' 6a 0b push $0xb 58 pop %eax 99 cltd 52 push %edx 68 2f 2f 73 68 push $0x68732f2f 68 2f 62 69 6e push $0x6e69622f 89 e3 mov %esp,%ebx 31 c9 xor %ecx,%ecx cd 80 int $0x80
O que sobrou foram os códigos de máquina e as instruções em assembly correspondentes. Para extrair somente o código de máquina, podemos considerar somente os 6 primeiros campos da linha tendo o espaço como separador.
$ objdump -d prog | grep '[0-9a-f]:' | grep -v file | cut -f2 -d ':' | cut -f1-6 -d' ' 6a 0b 58 99 52 68 2f 2f 73 68 68 2f 62 69 6e 89 e3 31 c9 cd 80
Por fim só falta formatarmos essa saída. Primeiro retiramos os espaços desnecessários:
$ objdump -d prog | grep '[0-9a-f]:' | grep -v file | cut -f2 -d ':' | cut -f1-6 -d' ' | tr -s ' ' | tr '\t' ' ' 6a 0b 58 99 52 68 2f 2f 73 68 68 2f 62 69 6e 89 e3 31 c9 cd 80
e usamos o sed para adicionar o \x no início de cada byte e as aspas no início e no fim da string. O paste unirá todas as linhas em uma só, usando o delimitador nulo:
$ objdump -d prog | grep '[0-9a-f]:' | grep -v file | cut -f2 -d ':' | cut -f1-6 -d' ' | tr -s ' ' | tr '\t' ' ' | sed 's/ $//' | sed 's/ /\\x/g' | paste -d '' -s | sed 's/^/"/' | sed 's/$/"/' "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"
Obviamente esses comandos podem ser melhorados para evitar a chamada de processos desnecessários (ex: um sed atrás do outro), mas preferi usar a forma original, como visto em [1].
Obtendo a shellcode e o assembly como comentário
Sempre é bom incluir as instruções em assembly junto a shellcode para facilitar a leitura da mesma. Pensando nisso, modifiquei os comandos apresentados anteriormente para gerar uma shellcode incluindo as instruções assembly como comentários em C.
$ objdump -d prog | grep '[0-9a-f]:' | grep -v file | cut -f2 -d ':' | tr '\t' ' ' | sed ':a;s/ \([0-9a-f][0-9a-f]\)/\\x\1/;ta;s,^\([^ ]*\) *\(.*\),"\1" /* \2 */,' | column -t "\x6a\x0b" /* push $0xb */ "\x58" /* pop %eax */ "\x99" /* cltd */ "\x52" /* push %edx */ "\x68\x2f\x2f\x73\x68" /* push $0x68732f2f */ "\x68\x2f\x62\x69\x6e" /* push $0x6e69622f */ "\x89\xe3" /* mov %esp,%ebx */ "\x31\xc9" /* xor %ecx,%ecx */ "\xcd\x80" /* int $0x80 */
Os comandos até o sed são os mesmos que os anteriores. O sed substituirá cada ocorrência de “<espaco>XX”, sendo XX dígitos hexas, por “\xXX”. Esse processo de substituição é feito usando um loop já que o uso do ‘g’ poderia pegar dígitos hexas nas instruções em assembly, o que não estaria correto. Quando a condição do loop falhar, a segunda substituição será realizada. Basicamente o que ela faz é pegar tudo antes do espaço (a shellcode em si) e adicionar aspas duplas, e o restante da string, incluir entre os indicadores de comentários em C, /* */. Por fim, o comando column embeleza a saída, criando uma tabela com espaçamentos iguais em todas as linhas.
Conclusão
Esse post foi para mostrar mais uma vez a eficiência das ferramentas padrões do linux e como o tratamento de texto merece também um pouco de atenção por parte dos usuários, por facilitar algumas tarefas entediantes, como obter uma shellcode à partir da saída do objdump.
Referências
[1] Get all shellcode on binary file from objdump, by gunslinger_ (Acessado em: Março/2012)http://www.commandlinefu.com/commands/view/6051/get-all-shellcode-on-binary-file-from-objdump
[2] exploit-db (Acessado em: Março/2012)
http://www.exploit-db.com/shellcode/