There are two official texts about the same Gitea hole. One says the attacker needs write access to a repository. The other, on the same page, says they need no privileges at all. It looks like a typo and it is not: it is an exact description of how the product is configured when you install it.
We are talking about CVE-2026-60004, fixed on 27 July in Gitea 1.27.1 and under exploitation for the past few days. It affects everything between version 1.17 and 1.27.0, both inclusive. The same release closed a second, equally serious hole, CVE-2026-59774, an arbitrary file read that did not even require authentication. The first was reported by Shai Rod, who signs as NightRang3r; the second was found independently by xbow-security and by NightRang3r himself.
Two sentences that look incompatible
CISA's catalogue entry says the flaw "allows an attacker with repository write access to send a malicious patch to the diffpatch API endpoint to plant an executable Git hook and run shell commands as the Gitea service account". Read that quickly in a Monday meeting and what stays in the air is "with repository write access". It sounds like you have to be somebody.
The vector published in the maintainers' advisory is CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, scored 9.8. That PR:N reads "privileges required: none". And the same advisory explains why, in a sentence we did not see quoted in any of the coverage we read yesterday: "With open registration enabled, the attack can be performed by an unauthenticated visitor after registering a normal account and creating a repository."
Does open registration ship enabled? Gitea's configuration documentation answers without ambiguity. In the [service] section, DISABLE_REGISTRATION is false, REGISTER_EMAIL_CONFIRM is false and REQUIRE_SIGNIN_VIEW is false. Anyone who reaches the URL can sign up without confirming an email address, create a repository, and hold write access over it. The requirement in the write-up is satisfied by filling in a form. (OpenID signup is tied to the same switch: its default is the opposite of DISABLE_REGISTRATION, so it closes on its own when you close registration, unless somebody forced it by hand.)
The useful question when you read "requires authentication"
In prioritisation meetings, "requires authentication" and "requires privileges" work as sedatives: they drop the flaw one rung down the list and the meeting moves on. What you have to ask is who can cross that threshold, and how fast. On an internal service where accounts are created by hand, the threshold genuinely buys you time. On a service with open registration published to the internet, it is a ten-second formality. The same sentence in the write-up describes two risks with nothing in common.
And it is not a discovery we made today: this is the third time this summer we have ended up in the same place. We wrote it in August about a WordPress PNG that was PostScript inside — in a content manager, "requires authentication" does not mean "requires being you", it means "requires one of the accounts they hand out" — and again with the cPanel CVE where the risk came from the neighbour, when a 5.6 needing no account turned out more urgent than an 8.8 that did. Three times in three months stops being an anecdote: the patching queue is sorted by "how much damage it does" when it should also be sorted by "how much it costs to get there".
Alongside the CVE, CISA publishes its SSVC assessment, which is more useful than the numeric score and which almost nobody reads: exploitation active, automatable yes, technical impact total. That "automatable: yes" is the one that matters. It means discovery, account creation and execution can be chained into a script that sweeps the internet, and that nobody needs any particular interest in your company for this to reach you.
From a patch to a hook
The maintainers' advisory sets out the mechanism with unusual candour, and it is worth reading because no hidden feature is involved. The diffpatch endpoint applies the patch you send inside a shared temporary bare clone. Send the same patch twice and you get an add/add collision, and Git's three-way merge fallback ends up writing indexed paths out to disk, despite the --cached flag. In a bare clone, the repository root is $GIT_DIR: an executable file called hooks/post-index-change stops being a file and becomes a live hook, run under the service account the next time Git writes the index.
The advisory itself lists three conditions for the trigger to work: Git 2.32 or newer, an enabled diffpatch route, and a writable and executable temporary filesystem. And the fix was to stop using a bare clone for this operation. Call it a bug or call it a design assumption that aged badly: what matters for the rest of us is that none of the parts were exotic. Git hooks have been code execution by design for twenty years. What failed was that the attacker could write inside $GIT_DIR.
The other hole in the same update
All the attention has now gone to CVE-2026-60004, which leaves the other hole closed by the same release in the shade. CVE-2026-59774 also scores 9.8, affects 1.22.1 through 1.27.0, and is an arbitrary file read through Org-mode's #+INCLUDE directive — the note format Gitea knows how to render. A public repository and a file with the right markup are enough to read anything the service account can read. Starting with app.ini, where the INTERNAL_TOKEN, the OAuth and JWT secrets and the database credentials live.
It is worth flagging because it is a recurring pattern in security releases: the flashiest CVE gets quoted, the patch is applied with that one in mind, and the rest of the list is skimmed. The fixed version is the same for both, so nothing changes in practice. What changes is the conversation with whoever signs off: you are not asking for a maintenance window because of one flaw, you are asking because of two, and one of them requires the attacker to hold absolutely nothing.
Eleven seconds, according to the person it happened to
There is a single public case, and it is worth saying where it comes from: it was written up by a developer signing as Andrey on Habr, the Russian technical platform, and picked up afterwards by Help Net Security and The Hacker News. It is the victim's own account, with no third-party verification, and it is still the most concrete thing available. The sequence, automated end to end: an account was registered, a repository created, the exploit fired, code executed inside the Gitea container as the git user, proof of access written to a branch of that same repository, a generic loader downloaded, and a payload dropped that looks like a cryptocurrency miner. That hedge is the victim's own: they say they could not confirm the miner family, the wallet or the pool. The active part lasted about eleven seconds.
What blew the thing open arrived by email: the hosting provider warning that the virtual server had been sitting above 70% CPU for a long stretch, that this breached the terms of service and that they were throttling its resources. We had seen that pattern before — the first warning coming through the billing or commercial-support channel rather than the security one — when we wrote about RMM agents from IT providers. The practical consequence: if eleven seconds of execution and sustained CPU load trigger nothing on your side, your detection threshold sits above the loudest noise an intruder can make.
What was within reach of that service account
The advisory is explicit: the application configuration and its secrets, the database credentials and contents, OAuth and integration credentials, and the mounted repositories. Near enough everything a code server needs in order to do its job, which is exactly the problem. A code server is not a development-team tool on the level of the task board: it is production infrastructure, because it is the origin of what gets deployed and the custodian of the keys used to deploy it. We saw the same pattern in the data dashboard that was also the keyring and in the production keys your CI/CD is holding. And in case anyone takes comfort in the whole team having the code cloned: cloning is not a backup of what that server holds.
In the Habr case there was luck, and the luck had a technical name. The container was not privileged, so the payload did not survive reboots; the victim found no traces of persistence via cron, systemd or new SSH keys. Their cleanup was to upgrade to 1.27.2, close registration, remove the surplus signup methods, rotate every secret and token, restrict the Docker networking and cut the container's outbound internet access. None of those containment decisions were made during the incident: the unprivileged container was decided the day the service was stood up, when the convenient option would have been the opposite. It is the axis we work on: failure is inevitable, an outage is a design decision.
With one limit worth not skipping, because it is where the container did not help: containment limited persistence, not confidentiality. Everything inside was readable for the seconds the attacker was there. Which is why rotating the secrets is the one item on the cleanup list that cannot wait until next week.
Twenty minutes, and today
CISA added the CVE to its exploited-vulnerabilities catalogue on 25 August and gave US federal agencies until the 28th. Nobody is imposing that on you, but the date is the best public reference there is for the speed at which this is considered urgent. If you run Gitea, this happens today:
- The version. Anything between 1.17 and 1.27.0 is affected. The fixed release is 1.27.1; the victim in the case went straight to 1.27.2. From outside, the version is usually printed in the footer of the login page.
- Registration. Check
DISABLE_REGISTRATION,REGISTER_EMAIL_CONFIRMandREQUIRE_SIGNIN_VIEW. If nobody touched them at install time, they are at the open values. - Exposure and recent signups. Does it answer from the internet? Shadowserver counted close to 5,000 publicly reachable Gitea instances. And the list of accounts created since late July will tell you more than any scanner.
- What it runs as. User, container privileges, which volumes are mounted, where it can reach outbound. That does not change whether they get in: it changes what they take and how long they stay.
- Rotate. If the version was vulnerable and the service was exposed, upgrading evicts nobody who already walked off with a token. Deployment tokens, database credentials, OAuth secrets, webhook keys.
- The traces. The documented chain leaves specific, cheap-to-check footprints: accounts nobody recognises, repositories freshly created by those accounts, new branches inside them, and repeated calls to the
diffpatchendpoint in the access log. With the same patch applied, those four checks are the difference between "we have patched" and "we know nobody got in". - The owner. Write down in the inventory who is accountable for that service and in which window it gets updated. If the honest answer is "nobody", that is the finding of the day and the CVE is the footnote.
When your case is different
If your Gitea only listens on the internal network, with signups closed and accounts created by hand, open registration stops being the door and the urgency drops considerably. It does not vanish: anyone with an account and a repository of their own can still run code on that server, and that list includes whoever left the company last week. But it is the difference between patching this morning and patching in the next window. Saying so matters, because "patch everything now" is the advice that ends with nobody patching anything.
And if you do not run Gitea, the lesson you take away is not about Gitea. Swap the name for the self-hosted task tracker, the metrics dashboard, the documentation server or the file-transfer tool somebody stood up on a Tuesday five years ago because it was needed for a week. The question is not which version you are on. It is how many of those services are exposed, who is listed as accountable for each one, and which CVE from this summer they still have open because the write-up said "requires authentication".
Who is accountable for your exposed services?
Inventorying what you have published, giving it an owner and an update window, and deciding which symptom wakes a human up is part of how we design and run infrastructure and cloud, and part of our managed cybersecurity work. If you would rather start with the short list of what needs patching this week, that works for us too.
Talk to everyWANSources
Gitea security advisory GHSA-rcr6-4jqh-j84m: mechanism, affected versions, the three trigger conditions, the impact, and the 9.8 score with the CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H vector (the score comes from the CNA; NVD is still listed as "Undergoing Analysis"). Gitea 1.27.1 release notes, 27 July 2026, for the dates and the credits on both CVEs. CISA Known Exploited Vulnerabilities catalogue: entry CVE-2026-60004 added on 25 August 2026, due date the 28th, and the SSVC assessment (exploitation active, automatable yes, technical impact total). Gitea official configuration documentation for the registration and OpenID defaults. Incident account and the eleven-second figure: the victim's own write-up on Habr, reported by Help Net Security on 26 August 2026 and by The Hacker News, which are the source of the provider warning about 70% CPU and of the caveat that the payload only "looks like" an unconfirmed miner. Exposed-instance count attributed to Shadowserver via BleepingComputer. The data come from those sources; the opinions about prioritisation, inventory and detection are ours.