On 4 November 2026 Debian and Ubuntu shipped fixes for CVE-2026-109518, a bug in APT that lets an attacker who can tamper with your package downloads swap a package after APT has verified it, and have the swapped one installed as root. Canonical rates it 7.7 (High) under CVSS 4.0. Every default Debian 12, Debian 13 and Ubuntu 22.04-or-newer machine that fetches packages over plain HTTP meets the preconditions, and that includes most cloud images, Dockerfiles and CI runners.
This article explains how the swap works, how to tell in two commands whether a machine is exposed, which apt version fixes each release, and how to upgrade without the upgrade itself being the moment you get hit. It is written for anyone who runs Debian or Ubuntu servers, builds container images on them, or maintains a mirror or caching proxy.
| What | APT installs a package that did not pass verification; its maintainer scripts run as root |
| Who can exploit it | An on-path attacker on the HTTP connection, or a malicious mirror or proxy (Ubuntu) |
| Preconditions | HTTP (not HTTPS) sources and a non-zero Acquire::Retries |
| Severity | CVSS 4.0 7.7, High (Canonical) |
| Advisories | DSA-6607-1 (Debian 13), DLA-4880-1 (Debian 12), USN-8996-1 (Ubuntu) |
| Found by | Max Justicz |
| Exploited in the wild? | Not known at disclosure; not in CISA KEV as of 4 November (Brandefense) |
What is CVE-2026-109518?
CVE-2026-109518 is a download verification bypass in APT's HTTP fetching code. In Ubuntu's words, APT "mishandled hard links when encountering redirects and retrying package downloads", so "a remote, on-path attacker could possibly exploit this to trick APT into installing an unverified package and achieve remote code execution with root privileges" (USN-8996-1).
Because the flaw sits in the HTTP backend of the APT library, every front end that uses it is affected: apt, apt-get, aptitude and the Ubuntu graphical updaters. FTP and other transports are not affected, HTTPS protects against the on-path case, and Snap packages are out of scope (Canonical).
How an attacker swaps a package after APT has verified it
The short answer: two downloads are made to share one file on disk, the first is verified, and the attacker then writes into the second. APT's signature model is sound; the bug is in how the downloaded bytes are stored between the check and the install, which makes it a classic time-of-check to time-of-use race.
APT trusts packages because the signed InRelease file lists the hash of each Packages index, and each index lists the hash of each .deb. An attacker who cannot forge a signature has to make APT check one set of bytes and install another. The sequence below follows ThreatDefence's reading of the advisories and fix; Debian's maintainers have not published a step-by-step account, so treat the ordering as an informed reconstruction rather than the official one.
- APT downloads packages over HTTP during an install or upgrade.
- The attacker answers with redirects that point two different package downloads at the same address, and APT ends up with the two target files hard-linked to each other on disk.
- The first download completes with genuine bytes and passes its hash check.
- The attacker returns a transient server error for the second, so APT retries and resumes it.
- On resume, APT reopens the existing file and writes into it. Because that file is a hard link to the already verified package, the attacker's bytes overwrite the verified one.
- dpkg installs the replaced
.deb, and its maintainer scripts run as root.
- On-path attacker → APT http method: redirect both to one URL
- APT http method → partial/pkg-a.deb: download + hash OK
- partial/pkg-a.deb ↔ partial/pkg-b.deb: hard link
- On-path attacker → APT http method: 5xx, then resume
- APT http method → partial/pkg-b.deb: resume writes here
- partial/pkg-a.deb → dpkg: install
The fix in upstream commit 22d0b1f1, titled "methods: Open target files exclusively", makes APT's download methods open their target files exclusively, so a second download can no longer write into a file another one already owns (Linuxiac, ThreatDefence). Upstream shipped it in APT 3.3.6 alongside two unrelated out-of-bounds fixes in the .deb parser and the solver.
Am I affected? Check HTTP sources and Acquire::Retries
You are exposed if both of these are true: at least one APT source uses http://, and Acquire::Retries is greater than zero. Retries default to 3 from APT 2.3.2 onwards (ThreatDefence), which is why Ubuntu says 22.04 LTS and newer are affected by default while older releases are affected only if someone turned retries on (Canonical). The attacker also needs a position on the network path or control of a mirror or proxy you use.
Here is the check on a stock Ubuntu 24.04 machine before patching:
$ dpkg-query -W -f='${Version}\n' apt2.8.3$ grep -rhE '^(deb |URIs:)' /etc/apt/sources.list /etc/apt/sources.list.d/URIs: http://archive.ubuntu.com/ubuntu/URIs: http://security.ubuntu.com/ubuntu/$ apt-config dump | grep -i retries$ apt-config shell R Acquire::Retries; echo "R=$R"R=Both sources are plain HTTP, and the apt version is below the fixed 2.8.4ubuntu0.1. Note what the last two commands show: nothing. Several write-ups suggest apt-config dump | grep Acquire::Retries as the test, but on this system the default of 3 is compiled in and never appears in the configuration dump. An empty result means "not overridden", not "zero". Only an explicit Acquire::Retries "0"; in /etc/apt/apt.conf.d/ turns retries off.
Fixed apt versions for Debian and Ubuntu
Upgrade the apt package (and libapt-pkg, which comes from the same source) to at least the version below for your release. The Ubuntu rows come from USN-8996-1; the Debian rows from the Debian advisories as quoted by ThreatDefence and Brandefense.
| Release | Fixed apt version | Where it comes from |
|---|---|---|
| Debian 13 trixie | 3.0.4 | DSA-6607-1, security archive |
| Debian 12 bookworm | 2.6.2 | DLA-4880-1 |
| Debian unstable sid | 3.3.6 | Uploaded 4 November |
| Debian testing forky | None yet (3.3.5 vulnerable) | Waiting for 3.3.6 to migrate |
| Ubuntu 26.10 | 3.3.3ubuntu0.1 | Security pocket |
| Ubuntu 26.04 LTS | 3.2.1ubuntu0.1 | Security pocket |
| Ubuntu 24.04 LTS | 2.8.4ubuntu0.1 | Security pocket |
| Ubuntu 22.04 LTS | 2.4.15ubuntu0.1 | Security pocket |
| Ubuntu 20.04 / 18.04 LTS | 2.0.12+esm1 / 1.6.19+esm1 | Ubuntu Pro (ESM) |
| Ubuntu 16.04 / 14.04 LTS | 1.2.36+esm1 / 1.0.1ubuntu2.24+esm4 | Ubuntu Pro, Legacy add-on |
How to patch apt without the upgrade itself being the attack
Run the upgrade with retries switched off for that one command. The fix travels through the same vulnerable code path it repairs, so a machine on a hostile network could be attacked while downloading the patch. Canonical's own instructions do exactly this:
sudo apt updatesudo apt install -o Acquire::Retries=0 --only-upgrade '?source-package(^apt$)' # or the full upgradesudo apt upgrade -o Acquire::Retries=0The ?source-package(^apt$) pattern selects every binary built from the apt source, so libapt-pkg is upgraded in step with apt. Then check: apt list --installed '?source-package(^apt$)' should show the fixed version on every line (Canonical). unattended-upgrades, on by default since Ubuntu 16.04, will apply the fix within about a day if you leave it alone; on machines that sit on networks you do not trust, drop an Acquire::Retries "0"; file into /etc/apt/apt.conf.d/ first so that automatic run is covered too.
Next, move your sources to HTTPS, which removes the on-path attacker from the picture for this bug and the next one. On Ubuntu, Canonical publishes this one-liner for the official archives:
sudo sed -Ei 's!http://((([a-z]+\.)?archive|security|ports)\.ubuntu\.com)!https://\1!' \ /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sourcessudo apt updateIt deliberately leaves third-party repositories and regional mirrors alone, because some community mirrors do not serve HTTPS; check those by hand. HTTPS needs ca-certificates, which minimal images often lack, so install that first (with retries off) before switching. HTTPS does not protect you from a malicious mirror you chose to trust, so patching is still the fix; HTTPS is the mitigation.
Docker images, CI runners and mirrors need the fix too
Every RUN apt-get in a Dockerfile runs this code as root, and many build networks route through a caching proxy. A base image pulled last week ships the vulnerable apt, so the first apt-get install in your build uses it. Rebuild from a fresh base image once your distribution's tag has been refreshed, and until then turn retries off inside the build:
FROM debian:trixie-slim RUN echo 'Acquire::Retries "0";' > /etc/apt/apt.conf.d/99no-retries \ && apt-get update \ && apt-get install -y --no-install-recommends --only-upgrade apt \ && apt-get install -y --no-install-recommends ca-certificates \ && sed -i 's|http://deb.debian.org|https://deb.debian.org|g' /etc/apt/sources.list.d/debian.sources \ && rm -rf /var/lib/apt/lists/*The order matters: retries off first, then upgrade apt itself, then install ca-certificates, and only then switch the deb822 sources file to HTTPS, because without CA certificates HTTPS fetches fail. Remove the 99no-retries file once your base image carries the fix, or flaky networks will start failing builds that retries used to hide.
Mirror and proxy operators (apt-cacher-ng, Artifactory, Nexus, a squid in front of CI) are on the path by design. They are not vulnerable themselves, but a compromised one is exactly the attacker this bug needs, so this is a good week to make sure they fetch upstream over HTTPS and are patched.
Why dpkg --verify will not tell you whether you were hit
dpkg --verify compares installed files with the checksums shipped inside the package, and a swapped package carries its own matching checksums. The useful evidence is outside the package: the cached .deb hashes against the signed index (apt-cache show pkg=version lists the expected SHA256), /var/log/apt/history.log and /var/log/dpkg.log for unexpected installs, and proxy logs showing .deb requests that got 3xx and 5xx answers from the same client in a short window (ThreatDefence). An empty /var/cache/apt/archives proves nothing either way, since most images clean it.
For most fleets the honest position is that there is no known exploitation at disclosure and the precondition is a network attacker, so patch, switch to HTTPS, and look harder only at machines that install packages on networks you do not control, such as laptops, edge devices and build agents on shared runners.
Why APT keeps getting bitten by its HTTP transport
This is not Max Justicz's first APT bug. In 2019 he reported CVE-2019-3462, where a crafted HTTP redirect let an on-path attacker inject content into APT's internal protocol and also install packages as root. Both bugs share a shape: the cryptography was fine, and the attacker went after the plumbing between the network and the verified bytes.
Signatures protect the bytes APT checks; they say nothing about the bytes it later installs.
APT kept HTTP as the default because packages are signed and HTTP mirrors cache well. That argument is correct about forgery and silent about bugs in the code that handles the download. HTTPS would not have fixed the hard-link logic, but it would have taken the on-path attacker, the realistic exploiter here, out of the threat model.
The one thing to remember: on Debian and Ubuntu, "signed packages" only protects you if the code between download and install is bug-free, and this week it was not. Upgrade apt with -o Acquire::Retries=0, rebuild your base images, and move your sources to HTTPS so the next bug of this kind needs a compromised mirror rather than a café Wi-Fi network.
Questions people ask about CVE-2026-109518
What is CVE-2026-109518?
It is a bug in APT, the Debian and Ubuntu package manager, where redirects and retried HTTP downloads could make two downloads share one hard-linked file. An on-path attacker could then overwrite a package after it was verified and have it installed as root.
Is my Ubuntu or Debian server vulnerable?
It is exposed if apt is older than the fixed version for your release, at least one source uses http://, and Acquire::Retries is not zero. Debian 12, Debian 13 and Ubuntu 22.04 or newer have retries on by default.
Which apt version fixes CVE-2026-109518?
Ubuntu 24.04 needs 2.8.4ubuntu0.1, 22.04 needs 2.4.15ubuntu0.1 and 26.04 needs 3.2.1ubuntu0.1. Debian 13 needs 3.0.4 and Debian 12 needs 2.6.2; see the table above for other releases.
Does using HTTPS sources protect me?
HTTPS stops an on-path attacker from tampering with downloads, which is the main way to exploit this bug. It does not protect against a malicious mirror you chose, so you still need the patched apt.
Is CVE-2026-109518 being exploited?
No exploitation was known at disclosure on 4 November 2026, and it was not in CISA's Known Exploited Vulnerabilities catalogue. That can change, so check the CVE record.
Why does apt-config dump not show Acquire::Retries?
The default value is compiled into apt and is not printed unless a configuration file sets it. An empty result means the default applies, which is 3 on current releases, not 0.
Do Docker images need rebuilding?
Yes. Images built from an older base keep the vulnerable apt, and every apt-get in a Dockerfile runs as root during the build, so rebuild from a refreshed base image or turn retries off in the build until you can.
Are Snap or Flatpak packages affected?
Canonical says Snap packages are not affected. The bug is in APT's HTTP download code, so only installs and upgrades done through APT front ends such as apt, apt-get and aptitude are in scope.
Sources and further reading
- Ubuntu: USN-8996-1: APT vulnerability and APT vulnerability fixes available
- Debian: DSA-6607-1 apt and DLA-4880-1 apt
- Upstream fix: methods: Open target files exclusively (22d0b1f1); APT 3.3.6 release notes summary
- CVE records: CVE.org, NVD
- Analysis: ThreatDefence on patching and hunting, Brandefense explainer, RuntimeWire
- Background: Max Justicz, Remote Code Execution in apt/apt-get (CVE-2019-3462)
Discussion
Join the discussion
Sign in with Google, Apple or an email code to comment and reply.