mrkeyoor.com_
Fri 11 Sept 09:40 UTC
Tech6 min read

Forgejo 15.0.8 and 16.0.4 Patch a Template-to-Host RCE

A six-line fix removes a crafted .git directory after template expansion, closing a path to host file theft and code execution on self-hosted Forgejo servers.

A six-line change in Forgejo's repository generator now stands between a crafted project template and the server hosting the forge. The patch removes a newly created .git directory after template variables are expanded, closing a path that Forgejo says could read arbitrary host data and run arbitrary processes. The project released Forgejo 16.0.4 and 15.0.8 LTS on September 10 with the same critical fix.

That consequence reaches beyond one repository. Forgejo is a self-hosted service for source code, issues, pull requests, packages and automation. Its own version 16 introduction describes it as a community-developed code collaboration platform. Code execution in the service's operating-system context can therefore put the data and credentials available to that process at risk.

How a template created a second .git directory

Forgejo's published account of the bug gives a precise sequence. To generate a repository from a template, the server clones the template, removes its original .git directory, expands variables in files selected by .forgejo/template, then initializes a fresh Git repository. Removing the cloned metadata first was meant to leave ordinary project files for the new repository. The vulnerable flow trusted that the directory would stay gone during expansion, according to the 16.0.4 release note.

A template could break that assumption through a variable in a path. Expansion could turn a differently named directory into .git before Forgejo called Git's initialization routine. Git would then adopt the attacker-influenced metadata. Forgejo's disclosure says a malicious template could use that behavior to read arbitrary data from the host or execute arbitrary processes there. The note does not publish a weaponized payload, and this article does not infer one beyond the project's stated impact.

The repair is unusually small. In the merged security patch, generateRepoCommit calls RemoveAll(".git") a second time after expansion and before InitRepository. The change adds six lines to the production file, including its comment and error handling. A 39-line integration test constructs a template path containing ${REPO_NAME}; when the generated repository is named it, that path resolves to .git/config. The test uses an invalid compression setting to prove that Git no longer consumes the injected configuration, then checks that repository generation succeeds.

The test is useful because it captures the broken trust boundary without demonstrating process execution. The first deletion only cleans metadata that arrived with the clone. The second deletion cleans metadata that the template engine itself may have materialized. Forgejo credits security researcher 0xLE with discovering and privately disclosing the flaw in the patch record.

What the remote label does and does not establish

The project calls this a remote code execution attack because the harmful input reaches a server-side repository-generation path. Its public note identifies a malicious template repository and a later generation step as parts of the chain. It does not say that viewing a repository, cloning one, or making an unauthenticated request is enough. It also does not spell out the minimum account or repository permissions needed on every deployment. Those missing preconditions matter when administrators assess exposure, but they do not reduce the stated impact once the chain succeeds.

The disclosed outcome is execution as the Forgejo host process, rather than an automatic jump to root. Forgejo's configuration reference recommends running the service under a dedicated operating-system account through RUN_USER. The practical reach depends on what that account can read, write and invoke on a particular server. A dedicated account can limit damage, but it cannot make arbitrary execution inside the forge process harmless.

No CVE identifier is attached to the template RCE in either new release note. Forgejo's security announcement promised patches for versions 15 and 16, then linked the two release milestones once they were public. Neither the announcement nor the release notes reports observed exploitation. Operators should keep those omissions separate from proof that no attack occurred.

Two more permission failures shipped in the same update

Both patch releases also correct a fine-grained access-token error around pull requests. Forgejo lets the owner of a pull request permit maintainers of the destination repository to edit the contributor's branch. The specialized permission check for that feature did not account for API-specific restrictions. A token restricted to one repository could consequently modify a branch outside that repository's allowed scope through the API, the release note explains.

The associated regression test makes the boundary concrete. A normal write token owned by a destination-repository maintainer may edit an opted-in pull-request branch. A repository-specific token limited to the destination must receive HTTP 403 when it tries the same edit on the contributor's fork. The patch passes Forgejo's authorization reducer into the permission lookup so the second request retains the token's repository limit, as shown in pull request 14301.

A third flaw exposed attachments from draft releases. Users with read access, including unauthenticated visitors to a public repository, could retrieve attachment metadata and full file contents even though the draft release was hidden. Forgejo says the GetReleaseAttachment API endpoint and the ServeAttachment download route lacked the draft check already used by the endpoints that fetch a release or list its attachments. Versions 15.0.8 and 16.0.4 add the missing protection, according to their published notes.

That attachment issue is the only one of the three for which the notes name an existing identifier. Forgejo describes it as the same class of bug fixed upstream in Gitea under CVE-2026-27660 and GHSA-q9pg-jj6x-j9p6. Reusing that CVE for the template RCE would be incorrect; the release text connects it only to the draft-attachment exposure.

The supported branches define the patch choice

Forgejo released the two builds minutes apart on September 10. Its current releases page lists 16.0.4 as the stable line, supported until October 29, 2026. Version 15.0.8 is the long-term-support build, with support scheduled through July 15, 2027. An instance already on version 16 should move to 16.0.4, while an instance on the 15 LTS line should move to 15.0.8.

The same page lists versions 14 and earlier as discontinued. Forgejo's public security notice offers these patches only for the supported 15 and 16 branches. That does not establish that an older branch is unaffected. Administrators still running a discontinued release need an upgrade plan or direct guidance from the project because the announcement provides no patched build for those lines.

This is a patch-level update, yet operators should still follow their normal backup and rollback procedure. Forgejo's version 16 release guidance recommends a full backup before upgrading. The project's download page provides signed binaries and the codeberg.org/forgejo/forgejo:16.0.4 container image; it also tells binary users to refresh the project's GPG key and verify the detached signature. Package-manager and container deployments should use their own documented replacement and restart path rather than copying a command meant for another installation type.

Patching closes the bug, not the investigation

After upgrading, administrators should confirm the running process reports the intended build and that their deployment is no longer pulling an older mutable image tag. The release itself contains no published indicators of compromise or log signatures. A local review can still focus on the disclosed path: recently created template repositories, repositories generated from them, unexpected failures during generation, and unexplained activity by the Forgejo service account. Those checks follow the sequence documented in the security patch; their value depends on each instance's audit and log retention.

If that review finds evidence that a crafted template reached the vulnerable generation flow, installing the fixed binary cannot establish that the earlier host state was clean. Forgejo's impact statement includes arbitrary file reads and arbitrary process execution. That is enough reason to isolate the affected service, preserve evidence and assess credentials accessible to its operating-system account before returning it to service.

The next useful disclosure would define the affected-version range and minimum permissions, and assign an identifier to the template flaw. Administrators should also watch whether Forgejo publishes forensic indicators or guidance for unsupported branches. Until then, the grounded release boundary is simple: 15.0.8 and 16.0.4 contain the fix; the project's public material does not promise it anywhere older.

We reviewed this

  1. Files — our honest review
  2. requests — our honest review
  3. fresh — our honest review

Sources

  1. Forgejo 16.0.4 release notes
  2. Forgejo 15.0.8 release notes
  3. Forgejo security patch pull request 14301
  4. Forgejo security announcement for 15.0.8 and 16.0.4
  5. Forgejo releases
  6. Forgejo download and signature verification
  7. Forgejo v16.0 release guidance
  8. Forgejo configuration reference