Proteção do comando shell com variável de cadeia

9

Dentro de uma linguagem de programação, eu executo um comando shell simples

cd var; echo > create_a_file_here

com var sendo uma variável que contém uma string de (espero) um diretório para o local onde eu quero criar o arquivo "create_a_file_here". Agora, se alguém vir esta linha de código, é possível explorá-la atribuindo, por exemplo:

var = "; rm -rf /"

As coisas podem ficar bem feias. Uma maneira de evitar o caso acima seria talvez pesquisar na string var vários caracteres especiais como ';' antes de executar o comando shell, mas duvido que isso abranja todas as explorações possíveis.

Alguém sabe uma boa maneira de garantir que "cd var" mude apenas um diretório e nada mais?

ES___
fonte
4
Dependendo de como você pode chamar o shell, você também pode passar varcomo argumento. Por exemplo, chamando shcom argumentos -c, 'cd "$1"; echo > create_a_file_here', 'sh', varobras e não precisa de qualquer alteração var. O 'sh'argumento é passado como $0.
Ipsec
1
Qual linguagem de programação? Você está usando o POSIX shou criando sua própria linguagem de programação com uma sintaxe semelhante, mas que se expande em varvez de exigir que você escreva cd "$var"? Ou isso é bashcom shopt -s cdable_vars? Ah, acho que você quer dizer que outro programa bifurca um shell para executar esses comandos. Então, basta citar var, mas certifique-se que não inclui em si um caracter de aspas ...
Peter Cordes
@ PeterCordes Se você está falando sobre o Bash, uma variável de aspas duplas que contém aspas duplas é boa. por exemplo, s='"'; echo "$s"impressões ".
Wjandrea
@WJAndrea: sim, mas não há uma citação "trunfo" que não possa ser derrotada ao construir uma atribuição de variável a partir de entradas não confiáveis. Ah, solução: faça var=untrusted stringno programa pai, também varé uma variável de ambiente que já está definida ao chamar sh. Então, você só precisa citá-lo toda vez que o expandir, o que é possível de forma confiável. Ah, vejo que idéia já é parte da resposta de Stéphane> <.
Peter Cordes

Respostas:

9

Se bem entendi, varé uma variável na sua linguagem de programação.

E na sua linguagem de programação, você está solicitando que um shell interprete uma string que é a concatenação "cd ", o conteúdo dessa variável e "; echo > create_a_file_here".

Nesse caso, sim, se o conteúdo de varnão for totalmente controlado, é uma vulnerabilidade de injeção de comando.

Você pode tentar citar adequadamente o conteúdo da variável¹ na sintaxe do shell, para garantir que ele seja passado como um único argumento para o cdbuiltin.

Outra abordagem seria passar o conteúdo dessa variável de outra maneira. Uma maneira óbvia seria passar isso em uma variável de ambiente. Por exemplo, em C:

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

Desta vez, o código que você solicita que o shell interprete seja corrigido, ainda precisamos escrevê-lo corretamente na sintaxe do shell (aqui assumida como um shell compatível com POSIX):

  • A expansão da variável do shell deve ser citada para evitar split + glob
  • você precisa -Ppara cdfazer uma simpleschdir()
  • você precisa --marcar o final das opções para evitar problemas ao variniciar com -(ou +em algumas conchas)
  • Definimos CDPATHa string vazia no caso de estar no ambiente
  • Só executamos o echocomando se tiver cdsido bem-sucedido.

Há (pelo menos) um problema restante: se varhouver -, ele não chdir no diretório chamado, -mas no diretório anterior (como armazenado em $OLDPWD) e OLDPWD=- CDPATH= cd -P -- "$DIR"não é garantido que ele funcione. Então você precisaria de algo como:

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

¹ Observe que apenas fazer um nãosystem(concat("cd \"", var, "\"; echo...")); é o caminho a seguir, você estaria apenas mudando o problema.

Por exemplo, um var = "$(rm -rf /)"ainda seria um problema.

A única maneira confiável de citar texto para shells semelhantes a Bourne é usar aspas simples e também cuidar das aspas simples que podem ocorrer na string. Por exemplo, vire char *var = "ab'cd"para char *escaped_var = "'ab'\\''cd'". Ou seja, substituir toda 'a '\''e embrulhe a coisa toda dentro '...'.

Que ainda assume que essa seqüência citada não é usado dentro de acentos graves, e você ainda precisa o --, -P, &&, CDPATH=...

Stéphane Chazelas
fonte
12

Solução simples: não chame o shell do seu programa. Em absoluto.

Seu exemplo aqui é trivial, alterar o diretório e criar arquivos deve ser fácil em qualquer linguagem de programação. Mas mesmo se você precisar executar um comando externo, geralmente não há necessidade de fazê-lo através do shell.

Então, por exemplo, em Python, em vez de executar os.system("somecmd " + somearg), use subprocess.run(["somecmd", somearg]). Em C, em vez de system(), use fork()e exec()(ou encontre uma biblioteca que faça isso).

Se você precisar usar o shell, cite os argumentos da linha de comando ou passe-os pelo ambiente, como na resposta de Stéphane . Além disso, se você se preocupar com caracteres especiais, a solução correta é não tentar filtrar (lista negra) caracteres potencialmente perigosos, mas apenas manter os caracteres conhecidos como seguros (lista de permissões).

Permita apenas os personagens cujas funções você conhece, para que haja menos risco de perder alguma coisa. O resultado final pode ser que você decide apenas permitir [a-zA-Z0-9_], mas isso pode ser suficiente para concluir o trabalho. Você também pode querer verificar se o seu local e conjunto de ferramentas não incluem letras acentuadas como äe öem que. Eles provavelmente não são considerados especiais por nenhum shell, mas, novamente, é melhor ter certeza se eles passam ou não.

ilkkachu
fonte
1
E verifique se o que você usa para comparar [a-zA-Z0-9]não inclui coisas como àcuja codificação também pode ser mal interpretada por alguns shells (como bash) em alguns locais.
Stéphane Chazelas
@ StéphaneChazelas (fora de interesse :) são eles (e logo abaixo dos locais afetados?) #
Wilf
10

Dentro de uma linguagem de programação, deve haver maneiras melhores de fazer coisas do que executar comandos shell. Por exemplo, a substituição cd varpelo equivalente de sua linguagem de programação chdir (var);deve garantir que qualquer truque com o valor de varapenas resulte em um erro "Diretório não encontrado" em vez de ações não intencionais e possivelmente maliciosas.

Além disso, você pode usar caminhos absolutos em vez de alterar diretórios. Apenas concatene o nome do diretório, a barra e o nome do arquivo que você deseja usar.

Em C, eu poderia fazer algo como:

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") );

Certamente sua linguagem de programação pode fazer algo semelhante?

telcoM
fonte
Obrigado pela sua resposta, mas infelizmente tenho que usar a solução alternativa com os comandos bash. Eu testei citando a variável - como muru sugeriu - e parece funcionar!
ES9_