Das Kompilieren des Quellcodes erzeugt eine Binärdatei. Während der Kompilierung können Sie dem Compiler Flags bereitstellen, um bestimmte Eigenschaften der Binärdatei zu aktivieren oder zu deaktivieren. Einige dieser Eigenschaften sind sicherheitsrelevant.
Checksec ist ein raffiniertes kleines Tool (und Shell-Skript), das neben anderen Funktionen die Sicherheitseigenschaften identifiziert, die in eine Binärdatei eingebaut wurden, als sie kompiliert wurde. Ein Compiler aktiviert möglicherweise einige dieser Eigenschaften standardmäßig, und Sie müssen möglicherweise bestimmte Flags bereitstellen, um andere zu aktivieren.
Dieser Artikel erklärt, wie man checksec verwendet, um die Sicherheitseigenschaften einer Binärdatei zu identifizieren, einschließlich:
- Die zugrunde liegenden Befehle, die checksec verwendet, um Informationen zu den Sicherheitseigenschaften zu finden
- So aktivieren Sie Sicherheitseigenschaften mit der GNU Compiler Collection (GCC) beim Kompilieren einer Beispielbinärdatei
Checksec installieren
Um checksec auf Fedora und anderen RPM-basierten Systemen zu installieren, verwenden Sie:
$ sudo dnf install checksec
Verwenden Sie für Debian-basierte Distributionen das entsprechende apt
Befehl.
Das Shell-Skript
Checksec ist ein Single-File-Shell-Skript, wenn auch ein ziemlich großes. Ein Vorteil ist, dass Sie das Skript schnell durchlesen und alle Systembefehle verstehen können, die ausgeführt werden, um Informationen über Binärdateien oder ausführbare Dateien zu finden:
$-Datei /usr/bin/checksec
/usr/bin/checksec:Bourne-Again-Shell-Skript, ausführbarer ASCII-Text, mit sehr langen Zeilen
$ wc -l /usr /bin/checksec
2111 /usr/bin/checksec
Nehmen Sie checksec für ein Laufwerk mit einer Binärdatei, die Sie wahrscheinlich täglich ausführen:das allgegenwärtige ls
Befehl. Das Format des Befehls ist checksec --file=
gefolgt vom absoluten Pfad des ls
binär:
$ checksec --file =/ usr / bin / ls
Relro-Stapel Kanarische NX-Pie REPPPLATE RUNPATH-Symbole Festung befestigungsfähige felifierbare Datei
Full Relro Canary gefunden NX-fähiger Pie Aktiviert Nein RPPath Kein Laufpfad Keine Symbole Ja 5 17 /usr/bin/ls
Wenn Sie dies in einem Terminal ausführen, sehen Sie eine Farbcodierung, die zeigt, was gut ist und was wahrscheinlich nicht. Ich sage "wahrscheinlich", denn selbst wenn etwas rot ist, bedeutet das nicht unbedingt, dass die Dinge schrecklich sind - es könnte nur bedeuten, dass die Distributionsanbieter beim Kompilieren der Binärdateien Kompromisse eingegangen sind.
Die erste Zeile stellt verschiedene Sicherheitseigenschaften bereit, die normalerweise für Binärdateien verfügbar sind, wie RELRO
, STACK CANARY
, NX
, und so weiter (ich erkläre es weiter unten im Detail). Die zweite Zeile zeigt den Status dieser Eigenschaften für die angegebene Binärdatei (ls
, in diesem Fall). Beispiel:NX enabled
bedeutet, dass einige Eigenschaften für diese Binärdatei aktiviert sind.
Eine Beispiel-Binärdatei
Für dieses Tutorial verwende ich das folgende "Hello World"-Programm als Beispielbinärdatei.
#include
int main()
{
printf("Hello World\n");
return 0;
}
Beachten Sie, dass ich gcc
nicht angegeben habe mit allen zusätzlichen Flags während der Kompilierung:
$ gcc hello.c -o hallo
$ file hallo
hello:ELF 64-bit LSB ausführbar, x86-64, Version 1 (SYSV), dynamisch verknüpft, Interpreter / lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, für GNU/Linux 3.2.0, nicht entfernt
$ ./hello
Hello World
Führen Sie die Binärdatei durch checksec. Einige der Eigenschaften sind anders als bei ls
Befehl oben (auf Ihrem Bildschirm werden diese möglicherweise rot angezeigt):
$ CheckSec --file =. / Hallo
Relro-Stapelkanarare NX Pie RPPath-Laufwerk-Symbole anständig befestigungsfähige felifierbare Datei
Partial Relro No Canary gefunden 0./Hallo
$
Ändern des Ausgabeformats
Checksec erlaubt verschiedene Ausgabeformate, die Sie mit --output
festlegen können . Ich wähle das JSON-Format und leite die Ausgabe an jq
weiter Dienstprogramm für hübsches Drucken.
Um mitzumachen, stellen Sie sicher, dass Sie jq
haben installiert, da dieses Tutorial dieses Ausgabeformat verwendet, um schnell nach bestimmten Eigenschaften aus der Ausgabe zu suchen und yes
zu melden oder no
jeweils:
$ checksec --file=./hello --output=json | jq
{
"./hello":{
"relro":"partial",
"canary":"no",
"nx":" ja",
"pie":"nein",
"rpath":"nein",
"runpath":"nein",
"symbols":"ja" ,
"fortify_source":"no",
"fortified":"0",
"fortify-able":"0"
}
}Durch die Sicherheitseigenschaften gehen
Mehr zur Sicherheit
- Der defensive Programmierleitfaden
- Webinar:Automatisierung der Systemsicherheit und Compliance mit einem Standardbetriebssystem
- 10 Ebenen der Linux-Containersicherheit
- SELinux-Malbuch
- Weitere Sicherheitsartikel
Die obige Binärdatei enthält mehrere Sicherheitseigenschaften. Ich werde diese Binärdatei mit ls
vergleichen binär oben, um zu untersuchen, was aktiviert ist, und zu erklären, wie checksec diese Informationen gefunden hat.
1. Symbole
Ich fange erstmal mit dem einfachen an. Während der Kompilierung werden bestimmte Symbole in die Binärdatei aufgenommen, hauptsächlich zum Debuggen. Diese Symbole sind erforderlich, wenn Sie Software entwickeln, und erfordern mehrere Zyklen zum Debuggen und Beheben von Problemen.
Diese Symbole werden normalerweise aus der endgültigen Binärdatei entfernt (entfernt), bevor sie zur allgemeinen Verwendung freigegeben wird. Dies wirkt sich in keiner Weise auf die Ausführung der Binärdatei aus; es läuft genauso wie mit den Symbolen. Das Strippen wird oft durchgeführt, um Platz zu sparen, da die Binärdatei nach dem Strippen der Symbole etwas leichter ist. In Closed-Source- oder proprietärer Software werden Symbole häufig entfernt, da diese Symbole in einer Binärdatei es etwas einfacher machen, auf das Innenleben der Software zu schließen.
Laut checksec sind Symbole in dieser Binärdatei vorhanden, aber sie waren nicht in ls
binär. Sie können diese Informationen auch finden, indem Sie die file
ausführen Befehl im Programm – Sie sehen not stripped
in der Ausgabe gegen Ende:
$ checksec --file=/bin/ls --output=json | jq | grep-Symbole
"symbols":"no",
$ checksec --file=./hello --output=json | jq | grep symbole
"symbols":"yes",
$ file hallo
hallo:ELF 64-bit LSB ausführbar, x86-64, Version 1 (SYSV), dynamisch verknüpft , Interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, für GNU/Linux 3.2.0, nicht entfernt
Wie hat checksec diese Informationen gefunden? Nun, es bietet ein praktisches --debug
Option, um anzuzeigen, welche Funktionen ausgeführt wurden. Daher sollte Ihnen das Ausführen des folgenden Befehls zeigen, welche Funktionen innerhalb des Shell-Skripts ausgeführt wurden:
$ checksec --debug --file=./hello
In diesem Lernprogramm suche ich nach den zugrunde liegenden Befehlen, mit denen diese Informationen gefunden werden. Da es sich um ein Shell-Skript handelt, können Sie immer Bash-Funktionen nutzen. Dieser Befehl gibt jeden Befehl aus, der innerhalb des Shell-Skripts ausgeführt wurde:
$ bash -x /usr/bin/checksec --file=./hello
Wenn Sie durch die Ausgabe scrollen, sollten Sie eine echo_message
sehen gefolgt von der Kategorie der Sicherheitseigenschaft. Hier ist, was checksec darüber berichtet, ob die Binärdatei Symbole enthält:
+ readelf -W --symbols ./hello
+ grep -q '\.symtab'
+ echo_message '\033[31m96) Symbols\t\033[m ' Symbole, ' Symbole ="ja"' '"symbole":"ja",'
Um dies zu vereinfachen, verwendet checksec das readelf
Dienstprogramm zum Lesen der Binärdatei und bietet einen speziellen --symbols
Flag, das alle Symbole innerhalb der Binärdatei auflistet. Dann sucht es nach einem speziellen Wert, .symtab
, das die Anzahl der gefundenen Einträge (Symbole) angibt. Sie können die folgenden Befehle an der oben kompilierten Test-Binärdatei ausprobieren:
$ readelf -W --symbols ./hello
$ readelf -W --symbols ./hello | grep -i symtab
So entfernen Sie Symbole
Sie können Symbole nach der Kompilierung oder während der Kompilierung entfernen.
- Beitragskompilierung: Nach der Kompilierung können Sie den
strip
verwenden Dienstprogramm auf der Binärdatei, um die Symbole zu entfernen. Bestätigen Sie, dass es funktioniert hat, indem Sie diefile
verwenden Befehl, der die Ausgabe jetzt alsstripped
anzeigt :$ gcc hello.c -o hallo
$
$ file hallo
hallo:ELF 64-bit LSB ausführbar, x86-64, Version 1 (SYSV) , dynamisch verknüpft, Interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, für GNU/Linux 3.2.0, nicht entfernt
$
$ strip hallo
$
$ file hallo
hallo:ELF 64-bit LSB ausführbar, x86-64, Version 1 (SYSV), dynamisch gelinkt, Interpreter /lib64/ld-linux-x86-64.so .2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, für GNU/Linux 3.2.0, entfernt
$
So entfernen Sie Symbole während der Kompilierung
Anstatt Symbole nach der Kompilierung manuell zu entfernen, können Sie den Compiler bitten, dies für Sie zu tun, indem Sie das -s
angeben Argument:
$ gcc -s hallo.c -o hallo
$
$ file hallo
hallo:ELF 64-bit LSB ausführbar, x86-64, Version 1 (SYSV), dynamisch gelinkt , Interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=247de82a8ad84e7d8f20751ce79ea9e0cf4bd263, für GNU/Linux 3.2.0, entfernt
$
Nach dem erneuten Ausführen von checksec können Sie diese symbols
sehen werden als no
angezeigt :
$ checksec --file=./hello --output=json | jq | grep-Symbole
"symbols":"no",
$
2. Kanarienvogel
Kanarienvögel sind bekannte Werte, die zwischen einem Puffer und Steuerdaten auf dem Stack platziert werden um Pufferüberläufe zu überwachen. Wenn eine Anwendung ausgeführt wird, werden ihr zwei Arten von Speicher zugewiesen. Einer davon ist ein Stapel , die einfach eine Datenstruktur mit zwei Operationen ist:push
, das Daten auf den Stack legt, und pop
, wodurch Daten in umgekehrter Reihenfolge aus dem Stack entfernt werden. Schädliche Eingaben könnten den Stapel mit speziell präparierten Eingaben überlaufen oder beschädigen und das Programm zum Absturz bringen:
$ checksec --file=/bin/ls --output=json | jq | grep canary
"canary":"yes",
$
$ checksec --file=./hello --output=json | jq | grep canary
"canary":"no",
$
Wie findet checksec heraus, ob die Binärdatei mit einem Canary aktiviert ist? Mit der obigen Methode können Sie es eingrenzen, indem Sie den folgenden Befehl im Shell-Skript ausführen:
$ readelf -W -s ./hello | grep -E '__stack_chk_fail|__intel_security_cookie'
Canary aktivieren
Zum Schutz vor diesen Fällen stellt der Compiler den -stack-protector-all
bereit Flag, das der Binärdatei zusätzlichen Code hinzufügt, um nach solchen Pufferüberläufen zu suchen:
$ gcc -fstack-protector-all hello.c -o hallo
$ checksec --file=./hello --output=json | jq | grep canary
"canary":"yes",
Checksec zeigt, dass die Eigenschaft jetzt aktiviert ist. Sie können dies auch überprüfen mit:
$ readelf -W -s ./hello | Grep-t '__stack_chk_fail | __intel_security_cookie'
2:000000000000000000 0 Func global Standard und __stack_chk_fail@glibc_2.4 (3)
83:000000000000000000 0 func global default und __stack_chk_fail @@ glibc_2.4
$
3. TORTE
PIE steht für positionsunabhängige ausführbare Datei. Wie der Name schon sagt, handelt es sich um Code, der unabhängig von seiner absoluten Adresse irgendwo im Speicher zur Ausführung abgelegt wird:
$ checksec --file=/bin/ls --output=json | jq | grep pie
"pie":"ja",
$ checksec --file=./hello --output=json | jq | grep pie
"pie":"nein",
Häufig wird PIE nur für Bibliotheken und nicht für eigenständige Befehlszeilenprogramme aktiviert. In der Ausgabe unten hello
wird als LSB executable
angezeigt , wohingegen die libc
Standardbibliothek (.so
)-Datei ist als LSB shared object
gekennzeichnet :
$ Datei Hallo
Hallo:ELF 64-Bit LSB ausführbar, x86-64, Version 1 (SYSV), dynamisch verlinkt, Interpreter /lib64/ld-linux-x86-64.so.2, BuildID[ sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, für GNU/Linux 3.2.0, nicht entfernt
$ file /lib64/libc-2.32.so
/lib64/libc-2.32.so:ELF 64-bit Gemeinsam genutztes LSB-Objekt, x86-64, Version 1 (GNU/Linux), dynamisch verknüpft, Interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4a7fb374097fb927fb93d35ef98ba89262d0c4a4, für GNU/Linux 3.2.0, nicht gestrippt
Checksec versucht diese Informationen zu finden mit:
$ readelf -W -h ./hallo | grep EXEC
Typ: EXEC (ausführbare Datei)
Wenn Sie denselben Befehl in einer gemeinsam genutzten Bibliothek anstelle von EXEC
versuchen , sehen Sie ein DYN
:
$ readelf -W -h /lib64/libc-2.32.so | grep DYN
Typ: DYN (Shared Object File)
PIE aktivieren
Um PIE in einem Testprogramm zu aktivieren, senden Sie die folgenden Argumente an den Compiler:
$ gcc -pie -fpie hello.c -o hello
Sie können mit checksec:
überprüfen, ob PIE aktiviert ist$ checksec --file=./hello --output=json | jq | grep pie
"pie":"ja",
$
Es sollte als ausführbare PIE-Datei angezeigt werden, deren Typ sich von EXEC
geändert hat zu DYN
:
$ file hallo
hallo:ELF 64-Bit LSB Pie ausführbar, x86-64, Version 1 (SYSV), dynamisch verknüpft, Interpreter /lib64/ld-linux-x86-64.so.2, BuildID [sha1]=bb039adf2530d97e02f534a94f0f668cd540f940, für GNU/Linux 3.2.0, nicht entfernt
$ readelf -W -h ./hello | grep DYN
Typ: DYN (Shared Object File)
4. NX
NX steht für „nicht ausführbar“. Es wird oft auf CPU-Ebene aktiviert, sodass ein Betriebssystem mit aktiviertem NX bestimmte Speicherbereiche als nicht ausführbar markieren kann. Pufferüberlauf-Exploits legen häufig Code auf dem Stack ab und versuchen dann, ihn auszuführen. Wenn Sie diesen beschreibbaren Bereich jedoch nicht ausführbar machen, können Sie solche Angriffe verhindern. Diese Eigenschaft ist standardmäßig während der regulären Kompilierung mit gcc
aktiviert :
$ checksec --file=/bin/ls --output=json | jq | grep nx
"nx":"ja",
$ checksec --file=./hello --output=json | jq | grep nx
"nx":"ja",
Checksec ermittelt diese Informationen mit dem folgenden Befehl. RW
gegen Ende bedeutet, dass der Stack lesbar und beschreibbar ist; da es kein E
gibt , es ist nicht ausführbar:
$ readelf -W -l ./Hallo | grep GNU_STACK
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
NX für Demozwecke deaktivieren
Es wird nicht empfohlen, aber Sie können NX
deaktivieren beim Kompilieren eines Programms mit dem -z execstack
Argument:
$ gcc -z execstack hallo.c -o hallo
$ checksec --file=./hello --output=json | jq | grep nx
"nx":"nein",
Nach dem Kompilieren wird der Stack ausführbar (RWE
), wodurch schädlicher Code ausgeführt werden kann:
$ readelf -W -l ./Hallo | grep GNU_STACK
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
5. RELRO
RELRO steht für Relocation Read-Only. Eine ELF-Binärdatei (Executable Linkable Format) verwendet eine Global-Offset-Tabelle (GOT), um Funktionen dynamisch aufzulösen. Wenn diese Sicherheitseigenschaft aktiviert ist, macht sie das GOT innerhalb der Binärdatei schreibgeschützt, was irgendeine Form von Verschiebungsangriffen verhindert:
$ checksec --file=/bin/ls --output=json | jq | grep relro
"relro":"full",
$ checksec --file=./hello --output=json | jq | grep relro
"relro":"teilweise",
Checksec findet diese Informationen mit dem folgenden Befehl. Hier wird eine der RELRO-Eigenschaften aktiviert; Daher zeigt die Binärdatei "teilweise" an, wenn sie über checksec verifiziert wird:
$ readelf -W -l ./Hallo | grep GNU_RELRO
GNU_RELRO 0x002e10 0x0000000000403e10 0x0000000000403e10 0x0001f0 0x0001f0 R 0x1
$ readelf -W -d ./hello | grep BIND_NOW
Vollständiges RELRO aktivieren
Um das vollständige RELRO zu aktivieren, verwenden Sie beim Kompilieren mit gcc
die folgenden Befehlszeilenargumente :
$ gcc -Wl,-z,relro,-z,jetzt hallo.c -o hallo
$ checksec --file=./hello --output=json | jq | grep relro
"relro":"voll",
Jetzt ist auch die zweite Eigenschaft aktiviert, wodurch das Programm vollständig RELRO wird:
$ readelf -W -l ./Hallo | grep GNU_RELRO
GNU_RELRO 0x002dd0 0x0000000000403dd0 0x0000000000403dd0 0x000230 0x000230 R 0x1
$ readelf -W -d ./hello | grep BIND_NOW
0x0000000000000018 (BIND_NOW)
6. Stärken
Fortify ist eine weitere Sicherheitseigenschaft, die jedoch in diesem Artikel nicht behandelt wird. Ich werde lernen, wie checksec Fortify in Binärdateien überprüft und wie es mit gcc
aktiviert wird als Übung für Sie.
$ checksec --file=/bin/ls --output=json | jq | grep -i forti
"fortify_source":"yes",
"fortified":"5",
"fortify-able":"17"
$ checksec --file=./hello --output=json | jq | grep -i forti
"fortify_source":"no",
"fortified":"0",
"fortify-able":"0"
Weitere Checksec-Funktionen
Das Thema Sicherheit ist endlos, und obwohl es hier nicht möglich ist, alles abzudecken, möchte ich doch noch ein paar weitere Funktionen von checksec
erwähnen Befehle, mit denen es ein Vergnügen ist, damit zu arbeiten.
Mit mehreren Binärdateien ausführen
Sie müssen Checksec nicht jede Binärdatei einzeln zur Verfügung stellen. Stattdessen können Sie einen Verzeichnispfad angeben, in dem sich mehrere Binärdateien befinden, und checksec überprüft sie alle auf einmal:
$ checksec --dir=/usr/bin
Prozesse
Neben Binärdateien arbeitet checksec auch an Programmen während der Ausführung. Der folgende Befehl findet die Sicherheitseigenschaften aller laufenden Programme auf Ihrem System. Sie können --proc-all
verwenden wenn Sie möchten, dass alle laufenden Prozesse überprüft werden, oder Sie können einen bestimmten Prozess anhand seines Namens auswählen:
$ checksec --proc-all
$ checksec --proc=bash
Kernel-Eigenschaften
Zusätzlich zu den in diesem Artikel beschriebenen Userland-Anwendungen von checksec können Sie damit auch die in Ihrem System integrierten Kernel-Eigenschaften überprüfen:
$ checksec --kernel
Probieren Sie es aus
Checksec ist ein guter Weg, um zu verstehen, welche Userland- und Kernel-Eigenschaften aktiviert sind. Gehen Sie jede Sicherheitseigenschaft im Detail durch und versuchen Sie, die Gründe für die Aktivierung jeder Funktion und die Arten von Angriffen zu verstehen, die sie verhindert.