Secure Software Development durch CI

Teil II - Web Application Scanning / DAST

Posted on July 17, 2018

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:

owaspzapinstallation1 owaspzapinstallation2 owaspzapinstallation3 owaspzapinstallation4 owaspzapinstallation5

Wir klicken auf ‘Official OWASP ZAP’:

owaspzapinstallation7

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’:

customtoolsinstallation1 customtoolsinstallation2 customtoolsinstallation3 customtoolsinstallation4

Wir gehen nun auf die Standard-Jenkins-Seite:

owaspzapinstallation9 owaspzapinstallation10

Nach etwas Zeit sollte dann der Login-Screen von Jenkins wieder erscheinen und man kann sich einloggen.

loginafterrestart

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:

installowaspzapbycustomtools1 installowaspzapbycustomtools2 installowaspzapbycustomtools3 installowaspzapbycustomtools4

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

installowaspzapbycustomtools5

und klicken schließlich auf ‘Save’.

Konfiguration Port ZAP

managejenkins configuresystem

Unter ‘ZAP’:

Wir setzen den Port auf etwas Seltenes, zB. 12123:

  • Default Host: localhost
  • Default Port: 12123

configowaspzapport

‘Save’

Konfiguration eines OWASP ZAP Jobs

Nun erstellen wir unseren ersten ‘OWASP ZAP Scan Job’:

newitem

Hierfür ‘zap_scan_demo’ eingeben und ‘Ok’ klicken

zapscandemo1 zapscandemo2 zapscandemo3 zapscandemo4

Nun für ‘Tool Selection’: ZAP_2.7.0

zapscandemo6 zapscandemo7 zapscandemo8 zapscandemo9 zapscandemo10

Wir geben an wo wir OWASP ZAP hin installiert haben möchten:

  • Path: /var/jenkins_home/owaspzap
  • Persist session: owasp_webgoat_zap_session

zapscandemo11

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`

installowaspzapbrewcask

Man kann das Programm nun über den Mac durch Eingabe von OWASP ZAP in Spotlight starten:

startowaspzap

Jetzt ‘No, I do not want to persist this session at this moment in time’ anklicken

notpersistingsessionowaspzap

Danach ‘Start’ ansteuern:

notpersistingsessionowaspzap2

Konfiguration Proxy ZAP

Wir klicken auf das kleine Rädchen:

preferencesowaspzap1

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.

foxyproxyfirefox

Danach öffnen wir sie über den Klick auf das Icon oben rechts im Browser:

foxyproxyconfig1

Und stellen folgendes ein:

foxyproxyconfig2 foxyproxyconfig3

  • 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

foxyproxyconfig4

Save

foxyproxyconfig5

Nun können wir den Proxy auswählen:

foxyproxyconfig1 foxyproxyconfig6

Das Icon wird zu:

foxyproxyconfig7

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

victim_easy_xss1

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://&#x3C;ermittelte_IP&#x3E;:1185/.*, z.B. http://172.17.0.4:1185/.*

sessionpropertiessimplexss_owaspzap

Unter ‘Attack Mode’ geben wir die Wurzel-URL ein:

http://&#x3C;ermittelte_IP&#x3E;:1185/ z.B.

http://172.17.0.4:1185/

sessionpropertiessimplexss_owaspzap2

‘Save’

Danach bauen wir das Item das erste mal.

‘Build Now’

buildnow

sessionpropertiessimplexss_owaspzap3

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,

foxyproxyconfig7 xss_easy_vuln1

sollte dies in OWASP ZAP erscheinen:

xss_easy_vuln2a xss_easy_vuln2b

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:

xss_easy_vuln3

Klicken wir auf ‘Back’, geben


<script>alert(1)</script>

ein und klicken auf ‘Save’, sehen wir ein Pop-up:

xss_easy_vuln4

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:

owaspzapscanpolicy1

Zunächst klicken wir auf ‘Add’

owaspzapscanpolicy2

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

owaspzapscanpolicy3

Unter ‘Injection’ setzen wir alles auf ‘Threshold’: OFF und Strength: Default, außer Einträge mit Cross-Site-Scripting. Diese setzen wir auf Low:

owaspzapscanpolicy4

‘Miscellaneous’, ‘External Redirect’, ‘Threshold’ schalten wir auf OFF, Strength auf Default und ‘Script Active Scan Rules’ auf Low, Default:

owaspzapscanpolicy5

‘Ok’

Dann ‘XSS’ und ‘Export’ und speichern die Datei als ‘XSS.policy’:

owaspzapscanpolicy6 owaspzapscanpolicy7 owaspzapscanpolicy8

‘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.

owaspzapscanpolicy8b

Die einzelnen Einträge können nachgeschlagen werden:

Policy Kürzel für aktive und passive Scans

owaspzapscanpolicy9

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:

xss_easy_vuln5

und wählen unter ‘Attack’,’Active Scan’:

xss_easy_vuln6

Unter ‘Policy’ wählen wir ‘XSS’ aus:

xss_easy_vuln7

und klicken auf ‘Start Scan’.

xss_easy_vuln8

Wenn wir nun auf ‘Alerts’ klicken

xss_easy_vuln9

Sehen wir dass ein XSS gefunden wurde:

xss_easy_vuln10

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://&#x3C;ermittelte_IP&#x3E;:1185/?comment=test&#x26;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

owaspzapattackmode1

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

owaspzapreport1

Unter ‘Add post-build action’, ‘Archive the artifacts’:

owaspzapreport2

  • Archive the artifacts
  • Files to archive: logs/*,reports/*

owaspzapreport3

und ‘Add post-build action’ ‘Publish HTML Reports’:

owaspzapreport4

‘Add’

owaspzapreport5

  • Publish HTML reports: Reports
  • HTML directory to archive: reports/
  • Index page[s]: JENKINS_ZAP_VULNERABILITY_REPORT_${BUILD_ID}
  • Report title: ZAP Scan Demo

owaspzapreport6

und schließlich klicken wir auf ‘Save’.

Finaler Scan

Um den finalen Scan zu starten wählen wir ‘Build now’:

buildnow

Der durchgeführte Scan sollte den XSS finden:

owaspzapscandemofinal1 owaspzapscandemofinal2 owaspzapscandemofinal3

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:

juicyshop1 juicyshop2

Wir melden uns an mit:

  • User: ' or 1=1;--
  • Password: beliebig

In der ‘History’ in OWASP ZAP müssten wir einen POST sehen:

juicyshop3

Username / Password Parameter

Das OWASP-ZAP Plugin in Jenkins benötigt für eine erfolgreiche Authentifizierung, welche Parameter für das Login verwendet werden:

juicyshop3b

Man kann sie in diesem Fall aus dem POST-Request ermitteln:

  • {"email":"' or 1=1;--","password":"p"}

juicyshop3a

Das können wir schon mal in Jenkins eintragen:

juicyshop3c

Ebenfalls können wir die Login-Credentials

  • Username: ' or 1=1;--
  • Password: beliebig

juicyshop3d

und die Login-URL aus ZAP entnehmen:

http://127.0.0.1:3000/rest/user/login

juicyshop3e

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:

juicyshop3f

Logged-in String

Um zu bestimmen, wann der Nutzer eingeloggt ist muss in Jenkins ein Logged-In-String angegeben werden:

juicyshop4

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.

juicyshop5

Dafür schauen wir uns die Response in OWASP ZAP für einen erfolgreichen Login an:

juicyshop5b juicyshop6

Und speichern diesen Response unter dem Namen response.raw.out:

Rechte Maustaste, ‘Save raw’, ‘Response’, ‘All’:

juicyshop6aa juicyshop6ab juiceshop8

Nun loggen wir uns in Juicy Shop wieder aus

juicyshop6ac

und versuchen uns mit falschen Credentials anzumelden.

Wir nehmen

  • Username: test
  • Passwort: test

juicyshop6ad

und suchen den POST-Request in ZAP:

juicyshop6a

Dann klicken wir auf Response:

juicyshop6b

Und speichern diesen:

Rechte Maustaste, ‘Save raw’:

juicyshop6d

‘Response’, ‘All’:

juicyshop6e

Wir nehmen wieder eine Text-Datei:

  • Save As: response2.out
  • File Format: Raw

juiceshop7 juiceshop8

Wenn wir nun die beiden Responses vergleichen (zB mittels vim-Tools vimdiff response.out.raw response2.out.raw),

juicyshop9

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.*

juicyshop10

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:

juicyshoploggedout1

Wir verwenden den String TITLE_LOGIN:

juicyshoploggedout2

und tragen in in Jenkins ein:

  • .*\QTITLE_LOGIN\E.*

juicyshoploggedout3

Fehlende Daten

Um unseren Scan zu vervollständigen tragen wir noch folgende Parameter ein:

  • Session Properties
  • Include in Context: http://&#x3C;ermittelte_IP&#x3E;:3000/.* zB http://172.17.0.3:3000/.*

juicyshopaddmissingdata1

Authentication sollte folgendermaßen aussehen:

juicyshopaddmissingdata2

Für den ‘Attack Mode’ setzen wir:

  • Starting Point: http://&#x3C;ermittelte_IP&#x3E;: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

juicyshopaddmissingdata3

‘Finalize Run’ und die Post-Build Aktionen belassen wir wie gehabt:

juicyshopaddmissingdata4 juicyshopaddmissingdata5

Scan starten

Nun können wir den Build / Scan starten. Dies wird einige Zeit in Anspruch nehmen.

buildnow scanowaspzapjuicyshop

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:

webgoat_registernewuser1

Wir nehmen

  • Username: webgoat
  • Password: webgoat

, akzeptieren die Nutzungsbedingungen und klicken auf ‘Sign up’:

webgoat_registernewuser2

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 über docker inspect <containerid_webgoat>|grep "IPA" (IP) bzw aus dem obigen docker-Aufruf ausgelesen werden (Port).
  • Username: webgoat
  • Password: webgoat

Für einen erfolgreichen Login:

webgoat_loginlogoutindicators1 webgoat_loginlogoutindicator1b

Für einen nicht-erfolgreichen Login:

webgoat_loginlogoutindicators2 webgoat_loginlogoutindicators2b

Im Header unterscheidet sich die Location:

webgoat_loginlogoutindicators3

Das verwenden wir für den Logged-In-Indicator:

.*\QLocation: http://172.17.0.5:8080/WebGoat/welcome.mvc\E.*

webgoat_loginlogoutindicators4

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:

owaspzapwebgoatfinal2 owaspzapwebgoatfinal1 owaspzapwebgoatfinal3 owaspzapwebgoatfinal4 owaspzapwebgoatfinal5

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:

owaspzapjenkinsbestpractices1

Zusammenfassung

In diesem Artikel wurde zusammengefasst wie man OWASP ZAP im Continuous Integration System Jenkins einbauen und verwenden kann.