Não importa como você resolva isso, ainda não fará muito. O cdúnico tem efeito dentro do bash -cshell.
Kusalananda
Estou apenas dando um exemplo mínimo para a pergunta, não é uma solução funcional para o meu problema real, que é unix.stackexchange.com/a/269080/674, mais que na cadeia de comando fornecida sudo bash -c, tenho uma expansão variável para um nome de caminho que pode conter espaços em branco.
Tim
Respostas:
13
Não há divisão de palavras (como no recurso que divide variáveis em expansões não citadas) nesse código, pois $myvarnão é entre aspas.
Existe, no entanto, uma vulnerabilidade de injeção de comando, que $myvaré expandida antes de ser passada para bash. Portanto, seu conteúdo é interpretado como código bash!
Os espaços lá farão com que vários argumentos sejam transmitidos cd, não por causa da divisão de palavras , mas porque eles serão analisados como vários tokens na sintaxe do shell. Com um valor de bye;reboot, isso será reiniciado! ¹
Aqui, você deseja:
sudo bash -c 'cd -P -- "$1"' bash "$myvar"
(onde você passa o conteúdo $myvarcomo o primeiro argumento desse script embutido; observe como $myvare $1foram citados para o respectivo shell para impedir a divisão de palavras IFS (e globbing)).
Ou:
sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'
(onde você passa o conteúdo $myvarem uma variável de ambiente).
Obviamente, você não conseguirá nada útil executando apenascd esse script embutido (além de verificar se rootpode cdentrar nele). Presumivelmente, você deseja que esse script chegue cdlá e faça outra coisa como:
Se a intenção era usar sudopara poder cdentrar em um diretório ao qual você não tem acesso, isso não pode realmente funcionar.
sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"
iniciará um interativo bashcom seu diretório atual em $myvar. Mas esse shell estará rodando como root.
Você poderia fazer:
sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
Para obter uma interatividade sem privilégios bashcom o diretório atual $myvar, mas se você não tiver as permissões para cdentrar nesse diretório em primeiro lugar, não poderá fazer nada nesse diretório, mesmo que seja seu diretório de trabalho atual.
$ 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
Uma exceção seria se você tiver permissão de pesquisa para o próprio diretório, mas não para um dos componentes do diretório de seu caminho:
$ myvar=1/2
$ mkdir -p "$myvar"
$ chmod 01
$ 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
¹ estritamente falando, para valores $myvariguais $(seq 10)(literalmente), haveria divisão de palavras, é claro, após a expansão dessa substituição de comando pelo bash shell, iniciada comoroot
Há uma nova mágica (citando o 4.4 do bash) que permite que você faça mais diretamente o que deseja. Dependendo do contexto maior, o uso de uma das outras técnicas pode ser melhor, mas funciona nesse contexto limitado.
ARGUMENT é impresso em um formato que pode ser reutilizado como entrada do shell, escapando a caracteres não imprimíveis com a $''sintaxe proposta do POSIX .
Veja minha resposta para uma versão portátil disso.
R .. GitHub Pare de ajudar o gelo
O GNU printfserá invocado apenas nos sistemas GNU e se $(printf '%q' "$myvar")for executado em um shell onde printfnão está embutido. A maioria das conchas tem printfconstruído hoje em dia. Nem todos suportam um %qpensamento e, quando o fazem, citam algo em um formato suportado pelo shell correspondente, que pode não ser necessariamente o mesmo que o entendido por bash. Na verdade, não bashé printfpossível citar as coisas corretamente, mesmo para alguns valores de $myvarem alguns locais.
Stéphane Chazelas
Com bash-4.3pelo menos, ainda é uma vulnerabilidade de injeção de comando myvar=$'\xa3``reboot`\xa3`' em um local zh_HK.big5hkscs, por exemplo.
Stéphane Chazelas
3
Primeiro de tudo, como outros observaram, seu cdcomando é inútil, pois acontece no contexto de um shell que sai imediatamente. Mas, em geral, a questão faz sentido. O que você precisa é uma maneira de citar uma string arbitrária para que ela possa ser usada em um contexto em que será interpretada como uma string entre aspas. Eu tenho um exemplo disso na minha página de truques sh :
quote (){ printf %s\\n "$1"| sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/";}
O que Stéphane Chazelas sugere é definitivamente a melhor maneira de fazer isso. Vou apenas fornecer uma resposta sobre como citar com segurança a sintaxe de expansão de parâmetro, em vez de usar um sedsubprocesso, como na resposta da R ..
Isso protegerá o CD contra arquivos que começam com um hífen (-) ou seguem os links que podem levar a problemas.
Isso funcionará se você puder confiar que a string $myvaré um caminho.
No entanto, a "injeção de código" ainda é possível, pois você está "executando uma sequência":
As aspas simples não funcionarão aqui, pois sua variável não será expandida. Se você tem certeza de que a variável do seu caminho é higienizada, a solução mais simples é simplesmente adicionar aspas à expansão resultante, uma vez que ela esteja no novo shell bash:
Ainda uma vulnerabilidade de injeção de comando, como por valores de $myvarcomo $(reboot)ou"; reboot; #
Stéphane Chazelas
1
o uso sudo bash -c "cd -P -- '$myvar'"limitaria o problema a apenas valores $myvar que contenham caracteres de aspas simples, que seriam mais fáceis de limpar.
cd
único tem efeito dentro dobash -c
shell.sudo bash -c
, tenho uma expansão variável para um nome de caminho que pode conter espaços em branco.Respostas:
Não há divisão de palavras (como no recurso que divide variáveis em expansões não citadas) nesse código, pois
$myvar
não é entre aspas.Existe, no entanto, uma vulnerabilidade de injeção de comando, que
$myvar
é expandida antes de ser passada parabash
. Portanto, seu conteúdo é interpretado como código bash!Os espaços lá farão com que vários argumentos sejam transmitidos
cd
, não por causa da divisão de palavras , mas porque eles serão analisados como vários tokens na sintaxe do shell. Com um valor debye;reboot
, isso será reiniciado! ¹Aqui, você deseja:
(onde você passa o conteúdo
$myvar
como o primeiro argumento desse script embutido; observe como$myvar
e$1
foram citados para o respectivo shell para impedir a divisão de palavras IFS (e globbing)).Ou:
(onde você passa o conteúdo
$myvar
em uma variável de ambiente).Obviamente, você não conseguirá nada útil executando apenas
cd
esse script embutido (além de verificar seroot
podecd
entrar nele). Presumivelmente, você deseja que esse script cheguecd
lá e faça outra coisa como:Se a intenção era usar
sudo
para podercd
entrar em um diretório ao qual você não tem acesso, isso não pode realmente funcionar.iniciará um interativo
bash
com seu diretório atual em$myvar
. Mas esse shell estará rodando comoroot
.Você poderia fazer:
Para obter uma interatividade sem privilégios
bash
com o diretório atual$myvar
, mas se você não tiver as permissões paracd
entrar nesse diretório em primeiro lugar, não poderá fazer nada nesse diretório, mesmo que seja seu diretório de trabalho atual.Uma exceção seria se você tiver permissão de pesquisa para o próprio diretório, mas não para um dos componentes do diretório de seu caminho:
¹ estritamente falando, para valores
$myvar
iguais$(seq 10)
(literalmente), haveria divisão de palavras, é claro, após a expansão dessa substituição de comando pelobash
shell, iniciada comoroot
fonte
Há uma nova mágica (citando o 4.4 do bash) que permite que você faça mais diretamente o que deseja. Dependendo do contexto maior, o uso de uma das outras técnicas pode ser melhor, mas funciona nesse contexto limitado.
fonte
Se você não pode confiar no conteúdo
$myvar
, a única abordagem razoável é usar o GNUprintf
para criar uma versão escapada dele:Como a
printf
página do manual diz:fonte
printf
será invocado apenas nos sistemas GNU e se$(printf '%q' "$myvar")
for executado em um shell ondeprintf
não está embutido. A maioria das conchas temprintf
construído hoje em dia. Nem todos suportam um%q
pensamento e, quando o fazem, citam algo em um formato suportado pelo shell correspondente, que pode não ser necessariamente o mesmo que o entendido porbash
. Na verdade, nãobash
éprintf
possível citar as coisas corretamente, mesmo para alguns valores de$myvar
em alguns locais.bash-4.3
pelo menos, ainda é uma vulnerabilidade de injeção de comandomyvar=$'\xa3``reboot`\xa3`'
em um local zh_HK.big5hkscs, por exemplo.Primeiro de tudo, como outros observaram, seu
cd
comando é inútil, pois acontece no contexto de um shell que sai imediatamente. Mas, em geral, a questão faz sentido. O que você precisa é uma maneira de citar uma string arbitrária para que ela possa ser usada em um contexto em que será interpretada como uma string entre aspas. Eu tenho um exemplo disso na minha página de truques sh :Com esta função, você pode:
fonte
O que Stéphane Chazelas sugere é definitivamente a melhor maneira de fazer isso. Vou apenas fornecer uma resposta sobre como citar com segurança a sintaxe de expansão de parâmetro, em vez de usar um
sed
subprocesso, como na resposta da R ..A saída desse script é:
fonte
Sim, há divisão de palavras.
Bem, tecnicamente, a linha de comando é dividida no bash analisando a linha.
Vamos primeiro citar corretamente:
A mais simples (e insegura) das soluções para que o comando funcione como eu acredito que você desejará é:
Vamos confirmar o que acontece (supondo
myvar="/path to/my directory"
):Como você pode ver, a cadeia de caminho foi dividida em espaços. E:
Não está dividido.
No entanto, o comando (conforme escrito) é inseguro. Melhor uso:
Isso protegerá o CD contra arquivos que começam com um hífen (-) ou seguem os links que podem levar a problemas.
Isso funcionará se você puder confiar que a string
$myvar
é um caminho.No entanto, a "injeção de código" ainda é possível, pois você está "executando uma sequência":
Você precisa de uma citação mais forte da string, como:
Como você pode ver acima, o valor não foi interpretado, mas apenas usado como
"$1'
.Agora podemos (com segurança) usar o sudo:
Se houver vários argumentos, você pode usar:
fonte
As aspas simples não funcionarão aqui, pois sua variável não será expandida. Se você tem certeza de que a variável do seu caminho é higienizada, a solução mais simples é simplesmente adicionar aspas à expansão resultante, uma vez que ela esteja no novo shell bash:
fonte
$myvar
como$(reboot)
ou"; reboot; #
sudo bash -c "cd -P -- '$myvar'"
limitaria o problema a apenas valores$myvar
que contenham caracteres de aspas simples, que seriam mais fáceis de limpar.