come posso citare un'espansione variabile all'interno di una stringa per evitare la divisione delle parole?


9
$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"

In tal caso, come posso citare $myvarper evitare la divisione delle parole a causa degli spazi bianchi nel valore di myvar?


4
Non importa come lo risolvi, non farà ancora molto. L' cdunico ha effetto all'interno della bash -cshell.
Kusalananda

Sto solo dando un esempio minimo solo per la domanda, non è una soluzione funzionante al mio vero problema, che è unix.stackexchange.com/a/269080/674 più che nella stringa di comando fornita a sudo bash -cI ho un'espansione variabile a un percorso che potrebbe contenere spazi bianchi.
Tim

Risposte:


13

Non c'è suddivisione di parole (come nella funzione che divide le variabili su espansioni non quotate) in quel codice in quanto $myvarnon è quotato.

Esiste tuttavia una vulnerabilità nell'iniezione di comandi che $myvarviene espansa prima di essere passata a bash. Quindi il suo contenuto è interpretato come codice bash!

Gli spazi all'interno causeranno il passaggio di numerosi argomenti cd, non a causa della divisione delle parole , ma perché verranno analizzati come diversi token nella sintassi della shell. Con un valore di bye;reboot, questo verrà riavviato! ¹

Qui, vorresti:

sudo bash -c 'cd -P -- "$1"' bash "$myvar"

(dove passi il contenuto $myvarcome primo argomento di quello script inline; nota come entrambi $myvare $1sono stati quotati per la loro rispettiva shell per prevenire la divisione delle parole IFS (e il globbing)).

O:

sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'

(dove passi il contenuto di $myvaruna variabile d'ambiente).

Ovviamente non otterrai nulla di utile eseguendo solo cd in quello script inline (oltre a verificare se è rootpossibile cdentrare lì). Presumibilmente, vuoi che lo script sia cdlì e poi fai qualcos'altro come:

sudo bash -c 'cd -P -- "$1" && do-something' bash "$myvar"

Se l'intenzione era quella sudodi essere in grado di accedere a cduna directory a cui altrimenti non si ha accesso, allora non può davvero funzionare.

sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"

avvierà un interattivo bashcon la sua directory corrente in $myvar. Ma quella shell funzionerà come root.

Potresti fare:

sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"

Per ottenere un'interattività senza privilegi bashcon la directory corrente $myvar, ma se non avessi i permessi per accedere a cdquella directory in primo luogo, non sarai in grado di fare nulla in quella directory anche se è la tua directory di lavoro corrente.

$ myvar=/var/spool/cron/crontabs
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ ls
ls: cannot open directory '.': Permission denied

Un'eccezione sarebbe se si dispone dell'autorizzazione di ricerca per la directory stessa ma non per uno dei componenti della directory del suo percorso:

$ myvar=1/2
$ mkdir -p "$myvar"
$ chmod 0 1
$ cd 1/2
cd: permission denied: 1/2
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ pwd
/home/stephane/1/2
bash-4.4$ mkdir 3
bash-4.4$ ls
3
bash-4.4$ cd "$PWD"
bash: cd: /home/stephane/1/2: Permission denied

¹ a rigor di termini, per valori di $myvarlike $(seq 10)(letteralmente), ci sarebbe naturalmente una divisione delle parole all'espansione di quel comando di sostituzione da parte della bash shell iniziata comeroot


4

C'è una nuova magia di citazione (bash 4.4) che ti consente di fare più direttamente quello che vuoi. A seconda del contesto più ampio, l'utilizzo di una delle altre tecniche potrebbe essere migliore, ma funziona in questo contesto limitato.

sudo bash -c "cd ${myvar@Q}; pwd"

4

Se non puoi fidarti del contenuto di $myvar, l'unico approccio ragionevole è usare GNU printfper crearne una versione di escape:

#!/bin/bash

myvar=$(printf '%q' "$myvar")
bash -c "echo $myvar"

Come printfdice la pagina man:

%q

ARGUMENT viene stampato in un formato che può essere riutilizzato come input della shell, sfuggendo ai caratteri non stampabili con la $''sintassi POSIX proposta .


Vedi la mia risposta per una versione portatile di questo.
R .. GitHub smette di aiutare ICE

GNU printfverrà invocato lì solo su sistemi GNU e se questo $(printf '%q' "$myvar")viene eseguito in una shell in cui printfnon è incorporato. La maggior parte delle shell sono printfintegrate al giorno d'oggi. Non tutti supportano un %qpensiero e quando lo fanno, citerebbero la cosa in un formato supportato dalla shell corrispondente, che potrebbe non essere necessariamente la stessa di quella compresa bash. In realtà, bash's printfnon riesce a citare le cose correttamente per sé, anche per alcuni valori di $myvarin alcuni locali.
Stéphane Chazelas,

Con bash-4.3almeno, che è ancora una vulnerabilità di iniezione di comando con myvar=$'\xa3``reboot`\xa3`' in zh_HK.big5hkscs Locale per esempio.
Stéphane Chazelas,

3

Prima di tutto, come altri hanno notato che il tuo cdcomando è inutile poiché accade nel contesto di una shell che esce immediatamente. Ma in generale la domanda ha un senso. Ciò di cui hai bisogno è un modo per citare shell una stringa arbitraria , in modo che possa essere utilizzata in un contesto in cui verrà interpretata come una stringa quotata a shell. Ne ho un esempio nella mia pagina trucchi :

quote () { printf %s\\n "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" ; }

Con questa funzione puoi quindi fare:

myvar="/path to/my directory"
sudo bash -c "cd $(quote "$myvar")"

3

Ciò che Stéphane Chazelas suggerisce è sicuramente il modo migliore per farlo. Fornirò solo una risposta su come quotare in modo sicuro con la sintassi di espansione dei parametri anziché utilizzare un sedsottoprocesso, come nella risposta di R ..

myvar='foo \'\''"bar'

quote() {
  printf "'%s'" "${1//\'/\'\\\'\'}"
}

printf "value: %s\n" "$myvar"
printf "quoted: %s\n" "$(quote "$myvar")"
bash -c "printf 'interpolated in subshell script: %s\n' $(quote "$myvar")"

L'output di quello script è:

value: foo \'"bar
quoted: 'foo \'\''"bar'
interpolated in subshell script: foo \'"bar

1

Sì, c'è una divisione delle parole.
Beh, tecnicamente, la linea di comando si divide su bash analizzando la linea.

Prima di tutto citiamo bene:

La soluzione più semplice (e insicura) per far funzionare il comando come credo che vorrai che sia:

bash -c "cd \"$myvar\""

Confermiamo cosa succede (supponendo myvar="/path to/my directory"):

$ bash -c "printf '<%s>\n' $myvar"
<./path>
<to/my>
<directory>

Come puoi vedere, la stringa del percorso è stata suddivisa in spazi. E:

$ bash -c "printf '<%s>\n' \"$myvar\""
<./path to/my directory>

Non è diviso.

Tuttavia, il comando (come scritto) non è sicuro. Migliore utilizzo:

$ bash -c "cd -P -- \"$myvar\"; pwd"
/home/user/temp/path to/my directory

Ciò proteggerà il cd dai file che iniziano con un trattino (-) o che seguono collegamenti che potrebbero causare problemi.

Funzionerà se puoi fidarti che la stringa in $myvarè un percorso.
Tuttavia, "iniezione di codice" è ancora possibile poiché si sta "eseguendo una stringa":

$ myvar='./path to/my directory";date;"true'
$ bash -c "printf '<%s> ' \"$myvar\"; echo"
<./path to/my directory> Tue May  8 12:25:51 UTC 2018

È necessario un preventivo più forte della stringa, come:

$ bash -c 'printf "<%s> " "$1"; echo' _ "$myvar"
<./path to/my directory";date -u;"true>

Come puoi vedere sopra, il valore non è stato interpretato ma semplicemente utilizzato come "$1'.

Ora possiamo (tranquillamente) usare sudo:

$ sudo bash -c 'cd -P -- "$1"; pwd' _ "$myvar"
_: line 0: cd: ./path to/my directory";date -u;"true: No such file or directory

Se sono presenti diversi argomenti, è possibile utilizzare:

$ sudo bash -c 'printf "<%s>" "$@"' _ "$val1" "$val2" "$val3"

-2

Le virgolette singole non funzioneranno qui, da allora la tua variabile non verrà espansa. Se sei sicuro che la variabile per il tuo percorso sia disinfettata, la soluzione più semplice è semplicemente aggiungere virgolette attorno all'espansione risultante, una volta che è nella nuova shell bash:

sudo bash -c "cd \"$myvar\""

2
Ancora una vulnerabilità legata all'iniezione di comandi, come per i valori di $myvarlike $(reboot)o"; reboot; #
Stéphane Chazelas,

1
l'utilizzo sudo bash -c "cd -P -- '$myvar'"limiterebbe il problema solo ai valori $myvar che contengono caratteri di virgoletta singola che sarebbero più facili da disinfettare.
Stéphane Chazelas,
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.