diff --git a/docker-compose.yml b/docker-compose.yml index 3d39f83..8172fb0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,9 @@ services: - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres + security_opt: + - no-new-privileges:true + read_only: true web: build: . image: pygoat/pygoat @@ -20,6 +23,9 @@ services: depends_on: - migration - db + security_opt: + - no-new-privileges:true + read_only: true migration: image: pygoat/pygoat command: python pygoat/manage.py migrate --noinput @@ -27,3 +33,6 @@ services: - .:/app depends_on: - db + security_opt: + - no-new-privileges:true + read_only: true diff --git a/introduction/apis.py b/introduction/apis.py index 7926708..ebf0a05 100644 --- a/introduction/apis.py +++ b/introduction/apis.py @@ -52,12 +52,6 @@ def ssrf_code_checker(request): return JsonResponse({'message':'method not allowed'},status = 405) else: return JsonResponse({'message':'UnAuthenticated User'},status = 401) - -# Insufficient Logging & Monitoring - - -@csrf_exempt -# @authentication_decorator def log_function_checker(request): if request.method == 'POST': csrf_token = request.POST.get("csrfmiddlewaretoken") @@ -66,31 +60,28 @@ def log_function_checker(request): dirname = os.path.dirname(__file__) log_filename = os.path.join(dirname, "playground/A9/main.py") api_filename = os.path.join(dirname, "playground/A9/api.py") - f = open(log_filename,"w") - f.write(log_code) - f.close() - f = open(api_filename,"w") - f.write(api_code) - f.close() + with open(log_filename,"w") as f: + f.write(escape(log_code)) + with open(api_filename,"w") as f: + f.write(escape(api_code)) # Clearing the log file before starting the test - f = open('test.log', 'w') - f.write("") - f.close() + with open('test.log', 'w') as f: + f.write("") url = "http://127.0.0.1:8000/2021/discussion/A9/target" payload={'csrfmiddlewaretoken': csrf_token } requests.request("GET", url) requests.request("POST", url) requests.request("PATCH", url, data=payload) requests.request("DELETE", url) - f = open('test.log', 'r') - lines = f.readlines() - f.close() + with open('test.log', 'r') as f: + lines = f.readlines() return JsonResponse({"message":"success", "logs": lines},status = 200) else: return JsonResponse({"message":"method not allowed"},status = 405) +def escape(s): + return s.replace("\\", "\\\\").replace('/', '\\/') #a7 codechecking api -@csrf_exempt def A7_disscussion_api(request): if request.method != 'POST': return JsonResponse({"message":"method not allowed"},status = 405) @@ -107,9 +98,7 @@ def A7_disscussion_api(request): return JsonResponse({"message":"success"},status = 200) return JsonResponse({"message":"failure"},status = 400) - #a6 codechecking api -@csrf_exempt def A6_disscussion_api(request): test_bench = ["Pillow==8.0.0","PyJWT==2.4.0","requests==2.28.0","Django==4.0.4"] @@ -121,18 +110,21 @@ def A6_disscussion_api(request): return JsonResponse({"message":"failure"},status = 400) except Exception as e: return JsonResponse({"message":"failure"},status = 400) +from django.views.decorators.csrf import csrf_exempt, csrf_protect +from django.utils.html import escape -@csrf_exempt +@csrf_protect def A6_disscussion_api_2(request): if request.method != 'POST': return JsonResponse({"message":"method not allowed"},status = 405) try: code = request.POST.get('code') + if code is None: + return JsonResponse({"message":"missing code"},status = 400) dirname = os.path.dirname(__file__) filename = os.path.join(dirname, "playground/A6/utility.py") - f = open(filename,"w") - f.write(code) - f.close() + with open(filename,"w") as f: + f.write(escape(code)) except: - return JsonResponse({"message":"missing code"},status = 400) - return JsonResponse({"message":"success"},status = 200) \ No newline at end of file + return JsonResponse({"message":"error"},status = 500) + return JsonResponse({"message":"success"},status = 200) diff --git a/introduction/mitre.py b/introduction/mitre.py index c899c21..6411073 100644 --- a/introduction/mitre.py +++ b/introduction/mitre.py @@ -150,6 +150,14 @@ def mitre_top24(request): def mitre_top25(request): if request.method == 'GET': return render(request, 'mitre/mitre_top25.html') +import os +import hashlib +import hmac +from django.http import HttpResponse, HttpResponseRedirect +import bcrypt +from datetime import datetime, timedelta +from jwt import encode, decode +from jwt.exceptions import InvalidToken @authentication_decorator def csrf_lab_login(request): @@ -158,23 +166,25 @@ def csrf_lab_login(request): elif request.method == 'POST': password = request.POST.get('password') username = request.POST.get('username') - password = md5(password.encode()).hexdigest() - User = CSRF_user_tbl.objects.filter(username=username, password=password) + hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()) + User = CSRF_user_tbl.objects.filter(username=username, password=hashed_password) if User: payload ={ 'username': username, 'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=300), 'iat': datetime.datetime.utcnow() } - cookie = jwt.encode(payload, 'csrf_vulneribility', algorithm='HS256') - response = redirect("/mitre/9/lab/transaction") - response.set_cookie('auth_cookiee', cookie) - return response + jwt_secret = os.environ.get('JWT_SECRET') + if jwt_secret: + cookie = encode(payload, jwt_secret, algorithm='HS256') + response = redirect("/mitre/9/lab/transaction") + response.set_cookie('auth_cookiee', cookie, secure=True, httponly=True, samesite='Lax') + return response + else: + return HttpResponse("JWT secret is not set") else : return redirect('/mitre/9/lab/login') - @authentication_decorator -@csrf_exempt def csrf_transfer_monei(request): if request.method == 'GET': try: @@ -208,14 +218,28 @@ def csrf_transfer_monei_api(request,recipent,amount): return redirect('/mitre/9/lab/transaction') else: return redirect ('/mitre/9/lab/transaction') +def safe_eval(expression): + allowed_operators = ['+', '-', '*', '/'] + for operator in allowed_operators: + if operator in expression: + num1, num2 = expression.split(operator) + if operator == '+': + return float(num1) + float(num2) + elif operator == '-': + return float(num1) - float(num2) + elif operator == '*': + return float(num1) * float(num2) + elif operator == '/': + if num2 != '0': + return float(num1) / float(num2) + else: + return 'Error: Division by zero' + return 'Error: Invalid operation' - -# @authentication_decorator -@csrf_exempt def mitre_lab_25_api(request): if request.method == "POST": expression = request.POST.get('expression') - result = eval(expression) + result = safe_eval(expression) return JsonResponse({'result': result}) else: return redirect('/mitre/25/lab/') @@ -228,13 +252,9 @@ def mitre_lab_25(request): @authentication_decorator def mitre_lab_17(request): return render(request, 'mitre/mitre_lab_17.html') - def command_out(command): - process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process = subprocess.Popen(command.split(), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return process.communicate() - - -@csrf_exempt def mitre_lab_17_api(request): if request.method == "POST": ip = request.POST.get('ip') @@ -244,4 +264,4 @@ def mitre_lab_17_api(request): err = err.decode() pattern = "STATE SERVICE.*\\n\\n" ports = re.findall(pattern, res,re.DOTALL)[0][14:-2].split('\n') - return JsonResponse({'raw_res': str(res), 'raw_err': str(err), 'ports': ports}) \ No newline at end of file + return JsonResponse({'raw_res': str(res), 'raw_err': str(err), 'ports': ports}) diff --git a/introduction/playground/A9/api.py b/introduction/playground/A9/api.py index 35e1bd2..f620f2a 100644 --- a/introduction/playground/A9/api.py +++ b/introduction/playground/A9/api.py @@ -1,16 +1,19 @@ from django.http import JsonResponse -from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.csrf import csrf_protect +from django.middleware.csrf import CsrfViewMiddleware from .main import Log - -@csrf_exempt +@csrf_protect def log_function_target(request): L = Log(request) if request.method == "GET": L.info("GET request") return JsonResponse({"message":"normal get request", "method":"get"},status = 200) if request.method == "POST": + csrf_token =(CsrfViewMiddleware().get_token(request)) + if csrf_token != request.POST.get('csrfmiddlewaretoken'): + return JsonResponse({"message":"CSRF token is invalid"},status = 403) username = request.POST['username'] password = request.POST['password'] L.info(f"POST request with username {username} and password {password}") @@ -30,4 +33,4 @@ def log_function_target(request): return JsonResponse({"message":"success", "method":"patch"},status = 200) if request.method == "UPDATE": return JsonResponse({"message":"success", "method":"update"},status = 200) - return JsonResponse({"message":"method not allowed"},status = 403) \ No newline at end of file + return JsonResponse({"message":"method not allowed"},status = 403) diff --git a/introduction/playground/A9/archive.py b/introduction/playground/A9/archive.py index c9db8fc..1d6a674 100644 --- a/introduction/playground/A9/archive.py +++ b/introduction/playground/A9/archive.py @@ -1,11 +1,13 @@ from django.http import JsonResponse -from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.csrf import csrf_protect +from django.middleware.csrf import get_token from .main import Log - -@csrf_exempt +@csrf_protect def log_function_target(request): + if 'csrftoken' not in request cookies: + get_token(request) L = Log(request) if request.method == "GET": L.info("GET request") diff --git a/introduction/static/js/a9.js b/introduction/static/js/a9.js index 9c58b8a..5a35db0 100644 --- a/introduction/static/js/a9.js +++ b/introduction/static/js/a9.js @@ -37,9 +37,10 @@ event3 = function(){ document.getElementById("a9_d3").style.display = 'flex'; for (var i = 0; i < data.logs.length; i++) { var li = document.createElement("li"); - li.innerHTML = data.logs[i]; + var textNode = document.createTextNode(data.logs[i]); + li.appendChild(textNode); document.getElementById("a9_d3").appendChild(li); } }) .catch(error => console.log('error', error)); - } \ No newline at end of file +} diff --git a/introduction/templates/Lab/A9/a9_lab.html b/introduction/templates/Lab/A9/a9_lab.html index 5a70b46..cd56d50 100644 --- a/introduction/templates/Lab/A9/a9_lab.html +++ b/introduction/templates/Lab/A9/a9_lab.html @@ -1,5 +1,6 @@ {% extends "introduction/base.html" %} {% load static %} +{% csrf_token %} {% block content %} {% block title %} A9 @@ -8,6 +9,7 @@

Yaml To Json Converter

+ {% csrf_token %}

@@ -34,4 +36,4 @@
Here is your output:

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/introduction/templates/Lab/A9/a9_lab2.html b/introduction/templates/Lab/A9/a9_lab2.html index cace076..6c2bd18 100644 --- a/introduction/templates/Lab/A9/a9_lab2.html +++ b/introduction/templates/Lab/A9/a9_lab2.html @@ -19,6 +19,7 @@

Some Example

+ {% csrf_token %} @@ -88,7 +89,8 @@

Some Example

form.submit(); } {% if error %} - alert("{{ data }}"); + var data = JSON.parse("{{ data|json_script }}"); + alert(data); {% endif %} diff --git a/introduction/templates/Lab/BrokenAccess/ba_lab.html b/introduction/templates/Lab/BrokenAccess/ba_lab.html index d45da9b..3281ece 100644 --- a/introduction/templates/Lab/BrokenAccess/ba_lab.html +++ b/introduction/templates/Lab/BrokenAccess/ba_lab.html @@ -1,5 +1,6 @@ {% extends "introduction/base.html" %} {% load static %} +{% csrf_token %} {% block content %} {% block title %} Broken Access Control. @@ -9,12 +10,10 @@

Admins Have the Secretkey

@@ -43,4 +42,4 @@

Please Provide Credentials

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/introduction/templates/Lab/BrokenAuth/otp.html b/introduction/templates/Lab/BrokenAuth/otp.html index 3d12cda..dc98fdc 100644 --- a/introduction/templates/Lab/BrokenAuth/otp.html +++ b/introduction/templates/Lab/BrokenAuth/otp.html @@ -7,6 +7,7 @@
Login Through Otp

+ {% csrf_token %} @@ -16,6 +17,7 @@
Login Through Otp

+ {% csrf_token %}

@@ -34,4 +36,4 @@

Login Successful as user : {{email}}

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/introduction/templates/Lab/CMD/cmd_lab.html b/introduction/templates/Lab/CMD/cmd_lab.html index 2998cd3..6175ff5 100644 --- a/introduction/templates/Lab/CMD/cmd_lab.html +++ b/introduction/templates/Lab/CMD/cmd_lab.html @@ -7,6 +7,7 @@

Name Server Lookup

+ {% csrf_token %}

@@ -33,4 +34,4 @@
Output

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/introduction/templates/Lab/CMD/cmd_lab2.html b/introduction/templates/Lab/CMD/cmd_lab2.html index a71a605..7319130 100644 --- a/introduction/templates/Lab/CMD/cmd_lab2.html +++ b/introduction/templates/Lab/CMD/cmd_lab2.html @@ -7,6 +7,7 @@

Evaluate any expression!

+ {% csrf_token %}

@@ -29,4 +30,4 @@
Output

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/introduction/templates/Lab/XSS/xss_lab_3.html b/introduction/templates/Lab/XSS/xss_lab_3.html index a550b9a..e6935b3 100644 --- a/introduction/templates/Lab/XSS/xss_lab_3.html +++ b/introduction/templates/Lab/XSS/xss_lab_3.html @@ -19,7 +19,8 @@

Welcome to XSS Challenge

{{code}}


diff --git a/introduction/templates/Lab/ssrf/ssrf_discussion.html b/introduction/templates/Lab/ssrf/ssrf_discussion.html index 7dc6678..2eb5868 100644 --- a/introduction/templates/Lab/ssrf/ssrf_discussion.html +++ b/introduction/templates/Lab/ssrf/ssrf_discussion.html @@ -123,22 +123,22 @@
ssrf_lab.html