Protezione del comando shell con variabile stringa


9

All'interno di un linguaggio di programmazione, eseguo un semplice comando shell

cd var; echo > create_a_file_here

con var che è una variabile che contiene una stringa di (si spera) una directory nel punto in cui voglio creare il file "create_a_file_here". Ora se qualcuno vede questa riga di codice, è possibile sfruttarla assegnando ad esempio:

var = "; rm -rf /"

Le cose possono diventare piuttosto brutte. Un modo per evitare il caso precedente potrebbe essere quello di cercare nella stringa in var alcuni caratteri speciali come ';' prima di eseguire il comando shell, ma dubito che questo copra tutti i possibili exploit.

Qualcuno conosce un buon modo per assicurarsi che "cd var" cambi solo una directory e nient'altro?


4
A seconda di come puoi invocare la shell, puoi anche passare varcome argomento. Ad esempio, chiamando shcon argomenti -c, 'cd "$1"; echo > create_a_file_here', 'sh', varopere e non ha bisogno di alcuna modifica var. L' 'sh'argomento è passato come $0.
ipsec,

1
Quale linguaggio di programmazione? Stai utilizzando POSIX sho stai creando il tuo linguaggio di programmazione con una sintassi simile ma che si espande varinvece di richiedere la scrittura cd "$var"? O è questo bashcon shopt -s cdable_vars? Oh, penso che intendi dire che qualche altro programma crea una shell per eseguire questi comandi. Quindi cita var, ma assicurati che non includa un carattere di citazione stesso ...
Peter Cordes,

@PeterCordes Se stai parlando di Bash, va bene una variabile tra virgolette doppie che contiene una virgoletta doppia. ad es . s='"'; echo "$s"stampe ".
wjandrea,

@WJAndrea: sì, ma non esiste una citazione "briscola" che non può essere sconfitta quando si costruisce un'assegnazione variabile da input non attendibile. Oh, soluzione: do var=untrusted stringnel programma genitore, quindi varè una variabile d'ambiente che è già impostata quando si richiama sh. Quindi devi solo citarlo ogni volta che lo espandi, il che è possibile fare in modo affidabile. Ah, vedo che l'idea fa già parte della risposta di Stéphane>. <
Peter Cordes,

Risposte:


9

Se ho capito bene, varè una variabile nel tuo linguaggio di programmazione.

E nel tuo linguaggio di programmazione, stai chiedendo a una shell di interpretare una stringa che è la concatenazione "cd ", il contenuto di quella variabile e "; echo > create_a_file_here".

In vartal caso, sì, se il contenuto di non è strettamente controllato, si tratta di una vulnerabilità all'iniezione di comando.

Potresti provare a citare correttamente il contenuto della variabile¹ nella sintassi della shell, in modo che venga garantito come un singolo argomento all'integrato cd.

Un altro approccio sarebbe quello di passare il contenuto di quella variabile in un altro modo. Un modo ovvio sarebbe passarlo in una variabile d'ambiente. Ad esempio, in C:

char *var =  "; rm -rf /";
setenv("DIR", var, 1);
system("CDPATH= cd -P -- \"$DIR\" && echo something > create_a_file_here");

Questa volta, il codice che chiedi alla shell di interpretare è fisso, dobbiamo ancora scriverlo correttamente nella sintassi della shell (qui si presume che sia una shell conforme a POSIX):

  • l'espansione della variabile shell deve essere quotata per evitare split + glob
  • è necessario -Pper cdfare un semplicechdir()
  • è necessario --contrassegnare la fine delle opzioni per evitare problemi con l' varinizio -(o +in alcune shell)
  • Impostiamo CDPATHla stringa vuota nel caso in cui fosse nell'ambiente
  • Eseguiamo il echocomando solo se ha cdesito positivo.

C'è (almeno) un altro problema: se lo varè -, non si trova nella directory chiamata -ma nella directory precedente (come memorizzata in $OLDPWD) e OLDPWD=- CDPATH= cd -P -- "$DIR"non è garantito per aggirarla. Quindi avresti bisogno di qualcosa come:

system(
  "case $DIR in\n"
  " (-) CDPATH= cd -P ./-;;\n"
  " (*) CDPATH= cd -P -- \"$DIR\";;\n"
  "esac && ....");

¹ Nota che fare semplicemente a nonsystem(concat("cd \"", var, "\"; echo...")); è la strada da percorrere, potresti semplicemente spostare il problema.

Ad esempio, a var = "$(rm -rf /)"sarebbe ancora un problema.

L' unico modo affidabile per citare il testo per le shell tipo Bourne è utilizzare le virgolette singole e occuparsi anche delle virgolette singole che possono verificarsi nella stringa. Ad esempio, girare char *var = "ab'cd"a char *escaped_var = "'ab'\\''cd'". Cioè, sostituire tutti 'per '\''e avvolgere il tutto all'interno '...'.

Ciò presuppone ancora che quella stringa tra virgolette non viene utilizzato entro apici inversi, e si sarebbe ancora bisogno del --, -P, &&, CDPATH=...


12

Soluzione semplice: non chiamare la shell dal programma. Affatto.

Il tuo esempio qui è banale, cambiare la directory e creare file dovrebbe essere facile in qualunque linguaggio di programmazione. Ma anche se è necessario eseguire un comando esterno, di solito non è necessario eseguirlo tramite la shell.

Quindi, ad esempio in Python, invece di eseguirlo os.system("somecmd " + somearg), usa subprocess.run(["somecmd", somearg]). In C, invece di system(), usa fork()e exec()(o trova una libreria che lo fa).

Se devi usare la shell, cita gli argomenti della riga di comando o passali attraverso l'ambiente, come nella risposta di Stéphane . Inoltre, se ti preoccupi dei caratteri speciali, la soluzione corretta è quella di non cercare di filtrare (lista nera) i personaggi potenzialmente pericolosi, ma di mantenere solo i personaggi conosciuti come sicuri ( lista bianca).

Consenti solo ai personaggi di cui conosci le funzioni, in questo modo c'è meno rischio di perdere qualcosa. Il risultato finale potrebbe essere che decidi di autorizzare [a-zA-Z0-9_], ma che potrebbe essere sufficiente per portare a termine il lavoro. Puoi anche verificare che le impostazioni internazionali e il set di strumenti non includano lettere accentate come äe öin quello. Probabilmente non sono considerati speciali da nessuna shell, ma, di nuovo, è meglio essere sicuri che passino o meno.


1
E assicurati che qualunque cosa tu usi per abbinare [a-zA-Z0-9]non include cose come la àcui codifica potrebbe anche essere male interpretata da alcune shell (come bash) in alcune localizzazioni.
Stéphane Chazelas,

@ StéphaneChazelas (per interesse :) sono (e proprio sotto quelle località interessate?)
Wilf

10

All'interno di un linguaggio di programmazione, ci dovrebbero essere modi migliori per fare le cose che eseguire i comandi della shell. Ad esempio, la sostituzione cd varcon l'equivalente del linguaggio di programmazione di chdir (var);dovrebbe garantire che qualsiasi inganno con il valore di varsolo si traduca in un errore "Directory non trovata" piuttosto che azioni indesiderate e possibilmente dannose.

Inoltre, è possibile utilizzare percorsi assoluti anziché cambiare directory. Basta concatenare il nome della directory, la barra e il nome file che si desidera utilizzare.

In C, potrei fare qualcosa del tipo:

char filepath[PATH_MAX];  /* alternative constant: MAXPATHLEN */

/* Join directory name in var and the filename, guarding against exceeding PATH_MAX */
snprintf (filepath, PATH_MAX, "%s/%s", var, "create_a_file_here");

/* create an empty file/truncate an existing one */
fclose (fopen (filepath, "w") );

Sicuramente il tuo linguaggio di programmazione può fare qualcosa di simile?


Grazie per la risposta, ma purtroppo devo usare la soluzione alternativa con i comandi bash. Ho provato a citare la variabile - come suggeriva Muru - e sembra funzionare!
ES___
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.