Come posso forzare in modo esplicito e sicuro l'uso di un comando integrato in bash


19

C'è una domanda simile che si occupa dello scenario "wrapping", in cui si desidera sostituire, ad esempio, cdun comando che chiama l'integrato cd.

Tuttavia, alla luce di shellshock et al e sapendo che bash importa le funzioni dall'ambiente, ho fatto alcuni test e non riesco a trovare un modo per chiamare in modo sicuro l'integrato cddal mio script.

Considera questo

cd() { echo "muahaha"; }
export -f cd

Qualsiasi script chiamato in questo ambiente utilizzando cdsi interromperà (considerare gli effetti di qualcosa di simile cd dir && rm -rf .).

Ci sono comandi per controllare il tipo di comando (chiamato convenientemente type) e comandi per eseguire la versione integrata piuttosto che una funzione ( builtine command). Ma ecco, questi possono essere ignorati anche usando le funzioni

builtin() { "$@"; }
command() { "$@"; }
type() { echo "$1 is a shell builtin"; }

Produrrà quanto segue:

$ type cd
cd is a shell builtin
$ cd x
muahaha
$ builtin cd x
muahaha
$ command cd x
muahaha

Esiste un modo per forzare in modo sicuro bash a utilizzare il comando incorporato o almeno rilevare che un comando non è un comando incorporato, senza cancellare l'intero ambiente?

Mi rendo conto che se qualcuno controlla il tuo ambiente probabilmente sei fregato comunque, ma almeno per gli alias hai la possibilità di non chiamare l'alias inserendo un \prima.


puoi eseguire il tuo script usando il envcomando prima, in questo modo:env -i <SCRIPT.sh>
Arkadiusz Drabczyk

Solo se envnon viene ridefinito anche come funzione. Questo è terrificante. Ho pensato per la prima volta che i caratteri speciali avrebbero aiutato: chiamare con un percorso completo che includesse /, usando .come sorgente e così via. Ma quelli possono anche essere usati per i nomi delle funzioni! È possibile ridefinire qualsiasi funzione desiderata, ma è difficile tornare a chiamare il comando originale.
orion

1
È vero, ma se eseguirai uno script su una macchina compromessa, sarai comunque fregato. In alternativa, scrivi lo script #/bin/shse questa non è la shell interattiva predefinita.
Arkadiusz Drabczyk,

Risposte:


16

Olivier D è quasi corretto, ma è necessario impostare POSIXLY_CORRECT=1prima di correre unset. POSIX ha una nozione di incorporamenti speciali e bash lo supporta . unsetè uno di questi builtin. Cerca SPECIAL_BUILTINin builtins/*.cnella fonte bash per una lista, che include set, unset, export, evale source.

$ unset() { echo muahaha-unset; }
$ unset unset
muahaha-unset
$ POSIXLY_CORRECT=1
$ unset unset

Il ladro unsetè stato rimosso dall'ambiente, Se viene disattivata command, type, builtinallora si dovrebbe essere in grado di procedere, ma unset POSIXLY_CORRECTse si fa affidamento sulla non-POSIX comportamento o funzionalità bash avanzate.

Tuttavia, questo non risolve gli alias, quindi è necessario \unsetassicurarsi che funzioni nella shell interattiva (o sempre, nel caso in cui expand_aliasessia attivo).

Per il paranoico, questo dovrebbe sistemare tutto, penso:

POSIXLY_CORRECT=1
\unset -f help read unset
\unset POSIXLY_CORRECT
re='^([a-z:.\[]+):' # =~ is troublesome to escape
while \read cmd; do 
    [[ "$cmd" =~ $re ]] && \unset -f ${BASH_REMATCH[1]}; 
done < <( \help -s "*" )

( while, do, doneE [[sono parole riservate e non hanno bisogno precauzioni.) Si noti che stiamo usando unset -fper essere sicuri di funzioni impostata, anche se le variabili e le funzioni condividono lo stesso spazio dei nomi è possibile per entrambi di esistere contemporaneamente (grazie a Etan Reisner), nel qual caso anche il disinserimento due volte farebbe il trucco. È possibile contrassegnare una funzione di sola lettura, bash non impedisce che si disinserimento una funzione di sola lettura fino al bash-4.2, bash-4.3 osta a voi, ma onora ancora i comandi incorporati speciali quando POSIXLY_CORRECTè impostato.

Un readonly POSIXLY_CORRECTnon è un vero problema, questo non è un valore booleano o contrassegna la sua presenza abilita la modalità POSIX, quindi se esiste come readonly puoi fare affidamento sulle funzionalità POSIX, anche se il valore è vuoto o 0. Devi semplicemente disinserire le funzioni problematiche in un modo diverso rispetto a quello sopra, forse con alcune funzioni taglia e incolla:

\help -s "*" | while IFS=": " read cmd junk; do echo \\unset -f $cmd; done

(e ignorare eventuali errori) o impegnarsi in alcuni altri script .


Altre note:

  • functionè una parola riservata, può essere aliasata ma non sovrascritta con una funzione. (L'aliasing functionè leggermente fastidioso perché \function non è accettabile come modo per aggirarlo)
  • [[, ]]sono parole riservate, possono essere aliasate (che verranno ignorate) ma non sovrascritte da una funzione (sebbene le funzioni possano essere chiamate così)
  • (( non è un nome valido per una funzione, né un alias

Interessante, grazie! Mi sembra strano che Bash non sia in grado di proteggere unsetEt al di essere sovrascritto.
falstro

Questo ha bisogno -fdell'argomento unset, no? Altrimenti, se vengono definite sia una variabile che una funzione denominata uguale a un built-in, unsetverrà selezionata per prima cosa la variabile da non impostare. Anche questo fallisce se viene impostata una delle funzioni oscuranti, readonlyvero? (Anche se questo è rilevabile e può essere commesso un errore fatale.) Anche questo [sembra non essere integrato.
Etan Reisner,

1
Non credo \unset -f unsetabbia senso, dato che sarà fatto nel ciclo di lettura. Se unsetè una funzione \unsetche normalmente risolveresti al posto di quella incorporata, ma puoi usarla in \unsetsicurezza finché la modalità POSIX è attiva. In modo esplicito chiamando \unset -fsu [, .e :potrebbe essere una buona idea, però, come i tuoi li esclude regex. Vorrei anche aggiungere -fa \unsetnel ciclo, e \unset POSIXLY_CORRECTdopo il ciclo, invece di prima. \unalias -a(dopo \unset -f unalias) consente anche di rinunciare in sicurezza alla fuga sui comandi successivi.
Adrian Günter il

1
@ AdrianGünter Try:, [[ ${POSIXLY_CORRECT?non-POSIX mode shell is untrusted}x ]]questo provocherà la chiusura di uno script non interattivo con l'errore indicato se la variabile non è impostata o continuerà se è impostata (vuota o qualsiasi valore).
mr.spuratic il

1
"devi usare \unset" ... Va notato che la citazione di qualsiasi carattere (s) funzionerebbe per evitare l'espansione dell'alias, non solo la prima. un"se"tfunzionerebbe altrettanto bene, se non meno leggibile. "La prima parola di ogni semplice comando, se non quotata, viene controllata per vedere se ha un alias." - Rif. Bash
Robin A. Meade,

6

Mi rendo conto che se qualcuno controlla il tuo ambiente probabilmente sei fregato comunque

Sì, quello. Se si esegue uno script in un ambiente sconosciuto, tutto può andare storto, a partire LD_PRELOADdal processo di shell per eseguire codice arbitrario prima ancora di leggere lo script. Tentare di proteggere un ambiente ostile dall'interno della sceneggiatura è inutile.

Sudo ha igienizzato l'ambiente rimuovendo tutto ciò che sembra una definizione della funzione bash da oltre un decennio. Da Shellshock , altri ambienti che eseguono script di shell in un ambiente non completamente affidabile hanno seguito l'esempio.

Non è possibile eseguire in modo sicuro uno script in un ambiente impostato da un'entità non attendibile. Quindi preoccuparsi delle definizioni delle funzioni non è produttivo. Disinfetta il tuo ambiente e nel fare ciò variabili che bash interpreterebbero come definizioni di funzione.


1
Non sono completamente d'accordo. La sicurezza non è una proposizione binaria centrata su "sanitized" o "unsanitized". Si tratta di rimuovere le opportunità di sfruttamento. E sfortunatamente, la complessità dei sistemi moderni significa che ci sono molte opportunità di sfruttamento che devono essere bloccate correttamente. Molte opportunità di sfruttamento sono incentrate su attacchi a monte apparentemente innocui, come la modifica della variabile PATH, la ridefinizione delle funzioni integrate, ecc. Offrire a una sola persona o programmare l'opportunità di fare entrambe le cose e l'intero sistema è vulnerabile.
Dejay Clayton,

@DejayClayton Sono pienamente d'accordo con il tuo commento, fatta eccezione per la prima frase, perché non vedo in che modo contraddica la mia risposta in alcun modo. Rimuovere un'opportunità di sfruttamento aiuta, ma non rimuoverne solo la metà dove una banale modifica dell'attacco gli consente di continuare a lavorare.
Gilles 'SO- smetti di essere malvagio' il

Il mio punto è che non puoi semplicemente "disinfettare il tuo ambiente" e poi smettere di preoccuparti di vari vettori di attacco. A volte vale la pena "proteggere da un ambiente ostile all'interno dello script". Anche se risolvi il problema della "definizione di funzione", devi comunque preoccuparti di cose come avere un PERCORSO in cui qualcuno potrebbe mettere uno script personalizzato chiamato "cat" o "ls" in una directory e quindi qualsiasi script che invoca "cat "o" ls "invece di" / bin / cat "e" / bin / ls "sta effettivamente eseguendo un exploit.
Dejay Clayton,

@DejayClayton Ovviamente la sanificazione dell'ambiente non aiuta con i vettori di attacco non correlati all'ambiente. (Ad esempio uno script che chiama /bin/catin un chroot potrebbe chiamare qualsiasi cosa.) Disinfettare l'ambiente ti dà una sensazione sana PATH(supponendo che tu lo stia facendo bene, ovviamente). Non vedo ancora il tuo punto.
Gilles 'SO- smetti di essere malvagio' il

Considerando che PATH è una delle maggiori opportunità di exploit e che molte discutibili pratiche PATH sono documentate come consigli consigliati anche nei blog sulla sicurezza, e anche considerando che a volte le applicazioni installate riordinano PATH per garantire che tali app funzionino, non riesco a vedere come la codifica difensiva all'interno di una sceneggiatura sarebbe una cattiva idea. Quello che consideri un PERCORSO sano può in effetti essere vulnerabile agli exploit che non hai mai incontrato.
Dejay Clayton,

1

È possibile utilizzare unset -fper rimuovere le funzioni integrate, comando e tipo.


2
scusa, se unset() { true; }ne occupa.
falstro

1
Dannazione! E l'elenco delle funzioni definite si basa anche su un builtin. Mi arrendo!
odc
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.