Einführung
Der letzte Beitrag hat gezeigt wie wir unsichere Abhängigkeiten in einem Projekt finden und beheben können. Neben dem Problem der mangelnde Abhängigkeiten zu beheben, die in einem Projekt in großer Anzahl vorhanden sein können, gibt es eine weitere Möglichkeit den eigenen Code zu härten.
Bei einer laufenden Anwendung im Internet gibt es ein “Grundrauschen”. Das heißt, jede Anwendung die sich öffentlich präsentiert ist nach gewisser Zeit bestimmten Angriffen ausgesetzt. Das kann von Botnetzen kommen, aber auch Personen, die mal “testen” wollen, ob man auf der Seite durch Hacking etwas holen kann.
Um die eigene Anwendung gegen solche Angriffe schon vor der Veröffentlichung zu wappnen gibt es DAST-Systeme, kurz für ‘Dynamic Application Security Testing’. Hierbei versucht ein Tool von außen verschiedene Angriffsmuster mit verschiedenen Eingaben auszuführen und wertet dann die Ergebnisse auf potenzielle Lücken aus. Im Prinzip ist das genau das, was der spätere bösartige Angreifer oder das Botnetz auch auf die Anwendung versucht. Dieser Beitrag unterscheidet sich von dem letzten dadurch, dass nicht Code analysiert wird, sondern ein “echter” Angreifer und mögliche Angriffs-Anfragen auf die eigene Anwendung losgelassen werden.
Ein populäres DAST-System, um das zu machen, ist das Open-Source-Werkzeug OWASP ZAP.
Im Nachfolgenden wird beschrieben wie man OWASP ZAP in Jenkins integrieren kann.
Viele Beschreibungen basieren auf der super Dokumentation Jenkins at your Service! Integrating ZAP in Continuous Delivery:
Die einzelnen Abschnitte sind aufgeteilt in:
Die hier beschriebenen Schritte wurden auf einem Mac OS X durchgeführt. Dies betrifft insbesondere das Kapitel in dem OWASP ZAP als Desktop-Anwendung installiert wird. Man kann die Schritte auch auf einem anderen Betriebssystem durchführen. Hierzu muss man dann allerdings die entsprechenden Kommandozeilen-Äquivalente auf einem anderen OS nachschlagen.
Installation OWASP ZAP in Jenkins
Alte Docker Container stoppen
Wer noch aus dem alten Kapitel folgt: Für die folgenden Erläuterungen benötigen wir den Git-Server erstmal nicht mehr. Wir können ihn stoppen mit:
docker stop <containerid_gitserver>
Installation
Als nächstes installieren wir OWASP ZAP in Jenkins.
Installation notwendiger Plugins
Zur Installation verwenden wir die Docker-Infrastruktur, welcher wir im letzten Kapitel beschrieben haben.
Als nächstes benötigen wir:
- das offizielle ‘OWASP ZAP Plugin’, das wir für die Scans gegen unsere Anwendung verwenden werden
- das ‘Custom Tool Plugin’ - zur vereinfachten Installation und
- das ‘HTML Publisher Plugin’, , um uns die Ergebnisse in den Builds anschauen zu können.
Hierfür folgende Schritte ausführen:
Wir klicken auf ‘Official OWASP ZAP’:
Ebenfalls wählen wir ‘Custom Tool’ aus. Das können wir über das Filter-Feld oben rechts machen. Danach klicken wir auf ‘Download now and install after restart’ und ‘Restart Jenkins when installation is complete and no jobs are running’:
Wir gehen nun auf die Standard-Jenkins-Seite:
Nach etwas Zeit sollte dann der Login-Screen von Jenkins wieder erscheinen und man kann sich einloggen.
Konfiguration Custom Tools
Wir suchen das fertige Installations-Paket ‘OWASP ZAP 2.7.0’ für Linux:
Auf der GitHub Release 2.7.0 Seite findet sich der Link auf das Linux Release: https://github.com/zaproxy/zaproxy/releases/download/2.7.0/ZAP_2.7.0_Linux.tar.gz
Und setzen es als Installations-Quelle:
Als Eingaben unter ‘Custom Tool’ verwenden wir:
- ‘Custom tool’
- Name :
ZAP_2.7.0
- Install automatically
- Download URL for binary archive:
https://github.com/zaproxy/zaproxy/releases/download/2.7.0/ZAP_2.7.0_Linux.tar.gz
- Subdirectory of extracted archive:
ZAP_2.7.0
und klicken schließlich auf ‘Save’.
Konfiguration Port ZAP
Unter ‘ZAP’:
Wir setzen den Port auf etwas Seltenes, zB. 12123:
- Default Host:
localhost
- Default Port:
12123
‘Save’
Konfiguration eines OWASP ZAP Jobs
Nun erstellen wir unseren ersten ‘OWASP ZAP Scan Job’:
Hierfür ‘zap_scan_demo’ eingeben und ‘Ok’ klicken
Nun für ‘Tool Selection’: ZAP_2.7.0
Wir geben an wo wir OWASP ZAP hin installiert haben möchten:
- Path:
/var/jenkins_home/owaspzap
- Persist session:
owasp_webgoat_zap_session
Konfiguration der Ziel-URL
Als nächstes müssen wir Angaben über die Ziel-URL machen, also die URL die wir angreifen wollen.
Hierfür benötigen wir folgende Angaben:
- Session Properties
- Include in Context: Welche URLs liegen im Test-Rahmen? Wir möchten keine URLs außerhalb scannen und uns unter Umständen strafbar machen
- Authentification: Hier kann eingestellt werden, wie sich der Scanner in die Anwendung einloggen kann und wie er das bemerkt. Jegliche Anwendung mit einem Login, die man auch innerhalb testen möchte, benötigt diese Einstellung.
- Attack Mode
- Starting Point: Die Adresse von der aus wir den Scan starten möchten.
- Spider Scan: Hier können wir auswählen ob der Scanner zuvor versucht anhand vorhandener Links in der Wurzel-Seite weitere Seiten zu finden. Das macht Sinn, da wir ja nicht nur eine Seite testen möchten.
- Active Scan: Hier können wir die Scan-Policy auswählen. Solange wir noch keine erstellt haben wird die Default Policy von OWASP ZAP genommen.
- Finalize Run: Hier kann man definieren, wie der Report erzeugt werden soll.
Anhand einer kleinen Beispiel-Anwendung, welche für XSS anfällig ist, erläutere ich zunächst wie wir die einzelnen Punkte konfigurieren können. Danach gehe ich auf die Authentifizierung in Anwendungen ein, und wie man diese im Scanner einstellt. Mit dem Gezeigten sollte es am Ende möglich sein auch ein größeres Projekt konfigurieren zu können.
Konfiguration OWASP ZAP
Installation
Um die Einstellungen einfacher vorzunehmen installieren wir uns lokal die originäre Anwendung von OWASP ZAP.
Um alles besser nachvollziehen zu können verwenden wir eine feste Version : 2.7.0
. Dafür verwenden wir einen bestimmten Git-Hash in der URL der speziell Version 2.7.0
installiert:
brew cask install https://raw.githubusercontent.com/caskroom/homebrew-cask/645dbb8228ec2f1f217ed1431e188687aac13ca5/Casks/owasp-zap.rb`
Man kann das Programm nun über den Mac durch Eingabe von OWASP ZAP
in Spotlight starten:
Jetzt ‘No, I do not want to persist this session at this moment in time
’ anklicken
Danach ‘Start’ ansteuern:
Konfiguration Proxy ZAP
Wir klicken auf das kleine Rädchen:
und setzen bei ‘Local Proxies’ folgende Daten:
- Address:
127.0.0.1
- Port:
9000
sowie speichern mit Ok
.
Konfiguration FoxyProxy
Im Browser konfigurieren wir uns einen Proxy für localhost:9000
.
Dafür kann man sowohl in Firefox (unter Add-ons) als auch Chrome (unter Extensions), die Erweiterung FoxyProxy verwenden.
Zunächst installieren wir die Erweiterung.
Danach öffnen wir sie über den Klick auf das Icon oben rechts im Browser:
Und stellen folgendes ein:
- Proxy Type:
HTTP
- Title or Description (optional):
OWASP ZAP 127.0.0.1:9000
- IP address, DNS name, server name:
127.0.0.1
- Port:
9000
Save
Nun können wir den Proxy auswählen:
Das Icon wird zu:
Nun läuft der Traffic über den Proxy.
Beispiel-Anwendung XSS
Wir starten eine Anwendung, welche anfällig ist für XSS:
docker run -ti --rm -p 127.0.0.1:1185:1185 \
-d secf00tprint/victim_easy_xss_server`
Wenn wir die Anwendung öffnen sehen wir Texteingabefelder für Kommentare. Hier kann man Code injizieren:
open http://127.0.0.1:1185
Wir ermitteln die IP im Docker-Netz:
docker inspect <containerid_victim_easy_xss_server>|grep "IPA"
Jenkins Session Properties
Danach tragen wir unter ‘Session Properties’ ein:
- Context Name:
zap_scan_demo
- Include in Context:
http://<ermittelte_IP>:1185/.*
, z.B.http://172.17.0.4:1185/.*
Unter ‘Attack Mode’ geben wir die Wurzel-URL ein:
http://<ermittelte_IP>:1185/
z.B.
http://172.17.0.4:1185/
‘Save’
Danach bauen wir das Item das erste mal.
‘Build Now’
Danach sollte ZAP im Ordner /var/jenkins_home/owaspzap
installiert sein.
Die Schwachstelle in der Beispiel-Anwendung prüfen
Wenn wir unseren Proxy starten und im Browser http://127.0.0.1:1185
aufrufen,
sollte dies in OWASP ZAP erscheinen:
Wir gehen nun wieder in den Browser und geben im GET-Feld folgendes ein:
- Comment (using GET):
Test
‘Show’
Ergebnis:
Die URL zeigt http://127.0.0.1:1185/?comment=Test&enter_comment=Show
und das Kommentar erscheint:
Klicken wir auf ‘Back’, geben
<script>alert(1)</script>
ein und klicken auf ‘Save’, sehen wir ein Pop-up:
Das heißt die Anwendung ist anfällig für XSS.
OWASP ZAP müsste diese finden. Das schauen wir uns doch jetzt mal etwas genauer an:
Konfiguration Scan Policy
Unter dem Icon mit dem Mischpult können wir einstellen wie wir scannen wollen:
Zunächst klicken wir auf ‘Add’
und wählen folgende Werte:
- Scan Policy
- Policy:
XSS
- Default Alert Threshold:
Medium
- Default Attack Strength:
Low
- Information Gathering: Threshold:
OFF
, Strength:Default
- Server Security: Threshold:
OFF
, Strength:Default
Unter ‘Injection’ setzen wir alles auf ‘Threshold’: OFF
und Strength: Default
, außer Einträge mit Cross-Site-Scripting. Diese setzen wir auf Low
:
‘Miscellaneous’, ‘External Redirect’, ‘Threshold’ schalten wir auf OFF
, Strength auf Default
und ‘Script Active Scan Rules’ auf Low
, Default
:
‘Ok’
Dann ‘XSS’ und ‘Export’ und speichern die Datei als ‘XSS.policy’:
‘Save’
Wenn wir uns die Datei anschauen, sehen wir dass es sich um eine XML-Datei handelt, die sich an ein bestimmtes Format hält.
Die einzelnen Einträge können nachgeschlagen werden:
Policy Kürzel für aktive und passive Scans
Wir merken uns den Ort der Datei, wo wir sie abgelegt haben, da wir sie nachher für Jenkins benötigen.
XSS Scan
Wir suchen nun in der ‘History’ von OWASP ZAP den Request in welchem wir ‘Test’ eingegeben haben:
und wählen unter ‘Attack’,’Active Scan’:
Unter ‘Policy’ wählen wir ‘XSS’ aus:
und klicken auf ‘Start Scan’.
Wenn wir nun auf ‘Alerts’ klicken
Sehen wir dass ein XSS gefunden wurde:
Dieses Finding wollen wir im Jenkins ebenfalls nachvollziehen.
Jenkins Attack Mode
Zunächst müssen wir die Policy in Jenkins kopieren. Hierfür folgenden Befehl in der Kommandozeile eingeben im Verzeichnis, wo XSS.policy
abgelegt wurde:
docker cp XSS.policy <containerid_jenkins>:/var/jenkins_home/owaspzap/policies/
Wenn wir nun die Konfigurationsseite OWASP ZAP in Jenkins öffnen können wir die Policy auswählen.
Attack Mode:
- Starting point:
http://<ermittelte_IP>:1185/?comment=test&enter_comment=Show
z.B.http://172.17.0.3:1185/?comment=test&enter_comment=Show
- Spider Scan: True
- Recurse: True
- Subtree Only: Max Children to Crawl:
2
- Active Scan
- Policy:
XSS
wählen
Jenkins Report
Um einen entsprechenden Report zu generieren,
setzen wir unter ‘Finalize Run’:
- Generate Reports: True
- Clean Workspace Reports: True
- Filename:
JENKINS_ZAP_VULNERABILITY_REPORT_${BUILD_ID}
- Generate Report: True
- Format: xml und html auswählen
Unter ‘Add post-build action’, ‘Archive the artifacts’:
- Archive the artifacts
- Files to archive:
logs/*,reports/*
und ‘Add post-build action’ ‘Publish HTML Reports’:
‘Add’
- Publish HTML reports: Reports
- HTML directory to archive:
reports/
- Index page[s]:
JENKINS_ZAP_VULNERABILITY_REPORT_${BUILD_ID}
- Report title:
ZAP Scan Demo
und schließlich klicken wir auf ‘Save’.
Finaler Scan
Um den finalen Scan zu starten wählen wir ‘Build now’:
Der durchgeführte Scan sollte den XSS finden:
Authentifizierung
Im folgenden Abschnitt werde ich nun erklären, wie man eine Authentifizierung einrichten kann. Um es besser zu verdeutlichen nehmen ich 2 Anwendungen: OWASP WebGoat und OWASP Juice Shop:
OWASP Juice Shop
Um diese Anwendung zu starten setzen wir folgenden Befehl auf der Kommandozeile ab:
docker run --rm -p 127.0.0.1:3000:3000 -d bkimminich/juice-shop
und öffnen die gestartete Anwendung im Browser:
open http://127.0.0.1:3000
Im Browser sollte der Proxy für OWASP ZAP gesetzt sein (vgl Kapitel Konfiguration FoxyProxy).
Nun gehen wir auf die Login-Maske:
Wir melden uns an mit:
- User:
' or 1=1;--
- Password: beliebig
In der ‘History’ in OWASP ZAP müssten wir einen POST sehen:
Username / Password Parameter
Das OWASP-ZAP Plugin in Jenkins benötigt für eine erfolgreiche Authentifizierung, welche Parameter für das Login verwendet werden:
Man kann sie in diesem Fall aus dem POST-Request ermitteln:
{"email":"' or 1=1;--","password":"p"}
Das können wir schon mal in Jenkins eintragen:
Ebenfalls können wir die Login-Credentials
- Username:
' or 1=1;--
- Password: beliebig
und die Login-URL aus ZAP entnehmen:
http://127.0.0.1:3000/rest/user/login
Wobei wir hier die IP noch mit der IP austauschen müssen, welche im Docker-Netz vorhanden ist, zB:
http://172.17.0.3:3000/rest/user/login
Das tragen wir in Jenkins ein:
Logged-in String
Um zu bestimmen, wann der Nutzer eingeloggt ist muss in Jenkins ein Logged-In-String angegeben werden:
Wenn man auf das Fragezeichen rechts klickt bekommt man angezeigt wie dieser ermittelt wird:
The Logged in indicator, when present in a response message (either the header or the body), signifies that the response message corresponds to an authenticated request.
Dafür schauen wir uns die Response in OWASP ZAP für einen erfolgreichen Login an:
Und speichern diesen Response unter dem Namen response.raw.out
:
Rechte Maustaste, ‘Save raw’, ‘Response’, ‘All’:
Nun loggen wir uns in Juicy Shop wieder aus
und versuchen uns mit falschen Credentials anzumelden.
Wir nehmen
- Username:
test
- Passwort:
test
und suchen den POST-Request in ZAP:
Dann klicken wir auf Response:
Und speichern diesen:
Rechte Maustaste, ‘Save raw’:
‘Response’, ‘All’:
Wir nehmen wieder eine Text-Datei:
- Save As:
response2.out
- File Format:
Raw
Wenn wir nun die beiden Responses vergleichen (zB mittels vim-Tools vimdiff response.out.raw response2.out.raw
),
sehen wir, dass ein Login an dem Aufkommen des Strings authentication
in der Response zu erkennen ist.
Das heißt wir setzen in Jenkins folgenden Reg-Ex für den Logged-In Indicator:
.*\Qauthentication\E.*
Logged-out String
Um zu bestimmen, wann wir ausgeloggt werden, schauen wir uns an welcher String nur auf Login-Page zu finden ist. Hierfür im Browser mit der Maus auf ‘Login’ hovern, rechte Maustaste drücken und das Element untersuchen:
Wir verwenden den String TITLE_LOGIN
:
und tragen in in Jenkins ein:
.*\QTITLE_LOGIN\E.*
Fehlende Daten
Um unseren Scan zu vervollständigen tragen wir noch folgende Parameter ein:
- Session Properties
- Include in Context:
http://<ermittelte_IP>:3000/.*
zBhttp://172.17.0.3:3000/.*
Authentication sollte folgendermaßen aussehen:
Für den ‘Attack Mode’ setzen wir:
- Starting Point:
http://<ermittelte_IP>:3000/
, z.B.http://172.17.0.3:3000/
. Achtung: Es ist hier wichtig den abschließenden Slash zu setzen, sonst funktioniert der Spider und Scanner unter Umständen nicht richtig. - Spider Scan
- Recurse: True
- Subtree Only: True
- Max Children To Crawl:
2
- Active Scan
- Policy:
Default Policy
, welche mit MEDIUM Stärke scannt - Recurse: True
‘Finalize Run’ und die Post-Build Aktionen belassen wir wie gehabt:
Scan starten
Nun können wir den Build / Scan starten. Dies wird einige Zeit in Anspruch nehmen.
OWASP Web Goat
Eine weitere Anwendung anhand derer wir die Authentifizierung veranschaulichen wollen ist OWASP WebGoat.
Wir starten sie mit:
docker run -p 127.0.0.1:10394:8080 -it webgoat/webgoat-8.0 /home/webgoat/start.sh open http://127.0.0.1:10394/WebGoat/
Nutzer erstellen
Zunächst müssen wir einen Nutzer registrieren:
Wir nehmen
- Username:
webgoat
- Password:
webgoat
, akzeptieren die Nutzungsbedingungen und klicken auf ‘Sign up’:
Logged-in / Logged-out Indicators
Bei Aufruf der URL http://127.0.0.1:10394/WebGoat/
werden wir auf http://127.0.0.1:10394/WebGoat/login
weitergeleitet.
Wenn wir uns nun mit den Credentials einloggen, OWASP ZAP mitschneiden lassen, und Requests und Responses für einen erfolgreichen und nicht erfolgreichen Login genauer anschauen, erkennen wir:
- Login-Parameter (POST):
username=webgoat&password=webgoat
- Username Parameter:
username
- Password Parameter:
password
- Login-URL:
http://172.17.0.5:8080/WebGoat/login
, die IP und Port sind diejenigen im Dockernetz. Beides kann überdocker inspect <containerid_webgoat>|grep "IPA"
(IP) bzw aus dem obigen docker-Aufruf ausgelesen werden (Port). - Username:
webgoat
- Password:
webgoat
Für einen erfolgreichen Login:
Für einen nicht-erfolgreichen Login:
Im Header unterscheidet sich die Location:
Das verwenden wir für den Logged-In-Indicator:
.*\QLocation: http://172.17.0.5:8080/WebGoat/welcome.mvc\E.*
Für den Logged-out-Indicator nehmen wir die URL der Login-Seite:
.*\Qhttp://127.0.0.5:8080/WebGoat/login\E.*
Sollte diese sich in der einer der Responses befinden, gehen wir davon aus, dass das System den Nutzer ausgeloggt hat.
Finale Einstellung WebGoat
Die finale Einstellung zum Scan sieht dann wie folgt aus:
Best Practices
Aufgrund der Dauer und des Traffics die so ein Scan in Anspruch nehmen kann, empfiehlt es sich diesen nur einmal über Nacht oder einmal die Woche laufen zu lassen.
Einen täglichen Scan kann man über ‘Build Triggers’ einstellen:
Zusammenfassung
In diesem Artikel wurde zusammengefasst wie man OWASP ZAP im Continuous Integration System Jenkins einbauen und verwenden kann.