Ho un piccolo server e vorrei verificare i tempi di compilazione sui programmi C forniti dagli utenti. I programmi non verranno mai eseguiti solo compilati.
Quali rischi ci sono per consentire agli utenti di compilare C arbitrari usando gcc 5.4.0?
Un po 'strano, ma: è un rischio di negazione del servizio o potenziale divulgazione di informazioni.
Poiché il preprocessore di C includerà con gioia qualsiasi file specificato in una direttiva #include
, qualcuno può #include "../../../../../../../../../../dev/zero"
e il preprocessore proverà a leggere fino alla fine di /dev/zero
(buona fortuna).
Allo stesso modo, specialmente se lasci che le persone vedano l'output dei loro tentativi di compilazione, qualcuno potrebbe provare a includere vari file che possono o non possono essere presenti sul tuo sistema, e potrebbe imparare cose sulla tua macchina. Combinato con un uso intelligente di #pragma poison
, loro potrebbero anche imparare cose sul contenuto del file anche se non si forniscono messaggi di errore completi.
In modo simile, i pragma possono alterare un sacco di comportamento del preprocessore, del compilatore o del linker e sono specificati nei file sorgente. Non c'è probabilmente che non permetta a qualcuno di fare qualcosa come specificare il nome del file di output o qualcosa del genere, ma se c'è, potrebbe essere abusato per sovrascrivere i file sensibili, o essere eseguito (scrivendo in cron o simili). Potrebbe esserci qualcosa di altrettanto pericoloso. Dovresti fare attenzione a compilare codice non affidabile.
C è un linguaggio molto potente, e alcune delle cose terribili che puoi fare con esso ti scioccano. Ad esempio, puoi creare un programma C di 16 byte che impiega 27 minuti per compilare e quando finisce, compila un file eseguibile 16 Gigabyte . E questo è solo utilizzando 16 byte. Quando calcoli il preprocessore e file di codice sorgente più grandi, sono sicuro che potresti creare bombe del compilatore molto più grandi.
Ciò significa che chiunque abbia accesso al tuo server potrebbe effettivamente fare un attacco DoS sul tuo server. Ora, per essere onesti, questo è significativamente meno pericoloso di avere qualcuno che abusa di una vulnerabilità nel compilatore, o di includere file sensibili per ottenere informazioni sul tuo server (come hanno parlato gli altri partecipanti).
Ma è ancora un altro possibile fastidio che incontrerai durante la compilazione di codice arbitrario. Sono sicuro che potresti impostare un limite di tempo su tutte le build e assicurarti di non archiviare mai i file binari. Anche se, naturalmente, è ancora necessario conservarlo sul disco mentre è in fase di creazione , quindi se qualcuno ipoteticamente ha realizzato una bomba del compilatore più grande del tuo disco rigido, ti troverai nei guai (se lasci che la build finitura).
@ AndréBorie è corretto. I compilatori e la configurazione corrispondente non saranno ben controllati per problemi di sicurezza, quindi in generale non dovresti compilare codice non affidabile.
Il rischio è che venga sfruttato un overflow del buffer o un qualche tipo di vulnerabilità di esecuzione della libreria e che l'utente malintenzionato ottenga l'accesso all'account utente (si spera non- root
!) che ha eseguito il compilatore. Anche un non- root
hack è serio nella maggior parte dei casi. Questo potrebbe essere elaborato in una domanda separata.
Creare una VM è una buona soluzione, per contenere qualsiasi potenziale exploit in modo che non possano danneggiare il resto della tua applicazione.
È meglio disporre di un modello di VM Linux che è possibile avviare in base alle esigenze con un ambiente di compilazione pulito.
Idealmente lo getterei via dopo ogni uso, ma questo potrebbe non essere strettamente necessario. Se si isola la macchina virtuale abbastanza bene e si sanificano correttamente i dati di risposta dalla VM, cosa che si dovrebbe fare comunque; quindi il peggiore che un hack potrebbe fare è DoS o creare false ore di compilazione. Questi non sono problemi seri da soli; almeno non così grave come accedere al resto della tua applicazione.
Tuttavia, reimpostare la VM dopo ogni utilizzo (cioè anziché quotidianamente) fornisce un ambiente più stabile in generale e può migliorare la sicurezza in alcuni casi limite.
Alcuni sistemi operativi forniscono contenitori come alternativa alle macchine virtuali. Questo può essere un approccio più snello, ma si applicano gli stessi principi.
Sì, è pericoloso: ma come hanno detto le persone è possibile farlo. Sono l'autore e il manutentore dei compilatori online al link , e ho trovato abbastanza pratico renderlo sicuro usando una combinazione di:
LD_PRELOAD
wrapper ( fonte qui ) che impedisce al compilatore di aprire file non presenti in una whitelist esplicita. Questo impedisce di leggere / etc / passwd o altre cose del genere (non che questo sarebbe di aiuto più di tanto). LD_PRELOAD
che rileva comportamenti scorretti. L'intera fonte è su GitHub , così come lo è l'origine di immagini del contenitore di memoria e compilatori e simili.
Ho scritto un post sul blog che spiega come viene eseguito anche l'intero setup.
Non si vorrebbe eseguire il compilatore come root, anche se ho visto questo accadere per ragioni di "facilità e convenienza". Sarebbe fin troppo facile per un utente malintenzionato includere qualcosa come:
#include "../../../../etc/passwd"
#include "../../../../etc/shadow"
e recupera il contenuto di questi file come parte del messaggio di errore del compilatore.
Anche i compilatori sono programmi come tutto il resto, e avranno i loro bug che potrebbero essere vulnerabili, sarebbe facile per qualcuno solo sfocare programmi C e causare problemi.
La maggior parte della sicurezza delle applicazioni si concentrerà prima di tutto sulla convalida degli input, sfortunatamente la definizione di input "sicuri e validi" per un compilatore C è probabilmente lassù con il problema di interruzione in termini di difficoltà:)
Se permetti a un utente di fornire un archivio contenente il codice, puoi avere problemi, non esattamente con il compilatore ma con il linker che usa;)
ld segue i collegamenti simbolici se puntano a un file che non esiste. Ciò significa che se compilate test.c sull'output a.out ma avete già un collegamento simbolico denominato a.out nella vostra directory che punta a un file non esistente, l'eseguibile compilato verrà scritto nella posizione che punta al file (con la limitazione dei diritti utente).
In pratica un utente malintenzionato potrebbe, ad esempio, includere una stringa contenente una chiave ssh pubblica nel suo codice e fornire un collegamento simbolico denominato a.out a ~ / .ssh / authorized_keys . Se questo file non esiste già, questo consente all'autore dell'attacco di inserire la sua chiave ssh nel computer di destinazione, consentendogli l'accesso esterno senza dover craccare alcuna password.