> For the complete documentation index, see [llms.txt](https://book.onosh.ovh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.onosh.ovh/ctf/hacktheshields-2026/forensic-lombre-dorion.md).

# Forensic - L'ombre d'Orion

### 1. Énoncé

> Lors du crash du drone de transport tactique d'Orion Aerospace, le noyau central de la machine a subi un arrêt brutal. Nos agents sur le terrain ont réussi à dumper la mémoire flash contenant le Core Dump du processus de communication sécurisé (`orion_agent.core`).

{% file src="/files/qXAhJFQRnoL8NKUwHUHn" %}

***

### 2. Reconnaissance initiale

Le fichier est de petite taille (**808 octets**) et n'est pas reconnu par `file` :

```
$ file orion_agent.core
orion_agent.core: data
```

Inspection des premiers octets :

```
$ xxd orion_agent.core | head
00000000: 7f48 5453 0201 0100 0000 0000 0000 0000  .HTS............
00000010: 0400 3e00 0100 0000 0000 0000 0000 0000  ..>.............
...
```

Le *magic number* est `7F 48 54 53` = `\x7f` + `"HTS"`. Un vrai ELF commence par `7F 45 4C 46` (`\x7f` + `"ELF"`).

**Le header a été volontairement corrompu** : `HTS` a remplacé `ELF`. Le reste de l'en-tête est cohérent avec un ELF 64 bits :

* `e_ident[EI_CLASS]=2` → ELFCLASS64
* `e_type=4` → **ET\_CORE** (core dump)
* `e_machine=0x3E` → **x86-64**

Il s'agit donc d'un **core dump ELF x86-64** déguisé.

***

### 3. Correction du binaire

On restaure le magic ELF pour rendre le fichier exploitable par les outils standards :

```python
data = bytearray(open("orion_agent.core","rb").read())
data[1:4] = b"ELF"
open("orion_agent.elf","wb").write(data)
```

Le fichier est désormais lisible par `readelf` :

```
$ readelf -a orion_agent.elf
```

***

### 4. Analyse de la structure ELF

Le core ne contient **que deux program headers** :

| PH | Type      | Offset | VAddr      | FileSz | Flags |
| -- | --------- | ------ | ---------- | ------ | ----- |
| 0  | `PT_NOTE` | 0x00b0 | -          | 0x164  | R     |
| 1  | `PT_LOAD` | 0x0214 | 0x00601000 | 0x114  | R/W   |

#### 4.1 La note `NT_PRSTATUS`

```
$ readelf -n orion_agent.elf

Displaying notes found at file offset 0x000000b0 with length 0x00000164:
  Owner                Data size    Description
  CORE                 0x00000150   NT_PRSTATUS (prstatus structure)
```

En parsant manuellement la structure `elf_prstatus` (64 bits), seules **trois valeurs sont non nulles** (tous les registres sont à zéro → dump fabriqué) :

| Offset (note) | Valeur       | Décimal      | Champ         | Interprétation                                   |
| ------------- | ------------ | ------------ | ------------- | ------------------------------------------------ |
| `0x28`        | `0x000012DD` | `4829`       | `pr_pgrp`     | **PID**                                          |
| `0x2C`        | `0x000012DC` | `4828`       | `pr_sid`      | PPID                                             |
| `0x38`        | `0x665674B0` | `1716942000` | `pr_utime`/ts | **Uptime / timestamp** (2024-05-29 00:20:00 UTC) |

#### 4.2 Le segment `PT_LOAD` (le heap)

```
00000210: 0000 0000 4845 4150 5f44 554d 505f 4f52  ....HEAP_DUMP_OR
00000220: 494f 4e5f 4147 454e 5400 ...             ION_AGENT.......
...
00000290: 0000 0000 c850 e562 7528 09ae 130d cac5  .....P.bu(......
000002a0: 2192 de8e 5434 83e7 0700 87eb 1e7d 1165  !...T4.......}.e
...
00000320: 6827 bbb0 ac2b e712                       h'...+..
```

Le segment chargé en mémoire (vaddr `0x601000`) contient :

* Le label ASCII `HEAP_DUMP_ORION_AGENT`
* Une zone de zéros (padding)
* À partir de l'offset fichier `0x294` : **148 octets de données à haute entropie**

C'est certainement notre flag ;).

***

### 5. Reconstruction de la clé

Un indice donné dans le challenge donne la recette : **`seed = PID ^ Uptime`**

```
PID    = 4829       (0x12DD)
Uptime = 1716942000 (0x665674B0)

seed = 4829 ^ 1716942000 = 1716938349
```

Le point clé (et le piège du challenge) : la seed n'est **pas** utilisée comme graine d'un `srand()`/PRNG, mais convertie en **chaîne de caractères décimale** qui sert directement de **clé RC4** :

```
clé RC4 = "1716938349"   (ASCII)
```

***

### 6. Déchiffrement

```python
#!/usr/bin/env python3
from Crypto.Cipher import ARC4

data = open("orion_agent.core", "rb").read()

blob = data[0x294:0x328]

PID    = 4829
UPTIME = 1716942000

seed = PID ^ UPTIME            
key  = str(seed).encode()

print(ARC4.new(key).decrypt(blob).decode())
```

Sortie :

```
--- ORION SECURE TRANSMISSION ---
SENDER_PID: 4829
UPTIME: 1716942000s
FLAG: HTS{qui_a_eteint_mon_elfe_de_maison}
---------------------------------
```

***

### 7. Conclusion

1. **Identifier** le magic corrompu `\x7fHTS` → core dump ELF x86-64 déguisé.
2. **Réparer** le header (`HTS` → `ELF`) pour utiliser `readelf`.
3. **Extraire** de la note `NT_PRSTATUS` : `PID = 4829` et `Uptime = 1716942000`.
4. **Localiser** le blob chiffré dans le segment `PT_LOAD` (`HEAP_DUMP_ORION_AGENT`).
5. **Calculer** la seed : `PID ^ Uptime = 1716938349`.
6. **Déchiffrer** en **RC4** avec la clé = seed sous forme de **chaîne ASCII**.

Avis personnel :&#x20;

* Rien dans le binaire n'indique `seed → str() ASCII → clé RC4`. L'indice « seed = PID ^ Uptime » oriente vers `srand(seed)` + XOR. Le saut vers RC4 textuel relève de la **devinette**. Sans l'indice, le challenge est difficilement réalisable dans le temps du CTF...
