GNU/Linux >> Linux Esercitazione >  >> Linux

Identifica le proprietà di sicurezza su Linux usando checksec

La compilazione del codice sorgente produce un binario. Durante la compilazione, puoi fornire dei flag al compilatore per abilitare o disabilitare determinate proprietà sul file binario. Alcune di queste proprietà sono rilevanti per la sicurezza.

Checksec è un piccolo e ingegnoso strumento (e script di shell) che, tra le altre funzioni, identifica le proprietà di sicurezza che sono state integrate in un binario quando è stato compilato. Un compilatore potrebbe abilitare alcune di queste proprietà per impostazione predefinita e potresti dover fornire flag specifici per abilitarne altre.

Questo articolo spiega come utilizzare checksec per identificare le proprietà di sicurezza su un file binario, tra cui:

  1. I comandi sottostanti che checksec usa per trovare informazioni sulle proprietà di sicurezza
  2. Come abilitare le proprietà di sicurezza utilizzando GNU Compiler Collection (GCC) durante la compilazione di un file binario di esempio

Installa checksec

Per installare checksec su Fedora e altri sistemi basati su RPM, usa:

$ sudo dnf install checksec

Per le distribuzioni basate su Debian, utilizzare l'equivalente apt comando.

Lo script della shell

Checksec è uno script di shell a file singolo, anche se piuttosto grande. Un vantaggio è che puoi leggere rapidamente lo script e comprendere tutti i comandi di sistema in esecuzione per trovare informazioni su binari o eseguibili:

$ file /usr/bin/checksec
/usr/bin/checksec: Bourne-Again shell script, ASCII text executable, with very long lines

$ wc -l /usr/bin/checksec
2111 /usr/bin/checksec

Prendi checksec per un disco con un binario che probabilmente esegui ogni giorno:l'onnipresente ls comando. Il formato del comando è checksec --file= seguito dal percorso assoluto di ls binario:

$ checksec --file=/usr/bin/ls
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   No Symbols        Yes   5       17              /usr/bin/ls

Quando lo esegui in un terminale, vedi la codifica a colori che mostra cosa è buono e cosa probabilmente non lo è. Dico "probabilmente" perché anche se qualcosa è in rosso, non significa necessariamente che le cose siano orribili, potrebbe semplicemente significare che i fornitori di distribuzioni hanno fatto dei compromessi durante la compilazione dei binari.

La prima riga fornisce varie proprietà di sicurezza che di solito sono disponibili per i binari, come RELRO , STACK CANARY , NX , e così via (spiegherò in dettaglio di seguito). La seconda riga mostra lo stato di queste proprietà per il file binario specificato (ls , in questo caso). Ad esempio, NX enabled significa che alcune proprietà sono abilitate per questo binario.

Un binario di esempio

Per questo tutorial, userò il seguente programma "hello world" come binario di esempio.

#include <stdio.h>

int main()
{
        printf("Hello World\n");
        return 0;
}
 

Nota che non ho fornito gcc con eventuali flag aggiuntivi durante la compilazione:

$ gcc hello.c -o hello
 
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, not stripped

$ ./hello
Hello World

Esegui il binario tramite checksec. Alcune delle proprietà sono diverse rispetto a ls comando sopra (sul tuo schermo, questi potrebbero essere visualizzati in rosso):

$ checksec --file=./hello
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   85) Symbols       No    0       0./hello
$

Modifica del formato di output

Checksec consente vari formati di output, che puoi specificare con --output . Sceglierò il formato JSON e reindirizzare l'output a jq utility per una bella stampa.

Per seguire, assicurati di avere jq installato perché questo tutorial usa questo formato di output per cercare rapidamente proprietà specifiche dall'output e segnalare yes o no su ciascuno:

$ checksec --file=./hello --output=json | jq
{
  "./hello": {
    "relro": "partial",
    "canary": "no",
    "nx": "yes",
    "pie": "no",
    "rpath": "no",
    "runpath": "no",
    "symbols": "yes",
    "fortify_source": "no",
    "fortified": "0",
    "fortify-able": "0"
  }
}

Esame delle proprietà di sicurezza

Maggiori informazioni sulla sicurezza

  • La guida alla codifica difensiva
  • Webinar:automazione della sicurezza del sistema e della conformità con un sistema operativo standard
  • 10 livelli di sicurezza dei container Linux
  • Libro da colorare SELinux
  • Altri articoli sulla sicurezza

Il file binario sopra include diverse proprietà di sicurezza. Confronterò quel binario con ls binario sopra per esaminare cosa è abilitato e spiegare come checksec ha trovato queste informazioni.

1. Simboli

Inizierò prima con quello facile. Durante la compilazione, alcuni simboli sono inclusi nel binario, principalmente per il debug. Questi simboli sono richiesti durante lo sviluppo di software e richiedono più cicli per il debug e la correzione delle cose.

Questi simboli vengono solitamente rimossi (rimossi) dal binario finale prima che venga rilasciato per uso generale. Ciò non influisce in alcun modo sull'esecuzione del binario; funzionerà proprio come farebbe con i simboli. Lo stripping viene spesso eseguito per risparmiare spazio, poiché il binario è un po' più leggero una volta che i simboli sono stati rimossi. Nel software closed-source o proprietario, i simboli vengono spesso rimossi perché avere questi simboli in un binario rende in qualche modo facile dedurre il funzionamento interno del software.

Secondo checksec, i simboli sono presenti in questo binario, ma non erano in ls binario. Puoi anche trovare queste informazioni eseguendo il file comando sul programma:vedi not stripped nell'output verso la fine:

$ checksec --file=/bin/ls --output=json | jq | grep symbols
    "symbols": "no",

$ checksec --file=./hello --output=json | jq | grep symbols
    "symbols": "yes",

$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, not stripped

Come ha fatto checksec a trovare queste informazioni? Bene, fornisce un pratico --debug opzione per mostrare quali funzioni sono state eseguite. Pertanto, l'esecuzione del seguente comando dovrebbe mostrarti quali funzioni sono state eseguite all'interno dello script della shell:

$ checksec --debug --file=./hello

In questo tutorial, sto cercando i comandi sottostanti utilizzati per trovare queste informazioni. Poiché è uno script di shell, puoi sempre utilizzare le funzionalità di Bash. Questo comando produrrà tutti i comandi eseguiti dall'interno dello script della shell:

$ bash -x /usr/bin/checksec --file=./hello

Se scorri l'output, dovresti vedere un echo_message seguito dalla categoria della proprietà di sicurezza. Ecco cosa riporta checksec sul fatto che il file binario contenga simboli:

+ readelf -W --symbols ./hello
+ grep -q '\.symtab'
+ echo_message '\033[31m96) Symbols\t\033[m  ' Symbols, ' symbols="yes"' '"symbols":"yes",'

Per semplificare, checksec utilizza il readelf utility per leggere il binario e fornisce uno speciale --symbols flag che elenca tutti i simboli all'interno del binario. Quindi cerca un valore speciale, .symtab , che fornisce un conteggio delle voci (simboli) che trova. Puoi provare i seguenti comandi sul binario di prova che hai compilato sopra:

$ readelf -W --symbols ./hello
$ readelf -W --symbols ./hello | grep -i symtab

Come rimuovere i simboli

Puoi rimuovere i simboli dopo la compilazione o durante la compilazione.

  • Post compilazione: Dopo la compilazione, puoi utilizzare la strip utility sul binario per rimuovere i simboli. Conferma che ha funzionato usando il file comando, che ora mostra l'output come stripped :
    $ gcc hello.c -o hello
    $
    $ file hello
    hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, for GNU/Linux 3.2.0, not stripped
    $
    $ strip hello
    $
    $ file hello
    hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, for GNU/Linux 3.2.0, stripped
    $

Come rimuovere i simboli durante la compilazione

Invece di rimuovere manualmente i simboli dopo la compilazione, puoi chiedere al compilatore di farlo per te fornendo il -s argomento:

$ gcc -s hello.c -o hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=247de82a8ad84e7d8f20751ce79ea9e0cf4bd263, for GNU/Linux 3.2.0, stripped
$

Dopo aver eseguito nuovamente checksec, puoi vedere quei symbols sono mostrati come no :

$ checksec --file=./hello --output=json | jq | grep symbols
    "symbols": "no",
$

2. Canarie

I canarini sono valori noti che vengono inseriti tra un buffer e i dati di controllo sullo stack per monitorare gli overflow del buffer. Quando un'applicazione viene eseguita, le vengono assegnati due tipi di memoria. Uno di questi è uno stack , che è semplicemente una struttura dati con due operazioni:push , che mette i dati nello stack e pop , che rimuove i dati dallo stack in ordine inverso. L'input dannoso potrebbe sovraccaricare o danneggiare lo stack con input appositamente predisposti e causare l'arresto anomalo del programma:

$ checksec --file=/bin/ls --output=json | jq | grep canary
    "canary": "yes",
$
$ checksec --file=./hello --output=json | jq | grep canary
    "canary": "no",
$

In che modo checksec scopre se il binario è abilitato con un canary? Usando il metodo sopra, puoi restringere il campo eseguendo il seguente comando all'interno dello script della shell:

$ readelf -W -s ./hello | grep -E '__stack_chk_fail|__intel_security_cookie'

Abilita canary

Per proteggersi da questi casi, il compilatore fornisce il -stack-protector-all flag, che aggiunge codice extra al file binario per verificare la presenza di tali overflow del buffer:

$ gcc -fstack-protector-all hello.c -o hello

$ checksec --file=./hello --output=json | jq | grep canary
    "canary": "yes",

Checksec mostra che la proprietà è ora abilitata. Puoi anche verificarlo con:

$ readelf -W -s ./hello | grep -E '__stack_chk_fail|__intel_security_cookie'
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@GLIBC_2.4 (3)
    83: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@@GLIBC_2.4
$

3. Torta

PIE sta per eseguibile indipendente dalla posizione. Come suggerisce il nome, è il codice che viene messo da qualche parte in memoria per l'esecuzione indipendentemente dal suo indirizzo assoluto:

$ checksec --file=/bin/ls --output=json | jq | grep pie
    "pie": "yes",

$ checksec --file=./hello --output=json | jq | grep pie
    "pie": "no",

Spesso, PIE è abilitato solo per le librerie e non per i programmi a riga di comando autonomi. Nell'output di seguito, hello viene mostrato come LSB executable , mentre la libc libreria standard (.so ) il file è contrassegnato come LSB shared object :

$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, not stripped

$ file /lib64/libc-2.32.so
/lib64/libc-2.32.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4a7fb374097fb927fb93d35ef98ba89262d0c4a4, for GNU/Linux 3.2.0, not stripped

Checksec cerca di trovare queste informazioni con:

$ readelf -W -h ./hello | grep EXEC
  Type:                              EXEC (Executable file)

Se provi lo stesso comando su una libreria condivisa invece di EXEC , vedrai un DYN :

$ readelf -W -h /lib64/libc-2.32.so | grep DYN
  Type:                              DYN (Shared object file)

Abilita PIE

Per abilitare PIE su un programma di test, inviare i seguenti argomenti al compilatore:

$ gcc -pie -fpie hello.c -o hello

Puoi verificare che PIE sia abilitato usando checksec:

$ checksec --file=./hello --output=json | jq | grep pie
    "pie": "yes",
$

Dovrebbe essere visualizzato come eseguibile PIE con il tipo modificato da EXEC a DYN :

$ file hello
hello: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bb039adf2530d97e02f534a94f0f668cd540f940, for GNU/Linux 3.2.0, not stripped

$ readelf -W -h ./hello | grep DYN
  Type:                              DYN (Shared object file)

4. NX

NX sta per "non eseguibile". È spesso abilitato a livello di CPU, quindi un sistema operativo con NX abilitato può contrassegnare determinate aree di memoria come non eseguibili. Spesso, gli exploit di buffer overflow mettono il codice nello stack e quindi tentano di eseguirlo. Tuttavia, rendere questa area scrivibile non eseguibile può prevenire tali attacchi. Questa proprietà è abilitata per impostazione predefinita durante la normale compilazione utilizzando gcc :

$ checksec --file=/bin/ls --output=json | jq | grep nx
    "nx": "yes",

$ checksec --file=./hello --output=json | jq | grep nx
    "nx": "yes",

Checksec determina queste informazioni con il comando seguente. RW verso la fine significa che lo stack è leggibile e scrivibile; poiché non esiste la E , non è eseguibile:

$ readelf -W -l ./hello | grep GNU_STACK
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10

Disabilita NX per scopi demo

Non è consigliato, ma puoi disabilitare NX durante la compilazione di un programma utilizzando -z execstack argomento:

$ gcc -z execstack hello.c -o hello

$ checksec --file=./hello --output=json | jq | grep nx
    "nx": "no",

Dopo la compilazione, lo stack diventa eseguibile (RWE ), che consente l'esecuzione di codice dannoso:

$ readelf -W -l ./hello | grep GNU_STACK
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

5. RELRO

RELRO sta per Trasferimento di sola lettura. Un binario ELF (Executable Linkable Format) utilizza una Global Offset Table (GOT) per risolvere le funzioni in modo dinamico. Se abilitata, questa proprietà di sicurezza rende il GOT all'interno del binario di sola lettura, il che impedisce una qualche forma di attacchi di riposizionamento:

$ checksec --file=/bin/ls --output=json | jq | grep relro
    "relro": "full",

$ checksec --file=./hello --output=json | jq | grep relro
    "relro": "partial",

Checksec trova queste informazioni utilizzando il comando seguente. Qui è abilitata una delle proprietà RELRO; pertanto, il binario mostra "parziale" durante la verifica tramite checksec:

$ readelf -W -l ./hello | grep GNU_RELRO
  GNU_RELRO      0x002e10 0x0000000000403e10 0x0000000000403e10 0x0001f0 0x0001f0 R   0x1

$ readelf -W -d ./hello | grep BIND_NOW

Abilita RELRO completo

Per abilitare RELRO completo, usa i seguenti argomenti della riga di comando durante la compilazione con gcc :

$ gcc -Wl,-z,relro,-z,now hello.c -o hello

$ checksec --file=./hello --output=json | jq | grep relro
    "relro": "full",

Ora, anche la seconda proprietà è abilitata, rendendo il programma completo RELRO:

$ readelf -W -l ./hello | grep GNU_RELRO
  GNU_RELRO      0x002dd0 0x0000000000403dd0 0x0000000000403dd0 0x000230 0x000230 R   0x1

$ readelf -W -d ./hello | grep BIND_NOW
 0x0000000000000018 (BIND_NOW)          

6. Fortifica

Fortify è un'altra proprietà di sicurezza, ma non rientra nell'ambito di questo articolo. Lascerò l'apprendimento come checksec verifica fortificare nei binari e come è abilitato con gcc come esercizio da affrontare.

$ 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"

Altre funzionalità di checksec

Il tema della sicurezza è infinito e, sebbene non sia possibile coprire tutto qui, voglio menzionare alcune altre funzionalità di checksec comando con cui è un piacere lavorare.

Esegui su più binari

Non è necessario fornire ogni binario a checksec individualmente. Invece, puoi fornire un percorso di directory in cui risiedono più binari e checksec li verificherà tutti per te in una volta sola:

$ checksec --dir=/usr/bin

Processi

Oltre ai binari, checksec funziona anche sui programmi durante l'esecuzione. Il comando seguente trova le proprietà di sicurezza di tutti i programmi in esecuzione sul sistema. Puoi usare --proc-all se vuoi che controlli tutti i processi in esecuzione, oppure puoi selezionare un processo specifico usando il suo nome:

$ checksec --proc-all

$ checksec --proc=bash

Proprietà del kernel

Oltre alle applicazioni userland di checksec descritte in questo articolo, puoi anche usarlo per controllare le proprietà del kernel integrate nel tuo sistema:

$ checksec --kernel

Fai una prova

Checksec è un buon modo per capire quali proprietà userland e kernel sono abilitate. Esamina in dettaglio ogni proprietà di sicurezza e cerca di capire i motivi per abilitare ciascuna funzionalità e i tipi di attacchi che previene.


Linux
  1. 13 tutorial sulla sicurezza di Linux

  2. 50 tutorial per amministratori di sistema UNIX/Linux

  3. Esempi di utilizzo del comando dmsetup in Linux

  4. Esempio di utilizzo di getnstimeofday nel kernel Linux

  5. Sposta una cartella in Linux usando il comando mv

Installa MongoDB usando Vagrant in Linux

15 cose da sapere prima di usare Kali Linux

Utilizzo di SSH Port Forwarding come strumento di sicurezza in Linux

Utilizzo del comando Watch in Linux

Utilizzo di cut su terminale Linux

Sicurezza Linux vs Windows