On November 4, OpenAI published a research post saying one automated run produced working root exploits for 948 distinct Linux kernel bugs, at a cost of $1,213,430 in API credits. That works out to $1,279 per exploit by OpenAI's count. The post also contains a sentence every developer who runs a Linux box should read twice: for the near and medium term, a low-privileged account on an unpatched system should be considered "functionally equivalent to root access for a well-resourced threat actor."
Most of the coverage stopped at the headline number. The useful part sits lower down, in the stage-by-stage costs and in the mistakes OpenAI admits to. This article pulls out what changes for small teams, including iOS and mobile teams whose apps talk to Linux backends and whose builds or coding agents run on Linux machines. Then it walks through the fixes in the order I would do them.
What OpenAI reported
The run happened in mid-October 2026 against the latest Linux kernel, using an unreleased model OpenAI calls only an "upcoming cyber model." The target virtual machines ran a stock desktop operating system with normal protections switched on, including SMEP, SMAP, KASLR and HARDENED_USERCOPY. OpenAI does not name the distribution.
| Stage | Output | API cost |
|---|---|---|
| Scout agents pick audit targets | 5,034 targets, about 2.8 million lines, roughly 7% of the kernel | $3,426 |
| Analyst agents scan those targets | 2,308 candidate findings after deduplication | $316,549 |
| Triage agents reproduce in a KASAN VM | 1,724 reproduced, about 74.7% of candidates | $73,330 |
| Screening agent reviews impact | 313 information leak candidates, 1,238 privilege escalation candidates | $44,194 |
| Exploit development agents | 291 leak proofs of concept, 1,169 root exploits, which reduce to 948 distinct bugs by patch | $775,929 |
Those five stages add up to about $1.21 million. Human researchers then reviewed the exploits, patches and proposed severity before reporting to maintainers. OpenAI says it has already sent hundreds of reports and patches for critical bugs, starting with those the kernel community asked to see first, and that the rest are going out gradually through its Patch the Planet partners.
For comparison, a run in late July with GPT-5.6 Sol found 166 unique privilege escalation bugs for $513,690. The newer model found more than five times as many in October, on a kernel that had gone through three more months of hardening.
What we don't know yet
Every number above comes from OpenAI. I found no independent check of the 948 figure, and no public response from kernel maintainers to this specific report when I wrote this. The model isn't available to the public, and OpenAI left out the details of the 10 exploitation techniques it calls new, for safety reasons. It also describes the costs as closer to an upper bound, since the team was measuring capability and spent little effort on reducing cost.
Some of these bugs were already moving through the normal process. When the scan and manual analysis ended after 10 days, 61 of the privilege escalation bugs had been patched upstream, 43 had staging patches, and 299 were being discussed publicly. That is roughly 400 of 948 bugs already visible to other people, by whatever route they got there. Upstream patches take time to reach the kernel your distribution ships, and a patched package does nothing until the machine boots into it.
The exploits were built for Linux, so this report says nothing about iOS or macOS kernels. OpenAI does point out that the Linux kernel underpins Android, but the targets in this run were desktop VMs, and the post makes no claim about Android phones. If you only ship an iOS app and own no servers, the direct exposure is small. Most teams own more Linux than they think, though.
Who should care
A local privilege escalation bug needs one thing first: the attacker must already be running code on the machine as an ordinary user. For years that condition made these bugs feel like a second-order problem. Look at how often developers now hand that condition out on purpose:
- A CI job runs code from a pull request, or a dependency's install script.
- A coding agent runs shell commands inside a Linux container or VM, sometimes after reading text from a web page or an issue.
- A web app has a remote code execution bug, and the attacker lands as the low-privileged
www-datauser. - A shared VPS or small server has several people or services on it, each under its own account.
- A product runs customer code: plugins, notebooks, previews, sandboxes for AI tools.
In each of these, the account boundary used to be a real wall on a patched machine. OpenAI's sentence says to stop counting on it when the machine is behind on patches. From there, the question for each host becomes what root would get an attacker: API keys, signing material, database credentials, other tenants, the build output your users install.
Step 1: List the Linux machines that run code you didn't write
Spend 20 minutes on this before touching anything. A short table in your team notes is enough. For each machine or managed service, write down who can get code running on it, what secrets it can reach, and who is responsible for its kernel.
The last column saves a lot of work. On managed platforms the provider usually patches the host kernel, so your job there is limited to the images you build. On a VPS, a self-hosted CI runner, or a home server used for agents, the job is yours. Machines with no named owner tend to be the ones that have not rebooted since spring.
Step 2: Check the kernel you are really running
The fix for a kernel bug only counts once the machine has booted into the fixed kernel. On Ubuntu and Debian, these commands show the gap between what is installed and what is running:
uname -r
ls /boot/vmlinuz-*
cat /var/run/reboot-required 2>/dev/null
uptime
If uname -r shows an older version than the newest vmlinuz file, the machine is running a kernel you have already replaced on disk. The reboot-required file appears on Ubuntu when an update asks for a restart.
Ubuntu's automatic updates come from the unattended-upgrades package, usually installed already. Reboots are off by default, which is how servers end up with patched packages and an old running kernel. Two options in /etc/apt/apt.conf.d/50unattended-upgrades control this:
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:30";
You can test the setup without changing anything:
sudo unattended-upgrade -v --dry-run
Pick a reboot time your service can survive, and if one reboot would take you down, treat that as a problem to fix in its own right. Canonical's Livepatch can shrink the window on Ubuntu LTS by applying high and critical kernel fixes in memory, and it comes with Ubuntu Pro. Canonical is clear that it doesn't replace updating the kernel package and rebooting, so keep the reboots either way.
A pace that made sense in 2023 may be too slow now. OpenAI writes that within the next few years, even less capable models may be able to develop exploits once a bug is disclosed. A monthly patch window leaves weeks in which the fix, and therefore a map to the bug, is sitting in public.
Step 3: Tighten containers, and know where that stops
A normal Docker container uses the host's kernel. Namespaces and cgroups give it a separate view of the system, but a kernel bug reached from inside the container is still a bug in the one kernel everything shares. A root exploit from a container is therefore a host problem.
You can still cut how much of the kernel a container can reach. Docker's default seccomp profile disables around 44 system calls out of more than 300, and Docker's documentation advises against changing that default. Leave it on, and never run untrusted work with --privileged. For a job that runs code you didn't write, a stricter starting point looks like this:
docker run --rm \
--cap-drop=ALL \
--security-opt no-new-privileges \
--read-only \
--tmpfs /tmp \
--pids-limit 256 \
--memory 2g \
--network none \
--user 1000:1000 \
your-image your-command
Add back only what the job fails without. --network none breaks anything that downloads dependencies, so many teams fetch dependencies in an earlier step and run the untrusted part offline. That split also makes it harder for a compromised job to send your secrets anywhere.
Docker's rootless mode runs the daemon as a non-root user. Its documentation frames this as protection against bugs in the daemon and container runtime. It still leaves the shared kernel in place, so treat it as a useful layer and keep going to Step 4 for untrusted code.
Step 4: Put untrusted code behind a second kernel
OpenAI names two tools directly: "sandboxes and isolation approaches like Firecracker and gVisor." Both address the same weakness, which is that all containers on a host talk to the same kernel.
gVisor is an application kernel written in Go that runs in user space. Its Sentry component handles the container's system calls itself and doesn't pass them through to the host. It plugs into Docker as a runtime, so trying it takes a few minutes once runsc is installed:
sudo runsc install
sudo systemctl restart docker
docker run --runtime=runsc --rm hello-world
There are costs. gVisor's documentation warns of poor performance for workloads heavy in system calls, and it doesn't implement every system call or /proc file, so test your real job rather than hello-world.
Firecracker takes the virtual machine route. It uses KVM to start small microVMs, each with its own guest kernel, in under 125 ms with under 5 MiB of overhead per VM, according to the project. It needs more plumbing than a Docker flag, so it suits people building a platform. If you run coding agents or customer code at any scale, it is worth the effort. For a small team, a full disposable VM from your cloud provider gives you the same boundary with less engineering: a new VM per job, destroyed afterwards.
On Kubernetes, a RuntimeClass lets you send only the risky pods to a sandboxed runtime and leave the rest alone. The Kubernetes documentation describes exactly this balance of performance against security between pods.
These layers have bugs too. OpenAI says it tested its own research infrastructure and disclosed and patched many vulnerabilities in Kata, Moby/Docker, gVisor and other projects. In its kernelCTF run, the system also found and exploited a QEMU TCG bug that had been fixed upstream but was still in the release build the team used. So update your sandbox software on the same schedule as your kernel.
Step 5: Make CI runners disposable
Self-hosted runners are the most common place small teams hand strangers a shell. GitHub's security guidance says self-hosted runners "should almost never be used for public repositories," because anyone can open a pull request. It also warns that they can be persistently compromised by untrusted code in a workflow.
For private repositories, register runners as ephemeral so each one takes a single job and then de-registers:
./config.sh --url https://github.com/YOUR-ORG/YOUR-REPO \
--token YOUR_TOKEN \
--ephemeral
The flag alone doesn't give you a clean machine. GitHub warns that reusing hardware for these runners can expose data from earlier jobs, so your automation has to rebuild the VM or container each time. If root on a long-lived runner gives up every future build, one bad job is enough to poison what you ship. GitHub-hosted runners avoid most of this because each job gets a fresh VM, so check whether you still need self-hosting at all.
iOS teams usually build on macOS, which this report doesn't touch. Their Linux exposure tends to sit beside the build: a backend deploy runner, a TestFlight upload step moved to a Linux job, or a shared box holding an App Store Connect API key. That key belongs on a machine with few other users. Give it the lowest role that does the job, and revoke it if the host can't be trusted.
Step 6: Move secrets off machines that run untrusted code
Assume that anything readable by root on a host is exposed if the host is compromised. That rule changes a few habits:
- Don't keep long-lived cloud keys in an environment file on a box that also runs agents or CI jobs.
- Prefer short-lived credentials issued per job, so a stolen token expires soon after the job ends.
- Run coding agents with a scoped token for one repository rather than your personal GitHub login.
- Keep production database credentials out of preview and staging hosts.
Coding agents deserve their own mention. An agent that reads a poisoned web page or issue can end up running a command it was tricked into. Until now the usual comfort was that it ran as an ordinary user, in a container. On an unpatched host, that comfort is gone, so run agents in a disposable VM or a sandboxed runtime, with only the credentials the current task needs.
Step 7: Borrow the pipeline for your own code
The more interesting lesson for app developers is how OpenAI organized the work, because the same shape fits a much smaller audit of your own backend. You can't buy this model yet, but the process applies to whichever agent you already use, on code you own.
Scope first. Scout agents cut the kernel to the 7% reachable by low-privileged users and relevant to security, for $3,426. In a web backend, the equivalent is every route reachable without login, file upload and parsing code, authorization checks, and webhooks. Start there and leave the admin screens for later.
Demand proof. OpenAI's validation stage only kept findings that could be reproduced with runtime evidence, and it found the cost of checking a true positive and a false positive was about the same. So filtering weak findings early saves money. For your code, a finding should come with a failing test or a request that reproduces it on a staging copy, and an unproven finding stays a question for a human.
Set a give-up rule. This is the buried number I liked most. Successful exploit attempts averaged $360 across 1,169 cases. Failed ones averaged $3,922 across just 69 cases. By my arithmetic, those 69 failures took about $270,000 of the $691,000 spent on privilege escalation candidates, roughly 39%. OpenAI says stopping after an hour with no progress would have cut that cost a lot. Put a time or token budget on every agent task.
Verify the path as well as the result. In an early run, the pipeline reported 200 successful exploits, and human review found that 20 of them reached root through a different zero-day than the one assigned. The success signal was true and the claim was wrong. This failure shows up in ordinary coding agents all the time: the test passes, but for a reason unrelated to the fix. Ask a second agent, or a second pass with a fresh context, to confirm the fix addresses the stated cause.
Review patches adversarially. OpenAI found that first-pass patches often worked but added too much code, broke conventions or missed variants of the same bug. It recommends using a model to attack both human and AI patches, looking for bypasses and missed paths.
Rescan when models improve. The jump from 166 to 948 bugs came with a stronger model on code that had already been hardened, so a clean scan today expires when the next model ships.
Here is a brief you can paste into a coding agent. Point it at a staging copy, with no production credentials in reach:
Audit this repository for security bugs in code reachable by an
unauthenticated or low-privileged user.
1. List the audit targets: public routes, uploads, parsers, webhooks,
authorization checks. Stop and show me the list before scanning.
2. For each target, describe its threat model, then look for bugs.
3. A finding counts only with proof: a failing test or a request that
reproduces it against the local staging setup.
4. Budget: stop work on any finding after 30 minutes without progress
and record it as unresolved.
5. For each proven finding, write the root cause, the reproduction,
and a minimal patch that follows the conventions in nearby code.
6. Check related code for variants of the same bug.
7. Never touch production systems or real credentials.
On cost, a rough scale helps. The analyst stage spent about $113 per 1,000 lines. At that rate a 30,000-line backend would come to about $3,400 for scanning alone. Treat that as a loose ceiling from a very different job: it was kernel code, with an unreleased model, and OpenAI calls its figures an upper bound. A scoped pass over your public routes with a model you already pay for should cost far less, though it will also find less.
A sensible order for this week
- List every Linux host that runs code you didn't write, with an owner for each.
- Compare running and installed kernels, reboot what's behind, and turn on scheduled reboots.
- Remove
--privilegedfrom anything untrusted and apply the stricterdocker runflags. - Take self-hosted runners off public repositories, and make the rest ephemeral.
- Move long-lived secrets off shared machines and scope agent tokens to one repository.
- Run coding agents and customer code under gVisor or in a disposable VM.
- Run one scoped, proof-required audit of your public routes.
The first two items can fit in an afternoon on a small setup, and they deal with the exact condition OpenAI's warning describes: a machine that isn't up to date.
If you want ready-made instructions for that kind of agent work, I sell a pack called The Coding Agent Lab. The free tier has 25 agent briefs covering areas such as security, testing, DevOps and iOS, delivered as a PDF guide and a TXT file you can copy from, for use in Claude Code, Cursor, ChatGPT or your own agent files. It won't do the kernel patching for you, but it gives the audit step above a clearer brief.
Sources
- OpenAI: A thousand fewer paths to root
- The Hacker News: OpenAI AI pipeline generates 948 Linux root escalation exploits
- Ubuntu Server documentation: Automatic updates
- Canonical: Mythbusting the scope of Livepatch protection
- Docker: Seccomp security profiles
- Docker: Rootless mode
- gVisor: What is gVisor?
- gVisor: Docker quick start
- Firecracker
- Kubernetes: Runtime Class
- GitHub Docs: Secure use reference
- GitHub Docs: Self-hosted runners reference

Comments