website/content/blog/2022-10-24-1.17.2-migration...

44 lines
3.4 KiB
Markdown
Raw Normal View History

2022-10-25 09:23:21 +00:00
+++
title = "[security] Gitea < 1.17.2 bypassing URL restrictions during migration explained"
date = 2022-10-24
description = "Prior to Gitea 1.17.2 a malicious Gitea server could be used to request local files using the migration web interface or the REST API."
[taxonomies]
tags = ['gna', 'gitea', 'security', 'problem', 'upgrade', 'solution']
[extra]
author = 'dachary'
+++
[Gitea 1.17.2](https://pouet.chapril.org/@gna/108953815492944898) includes a [security patch](https://lab.forgefriends.org/forgefriends/forgefriends/-/commit/e6b3be460840f1f982d5358198466e7d6f509d21) that prevents bypassing URL restrictions during the migration of a remote repository.
When using the web interface or the REST API to [import / migrate a repository](https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/upstream/routers/api/v1/repo/migrate.go#L209-212) that exists on another forge, the URL of the remote forge is [verified](https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/9fb251fb6fa2bb857bb8f5ae27f06c9d597bc1eb/routers/api/v1/repo/migrate.go#L109) and rejected if not allowed. For instance if it starts with **file://**, contains **%0c** [and more](https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/9fb251fb6fa2bb857bb8f5ae27f06c9d597bc1eb/services/migrations/migrate.go#L43).
The migration then starts by [asking for more information](https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/9fb251fb6fa2bb857bb8f5ae27f06c9d597bc1eb/services/migrations/migrate.go#L185) about the software project to be migrated, using this verified URL. The structure that is returned is supposed to contain an exact copy of the URL from which the migration must be done. But there is no guarantee that it does and some of the drivers implemented in Gitea may return a different URL.
For instance, when migrating a project from another Gitea instance, the Gitea migration driver [calls the GetRepo](https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/9fb251fb6fa2bb857bb8f5ae27f06c9d597bc1eb/services/migrations/gitea_downloader.go#L153) function of the Gitea SDK, which returns the result of the [/repos/{owner}/{repo}](https://try.gitea.io/api/swagger#/repository/repoGet) endpoint verbatim.
If a malicious server is setup by an adversary so that the **/repos/{owner}/{repo}** enpoint returns a URL designed to leak information from the server such as **file:///etc/group**, it should also be verified and discarded. This is the purpose of the [check that was added](https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/9fb251fb6fa2bb857bb8f5ae27f06c9d597bc1eb/services/migrations/migrate.go#L201-220) in Gitea 1.17.2.
```go
// SECURITY: If the downloader is not a RepositoryRestorer then we need to recheck the CloneURL
if _, ok := downloader.(*RepositoryRestorer); !ok {
// Now the clone URL can be rewritten by the downloader so we must recheck
if err := IsMigrateURLAllowed(repo.CloneURL, doer); err != nil {
return err
}
// SECURITY: Ensure that we haven't been redirected from an external to a local filesystem
// Now we know all of these must parse
cloneAddrURL, _ := url.Parse(opts.CloneAddr)
cloneURL, _ := url.Parse(repo.CloneURL)
if cloneURL.Scheme == "file" || cloneURL.Scheme == "" {
if cloneAddrURL.Scheme != "file" && cloneAddrURL.Scheme != "" {
return fmt.Errorf("repo info has changed from external to local filesystem")
}
}
// We don't actually need to check the OriginalURL as it isn't used anywhere
}
```