BROP - Blind Return Oriented Programming
Informações Básicas
O objetivo deste ataque é ser capaz de abusar de um ROP por meio de um estouro de buffer sem nenhuma informação sobre o binário vulnerável. Este ataque é baseado no seguinte cenário:
Uma vulnerabilidade de pilha e conhecimento de como ativá-la.
Uma aplicação de servidor que reinicia após uma falha.
Ataque
1. Encontrar o deslocamento vulnerável enviando um caractere a mais até que uma falha no servidor seja detectada
2. Forçar o canário para vazá-lo
3. Forçar os endereços RBP e RIP armazenados na pilha para vazá-los
Você pode encontrar mais informações sobre esses processos aqui (BF Forked & Threaded Stack Canaries) e aqui (BF Addresses in the Stack).
4. Encontrar o gadget de parada
Este gadget basicamente permite confirmar que algo interessante foi executado pelo gadget ROP porque a execução não travou. Geralmente, este gadget será algo que interrompe a execução e é posicionado no final da cadeia ROP ao procurar gadgets ROP específicos que foram executados.
5. Encontrar o gadget BROP
Esta técnica usa o gadget ret2csu. E isso ocorre porque se você acessar este gadget no meio de algumas instruções, você obtém gadgets para controlar rsi
e rdi
:
Estes seriam os gadgets:
pop rsi; pop r15; ret
pop rdi; ret
Observe como com esses gadgets é possível controlar 2 argumentos de uma função a ser chamada.
Além disso, observe que o gadget ret2csu tem uma assinatura muito única porque ele vai desempilhar 6 registradores da pilha. Então, enviando uma cadeia como:
'A' * deslocamento + canário + rbp + ENDEREÇO + 0xdead * 6 + PARADA
Se a PARADA for executada, isso basicamente significa que um endereço que está desempilhando 6 registradores da pilha foi usado. Ou que o endereço usado também foi um endereço de PARADA.
Para remover esta última opção, uma nova cadeia como a seguinte é executada e não deve executar o gadget de PARADA para confirmar que o anterior desempilhou 6 registradores:
'A' * deslocamento + canário + rbp + ENDEREÇO
Conhecendo o endereço do gadget ret2csu, é possível inferir o endereço dos gadgets para controlar rsi
e rdi
.
6. Encontrar PLT
A tabela PLT pode ser pesquisada a partir de 0x400000 ou do endereço RIP vazado da pilha (se o PIE estiver sendo usado). As entradas da tabela são separadas por 16B (0x10B), e quando uma função é chamada, o servidor não trava mesmo se os argumentos não estiverem corretos. Além disso, verificar o endereço de uma entrada na PLT + 6B também não trava pois é o primeiro código executado.
Portanto, é possível encontrar a tabela PLT verificando os seguintes comportamentos:
'A' * deslocamento + canário + rbp + ENDEREÇO + PARADA
-> sem travamento'A' * deslocamento + canário + rbp + (ENDEREÇO + 0x6) + PARADA
-> sem travamento'A' * deslocamento + canário + rbp + (ENDEREÇO + 0x10) + PARADA
-> sem travamento
7. Encontrar strcmp
A função strcmp
define o registrador rdx
como o comprimento da string sendo comparada. Observe que rdx
é o terceiro argumento e precisamos que ele seja maior que 0 para posteriormente usar write
para vazar o programa.
É possível encontrar a localização do strcmp
na PLT com base em seu comportamento usando o fato de que agora podemos controlar os 2 primeiros argumentos das funções:
strcmp(<endereço não lido>, <endereço não lido>) -> travamento
strcmp(<endereço não lido>, <endereço lido>) -> travamento
strcmp(<endereço lido>, <endereço não lido>) -> travamento
strcmp(<endereço lido>, <endereço lido>) -> sem travamento
É possível verificar isso chamando cada entrada da tabela PLT ou usando o caminho lento da PLT que basicamente consiste em chamar uma entrada na tabela PLT + 0xb (que chama para dlresolve
) seguido na pilha pelo número da entrada que se deseja sondar (começando em zero) para escanear todas as entradas da PLT:
strcmp(<endereço não lido>, <endereço lido>) -> travamento
b'A' * deslocamento + canário + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + PARADA
-> Vai travarstrcmp(<endereço lido>, <endereço não lido>) -> travamento
b'A' * deslocamento + canário + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + PARADA
strcmp(<endereço lido>, <endereço lido>) -> sem travamento
b'A' * deslocamento + canário + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + PARADA
Lembre-se de que:
BROP + 0x7 aponta para
pop RSI; pop R15; ret;
BROP + 0x9 aponta para
pop RDI; ret;
PLT + 0xb aponta para uma chamada para dl_resolve.
Tendo encontrado strcmp
, é possível definir rdx
com um valor maior que 0.
Observe que geralmente rdx
já terá um valor maior que 0, então esta etapa pode não ser necessária.
### 8. Encontrando Write ou equivalente
Finalmente, é necessário um gadget que exfiltra dados para exfiltrar o binário. E neste momento é possível controlar 2 argumentos e definir rdx
maior que 0.
Existem 3 funções comuns que poderiam ser abusadas para isso:
puts(data)
dprintf(fd, data)
write(fd, data, len(data)
No entanto, o artigo original menciona apenas o write
, então vamos falar sobre ele:
O problema atual é que não sabemos onde a função write está dentro da PLT e não sabemos um número de fd para enviar os dados para nosso socket.
No entanto, sabemos onde está a tabela PLT e é possível encontrar write com base em seu comportamento. E podemos criar várias conexões com o servidor e usar um FD alto esperando que corresponda a algumas de nossas conexões.
Assinaturas de comportamento para encontrar essas funções:
'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Se houver dados impressos, então puts foi encontrado'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Se houver dados impressos, então dprintf foi encontrado'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Se houver dados impressos, então write foi encontrado
Exploração Automática
Referências
Artigo original: https://www.scs.stanford.edu/brop/bittau-brop.pdf
Last updated