"Eseguire qualsiasi comando che passerà i dati non attendibili ai comandi che interpretano gli argomenti come comandi"


18

Dal manuale di findutils:

Ad esempio costrutti come questi due comandi

# risky
find -exec sh -c "something {}" \;
find -execdir sh -c "something {}" \;

sono molto pericolosi. La ragione di ciò è che "{}" è espanso in un nome file che potrebbe contenere un punto e virgola o altri caratteri speciali per la shell. Se, ad esempio, qualcuno crea il file, /tmp/foo; rm -rf $HOMEi due comandi precedenti potrebbero eliminare la home directory di qualcuno.

Pertanto, per questo motivo non eseguire alcun comando che passi dati non attendibili (come i nomi dei file) a comandi che interpretano argomenti come comandi da interpretare ulteriormente (ad esempio 'sh').

Nel caso della shell, esiste una soluzione intelligente per questo problema:

# safer
find -exec sh -c 'something "$@"' sh {} \;
find -execdir sh -c 'something "$@"' sh {} \;

Questo approccio non è garantito per evitare ogni problema, ma è molto più sicuro che sostituire i dati scelti da un utente malintenzionato nel testo di un comando di shell.

  1. La causa del problema è find -exec sh -c "something {}" \;che la sostituzione per {}non è quotata e quindi non viene considerata come una singola stringa?
  2. Nella soluzione find -exec sh -c 'something "$@"' sh {} \;,

    • il primo {}viene sostituito, ma poiché {}non è quotato, non "$@"ha lo stesso problema del comando originale? Ad esempio, "$@"sarà ampliato a "/tmp/foo;", "rm", "-rf", e "$HOME"?

    • perché {}non è evaso o citato?

  3. Potresti fornire altri esempi (ancora con sh -co senza di essa, se applicabile; con o senza i findquali potrebbe non essere necessario) in cui si applicano lo stesso tipo di problema e soluzione e quali sono esempi minimi in modo che possiamo concentrarci sul problema e sulla soluzione con minima distrazione possibile? Vedi Modi per fornire argomenti a un comando eseguito da `bash -c`

Grazie.


Risposte:


29

Questo non è in realtà correlato alla citazione, ma piuttosto all'elaborazione degli argomenti.

Considera l'esempio rischioso:

find -exec sh -c "something {}" \;
  • Questo viene analizzato dalla shell, e suddivisa in sei parole: find, -exec, sh, -c, something {}(senza virgolette più), ;. Non c'è niente da espandere. La shell funziona findcon queste sei parole come argomenti.

  • Quando si findtrova qualcosa di processo, per esempio foo; rm -rf $HOME, sostituisce {}con foo; rm -rf $HOME, e corre shcon gli argomenti sh, -c, e something foo; rm -rf $HOME.

  • shora vede -ce, di conseguenza, analizza something foo; rm -rf $HOME( il primo argomento non opzionale ) ed esegue il risultato.

Ora considera la variante più sicura:

find -exec sh -c 'something "$@"' sh {} \;
  • Le piste di shell findcon gli argomenti find, -exec, sh, -c, something "$@", sh, {}, ;.

  • Ora, quando findreperti foo; rm -rf $HOME, che sostituisce {}ancora una volta, e corre shcon gli argomenti sh, -c, something "$@", sh, foo; rm -rf $HOME.

  • shvede -c, e analizza something "$@"come il comando da eseguire, ed she foo; rm -rf $HOMEcome parametri posizionali ( a partire da$0 ), si espande "$@"ad foo; rm -rf $HOME un singolo valore , e corre somethingcon il singolo argomento foo; rm -rf $HOME.

Puoi vederlo usando printf. Crea una nuova directory, inseriscila ed esegui

touch "hello; echo pwned"

Eseguendo la prima variante come segue

find -exec sh -c "printf \"Argument: %s\n\" {}" \;

produce

Argument: .
Argument: ./hello
pwned

mentre la seconda variante, eseguire come

find -exec sh -c 'printf "Argument: %s\n" "$@"' sh {} \;

produce

Argument: .
Argument: ./hello; echo pwned

Nella variante più sicura, perché {}non è evitato o citato?
Tim

1
In genere non ha bisogno di essere citato; avrebbe solo bisogno di essere citato in una shell dove ha qualche altro significato, e non penso che sia il caso di nessuna delle shell principali in uso al giorno d'oggi.
Stephen Kitt,

Da gnu.org/software/findutils/manual/html_mono/… : "Entrambe queste costruzioni ( ;e {}) devono essere evase (con un '\') o quotate per proteggerle dall'espansione della shell." Vuoi dire che non è corretto per bash?
Tim

3
Voglio dire che non è corretto per le shell in generale. Vedi POSIX . Quella particolare affermazione nel findutilsmanuale risale almeno al 1996 ... Naturalmente non fa male citare {}, come '{}'o "{}", ma non è necessario.
Stephen Kitt,

@Tim Stai vivendo una situazione comune in cui la tua intuizione non sta ancora spiegando il fatto che ci sono due tipi molto diversi di elaborazione degli argomenti in corso qui: quando / come la shell analizza gli argomenti da una riga di testo (che è dove le citazioni contano) è diversa dal passaggio di argomenti del sistema operativo grezzo (dove le virgolette sono solo caratteri regolari). Nel comando shell find some/path -exec sh -c 'something "$@"' {} \;ci sono in realtà tre livelli di elaborazione / passaggio degli argomenti, due della varietà di shell e una delle varietà di base del sistema operativo grezzo.
mtraceur,

3

Parte 1:

find utilizza solo la sostituzione del testo.

Sì, se lo hai fatto non quotato, in questo modo:

find . -type f -exec sh -c "echo {}" \;

e un utente malintenzionato era in grado di creare un file chiamato ; echo owned, quindi veniva eseguito

sh -c "echo ; echo owned"

che porterebbe a guscio esegue echopoi echo owned.

Ma se hai aggiunto le virgolette, l'attaccante potrebbe semplicemente terminare le virgolette e inserire il comando dannoso creando un file chiamato '; echo owned:

find . -type f -exec sh -c "echo '{}'" \;

che porterebbe a guscio corsa echo '', echo owned.

(se hai scambiato le doppie virgolette con virgolette singole, l'attaccante potrebbe usare anche l'altro tipo di virgolette.)


Parte 2:

In find -exec sh -c 'something "$@"' sh {} \;, {}inizialmente non viene interpretato dalla shell, viene eseguito direttamente con execve, quindi l'aggiunta di citazioni di shell non sarebbe di aiuto.

find -exec sh -c 'something "$@"' sh "{}" \;

non ha alcun effetto, poiché la shell rimuove le doppie virgolette prima di eseguire find.

find -exec sh -c 'something "$@"' sh "'{}'" \;

aggiunge le virgolette che la shell non tratta in modo speciale, quindi nella maggior parte dei casi significa solo che il comando non farà quello che vuoi.

Avendo espandersi a /tmp/foo;, rm, -rf, $HOMEnon dovrebbe essere un problema, perché questi sono argomenti di something, e somethingprobabilmente non trattare i suoi argomenti come comandi da eseguire.


Parte 3:

Presumo considerazioni simili valgono per tutto ciò che accetta input non attendibili e lo esegue come (parte di) un comando, ad esempio xargse parallel.


xargsè pericoloso solo se -Io -Jviene utilizzato. Durante il normale funzionamento si aggiungono solo argomenti alla fine dell'elenco, proprio come -exec ... {} +fa.
Charles Duffy,

1
( parallelal contrario, esegue una shell per impostazione predefinita senza esplicita richiesta da parte dell'utente, aumentando la superficie vulnerabile; questa è una questione che è stata oggetto di molte discussioni; vedi unix.stackexchange.com/questions/349483/… per una spiegazione, e il collegamento incluso a lists.gnu.org/archive/html/bug-parallel/2015-05/msg00005.html ).
Charles Duffy,

@CharlesDuffy Intendi " ridurre la superficie vulnerabile". L'attacco illustrato da OP in effetti non funziona di default con GNU Parallel.
Ole Tange,

1
Sì, lo so che ne hai bisogno per la parità su SSH - se non hai generato un parallelprocesso "ricevitore" remoto sull'altra estremità di un socket, in grado di eseguire una shell-less diretta execv. Il che è esattamente quello che avrei fatto se avessi implementato lo strumento nei tuoi panni. Avere un programma non interattivo si comporta in modo diverso in base al valore attuale di SHELLnon è affatto uno stupore.
Charles Duffy,

1
Sì, ritengo che pipe e reindirizzamenti dovrebbero essere impossibili a meno che l'utente non avvii esplicitamente una shell, a quel punto ottengono solo il comportamento esplicito della shell che hanno avviato esplicitamente. Se uno può aspettarsi solo ciò che execvfornisce, è più semplice e meno sorprendente e una regola che non cambia tra località / ambienti di runtime.
Charles Duffy,

3

1. La causa del problema è find -exec sh -c "something {}" \;che la sostituzione per {}non è quotata e quindi non viene trattata come una singola stringa?

In un certo senso, ma la citazione non può aiutare qui . Il nome file che viene sostituito al posto di {}può contenere qualsiasi carattere, comprese le virgolette . Qualunque sia la forma di quotazione utilizzata, il nome file potrebbe contenere la stessa e "interruzione" della quotazione.

2. ... ma poiché {}non è quotato, non "$@"ha lo stesso problema del comando originale? Ad esempio, "$@"verrà espanso in "/tmp/foo;", "rm", "-rf", and "$HOME"?

No. si "$@"espande nei parametri posizionali, come parole separate, e non li divide ulteriormente. Qui, {}è un argomento findin sé e findpassa il nome del file corrente anche come argomento distinto a sh. È direttamente disponibile come variabile nello script della shell, non viene elaborato come comando di shell stesso.

... perché {}non è evaso o citato?

Non ha bisogno di esserlo, nella maggior parte delle conchiglie. Se corri fish, deve essere: fish -c 'echo {}'stampa una riga vuota. Ma non importa se lo citate, la shell rimuoverà semplicemente le virgolette.

3. Potresti fare altri esempi ...

Ogni volta che si espande un nome di file (o un'altra stringa non controllata) così com'è all'interno di una stringa che viene considerata come una sorta di codice (*) , esiste la possibilità di eseguire un comando arbitrario.

Ad esempio, questo espande $fdirettamente il codice Perl e causerà problemi se un nome file contiene una doppia virgoletta. La citazione nel nome file termina la citazione nel codice Perl e il resto del nome file può contenere qualsiasi codice Perl:

touch '"; print "HELLO";"'
for f in ./*; do
    perl -le "print \"size: \" . -s \"$f\""
done

(Il nome del file deve essere un po 'strano poiché Perl analizza l'intero codice in anticipo, prima di eseguirne uno qualsiasi. Quindi dovremo evitare un errore di analisi.)

Mentre questo lo passa in modo sicuro attraverso un argomento:

for f in ./*; do
    perl -le 'print "size: " . -s $ARGV[0]' "$f"
done

(Non ha senso eseguire un'altra shell direttamente da una shell, ma se lo fai, è simile al find -exec sh ...caso)

(* un qualche tipo di codice include SQL, quindi XKCD obbligatorio: https://xkcd.com/327/ più spiegazione: https://www.explainxkcd.com/wiki/index.php/Little_Bobby_Tables )


1

La tua preoccupazione è precisamente il motivo per cui le citazioni parallele GNU immettono:

touch "hello; echo pwned"
find . -print0 | parallel -0 printf \"Argument: %s\\n\" {}

Questo non funzionerà echo pwned.

Eseguirà una shell, in modo che se estendi il tuo comando, non otterrai improvvisamente una sorpresa:

# This _could_ be run without spawining a shell
parallel "grep -E 'a|bc' {}" ::: foo
# This cannot
parallel "grep -E 'a|bc' {} | wc" ::: foo

Per ulteriori dettagli sulla questione spawning-a-shell, consultare: https://www.gnu.org/software/parallel/parallel_design.html#Always-running-commands-in-a-shell


1
... detto questo, find . -print0 | xargs -0 printf "Argument: %s\n"è altrettanto sicuro (o meglio, moreso, poiché con -print0uno gestisce correttamente i nomi dei file con le nuove righe); la quotazione di parallel è una soluzione alternativa per un problema che non esiste affatto quando non è presente alcuna shell.
Charles Duffy,

Ma non appena aggiungi | wcil comando devi saltare attraverso i cerchi per renderlo sicuro. GNU Parallel è sicuro per impostazione predefinita.
Ole Tange,

ITYM: cerca di coprire ogni stranezza del linguaggio shell attraverso il complicato processo di quotazione accurata di tutto. Non è così sicuro come archiviare correttamente i dati in variabili, non mescolati con il codice. Ora, se dovesse convertirlo automaticamente foo {} | wcin sh -c 'foo "$1" | wcsh {} `, potrebbe essere diverso.
ilkkachu,
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.