Você está na página 1de 67

Programação Python e

Bash Script
Prof. Renato Costa do Carmo Fonseca
2022
SUMÁRIO
Capítulo 1. Introdução à Lógica de Programação ............................................. 5

1.1. Algoritmos e Programas................................................................................................... 5

1.2. Desenvolvendo Algoritmo ............................................................................................... 6

1.3. Constantes, Variáveis e Tipos de Variáveis ............................................................. 7

1.4. Operadores Aritméticos, Relacionais e Lógicos .................................................... 8

1.5. Estruturas de Decisão e Repetição .......................................................................... 11

Capítulo 2. Introdução ao Framework ................................................................. 16

2.1. Exemplos de Framework ............................................................................................... 16

2.2. Framework de Desenvolvimento ............................................................................... 17

Capítulo 3. Introdução à Linguagem Python ................................................... 21

3.1. Instalação Interpretador Python ............................................................................. 21

3.2. Nosso primeiro Script ..................................................................................................... 25

3.3. Estruturas de Decisão e Repetição .......................................................................... 29

Capítulo 4. Construção de Códigos em Python ............................................... 36

4.1. Reverse Shell ...................................................................................................................... 36

4.2. Bibliotecas ........................................................................................................................... 37

4.3. Reverse Shell – Primeira Parte................................................................................... 38

4.4. Reverse Shell – Segunda Parte.................................................................................. 41

4.5. Reverse Shell – Testando o Script............................................................................ 42

Capítulo 5. Introdução à Linguagem Bash Script .......................................... 45

5.1. Primeiro Script em Bash................................................................................................ 45

5.2. Melhorando o Script ........................................................................................................ 46

Capítulo 6. Construção de Código Ofensivo em Bash Script ..................... 50

6.1. Comandos Iniciais ........................................................................................................... 50

6.2. Construindo o Código – Primeira Parte ................................................................. 51

2
6.3. Construindo o Código – Segunda Parte ................................................................ 53

Capítulo 7. Introdução à Engenharia Reversa ................................................. 55

7.1. Introdução ........................................................................................................................... 55

7.2. Ferramenta Ghidra ........................................................................................................... 56

7.3. Analisando Código com Ghidra .................................................................................. 57

Capítulo 8. Códigos Maliciosos ............................................................................... 64

8.1. Tipos de Malware .............................................................................................................. 64

Referências ......................................................................................................................... 67

3
1
Capítulo 1. Introdução à Lógica de Programação
As primeiras Linguagens de Programação datam de antes de 1940.
Entre 1943 e 1946, o cientista alemão Konrad Zuse desenvolveu a
linguagem Plankalkül, considerada por muitos como a primeira linguagem
de programação de alto nível.

Toda linguagem de programação tem uma característica que lhes é


comum, denominada Lógica de Programação. Em seu conceito básico, a
Lógica de Programação é a técnica de encadear pensamentos para atingir
determinado propósito. O principal objetivo no estudo da lógica de
programação é a construção de algoritmos que permitam alcançar a
resolução de determinado problema.

Em programação, os pensamentos encadeados podem ser


traduzidos em Sequências Lógicas. Instruções concatenadas de uma
determinada tarefa, que representam os passos a serem executados para
atingir um objetivo específico.

1.1. Algoritmos e Programas


Quando juntamos Sequências Lógicas de forma concatenada em um
script, criamos o Algoritmo. O Algoritmo é a ordem de passos que permite a
execução de uma tarefa.

Como exemplo, imagine sua rotina diária para chegar ao trabalho.


Você se levanta às 06:00 horas da manhã, toma banho, escova os dentes,
toma o café da manhã, se arruma, sai de casa, toma uma ou mais conduções,
chega ao prédio onde trabalha, caminha até a sua sala e, finalmente, senta-
se ao computador.

Essas ações formam o seu Algoritmo matinal, cuja resolução de


problema é chegar ao trabalho. Cada atividade executada representa um

5
conjunto de instruções, concatenadas em uma sequência lógica que faz
sentido para aquele objetivo.

A combinação de vários algoritmos forma um Programa. Quando


tratamos de programas para computador, os algoritmos são desenvolvidos
em uma linguagem de programação que pode ser interpretada e executada
pela máquina.

A legislação brasileira atual não obriga o registro de programas


desenvolvidos. Entretanto, para fins de proteção contra pirataria,
concorrência desleal ou cópias não autorizadas, a Lei de Direito Autoral (Lei
nº 9.610/1998) confere proteção aos programas registrados no INPI
(Instituto Nacional da Propriedade Industrial).

1.2. Desenvolvendo Algoritmo


Qualquer algoritmo deve ter um propósito, um objetivo a ser
alcançado. Para que o algoritmo tenha sucesso, três fases devem ser
seguidas.

1.2.1. Fase 1 – Entrada de Dados

Parte do algoritmo onde o programador prepara o código para


receber informações de entrada, as quais serão processadas na próxima
fase. Normalmente, a Entrada de Dados é realizada pelo usuário que faz uso
do algoritmo, através de periféricos de inserção como teclado ou mouse de
computador.

1.2.2. Fase 2 – Processamento

São todas as atividades executadas pelo algoritmo desenvolvido,


nas quais os dados de entrada da etapa anterior são tratados, para que
sejam alcançados os resultados da próxima fase.

6
1.2.3. Fase 3 – Saída de Dados

Etapa onde é apresentado o resultado do Processamento da Entrada


de Dados.

Figura 1 – Diagrama de Bloco – Fases Algoritmo

Entrada Processamento Saída

Tomando como exemplo a criação de um algoritmo que calcule a


média aritmética das notas de uma turma de cinco alunos, podemos
estabelecer o seguinte diagrama de blocos:

Figura 2 – Diagrama de Bloco – Cálculo Média

Nota Nota Nota Nota


Cálculo Média
Aluno 01 Aluno 02 Aluno ... Aluno 5

Neste caso, temos as notas dos alunos como fase de Entrada de


Dados do algoritmo, o bloco “Cálculo” que representa a fase de
Processamento, e, por fim, a “Média” que identifica a fase de Saída do
algoritmo.

1.3. Constantes, Variáveis e Tipos de Variáveis


O trabalho de qualquer algoritmo é lidar com dados, sejam eles
oriundos do próprio código, ou inseridos por meio de interface interativa com
o usuário. O fato é que, para que estes dados possam ser tratados pelo
algoritmo, os mesmos precisam estar disponíveis na memória do
computador quando necessário. Neste ponto, o algoritmo lança mão das
Constantes e Variáveis.

1.3.1. Constantes

Como o próprio nome já diz, as Constantes são valores fixos e não


podem ser alterados durante a execução do algoritmo. Normalmente, são
declaradas logo no início do código, justamente com o propósito de
carregamento em memória.

7
Tomando como exemplo o algoritmo da média de notas, apresentado
no item anterior, a Constante seria o número de alunos na turma (5).

1.3.2. Variáveis

Por sua vez, as Variáveis são valores em memória que podem ser
manipulados ao decorrer da execução do código. Entretanto, vale destacar
que, mesmo com esta possibilidade de alternância de valores, uma variável
só pode armazenar um valor a cada instante.

No caso do nosso algoritmo de média de notas, as variáveis seriam


as entradas de notas dos alunos.

1.3.3. Tipos de Variáveis

As Variáveis carregam uma característica fundamental para sua


utilização no algoritmo. Estamos falando dos Tipos de Variáveis. Para cada
Tipo de Variável, o código aplicará uma forma distinta de tratamento ao valor
declarado.

A depender da linguagem de programação utilizada, diversos Tipos


de Variáveis podem ser aplicados. As mais comuns são:

• Int – Variável do tipo número inteiro, seja ele positivo ou negativo.

• Float – Variável do tipo número decimal, seja ele positivo ou


negativo.

• String – Variável do tipo caracteres de texto.

• Boolean – Variável do tipo lógico, cujo valor pode ser Verdadeiro ou


Falso.

1.4. Operadores Aritméticos, Relacionais e Lógicos


Dentro dos algoritmos, os Operadores são os elementos que nos
permitem lidar com os dados de entrada, de forma a alcançar os objetivos de
saída. Podemos incrementar, decrementar, comparar, avaliar etc.

8
1.4.1 Operadores Aritméticos

Aplicados para obter resultados numéricos, os Operadores


Aritméticos também podem ser utilizados para funções de exponenciação,
além das operações básicas de adição, subtração, divisão e multiplicação. A
seguir os símbolos dos operadores aritméticos:

Operação Aritmética Símbolo

Adição +

Subtração -

Divisão /

Multiplicação *

Exponenciação **

Ainda sobre este tipo de operadores, é importante destacar que os


algoritmos devem ater-se à hierarquia das operações aritméticas, quando da
execução de funções relacionadas.

1º - Executar os cálculos entre parênteses; 2º - Executar as funções


de exponenciação; 3º - Executar os cálculos de multiplicação ou divisão (o
que vier primeiro); 4º - Executar os cálculos de adição ou subtração (o que
vier primeiro).

1.4.2. Operadores Relacionais

Aplicados para comparar valores, o resultado de uma expressão com


Operadores Relacionais gera um valor Boolean (verdadeiro ou falso). A
simbologia pode variar de linguagem para linguagem, mas basicamente é a
seguinte:

Operador Símbolo

Igual a == ou =

Diferente de <> ou !=

Maior que >

Menor que <

9
Maior ou igual a >=

Menor ou igual a <=

Como exemplo de aplicabilidade, vamos considerar as variáveis A e


B, onde A = 7 e B = 5. Comparando estas variáveis entre si, fazendo uso dos
Operadores Relacionais na tabela anterior, podemos obter os seguintes
resultados:

Expressão Comparativa Resultado

A=B Falso

A != B Verdadeiro

A>B Verdadeiro

A<B Falso

A >= B Verdadeiro

A <= B Falso

1.4.3. Operadores Lógicos

Semelhantes aos Operadores Relacionais, os Operadores Lógicos


também retornam valor Verdadeiro ou Falso. Entretanto, são aplicados para
combinar resultados de expressões anteriores. A tabela a seguir traz os
Operadores Lógicos e suas respectivas aplicabilidades em expressões
comparativas.

Operador Lógico Aplicabilidade

AND (E) Expressão AND verdadeira, se todas as condições forem verdadeiras

Expressão OR verdadeira, se pelo menos uma condição for


OR (OU)
verdadeira.

Expressão NOT inverte o valor da condição. Verdadeiro torna-se


NOT (NÃO)
falso e vice-versa.

Como exemplo de aplicabilidade, consideremos as variáveis A, B e C,


onde A = 3, B = 6 e C = 1. Vamos comparar as variáveis entre si, aplicando os
Operadores Relacionais, e depois submetemos os resultados aos
Operadores Lógicos.

10
Expressões Resultado

A=B AND B>C Falso

A <> B OR B<C Verdadeiro

A>B NOT Verdadeiro

A<B AND B>C Verdadeiro

A >= B OR B=C Falso

A <= B NOT Falso

1.5. Estruturas de Decisão e Repetição


Outros recursos que permitem lidar de forma mais eficiente com os
dados de entrada são as estruturas de Decisão e Repetição. Dentre outras
possibilidades, de forma combinada e/ou isolada, com estes elementos,
podemos testar valores, impor condicionantes, repetir procedimentos e
muito mais.

1.5.1. Estruturas de Decisão

Dependendo das funcionalidades atribuídas ao algoritmo, este


necessita tomar decisões ao longo de sua execução. Para que esta
capacidade seja implementada, o código criado depende de uma Estrutura
de Decisão, normalmente representada pelas expressões em inglês “IF”,
“THEN” e “ELSE”. “SE”, “ENTÃO” e “SENÃO”, respectivamente.

Usando novamente o exemplo da média de notas, imagine agora que


o algoritmo criado deve informar ao professor se a turma avaliada está acima
da média ou abaixo, e que a média de referência é a nota sete. Sem distinguir
a linguagem aplicada, uma estrutura simples de decisão traria o seguinte
código:

IF MEDIA >= 7 THEN


RESULTADO = “TURMA ACIMA DA MÉDIA”
ELSE
RESULTADO = “TURMA ABAIXO DA MÉDIA”

11
Traduzindo este código em um parágrafo didático, teremos: “SE o
valor da variável MEDIA for maior ou igual à 7, ENTÃO a variável RESULTADO
recebe o texto TURMA ACIMA DA MÉDIA. SENÃO, a variável RESULTADO
recebe o texto TURMA ABAIXO DA MÉDIA”.

Observem que as linhas 2 e 4 do código estão deslocadas em relação


às linhas 1 e 3. Esta é uma condição proposital, indicando que os códigos
nas linhas pares estão “dentro” das funções de código nas linhas ímpares.
Chamamos isso de indentação do código.

1.5.2. Estruturas de Repetição

Também conhecidas como Laços ou Loops, as estruturas de


repetição permitem ao algoritmo repetir determinada função, ou linha de
código, até que seja satisfeita a condição de saída do Loop de repetição. As
estruturas mais comuns utilizadas são o “FOR” (Para) e o “WHILE”
(Enquanto).

A estrutura “FOR” normalmente é aplicada quando conhecemos a


quantidade de repetições que serão executadas. Esta quantidade é definida
em um elemento contador, o qual será incrementado em cada Loop de
repetição. A condição de saída do Loop acontece quando o contador alcança
a quantidade de repetições previamente definida. Vejamos um exemplo:

FOR (contador = 1; contador <= 5; contador+1) {


RESULTADO = “ALUNO” + contador
print (RESULTADO)
}

Na primeira linha deste código, dentro dos parênteses, temos três


atribuições à variável “contador”. Na primeira atribuição, até o primeiro
marcador “;”, definimos o valor inicial da variável, que, neste caso, é 1. Na
segunda atribuição, definimos a quantidade máxima de repetições que
serão executadas, nossa condição de saída que é 5. Na terceira atribuição,

12
definimos o padrão de incremento da variável, que, neste caso, é de +1 em
cada Loop.

Na segunda linha do código, que já é dentro da estrutura de


repetição do “FOR”, temos a variável RESULTADO recebendo a palavra
“ALUNO”, associada ao conteúdo da variável “contador” no momento do
Loop. Na terceira linha, temos uma função de exemplo que imprime o
conteúdo da variável RESULTADO. Os colchetes delimitam a parte de
atuação da estrutura “FOR” de repetição. Na interface interativa do usuário,
o resultado deste código seria:

Aluno 1; Aluno 2; Aluno 3; Aluno 4; Aluno 5

Por sua vez, a estrutura do “WHILE” é aplicada quando não


conhecemos a quantidade de repetições que serão aplicadas, e quando a
condição de saída depende de valores de entrada atribuídos pelo usuário
que executa o código. Vejamos um exemplo:

NOME = “FELIPE”
WHILE (NOME != “PAULA”) {
print (“QUAL O SEU NOME?”)
NOME = INPUT()
}
print(“Obrigado”+ NOME)

Na primeira linha do código, definimos o conteúdo da variável NOME


como “FELIPE”. A estrutura de WHILE requer a declaração inicial de valor da
variável que será testada dentro de sua estrutura de repetição.

Na segunda linha, dentro dos parênteses, temos a condição que


avalia se o loop pode ser encerrado ou não. Enquanto a variável NOME for
diferente da palavra “PAULA”, as funções dentro da estrutura de repetição
serão executadas. Considerando que na primeira linha a variável NOME

13
recebeu a palavra “FELIPE”, diferente de “PAULA”, a estrutura de repetição
acontecerá.

Na terceira linha, temos o texto impresso na interface interativa do


usuário, requisitando o nome da pessoa que está interagindo. Na linha
seguinte, temos um exemplo de função de entrada, que vai salvar na variável
NOME o conteúdo digitado pelo usuário. Até que este conteúdo seja
diferente da palavra “PAULA”, o código permanece executando a estrutura
de repetição.

14
2
Capítulo 2. Introdução ao Framework
Conceitualmente, o termo Framework quer dizer uma estrutura
combinada de recursos, voltada para auxiliar a execução de determinada
tarefa. Frameworks podem ser aplicados para diversas finalidades, incluindo
o desenvolvimento de software.

Para melhor compreender este conceito, vamos trazer exemplos de


Frameworks, com suas respectivas áreas de atuação.

2.1. Exemplos de Framework


2.1.1. PMBOK – Project Management Body of Knowlegde

Desenvolvido e mantido pelo PMI – Project Management Institute,


o PMBOK é um framework voltado para as atividades de gerenciamento de
projetos. Está em sua 7ª edição, lançada em agosto de 2021, e reúne as
melhores práticas de gerenciamento de projetos experimentadas por
diversas organizações ao redor do mundo.

Em sua 6ª edição, o framework está dividido em cinco fases:


Iniciação, Planejamento, Execução, Monitoramento e Controle e
Encerramento. Cada fase estabelece processos de gerenciamento de
projetos, em um total de quarenta e nove processos.

Os processos também podem ser agrupados por área de


conhecimento, em um total de dez áreas: Escopo, Custos, Cronograma,
Partes Interessadas, Qualidade, Recursos, Aquisições, Riscos, Integração e
Comunicações. Para saber mais sobre o PMBOK 6ª Edição, acesse
https://ricardo-vargas.com/pt/pmbok6-processes-flow/.

16
2.1.2. NIST CSF – Cybersecurity Framework

Desenvolvido e mantido pelo National Institute of Standards and


Technology (EUA), o Framework NIST é baseado em perfis de operação,
alinhados aos objetivos organizacionais, e oferece uma estrutura de
padrões, diretrizes e melhores práticas, para gerenciamento de riscos em
projetos na área de segurança da informação.

O NIST Cybersecurity Framework preconize três elementos


principais: Núcleo (Core), Camadas (Tiers) e Perfis (Profile). No Core, temos
cinco funções principais que são fragmentadas em categorias e
subcategorias. Para as Tiers, o framework estabelece quatro níveis sobre
como a organização deve visualizar e processar a gestão de risco. Para os
Profiles, o framework alinha Funções, Categorias e Subcategorias para se
encaixar aos requisitos de negócio da empresa:
https://ww.nist.gov/cyberframework/.

2.2. Framework de Desenvolvimento


Neste tipo de framework, as plataformas de desenvolvimento são
uma estrutura integrada de funcionalidades para favorecer o trabalho do
desenvolvedor, agregando recursos pré-definidos. Como exemplo, vamos
conhecer o Flask, um micro-framework para desenvolvimento web em
Python.

2.2.1. Instalando Micro-framework Flask

Para usar o Flask no Windows 10, vamos instalar o Python como


linguagem e o Sublime Text como editor. Para o Python, veja o conteúdo do
item 3.1 da apostila. Para o Sublime Text, acesse o site
https://www.sublimetext.com/3, faça o download para a versão Windows e
instale.

Após instalar esses componentes, abra o terminal de comandos do


Windows (CMD), pela linha de comando, navegue até a pasta “ Desktop” e,
com o comando “mkdir projetos_python”, crie o diretório “projetos_python”.

17
Entre no diretório criado e, com o comando “pip install virtualenv”, instale o
pacote “virtualenv”. Este pacote vai permitir criar ambientes virtuais
exclusivos para o desenvolvimento.

Para criar o ambiente virtual, entre com o comando “virtualenv


projeto01”. O termo “projeto01” é o nome dado ao nosso primeiro projeto.
Conforme imagem a seguir, o terminal vai trazer a informação de criação do
ambiente:

Com o comando “projeto01\Scripts\activate”, vamos ativar o


ambiente virtual. Observe que a linha de comando do terminal migra para
dentro do ambiente virtualizado. Agora, vamos instalar o Flask com o
comando “pip install Flask”.

2.2.2. Criando o Código

Agora, vamos iniciar o Sublime Text e abrir a pasta do projeto que


criamos no diretório Desktop. Clique em “Flie>Open Folder” e navegue até a
pasta do projeto. Após abrir a pasta, note que, ao lado esquerdo, temos a
árvore de arquivos. Dentro da pasta “projeto01”, vamos criar um novo
arquivo com o nome “aula2.py”. Neste arquivo, entre com as linhas de código
conforme imagem a seguir:

18
Na linha 1, temos a importação de elementos da biblioteca Flask. Na
linha 3, definimos, na variável “f”, a própria da instância Flask. Na linha 4,
associamos a rota que orienta o ponto base onde os arquivos do projeto
serão consultados pelo navegador. Na linha 5, temos uma função do código
que vai trazer o conteúdo da página. Na linha 6, dentro da função “raiz()”,
temos o texto que vai ser retornado na página. Por fim, na linha 8, temos o
comando que efetivamente executa o código.

Salve o arquivo e retorne ao CMD do Windows. No terminal, execute


o comando “python aula2.py”:

Abra o navegador de sua preferência e, na barra de endereços, digite


o http://127.0.0.1:5000. O navegador vai retornar “Olá mundo”.

19
3
Capítulo 3. Introdução à Linguagem Python
Criada em 1991 pelo programador holandês Guido Van Rossumem,
a linguagem de programação Python foi desenvolvida com a finalidade de
ser simples e de fácil compreensão. Entretanto, a linguagem Python é muito
poderosa e versátil, podendo ser aplicada com inúmeras finalidades.

Uma característica peculiar da linguagem Python é a legibilidade dos


programas escritos. Enquanto outras linguagens demandam excessivo uso
de caracteres de marcação, como ponto, ponto vírgula, chaves, parênteses e
colchetes, a linguagem Python reduz a aplicação desses recursos, tornando
fácil a leitura e a compreensão do código.

3.1. Instalação Interpretador Python


Assim como em outras linguagens de programação, Python também
requer do sistema operacional do computador um elemento interpretador,
que será capaz de executar as linhas de código criadas. Em nosso conteúdo,
vamos tratar da execução de códigos em Python para os sistemas
operacionais Linux e Windows.

3.1.1. Instalando Python no Linux

Como no nosso curso está voltado à segurança ofensiva, faremos a


instalação do Python no sistema operacional Kali Linux. No link
https://www.kali.org/get-kali/#kali-virtual-machines é possível encontrar
detalhes sobre esse sistema operacional, bem como o passo a passo de
instalação de uma VM (Virtual Machine) Kali para as plataformas de
virtualização VirtualBox ou VMware. Em nosso ambiente, usaremos o
VirtualBox (https://www.virtualbox.org/).

As versões mais atualizadas do Kali já trazem a linguagem Python de


forma nativa, e não demandam instalação. Caso a versão Kali da sua VM não

21
traga o Python, acesse o terminal do Kali e digite o comando “sudo apt install
python -y” para iniciar a instalação. Ao término, execute o comando
“python”. Se o resultado for o mesmo da imagem a seguir, significa que a
instalação foi bem-sucedida.

Agora, precisamos de um ambiente para a criação de nossos códigos


em Python. Chamamos este ambiente de IDE (Integrated Development
Enviroment), ou Ambiente Integrado de Desenvolvimento. Para o Kali,
vamos utilizar o IDLE. No terminal, digite o comando “sudo apt install idle-
python3.10 -y”.

Ao término da instalação, execute o comando “idle-python3.10”.


Correndo tudo bem, será aberto o console de desenvolvimento da
ferramenta.

3.1.2. Instalando Python no Windows

Antes de seguir com o processo de instalação do Python no sistema


operacional Windows, vai aqui uma sugestão, já pensando nos cenários de
testes das próximas etapas deste módulo: recomendo criar uma VM
Windows 10 específica para as atividades do módulo. Isso permitirá separar

22
o ambiente de estudo do seu ambiente de testes. Além disso, caso algum
problema mais grave aconteça durante a fase de instalação do Python, ou
de desenvolvimento e teste dos códigos, basta descartar a VM e criar outra.

Para criar a VM do Windows 10, faça o download da versão de


avaliação no link https://www.microsoft.com/en-us/evalcenter/evaluate-
windows-10-enterprise, e depois crie a VM normalmente no VirtualBox. Para
instalar o Python, acesse a página www.python.org e, na aba Downloads,
selecione a versão para Windows.

Ao término do Download, faça a instalação do Python de forma


padrão, seguindo os passos orientados do instalador. Um ponto importante
nesta parte é fazer a instalação das variáveis de ambiente, para que as
ferramentas agregadas possam ser executadas na linha de comando.
Selecione a caixa de marcação, conforme imagem a seguir, e depois clique
em “Install Now”:

23
Depois de instalado, clique no menu Iniciar e procure por “Python
3.10 (64-bit)”, caso seu sistema seja na versão 64 bits. Conforme imagem a
seguir, um novo prompt de comando será mostrado:

24
3.2. Nosso primeiro Script
A partir de agora, vamos iniciar nossa jornada na construção dos
scripts em Python, onde o primeiro vai imprimir, na tela, o nosso nome. Com
o comando “mkdir Scripts”, vamos criar a pasta para guardar nossos códigos.
Execute o comando “ls -l” para identificar se ela foi criada.

Agora, vamos chamar o editor Python com o comando “idle-


python3.10”. Vamos criar um novo programa clicando em “File > New File”.
As linhas de código serão criadas nesta nova janela e a primeira caixa pode
ser minimizada:

25
Nesta nova caixa, digite os comandos conforme a imagem a seguir.
Observe que seu nome deve estar entre aspas:

Salve o arquivo criado clicando em “File > Save”. Na janela seguinte,


selecione a pasta “Scripts”, atribua um nome ao seu código e clique em
“Save”. No meu caso, o nome aplicado foi “nome”. Neste momento, é
necessário alterar o tipo de arquivo.

Depois de salvar o arquivo, retorne ao terminal do Kali e abra uma


nova aba de comando, clicando em “File > + New Tab”:

Nesta nova aba, vamos executar o arquivo “nome.py” que acabamos


de criar. Entre na pasta “Scripts” com o comando “cd Scripts/” e depois

26
execute o arquivo através do comando “python nome.py”. A execução do
script imprime, no terminal, o conteúdo da variável “nome”.

Agora, vamos incrementar o script aplicando algumas funções.


Retorne à caixa de edição do arquivo “nome.py” e digite as linhas de código
da imagem a seguir:

Agora, temos elementos bem diferentes do código original. O


objetivo agora é trazer, na tela, a idade de quem interage com o código, por
meio da função input.

Na linha 1, temos a importação da biblioteca “datetime”. No próximo


capítulo, falaremos mais sobre as bibliotecas; por enquanto, entenda que
elas incorporam funções que não são parte do conjunto padrão do Python.

27
Na linha 3, a variável “hoje” recebe o resultado da função
“datetime.date.today()”. Aplicando a biblioteca “datetime” e o classmethod
“date.today()”, o resultado da função retorna à data local atual, com ano, mês
e dia.

Mais detalhes sobre este método em: https://docs.python.org/pt-


br/3/library/datetime.html?highlight=datetime .

Na linha 4, a variável “ano_nascimento” recebe os dados de entrada


do usuário através da função “input”. O texto “Ano de nascimento:” vai
aparecer na tela do terminal. Observe que a função “input” está associada à
função “int”, definindo que o conteúdo gravado na variável será do tipo
inteiro.

Na linha 5, a variável “ano_atual” recebe o resultado da função


“hoje.year”. Nesta função, o método “year” extrai do conteúdo da variável
“hoje” o ano atual. Na linha 6, a variável “idade” que vai receber o resultado
da função aritmética entre os conteúdos das variáveis “ano_atual” e
“ano_nascimento”.

Na linha 8, a variável “resultado” recebe o texto final que vai ser


impresso na tela do terminal. Para concatenar o conteúdo da variável “idade”
com texto simples, observe que precisamos transformá-la em tipo string,
através da função str(). Além disso, para variáveis deste tipo, a concatenação
acontece aplicando o símbolo “+” antes e depois da variável. Na imagem a
seguir, o resultado de execução:

28
3.3. Estruturas de Decisão e Repetição
Aproveitando o script criado, vamos trabalhar as estruturas de
Decisão e Repetição. As de Decisão são “if”, “elif” e “else”. As de Repetição
são “while” e “for”.

3.3.1. Estrutura If/Else

Para a estrutura “if / eles”, vamos adaptar o código anterior e validar


se o usuário que interage tem autorização para dirigir. Abra o código e digite
as linhas de comando conforme imagem. Fique atento à indentação do
código:

Na linha dez, temos a estrutura de decisão que avalia se a idade


calculada do usuário permite ou não que ele dirija, através da expressão
comparativa “idade >= 18”. Dependendo do resultado, textos específicos

29
serão impressos no terminal. Observe que é mantida a impressão do
conteúdo da variável “resultado”, independente do resultado. A imagem a
seguir traz a execução do código.

3.3.2. Estrutura If/Elif/Else

Mantendo a metodologia de análise da idade, vamos ampliar a


estrutura de decisão para incluir o elemento “elif”. A figura a seguir mostra
o novo código. Notem que o nome do arquivo foi alterado para “dirigir.py”:

Observem que agora temos, entre os elementos “if” e “eles”, o “elif”.


Este elemento aplica uma outra avaliação, antes da condição final “ else”.
Para suprir outras demandas de avaliação, o “elif” pode ser aplicado de forma

30
repetitiva na cadeia de decisão. A imagem a seguir traz o resultado de
execução do código:

3.3.3. Estrutura While

Para melhor compreender a estrutura de decisão “while”, vamos criar


um novo script que avalie se a senha entrada pelo usuário é válida. Clicando
em “File > New File”, vamos nomear o arquivo como password.py. Após criar
o arquivo, preencha o código conforme imagem a seguir:

Nas duas primeiras linhas, temos a importação de duas bibliotecas,


“socket” e “getpass”. A biblioteca socket permite orquestrar a comunicação
entre dois computadores, sendo a interface da camada de aplicação para as
demais etapas do modelo TCP.

31
Por sua vez, a biblioteca “getpass” oferece a possibilidade de que o
conteúdo digitado no terminal não apareça para o usuário. Semelhante, por
exemplo, ao que acontece quando, no sistema operacional Linux, digitamos
a senha na linha de comando.

Na terceira linha, definimos o valor inicial para o conteúdo que


queremos aplicar na variável “senha”. Na quarta linha, temos a definição de
um valor nulo para a variável “entrada”. Em sequência, na linha 5, entramos
na estrutura “while” já definindo a condicional de que “enquanto diferentes
os conteúdos armazenados nas variáveis entrada e senha, faça o
seguinte”.

A partir da linha 5, criamos um loop condicional que só será


terminado caso as variáveis entrada e senha sejam iguais. Partindo dessa
definição, as linhas seguintes do código executam funções que estão dentro
da estrutura do “while”. Na linha 6, definimos que a variável “entrada”
receberá conteúdo digitado pelo usuário que interage com o código via
terminal. Aplicando a função “getpass()”, o que for digitado pelo usuário não
aparecerá no terminal.

Na linha 7, entramos numa estrutura de decisão “if / else”, cuja


argumentação (entrada == senha) verifica se a senha digitada pelo usuário
é igual ao conteúdo da variável “senha”. Caso positivo, inicia-se uma
estrutura com as funções da biblioteca “socket”. Na linha 8, armazenamos,
na variável “s”, o resultado da função padrão “socket.socket()”, e, na linha 9,
aplicamos a função “connect()” com os argumentos de destino (8.8.8.8) e
porta (53). A função “connect()” abre uma conexão TCP com o endereço e
porta definidos nos argumentos.

A linha 10 traz o retorno da conexão e armazena, na variável


“ip_address”, o conteúdo na posição zero ([0]) do próprio socket que originou
a conexão. Neste caso, o endereço IP da própria máquina. Na linha 11
seguinte, vamos imprimir, na tela do terminal, a informação de que o acesso

32
foi autorizado, já que o usuário digitou a senha correta, e o endereço IP
guardado na variável “ip_address”. Aqui, vale destacar que o loop “while” é
encerrado. Por fim, nas linhas 12 e 13, temos o código, caso o usuário digite
a senha errada, e onde o loop “while” retorna ao início.

Execução do código na imagem a seguir. Primeiro, quando


propositalmente erramos a senha duas vezes, e depois o resultado quando
a senha está correta.

3.3.4. Estrutura For

Para a estrutura de repetição “for”, faremos um código que vai


avaliar, dentro de um conjunto de portas predefinidas, qual delas está aberta
no computador local. Crie um novo arquivo da mesma forma que o anterior,
salve com o nome “ports.py” e digite o código conforme imagem a seguir:

Semelhante ao código anterior, importamos a biblioteca “socket”


para as funções da cadeia TCP e, já na linha 3, atribuímos à variável portas
um range de portas que serão verificadas na máquina local. Em sequência,
na linha 4, iniciamos a estrutura do “for”, definindo que “para cada porta (p)

33
no range portas, execute o seguinte código....”. Nessa estrutura, o script
vai correr todos os itens dentro da variável “porta” e para cada um deles
executar as linhas de código subsequentes.

Através da estrutura de decisão “if/else” nas linhas de 5 a 8, o código


vai avaliar se é possível criar conexões TCP com a máquina local, utilizando
cada número no range portas. Em primeira tentativa de execução, podemos
observar que o resultado do código traz todas as portas testadas como
“fechadas”.

Como exemplo do estado “Porta aberta”, vamos utilizar a ferramenta


“netcat” para forçar a máquina local a escutar conexões em determinada
porta. Para tanto, abra uma nova aba no terminal do Kali e digite o comando
“netcat -vlp 53”. A parte “-vnlp” são os argumentos de configuração da
ferramenta, e o número 53 representa a porta que queremos escutar.

A figura a seguir mostra o resultado do script após a execução do


“netcat”. Observem que a porta 53 agora é mostrada como “aberta”:

34
4
Capítulo 4. Construção de Códigos em Python
4.1. Reverse Shell
Os sistemas de segurança estão cada vez mais eficientes, atentos a
tudo e a todos. Técnicas de exploração de vulnerabilidades a partir do mundo
externo são alternativas relativamente fáceis de detecção, diminuindo as
chances de sucesso.

Para “burlar” esta segurança, os atacantes desenvolvem métodos


especializados, que permitem iniciar o ataque a partir de uma máquina
dentro do ambiente alvo. Uma vez comprometida, e dependendo do nível de
privilégio acessado, o atacante pode ter todos os ativos da rede a sua
disposição. Mas como controlar a máquina remotamente sem ser percebido?

Para responder a essa pergunta, vamos conhecer o “Reverse Shell”


com base no protocolo TCP. Basicamente, o “Reverse Shell” é um código
malicioso implantado na máquina da vítima, que vai originar conexões para
fora do ambiente, com destino a uma máquina externa. Esse destino externo
é a máquina do atacante, que, por sua vez, terá domínio sobre a máquina da
vítima. A imagem a seguir ilustra está dinâmica de funcionamento do
“Reverse Shell”:

36
Em um cenário hipotético, do lado esquerdo da figura, temos a
máquina do atacante conectada à internet. No centro da imagem, temos o
dispositivo firewall que protege e faz a interface entre o barramento e a
internet. Esse barramento representa a rede interna do ambiente onde estão
a máquina alvo e outros servidores.

Alternativas de ataque de fora para dentro são muito complexas de


conseguir. São muitos os elementos no caminho, incluindo o firewall que
tende a bloquear ataques tráfegos suspeitos. Mas e se o ataque acontecer
de dentro do ambiente?

Imagine que um código malicioso foi recebido via e-mail pelo


usuário da máquina alvo, embutido em um suposto arquivo PDF. Sem saber,
o usuário pode abrir o arquivo e instalar o malware. A execução deste
malware cria a porta de entrada do atacante através do Reverse Shell.

Neste capítulo, vamos criar um mecanismo “Reverse Shell”, em


Python, para atacar uma máquina com sistema operacional Windows 10. O
código “Reverse Shell” será dividido em duas partes. Na primeira parte,
faremos o desenvolvimento do script relativo ao lado do atacante, também
denominado lado servidor. Na segunda parte, vamos tratar do código que vai
rodar na máquina alvo.

4.2. Bibliotecas
Antes de iniciar a construção efetiva do script para o “Reverse Shell”,
vamos entender um pouco sobre as bibliotecas e suas aplicações para o
código.

Em Python, as bibliotecas funcionam como uma memória que


armazena funções pré-definidas, as quais podem ser aplicadas e reaplicadas
em qualquer ponto no código. Algumas já são parte do perfil padrão do
Python, como a “math” para funções matemáticas, ou a “time” para funções
de tempo.

37
Além dessas bibliotecas padrões, outras também podem ser
importadas para compor as funções que o código precisa. É o caso da
“socket” e da “datetime” que aplicamos no capítulo anterior. Para importar
uma biblioteca ao código, no início do script, entramos com o comando
“import nomedabiblioteca”. Mais detalhes sobre bibliotecas em Python no
link https://docs.python.org/3/library/.

4.3. Reverse Shell – Primeira Parte


Abrindo o editor de Python no Kali, vamos criar o arquivo
“reverse_server.py”. Depois de criado, entre com as linhas de código
conforme imagem a seguir:

Na linha 2, importamos da biblioteca “socket”, e, na linha 3, a


biblioteca “sys”. A “sys” agrega funções que permitem lidar com variáveis do
próprio interpretador Python. No caso do nosso script, vai auxiliar na
configuração de parâmetros da linha 5. Com a função “tracebacklimit”
ajustada para “0”, apenas eventuais erros de execução de código serão
apresentados no terminal.

38
Na linha 8, definimos a função “conexao”, que vai permitir ao script
escutar por demandas de conexões vindas de fora, além de possibilitar o
envio de comandos para a máquina alvo. Ainda através desta função, vamos
receber os resultados dos comandos digitados pelo atacante.

Na linha 9, atribuímos, ao elemento “s”, os resultados da função


“socket”. Este artifício permite que a expressão não tenha de ser repetida ao
longo do código, facilitando a construção. Na linha 10, associamos ao
“socket” o endereço IP do Kali, “10.10.10.241” e a porta “4444” para escutar
conexões vindas de fora. O endereço IP depende do seu ambiente de rede e
a porta fica a critério.

Na linha 11, associamos ao “socket” a função “listen()”, na qual


vamos escutar as conexões vindas de fora. O parâmetro de configuração “1”
indica quantas vezes vamos escutar.

Na linha 12, criamos duas variáveis para armazenar dados das


conexões externas. A variável “s_remoto” vai guardar detalhes sobre o
“socket” originado no lado da máquina alvo, e a variável “ip_remoto” vai
receber o respectivo endereço IP. Esses valores serão recebidos pela função
“accept()”, associada ao “socket” criado. Na linha 13, vamos imprimir na tela
do terminal a informação de origem da conexão.

Passando para a segunda parte da função “conexao”, a partir da linha


16, temos a estrutura de repetição “while”, aplicando como argumento o
termo “True”. Isso permite criar o que chamamos de loop infinito, onde a
condição de entrada no loop será sempre atendida (verdadeira). Já dentro
da estrutura, na linha 17, atribuímos à variável “comando” tudo que for
digitado pelo atacante através da função “input”.

Na linha 18, criamos uma estrutura de decisão para permitir


encerramento do loop “while”, caso o conteúdo da variável “comando” seja
“sair”. Uma vez atendida tal condição, o script encerra a conexão com o alvo

39
e envia, para a máquina remota, a palavra “sair”. Isso acontece através dos
comandos da linha 16, utilizando a função “send()” associada ao socket
remoto “s_remoto”. Neste ponto, observe que associamos ao termo “sair” a
função “encode()”. Isso faz com que a palavra “sair” seja codificada em bytes
inteligíveis para a transmissão à máquina alvo.

Na linha 20, terminamos a conexão com a socket remoto associando


a função “close()” e, na linha 18, encerramos o loop “while” aplicando o
elemento “break”. Na linha 23, temos a estrutura de decisão “else”. Dentro
dela, enviaremos comandos codificados ao socket remoto do alvo
(“s_remoto”) e, linha 24, vamos imprimir, no terminal, os resultados dos
comandos digitados. Na linha 26, observe que aplicamos duas novas
funções ao socket remoto “s_remoto”. A função “recv()” estabelece o
tamanho máximo de dados que podem ser recebidos através do socket, e a
função “decode()” transforma os bytes recebidos em palavras inteligíveis.
Como argumentos para esta última função, definimos que o código vai
ignorar erros de decodificação no padrão UTF-8. Por fim, na linha 29, o
código executa a função “conexao”.

Execute o script com o comando “python3 reverse_server.py”. Para


verificar se o Kali está escutando a porta definida, “4444”, em outra aba de
terminal, execute o comando “netstat -na | more”. Observe que, na linha
marcada, temos a confirmação:

40
4.4. Reverse Shell – Segunda Parte
A partir deste ponto, vamos criar a segunda parte do código já no
editor de Python dentro do Windows 10. No editor, crie um novo arquivo e
salve como “reverse_client.py”. Entre com o código conforme imagem a
seguir:

Na linha 1, importamos da biblioteca “socket” e, na linha 2, a


“subprocess”. A biblioteca “subprocess” agrega funções para executar
subprocessos do sistema operacional da máquina. No caso deste script,
permitirá execuções em linha de comando, como o CMD do Windows, bem
como receber os resultados execuções.

Na linha 6, definimos a função “conexao”, semelhante ao código na


primeira parte do Reverse Shell. Na linha 7, temos o elemento “socket”; na
linha 8, associamos a função “connect()” com os argumentos de endereço e
porta da máquina do atacante. Lembrando que a porta “4444” foi definida
na primeira parte do script.

Na linha 9, entramos no loop infinito da estrutura de repetição


“while”. Na linha 10, definimos a variável “comando” para armazenar os
comandos vindos da máquina do atacante, associando ao “socket” com a
função “recv()” e limite de 1024 bytes.

41
Na linha 11, entramos na estrutura de decisão, avaliando se o
comando recebido inclui o termo “sair”. Caso positivo, o socket é encerrado,
na linha 12, bem como o loop “while” com o recurso “break”, linha 13.
Observem que, na linha 11, associamos à variável “comando” a função
“decoce()”, que vai transformar os bytes recebidos em uma linha linguagem
inteligível.

Na estrutura do “else”, a partir da linha 14, definimos ao elemento


“SHELL” os resultados do subprocesso de linha de comando, linha 16. O
subprocesso está associado à função “Popen()”, na qual declaramos os
argumentos que vão orientar o uso da linha de comando.

O primeiro argumento “comando.decode()” define o conteúdo que


queremos aplicar na linha de comando. A função “decode()” vai transformar
os bytes vindos da máquina do atacante em linguagem inteligível. O
segundo argumento, “shell = True”, define que os elementos do subprocesso
serão aplicados ao Shell do Windows. O terceiro argumento “stdout =
subprocess.PIPE” atribui ao elemento especial “stdout” os resultados
positivos dos comandos aplicados à linha de comando. Por sua vez, o quarto
argumento “stderr = subprocess.PIPE” atribui ao elemento especial “stderr”
os resultados de comandos não reconhecidos pela linha de comando.

Nas linhas 17 e 18, através da função “send()” associada ao socket,


o script envia à máquina do atacante os resultados dos comandos
executados no Shell do Windows. Observem que cada elemento especial
(“stdout” ou “stderr”) está associado à função “read()”, orientando que o
conteúdo seja lido pelo destino. Por fim, na linha 21, o script vai “chamar” a
função “conexão()”.

4.5. Reverse Shell – Testando o Script


Para testar o script criado, vamos retornar ao terminal do Kali e
chamar a primeira parte do código. Execute o comando “python3
reverse_server.py”:

42
Na sequência, já no lado do Windows 10, vamos rodar a segunda
parte do código. No menu superior do terminal de edição, cliquem “ Run >
Run Module”:

Observem que o terminal do Kali indica que uma conexão foi


estabelecida com as informações de endereço IP e porta que deram origem
à conexão. Isso indica que o Shell Reverse foi estabelecido.

No terminal do Kali, vamos entrar com o comando básico de


identificação de rede para validar a funcionalidade do código. Ao digitar o
comando “ipconfig”, remotamente temos acesso às informações de
configuração de rede do alvo.

43
5
Capítulo 5. Introdução à Linguagem Bash Script
Assim como o Shell, o Bash (Bourne Again Shell) é um programa
interpretador de comandos que faz a ligação entre o usuário e o Kernel do
sistema operacional. Por sua vez, o Kernel é quem acessa os elementos de
hardware da máquina. Juntos, Bash e Kernel oferecem ao usuário uma
interface bastante eficiente de execução de comandos.

5.1. Primeiro Script em Bash


Para criar o primeiro script em Bash, vamos abrir o terminal do Kali
Linux e, através da linha de comando, navegar até a pasta Scripts criada nos
capítulos anteriores e executar o comando “vi sistema”:

O comando “vi” é o editor que vamos utilizar para criar o script


“sistema”. Caso sua versão do Kali não tenha o “vi”, basta instalar com o
comando “sudo apt install vim-tiny”. Fique à vontade para aplicar qualquer
editor de sua preferência.

O editor vai abrir um console de edição vazio. Para facilitar a


interação com a ferramenta, digite o comando “:set number” para exibir, no
console, a numeração das linhas de código. Na sequência, pressione a tecla
“i” no teclado para entrar no modo de inserção, depois digite o código
conforme imagem a seguir:

45
Para sair do modo de inserção, pressione a tecla “Esc” no teclado.
Neste momento, observe que a linha de execução de comandos do editor
passa para a parte inferior do console. Digite o comando “:wq” para salvar o
arquivo e sair da ferramenta.

De volta ao terminal, vamos transformar o arquivo “sistema” em um


elemento executável como comando “chmod +x sistema”. Para executar o
código, digite “./sistema”. Resultado de execução:

Na linha 1, temos o código “#!/bin/bash”. Esta linha vai indicar, ao


sistema operacional, que o código tem de ser interpretado pelo Bash. Nas
linhas 2, 3 e 4, temos o comando “echo”, que vai imprimir, na tela, o texto
que estiver entre aspas (“”). Na linha 5, temos o comando “date”, que vai
trazer informações de data e hora do sistema. Na linha 6, indicamos
novamente, com o comando “echo”, o que vai ser impresso no terminal. Na
linha 7, executamos o comando “df”, que vai trazer informações sobre uso
de disco do sistema e, na linha 9, executamos o comando “w”, que vai trazer
detalhes sobre os usuários logados na máquina.

5.2. Melhorando o Script


Para incrementar o script, vamos aplicar as estruturas de Condição
e de Repetição.

46
5.2.1. Estrutura de Decisão

Abra o arquivo “sistema” com o console de edição do “vi”. Através do


comando “vi sistema”, adicione as linhas de código conforme imagem a
seguir:

A partir da linha 3, a variável “RESPOSTA” recebe o valor de entrada


que será informado pelo usuário, conforme a pergunta na linha 2. Na linha
4, aplicamos a estrutura condicional “if/then/else”, onde a condição de
verificação avalia se o conteúdo da variável “RESPOSTA” é diferente do valor
“s”. Nesta linha, observe que o símbolo “≠” não é nativo do teclado. O mesmo
aparece de forma automática quando digitamos “!=”. Na linha 17, temos o
marcador “fi” que delimita a estrutura condicional.

5.2.2. Estrutura de Repetição

Para demonstrar a estrutura de repetição, vamos criar um novo


arquivo chamado “ping_test”. Neste script, vamos utilizar o comando “ping”
dentro da estrutura de repetição “FOR”. Entre com as linhas de código,
conforme imagem a seguir:

47
Partindo da linha 10, o código vai questionar, ao usuário,
informações de endereço e quantidade para a execução do ping. Na linha 15,
apenas para tornar o script mais interativo, temos o comando “sleep”, que
vai aguardar 2 segundos para continuar a execução do script.

Na linha 16, entramos na estrutura de repetição do “FOR”. Aplicamos


a variável “n” como elemento contador e utilizamos a função “seq” para gerar
uma sequência numérica com base no conteúdo da variável “VALOR”,
informado pelo usuário na linha 13. Nas linhas 17 e 19, temos os marcadores
“do” e “done”, que delimitam a execução do “FOR” e, na linha 18, temos o
comando ping propriamente dito, onde o destino será o conteúdo da variável
“ENDERECO”.

48
6
Capítulo 6. Construção de Código Ofensivo em Bash Script
Como parte em qualquer projeto de teste de intrusão, a fase inicial
de reconhecimento do alvo talvez seja a mais importante de todas. Através
dela, vamos obter as características operacionais do objeto de ataque, bem
como identificar pontos de vulnerabilidade que possam ser explorados.

Combinados com outras ferramentas, os códigos em Bash Script


podem e devem auxiliar nesta tarefa. Normalmente intuitivos e de fácil
implementação, são alternativas bastante práticas de apoio ao trabalho do
Hacker Ético.

Neste capítulo, vamos construir um código utilizando os recursos do


Bash Script, integrado a outras ferramentas já padronizadas, com o objetivo
de descobrir elementos ativos na rede e encontrar portas de conexão
abertas.

6.1. Comandos Iniciais


Para auxiliar na fase inicial de construção do nosso código, vamos
aplicar as ferramentas “ifconfig”, “grep” e “cut” para filtrar resultados e obter
as informações específicas que queremos sobre a rede local.

No terminal do Kali Linux, digite o seguinte comando: “ifconfig | grep


"broadcast" | cut -d " " -f 10 | cut -d "." -f 1,2,3”. Resultado do comando:

Neste primeiro conjunto de comandos, o “ifconfig” traz as


informações de endereçamento IP das interfaces configuradas da máquina.
Com o artifício do elemento “pipe” “|” e da ferramenta “grep”, os resultados

50
do “ifconfig” serão filtrados pelo termo “broadcast”. Por sua vez, o conteúdo
decorrente deste filtro será submetido à ferramenta “cut”, onde, através do
delimitador “n”, vamos restringir o resultado ao campo 2. Na sequência,
aplicamos novamente a ferramenta “cut” com o delimitador “ ” e novamente
o campo 2. Por fim, uma terceira aplicação do “cut” com delimitador “.” e
campos 1,2 e 3 vai trazer o endereço de rede que estamos buscando.

Como segunda parte dos comandos iniciais, digite o seguinte: “ping


-c 1 xx.xx.xx.xx | grep "64" | cut -d " " -f 4 | cut -d ":" -f 1”. Resultados do
comando:

Neste segundo conjunto, aplicamos a ferramenta “ping” associada


ao argumento “-c 1”, onde delimitamos em uma quantidade de pings a serem
realizados. Na sequência, aplicamos as ferramentas “grep” e “cut” para
encontrar o endereço IP que buscamos.

OBS.: Os endereços IP utilizados nesta demonstração vão depender


do seu ambiente de virtualização.

6.2. Construindo o Código – Primeira Parte


Para construir o código de verificação de rede em Bash Script, vamos
abrir o terminal do Kali Linux, entrar no diretório Script e criar o arquivo
“network” com o comando “vi network”. Uma vez dentro do editor, pressione
a tecla “i” do teclado para entrar no modo de inserção e entre com o código
conforme imagem a seguir:

51
Por padrão, na linha 1, temos o código que define a execução via
Bash. Na linha 4, importamos o primeiro conjunto de comandos que vimos
no item 6.1 anterior. A diferença é que, ao final da linha, temos o elemento
“>”, que vai guardar no arquivo “rede” os resultados do comando.

Na linha 7, temos a variável “REDE” que vai armazenar o conteúdo


lido do arquivo “rede” com a ferramenta “cat”. Na linha 10, temos a estrutura
do “FOR”, onde cada número da sequência de 1 a 254 fará parte do código
dentro do loop.

Na linha 12, aplicamos o segundo conjunto de comandos que


também aprendemos no item 6.1. A diferença é que, no lugar do endereço
IP do alvo, temos a junção de dois elementos, a variável “REDE”, definida na
linha 7, e a variável “ip”, que é parte da estrutura “FOR” na linha 10. Além
disso, ao final desta linha 12, temos o elemento “>>” que vai atualizar o
arquivo “resultado” com os resultados do comando ping, e temos o caractere
“&”, que vai fazer o “FOR” executar os loops todos ao mesmo tempo.

Por fim, na linha 15, vamos ler o conteúdo do arquivo “resultado”,


criado no comando da linha 12. Resultado da execução:

52
6.3. Construindo o Código – Segunda Parte
Nesta segunda parte, vamos levar os resultados da primeira parte
para dentro da ferramenta NMAP, com o objetivo de fazer a varredura de
portas. Editando o arquivo “network” com o “vi”, digite o código conforme
imagem a seguir:

Na linha 16, temos a integração do nosso código com a ferramenta


Nmap. Com ela, vamos scanear as portas de conexão 22, 80, 443 e 53 em
cada endereço listado no arquivo “resultado”.

53
7
Capítulo 7. Introdução à Engenharia Reversa
7.1. Introdução
Os ataques de códigos maliciosos estão cada vez mais refinados e
complexos, tornando o trabalho das equipes de segurança um desafio árduo.
Em alguns casos, dependendo do resultado do ataque, as vítimas não
“gastam” esforços na investigação do código.

Do ponto de vista técnico e de aprendizado para evitar eventos


reincidentes, este “descaso” não é uma iniciativa recomendada. O ideal é
buscar o máximo de detalhes sobre o ocorrido e criar uma base de
conhecimento que permita consultas futuras para respostas mais
eficientes.

Neste sentido, a Engenharia Reversa é uma técnica de intervenção


investigativa para o estudo do comportamento dos códigos maliciosos,
permitindo avaliar o passo a passo do código sem o risco de infecção ou
prejuízo ao ambiente real de operação.

O processo investigativo identifica, isola e transfere para o ambiente


de simulação os arquivos infectados. Este novo ambiente deve estar
totalmente desconectado do ambiente de produção, porém deve ser o mais
parecido com este último, para que o código malicioso tenha o
comportamento pretendido.

Normalmente, sistemas virtualizados são aplicados para esta


simulação. Todavia, vale destacar que já existem códigos maliciosos capazes
de identificar que a máquina alvo está operando em uma plataforma
virtualizada. Isso pode fazer com que o código não tenha o comportamento
esperado, prejudicando a análise. Por este motivo, em alguns casos,
recomenda-se o uso de máquina física.

55
7.2. Ferramenta Ghidra
Felizmente, existem ferramentas que auxiliam no trabalho de
Engenharia Reversa. Neste item, vamos conhecer um pouco da ferramenta
GHIDRA (https://ghidra-sre.org/), fazendo sua instalação no Windows 10
virtualizado.

Acesse o site e clique em “Download from GitHub”. O navegador será


direcionado para a página da ferramenta no repositório GitHub. Faça o
download do arquivo .zip, conforme imagem a seguir:

Ao término do download, faça a extração da pasta raiz na unidade


C:\. Isso vai facilitar a execução via linha de comando.

Para executar o Ghidra, também será necessário instalar os


componentes Java Runtime e Java Development Kit. Para o Java Runtime,
acesse https://java.com/pt-BR/, faça o download e instale. Para o Java
Development Kit, acesse https://www.oracle.com/java/technologies/java-se-
development-kit11-downloads.html, faça o download da versão executável
e instale. Atenção: para este último download, o site da Oracle vai demandar
a criação de uma conta.

Para rodar a ferramenta, abra o CMD do Windows, navegue até a


pasta crida e execute o arquivo “ghidraRun”. Após alguns segundos, um

56
terminal de inicialização vai aparecer na tela e, na sequência, o console de
interação da ferramenta.

7.3. Analisando Código com Ghidra


Para demonstrar a utilização do Ghidra, vamos fazer a análise do
arquivo crackme0x00.exe disponível para download no link de arquivos
complementares do ambiente de ensino.

Vamos criar um novo projeto clicando em “File>New Project...”. Deixe


marcada a opção “Non-Shared Project” e clique em “Next >>”. Na janela
seguinte, defina o diretório que deseja salvar o projeto e também defina um
nome para ele. Para o nosso exemplo, vamos deixar o diretório padrão e
definir o nome “projeto 01”. Clique em “Finish” e vamos ver o projeto criado.

No painel principal, deixe o projeto selecionado e vamos importar o


arquivo crackme0x00.exe. Clique em “File>Import File”, navegue até a pasta
onde fez o download e selecione o arquivo clicando em “Select File To
Import”. Automaticamente, o Ghidra vai reconhecer o arquivo e apresentar
uma janela de detalhes. Observe que, em “Language”, a ferramenta já
identifica o padrão de codificação do Windows.

57
Clicando em “OK”, o Ghidra inicia a importação. A janela seguinte
traz detalhes sobre o processo e algumas informações adicionais.
Novamente clicando em “OK”, seguimos para próxima etapa.

O arquivo crackme0x00.exe é um código simples que demanda uma


senha para ser aberto. Entretanto, antes de executá-lo, vamos desativar a
comunicação de rede da VM no VirtualBox para isolar o Windows do restante
da rede. Abra o console do VirtualBox, selecione a VM Windows 10, clique
em Configurações e depois em Rede. Na caixa de seleção “Conectado a”,
selecione “Não conectado”.

Voltando à análise do executável, abra o CMD do Windows e navegue


até o diretório onde fez o download. Vamos abrir o arquivo apenas digitando
seu nome “crackme0x00.exe”. Quando executado, observem que o terminal
apresenta um texto demandando uma senha específica para acesso. Esta é
a senha que vamos descobrir aplicando a Engenharia Reversa com o Ghidra.

58
Como pista inicial para a investigação, vamos buscar pelo termo
“Invalid Passwor!”, já que aparece no terminal quando digitamos uma senha
incorreta. No console do Ghidra, vamos dar dois cliques no arquivo que
queremos verificar e um novo terminal do “CodeBrowser” será mostrado.
Uma janela de análise informa que o arquivo crackme0x00.exe ainda não foi
analisado e pergunta se queremos analisá-lo:

Clicando em “Yes”, uma nova janela com opções de análise será


exibida. Neste momento, vamos manter o padrão já selecionado pelo Ghidra.
Na sequência, a ferramenta vai avaliar o arquivo com base nas opções de
análise da tela anterior. Terminada a verificação, vamos clicar no menu
“Window>Defined Strings” para ver as “strings” coletadas pela ferramenta
durante a depuração do código.

59
Nas duas linhas destacadas, temos valores de “strings” bastante
interessantes. Na primeira, vemos o termo “Invalid Password”, e, na
segunda, algo ainda melhor: a expressão “Password OK”. Vamos avançar com
a segunda linha e dar um duplo clique nela. Neste momento, notem que, na
janela “Listing”, a ferramenta marca em azul uma linha com a mesma
referência de localização (00404041).

Nosso objetivo agora será buscar outras posições no código, onde


essa referência de localização apareça novamente. Na janela “Listing”, clique
com o botão direito na linha marcada e selecione “References>Show
References To Address”. Uma nova janela será aberta, indicando que uma
referência foi identificada. Clicando nesta linha da nova janela, a ferramenta
nos leva para a posição encontrada:

Nesta nova porção do código, observamos três elementos de função


que devemos investigar. O primeira está na posição “0040138a”, onde
encontramos o termo que nos trouxe até aqui (Password_OK), o segundo
está na posição “0040137c”, onde temos a expressão “Invalid_Password”, e
a terceira está na posição “00401378”, onde temos uma movimentação do
tipo “TEST”.

60
Para depurar este elemento “TEST”, vamos selecionar sua linha
respectiva e, no menu superior, clicar em “Window>Decompiler”. A janela do
“Decompiler” traz uma estrutura de código com elementos que já
conhecemos de outros capítulos. Vamos analisar as linhas 11 a 21:

Nas linhas 11 e 12, temos funções de impressão para a interação de


usuário no terminal. Na linha 13, temos outra função (scanf) com dois
argumentos, “%s” e “local_c”. Notem que, na linha 14, este mesmo
argumento aparece na função “strcmp”. Ainda sobre esta linha, a variável
“iVarl” recebe o resultado da função “strcmp”, que parece ser a comparação
entre dois argumentos (local_lc e “250382”). Na linha 15, a estrutura de
decisão “if/else” verifica se a variável “iVarl” tem valor zero. Caso positivo, o
código imprime a expressão “Password OK”.

Depurando as linhas, é nítido que a condição de senha correta


depende do valor da variável “iVarl”, e esta resulta da função “strcmp”.
Supondo que o argumento “local_lc” é o conteúdo de entrada digitado pelo
usuário no terminal, vamos testar se a comparação feita pela função é uma
operação de subtração com o valor “250382”:

61
Ao executar o código novamente, e entrar com o valor “250382”,
comprovamos que a função era mesmo de subtração.

62
8
Capítulo 8. Códigos Maliciosos
Também conhecidos com malwares, os códigos maliciosos são
softwares, ou pedaços de código, desenvolvidos para danificar, perturbar,
roubar ou infligir qualquer má ação em dados, redes ou dispositivos finais.

Esse tipo de código é frequentemente utilizado pelos atacantes,


pois, uma vez instalado na máquina alvo, permite explorar vulnerabilidades
não só na máquina comprometida, como também nos dispositivos aos quais
ela tem contato. Além disso, os malwares podem se “transformar” de forma
muito rápida, tornando difícil sua identificação e combate.

8.1. Tipos de Malware


8.1.1. Vírus

Vírus são códigos maliciosos que se espalham fazendo cópias de si


mesmo para infectar outros programas. Normalmente dependentes de
ações do usuário, por exemplo a instalação de um executável ou conexão de
uma unidade USB, os vírus podem ficar inativos por determinado período e
entrar em ação em momento pré-programado.

Quando ativos, podem infectar unidades de disco, arquivos,


computadores etc. Também pode podem modificar sua estrutura de código
para não serem detectados por plataformas antivírus.

Boa parte dos vírus são espalhados através de unidades USB, CDs, e-
mails e compartilhamentos de rede.

8.1.2. Cavalos de Tróia

Trazido da mitologia grega, o termo Cavalo de Troia, faz alusão a algo


que parece legítimo, mas que esconde uma ameaça. Para o mundo de
segurança da informação, seria um software supostamente confiável, mas
que está contaminado com um código malicioso.

64
Podem ser encontrados em jogos online, instaladores de aplicativos
e arquivos/programas baixados da internet. Normalmente se instalam e
atuam em background sem a percepção do usuário.

Podem se tornar porta de entrada para atacantes externos, ou


mesmo ser aponte de envio de dados críticos para o atacante na internet.

8.1.3. Worms

Semelhantes aos vírus, os Worms (vermes) também se replicam e


infectam outros dispositivos, com a diferença de que não precisam de ação
do usuário, ou de um programa hospedeiro.

Explorando vulnerabilidades em dispositivos de rede, os Worms


podem causar lentidão ou mesmo interrupção das operações de uma rede.

8.1.4. Ransomware

Pode-se dizer que o ransomware é a evolução dos códigos


maliciosos. Vetor de ataque mais comum nos dias de hoje, o ransomware,
tem como principal ação o sequestro de dados da máquina alvo, através da
aplicação de uma função criptográfica que impede o acesso aos dados
capturados.

Normalmente, o atacante só “devolve” os dados mediante


pagamento de resgate. Geralmente, os pagamentos são feitos em
criptomoedas não rastreáveis. O fato é que o retorno dos dados acaba
dependendo dos interesses do próprio atacante, independente do
pagamento do resgate.

Os ataques de ransomware também criam a condição de Dupla


Extorsão. Com a LGPD (Lei Geral de Proteção de Dados,
https://www.planalto.gov.br/ccivil_03/_ato2015-2018/2018/lei/l13709.htm)
em vigor aqui no Brasil, o vazamento de informações críticas pode trazer
sérias consequências para as empresas. Os atacantes se valem desta

65
condição para também extorquir as empresas pela não divulgação dos
dados.

8.1.5. Outros Tipos

A tabela a seguir relaciona os tipos mais comuns de malware:

Tipos de
Descrição
Malware
Aplicado para obter informações sem o consentimento ou conhecimento da
Spyware
vítima. Os dados são enviados para outro destino.

Aplicado para mostrar na tela do usuário avisos e propagandas indesejadas.


Adware Normalmente são baseados em assuntos de interesse da vítima, identificados
pela monitoração de comportamento online do alvo.

Aplicado para assustar ou influenciar a vítima na execução de determinada


Scareware
ação de interesse do atacante.

Aplicado para ludibriar a vítima, com o objetivo de obter informações


Phishing
sensíveis.

Aplicado para comprometer o sistema da vítima, criando portas de acesso não


Rootkits detectadas pelos sistemas de segurança, mas que promovem pleno acesso
remoto ao atacante.

A cada dia um novo código malicioso é desenvolvido, tanto para


burlar os sistemas de segurança, quanto para criar novos mecanismos de
comprometimento e exploração de vulnerabilidades.

66
Referências
[1] Lógica de Programação – A Construção de Algoritmos e Estruturas de
Dados - Forbellone, André Luiz Villar – MAKRON – 1993.

[2] Bash Reference Manual – Reference Documentation for Bash: RAMEY,


Chet – FOX, Brian – 2020.

[3] Penetration Testing with the Bash shell – Get Started with Shell for
Penetration Testing – MAKAN, Keith – 2014.

[4] Testes de invasão: Uma introdução prática ao hacking – WEIDMAN,


Georgia – 2017.

[5] Python for Offensive PenTest: A practical guide to ethical hacking and
penetration testing using Python – KHRAIS, Hussam – 2018.

[6] Ghidra Software Reverse Engineering for Beginners: Analyze, identify,


and avoid malicious code and potential threats in your networks and
systems – DAVID, A. P. – 2021.

[7] A Guide to the Project Management Body of Knowledge (PMBOK®


Guide)–Sixth Edition Sixth Edition, Sixth edition – PMI, Project Management
Institute – 2017.

[8] NIST Cybersecurity Framework: A pocket guide – CALDER, Alan – 2018.

67

Você também pode gostar