Why the fuck did I write a Rust crate?
tl;dr summary
I wrote a Rust crate for Freebox OS because vulnerability research stopped being fun once curl and Python scripts became the weakest part of the investigation. The crate turned auth, tokens, TLS, typed endpoints, and raw probing into reliable primitives while I reported two vulnerabilities, kept digging into a third, and accidentally rediscovered an old Freebox easter egg.
table of contents
This whole thing started with a stupidly simple question:
Could I get something RCE-shaped on a Freebox, software-only?
I had been reading hardware-heavy research, especially Lennert Wouters’ Starlink terminal work. The public write-ups are great: WIRED covered the custom modchip and voltage fault injection path, Ars Technica wrote about the solder-heavy route to code execution, and the talk, Glitched on Earth by Humans, is exactly the kind of thing that makes consumer network hardware look much more interesting.
I loved the idea. Unfortunately, I suck at hardware.
So I looked at my Freebox instead: a router in my living room, full of software, exposing a documented API, and carrying years of historical Freebox weirdness.
The weekend ended with:
- a Rust crate for Freebox OS
- two privately reported vulnerabilities, self-scored at CVSS 8.5 and 8.7
- a third bug I am still investigating because it got me weirdly close to root
- the rediscovery of an old easter egg that made the whole thing feel like software archaeology
No endpoints, no payloads, no repro steps.
The cursed beginning
The first research stack was extremely normal:
curl- Python +
requests - JSON
- regret
That was fine for the first hour. Then Freebox OS started being bigger than my scripts.
The API has auth, permissions, system config, LAN, DHCP, filesystem operations, downloads, Wi-Fi, TV/PVR, player control, storage, firewall, notifications, VMs, domotics, and a long tail of optional domains. Every idea required the same setup: discover the API version, handle the login challenge, compute the session password, carry a session token, refresh it when needed, store the app token, and then finally send the request I actually cared about.
That ceremony became the weakest part of the research.
So I wrote a crate.
Why Rust?
The honest answer is that I wanted to learn Rust.
The nicer answer is that Rust fit the job pretty well. The crate needed typed auth, careful token storage, async HTTP, TLS handling, redacted secrets, endpoint models, request builders, and raw escape hatches for parts of the API that were not stable enough to model yet.
The API ended up looking boring in a useful way:
let client = Freebox::client_for(&base_url, TlsPolicy::Auto)?;
let token_store = TokenStore::default_for(&app)?;
let token = match token_store.load(&app)? {
TokenLoad::Valid(token) => token,
TokenLoad::Missing | TokenLoad::NeedsAuthorization => {
authorize_app(&client, &base_url, &app, &token_store, config, |track_id| {
eprintln!("approve this app on the Freebox display: {track_id}");
})
.await?
}
};
let freebox = Freebox::from_app_token(client, base_url, app.app_id, token.app_token);
let config = freebox.system().config().await?;
Freebox OS auth also has a physical approval flow. A tool asks for authorization, the actual Freebox shows a prompt, and you approve it on the device. That is cool as hell the first time. It is also annoying when every script reinvents token storage.
Once the crate handled pairing, tokens, sessions, TLS, refresh, and redacted debug output, I could stop thinking about the boring parts.
What the crate changed
The useful thing was speed.
I could authenticate cleanly, enumerate domains, inspect raw shapes, promote stable responses into typed models, mutate config through builders, compare state before and after a change, and fuzz around an endpoint without rewriting setup.
Manual API fuzzing is usually structured, not random screaming. You vary one field, keep the rest stable, observe error classes, pivot to nearby endpoints, compare authenticated and unauthenticated behavior, and keep enough notes to reconstruct what happened.
The difference between “I should test this later” and “I can test this in thirty seconds” is huge. Most ideas are wrong. Good tooling lets you kill the bad ones quickly and spend more time on the weird ones.
And god did I have weird ideas, including physically shaking my Freebox at one point.
What I found
I privately reported two vulnerabilities.
At the time of writing, both are still in triage and still worked on Freebox OS 4.10.1. That is timeline context, not an invitation.
My self-assessed CVSS 3.1 scores:
| Finding | Status | Self-assessed score | High-level impact |
|---|---|---|---|
| Vulnerability 1 | reported, triage ongoing | 8.5 | 1-click account takeover on LAN |
| Vulnerability 2 | reported, triage ongoing | 8.7 | 1-click account takeover |
| Vulnerability 3 | not reported yet, still investigating | ? | got me unusually close to root privileges |
The third bug is still a rabbit hole. I have not reported it yet because I want to understand whether it can become an RCE. If it can, that gives me a much better way to explore the runtime and find more issues responsibly.
It also made me rediscover an old easter egg.
The easter egg
Old consumer devices are funny because code survives.
Freebox history has a lot of that energy. A Univers Freebox article about Frédéric Hoguin’s old Freebox HD work describes a 13-year-old story involving Doom, root access, and developer easter eggs. It mentions user id 4242, a Hitchhiker’s Guide to the Galaxy reference, and a root password string containing find_this_openfreebox as a nod to OpenFreebox: Après 13 ans, une faille insolite et ultra pratique sur une ancienne Freebox dévoilée mais….
Rediscovering that kind of thing during modern security research feels like opening a wall in your apartment and finding a message from a previous tenant.
screenshot of me, reading /etc/passwd remotely, by exploiting my fancy third vulnerability
Why the crate still matters
Even with the security motivation, freebox-rs is meant to be a real Freebox OS client.
It is async, token-aware, TLS-aware, typed where useful, raw where necessary, and usable for normal automation. The security angle just made the design pressure much higher.
The crate does not need exploit code to be security-relevant. It gives researchers and automation code a stable way to interact with Freebox OS. That is already useful.
So, why the fuck did I write a Rust crate?
Because curl got me curious, Python got me moving, and then both became too annoying for the research I wanted to do.
Because Freebox OS has enough surface area that the boring parts had to be solid before the weird parts became visible.
Because I wanted to learn Rust on something real.
And because after one weekend, two private reports, one still-unreported rabbit hole, and one decade-old easter egg, the crate stopped feeling like side tooling and started feeling like the only sane way to keep going.