Wie kann ein Pufferunterlauf zur Remotecodeausführung auf 64-Bit führen?

Ich habe den folgenden zusammengefassten Pseudocode in C89 aus einer SSH-Serverbibliothek, die nur Zugriff auf Dinge wie Git-Shell (/bin/bash wird durch das auszuführende Programm ersetzt, daher ist es nicht möglich, etwas anderes zu tun. :

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; } 

Hier ist wie der Befehl später ausgeführt wird (die Zeichenfolge command bleibt nach get_command()) unverändert.

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. 

Ich kann memcpy(command,string.st.data,0) nicht ausführen, da das dritte Mitglied von memcpy eine hat Mindestgröße 1, und in meinem Kontext verwendet size_t eine 64-Bit-Ganzzahl. Ich kann keinen Pufferüberlauf durchführen, da len.

Ich kann len nur auf einen Wert setzen, der größer ist als der Wert, der string.st.data zugewiesen ist. Dies ist ein Pufferunterlauf, der es mir ermöglicht, nicht zugewiesenen Speicher zu lesen.
Ich kann den Serverspeicher lesen, kann jedoch nicht sehen, welche vertraulichen Daten ein öffentlicher SSH-Server enthalten kann (in meinem Fall die Liste der Benutzer, die dies tun kann ausführen ssh ist öffentlich) .

Ein Pufferunterlauf auf memcpy ermöglicht Remote Codeausführung?

Kommentare

  • Wenn der Code wirklich command[len] = 0 dann ist das ein Pufferüberlauf, da der maximale Index für einen Puffer der Länge len len-1 ist. Wenn der tatsächliche Code einen malloc(len+1) anstelle eines malloc(len) ausführt, können Sie alternativ einen massiven Pufferüberlauf durchführen, indem Sie die len Wert auf 0xFFFFFFFF.
  • @ThomasPornin: Nein, dies ist ein Fehler von meiner Seite, tatsächlich werden mehrere Funktionen aufgerufen Dies. Angenommen, der Rest ist wahr.
  • @ThomasPornin: Ich spreche von 64 Bit, da len size_t ist, wird eine 64-Bit-Ganzzahl verwendet. Es gibt also keine Möglichkeit für einen Ganzzahlüberlauf.
  • @ThomasPornin Sie weisen len+1 zu, sodass die Einstellung auf 0 gültig sein sollte.
  • @ RoraΖ: Er sprach davon, string.st.len auf ‑1 zu setzen.

Antwort

Im Allgemeinen kann ein Pufferunterlauf nicht für die Remotecodeausführung verwendet werden. Da von Angreifern kontrollierte Daten niemals den dafür zugewiesenen Speicherplatz verlassen, können sie niemals den Ausführungsfluss des Programms übernehmen.

Ein Pufferunterlauf kann andere Arten von Angriffen verursachen, z Offenlegung von Informationen (wenn das Programm darauf zählt, dass der ursprüngliche Inhalt des Puffers durch die neuen Daten gelöscht wird).

Kommentare

  • Oder als I. sah kürzlich in git, später in einen Pufferüberlauf umgewandelt werden.

Antwort

Als ich diese Antwort ursprünglich schrieb Anscheinend habe ich einige Missverständnisse, die Sie haben, beschönigt. Diese Missverständnisse würden Sie wahrscheinlich daran hindern, meine Antwort zu verstehen. Um es klar zu sagen, ich werde diesen Text groß machen, um Hervorhebungen hervorzuheben und nicht respektlos zu sein:

Viele der Begriffe, die Sie verwenden, bedeuten nicht, was Sie zu bedeuten scheinen.

Zum Beispiel:

  • „das dritte Mitglied von memcpy ist nicht vorhanden, weil memcpy ist eine Funktion, keine struct oder union.
  • „Ich kann keinen Pufferüberlauf durchführen, da len vorhanden ist und mir das Benzin nicht ausgehen kann, da Benzin vorhanden ist Frage? Es scheint mir eine passende Analogie zu sein, zumal Ihr Code theoretisch einen Pufferüberlauf auslösen könnte.
  • „Ich kann nur len auf einen Wert, der größer ist als der Wert, der string.st.data zugewiesen ist. Dies ist ein Pufferunterlauf … „ Nein, das ist nicht die Definition eines Pufferunterlaufs. Ein Pufferunterlauf tritt auf, wenn Sie mit einem negativen Index oder einem Index, der dies verursachen würde, auf ein Array außerhalb der Grenzen zugreifen Zeigerarithmetisches Umbrechen kann auftreten. Letzteres ist möglicherweise für einige Konfigurationen möglich (sogar auf „64-Bit-Systemen“, was auch immer das bedeutet), aber ich bezweifle, dass Sie dies beim Schreiben dieser Wörter gemeint haben, weil Sie sie dann weiterverfolgt haben mit:
  • „… damit ich nicht zugewiesenen Speicher lesen kann.“ Ich denke, Sie meinten vielleicht „nicht initialisierten Speicher“. Um es klar zu sagen, ich denke, Sie wollten sagen, dass Sie „ve hat einen Überschuss zugewiesen und den Überschuss nicht initialisiert , und vielleicht möchten Sie etwas dagegen tun (siehe calloc oder memset).

Betrachten wir für einen Moment char *fubar = NULL; … diese Art von Zeiger hat im Allgemeinen eine Null Wert.Das Dereferenzieren wird als Nullzeiger-Dereferenzierung betrachtet, aber wenn wir etwas wie 0["hello"] schreiben, erhalten wir dasselbe wie "hello"[0] (das ist "h"). Daher kann bei Angriffen eine Nullzeiger-Dereferenzierung verwendet werden, wenn der Angreifer den Ausdruck zwischen den eckigen Klammern steuert (wie in Ihrer Situation).

Zurück zum fubar thing; Nehmen wir an, wir memset(&fubar, UCHAR_MAX, sizeof fubar);, jetzt fubar sind alle 1-Bit, was bedeutet, dass möglicherweise eine Adresse ist Dies ist die größte Adresse, die unser System (theoretisch) aufnehmen kann. Was ist, wenn wir auf fubar[1] zugreifen? Wie können Sie auf das Element nach der größten Adresse zugreifen? Adresse dann wieder umwickeln? Technisch gesehen ist dies alles undefiniertes Verhalten, aber wenn ich das auf einer gemeinsamen Architektur nennen würde, wäre es:

  1. Arithmetischer Überlauf von ein Zeiger, der zu …
  2. Nullzeiger-Dereferenzierung und / oder möglicherweise einem Pufferunterlauf führt.

Ermöglicht ein Pufferunterlauf auf memcpy die Remotecodeausführung?

Ein Puffer Ein Unterlauf kann es einem Angreifer ermöglichen, Funktionszeiger zu überschreiben, die sich in den Speicherbereichen vor dem betreffenden Array befinden. Wenn diese Funktionszeiger auf Shellcode zeigen, kann dieser Shellcode ausgeführt werden, wenn sie später erneut aufgerufen werden.

In diesem etwas undurchsichtigen Code scheint das Risiko eines Pufferunterlaufs zu bestehen, wenn int einen breiteren hat Domäne als uint32_t, da ntohl(string.st.len)+1 dazu führen würde, dass der Wert uint32_t in einen int Typ. Stellen Sie sich zum Beispiel vor, ob INT_MIN -4294967296 ist (was weniger als 0 - UINT32_MAX ist). und INT_MAX ist 4294967295 … dies wäre im Wesentlichen ein 33-Bit int mit Auffüllen, um auf die Breite eines Bytes aufzufüllen; ungewöhnlich, aber möglich. Unter diesen Umständen ist der Ausdruck ntohl(string.st.len)+1 nicht uint32_t vom Typ; Der Typ „s int wird nicht auf 0 zurückgesetzt, wenn ein vorzeichenloser Ganzzahlüberlauf auftritt, sondern wahrscheinlich auf -4294967296 wenn ein vorzeichenbehafteter Ganzzahlüberlauf auftritt.

Wenn Sie „nach einer Garantie gegen Pufferunterlauf suchen, verwenden Sie die U Ganzzahl-Literal-Suffix (dh ntohl(string.st.len)+1U). In dieser Situation wird der Ausdruck entweder uint32_t oder unsigned int sein (je nachdem, welcher Typ den größten hat domain).

Wenn Sie bedenken, dass ntohl(string.st.len) möglicherweise einen Wert weniger als den Maximalwert für diesen vorzeichenlosen Typ zurückgibt (was auch immer es ist), dann len=ntohl(string.st.len)+1 führt zum Maximalwert. malloc(len+1) führt zu unsigned wrapping , sodass Sie am Ende und dann command[len]=0 schreiben gut und wahrhaftig über das Ende des Arrays hinaus. Dann haben Sie natürlich auch ein Problem mit memcpy(command,string.st.data,len); (dies ist ein Pufferüberlauf).

Puffer läuft unter und Überläufe sind nicht das einzige Risiko. Wenn Sie nicht „t verwenden, überprüfen Sie den Rückgabewert von malloc und malloc gibt NULL zurück, dann kann eine Nullzeiger-Dereferenzierung verwendet werden, um eine willkürliche Codeausführung zu bewirken. Dies impliziert, dass Sie eine Methode zur Kommunikation malloc Fehler an den Anrufer zurück. Dies bedeutet auch, dass Sie dieselbe Methode verwenden können, um dieses Wrapping-Problem zu überprüfen und an den Anrufer zurückzusenden.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.