Variáveis da Shell


Cyberguard: Linux Luminarium.

A interface de linha de comando do Linux é, na verdade, uma sofisticada linguagem de programação com a qual você pode criar programas completos! Como a interface de linha de comando é chamada coloquialmente de "shell", os programas escritos nessa linguagem são conhecidos como "scripts de shell" (shell scripts). Quando você está interagindo no terminal, está essencialmente escrevendo e executando um script de shell linha por linha!

Como a grande maioria das linguagens de programação, a shell oferece suporte total a variáveis. Este módulo tornará você fluente na definição, exibição, exportação e manipulação dessas variáveis!


Vamos começar imprimindo o conteúdo de variáveis na tela. O programa /challenge/run não entregará — e não tem como entregar — a flag para você, mas tudo bem: a flag já foi gravada na variável chamada "FLAG"! Basta fazer sua shell imprimi-la!

Existem várias formas de fazer isso, mas começaremos com o echo. Esse comando simplesmente imprime dados no terminal. Por exemplo:

hacker@dojo:~$ echo Hello Hackers!
Hello Hackers!

Você também pode imprimir variáveis com o echo, bastando prefixar o nome da variável com o caractere $. Por exemplo, existe uma variável chamada PWD que sempre armazena o diretório de trabalho atual da shell. Você pode visualizá-la assim:

hacker@dojo:~$ echo $PWD
/home/hacker

Agora é a sua vez. Faça sua shell exibir a variável FLAG e resolva este desafio!

Naturalmente, além de ler os valores armazenados em variáveis, você também pode gravar valores nelas. Isso é feito, como em muitas outras linguagens de programação, utilizando o operador =. Para atribuir o valor 1337 à variável VAR, você utiliza:

hacker@dojo:~$ VAR=1337

Observe que não deve haver espaços ao redor do sinal de =! Se você colocar espaços (por exemplo, VAR = 1337), a shell não reconhecerá a operação como uma atribuição de variável e tentará, em vez disso, executar um comando chamado VAR (que não existe).

Note também que aqui usamos VAR e não $VAR: o $ só é prefixado quando queremos acessar o conteúdo da variável. Nos termos da shell, o uso de $ dispara o que chamamos de expansão de variável (variable expansion), sendo inclusive a origem de inúmeras vulnerabilidades de segurança comuns (se tiver interesse nisso, confira o dojo Art of the Shell assim que estiver confortável com a linha de comando!).

Após definir as variáveis, você pode acessá-las com os métodos aprendidos anteriormente:

hacker@dojo:~$ echo $VAR
1337

Para resolver este nível, você deve definir a variável PWN com o valor COLLEGE. Atenção: tanto os nomes quanto os valores das variáveis diferenciam maiúsculas de minúsculas (case-sensitive)! PWN não é o mesmo que pwn, e COLLEGE não é o mesmo que College.

Neste nível, você aprenderá sobre o uso de aspas (quoting). Os espaços em branco possuem um significado sintático especial na shell, e não podem ser usados de maneira avulsa. Relembre como definimos uma variável:

hacker@dojo:~$ VAR=1337

Isso atribui o valor 1337 à variável VAR, mas e se você quisesse definir o valor como 1337 SAUCE? Você poderia ser tentado a digitar:

hacker@dojo:~$ VAR=1337 SAUCE

Isso parece intuitivo, mas não funciona, por motivos análogos à regra de não colocar espaços ao redor de =. Quando a shell encontra um espaço não delimitado, ela encerra a atribuição da variável e interpreta o próximo termo (SAUCE, neste caso) como sendo um comando a executar. Para definir VAR com o valor 1337 SAUCE, você precisa delimitar a expressão com aspas (quotes):

hacker@dojo:~$ VAR="1337 SAUCE"

Dessa forma, a shell interpreta 1337 SAUCE como um único argumento/token textual, atribuindo com sucesso o valor completo para VAR. Neste nível, você precisa definir a variável PWN com o valor COLLEGE YEAH. Boa sorte!

Por padrão, as variáveis que você define em uma sessão de shell são estritamente locais àquele processo da shell. Ou seja, outros comandos e programas que você executar a partir dela não as herdarão. Você pode testar esse comportamento invocando uma nova shell filha dentro da sua própria shell:

hacker@dojo:~$ VAR=1337
hacker@dojo:~$ echo "VAR is: $VAR"
VAR is: 1337
hacker@dojo:~$ sh
$ echo "VAR is: $VAR"
VAR is: 

Na saída acima, o prompt $ pertence ao sh, uma implementação minimalista de shell invocada como processo filho da sua shell principal. Observe que ela não recebeu o conteúdo da variável VAR!

Isso faz total sentido do ponto de vista de segurança e isolamento. As variáveis da sua shell podem conter senhas, segredos ou dados arbitrários que não devem vazar para todos os utilitários que você roda, a menos que isso seja intencional. Como sinalizar à shell que uma variável deve ser repassada? Você exporta essa variável. Ao exportar variáveis, elas passam a compor as variáveis de ambiente (environment variables) transmitidas aos processos filhos. Veja um exemplo:

hacker@dojo:~$ VAR=1337
hacker@dojo:~$ export VAR
hacker@dojo:~$ sh
$ echo "VAR is: $VAR"
VAR is: 1337

Aqui, o processo filho recebeu o valor de VAR e conseguiu imprimi-lo! Você também pode condensar a declaração e exportação em uma única linha:

hacker@dojo:~$ export VAR=1337
hacker@dojo:~$ sh
$ echo "VAR is: $VAR"
VAR is: 1337

Neste desafio, você deve executar /challenge/run com a variável PWN exportada e definida com o valor COLLEGE, e com a variável COLLEGE definida com o valor PWN, mas não exportada (ou seja, não herdada pelo /challenge/run). Boa sorte!

Existem várias formas de acessar e inspecionar variáveis no bash. O comando echo foi apenas uma delas, e agora aprenderemos outro método fundamental.

Experimente o comando env: ele imprime na tela todas as variáveis exportadas atualmente definidas na sua shell. Você pode analisar essa lista para localizar a variável FLAG!

No dia a dia com a shell, frequentemente você desejará armazenar a saída gerada por um comando dentro de uma variável. Felizmente, a shell torna isso extremamente prático através do mecanismo de Substituição de Comandos (Command Substitution)! Observe:

hacker@dojo:~$ FLAG=$(cat /flag)
hacker@dojo:~$ echo "$FLAG"
pwn.college{blahblahblah}
hacker@dojo:~$

Incrível, não? Agora é a sua vez de praticar. Capture a saída do comando /challenge/run diretamente em uma variável chamada PWN, e ela conterá a sua flag!


Curiosidade: Você também pode encontrar o uso de crases (backticks) em vez de $(): FLAG=`cat /flag` em vez de FLAG=$(cat /flag) no exemplo acima. Esse formato de crases é uma sintaxe legada e traz sérias desvantagens, especialmente ao aninhar substituições. Como você aninharia comandos como $(cat $(find / -name flag)) usando crases sem quebrar a interpretação da shell? Seria necessário escapar tudo com barras invertidas. A recomendação oficial do pwn.college e das melhores práticas modernas de bash é sempre preferir $(comando) em vez de `comando`.

Agora aprenderemos a capturar dados fornecidos pelo usuário (você). Isso é feito com o comando embutido read, que lê dados da entrada padrão e os armazena em uma variável!

Veja um exemplo utilizando a opção -p, que permite definir um prompt de texto (caso contrário, seria difícil distinguir o que é entrada do usuário e o que é saída do sistema no exemplo a seguir):

hacker@dojo:~$ read -p "INPUT: " MY_VARIABLE
INPUT: Hello!
hacker@dojo:~$ echo "You entered: $MY_VARIABLE"
You entered: Hello!

Lembre-se: o read lê dados a partir da sua entrada padrão (stdin)! O primeiro Hello! acima foi digitado por você como entrada, e não exibido pelo comando. Para deixar isso ainda mais claro, veja a anotação explícita de INPUT (entrada do usuário) e OUTPUT (saída exibida no terminal):

 INPUT: hacker@dojo:~$ echo $MY_VARIABLE
OUTPUT:
 INPUT: hacker@dojo:~$ read MY_VARIABLE
 INPUT: Hello!
 INPUT: hacker@dojo:~$ echo "You entered: $MY_VARIABLE"
OUTPUT: You entered: Hello!

Neste desafio, seu objetivo é utilizar o comando read para atribuir o valor COLLEGE à variável PWN. Boa sorte!

Frequentemente, quando usuários de shell querem carregar o conteúdo de um arquivo para uma variável, eles fazem algo como:

hacker@dojo:~$ echo "test" > some_file
hacker@dojo:~$ VAR=$(cat some_file)
hacker@dojo:~$ echo $VAR
test

Embora funcione, isso se enquadra no que veteranos de Unix chamam de "Uso Inútil do Cat" (Useless Use of Cat). Ou seja, instanciar um processo extra inteiro apenas para ler o conteúdo de um arquivo é um desperdício desnecessário de recursos. Acontece que você pode fazer isso usando os recursos nativos da própria shell!

Anteriormente, você usou o read para capturar a entrada do usuário em uma variável. Você também aprendeu a redirecionar arquivos para a entrada de comandos usando o operador <. Juntando esses dois conceitos, você pode ler arquivos diretamente na shell:

hacker@dojo:~$ echo "test" > some_file
hacker@dojo:~$ read VAR < some_file
hacker@dojo:~$ echo $VAR
test

O que aconteceu aqui? O exemplo redireciona some_file para a entrada padrão do read. Assim, quando o read popula a variável VAR, ele consome o texto diretamente do arquivo! Agora, utilize essa técnica para carregar o arquivo /challenge/read_me na variável de ambiente PWN, e liberaremos a flag! O conteúdo de /challenge/read_me muda continuamente, portanto você precisa lê-lo diretamente para a variável PWN em um único comando!


30-Day Scoreboard:

This scoreboard reflects solves for challenges in this module after the module launched in this dojo.

Rank Hacker Badges Score