Implement authorization control system? #5

Open
opened 2026-05-28 20:28:58 +00:00 by j4k0xb · 3 comments
Member

The network plugin includes secrets - most notably an RSA private key - but it is not used as a conventional security measure. Instead, it serves as a proof-of-possession mechanism to indicate to the printer/cloud that the official plugin is in use, effectively functioning as an ecosystem control feature.
Of course, the term "proof-of-possession" feels a bit ironic in practice, given that the key is not actually secret in any meaningful sense and is shipped to every user through the network plugin installation.
Here's my more detailed research about the implementation: https://f.sfconservancy.org/j4k0xb/reverse-networking/src/branch/authorization-control/Authorization%20Control

In practice, any legitimate user with a valid account and printer can perform the relevant "critical operations" through Bambu Studio. However, without these cryptographic components, full compatibility with newer firmware versions and the associated cloud features is not achievable.
It should also be noted that developer mode does not provide full simultaneous LAN and cloud operation, nor cloud compatibility, since some features remain gated by authentication and attestation mechanisms.

Regarding the baltobu reverse-networking project:

  • Will this project aim for a minimal, clean-room reimplementation with partial functionality,
  • or leverage the distributed secrets - which would remain subject to AGPLv3 - to achieve full compatibility with printers and the cloud, while staying within legal and ethical boundaries?

I'm asking not only because of third-party slicers, but also regarding Bambu Studio itself:

SFC and our volunteers are within our rights to reverse-engineer these libraries for the purpose of creating our own Source Code that can function as a drop-in replacement in Bambu Studio.
https://sfconservancy.org/news/2026/may/18/bambu-studio-3d-printer-agpl-violation-response/

The network plugin includes secrets - most notably an RSA private key - but it is not used as a conventional security measure. Instead, it serves as a proof-of-possession mechanism to indicate to the printer/cloud that the official plugin is in use, effectively functioning as an ecosystem control feature. Of course, the term "proof-of-possession" feels a bit ironic in practice, given that the key is not actually secret in any meaningful sense and is shipped to every user through the network plugin installation. Here's my more detailed research about the implementation: https://f.sfconservancy.org/j4k0xb/reverse-networking/src/branch/authorization-control/Authorization%20Control In practice, any legitimate user with a valid account and printer can perform the relevant ["critical operations"](https://blog.bambulab.com/firmware-update-introducing-new-authorization-control-system-2/) through Bambu Studio. However, without these cryptographic components, full compatibility with newer firmware versions and the associated cloud features is not achievable. It should also be noted that [developer mode](https://wiki.bambulab.com/en/knowledge-sharing/enable-developer-mode) does not provide full simultaneous LAN and cloud operation, nor cloud compatibility, since some features remain gated by authentication and attestation mechanisms. **Regarding the baltobu reverse-networking project:** - Will this project aim for a minimal, clean-room reimplementation with partial functionality, - or leverage the distributed secrets - which would remain subject to AGPLv3 - to achieve full compatibility with printers and the cloud, while staying within legal and ethical boundaries? I'm asking not only because of third-party slicers, but also regarding Bambu Studio itself: > SFC and our volunteers are within our rights to reverse-engineer these libraries for the purpose of creating our own Source Code that can function as a drop-in replacement in Bambu Studio. > https://sfconservancy.org/news/2026/may/18/bambu-studio-3d-printer-agpl-violation-response/
Member

Hi — thanks for the Authorization Control / credential-rotation write-up. I’ve been reproducing it against the live api.bambulab.com endpoint and the official Linux libbambu_networking.so (Studio / network agent path, not Bambu Connect’s Electron JS).

Doc correction (request side)
In Credential Rotation, step 3 says to RSA-wrap session_key with the app certificate root public key. That always gets me HTTP code:101 (“outdated”). What actually works is a different RSA-2048 public key baked into the plugin (cloud wrap key; not application_root / chain root). With that key, PKCS#1 v1.5 wrap of a random 32-byte session_key, and AES-256-GCM of client_auth_secret in the path (as documented), the API returns code:0 with cert / crl / cert_id.

What works for me

  • Extract client_auth_secret and the wrap key from a live official plugin
  • Replay / hand-build the rotation GET successfully
  • Recover the live session_key from process memory (matches the URL’s encrypted secret)
  • Capture live response.key blobs (always 704 bytes)
  • Still get the slicer private key via RSA CRT capture in the plugin (so the material exists — I just can’t unpack response.key)

What’s stuck
Decrypting response.key to PEM/CRT with the request session_key under the documented layout (IV[12] || ct || tag[16] → AES-256-GCM) always fails with InvalidTag. Same for HKDF/SHA256 of session_key, CBC/CTR/etc., and treating the 672-byte body as the GCM payload. Flat GCM(PEM) would also be ~1.7 KB, not 704 B; GCM(CRT 640) would be 668 B — close to 672, but still not decrypting.

I also drove bambu_network_update_cert on the official plugin, caught URL + session_key + candidate 704 B frames in memory, and put HW watchpoints on the blob — no useful decrypt traps yet (path looks VMProtect’d / decrypt_aes_256_gcm inside update_cert_protected).

Question
For the native Studio/plugin path (not Connect JS), is response.key still AES-GCM with the same request session_key and the flat IV||ct||tag packing you document, or is there a different frame/KDF/AAD (given the stable 28+672 layout)? Any pointer on how the official client unpacks that field would help a lot.

Happy to share a matching (session_key, response.key) pair offline if useful.

Thanks again for the docs.

Hi — thanks for the Authorization Control / credential-rotation write-up. I’ve been reproducing it against the live `api.bambulab.com` endpoint and the official Linux `libbambu_networking.so` (Studio / network agent path, not Bambu Connect’s Electron JS). **Doc correction (request side)** In *Credential Rotation*, step 3 says to RSA-wrap `session_key` with the **app certificate root** public key. That always gets me HTTP `code:101` (“outdated”). What actually works is a **different RSA-2048 public key baked into the plugin** (cloud wrap key; not `application_root` / chain root). With that key, PKCS#1 v1.5 wrap of a random 32-byte `session_key`, and AES-256-GCM of `client_auth_secret` in the path (as documented), the API returns `code:0` with `cert` / `crl` / `cert_id`. **What works for me** - Extract `client_auth_secret` and the wrap key from a live official plugin - Replay / hand-build the rotation GET successfully - Recover the live `session_key` from process memory (matches the URL’s encrypted secret) - Capture live `response.key` blobs (always **704 bytes**) - Still get the slicer private key via RSA CRT capture in the plugin (so the material exists — I just can’t unpack `response.key`) **What’s stuck** Decrypting `response.key` to PEM/CRT with the request `session_key` under the documented layout (`IV[12] || ct || tag[16]` → AES-256-GCM) always fails with **InvalidTag**. Same for HKDF/SHA256 of `session_key`, CBC/CTR/etc., and treating the 672-byte body as the GCM payload. Flat GCM(PEM) would also be ~1.7 KB, not 704 B; GCM(CRT 640) would be 668 B — close to 672, but still not decrypting. I also drove `bambu_network_update_cert` on the official plugin, caught URL + `session_key` + candidate 704 B frames in memory, and put HW watchpoints on the blob — no useful decrypt traps yet (path looks VMProtect’d / `decrypt_aes_256_gcm` inside `update_cert_protected`). **Question** For the **native Studio/plugin** path (not Connect JS), is `response.key` still AES-GCM with the same request `session_key` and the flat `IV||ct||tag` packing you document, or is there a different frame/KDF/AAD (given the stable 28+672 layout)? Any pointer on how the official client unpacks that field would help a lot. Happy to share a matching `(session_key, response.key)` pair offline if useful. Thanks again for the docs.
Author
Member

Hi, the code may have changed a bit since I last looked at it. afaik libbambu_networking and bambu connect use the same implementation (compared using observed http and mqtt traffic).

Accidentally selected the cert based on issuer CN instead of subject CN . Updated the docs to use service.bambulab.com.
j4k0xb/reverse-networking@eee3140455

Please confirm if this works or whether there are other discrepancies: https://bambuzled.github.io/posts/bambu-auth-control/

That said, while the request data looks identical, the response returned by the server has changed a bit. It’s no longer Nonce||Ciphertext||Tag, it’s Header||Length||Ciphertext, where Header is likely Nonce||Tag. This appears to be fundamentally the same cryptographic response, just rearranged a bit. So there’s nothing that obviously indicates the decryption process has changed, but there’s always the possibility that Bambu added some additional obfuscation steps in there somewhere.

I updated it for now (j4k0xb/reverse-networking@08ac7dcbfe, j4k0xb/reverse-networking@b30a368a6b) and confirmed Length, the other fields are an assumption until proven.

Hi, the code may have changed a bit since I last looked at it. afaik libbambu_networking and bambu connect use the same implementation (compared using observed http and mqtt traffic). Accidentally selected the cert based on issuer CN instead of subject CN . Updated the docs to use `service.bambulab.com`. https://f.sfconservancy.org/j4k0xb/reverse-networking/commit/eee31404559c3a6401cff6ad9adb4eac086e8c8e Please confirm if this works or whether there are other discrepancies: https://bambuzled.github.io/posts/bambu-auth-control/ > That said, while the request data looks identical, the response returned by the server has changed a bit. It’s no longer Nonce||Ciphertext||Tag, it’s Header||Length||Ciphertext, where Header is likely Nonce||Tag. This appears to be fundamentally the same cryptographic response, just rearranged a bit. So there’s nothing that obviously indicates the decryption process has changed, but there’s always the possibility that Bambu added some additional obfuscation steps in there somewhere. I updated it for now (https://f.sfconservancy.org/j4k0xb/reverse-networking/commit/08ac7dcbfe5d1e5ad254d6a176902be6e39b7404, https://f.sfconservancy.org/j4k0xb/reverse-networking/commit/b30a368a6b30ddd76d771649eece73109db9ea80) and confirmed Length, the other fields are an assumption until proven.
Member

No luck so far - I still can’t decode response.key :(

No luck so far - I still can’t decode `response.key` :(
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
baltobu/reverse-networking#5
No description provided.