Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
forgejo-keys-idor — Forgejo 16.0.5 - High - Remote - IDOR - GET /api/v1/user/keys/{id} returns other users SSH and deploy keys | Kitploit
Tools/GitHubGitHub/abraxas/forgejo-keys-idor
Authentication & AuthorizationVulnerability AnalysisExploitationWeb Application ExploitationInformation GatheringPenetration TestingAPI SecurityLabs & Practice
GitHubabraxas/forgejo-keys-idor

forgejo-keys-idor

Forgejo 16.0.5 - High - Remote - IDOR - GET /api/v1/user/keys/{id} returns other users SSH and deploy keys

54 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
View Repository

Abraxas Labs - forgejo-keys-idor

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  forgejo-keys-idor

forgejo-keys-idor

Class: IDOR Reach: Remote

Forgejo 16.0.5 (8d24b7d) - Forgejo

GET /api/v1/user/keys/{id} sits in the current-user group (reqToken, user scope). The handler still loads public_key by numeric ID with no owner check. The same table holds user keys, deploy keys, and principals. GPG on this pin does it right: GetGPGKeyForUserByID(ctx, ctx.Doer().ID, id). Private extra fields are gated. The public key material, title, and fingerprint are not. IDs are sequential.

A read:user token walks 1, 2, 3 and inventories other people's SSH and deploy keys.

IDno CVE yet
ClassIDOR
ReachRemote (authenticated read:user / session)
CWECWE-639
CVSSHigh: 7.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
ProductForgejo
Affectedthrough v16.0.5 (8d24b7d)
Authauthenticated (read:user token or session)
LicenseGNU Affero GPL v3.0
Lab127.0.0.1 only

What an attacker can do

Register or use any account, mint a read:user token (or use a session cookie), walk /api/v1/user/keys/{id}. They get titles, fingerprints, and public key material for keys they do not own. They do not get the private key. They do not get a shell. They inventory who has access to what.

PrivilegeMethod / sinkRecon / integrity / availabilityLab-proven or inferred
Read another user's SSH public key title, fingerprint, and key materialGET /api/v1/user/keys/{id} as a foreign ownerRecon (confidentiality of public-key inventory)Lab-proven. Witness title FORGEJO-KEYS-IDOR-WITNESS on id 1; attacker list is only id 2
Inventory deploy keys the same waySame handler, KeyTypeDeploy rows in public_key (OwnerID 0)ReconInferred from the table. Lab planted a user SSH key, not a deploy key
Read SSH certificate principalsSame handler, KeyTypePrincipal rows. ListPublicKeys excludes this typeReconInferred from the table
See keys that belong to limited or private usersNo visibility check on this lookupReconInferred from the handler
Extra JSON (user, key_type, read_only)appendPrivateInformation only if admin or ownerReconLab-proven closed: non-owner response had no user field

Closed on this route: private key material, a shell, GET /users/{username}/keys (username-scoped public list), GPG GET /api/v1/user/gpg_keys/{id} (404 on a foreign id), DeletePublicKey (owner/admin 403). Cross-repo GET /repos/{owner}/{repo}/keys/{id} 404s when RepoID does not match.

v16.0.5 security patches (#14370) were git-apply RCE and OpenID CSRF. They did not close this lookup. Same class as gitea-user-keys-idor, independently still open here.

How I found it

I pinned Forgejo v16.0.5 after the 2026-09 wave. Gitea 1.27.3 still had this lookup. Forgejo has diverged, so I re-read the current-user key handler next to GPG instead of copying the Gitea pack. GPG 404s a foreign id. SSH does not.

Wrong turns already recorded: treating the attacker's own list as the leak (it is not - list is scoped, GET-by-id is not); treating DISABLE_REGISTRATION=true on the installer as a close (any existing account is enough); a reverse shell. Theatre.

Lab

python3 forgejo-keys-idor-Abraxas-Labs.py

or:

cd lab
./run.sh

Target only http://127.0.0.1:19310. Image codeberg.org/forgejo/forgejo:16.0.5. Two accounts. Witness FORGEJO-KEYS-IDOR-WITNESS.

IOC planted victim-id=1 attacker-id=2
IOC attacker-list status=200 ... title=attacker-own-key
IOC get id=1 status=200 ... FORGEJO-KEYS-IDOR-WITNESS
IOC gpg-neg id=1 status=404
SUCCESS forgejo-keys-idor victim-id=1 get-http=200 list-has-id=no gpg-http=404 FORGEJO-KEYS-IDOR-WITNESS

The fix

404 unless key.OwnerID == ctx.Doer().ID (or site admin). Reject KeyTypeDeploy / KeyTypePrincipal on this route. Match GetGPGKeyForUserByID.

References

  • codeberg.org/forgejo/forgejo tag v16.0.5
  • GetPublicKey
  • Same class on Gitea: gitea-user-keys-idor
  • Nearby patched (not this lookup): v16.0.5 security patches
  • CWE-639

License

GNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.

Download Tool