website/blog/1-17-2-migration-security/index.html

339 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<link rel="me" href="https://pouet.chapril.org/@gna" />
<link rel="stylesheet" href="https://gna.org/main.css" />
<link
rel="stylesheet"
media="screen and (max-width: 1300px)"
href="https://gna.org/mobile.css"
/>
<meta name="referrer" content="no-referrer-when-downgrade" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://gna.org/main.css" />
<link
rel="stylesheet"
media="screen and (max-width: 1300px)"
href="https://gna.org/mobile.css"
/>
<meta name="referrer" content="no-referrer-when-downgrade" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>[security] Gitea &lt; 1.17.2 bypassing URL restrictions during migration explained | Gna!: Managed Gitea Hosting </title>
<meta name="referrer" content="no-referrer-when-downgrade" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="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." />
<meta property="og:title" content="[security] Gitea &lt; 1.17.2 bypassing URL restrictions during migration explained | Gna!: Managed Gitea Hosting " />
<meta property="og:type" content="article" />
<meta property="og:url" content="https:&#x2F;&#x2F;gna.org" />
<meta property="og:description" content="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." />
<meta
property="og:site_name"
content="[security] Gitea &lt; 1.17.2 bypassing URL restrictions during migration explained | Gna!: Managed Gitea Hosting "
/>
<link
rel="apple-touch-icon"
sizes="57x57"
href="https://gna.org/apple-icon-57x57.png?h=c21de14cfdf862a6472ae977557fa048a7c36d39337e61d3274705e9bd8e857f"
/>
<link
rel="apple-touch-icon"
sizes="60x60"
href="https://gna.org/apple-icon-60x60.png?h=67089d9025a52d0d1ddce450078c7acefe2c150a2427dec9f5e13c6314f74281"
/>
<link
rel="apple-touch-icon"
sizes="72x72"
href="https://gna.org/apple-icon-72x72.png?h=70725943de8884804f9da28202ced0ad6fed483ae9cf8f6d874aa133e30cb693"
/>
<link
rel="apple-touch-icon"
sizes="76x76"
href="https://gna.org/apple-icon-76x76.png?h=1e6e8072df3b21bdcea254a42aac6e993611e845f91ddd79f6f35a6c441710a5"
/>
<link
rel="apple-touch-icon"
sizes="114x114"
href="https://gna.org/apple-icon-114x114.png?h=c20099f8190ed3962fab5726c5594857a871cdb3ee98439343c622cd3727fed6"
/>
<link
rel="apple-touch-icon"
sizes="120x120"
href="https://gna.org/apple-icon-120x120.png?h=4df78e402e60b58c6d44764678bdd737b5b6a836aeb85fb75fa49f706f7e8c81"
/>
<link
rel="apple-touch-icon"
sizes="144x144"
href="https://gna.org/apple-icon-144x144.png?h=0c44e6655d714f89ee95cc151032d1f0dc3204bd24d1ca2ee9d94692d4ede84d"
/>
<link
rel="apple-touch-icon"
sizes="152x152"
href="https://gna.org/apple-icon-152x152.png?h=157918f883ff95d4eeb6452d0ebb61ca5e21ea0dcac1aefe825f3e2f3999052f"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="https://gna.org/apple-icon-180x180.png?h=7d5c16d379b7db6d8ea5aae64921d7162b84f543763acd8fc7c107f80a600213"
/>
<link
rel="icon"
type="image/png"
sizes="192x192"
href="https://gna.org/android-icon-192x192.png?h=095e3835b082dba07f606c33fa6f71bcd671a71e987b0ab2e46dcddceef52b9c"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="https://gna.org/favicon-32x32.png?h=1bf54bf111572b1d1639192b5360ee4345f702e563aa71bb66610a95a7290437"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="https://gna.org/favicon-96x96.png?h=5a6ed96c09f5055526e3b236867a1272a26f7ba957d48b267bccd51ef0845fbe"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="https://gna.org/favicon-16x16.png?h=1e5fa59ae78516055f662e40bb2599dc3828a7adb34567e9d8d2cfcaa6b7aa5f"
/>
<link
rel="manifest"
href="https://gna.org/manifest.json?h=27eca3e8297eb7ff340deb3849b210185a459b3845456aa4d0036f6d966b3518"
/>
<meta name="msapplication-TileColor" content="#ffffff" />
<meta
name="msapplication-TileImage"
content="https://gna.org/ms-icon-144x144.png?h=8170ab51b871b84b8f98bd03cf441afdffb2998b7dfffb04abb7ebf5deeb1f94"
/>
<meta name="theme-color" content="#ffffff" />
</head>
</head>
<body class="base">
<header>
<nav class="nav__container">
<input type="checkbox" class="nav__toggle" id="nav__toggle" />
<div class="nav__header">
<a class="nav__logo-container" href="/">
<img src="https://gna.org/gna-logo-rectangle-48px.png?h=ba9eab043277265f94c51b87d5e14f9ca35789403ecb8afc9bd1e33b13c6a2a5" alt="Gna!"/>
</a>
<label class="nav__hamburger-menu" for="nav__toggle">
<span class="nav__hamburger-inner"></span>
</label>
</div>
<div class="nav__spacer--small"></div>
<div class="nav__link-group">
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="&#x2F;about&#x2F;">About</a>
</div>
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="&#x2F;blog&#x2F;">Blog</a>
</div>
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="https:&#x2F;&#x2F;matrix.to&#x2F;#&#x2F;#gna:matrix.batsense.net">Chat</a>
</div>
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="&#x2F;gitea-clinic&#x2F;">Clinic</a>
</div>
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="https:&#x2F;&#x2F;forum.gna.org">Forum</a>
</div>
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="https:&#x2F;&#x2F;pouet.chapril.org&#x2F;@gna">Mastodon</a>
</div>
</div>
<div class="nav__spacer"></div>
<div class="nav__link-group--small">
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="https:&#x2F;&#x2F;hosteadashboard.gna.org&#x2F;login&#x2F;">Login</a>
</div>
<div class="nav__link-container--action">
<a class="nav__link" rel="noreferrer" href="https:&#x2F;&#x2F;hosteadashboard.gna.org&#x2F;register&#x2F;">Join</a>
</div>
</div>
</nav>
</header>
<!-- See ../sass/main.scss. Required for pushing footer to the very
bottom of the page -->
<div class="main__content-container">
<main>
<div class="page__container">
<h1 class="page__group-title">[security] Gitea &lt; 1.17.2 bypassing URL restrictions during migration explained</h1>
<p class="blog__post-meta">
<a href="https:&#x2F;&#x2F;dachary.org" class="post__author">Loïc Dachary</a>
&middot; 24
October
,
2022 &middot; <b>2 min read</b>
</p>
<div class="blog__content">
<p><a href="https://pouet.chapril.org/@gna/108953815492944898">Gitea 1.17.2</a> includes a <a href="https://lab.forgefriends.org/forgefriends/forgefriends/-/commit/e6b3be460840f1f982d5358198466e7d6f509d21">security patch</a> that prevents bypassing URL restrictions during the migration of a remote repository.</p>
<p>When using the web interface or the REST API to <a href="https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/upstream/routers/api/v1/repo/migrate.go#L209-212">import / migrate a repository</a> that exists on another forge, the URL of the remote forge is <a href="https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/9fb251fb6fa2bb857bb8f5ae27f06c9d597bc1eb/routers/api/v1/repo/migrate.go#L109">verified</a> and rejected if not allowed. For instance if it starts with <strong>file://</strong>, contains <strong>%0c</strong> <a href="https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/9fb251fb6fa2bb857bb8f5ae27f06c9d597bc1eb/services/migrations/migrate.go#L43">and more</a>.</p>
<p>The migration then starts by <a href="https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/9fb251fb6fa2bb857bb8f5ae27f06c9d597bc1eb/services/migrations/migrate.go#L185">asking for more information</a> 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.</p>
<p>For instance, when migrating a project from another Gitea instance, the Gitea migration driver <a href="https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/9fb251fb6fa2bb857bb8f5ae27f06c9d597bc1eb/services/migrations/gitea_downloader.go#L153">calls the GetRepo</a> function of the Gitea SDK, which returns the result of the <a href="https://try.gitea.io/api/swagger#/repository/repoGet">/repos/{owner}/{repo}</a> endpoint verbatim.</p>
<p>If a malicious server is setup by an adversary so that the <strong>/repos/{owner}/{repo}</strong> enpoint returns a URL designed to leak information from the server such as <strong>file:///etc/group</strong>, it should also be verified and discarded. This is the purpose of the <a href="https://lab.forgefriends.org/forgefriends/forgefriends/-/blob/9fb251fb6fa2bb857bb8f5ae27f06c9d597bc1eb/services/migrations/migrate.go#L201-220">check that was added</a> in Gitea 1.17.2.</p>
<pre data-lang="go" style="background-color:#2b303b;color:#c0c5ce;" class="language-go "><code class="language-go" data-lang="go"><span> </span><span style="color:#65737e;">// SECURITY: If the downloader is not a RepositoryRestorer then we need to recheck the CloneURL
</span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">_</span><span>, </span><span style="color:#bf616a;">ok </span><span>:= </span><span style="color:#bf616a;">downloader</span><span>.(*</span><span style="color:#b48ead;">RepositoryRestorer</span><span>); !</span><span style="color:#bf616a;">ok </span><span>{
</span><span> </span><span style="color:#65737e;">// Now the clone URL can be rewritten by the downloader so we must recheck
</span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">err </span><span>:= </span><span style="color:#bf616a;">IsMigrateURLAllowed</span><span>(</span><span style="color:#bf616a;">repo</span><span>.</span><span style="color:#bf616a;">CloneURL</span><span>, </span><span style="color:#bf616a;">doer</span><span>); </span><span style="color:#bf616a;">err </span><span>!= </span><span style="color:#d08770;">nil </span><span>{
</span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">err
</span><span> }
</span><span>
</span><span> </span><span style="color:#65737e;">// SECURITY: Ensure that we haven&#39;t been redirected from an external to a local filesystem
</span><span> </span><span style="color:#65737e;">// Now we know all of these must parse
</span><span> </span><span style="color:#bf616a;">cloneAddrURL</span><span>, </span><span style="color:#bf616a;">_ </span><span>:= </span><span style="color:#bf616a;">url</span><span>.</span><span style="color:#bf616a;">Parse</span><span>(</span><span style="color:#bf616a;">opts</span><span>.</span><span style="color:#bf616a;">CloneAddr</span><span>)
</span><span> </span><span style="color:#bf616a;">cloneURL</span><span>, </span><span style="color:#bf616a;">_ </span><span>:= </span><span style="color:#bf616a;">url</span><span>.</span><span style="color:#bf616a;">Parse</span><span>(</span><span style="color:#bf616a;">repo</span><span>.</span><span style="color:#bf616a;">CloneURL</span><span>)
</span><span>
</span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">cloneURL</span><span>.</span><span style="color:#bf616a;">Scheme </span><span>== &quot;</span><span style="color:#a3be8c;">file</span><span>&quot; || </span><span style="color:#bf616a;">cloneURL</span><span>.</span><span style="color:#bf616a;">Scheme </span><span>== &quot;&quot; {
</span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">cloneAddrURL</span><span>.</span><span style="color:#bf616a;">Scheme </span><span>!= &quot;</span><span style="color:#a3be8c;">file</span><span>&quot; &amp;&amp; </span><span style="color:#bf616a;">cloneAddrURL</span><span>.</span><span style="color:#bf616a;">Scheme </span><span>!= &quot;&quot; {
</span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Errorf</span><span>(&quot;</span><span style="color:#a3be8c;">repo info has changed from external to local filesystem</span><span>&quot;)
</span><span> }
</span><span> }
</span><span>
</span><span> </span><span style="color:#65737e;">// We don&#39;t actually need to check the OriginalURL as it isn&#39;t used anywhere
</span><span> }
</span></code></pre>
</div>
<br>
<br>
<div class="blog__post-tag-container">
<a class="blog__post-tag" href="/tags/gna">#gna</a>
<a class="blog__post-tag" href="/tags/gitea">#gitea</a>
<a class="blog__post-tag" href="/tags/security">#security</a>
<a class="blog__post-tag" href="/tags/problem">#problem</a>
<a class="blog__post-tag" href="/tags/upgrade">#upgrade</a>
<a class="blog__post-tag" href="/tags/solution">#solution</a>
</div>
</div>
</main>
<footer>
<div class="footer__container">
<!-- <div class="footer__column"> --->
<p class="footer__column license__conatiner">
All text <a
class="license__link"
rel="noreferrer"
href="http://creativecommons.org/licenses/by-sa/4.0/"
target="_blank"
>&nbsp;CC-BY-SA&nbsp;</a
>
&amp; code
<a
class="license__link"
rel="noreferrer"
href="https://www.gnu.org/licenses/agpl-3.0.en.html"
target="_blank"
>&nbsp;AGPL&nbsp;</a
>
|
<a
class="license__link"
rel="noreferrer"
href="https://www.eff.org/issues/do-not-track/amp/"
target="_blank"
>&nbsp;No AMP&nbsp;</a
>
</p>
<!-- </div> -->
<div class="footer__column--center">
<a href="/blog/atom.xml" target="_blank" rel="noopener" title="RSS">
<img
src="https://gna.org/icons/rss.svg?h=f6cd584bdbcd2eb4d1b8b84c9cf083ef45f772167c33fdcee754b35ae8ff4c7d"
class="footer__icon"
alt="Email icon"
/>
</a>
</div>
<div class="footer__column">
<a href="/about" title="About">About</a>
<a href="/coc" title="Code of Conduct">CoC</a>
<span class="footer__column-divider--mobile-only">|</span>
<a href="/legalese" title="Legalese">Legalese</a>
<a href="/privacy-policy" title="Privacy Policy">Privacy</a>
<span class="footer__column-divider--mobile-only">|</span>
<a
href="https://gitea.gna.org/Gna"
rel="noreferrer"
target="_blank"
title="Status"
>Source Code</a
>
<a href="/tos" title="Terms of Service">ToS</a>
</div>
</div>
</footer>
</div>
</body>
</html>