Python Forensics - Guia rápido

Python é uma linguagem de programação de propósito geral com código fácil e legível que pode ser facilmente compreendido tanto por desenvolvedores profissionais quanto por programadores novatos. Python é composto por muitas bibliotecas úteis que podem ser usadas com qualquer estrutura de pilha. Muitos laboratórios contam com Python para construir modelos básicos para previsões e para executar experimentos. Também ajuda a controlar sistemas operacionais críticos.

O Python possui recursos integrados para apoiar a investigação digital e proteger a integridade das evidências durante uma investigação. Neste tutorial, vamos explicar os conceitos fundamentais da aplicação de Python em análise forense digital ou de computação.

O que é computação forense?

Computational Forensics é um domínio de pesquisa emergente. Ele lida com a resolução de problemas forenses usando métodos digitais. Ele usa ciência computacional para estudar evidências digitais.

Computação Forense inclui uma ampla gama de assuntos que tem objetos, substâncias e processos investigados, principalmente com base em evidências de padrões, como marcas de ferramentas, impressões digitais, impressões de sapatos, documentos etc., e também inclui padrões fisiológicos e comportamentais, DNA e evidências digitais em cenas de crime.

O diagrama a seguir mostra a ampla gama de assuntos cobertos em Computational Forensics.

A ciência forense computacional é implementada com a ajuda de alguns algoritmos. Esses algoritmos são usados ​​para processamento de sinais e imagens, visão computacional e gráficos. Também inclui mineração de dados, aprendizado de máquina e robótica.

A ciência forense computacional envolve diversos métodos digitais. A melhor solução para facilitar todos os métodos digitais em ciência forense é usar uma linguagem de programação de propósito geral como Python.

Como precisamos do Python para todas as atividades de análise forense computacional, vamos avançar passo a passo e entender como instalá-lo.

Step 1 - Vá para https://www.python.org/downloads/ e baixe os arquivos de instalação do Python de acordo com o sistema operacional que você possui em seu sistema.

Step 2 - Depois de baixar o pacote / instalador, clique no arquivo exe para iniciar o processo de instalação.

Você verá a tela a seguir após a conclusão da instalação.

Step 3 - A próxima etapa é definir as variáveis ​​de ambiente do Python em seu sistema.

Step 4 - Depois de definir as variáveis ​​de ambiente, digite o comando "python" no prompt de comando para verificar se a instalação foi bem-sucedida ou não.

Se a instalação foi bem-sucedida, você obterá a seguinte saída no console.

Os códigos escritos em Python são bastante semelhantes aos códigos escritos em outras linguagens de programação convencionais, como C ou Pascal. Também é dito que a sintaxe do Python é fortemente emprestada de C. Isso inclui muitas das palavras-chave do Python que são semelhantes à linguagem C.

Python inclui instruções condicionais e em loop, que podem ser usadas para extrair os dados com precisão para fins forenses. Para controle de fluxo, ele forneceif/else, while, e um alto nível for instrução que faz um loop sobre qualquer objeto "iterável".

if a < b: 
   max = b 
else: 
   max = a

A principal área em que o Python difere de outras linguagens de programação é no uso de dynamic typing. Ele usa nomes de variáveis ​​que se referem a objetos. Essas variáveis ​​não precisam ser declaradas.

Tipos de dados

Python inclui um conjunto de tipos de dados embutidos, como strings, booleano, números, etc. Existem também tipos imutáveis, o que significa que os valores não podem ser alterados durante a execução.

Python também possui tipos de dados integrados que incluem tuples que são matrizes imutáveis, lists, e dictionariesque são tabelas hash. Todos eles são usados ​​em análise forense digital para armazenar valores durante a coleta de evidências.

Módulos e pacotes de terceiros

Python suporta grupos de módulos e / ou pacotes que também são chamados third-party modules (código relacionado agrupado em um único arquivo de origem) usado para organizar programas.

Python inclui uma extensa biblioteca padrão, que é uma das principais razões de sua popularidade na computação forense.

Ciclo de vida do código Python

  • A princípio, quando você executa um código Python, o interpretador verifica se há erros de sintaxe no código. Se o interpretador descobrir quaisquer erros de sintaxe, eles serão exibidos imediatamente como mensagens de erro.

  • Se não houver erros de sintaxe, o código é compilado para produzir um bytecode e enviado para PVM (Python Virtual Machine).

  • O PVM verifica o bytecode para qualquer tempo de execução ou erros lógicos. No caso de o PVM encontrar quaisquer erros de tempo de execução, eles são relatados imediatamente como mensagens de erro.

  • Se o bytecode estiver livre de erros, o código será processado e você obterá sua saída.

A ilustração a seguir mostra de maneira gráfica como o código Python é interpretado pela primeira vez para produzir um bytecode e como o bytecode é processado pelo PVM para produzir a saída.

Para criar um aplicativo de acordo com as diretrizes forenses, é importante compreender e seguir suas convenções e padrões de nomenclatura.

Convenções de Nomenclatura

Durante o desenvolvimento de aplicativos Python forensics, as regras e convenções a serem seguidas são descritas na tabela a seguir.

Constantes Letras maiúsculas com separação de sublinhado TEMPERATURA ALTA
Nome da variável local Minúsculas com tampas irregulares (sublinhados são opcionais) currentTemperature
Nome da variável global Prefixo gl minúsculo com maiúsculas salientes (sublinhados são opcionais) gl_maximumRecordedTemperature
Nome das funções Letras maiúsculas com pontas salientes (sublinhados opcionais) com voz ativa ConvertFarenheitToCentigrade (...)
Nome do objeto Prefixo ob_ minúsculo com maiúsculas salientes ob_myTempRecorder
Módulo Um sublinhado seguido por minúsculas com maiúsculas salientes _tempRecorder
Nomes de classes Prefixe class_ e depois capas irregulares e seja breve class_TempSystem

Tomemos um cenário para entender a importância das convenções de nomenclatura na Computação Forense. Suponha que temos um algoritmo de hash que normalmente é usado para criptografar dados. O algoritmo de hash unilateral recebe a entrada como um fluxo de dados binários; pode ser uma senha, um arquivo, dados binários ou quaisquer dados digitais. O algoritmo de hashing então produz ummessage digest (md) em relação aos dados recebidos na entrada.

É praticamente impossível criar uma nova entrada binária que irá gerar um determinado resumo da mensagem. Mesmo um único bit dos dados de entrada binários, se alterado, irá gerar uma mensagem única, que é diferente da anterior.

Exemplo

Dê uma olhada no programa de amostra a seguir, que segue as convenções mencionadas acima.

import sys, string, md5   # necessary libraries
print "Please enter your full name"
line = sys.stdin.readline()
line = line.rstrip()
md5_object = md5.new()
md5_object.update(line)
print md5_object.hexdigest()   # Prints the output as per the hashing algorithm i.e. md5
exit

O programa acima produz a seguinte saída.

Neste programa, o script Python aceita a entrada (seu nome completo) e a converte de acordo com o algoritmo de hash md5. Ele criptografa os dados e protege as informações, se necessário. De acordo com as diretrizes forenses, o nome das evidências ou quaisquer outras provas podem ser garantidas neste padrão.

UMA hash functioné definido como a função que mapeia uma grande quantidade de dados para um valor fixo com um comprimento especificado. Essa função garante que a mesma entrada resulte na mesma saída, que na verdade é definida como uma soma hash. A soma de hash inclui uma característica com informações específicas.

Esta função é praticamente impossível de reverter. Assim, qualquer ataque de terceiros, como o ataque de força bruta, é praticamente impossível. Além disso, este tipo de algoritmo é chamadoone-way cryptographic algorithm.

Uma função de hash criptográfica ideal tem quatro propriedades principais -

  • Deve ser fácil calcular o valor de hash para qualquer entrada fornecida.
  • Deve ser inviável gerar a entrada original a partir de seu hash.
  • Deve ser inviável modificar a entrada sem alterar o hash.
  • Deve ser inviável encontrar duas entradas diferentes com o mesmo hash.

Exemplo

Considere o exemplo a seguir, que ajuda na correspondência de senhas usando caracteres em formato hexadecimal.

import uuid
import hashlib
  
def hash_password(password):
   # userid is used to generate a random number
   salt = uuid.uuid4().hex #salt is stored in hexadecimal value
   return hashlib.sha256(salt.encode() + password.encode()).hexdigest() + ':' + salt
     
def check_password(hashed_password, user_password):
   # hexdigest is used as an algorithm for storing passwords
   password, salt = hashed_password.split(':')
   return password == hashlib.sha256(salt.encode()
      + user_password.encode()).hexdigest()

new_pass = raw_input('Please enter required password ')
hashed_password = hash_password(new_pass)
print('The string to store in the db is: ' + hashed_password)
old_pass = raw_input('Re-enter new password ')

if check_password(hashed_password, old_pass):
   print('Yuppie!! You entered the right password')
else:
   print('Oops! I am sorry but the password does not match')

Fluxograma

Explicamos a lógica deste programa com a ajuda do seguinte fluxograma -

Resultado

Nosso código produzirá a seguinte saída -

A senha digitada duas vezes corresponde à função hash. Isso garante que a senha digitada duas vezes seja precisa, o que ajuda a reunir dados úteis e salvá-los em um formato criptografado.

Neste capítulo, aprenderemos como quebrar os dados de um texto buscados durante a análise e a evidência.

Um texto simples em criptografia é algum texto legível normal, como uma mensagem. Um texto cifrado, por outro lado, é a saída de um algoritmo de criptografia obtido após você inserir o texto simples.

Um algoritmo simples de como transformamos uma mensagem de texto simples em um texto cifrado é a cifra de César, inventada por Júlio César para manter o texto simples em segredo de seus inimigos. Essa cifra envolve o deslocamento de cada letra da mensagem "para frente" em três casas do alfabeto.

A seguir está uma ilustração de demonstração.

a → D

b → E

c → F

....

w → Z

x → A

y → B

z → C

Exemplo

Uma mensagem inserida quando você executa um script Python oferece todas as possibilidades de caracteres, que são usados ​​para evidência de padrão.

Os tipos de evidências de padrão usados ​​são os seguintes -

  • Trilhas e marcas de pneus
  • Impressions
  • Fingerprints

Todos os dados biométricos são compostos por dados vetoriais, que precisamos decifrar para reunir evidências completas.

O seguinte código Python mostra como você pode produzir um texto cifrado a partir de texto simples -

import sys

def decrypt(k,cipher): 
   plaintext = '' 
   
   for each in cipher: 
      p = (ord(each)-k) % 126 
      
      if p < 32: 
         p+=95 
         plaintext += chr(p) 
         print plaintext 

def main(argv):
   if (len(sys.argv) != 1): 
      sys.exit('Usage: cracking.py') 
      cipher = raw_input('Enter message: ') 
      
      for i in range(1,95,1): 
         decrypt(i,cipher)
         
if __name__ == "__main__": 
   main(sys.argv[1:])

Resultado

Agora, verifique a saída deste código. Quando inserimos um texto simples "Radhika", o programa produzirá o seguinte texto cifrado.

Virtualizationé o processo de emular sistemas de TI, como servidores, estações de trabalho, redes e armazenamento. Nada mais é do que a criação de uma versão virtual em vez de real de qualquer sistema operacional, um servidor, um dispositivo de armazenamento ou processos de rede.

O principal componente que ajuda na emulação de hardware virtual é definido como um hyper-visor.

A figura a seguir explica os dois principais tipos de virtualização de sistema usados.

A virtualização tem sido usada em computação forense de várias maneiras. Ajuda o analista de forma que a estação de trabalho possa ser utilizada em um estado validado para cada investigação. A recuperação de dados é possível anexando a imagem dd de uma unidade como uma unidade secundária em uma máquina virtual em particular. A mesma máquina pode ser usada como um software de recuperação para reunir as evidências.

O exemplo a seguir ajuda a entender a criação de uma máquina virtual com a ajuda da linguagem de programação Python.

Step 1 - Deixe a máquina virtual ser nomeada 'dummy1'.

Cada máquina virtual deve ter 512 MB de memória na capacidade mínima, expressa em bytes.

vm_memory = 512 * 1024 * 1024

Step 2 - A máquina virtual deve ser conectada ao cluster padrão, que foi calculado.

vm_cluster = api.clusters.get(name = "Default")

Step 3 - A máquina virtual deve inicializar a partir da unidade de disco rígido virtual.

vm_os = params.OperatingSystem(boot = [params.Boot(dev = "hd")])

Todas as opções são combinadas em um objeto de parâmetro de máquina virtual, antes de usar o método add da coleção vms para a máquina virtual.

Exemplo

A seguir está o script Python completo para adicionar uma máquina virtual.

from ovirtsdk.api import API #importing API library
from ovirtsdk.xml import params

try: #Api credentials is required for virtual machine
   api = API(url = "https://HOST", 
      username = "Radhika", 
      password = "a@123", 
      ca_file = "ca.crt")
      
   vm_name = "dummy1"
   vm_memory = 512 * 1024 * 1024 #calculating the memory in bytes
   vm_cluster = api.clusters.get(name = "Default")
   vm_template = api.templates.get(name = "Blank")
   
   #assigning the parameters to operating system
   vm_os = params.OperatingSystem(boot = [params.Boot(dev = "hd")])
   
   vm_params = params.VM(name = vm_name,
      memory = vm_memory,
      cluster = vm_cluster,
      template = vm_template
      os = vm_os)

   try: 
      api.vms.add(vm = vm_params) 
      print "Virtual machine '%s' added." % vm_name #output if it is successful. 
   except Exception as ex: 
      print "Adding virtual machine '%s' failed: %s" % (vm_name, ex) 
      api.disconnect()
      
except Exception as ex: 
   print "Unexpected error: %s" % ex

Resultado

Nosso código produzirá a seguinte saída -

O cenário dos ambientes de rede modernos é tal que a investigação pode ser complicada devido a uma série de dificuldades. Isso pode acontecer se você estiver respondendo a um suporte de violação, investigando atividades internas, realizando avaliações relacionadas à vulnerabilidade ou validando uma conformidade regulatória.

Conceito de programação de rede

As seguintes definições são usadas na programação de rede.

  • Client - O cliente é uma parte da arquitetura cliente-servidor da programação de rede que roda em um computador pessoal e estação de trabalho.

  • Server - O servidor é uma parte da arquitetura cliente-servidor que fornece serviços a outros programas de computador no mesmo ou em outros computadores.

  • WebSockets- Os WebSockets fornecem um protocolo entre o cliente e o servidor, que é executado em uma conexão TCP persistente. Com isso, mensagens bidirecionais podem ser enviadas entre a conexão do soquete TCP (simultaneamente).

Os WebSockets vêm depois de muitas outras tecnologias que permitem que os servidores enviem informações ao cliente. Além do handshake do cabeçalho de atualização, o WebSockets é independente do HTTP.

Esses protocolos são usados ​​para validar as informações que são enviadas ou recebidas por terceiros usuários. Como a criptografia é um dos métodos usados ​​para proteger as mensagens, também é importante proteger o canal por meio do qual as mensagens foram transferidas.

Considere o seguinte programa Python, que o cliente usa para handshaking.

Exemplo

# client.py
import socket

# create a socket object
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# get local machine name
host = socket.gethostname()
port = 8080

# connection to hostname on the port.
s.connect((host, port))

# Receive no more than 1024 bytes
tm = s.recv(1024)
print("The client is waiting for connection")
s.close()

Resultado

Ele produzirá a seguinte saída -

O servidor que aceita a solicitação de canal de comunicação incluirá o seguinte script.

# server.py
import socket
import time

# create a socket object
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# get local machine name 
host = socket.gethostname()
port = 8080

# bind to the port
serversocket.bind((host, port))

# queue up to 5 requests 
serversocket.listen(5)

while True:
   # establish a connection 
   clientsocket,addr = serversocket.accept()
   print("Got a connection from %s" % str(addr))
   currentTime = time.ctime(time.time()) + "\r\n"
   clientsocket.send(currentTime.encode('ascii'))
   clientsocket.close()

O cliente e o servidor criados com a ajuda da programação Python ouvem o número do host. Inicialmente, o cliente envia uma solicitação ao servidor com relação aos dados enviados no número do host e o servidor aceita a solicitação e envia uma resposta imediatamente. Dessa forma, podemos ter um canal de comunicação seguro.

Módulos em programas Python ajudam na organização do código. Eles ajudam a agrupar o código relacionado em um único módulo, o que o torna mais fácil de entender e usar. Inclui valores nomeados arbitrariamente, que podem ser usados ​​para vinculação e referência. Em palavras simples, um módulo é um arquivo que consiste em código Python que inclui funções, classes e variáveis.

O código Python para um módulo (arquivo) é salvo com .py extensão que é compilada como e quando necessário.

Example

def print_hello_func( par ): 
   print "Hello : ", par 
   return

Declaração de Importação

O arquivo-fonte Python pode ser usado como um módulo executando um importdeclaração que importa outros pacotes ou bibliotecas de terceiros. A sintaxe usada é a seguinte -

import module1[, module2[,... moduleN]

Quando o interpretador Python encontra a instrução import, ele importa o módulo especificado que está presente no caminho de pesquisa.

Example

Considere o seguinte exemplo.

#!/usr/bin/python

# Import module support
import support

# Now you can call defined function that module as follows
support.print_func("Radhika")

Ele produzirá a seguinte saída -

Um módulo é carregado apenas uma vez, independentemente do número de vezes que foi importado pelo código Python.

De ... declaração de importação

Fromattribute ajuda a importar atributos específicos de um módulo para um namespace atual. Aqui está sua sintaxe.

from modname import name1[, name2[, ... nameN]]

Example

Para importar a função fibonacci do módulo fib, use a seguinte declaração.

from fib import fibonacci

Localizando Módulos

Quando o módulo está sendo importado, o interpretador Python procura as seguintes sequências -

  • O diretório atual.

  • Se o módulo não existir, o Python pesquisará cada diretório na variável shell PYTHONPATH.

  • Se a localização da variável shell falhar, o Python verifica o caminho padrão.

A perícia computacional usa módulos Python e módulos de terceiros para obter as informações e extrair evidências com mais facilidade. Os próximos capítulos enfocam a implementação de módulos para obter a saída necessária.

DShell

Dshellé um kit de ferramentas de análise forense de rede baseado em Python. Este kit de ferramentas foi desenvolvido pelo Laboratório de Pesquisa do Exército dos EUA. O lançamento deste kit de ferramentas de código aberto foi no ano de 2014. O principal foco deste kit de ferramentas é fazer investigações forenses com facilidade.

O kit de ferramentas consiste em um grande número de decodificadores listados na tabela a seguir.

Sr. Não. Nome e descrição do decodificador
1

dns

Isso é usado para extrair consultas relacionadas ao DNS

2

reservedips

Identifica as soluções para problemas de DNS

3

large-flows

Listagem dos fluxos de rede

4

rip-http

É usado para extrair os arquivos do tráfego HTTP

5

Protocols

Usado para identificação de protocolos não padronizados

O Laboratório do Exército dos EUA manteve o repositório de clones no GitHub no seguinte link -

https://github.com/USArmyResearchLab/Dshell

O clone consiste em um script install-ubuntu.py () usado para a instalação deste kit de ferramentas.

Assim que a instalação for bem-sucedida, ele criará automaticamente os executáveis ​​e dependências que serão usados ​​posteriormente.

As dependências são as seguintes -

dependencies = { 
   "Crypto": "crypto", 
   "dpkt": "dpkt", 
   "IPy": "ipy", 
   "pcap": "pypcap" 
}

Este kit de ferramentas pode ser usado contra os arquivos pcap (captura de pacotes), que geralmente são registrados durante os incidentes ou durante o alerta. Esses arquivos pcap são criados por libpcap na plataforma Linux ou WinPcap na plataforma Windows.

Scapy

Scapy é uma ferramenta baseada em Python usada para analisar e manipular o tráfego da rede. A seguir está o link para o kit de ferramentas Scapy -

http://www.secdev.org/projects/scapy/

Este kit de ferramentas é usado para analisar a manipulação de pacotes. É muito capaz de decodificar pacotes de um grande número de protocolos e capturá-los. Scapy difere do kit de ferramentas Dshell ao fornecer uma descrição detalhada ao investigador sobre o tráfego de rede. Essas descrições foram registradas em tempo real.

Scapy tem a capacidade de plotar usando ferramentas de terceiros ou impressão digital do sistema operacional.

Considere o seguinte exemplo.

import scapy, GeoIP #Imports scapy and GeoIP toolkit 
from scapy import * 
geoIp = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE) #locates the Geo IP address 
def locatePackage(pkg): 
src = pkg.getlayer(IP).src #gets source IP address 
dst = pkg.getlayer(IP).dst #gets destination IP address 
srcCountry = geoIp.country_code_by_addr(src) #gets Country details of source 
dstCountry = geoIp.country_code_by_addr(dst) #gets country details of destination 
print src+"("+srcCountry+") >> "+dst+"("+dstCountry+")\n"

Este script fornece a descrição detalhada dos detalhes do país no pacote de rede, que estão se comunicando.

O script acima produzirá a seguinte saída.

Searchingé certamente um dos pilares da investigação forense. Hoje em dia, a pesquisa é tão boa quanto o investigador que está executando as evidências.

Pesquisar uma palavra-chave na mensagem desempenha um papel vital na perícia, quando procuramos uma evidência com a ajuda de uma palavra-chave. O conhecimento do que deve ser pesquisado em um determinado arquivo, juntamente com os dos arquivos excluídos, requer experiência e conhecimento.

Python tem vários mecanismos integrados com módulos de biblioteca padrão para oferecer suporte searchOperação. Fundamentalmente, os investigadores usam a operação de pesquisa para encontrar respostas a perguntas como "quem", "o quê", "onde", "quando" etc.

Exemplo

No exemplo a seguir, declaramos duas strings e, em seguida, usamos a função find para verificar se a primeira string contém a segunda string ou não.

# Searching a particular word from a message
str1 = "This is a string example for Computational forensics of gathering evidence!";
str2 = "string";

print str1.find(str2)
print str1.find(str2, 10)
print str1.find(str2, 40)

O script acima produzirá a seguinte saída.

“find”A função em Python ajuda a pesquisar uma palavra-chave em uma mensagem ou parágrafo. Isso é fundamental para a coleta de evidências adequadas.

Indexingrealmente fornece ao investigador uma visão completa de um arquivo e coletar evidências potenciais a partir dele. A evidência pode estar contida em um arquivo, uma imagem de disco, um instantâneo da memória ou um rastreamento de rede.

A indexação ajuda a reduzir o tempo para tarefas demoradas, como keyword searching. A investigação forense também envolve a fase de pesquisa interativa, onde o índice é usado para localizar rapidamente as palavras-chave.

A indexação também ajuda a listar as palavras-chave em uma lista classificada.

Exemplo

O exemplo a seguir mostra como você pode usar indexing em Python.

aList = [123, 'sample', 'zara', 'indexing'];

print "Index for sample : ", aList.index('sample')
print "Index for indexing : ", aList.index('indexing')

str1 = "This is sample message for forensic investigation indexing";
str2 = "sample";

print "Index of the character keyword found is " 
print str1.index(str2)

O script acima produzirá a seguinte saída.

Extrair informações valiosas dos recursos disponíveis é uma parte vital da análise forense digital. Ter acesso a todas as informações disponíveis é essencial para um processo de investigação, pois ajuda a recuperar as evidências adequadas.

Os recursos que contêm dados podem ser estruturas de dados simples, como bancos de dados, ou estruturas de dados complexas, como uma imagem JPEG. Estruturas de dados simples podem ser acessadas facilmente usando ferramentas de desktop simples, enquanto a extração de informações de estruturas de dados complexas requer ferramentas de programação sofisticadas.

Biblioteca de imagens Python

A Python Imaging Library (PIL) adiciona recursos de processamento de imagem ao seu interpretador Python. Esta biblioteca oferece suporte a vários formatos de arquivo e fornece processamento de imagem poderoso e recursos gráficos. Você pode baixar os arquivos fonte do PIL em:http://www.pythonware.com/products/pil/

A ilustração a seguir mostra o diagrama de fluxo completo de extração de dados de imagens (estruturas de dados complexas) em PIL.

Exemplo

Agora, vamos ter um exemplo de programação para entender como realmente funciona.

Step 1 - Suponha que temos a seguinte imagem de onde precisamos extrair informações.

Step 2- Quando abrimos esta imagem usando PIL, primeiro observará os pontos necessários para extrair evidências, o que inclui vários valores de pixel. Aqui está o código para abrir a imagem e registrar seus valores de pixel -

from PIL import Image
im = Image.open('Capture.jpeg', 'r')
pix_val = list(im.getdata())
pix_val_flat = [x for sets in pix_val for x in sets]
print pix_val_flat

Step 3 - Nosso código produzirá a seguinte saída, após extrair os valores de pixel da imagem.

A saída entregue representa os valores de pixel da combinação RGB, o que dá uma imagem melhor de quais dados são necessários para a evidência. Os dados buscados são representados na forma de uma matriz.

A investigação forense e a análise de hardware de computador padrão, como discos rígidos, tornaram-se uma disciplina estável e são acompanhadas com a ajuda de técnicas para analisar hardware não padrão ou evidências temporárias.

Embora os smartphones sejam cada vez mais usados ​​em investigações digitais, eles ainda são considerados fora do padrão.

Análise forense

As investigações forenses procuram dados como ligações recebidas ou números discados do smartphone. Pode incluir mensagens de texto, fotos ou qualquer outra evidência incriminatória. A maioria dos smartphones possui recursos de bloqueio de tela usando senhas ou caracteres alfanuméricos.

Aqui, vamos dar um exemplo para mostrar como o Python pode ajudar a quebrar a senha de bloqueio de tela para recuperar dados de um smartphone.

Exame Manual

O Android suporta bloqueio de senha com número PIN ou senha alfanumérica. O limite de ambas as senhas deve ser entre 4 e 16 dígitos ou caracteres. A senha de um smartphone é armazenada no sistema Android em um arquivo especial chamadopassword.key dentro /data/system.

O Android armazena um SHA1-hashsum com sal e MD5-hashsum da senha. Essas senhas podem ser processadas no código a seguir.

public byte[] passwordToHash(String password) {

   if (password == null) { 
      return null; 
   }

   String algo = null;
   byte[] hashed = null;

   try { 
      byte[] saltedPassword = (password + getSalt()).getBytes(); 
      byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
      byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword); 
      hashed = (toHex(sha1) + toHex(md5)).getBytes(); 
   } catch (NoSuchAlgorithmException e) { 
      Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo); 
   }
   
   return hashed;
}

Não é possível quebrar a senha com a ajuda de dictionary attack já que a senha com hash é armazenada em um salt file. estesalté uma string de representação hexadecimal de um inteiro aleatório de 64 bits. É fácil acessar osalt usando Rooted Smartphone ou JTAG Adapter.

Smartphone Enraizado

O despejo do arquivo /data/system/password.key é armazenado no banco de dados SQLite sob o lockscreen.password_saltchave. Debaixosettings.db, a senha é armazenada e o valor é claramente visível na imagem a seguir.

Adaptador JTAG

Um hardware especial conhecido como adaptador JTAG (Joint Test Action Group) pode ser usado para acessar o salt. Da mesma forma, umRiff-Box ou um JIG-Adapter também pode ser usado para a mesma funcionalidade.

Usando as informações obtidas do Riff-box, podemos encontrar a posição dos dados criptografados, ou seja, o salt. A seguir estão as regras -

  • Pesquise a string associada "lockscreen.password_salt."

  • O byte representa a largura real do sal, que é seu length.

  • Este é o comprimento que é realmente pesquisado para obter a senha / PIN armazenado dos smartphones.

Esse conjunto de regras ajuda a obter os dados de sal apropriados.

O protocolo de sincronização de tempo mais utilizado e amplamente aceito como prática é o protocolo NTP (Network Time Protocol).

O NTP usa o User Datagram Protocol (UDP) que usa o tempo mínimo para comunicar os pacotes entre o servidor e o cliente que deseja sincronizar com a fonte de tempo fornecida.

As características do Network Time Protocol são as seguintes -

  • A porta do servidor padrão é 123.

  • Este protocolo consiste em muitos servidores de tempo acessíveis sincronizados com laboratórios nacionais.

  • O protocolo padrão NTP é regido pela IETF e o padrão proposto é RFC 5905, intitulado “Network Time Protocol Version 4: Protocol and Algorithms Specification” [NTP RFC]

  • Os sistemas operacionais, programas e aplicativos usam o NTP para sincronizar o tempo de maneira adequada.

Neste capítulo, vamos nos concentrar no uso de NTP com Python, que é viável a partir da biblioteca Python ntplib de terceiros. Esta biblioteca trata com eficiência o trabalho pesado, que compara os resultados com o relógio do meu sistema local.

Instalando a Biblioteca NTP

o ntplib está disponível para download em https://pypi.python.org/pypi/ntplib/ conforme mostrado na figura a seguir.

A biblioteca fornece uma interface simples para servidores NTP com a ajuda de métodos que podem traduzir campos de protocolo NTP. Isso ajuda a acessar outros valores-chave, como segundos bissextos.

O programa Python a seguir ajuda a entender o uso do NTP.

import ntplib
import time

NIST = 'nist1-macon.macon.ga.us'
ntp = ntplib.NTPClient()
ntpResponse = ntp.request(NIST)

if (ntpResponse):
   now = time.time()
   diff = now-ntpResponse.tx_time
   print diff;

O programa acima produzirá a seguinte saída.

A diferença de tempo é calculada no programa acima. Esses cálculos ajudam nas investigações forenses. Os dados de rede obtidos são fundamentalmente diferentes da análise de dados encontrados no disco rígido.

A diferença de fusos horários ou a obtenção de fusos horários precisos podem ajudar na coleta de evidências para capturar as mensagens por meio deste protocolo.

Os especialistas forenses normalmente têm dificuldade em aplicar soluções digitais para analisar as montanhas de evidências digitais em crimes comuns. A maioria das ferramentas de investigação digital é de thread único e pode executar apenas um comando de cada vez.

Neste capítulo, vamos nos concentrar nos recursos de multiprocessamento do Python, que podem estar relacionados aos desafios forenses comuns.

Multiprocessamento

Multiprocessamento é definido como a capacidade do sistema de computador de suportar mais de um processo. Os sistemas operacionais que suportam multiprocessamento permitem que vários programas sejam executados simultaneamente.

Existem vários tipos de multiprocessamento, como symmetric e asymmetric processing. O diagrama a seguir se refere a um sistema de multiprocessamento simétrico que geralmente é seguido em investigação forense.

Exemplo

O código a seguir mostra como diferentes processos são listados internamente na programação Python.

import random
import multiprocessing

def list_append(count, id, out_list): 
   #appends the count of number of processes which takes place at a time
   for i in range(count):
      out_list.append(random.random())
         
   if __name__ == "__main__": 
      size = 999  
      procs = 2
      # Create a list of jobs and then iterate through 
      # the number of processes appending each process to 
      # the job list  
      jobs = []
         
   for i in range(0, procs): 
      out_list = list() #list of processes 
      process1 = multiprocessing.Process(
         target = list_append, args = (size, i, out_list))

      # appends the list of processes
      jobs.append(process)

   # Calculate the random number of processes
   for j in jobs:
      j.start()  #initiate the process

   # After the processes have finished execution
   for j in jobs:
      j.join()
      print "List processing complete."

Aqui, a função list_append() ajuda a listar o conjunto de processos no sistema.

Resultado

Nosso código produzirá a seguinte saída -

Neste capítulo, vamos nos concentrar em investigar a memória volátil com a ajuda de Volatility, uma estrutura forense baseada em Python aplicável nas seguintes plataformas: Android e Linux.

Memória volátil

A memória volátil é um tipo de armazenamento onde o conteúdo é apagado quando o sistema é desligado ou interrompido. RAM é o melhor exemplo de memória volátil. Isso significa que se você estiver trabalhando em um documento que não foi salvo em uma memória não volátil, como um disco rígido, e o computador ficar sem energia, todos os dados serão perdidos.

Em geral, a análise forense de memória volátil segue o mesmo padrão de outras investigações forenses -

  • Selecionando o alvo da investigação
  • Aquisição de dados forenses
  • Análise forense

O básico volatility plugins que são usados ​​para coletas Android RAM dumppara análise. Depois que o dump de RAM é coletado para análise, é importante começar a procurar malware na RAM.

Regras YARA

YARA é uma ferramenta popular que fornece uma linguagem robusta, é compatível com expressões regulares baseadas em Perl e é usada para examinar os arquivos / diretórios suspeitos e as strings de correspondência.

Nesta seção, usaremos o YARA com base na implementação de correspondência de padrões e os combinaremos com a energia da concessionária. O processo completo será benéfico para a análise forense.

Exemplo

Considere o seguinte código. Este código ajuda a extrair o código.

import operator
import os
import sys

sys.path.insert(0, os.getcwd())
import plyara.interp as interp

# Plyara is a script that lexes and parses a file consisting of one more Yara
# rules into a python dictionary representation.
if __name__ == '__main__': 
   file_to_analyze = sys.argv[1] 
   rulesDict = interp.parseString(open(file_to_analyze).read()) 
   authors = {} 
   imps = {} 
   meta_keys = {} 
   max_strings = [] 
   max_string_len = 0 
   tags = {} 
   rule_count = 0  

   for rule in rulesDict: 
      rule_count += 1  
   
   # Imports 
   if 'imports' in rule: 
      for imp in rule['imports']: 
         imp = imp.replace('"','') 
         
         if imp in imps: 
            imps[imp] += 1 
         else: 
            imps[imp] = 1  
   # Tags 
   if 'tags' in rule: 
      for tag in rule['tags']: 
         if tag in tags: 
            tags[tag] += 1 
         else: 
            tags[tag] = 1
            
   # Metadata 
   if 'metadata' in rule: 
      for key in rule['metadata']: 
         if key in meta_keys: 
            meta_keys[key] += 1
         else: 
            meta_keys[key] = 1 
         
         if key in ['Author', 'author']: 
            if rule['metadata'][key] in authors: 
               authors[rule['metadata'][key]] += 1 
            else: 
               authors[rule['metadata'][key]] = 1  

   #Strings 
   if 'strings' in rule: 
      for strr in rule['strings']: 
         if len(strr['value']) > max_string_len: 
            max_string_len = len(strr['value']) 
            max_strings = [(rule['rule_name'], strr['name'], strr['value'])] 
         elif len(strr['value']) == max_string_len: 
            max_strings.append((rule['rule_name'], strr['key'], strr['value']))  
   
   print("\nThe number of rules implemented" + str(rule_count))
   ordered_meta_keys = sorted(meta_keys.items(), key = operator.itemgetter(1),
      reverse = True)
   ordered_authors = sorted(authors.items(), key = operator.itemgetter(1), 
      reverse = True)
   ordered_imps = sorted(imps.items(), key = operator.itemgetter(1), reverse = True)
   ordered_tags = sorted(tags.items(), key = operator.itemgetter(1), reverse = True)

O código acima produzirá a seguinte saída.

O número de regras YARA implementadas ajuda a dar uma imagem melhor dos arquivos suspeitos. Indiretamente, a lista de arquivos suspeitos ajuda na coleta de informações apropriadas para perícia.

A seguir está o código-fonte no github: https://github.com/radhikascs/Python_yara

A principal preocupação das investigações digitais é proteger evidências ou dados importantes com criptografia ou qualquer outro formato. O exemplo básico é armazenar as senhas. Portanto, é necessário entender o uso do sistema operacional Linux para implementação forense digital para proteger esses dados valiosos.

As informações de todos os usuários locais são principalmente armazenadas nos dois arquivos a seguir -

  • /etc/passwd
  • etc/shadow

O primeiro é obrigatório, que armazena todas as senhas. O segundo arquivo é opcional e armazena informações sobre os usuários locais, incluindo as senhas com hash.

Surgem problemas relacionados à questão de segurança de armazenar as informações de senha em um arquivo, que pode ser lido por todos os usuários. Portanto, as senhas com hash são armazenadas em/etc/passwd, onde o conteúdo é substituído por um valor especial "x"

Os hashes correspondentes devem ser pesquisados ​​em /etc/shadow. As configurações em/etc/passwd pode substituir os detalhes em /etc/shadow.

Ambos os arquivos de texto no Linux incluem uma entrada por linha e a entrada consiste em vários campos, separados por dois pontos.

O formato de /etc/passwd é o seguinte -

Sr. Não. Nome e descrição do campo
1

Username

Este campo consiste em atributos de formato legível por humanos

2

Password hash

Consiste na senha em uma forma codificada de acordo com a função Posix crypt

Se a senha hash for salva como empty, o usuário correspondente não exigirá nenhuma senha para fazer login no sistema. Se este campo contiver um valor que não pode ser gerado pelo algoritmo de hash, como um ponto de exclamação, o usuário não poderá fazer logon usando uma senha.

Um usuário com uma senha bloqueada ainda pode fazer logon usando outros mecanismos de autenticação, por exemplo, chaves SSH. Conforme mencionado anteriormente, o valor especial "x"significa que o hash da senha deve ser encontrado no arquivo shadow.

o password hash inclui o seguinte -

  • Encrypted salt - o encrypted salt ajuda a manter os bloqueios de tela, pinos e senhas.

  • Numerical user ID- Este campo denota o ID do usuário. O kernel do Linux atribui esse ID do usuário ao sistema.

  • Numerical group ID - Este campo se refere ao grupo principal do usuário.

  • Home directory - Os novos processos são iniciados com uma referência a este diretório.

  • Command shell - Este campo opcional indica o shell padrão que deve ser iniciado após um login bem-sucedido no sistema.

A perícia digital inclui a coleta de informações que são relevantes para rastrear uma evidência. Conseqüentemente, os IDs de usuário são úteis na manutenção dos registros.

Usando Python, todas essas informações podem ser analisadas automaticamente para os Indicadores de Análise, reconstruindo a atividade recente do sistema. O rastreamento é simples e fácil com a implementação do Linux Shell.

Programação Python com Linux

Exemplo

import sys
import hashlib
import getpass

def main(argv):
   print '\nUser & Password Storage Program in Linux for forensic detection v.01\n' 
  
   if raw_input('The file ' + sys.argv[1] + ' will be erased or overwrite if 
         it exists .\nDo you wish to continue (Y/n): ') not in ('Y','y') : 
   sys.exit('\nChanges were not recorded\n') 
  
   user_name = raw_input('Please Enter a User Name: ')
   password = hashlib.sha224(getpass.getpass('Please Enter a Password:')).hexdigest()
   
   # Passwords which are hashed  
   try: 
      file_conn = open(sys.argv[1],'w') 
      file_conn.write(user_name + '\n') 
      file_conn.write(password + '\n') 
      file_conn.close() 
   except: 
      sys.exit('There was a problem writing the passwords to file!')
      
if __name__ == "__main__": 
   main(sys.argv[1:])

Resultado

A senha é armazenada em formato hexadecimal em pass_db.txtcomo mostrado na imagem a seguir. Os arquivos de texto são salvos para uso posterior em perícia computacional.

Indicadores de comprometimento (IOC) são definidos como "pedaços de dados forenses, que incluem dados encontrados em entradas de log do sistema ou arquivos, que identificam atividades potencialmente maliciosas em um sistema ou rede."

Ao monitorar o IOC, as organizações podem detectar ataques e agir rapidamente para evitar que tais violações ocorram ou limitar os danos interrompendo os ataques em estágios anteriores.

Existem alguns casos de uso que permitem consultar os artefatos forenses, como -

  • Procurando por um arquivo específico por MD5
  • Procurando uma entidade específica, que está realmente armazenada na memória
  • Entrada específica ou conjunto de entradas, que é armazenado no registro do Windows

A combinação de todos os itens acima fornece melhores resultados na pesquisa de artefatos. Como mencionado acima, o registro do Windows oferece uma plataforma perfeita na geração e manutenção de IOC, o que ajuda diretamente na análise forense computacional.

Metodologia

  • Procure os locais no sistema de arquivos e, especificamente, agora no registro do Windows.

  • Pesquise o conjunto de artefatos, que foram projetados por ferramentas forenses.

  • Procure sinais de atividades adversas.

Ciclo de vida investigativo

O ciclo de vida investigativo segue o IOC e procura por entradas específicas em um registro.

  • Stage 1: Initial Evidence- A evidência do comprometimento é detectada em um host ou na rede. Os respondentes investigarão e identificarão a solução exata, que é um indicador forense concreto.

  • Stage 2: Create IOCs for Host & Network- Após os dados coletados, é criado o IOC, o que é facilmente possível com o registro do Windows. A flexibilidade do OpenIOC oferece um número ilimitado de permutações sobre como um indicador pode ser criado.

  • Stage 3: Deploy IOCs in the Enterprise - Assim que o IOC especificado for criado, o investigador implantará essas tecnologias com a ajuda da API nos registros do Windows.

  • Stage 4: Identification of Suspects- A implantação do IOC auxilia na identificação de suspeitos de forma normal. Mesmo sistemas adicionais serão identificados.

  • Stage 5: Collect and Analyze Evidence - As provas contra os suspeitos são recolhidas e analisadas em conformidade.

  • Stage 6: Refine & Create New IOCs - A equipe investigativa pode criar novos IOCs com base em suas evidências e dados encontrados na empresa e inteligência adicional, e continuar a refinar seu ciclo.

A ilustração a seguir mostra as fases do Ciclo de Vida Investigativo -

Cloud computingpode ser definido como uma coleção de serviços hospedados fornecidos aos usuários pela Internet. Ele permite que as organizações consumam ou até mesmo computem o recurso, que inclui máquinas virtuais (VMs), armazenamento ou um aplicativo como utilitário.

Uma das vantagens mais importantes de construir aplicativos na linguagem de programação Python é que inclui a capacidade de implantar aplicativos virtualmente em qualquer plataforma, o que inclui cloudtambém. Isso implica que o Python pode ser executado em servidores em nuvem e também pode ser iniciado em dispositivos úteis, como desktop, tablet ou smartphone.

Uma das perspectivas interessantes é a criação de uma base de nuvem com a geração de Rainbow tables. Ajuda na integração de versões simples e de multiprocessamento do aplicativo, o que requer algumas considerações.

Pi Cloud

Pi Cloud é a plataforma de computação em nuvem que integra a linguagem de programação Python com o poder de computação da Amazon Web Services.

Vamos dar uma olhada em um exemplo de implementação de nuvens Pi com rainbow tables.

Tabelas arco-íris

UMA rainbow table é definido como uma lista de todas as permutações de texto simples possíveis de senhas criptografadas específicas para um determinado algoritmo de hash.

  • As tabelas do arco-íris seguem um padrão padrão, que cria uma lista de senhas em hash.

  • Um arquivo de texto é usado para gerar senhas, que incluem caracteres ou texto simples de senhas a serem criptografadas.

  • O arquivo é usado pela nuvem Pi, que chama a função principal a ser armazenada.

  • A saída de senhas com hash também é armazenada no arquivo de texto.

Este algoritmo também pode ser usado para salvar senhas no banco de dados e ter um armazenamento de backup no sistema em nuvem.

O seguinte programa embutido cria uma lista de senhas criptografadas em um arquivo de texto.

Exemplo

import os
import random
import hashlib
import string
import enchant    #Rainbow tables with enchant 
import cloud      #importing pi-cloud

def randomword(length): 
   return ''.join(random.choice(string.lowercase) for i in range(length))

print('Author- Radhika Subramanian')

def mainroutine():
   engdict = enchant.Dict("en_US")
   fileb = open("password.txt","a+")

   # Capture the values from the text file named password
   while True:
      randomword0 = randomword(6)
      if engdict.check(randomword0) == True:
         randomkey0 = randomword0+str(random.randint(0,99))
      elif engdict.check(randomword0) == False:
         englist = engdict.suggest(randomword0)
         if len(englist) > 0:
            randomkey0 = englist[0]+str(random.randint(0,99))
         else:
            randomkey0 = randomword0+str(random.randint(0,99))

      randomword3 = randomword(5)
      if engdict.check(randomword3) == True:
         randomkey3 = randomword3+str(random.randint(0,99))
      elif engdict.check(randomword3) == False:
         englist = engdict.suggest(randomword3)
         if len(englist) > 0:
            randomkey3 = englist[0]+str(random.randint(0,99))
         else:
            randomkey3 = randomword3+str(random.randint(0,99))
      
      if 'randomkey0' and 'randomkey3' and 'randomkey1' in locals():
         whasher0 = hashlib.new("md5")
         whasher0.update(randomkey0)
         whasher3 = hashlib.new("md5")
         whasher3.update(randomkey3)
         whasher1 = hashlib.new("md5")
         whasher1.update(randomkey1)
         print(randomkey0+" + "+str(whasher0.hexdigest())+"\n")
         print(randomkey3+" + "+str(whasher3.hexdigest())+"\n")
         print(randomkey1+" + "+str(whasher1.hexdigest())+"\n")
         fileb.write(randomkey0+" + "+str(whasher0.hexdigest())+"\n") 
         fileb.write(randomkey3+" + "+str(whasher3.hexdigest())+"\n")
         fileb.write(randomkey1+" + "+str(whasher1.hexdigest())+"\n")

jid = cloud.call(randomword)  #square(3) evaluated on PiCloud
cloud.result(jid)
print('Value added to cloud')
print('Password added')
mainroutine()

Resultado

Este código produzirá a seguinte saída -

As senhas ficam armazenadas nos arquivos de texto, que ficam visíveis, conforme mostrado na imagem a seguir.