Becoming Aware#

As is so often the case, this story begins with a network of human communication. This invaluable exchange has already contributed significantly to the security of the software powering this blog. Now, I am able to close another security vulnerability. But first things first.

Gitea as a Target#

I heard that the self-hosted version control solution Gitea used by an acquaintance of mine had become the target of an attack. This attack enabled RCE (Remote Code Execution), which was subsequently exploited.

Why is Gitea an attractive target?#

  • Theft of (corporate) secrets: Access to repositories puts confidential source code at risk of exposure.
  • Exfiltration of access credentials: If a product with interfaces linked to Gitea is managed there, API keys, accounts, and passwords are at risk if stored unencrypted. Tools like git-secret offer a remedy, but they are not yet widely adopted due to the added complexity they introduce.
  • Classic privilege escalation: Under certain circumstances, breaking out of the Gitea software can allow an attacker to take over the entire machine.
  • Exploiting the runner: If an attacker manages to hijack the runner, they can use the host machine’s resources. Even without elevated privileges. And misuse them for their own purposes (e.g., cryptominers in CI/CD).

What is RCE?#

Remote Code Execution (RCE) refers to a successful attack in which unauthorized code is injected and executed remotely over a network, without any action required by the owner or administrator of the affected resource.

The Gitea Ecosystem#

As a version control system and forge, Gitea’s core functionality includes the ability to develop and build software collaboratively via a web interface. Consequently, the system is designed to allow users to register and, in some cases, execute Actions to automate software builds and/or deployments.

This very mechanism of automatically fetching and executing code can have serious consequences if security vulnerabilities exist within the management software itself.

Vulnerability via Open Registration#

If an operator leaves the new account registration setting (“self-registration”) at its default value (DISABLE_REGISTRATION = false) in the Gitea configuration, users can sign up, create their own repositories, open issues, and perform any action that does not require administrative privileges.

If the default setting REQUIRE_SIGNIN_VIEW = false is also used, unauthenticated visitors can view public repositories, browse the Gitea site, and, most importantly, interact with the public Gitea API.

Vulnerability due to default configuration for reverse proxies#

In the default configuration of the Gitea Docker image, Gitea versions prior to v1.26.3 trust all proxies.

# gitea/app.ini

[security]
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = * # This is a vulnerability!

The Security Vulnerabilities#

What are we dealing with here? Several critical security vulnerabilities with a CVSS score of 9.8/10, for which known instances of exploitation exist.

Image: Common vulnerability scoring system calculator. The selectors are switched in a way that reproduces a critical vulnerability over a network vector without user interaction as discussed in this post.

Side Note: The CVSS Rating#

To put the “critical” label into perspective: CVSS criteria are structured similarly to those used in classical hazard and risk analysis, a branch of engineering. Unlike in that field, however, the focus is not on analyzing potential failure modes, severity, probabilities of occurrence or failure, or detectability; instead, it centers on the difficulty of exploitation and the potential impact.

Significance of Individual CVSS Metrics#

The higher the CVSS score, the easier it is to exploit the vulnerability and the more severe the potential consequences regarding data security criteria (Confidentiality, Integrity, Availability). The image above illustrates a scenario involving a potential remote (network-based) attack that requires minimal effort (low complexity regarding system hardening) and no specific preconditions (such as targeting a specific vulnerable code path, requiring a specific system state, triggering a race condition, etc.). Furthermore, the attack requires no elevated privileges (it can be performed as an unauthenticated/unauthorized guest) and involves no interaction with the system’s user or administrator.

In isolation, this might not be critical if the system is not required for operations (Score 0.0) and holds no valuable data. However, if sensitive data is present (Confidentiality, 8.7), or if there is a database whose values can be manipulated (Integrity, 8.7), or even if the availability of essential data is compromised (Availability, 8.7), the risk level shifts to “High.”

With this context in mind, we can now apply these concepts to the critical vulnerabilities found in Gitea.

CVE-2026-20896: Authentication bug in Gitea through reverse proxies#

If an attacker gains access behind the reverse proxy (or if no reverse proxy is deployed in front of the system), they can send a request containing the X-WEBAUTH-USER header directly to the Gitea instance, effectively impersonating a reverse proxy. If Gitea is configured to trust all reverse proxies (REVERSE_PROXY_TRUSTED_PROXIES = *), it assumes that the user has already been authenticated and a valid session exists. The attacker thus gains user rights with write access to repositories, even though they haven’t even created an account.

The actual vulnerability here is the unquestioning trust placed in the header’s sender without any further validation against a session token or similar mechanism; simply accepting the header from external sources is what creates the vulnerability.

There is evidence of attacks that have already exploited this bug.

CVE-2026-59774: File-read flaw affecting any file readable by the Gitea service account#

This critical vulnerability is caused by insufficient input validation in Gitea’s built-in text renderer for org-mode files. A detailed description of the attack vector and potential exploitation methods can be found on Satyam Rastogi’s excellent blog.

In short, an attacker with access to at least one public repository can craft a malicious .org file, e.g. to read Gitea’s app.ini configuration. This allows them to access secrets contained within the file, granting them elevated privileges or enabling lateral movement within the system.

According to The Hacker News and Gitea’s own team, this flaw can also be exploited to extract runner registration tokens and hijack the runners themselves.

CVE-2026-60004: RCE allowing an attacker with standard user privileges to execute arbitrary shell commands as the Gitea OS user.#

This security vulnerability is particularly severe because it also enables privilege escalation.

“The vulnerability in question is CVE-2026-60004 (CVSS score: 9.8), a case of remote code execution that allows an attacker with ordinary write access to a repository to execute arbitrary shell commands as the Gitea OS user.” - thehackernews, Aug-2026

Exploiting this vulnerability can therefore allow an attacker to take over the entire Gitea instance, provided they have write access to any repository (even a public one).

The Situation#

Image: A decision tree, connecting the dots between the three discussed CVEs. Baseline message is that the system has to be regarded as compromised in case an attacker is able to get ordinary write access to any repository on the instance which can be done by exploiting any of the discussed CVEs.

There are other critical security vulnerabilities; however, by combining the scenarios described above, attackers can (without significant effort)

  1. Operate as normal users on the Gitea system without having registered and without requiring SSH access
  2. Impersonate the Gitea service user
  3. Take over Actions or even inject and execute workflows
  4. With slightly more effort, take over the entire instance, encrypt it, demand a ransom, etc.

My acquaintance’s experience involved points one and three. The attack was most likely automated, carried out after the internet had been scanned for vulnerable instances.

In summary, the case at hand appears to be a mass-exploitation campaign aimed at cryptomining.

The Cryptominer#

On my acquaintance’s system, the following file is stored as a statically linked executable (!): gitea/data/gitea/sys_health_s3 In his view, the file’s hash suggests that an XMRig cryptominer was deployed as a malicious payload; it communicates with pool.hashvault.pro and consumes a large portion of the available CPU power.

Potential for Even Greater Damage#

I was even able to find a recent (Sep 2026) example on Hacker News regarding a worst-case scenario: source code theft, credential harvesting, and privilege escalation leading to root access on the affected system.

How do I detect an attack?#

In the worst case, you don’t. If professionals are at work targeting the system, only the log files from the time immediately following the breach might offer clues. Provided, of course, that you read and interpret them right away. But I digress1.

Nevertheless, you should ask yourself the following questions:

  1. Am I running a vulnerable version of Gitea (<1.27.2)?
  2. Have I avoided to use a monitoring solution that would have alerted me to anomalies?
  3. Is there unusually high system CPU load? This could reveal a cryptominer infection.
  4. For this specific miner, you can also check if the following file exists on the system:
find / -name '.sys_health_s3' 2>/dev/null

If the answer to even one of these questions is “yes,” immediate action is required. Given the severity of the vulnerabilities, the entire host system could be considered compromised, and should be if the Gitea container or any attached runner was configured as “rootful” (i.e., granted access to the Docker socket).

In Case of Infection: Quarantine#

Next steps: Immediately isolate the affected machine and take it offline. Shut down the containers, reboot the host machine, and apply updates. Afterward, update your Docker registry images and check log files (look at timestamps!) to determine if other parts of the system are affected. For further details, see my article about a Gitea attack I experienced firsthand.

Solutions#

Update Gitea!#

Gitea urgently needs to be updated to a version >1.27.1. To do this, you can modify the docker-compose.yml file and then restart the containers:

# docker-compose.yml
gitea:
image: gitea/gitea:latest
container_name: gitea

Further information on recent fixes can be found on Gitea’s blog. The major release (version 27) addresses a long list of critical security vulnerabilities. I have singled out only a few of them in this article, as they affect my immediate environment and exploits for them are known.

Running Gitea and the runner in “rootless” mode#

If the exploitation of a security vulnerability leads to the compromise of Gitea or the Action Runner, the host system itself should not also be compromised. Running them in a container can limit the potential damage. I have therefore described how to set up a “rootless” solution here.

Additional measures (hardening)#

CVE-2026-20896: Restricting network access#

For the next steps, I am following the recommendation from d00ftech.

  1. Include the address range of your reverse proxy. First, you need to determine the network address at which the reverse proxy is accessible. On my server, for example, both Gitea and the reverse proxy (Caddy) run within Docker. I therefore find the IP address as follows:
docker inspect caddy \
--format='{{range $name, $network := .NetworkSettings.Networks}}{{$name}}: {{$network.IPAddress}}{{"\n"}}{{end}}'
caddy-proxy: 172.20.0.2

This value is now entered into gitea/app.ini:

[security]
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = 172.20.0.2/32
  1. Block direct network access to Gitea that bypasses your own reverse proxy. To do this, the Gitea port (usually 3000) must not appear in the port mappings—for example, in the Docker configuration:
# docker-compose.yml
services:
  gitea:
    ports:
      - "3000:3000" # remove this!

CVE-2026-59774: Update Gitea!#

While it is possible to disable the org-mode renderer in app.ini (see the Gitea documentation on this topic), in my view, the far better approach is to update Gitea itself!

CVE-2026-60004: Disable self-registration if possible#

The actual fix requires updating the Gitea version. For smaller instances with few users, it may also make sense to disable self-registration for new users. From now on, they will have to request registration from the administrators (unless they exploit one of the other CVEs described above).

# gitea/app.ini
[service]
DISABLE_REGISTRATION = true

Other helpful settings include:

REGISTER_EMAIL_CONFIRM = true # Requires user's email to be confirmed for registration to complete
ENABLE_OPENID_SIGNUP = false # Disallow logons via external API
REQUIRE_SIGNIN_VIEW = true # Force users to login before they can view content or use the public API

Outlook and Conclusion#

Version control systems are highly attractive targets for a wide range of attackers: from cryptominers seeking to hijack resources and ransomware groups targeting source code and credentials, to highly professional, targeted attackers and state-sponsored actors, given that the software effectively serves as a form of infrastructure.

Therefore:

“Apply all patches. Always. Immediately. Without exception.” - Felix von Leitner, however, also says BSI

What leaves me feeling a bit perplexed is the sheer number and severity of the security vulnerabilities that have been patched (35 CVEs in a single month, according to Gitea’s own blog!). From my naive security perspective, this solution is full of holes and I’m not sure if I should keep running Gitea.

Immediate: Harden the system#

Obviously: Update Gitea to latest and implement the hardening measures described above.

Short-term: Automated updates#

Over the coming days and weeks, I’ll be working on automatically scanning the Docker registry for software updates and deploying them to my server automatically as well. A blog post will follow!

Medium-term: Time for monitoring#

I’ll be looking for a small, lightweight monitoring solution that flags anomalies—such as “CPU load unusually high since HH:MM:SS”—and can both execute and monitor health checks. Crucially, the tool itself shouldn’t become a potential entry point for attackers, meaning it needs to operate with virtually no privileges. I wonder if that’s easily achievable?

Medium-term 2: Get rid of secrets#

Unencrypted secrets have no place in repositories. Even a .env file sitting on the host itself can no longer be considered secure. I’ll be giving this some thought and will report back once I’ve rolled out a solution.

Medium-term 3: Set up news feeds#

This involves subscribing to newsletters or RSS feeds, ideally from all my software vendors, IT security institutions or journalists, and other sources. To stay informed about security vulnerabilities, patches, and system hardening measures.

Long-term: Keeping the conversation going#

It ends just as it begins. Before I can solve problems with my software, before I even know those problems exist, right there! At the very beginning, before I’ve even decided which software to use, there are: other people.

People I can ask questions or turn to for help. People whose time I can claim for my own needs. Like messaging them out of the blue and pulling them away from what they were focused on.

People who know more than I do, who see things differently, or whose opinions I find interesting.

Colleagues, current and former. Acquaintances, friends, and family whom I meet, whether by chance or by appointment, to connect. Over coffee, ice cream, or a cold drink; to make music or spend an afternoon playing board games.

People who help me kindly and patiently, despite the interruptions and my silly questions. People who might roll their eyes for a moment but then explain things to me one more time anyway. People who somehow tolerate my monologues, critical follow-up questions, and occasional pushback.

People with whom I can engage in an exchange that, in the end, hopefully benefits both sides.

Image: A computer-drawn image of Schallbert, pixelized, and in 256 colors.
Thanks to all of you. Everything is much easier with people like you. Including running this blog, which takes a lot of my time. With you, I can discuss decisions, learn how you did things (and why you did them that way), and shift perspectives. And develop new ideas.

Please stay in my life.


  1. A nod to “Fefe” Felix von Leitner. Quote: “Assumptions […]: We are being attacked. We notice it promptly. Experience shows: No, we don’t. Typical timeframe before detecting stowaways: six months to five years.” - from the Hackback presentation; link leads to Codeberg. ↩︎