mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Add content from: Extraction of Synology Encrypted Archives - Pwn2Own Ireland ...
This commit is contained in:
parent
30fa8082d2
commit
8af174c0ea
@ -768,6 +768,7 @@
|
||||
- [Ret2vDSO](binary-exploitation/rop-return-oriented-programing/ret2vdso.md)
|
||||
- [SROP - Sigreturn-Oriented Programming](binary-exploitation/rop-return-oriented-programing/srop-sigreturn-oriented-programming/README.md)
|
||||
- [SROP - ARM64](binary-exploitation/rop-return-oriented-programing/srop-sigreturn-oriented-programming/srop-arm64.md)
|
||||
- [Synology Encrypted Archive Decryption](hardware-physical-access/firmware-analysis/synology-encrypted-archive-decryption.md)
|
||||
- [Array Indexing](binary-exploitation/array-indexing.md)
|
||||
- [Chrome Exploiting](binary-exploitation/chrome-exploiting.md)
|
||||
- [Integer Overflow](binary-exploitation/integer-overflow.md)
|
||||
|
@ -4,6 +4,13 @@
|
||||
|
||||
## **Introduction**
|
||||
|
||||
### Related resources
|
||||
|
||||
{{#ref}}
|
||||
synology-encrypted-archive-decryption.md
|
||||
{{#endref}}
|
||||
|
||||
|
||||
Firmware is essential software that enables devices to operate correctly by managing and facilitating communication between the hardware components and the software that users interact with. It's stored in permanent memory, ensuring the device can access vital instructions from the moment it's powered on, leading to the operating system's launch. Examining and potentially modifying firmware is a critical step in identifying security vulnerabilities.
|
||||
|
||||
## **Gathering Information**
|
||||
|
@ -0,0 +1,182 @@
|
||||
# Synology PAT/SPK Encrypted Archive Decryption
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Overview
|
||||
|
||||
Several Synology devices (DSM/BSM NAS, BeeStation, …) distribute their firmware and application packages in **encrypted PAT / SPK archives**. Those archives can be decrypted *offline* with nothing but the public download files thanks to hard-coded keys embedded inside the official extraction libraries.
|
||||
|
||||
This page documents, step-by-step, how the encrypted format works and how to fully recover the clear-text **TAR** that sits inside each package. The procedure is based on Synacktiv research performed during Pwn2Own Ireland 2024 and implemented in the open-source tool [`synodecrypt`](https://github.com/synacktiv/synodecrypt).
|
||||
|
||||
> ⚠️ The format is exactly the same for both `*.pat` (system update) and `*.spk` (application) archives – they only differ in the pair of hard-coded keys that are selected.
|
||||
|
||||
---
|
||||
|
||||
## 1. Grab the archive
|
||||
|
||||
The firmware/application update can normally be downloaded from Synology’s public portal:
|
||||
|
||||
```bash
|
||||
$ wget https://archive.synology.com/download/Os/BSM/BSM_BST150-4T_65374.pat
|
||||
```
|
||||
|
||||
## 2. Dump the PAT structure (optional)
|
||||
|
||||
`*.pat` images are themselves a **cpio bundle** that embeds several files (boot loader, kernel, rootfs, packages…). The free utility [`patology`](https://github.com/sud0woodo/patology) is convenient to inspect that wrapper:
|
||||
|
||||
```bash
|
||||
$ python3 patology.py --dump -i BSM_BST150-4T_65374.pat
|
||||
[…]
|
||||
$ ls
|
||||
DiskCompatibilityDB.tar hda1.tgz rd.bin packages/ …
|
||||
```
|
||||
|
||||
For `*.spk` you can directly jump to step 3.
|
||||
|
||||
## 3. Extract the Synology extraction libraries
|
||||
|
||||
The real decryption logic lives in:
|
||||
|
||||
* `/usr/syno/sbin/synoarchive` → main CLI wrapper
|
||||
* `/usr/lib/libsynopkg.so.1` → calls the wrapper from DSM UI
|
||||
* `libsynocodesign.so` → **contains the cryptographic implementation**
|
||||
|
||||
Both binaries are present in the system rootfs (`hda1.tgz`) **and** in the compressed init-rd (`rd.bin`). If you only have the PAT you can get them this way:
|
||||
|
||||
```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. Recover the hard-coded keys (`get_keys`)
|
||||
|
||||
Inside `libsynocodesign.so` the function `get_keys(int keytype)` simply returns two 128-bit global variables for the requested archive family:
|
||||
|
||||
```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** → Ed25519 public key used to verify the archive header.
|
||||
* **master_key** → Root key used to derive the per-archive encryption key.
|
||||
|
||||
You only have to dump those two constants once for each DSM major version.
|
||||
|
||||
## 5. Header structure & signature verification
|
||||
|
||||
`synoarchive_open()` → `support_format_synoarchive()` → `archive_read_support_format_synoarchive()` performs the following:
|
||||
|
||||
1. Read magic (3 bytes) `0xBFBAAD` **or** `0xADBEEF`.
|
||||
2. Read little-endian 32-bit `header_len`.
|
||||
3. Read `header_len` bytes + the next **0x40-byte Ed25519 signature**.
|
||||
4. Iterate over all embedded public keys until `crypto_sign_verify_detached()` succeeds.
|
||||
5. Decode the header with **MessagePack**, yielding:
|
||||
|
||||
```python
|
||||
[
|
||||
data: bytes,
|
||||
entries: [ [size: int, sha256: bytes], … ],
|
||||
archive_description: bytes,
|
||||
serial_number: [bytes],
|
||||
not_valid_before: int
|
||||
]
|
||||
```
|
||||
|
||||
`entries` later allows libarchive to integrity-check each file as it is decrypted.
|
||||
|
||||
## 6. Derive the per-archive sub-key
|
||||
|
||||
From the `data` blob contained in the MessagePack header:
|
||||
|
||||
* `subkey_id` = little-endian `uint64` at offset 0x10
|
||||
* `ctx` = 7 bytes at offset 0x18
|
||||
|
||||
The 32-byte **stream key** is obtained with libsodium:
|
||||
|
||||
```c
|
||||
crypto_kdf_derive_from_key(kdf_subkey, 32, subkey_id, ctx, master_key);
|
||||
```
|
||||
|
||||
## 7. Synology’s custom **libarchive** backend
|
||||
|
||||
Synology bundles a patched libarchive that registers a fake "tar" format whenever the magic is `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)
|
||||
```
|
||||
|
||||
The decrypted `tar_hdr` is a **classical POSIX TAR header**.
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
Each **0x18-byte nonce** is prepended to the encrypted chunk.
|
||||
|
||||
Once all entries are processed libarchive produces a perfectly valid **`.tar`** that can be unpacked with any standard tool.
|
||||
|
||||
## 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` automatically detects PAT/SPK, loads the correct keys and applies the full chain described above.
|
||||
|
||||
## 9. Common pitfalls
|
||||
|
||||
* Do **not** swap `signature_key` and `master_key` – they serve different purposes.
|
||||
* The **nonce** comes *before* the ciphertext for every block (header and data).
|
||||
* The maximum encrypted chunk size is **0x400000 + 0x11** (libsodium tag).
|
||||
* Archives created for one DSM generation may switch to different hard-coded keys in the next release.
|
||||
|
||||
## 10. Additional tooling
|
||||
|
||||
* [`patology`](https://github.com/sud0woodo/patology) – parse/dump PAT archives.
|
||||
* [`synodecrypt`](https://github.com/synacktiv/synodecrypt) – decrypt PAT/SPK/others.
|
||||
* [`libsodium`](https://github.com/jedisct1/libsodium) – reference implementation of XChaCha20-Poly1305 secretstream.
|
||||
* [`msgpack`](https://msgpack.org/) – header serialisation.
|
||||
|
||||
## References
|
||||
|
||||
- [Extraction of Synology encrypted archives – Synacktiv (Pwn2Own IE 2024)](https://www.synacktiv.com/publications/extraction-des-archives-chiffrees-synology-pwn2own-irlande-2024.html)
|
||||
- [synodecrypt on GitHub](https://github.com/synacktiv/synodecrypt)
|
||||
- [patology on GitHub](https://github.com/sud0woodo/patology)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -129,6 +129,32 @@ powershell C:**2\n??e*d.*? # notepad
|
||||
../linux-hardening/bypass-bash-restrictions/
|
||||
{{#endref}}
|
||||
|
||||
### Node.js `child_process.exec` vs `execFile`
|
||||
|
||||
When auditing JavaScript/TypeScript back-ends you will often encounter the Node.js `child_process` API.
|
||||
|
||||
```javascript
|
||||
// Vulnerable: user-controlled variables interpolated inside a template string
|
||||
const { exec } = require('child_process');
|
||||
exec(`/usr/bin/do-something --id_user ${id_user} --payload '${JSON.stringify(payload)}'`, (err, stdout) => {
|
||||
/* … */
|
||||
});
|
||||
```
|
||||
|
||||
`exec()` spawns a **shell** (`/bin/sh -c`), therefore any character that has a special meaning to the shell (back-ticks, `;`, `&&`, `|`, `$()`, …) will result in **command injection** when user input is concatenated in the string.
|
||||
|
||||
**Mitigation:** use `execFile()` (or `spawn()` without the `shell` option) and provide **each argument as a separate array element** so no shell is involved:
|
||||
|
||||
```javascript
|
||||
const { execFile } = require('child_process');
|
||||
execFile('/usr/bin/do-something', [
|
||||
'--id_user', id_user,
|
||||
'--payload', JSON.stringify(payload)
|
||||
]);
|
||||
```
|
||||
|
||||
Real-world case: *Synology Photos* ≤ 1.7.0-0794 was exploitable through an unauthenticated WebSocket event that placed attacker controlled data into `id_user` which was later embedded in an `exec()` call, achieving RCE (Pwn2Own Ireland 2024).
|
||||
|
||||
## Brute-Force Detection List
|
||||
|
||||
{{#ref}}
|
||||
@ -137,7 +163,9 @@ https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/command_inject
|
||||
|
||||
## References
|
||||
|
||||
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Command%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Command%20Injection)
|
||||
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Command%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Command%20Injection)
|
||||
- [https://portswigger.net/web-security/os-command-injection](https://portswigger.net/web-security/os-command-injection)
|
||||
- [Extraction of Synology encrypted archives – Synacktiv 2025](https://www.synacktiv.com/publications/extraction-des-archives-chiffrees-synology-pwn2own-irlande-2024.html)
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -48,7 +48,7 @@ Yes, you can, but **don't forget to mention the specific link(s)** where the con
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> - **How can I cite a page of HackTricks?**
|
||||
> - **How can I a page of HackTricks?**
|
||||
|
||||
As long as the link **of** the page(s) where you took the information from appears it's enough.\
|
||||
If you need a bibtex you can use something like:
|
||||
@ -144,4 +144,3 @@ This license does not grant any trademark or branding rights in relation to the
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user