W jaki sposób niedomiar buforu może prowadzić do zdalnego wykonania kodu na 64-bitach?

Mam następujący podsumowany pseudo kod w C89 z biblioteki serwera ssh, który zapewnia dostęp tylko do rzeczy takich jak git-shell (/bin/bash jest zastępowane programem do uruchomienia, więc nie można zrobić czegoś innego) :

struct _raw_uncapped_ssh_string { // no limit on the size of the string; uint32_t len; char non_null_terminated_string[]; // by protocol design it have a minimum length of 1 }; typedef struct _raw_uncapped_ssh_string raw_ssh_string; union buffer { void * uncapped_zlib_decompressed_network_data; // yes, the size is uncapped, so it’s possible to put 4Gb of // data in it that would be copied later into memory. zlib // allow easily to turn some Mb in Gb of data, but it’s not // the point of the question. raw_ssh_string st; }; get_command (compressed_network_data) { size_t len; char * command; buffer string=uncompress_to_buffer(compressed_network_data); len=ntohl(string.st.len)+1; command=malloc(len+1); command[len]=0; // here’s the point, both the string length and content as // well it’s supplied size is controlled by the attacker. memcpy(command,string.st.data,len); return command; } 

Oto jak polecenie jest wykonywane później (ciąg command pozostaje niezmieniony po get_command()) .

const char *args[]={"/bin/bash",command,NULL}; // /bin/bash isn’t the shell, it has been replaced by git‑shell. // redirect the program output to the network. dup2(stdin, 0); dup2(stdout,1); dup2(stdout,2); close(stdin); close(stdout); //if this return execution failed and print an error message return execv(args[0],(char * const *)args); // I don’t know which is the system, so I can’t know about the libc behaviour. 

Nie mogę zrobić memcpy(command,string.st.data,0), ponieważ trzeci członek memcpy ma minimalny rozmiar 1, aw moim kontekście size_t używa 64-bitowej liczby całkowitej, nie mogę wykonać przepełnienia bufora, ponieważ jest len.

Wszystko, co mogę zrobić, to ustawić len na wartość większą niż ta przydzielona do string.st.data. Jest to niedomiar buforu, który umożliwia mi odczyt nieprzydzielonej pamięci.
Mogę odczytać pamięć serwera, jednak nie widzę, jakie poufne dane może przechowywać publiczny serwer ssh (w moim przypadku lista użytkowników, którzy może wykonać ssh jest publiczne) .

Czy niedopełnienie bufora w memcpy zezwala na zdalne wykonanie kodu?

Komentarze

  • Jeśli kod naprawdę działa command[len] = 0 to jest to przepełnienie bufora, ponieważ maksymalny indeks dla bufora o długości len to len-1. Alternatywnie, jeśli rzeczywisty kod powoduje malloc(len+1) zamiast malloc(len), możesz spowodować ogromne przepełnienie bufora, ustawiając len 0xFFFFFFFF.
  • @ThomasPornin: Nie, to błąd z mojej strony, w rzeczywistości wywołuje kilka funkcji do wykonania to. Załóżmy, że pozostałe są prawdziwe.
  • @ThomasPornin: mówię o 64 bitach, ponieważ len to size_t, używa 64-bitowej liczby całkowitej. Więc nie ma sposobu na przepełnienie liczb całkowitych.
  • @ThomasPornin Przydzielają len+1, więc ustawienie 0 powinno być prawidłowe.
  • @ RoraΖ: Mówił o ustawieniu string.st.len na –1.

Odpowiedź

Ogólnie rzecz biorąc, nie, niedopełnienie buforu nie może być używane do zdalnego wykonania kodu. Ponieważ dane kontrolowane przez atakującego nigdy nie opuszczają przydzielonej im przestrzeni, nigdy nie mają możliwości przejęcia przepływu wykonywania programu.

Niedopełnienie bufora może potencjalnie powodować inne typy ataków, takie jak ujawnienie informacji (jeśli program liczy, że oryginalna zawartość bufora zostanie wymazana przez nowe dane).

Komentarze

  • Lub jak ja widziałem ostatnio w git, później przekształciłem się w przepełnienie bufora.

Odpowiedź

Kiedy oryginalnie napisałem tę odpowiedź , wygląda na to, że przeoczyłem kilka nieporozumień, które utrzymujesz. Te nieporozumienia prawdopodobnie uniemożliwiłyby ci zrozumienie mojej odpowiedzi. Żeby było jasne, zamierzam uczynić ten tekst obszernym, aby podkreślić akcenty, a nie okazywać brak szacunku:

Wiele terminów, których używasz, nie oznacza tego, co myślisz, że znaczą.

Na przykład:

  • „trzeci element członkowski memcpy nie istnieje, ponieważ memcpy to funkcja, a nie struct czy union.
  • „Nie mogę wykonać przepełnienia bufora, ponieważ jest len i nie mogę zabraknąć benzyny, ponieważ jest benzyna. Czy błagam Pytanie? Wydaje mi się to pasującą analogią, zwłaszcza że teoretycznie twój kod mógłby wywołać przepełnienie bufora.
  • „Jedyne, co mogę zrobić, to ustawić len na wartość większą niż wartość przydzielona do string.st.data. To jest niedomiar buforu … ” Nie, to nie jest definicja niedopełnienia buforu. Niedopełnienie buforu występuje, gdy uzyskujesz dostęp do tablicy poza zakresem przy użyciu indeksu ujemnego lub indeksu, który spowodowałby arytmetyczne zawijanie wskaźnika. To drugie może być możliwe w przypadku niektórych konfiguracji (nawet w „systemach 64-bitowych”, cokolwiek to znaczy), ale wątpię, że to miałeś na myśli, pisząc te słowa, ponieważ później zastosowałeś się do nich z:
  • „… umożliwiając mi odczytywanie nieprzydzielonej pamięci”. Myślę, że być może miałeś na myśli „pamięć niezainicjowaną”. Aby było jasne, myślę, że chciałeś stwierdzić, że „ve przydzielił nadmiar, a nadwyżkę pozostawił niezainicjowany , a być może chcesz coś z tym zrobić (patrz calloc lub memset).

Rozważmy przez chwilę char *fubar = NULL; … ten rodzaj wskaźnika ma zazwyczaj zero wartość.Dereferencja jest uważana za dereferencję wskaźnika zerowego , ale jeśli napiszemy coś takiego jak 0["hello"], otrzymamy to samo, co "hello"[0] (czyli "h"). W związku z tym w atakach można zastosować odwołanie do zerowego wskaźnika, gdy atakujący kontroluje wyrażenie między nawiasami kwadratowymi (tak jak w Twojej sytuacji).

Wracając do fubar rzecz; powiedzmy, że memset(&fubar, UCHAR_MAX, sizeof fubar);, teraz fubar to wszystko 1-bitowe, co oznacza, że może to być adres to największy adres, jaki nasz system mógłby (teoretycznie) pomieścić. A co, jeśli uzyskamy dostęp do fubar[1]? Jak uzyskać dostęp do elementu po największym adresie? adres, a następnie zawinąć? Technicznie, wszystko to jest niezdefiniowanym zachowaniem, ale gdybym miał nazwać to w typowej architekturze, byłoby to:

  1. Przepełnienie arytmetyczne wskaźnik prowadzący do …
  2. wyłuskiwanie wskaźnika o wartości Null i / lub potencjalnie niedopełnienie bufora .

czy niedomiar bufora w memcpy zezwala na zdalne wykonanie kodu?

Bufor niedopełnienie może pozwolić atakującemu na nadpisanie wskaźników funkcji znajdujących się w obszarach pamięci przed daną tablicą. Jeśli te wskaźniki funkcji będą wskazywać na szelkod, ten szelkod może być wykonywane, gdy zostaną „ponownie wywołane później.

W tym nieco niejasnym fragmencie kodu wydaje się, że istnieje ryzyko niedopełnienia bufora, gdy int ma szerszy domeny niż uint32_t, ponieważ ntohl(string.st.len)+1 spowodowałoby przekonwertowanie wartości uint32_t na int typ. Zastanów się na przykład, czy INT_MIN to -4294967296 (czyli o jeden mniej niż 0 - UINT32_MAX) a INT_MAX to 4294967295 … byłby to zasadniczo 33-bitowy int z dopełnienie do szerokości bajtu; rzadkie, ale możliwe. W takiej sytuacji wyrażenie ntohl(string.st.len)+1 nie jest uint32_t w typie; ma int w typie i zamiast zawijać z powrotem do 0, gdy wystąpi przepełnienie liczby całkowitej bez znaku , prawdopodobnie zawija się do -4294967296 w przypadku wystąpienia przepełnienia liczby całkowitej ze znakiem .

Jeśli „szukasz gwarancji przeciwko niedopełnieniu buforu , użyj funkcji U przyrostek literału liczby całkowitej (tj. ntohl(string.st.len)+1U). Wtedy w takiej sytuacji „skończysz z wyrażeniem uint32_t lub unsigned int (w zależności od tego, który typ ma największy domain).

Jeśli weźmiesz pod uwagę, że ntohl(string.st.len) może zwrócić o jeden mniej niż maksymalna wartość dla tego typu bez znaku (cokolwiek to jest), to len=ntohl(string.st.len)+1 zwróci maksymalną wartość, malloc(len+1) spowoduje zawijanie bez znaku , więc w końcu wywołasz malloc(0), a następnie command[len]=0 zapisze dobrze i dobrze poza końcem tablicy. Wtedy oczywiście „będziesz także mieć problem z memcpy(command,string.st.data,len); (jest to przepełnienie bufora).

Niedopełnienia bufora i przepełnienia nie są jedynym ryzykiem. Jeśli nie „t sprawdź zwracaną wartość malloc i malloc zwraca NULL, a następnie odwołanie do pustego wskaźnika może zostać użyte do spowodowania wykonania dowolnego kodu. Oznacza to, że powinieneś mieć metodę komunikowania się malloc nie udaje się z powrotem do dzwoniącego. Oznacza to również, że możesz użyć tej samej metody do sprawdzenia i przekazania informacji o problemie zawijania do dzwoniącego.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *