Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions challenges/2-Blue-Team-Memory-Forensics-Jakub/.gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# Secret réel du challenge — ne JAMAIS committer (voir challenge.env.example)
challenge.env

# Fichiers générés au build Docker (recréés automatiquement)
challenge/memory.dmp
challenge/network_capture.pcap
challenge/hints.txt
valout/
solution/flag.sha256

# Export Docker (trop lourd pour git)
*.tar
Expand Down
3 changes: 3 additions & 0 deletions challenges/2-Blue-Team-Memory-Forensics-Jakub/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ COPY report/ ./report/
# Copier le script de validation (le joueur peut valider ses réponses)
COPY solution/validate_flag.py ./solution/validate_flag.py

# Hash SHA256 du flag (généré au build depuis challenge.env) — jamais le flag en clair
COPY --from=builder /build/valout/flag.sha256 ./solution/flag.sha256

# Copier le README
COPY README.md ./README.md

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,34 @@ Challenge type: `container`
## 6) Creation du flag (obligatoire)
Dans l'onglet `Flags`:
- **Type**: `Static`
- **Valeur**: `blue{m3m_f0r3ns1cs_v0l4t1l1ty_m4st3r}`
- **Valeur**: la valeur définie dans `setup/challenge.env` (`FLAG=...`) — **pas** de flag en clair dans le dépôt. Voir « Fournir le flag » ci-dessous.
- **Case Sensitive**: activé

## 7) Sequence de validation
1. Laisser le challenge en `Hidden`.
2. Tester `Start Instance` avec un compte joueur.
3. Vérifier l'accès HTTP aux fichiers : `http://<instance>:8000/memory.dmp`
4. Vérifier que `hints.txt` et `network_capture.pcap` sont bien accessibles.
5. Soumettre le flag `blue{m3m_f0r3ns1cs_v0l4t1l1ty_m4st3r}` pour valider.
5. Soumettre le flag (celui de `challenge.env`) pour valider.
6. Passer en `Visible`.

## Fournir le flag (ne PAS le committer)

Le flag n'est plus codé en dur : il est fourni au build via un fichier gitignoré,
puis cuit dans le dump + le PCAP, et son hash SHA256 est généré pour le validateur.

```bash
cd setup
cp challenge.env.example challenge.env
$EDITOR challenge.env # FLAG=blue{...} (choisir une valeur rotée)
```
Puis (re)builder l'image ; `gen`/`generate_challenge.py` lit `challenge.env` automatiquement.
Si aucun `challenge.env` n'est fourni, le build réussit mais avec un flag **placeholder**
(`blue{PLACEHOLDER_...}`). Le même flag doit être saisi côté CTFd (étape 6).

> ⚠️ Rotation : l'ancien flag `blue{m3m_f0r3ns1cs_v0l4t1l1ty_m4st3r}` est resté public
> dans l'historique git — choisir une **nouvelle** valeur dans `challenge.env`.

## 8) Troubleshooting
Si l'instance ne démarre pas :

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# =====================================================================
# Modele de configuration du flag — challenge Memory Forensics.
#
# 1. Copiez : cp challenge.env.example challenge.env
# 2. Mettez le VRAI flag (roté) dans challenge.env
# 3. challenge.env est gitignore : il ne sera jamais committe.
#
# Le flag est ensuite cuit dans le dump/PCAP au build, et son hash
# SHA256 est genere pour le validateur. Alternative : passer FLAG en
# variable d'environnement / --build-arg (priorite sur ce fichier).
Comment on lines +1 to +10
# =====================================================================

FLAG=blue{exemple_a_changer}
25 changes: 25 additions & 0 deletions challenges/2-Blue-Team-Memory-Forensics-Jakub/setup/flag_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# =====================================================================
# Résolution du flag du challenge (rotation hors dépôt public).
# Priorité : variable d'env FLAG > setup/challenge.env > placeholder.
# Le flag réel n'est donc jamais committé (challenge.env est gitignoré).
# =====================================================================

import os

_PLACEHOLDER = "blue{PLACEHOLDER_definir_dans_challenge_env}"


def resolve_flag():
env = os.environ.get("FLAG")
if env:
return env
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "challenge.env")
if os.path.isfile(path):
with open(path, encoding="utf-8") as f:
for line in f:
line = line.strip()
if line.startswith("FLAG="):
return line[len("FLAG="):].strip().strip('"').strip("'")
return _PLACEHOLDER
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@
import time
from datetime import datetime, timedelta

from flag_config import resolve_flag

# ─── Configuration du challenge ───────────────────────────────────────────────

CHALLENGE_CONFIG = {
"flag": "blue{m3m_f0r3ns1cs_v0l4t1l1ty_m4st3r}",
"flag": resolve_flag(),
"c2_domain": "c2.darkops-syndicate.net",
"c2_ip": "185.141.27.83",
"c2_port": 4444,
Expand Down Expand Up @@ -408,6 +410,13 @@ def main():
dump_path = generate_memory_dump(challenge_dir)
generate_hints(challenge_dir)

# Hash SHA256 du flag pour le validateur (jamais le flag en clair).
valout_dir = os.path.join(project_dir, "valout")
os.makedirs(valout_dir, exist_ok=True)
flag_hash = hashlib.sha256(CHALLENGE_CONFIG["flag"].encode()).hexdigest()
with open(os.path.join(valout_dir, "flag.sha256"), "w") as f:
f.write(flag_hash + "\n")

print()
print("[✓] Challenge prêt ! Fichiers dans :", challenge_dir)
print()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
GW_MAC = b"\x52\x54\x00\x12\x35\x00"
DNS_SERVER = "192.168.1.1"

FLAG = "blue{m3m_f0r3ns1cs_v0l4t1l1ty_m4st3r}"
from flag_config import resolve_flag
FLAG = resolve_flag()


# ═══════════════════════════════════════════════════════════════════════════════
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# 🔵 Solution Complète – Blue Team Memory Forensics

> ⚠️ **SPOILERS** – Ne lisez ceci qu'après avoir tenté le challenge !
>
> Le **flag** est roté par déploiement (défini dans `setup/challenge.env`, jamais committé) :
> la valeur réelle dépend de l'instance déployée. Les autres réponses sont fixes.

---

Expand All @@ -13,7 +16,7 @@
| **IP du C2** | `185.141.27.83` |
| **Domaine C2** | `c2.darkops-syndicate.net` |
| **Port C2** | `4444` |
| **Flag** | `blue{m3m_f0r3ns1cs_v0l4t1l1ty_m4st3r}` |
| **Flag** | `blue{…roté, défini dans setup/challenge.env…}` |

---

Expand Down Expand Up @@ -171,7 +174,7 @@ C2_FALLBACK=185.141.27.83:8443
BEACON_SLEEP=30
XOR_KEY=X0rK3y!@#2026
EXFIL_TAG=CONFIDENTIAL_PROJECT_OMEGA_FILES
FLAG=blue{m3m_f0r3ns1cs_v0l4t1l1ty_m4st3r}
FLAG=blue{…roté, défini dans setup/challenge.env…}
```

**Chaînes SUSPECTES :**
Expand All @@ -185,7 +188,7 @@ Host: c2.darkops-syndicate.net
powershell -ep bypass -nop -w hidden -c "IEX(...)"
```

→ **FLAG TROUVÉ : `blue{m3m_f0r3ns1cs_v0l4t1l1ty_m4st3r}`**
→ **FLAG TROUVÉ : `blue{…roté, défini dans setup/challenge.env…}`**

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,29 @@
"""

import hashlib
import os
import sys

# ─── Hashes SHA256 des réponses attendues ─────────────────────────────────────
# Les réponses en clair ne sont PAS stockées ici pour éviter le spoil.
# Ces hashes ont été générés lors de la création du challenge.
# Le hash du FLAG est généré au build (rotation via challenge.env) et lu
# depuis flag.sha256 ; les autres réponses sont des faits fixes du challenge.

def _load_flag_hash():
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "flag.sha256")
try:
with open(path) as f:
return f.read().strip()
except OSError:
return None
Comment on lines +18 to +24

EXPECTED_HASHES = {
"pid": "890ebdf964b651bc670b2001f32aad8eb1c0e9704f7857b4961fd0a602eed83b",
"process_name": "47a648b1bbc9399b36e7189ea5ee0f855262c39c5dff523bc60eec2bc89fd963",
"c2_ip": "4e0a24277c461cb7a6b904173aabf898ba90cf9f5832a1a54c5258a6ea26c08b",
"c2_domain": "b6bd3f76cc36838799e6969e675143ffa8a738743b613a8e67400cf255d11163",
"c2_port": "79f06f8fde333461739f220090a23cb2a79f6d714bee100d0e4b4af249294619",
"flag": "adbace5b3008f4f51eb858fa0ff6ac58923b88da172a4f86902079bb9469abef",
"flag": _load_flag_hash(),
}

SCORING = {
Expand Down