.. | ||
pointer-redirecting.md | ||
README.md | ||
ret2win.md | ||
stack-pivoting-ebp2ret-ebp-chaining.md | ||
stack-shellcode.md | ||
uninitialized-variables.md |
Przepełnienie stosu
Nauka hakowania AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!
Inne sposoby wsparcia HackTricks:
- Jeśli chcesz zobaczyć swoją firmę reklamowaną w HackTricks lub pobrać HackTricks w formacie PDF, sprawdź PLANY SUBSKRYPCYJNE!
- Zdobądź oficjalne gadżety PEASS & HackTricks
- Odkryj Rodzinę PEASS, naszą kolekcję ekskluzywnych NFT
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się swoimi sztuczkami hakowania, przesyłając PR-y do HackTricks i HackTricks Cloud na GitHubie.
Co to jest przepełnienie stosu
Przepełnienie stosu to podatność, która występuje, gdy program zapisuje więcej danych na stosie, niż jest mu przydzielone do przechowywania. Nadmiarowe dane nadpisują sąsiednie miejsce w pamięci, prowadząc do uszkodzenia poprawnych danych, zakłócenia przepływu kontroli i potencjalnie wykonania złośliwego kodu. Ten problem często wynika z użycia funkcji niebezpiecznych, które nie wykonują sprawdzania granic danych wejściowych.
Głównym problemem tego nadpisania jest to, że zachowany wskaźnik instrukcji (EIP/RIP) i zachowany wskaźnik bazowy (EBP/RBP) do powrotu do poprzedniej funkcji są przechowywane na stosie. Dlatego atakujący będzie mógł nadpisać te wartości i kontrolować przepływ wykonania programu.
Podatność zazwyczaj pojawia się, gdy funkcja kopiuję więcej bajtów na stosie niż jest na niego zaalokowane, co pozwala na nadpisanie innych części stosu.
Niektóre powszechne funkcje podatne na to to: strcpy
, strcat
, sprintf
, gets
... Ponadto funkcje takie jak fgets
, read
& memcpy
, które przyjmują argument długości, mogą być używane w podatny sposób, jeśli określona długość jest większa niż zaalokowana.
Na przykład, następujące funkcje mogą być podatne:
void vulnerable() {
char buffer[128];
printf("Enter some text: ");
gets(buffer); // This is where the vulnerability lies
printf("You entered: %s\n", buffer);
}
Znajdowanie przesunięć przepełnienia stosu
Najczęstszym sposobem na znalezienie przepełnienia stosu jest podanie bardzo dużego wejścia z literami A
(np. python3 -c 'print("A"*1000)'
) i oczekiwanie Segmentation Fault
, co wskazuje, że próbowano uzyskać dostęp do adresu 0x41414141
.
Ponadto, gdy już zidentyfikujesz podatność na przepełnienie stosu, będziesz musiał znaleźć przesunięcie, aż będzie możliwe nadpisanie adresu powrotu, w tym celu zazwyczaj używa się sekwencji De Bruijna. Dla danego alfabetu o rozmiarze k i podsekwencji o długości n, jest to cykliczna sekwencja, w której każda możliwa podsekwencja o długości n występuje dokładnie raz jako ciągła podsekwencja.
W ten sposób, zamiast ręcznie określać potrzebne przesunięcie do kontrolowania EIP, można użyć jednej z tych sekwencji jako dopełnienia, a następnie znaleźć przesunięcie bajtów, które nadpisały ten adres.
Można to zrobić przy użyciu pwntools:
from pwn import *
# Generate a De Bruijn sequence of length 1000 with an alphabet size of 256 (byte values)
pattern = cyclic(1000)
# This is an example value that you'd have found in the EIP/IP register upon crash
eip_value = p32(0x6161616c)
offset = cyclic_find(eip_value) # Finds the offset of the sequence in the De Bruijn pattern
print(f"The offset is: {offset}")
lub GEF:
#Patterns
pattern create 200 #Generate length 200 pattern
pattern search "avaaawaa" #Search for the offset of that substring
pattern search $rsp #Search the offset given the content of $rsp
Wykorzystywanie przepełnień stosu
Podczas przepełnienia (przy założeniu, że rozmiar przepełnienia jest wystarczająco duży) będziesz mógł nadpisać wartości zmiennych lokalnych na stosie aż do osiągnięcia zapisanych EBP/RBP i EIP/RIP (lub nawet więcej).
Najczęstszym sposobem wykorzystania tego rodzaju podatności jest modyfikacja adresu powrotu, aby po zakończeniu funkcji przepływ sterowania został przekierowany w miejsce, które użytkownik określił w tym wskaźniku.
Jednakże, w innych scenariuszach może być wystarczające nadpisanie wartości niektórych zmiennych na stosie dla eksploatacji (jak w łatwych wyzwaniach CTF).
Ret2win
W tego rodzaju wyzwaniach CTF, istnieje funkcja wewnątrz binariów, która nigdy nie jest wywoływana i musisz ją wywołać, aby wygrać. W tych wyzwaniach wystarczy znaleźć przesunięcie do nadpisania adresu powrotu i znaleźć adres funkcji do wywołania (zwykle ASLR byłoby wyłączone), więc gdy podatna funkcja zakończy działanie, ukryta funkcja zostanie wywołana:
{% content-ref url="ret2win.md" %} ret2win.md {% endcontent-ref %}
Shellcode na stosie
W tym scenariuszu atakujący może umieścić shellcode na stosie i wykorzystać kontrolowany EIP/RIP, aby przeskoczyć do shellcode i wykonać dowolny kod:
{% content-ref url="stack-shellcode.md" %} stack-shellcode.md {% endcontent-ref %}
ROP i techniki Ret2...
Ta technika stanowi podstawową strukturę do obejścia głównej ochrony poprzedniej techniki: Brak wykonalnego stosu (NX). Pozwala ona na wykonanie kilku innych technik (ret2lib, ret2syscall...), które zakończą się wykonaniem dowolnych poleceń poprzez nadużywanie istniejących instrukcji w binariach:
{% content-ref url="../rop-return-oriented-programing/" %} rop-return-oriented-programing {% endcontent-ref %}
Rodzaje zabezpieczeń
Istnieje kilka zabezpieczeń próbujących zapobiec wykorzystaniu podatności, sprawdź je w:
{% content-ref url="../common-binary-protections-and-bypasses/" %} common-binary-protections-and-bypasses {% endcontent-ref %}