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

8

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 seqüência de (esperançosamente) um diretório para o lugar onde eu quero criar o arquivo "create_a_file_here". Agora, se alguém vir essa 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 a string em var para alguns caracteres especiais como ';' antes de executar o comando shell, mas duvido que isso cubra todas as possíveis explorações.

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

    
por ES___ 09.01.2018 / 10:49

3 respostas

9

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

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

Se então, sim, se o conteúdo de var não for rigidamente controlado, é uma vulnerabilidade de injeção de comando.

Você pode tentar citar corretamente o conteúdo da variável¹ na sintaxe do shell, portanto, é garantido que ele seja passado como um único argumento para o cd builtin.

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ê pede ao shell para interpretar é fixo, ainda precisamos escrevê-lo corretamente na sintaxe do shell (aqui assumido como sendo um shell compatível com POSIX):

    A expansão de variável de shell
  • deve ser citada para evitar o split + glob
  • você precisa de -P para cd para fazer um simples chdir()
  • você precisa de -- para marcar o final das opções para evitar problemas com var começando com - (ou + em alguns shells)
  • Definimos CDPATH para a sequência vazia, caso ela esteja no ambiente
  • Só executamos o comando echo se cd foi bem-sucedido.

Há (pelo menos) um problema remanescente: se var for - , ele não entrará no diretório chamado - , mas no diretório anterior (conforme armazenado em $OLDPWD ) e OLDPWD=- CDPATH= cd -P -- "$DIR" não é garantido para contornar isso. Então você precisaria de algo como:

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

¹ Note que apenas fazendo um system(concat("cd \"", var, "\"; echo...")); é não o caminho a seguir, você estaria apenas movendo o problema.

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

A forma confiável somente de citar texto para shells parecidos com Bourne é usar aspas simples e também cuidar das aspas simples que podem ocorrer na string. Por exemplo, ative um char *var = "ab'cd" para char *escaped_var = "'ab'\''cd'" . Ou seja, substitua todos os ' para '\'' e coloque a coisa toda dentro de '...' .

Isso ainda pressupõe que essa string citada não seja usada em backticks e você ainda precisa do -- , -P , && , CDPATH= ...

    
por 09.01.2018 / 14:16
12

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

Seu exemplo aqui é trivial, mudar o diretório e criar arquivos deve ser fácil em qualquer linguagem de programação. Mas mesmo que você precise executar um comando externo, geralmente não há necessidade de fazer isso 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 o faça).

Se você precisar usar o shell, cite os argumentos da linha de comando ou passe-os pelo ambiente, como em resposta de Stéphane . Além disso, se você estiver preocupado com caracteres especiais, a solução correta é não tentar filtrar (lista negra) caracteres potencialmente perigosos, mas apenas manter caracteres conhecidos por serem seguro (lista branca).

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

    
por 09.01.2018 / 14:43
10

Dentro de uma linguagem de programação, deve haver maneiras melhores de fazer as coisas do que executar comandos de shell. Por exemplo, substituir cd var pelo equivalente em chdir (var); da sua linguagem de programação deve garantir que qualquer truque com o valor var 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 os diretórios. Basta concatenar o nome do diretório, a barra e o nome do arquivo que você deseja usar.

Em C, posso 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?

    
por 09.01.2018 / 11:28