Off by one overflow

Reading time: 5 minutes

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks

Podstawowe informacje

Mając dostęp tylko do przepełnienia 1B, atakujący może zmodyfikować pole size następnego kawałka. Umożliwia to manipulację tym, które kawałki są faktycznie zwalniane, potencjalnie generując kawałek, który zawiera inny legalny kawałek. Eksploatacja jest podobna do double free lub nakładających się kawałków.

Istnieją 2 typy podatności off by one:

  • Dowolny bajt: Ten typ pozwala na nadpisanie tego bajtu dowolną wartością
  • Bajt null (off-by-null): Ten typ pozwala na nadpisanie tego bajtu tylko wartością 0x00
  • Typowym przykładem tej podatności można zobaczyć w poniższym kodzie, gdzie zachowanie strlen i strcpy jest niespójne, co pozwala ustawić bajt 0x00 na początku następnego kawałka.
  • Może to być wykorzystane z House of Einherjar.
  • Jeśli używasz Tcache, można to wykorzystać do sytuacji double free.
Off-by-null
c
// From https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/off_by_one/
int main(void)
{
char buffer[40]="";
void *chunk1;
chunk1 = malloc(24);
puts("Get Input");
gets(buffer);
if(strlen(buffer)==24)
{
strcpy(chunk1,buffer);
}
return 0;
}

Wśród innych kontroli, teraz za każdym razem, gdy kawałek jest zwalniany, poprzedni rozmiar jest porównywany z rozmiarem skonfigurowanym w metadanych kawałka, co sprawia, że atak ten jest dość skomplikowany od wersji 2.28.

Przykład kodu:

Cel

  • Sprawić, aby kawałek był zawarty w innym kawałku, tak aby dostęp do zapisu w tym drugim kawałku pozwalał na nadpisanie zawartego.

Wymagania

  • Off by one overflow, aby zmodyfikować informacje o rozmiarze metadanych.

Ogólny atak off-by-one

  • Przydziel trzy kawałki A, B i C (powiedzmy rozmiar 0x20), oraz kolejny, aby zapobiec konsolidacji z top-chunk.
  • Zwalnij C (wstawiony do listy zwolnionych kawałków 0x20 Tcache).
  • Użyj kawałka A, aby przepełnić B. Nadużyj off-by-one, aby zmodyfikować pole size B z 0x21 na 0x41.
  • Teraz mamy B zawierający zwolniony kawałek C.
  • Zwalnij B i przydziel kawałek 0x40 (zostanie umieszczony tutaj ponownie).
  • Możemy zmodyfikować wskaźnik fd z C, który wciąż jest zwolniony (zatrucie Tcache).

Atak off-by-null

  • 3 kawałki pamięci (a, b, c) są rezerwowane jeden po drugim. Następnie środkowy kawałek jest zwalniany. Pierwszy kawałek zawiera lukę off by one, a atakujący nadużywa jej z 0x00 (jeśli poprzedni bajt był 0x10, spowodowałoby to, że środkowy kawałek wskazywałby, że jest o 0x10 mniejszy niż w rzeczywistości).
  • Następnie w zwolnionym kawałku (b) przydzielane są 2 mniejsze kawałki, jednak b + b->size nigdy nie aktualizuje kawałka c, ponieważ wskazywany adres jest mniejszy niż powinien.
  • Następnie, b1 i c są zwalniane. Ponieważ c - c->prev_size wciąż wskazuje na b (b1 teraz), oba są konsolidowane w jeden kawałek. Jednak b2 wciąż znajduje się pomiędzy b1 a c.
  • Na koniec wykonywana jest nowa alokacja malloc, odzyskując ten obszar pamięci, który w rzeczywistości będzie zawierał b2, co pozwala właścicielowi nowego malloca kontrolować zawartość b2.

Ten obrazek doskonale wyjaśnia atak:

https://heap-exploitation.dhavalkapil.com/attacks/shrinking_free_chunks

Inne przykłady i odniesienia

  • https://heap-exploitation.dhavalkapil.com/attacks/shrinking_free_chunks
  • Bon-nie-appetit. HTB Cyber Apocalypse CTF 2022
  • Off-by-one z powodu strlen uwzględniającego pole size następnego kawałka.
  • Używane jest Tcache, więc ogólne ataki off-by-one działają, aby uzyskać dowolną prymitywę zapisu z zatruciem Tcache.
  • Asis CTF 2016 b00ks
  • Możliwe jest nadużycie off by one, aby wyciekł adres z sterty, ponieważ bajt 0x00 na końcu ciągu jest nadpisywany przez następne pole.
  • Dowolny zapis uzyskuje się przez nadużycie zapisu off by one, aby wskaźnik wskazywał w inne miejsce, gdzie zostanie zbudowana fałszywa struktura z fałszywymi wskaźnikami. Następnie możliwe jest podążanie za wskaźnikiem tej struktury, aby uzyskać dowolny zapis.
  • Adres libc jest wyciekany, ponieważ jeśli sterta jest rozszerzana za pomocą mmap, pamięć przydzielona przez mmap ma stały offset od libc.
  • Na koniec dowolny zapis jest nadużywany, aby zapisać w adresie __free_hook z adresem one gadget.
  • plaidctf 2015 plaiddb
  • Istnieje luka NULL off by one w funkcji getline, która odczytuje linie wejściowe użytkownika. Ta funkcja jest używana do odczytu "klucza" treści, a nie samej treści.
  • W opisie pięć początkowych kawałków jest tworzonych:
  • chunk1 (0x200)
  • chunk2 (0x50)
  • chunk5 (0x68)
  • chunk3 (0x1f8)
  • chunk4 (0xf0)
  • chunk obronny (0x400), aby uniknąć konsolidacji z top chunk.
  • Następnie kawałki 1, 5 i 3 są zwalniane, więc:

[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]

- Następnie nadużywając chunk3 (0x1f8), luka null off-by-one jest nadużywana, zapisując `prev_size` na `0x4e0`.
- Zauważ, jak rozmiary początkowo przydzielonych kawałków 1, 2, 5 i 3 oraz nagłówki 4 z tych kawałków sumują się do `0x4e0`: `hex(0x1f8 + 0x10 + 0x68 + 0x10 + 0x50 + 0x10 + 0x200) = 0x4e0`
- Następnie kawałek 4 jest zwalniany, generując kawałek, który pochłania wszystkie kawałki aż do początku:
- ```python
[ 0x4e0 Chunk 1-2-5-3 (free) ] [ 0xf0 Chunk 4 (corrupted) ] [ 0x400 Chunk defense ]

[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]