Unerwartete Exec-Berechtigung von mmap, wenn Assembly-Dateien im Projekt enthalten sind

Lesezeit: 6 Minuten

Benutzeravatar von Ben Hirschberg
Ben Hirschberg

Ich schlage damit meinen Kopf gegen die Wand.

In meinem Projekt, wenn ich Speicher mit zuweise mmap die Zuordnung (/proc/self/maps) zeigt, dass es sich um eine lesbare und ausführbare Region handelt Trotz Ich habe nur lesbaren Speicher angefordert.

Nachdem ich mich mit strace (was gut aussah) und anderem Debugging befasst hatte, konnte ich das einzige identifizieren, das dieses seltsame Problem zu vermeiden scheint: das Entfernen von Assembly-Dateien aus dem Projekt und das Belassen von reinem C. (was?!)

Hier ist also mein seltsames Beispiel, ich arbeite an Ubunbtu 19.04 und Standard-gcc.

Wenn Sie die ausführbare Zieldatei mit der ASM-Datei (die leer ist) kompilieren, dann mmap gibt eine lesbare und ausführbare Region zurück, wenn Sie ohne bauen, verhält es sich korrekt. Siehe die Ausgabe von /proc/self/maps die ich in mein Beispiel eingebettet habe.

Beispiel.c

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

int main()
{
    void* p;
    p = mmap(NULL, 8192,PROT_READ,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);

    {
        FILE *f;
        char line[512], s_search[17];
        snprintf(s_search,16,"%lx",(long)p);
        f = fopen("/proc/self/maps","r");
        while (fgets(line,512,f))
        {
            if (strstr(line,s_search)) fputs(line,stderr);
        }

        fclose(f);
    }

    return 0;
}

Beispiel.s: Ist eine leere Datei!

Ausgänge

Mit der ASM-inklusive-Version

VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0 

Ohne die ASM enthaltene Version

VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0 

  • Das ist ernsthaft seltsam.

    – fuz

    6. Oktober 2019 um 19:23 Uhr

  • Ich habe es geschafft, dies nur mit GCC (kein CMake) zu reproduzieren, also habe ich die Frage bearbeitet, um das Beispiel minimaler zu machen.

    – Joseph Sible – Wiedereinsetzung von Monica

    6. Oktober 2019 um 19:25 Uhr

  • Möglicherweise verwandter stackoverflow.com/questions/32730643/…

    – Sami Kuhmonen

    6. Oktober 2019 um 19:38 Uhr

  • Sie könnten Recht haben, ein Teil der Antwort muss sich um die Person READ_IMPLIES_EXEC drehen

    – Ben Hirschberg

    6. Oktober 2019 um 19:47 Uhr

  • Stellen Sie Ihre Quelldateien zusammen mit -Wa,--noexecstack.

    – jww

    7. Oktober 2019 um 4:59 Uhr

Joseph Sible – Stellen Sie Monicas Benutzeravatar wieder her
Joseph Sible – Wiedereinsetzung von Monica

Linux hat eine Ausführungsbereich genannt READ_IMPLIES_EXECwas dazu führt, dass alle Seiten mit zugewiesen werden PROT_READ auch zu geben PROT_EXEC. Ältere Linux-Kernel verwendeten dies für ausführbare Dateien, die das Äquivalent von verwendeten gcc -z execstack. Dieses Programm zeigt Ihnen, ob das für sich selbst aktiviert ist:

#include <stdio.h>
#include <sys/personality.h>

int main(void) {
    printf("Read-implies-exec is %s\n", personality(0xffffffff) & READ_IMPLIES_EXEC ? "true" : "false");
    return 0;
}

Wenn Sie das zusammen mit einer leeren kompilieren .s -Datei sehen Sie, dass sie aktiviert ist, aber ohne eine wird sie deaktiviert. Der Anfangswert dieser stammt aus den ELF-Metainformationen in Ihrer Binärdatei. Tun readelf -Wl example. Sie sehen diese Zeile, wenn Sie ohne Leerzeichen kompiliert haben .s Datei:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10

Aber dieses, wenn Sie damit kompiliert haben:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

Notiz RWE statt nur RW. Der Grund dafür ist, dass der Linker davon ausgeht, dass Ihre Assemblydateien read-implies-exec erfordern, es sei denn, es wird ausdrücklich darauf hingewiesen, dass dies nicht der Fall ist, und wenn ein Teil Ihres Programms read-implies-exec erfordert, ist es für Ihr gesamtes Programm aktiviert . Die Assembly-Dateien, die GCC kompiliert, teilen ihm mit dieser Zeile mit, dass dies nicht erforderlich ist (Sie werden dies sehen, wenn Sie mit kompilieren -S):

    .section        .note.GNU-stack,"",@progbits

Die standardmäßigen Abschnittsberechtigungen beinhalten nicht exec. Siehe den ELF-Teil der .section Dokumentation für die Bedeutung der “Flags” und @attributes.

(Und vergessen Sie nicht, zu einem anderen Abschnitt zu wechseln, z .text oder .data danach .section Richtlinie, wenn Ihre .s war darauf angewiesen .text weil der Standardabschnitt oben in der Datei.)

Setzen Sie diese Zeile ein example.s (und alle anderen .s Datei in Ihrem Projekt). Das Vorhandensein davon .note.GNU-stack Abschnitt dient dazu, dem Linker mitzuteilen, dass diese Objektdatei nicht von einem ausführbaren Stack abhängt, also wird der Linker verwenden RW Anstatt von RWE auf der GNU_STACK Metadaten, und Ihr Programm wird dann wie erwartet funktionieren.

Ähnlich für NASM, a section Direktive mit den richtigen Flags gibt nicht ausführbare Stacks an.


Moderne Linux-Kernel zwischen 5.4 und 5.8 haben das Verhalten geändert des ELF-Programmladers. Bei x86-64 schaltet sich nichts ein READ_IMPLIES_EXEC mehr. Höchstens (mit einem RWE GNU_STACK hinzugefügt von ld), erhalten Sie den Stapel selbst ausführbar, nicht jede lesbare Seite. (Diese Antwort deckt die letzte Änderung in 5.8 ab, aber es muss vorher andere Änderungen gegeben haben, da diese Frage die erfolgreiche Ausführung des Codes in zeigt .data auf x86-64 Linux 5.4)

exec-all (READ_IMPLIES_EXEC) tritt nur bei älteren ausführbaren 32-Bit-Dateien auf, bei denen der Linker keine hinzugefügt hat GNU_STACK Header-Eintrag überhaupt. Aber wie hier gezeigt, modern ld fügt das immer mit der einen oder anderen Einstellung hinzu, auch bei einer Eingabe .o Datei fehlt eine Notiz.

Diese sollten Sie weiterhin nutzen .note -Abschnitt, um nicht ausführbare Stacks in normalen Programmen zu signalisieren. Aber wenn Sie gehofft haben, selbstmodifizierenden Code in zu testen .data oder nach einem alten Tutorial zum Testen von Shellcode, das ist keine Option für moderne Kernel.

  • Heilige Scheiße, das ist eine seltsame Standardeinstellung. Ich denke, die Toolchain existierte vor noexec, und noexec zum Standard zu machen, hätte einiges kaputt machen können. Jetzt bin ich gespannt, wie andere Assembler wie NASM/YASM das schaffen .o Dateien! Aber wie auch immer, ich denke, das ist der Mechanismus, der gcc -zexecstack verwendet und warum es nicht nur den Stack, sondern alles ausführbar macht.

    – Peter Cordes

    6. Oktober 2019 um 20:03 Uhr

  • @Peter – Deshalb werden Projekte wie Botan, Crypto++ und OpenSSL, die den Assembler verwenden, hinzugefügt -Wa,--noexecstack. Ich denke, es ist eine sehr böse scharfe Kante. Stiller Verlust von NX-Stacks sollte eine Sicherheitslücke sein. Die Binutil-Leute sollten es beheben.

    – jww

    7. Oktober 2019 um 4:58 Uhr


  • @jww Es ist in der Tat ein Sicherheitsproblem, seltsam, dass es noch nie jemand gemeldet hat

    – Ben Hirschberg

    7. Oktober 2019 um 5:05 Uhr


  • +1, aber diese Antwort wäre viel besser, wenn die Bedeutung/Logik der Zeile .note.GNU-stack,"",@progbits wurde erklärt – im Moment ist es undurchsichtig, äquivalent zu “diese magische Zeichenfolge verursacht diesen Effekt”, aber die Zeichenfolge sieht eindeutig so aus, als hätte sie eine Art Semantik.

    – mtraceur

    8. Oktober 2019 um 20:44 Uhr

  • Dies scheint durch eine kürzlich erfolgte Änderung des Linux-Kernels behoben worden zu sein: Der Standard ohne .note.GNU-Stack-Abschnitt ist exec-none, statt exec-all Linux-Standardverhalten gegenüber dem `.data`-Abschnitt und sogar gcc -zexecstack (RWX) ist nur wirklich Exec-Stack, nicht Exec-All.

    – Peter Cordes

    17. November 2020 um 15:56 Uhr


R.. GitHub STOP HELPING ICEs Benutzeravatar
R.. GitHub HÖREN SIE AUF, ICE ZU HELFEN

Als Alternative zum Ändern Ihrer Assemblydateien mit GNU-spezifischen Abschnittsdirektivenvarianten können Sie hinzufügen -Wa,--noexecstack zu Ihrer Befehlszeile zum Erstellen von Assemblydateien. Sehen Sie sich zum Beispiel an, wie ich es in musl’s mache configure:

https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a

Ich glaube, dass zumindest einige Versionen von Clang mit integriertem Assembler erfordern, dass es als übergeben wird --noexecstack (ohne das -Wa), also sollte Ihr Konfigurationsskript wahrscheinlich beide überprüfen und sehen, welches akzeptiert wird.

Sie können auch verwenden -Wl,-z,noexecstack zur Verbindungszeit (in LDFLAGS) um das gleiche Ergebnis zu erhalten. Der Nachteil davon ist, dass es nicht hilft, wenn Ihr Projekt statische (.a)-Bibliotheksdateien für die Verwendung durch andere Software, da Sie dann die Verbindungszeitoptionen nicht steuern, wenn sie von anderen Programmen verwendet werden.

  • Hmm … Ich wusste nicht, dass Sie Rich Felker sind, bevor Sie diesen Beitrag gelesen haben. Warum ist Ihr Anzeigename nicht Dalias?

    – SS Anne

    8. Oktober 2019 um 20:35 Uhr

1421200cookie-checkUnerwartete Exec-Berechtigung von mmap, wenn Assembly-Dateien im Projekt enthalten sind

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy