mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
163 lines
6.9 KiB
Markdown
163 lines
6.9 KiB
Markdown
# Synology PAT/SPK Encrypted Archive Decryption
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## Overview
|
||
|
||
Vários dispositivos Synology (DSM/BSM NAS, BeeStation, …) distribuem seu firmware e pacotes de aplicativos em **arquivos PAT / SPK criptografados**. Esses arquivos podem ser descriptografados *offline* com nada além dos arquivos de download públicos, graças a chaves codificadas embutidas dentro das bibliotecas de extração oficiais.
|
||
|
||
Esta página documenta, passo a passo, como o formato criptografado funciona e como recuperar completamente o **TAR** em texto claro que está dentro de cada pacote. O procedimento é baseado na pesquisa da Synacktiv realizada durante o Pwn2Own Irlanda 2024 e implementado na ferramenta de código aberto [`synodecrypt`](https://github.com/synacktiv/synodecrypt).
|
||
|
||
> ⚠️ O formato é exatamente o mesmo para os arquivos `*.pat` (atualização do sistema) e `*.spk` (aplicativo) – eles diferem apenas no par de chaves codificadas que são selecionadas.
|
||
|
||
---
|
||
|
||
## 1. Grab the archive
|
||
|
||
A atualização do firmware/aplicativo pode normalmente ser baixada do portal público da Synology:
|
||
```bash
|
||
$ wget https://archive.synology.com/download/Os/BSM/BSM_BST150-4T_65374.pat
|
||
```
|
||
## 2. Despeje a estrutura PAT (opcional)
|
||
|
||
`*.pat` imagens são, elas mesmas, um **pacote cpio** que incorpora vários arquivos (carregador de inicialização, kernel, rootfs, pacotes…). A ferramenta gratuita [`patology`](https://github.com/sud0woodo/patology) é conveniente para inspecionar esse wrapper:
|
||
```bash
|
||
$ python3 patology.py --dump -i BSM_BST150-4T_65374.pat
|
||
[…]
|
||
$ ls
|
||
DiskCompatibilityDB.tar hda1.tgz rd.bin packages/ …
|
||
```
|
||
Para `*.spk` você pode pular diretamente para o passo 3.
|
||
|
||
## 3. Extraia as bibliotecas de extração da Synology
|
||
|
||
A verdadeira lógica de descriptografia está em:
|
||
|
||
* `/usr/syno/sbin/synoarchive` → wrapper CLI principal
|
||
* `/usr/lib/libsynopkg.so.1` → chama o wrapper da interface do DSM
|
||
* `libsynocodesign.so` → **contém a implementação criptográfica**
|
||
|
||
Ambos os binários estão presentes no rootfs do sistema (`hda1.tgz`) **e** no init-rd comprimido (`rd.bin`). Se você tiver apenas o PAT, pode obtê-los desta forma:
|
||
```bash
|
||
# rd.bin is LZMA-compressed CPIO
|
||
$ lzcat rd.bin | cpio -id 2>/dev/null
|
||
$ file usr/lib/libsynocodesign.so
|
||
usr/lib/libsynocodesign.so: ELF 64-bit LSB shared object, ARM aarch64, …
|
||
```
|
||
## 4. Recuperar as chaves codificadas (`get_keys`)
|
||
|
||
Dentro de `libsynocodesign.so`, a função `get_keys(int keytype)` simplesmente retorna duas variáveis globais de 128 bits para a família de arquivos solicitada:
|
||
```c
|
||
case 0: // PAT (system)
|
||
case 10:
|
||
case 11:
|
||
signature_key = qword_23A40;
|
||
master_key = qword_23A68;
|
||
break;
|
||
|
||
case 3: // SPK (applications)
|
||
signature_key = qword_23AE0;
|
||
master_key = qword_23B08;
|
||
break;
|
||
```
|
||
* **signature_key** → Chave pública Ed25519 usada para verificar o cabeçalho do arquivo.
|
||
* **master_key** → Chave raiz usada para derivar a chave de criptografia por arquivo.
|
||
|
||
Você só precisa despejar essas duas constantes uma vez para cada versão principal do DSM.
|
||
|
||
## 5. Estrutura do cabeçalho e verificação de assinatura
|
||
|
||
`synoarchive_open()` → `support_format_synoarchive()` → `archive_read_support_format_synoarchive()` realiza o seguinte:
|
||
|
||
1. Ler mágica (3 bytes) `0xBFBAAD` **ou** `0xADBEEF`.
|
||
2. Ler little-endian 32-bit `header_len`.
|
||
3. Ler `header_len` bytes + a próxima **assinatura Ed25519 de 0x40 bytes**.
|
||
4. Iterar sobre todas as chaves públicas incorporadas até que `crypto_sign_verify_detached()` tenha sucesso.
|
||
5. Decodificar o cabeçalho com **MessagePack**, resultando em:
|
||
```python
|
||
[
|
||
data: bytes,
|
||
entries: [ [size: int, sha256: bytes], … ],
|
||
archive_description: bytes,
|
||
serial_number: [bytes],
|
||
not_valid_before: int
|
||
]
|
||
```
|
||
`entries` permite que o libarchive verifique a integridade de cada arquivo à medida que é descriptografado.
|
||
|
||
## 6. Derivar a sub-chave por arquivo
|
||
|
||
A partir do blob `data` contido no cabeçalho MessagePack:
|
||
|
||
* `subkey_id` = little-endian `uint64` no deslocamento 0x10
|
||
* `ctx` = 7 bytes no deslocamento 0x18
|
||
|
||
A chave de **stream** de 32 bytes é obtida com libsodium:
|
||
```c
|
||
crypto_kdf_derive_from_key(kdf_subkey, 32, subkey_id, ctx, master_key);
|
||
```
|
||
## 7. Backend **libarchive** personalizado da Synology
|
||
|
||
A Synology agrupa uma libarchive corrigida que registra um formato "tar" falso sempre que o magic é `0xADBEEF`:
|
||
```c
|
||
register_format(
|
||
"tar", spk_bid, spk_options,
|
||
spk_read_header, spk_read_data, spk_read_data_skip,
|
||
NULL, spk_cleanup, NULL, NULL);
|
||
```
|
||
### spk_read_header()
|
||
```
|
||
- Read 0x200 bytes
|
||
- nonce = buf[0:0x18]
|
||
- cipher = buf[0x18:0x18+0x193]
|
||
- crypto_secretstream_xchacha20poly1305_init_pull(state, nonce, kdf_subkey)
|
||
- crypto_secretstream_xchacha20poly1305_pull(state, tar_hdr, …, cipher, 0x193)
|
||
```
|
||
O `tar_hdr` descriptografado é um **cabeçalho TAR POSIX clássico**.
|
||
|
||
### spk_read_data()
|
||
```
|
||
while (remaining > 0):
|
||
chunk_len = min(0x400000, remaining) + 0x11 # +tag
|
||
buf = archive_read_ahead(chunk_len)
|
||
crypto_secretstream_xchacha20poly1305_pull(state, out, …, buf, chunk_len)
|
||
remaining -= chunk_len - 0x11
|
||
```
|
||
Cada **nonce de 0x18 bytes** é precedido ao bloco criptografado.
|
||
|
||
Uma vez que todas as entradas são processadas, libarchive produz um **`.tar`** perfeitamente válido que pode ser descompactado com qualquer ferramenta padrão.
|
||
|
||
## 8. Decrypt everything with synodecrypt
|
||
```bash
|
||
$ python3 synodecrypt.py SynologyPhotos-rtd1619b-1.7.0-0794.spk
|
||
[+] found matching keys (SPK)
|
||
[+] header signature verified
|
||
[+] 104 entries
|
||
[+] archive successfully decrypted → SynologyPhotos-rtd1619b-1.7.0-0794.tar
|
||
|
||
$ tar xf SynologyPhotos-rtd1619b-1.7.0-0794.tar
|
||
```
|
||
`synodecrypt` detecta automaticamente PAT/SPK, carrega as chaves corretas e aplica toda a cadeia descrita acima.
|
||
|
||
## 9. Armadilhas comuns
|
||
|
||
* Não troque `signature_key` e `master_key` – eles servem a propósitos diferentes.
|
||
* O **nonce** vem *antes* do texto cifrado para cada bloco (cabeçalho e dados).
|
||
* O tamanho máximo do bloco criptografado é **0x400000 + 0x11** (tag libsodium).
|
||
* Arquivos criados para uma geração do DSM podem mudar para chaves codificadas diferentes na próxima versão.
|
||
|
||
## 10. Ferramentas adicionais
|
||
|
||
* [`patology`](https://github.com/sud0woodo/patology) – analisar/dump arquivos PAT.
|
||
* [`synodecrypt`](https://github.com/synacktiv/synodecrypt) – descriptografar PAT/SPK/outros.
|
||
* [`libsodium`](https://github.com/jedisct1/libsodium) – implementação de referência de XChaCha20-Poly1305 secretstream.
|
||
* [`msgpack`](https://msgpack.org/) – serialização de cabeçalho.
|
||
|
||
## Referências
|
||
|
||
- [Extração de arquivos criptografados da Synology – Synacktiv (Pwn2Own IE 2024)](https://www.synacktiv.com/publications/extraction-des-archives-chiffrees-synology-pwn2own-irlande-2024.html)
|
||
- [synodecrypt no GitHub](https://github.com/synacktiv/synodecrypt)
|
||
- [patology no GitHub](https://github.com/sud0woodo/patology)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|