diff --git a/docker-compose.yml b/docker-compose.yml index 3d39f83..ab88e2f 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..ace3c93 100644 --- a/introduction/apis.py +++ b/introduction/apis.py @@ -12,25 +12,17 @@ from .utility import * from .views import authentication_decorator - - -# steps --> -# 1. covert input code to corrosponding code and write in file -# 2. extract inputs form 2nd code -# 3. Run the code -# 4. get the result -@csrf_exempt def ssrf_code_checker(request): if request.user.is_authenticated: if request.method == 'POST': - python_code = request.POST['python_code'] - html_code = request.POST['html_code'] + python_code = request.POST.get('python_code') + html_code = request.POST.get('html_code') if not (ssrf_code_converter(python_code)): return JsonResponse({"status": "error", "message": "Invalid code"}) test_bench1 = ssrf_html_input_extractor(html_code) - if (len(test_bench1) >4): - return JsonResponse({'message':'too many inputs in Html\n Try again'},status = 400) + if (len(test_bench1) > 4): + return JsonResponse({'message': 'too many inputs in Html\n Try again'}, status=400) test_bench2 = ['secret.txt'] correct_output1 = [{"blog": "blog1-passed"}, {"blog": "blog2-passed"}, {"blog": "blog3-passed"}, {"blog": "blog4-passed"}] outputs = [] @@ -39,58 +31,61 @@ def ssrf_code_checker(request): if outputs == correct_output1: outputs = [] else: - return JsonResponse({'message':'Testbench failed, Code is not working\n Try again'},status = 200) + return JsonResponse({'message': 'Testbench failed, Code is not working\n Try again'}, status=200) correct_output2 = [{"blog": "No blog found"}] for inputs in test_bench2: outputs.append(main.ssrf_lab(inputs)) if outputs == correct_output2: - return JsonResponse({'message':'Congratulation, you have written a secure code.', 'passed':1}, status = 200) + return JsonResponse({'message': 'Congratulation, you have written a secure code.', 'passed': 1}, status=200) - return JsonResponse({'message':'Test bench passed but the code is not secure'}, status = 200,safe = False) + return JsonResponse({'message': 'Test bench passed but the code is not secure'}, status=200, safe=False) else: - return JsonResponse({'message':'method not allowed'},status = 405) + return JsonResponse({'message': 'method not allowed'}, status=405) else: - return JsonResponse({'message':'UnAuthenticated User'},status = 401) - + return JsonResponse({'message': 'UnAuthenticated User'}, status=401) # Insufficient Logging & Monitoring - -@csrf_exempt +# @csrf_exempt # @authentication_decorator def log_function_checker(request): if request.method == 'POST': - csrf_token = request.POST.get("csrfmiddlewaretoken") + # csrf_token = request.POST.get("csrfmiddlewaretoken") - CSRF token not needed here log_code = request.POST.get('log_code') api_code = request.POST.get('api_code') 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() + + # Sanitize input data before writing to files + log_code_sanitized = escape(log_code) + api_code_sanitized = escape(api_code) + + with open(log_filename, "w") as f: + f.write(log_code_sanitized) + + with open(api_filename, "w") as f: + f.write(api_code_sanitized) + # 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 } + # payload={'csrfmiddlewaretoken': csrf_token } - CSRF token not needed in payload + requests.request("GET", url) requests.request("POST", url) - requests.request("PATCH", url, data=payload) + requests.request("PATCH", url, data={}) # Removed user-controlled data requests.request("DELETE", url) - f = open('test.log', 'r') - lines = f.readlines() - f.close() - return JsonResponse({"message":"success", "logs": lines},status = 200) + + 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) - + return JsonResponse({"message": "method not allowed"}, status=405) #a7 codechecking api -@csrf_exempt def A7_disscussion_api(request): if request.method != 'POST': return JsonResponse({"message":"method not allowed"},status = 405) @@ -107,9 +102,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 +114,19 @@ def A6_disscussion_api(request): return JsonResponse({"message":"failure"},status = 400) except Exception as e: return JsonResponse({"message":"failure"},status = 400) - -@csrf_exempt def A6_disscussion_api_2(request): if request.method != 'POST': - return JsonResponse({"message":"method not allowed"},status = 405) + return JsonResponse({"message": "method not allowed"}, status=405) try: code = request.POST.get('code') - dirname = os.path.dirname(__file__) - filename = os.path.join(dirname, "playground/A6/utility.py") - f = open(filename,"w") - f.write(code) - f.close() - except: - return JsonResponse({"message":"missing code"},status = 400) - return JsonResponse({"message":"success"},status = 200) \ No newline at end of file + if code is not None: + sanitized_code = code[:1024] # Limit code length to prevent potential abuse + dirname = os.path.dirname(os.path.abspath(__file__)) + filename = os.path.join(dirname, "playground/A6/utility.py") + with open(filename, "w") as f: + f.write(sanitized_code) + else: + return JsonResponse({"message": "missing code"}, status=400) + except Exception as e: + return JsonResponse({"message": "error occurred", "details": str(e)}, status=400) + return JsonResponse({"message": "success"}, status=200) diff --git a/introduction/mitre.py b/introduction/mitre.py index c899c21..2ac02f1 100644 --- a/introduction/mitre.py +++ b/introduction/mitre.py @@ -150,6 +150,8 @@ def mitre_top24(request): def mitre_top25(request): if request.method == 'GET': return render(request, 'mitre/mitre_top25.html') +import hashlib +import jwt @authentication_decorator def csrf_lab_login(request): @@ -158,23 +160,21 @@ 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) - if User: - payload ={ + password_hash = hashlib.scrypt(password.encode(), salt=b'salt', n=2**14, r=8, p=1) + user = CSRF_user_tbl.objects.filter(username=username, password=password_hash.hex()) + 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') + cookie = jwt.encode(payload, 'your_secret_key_here', algorithm='HS256') response = redirect("/mitre/9/lab/transaction") - response.set_cookie('auth_cookiee', cookie) + response.set_cookie('auth_cookiee', cookie, secure=True, httponly=True, samesite='Lax') return response - else : + else: return redirect('/mitre/9/lab/login') - @authentication_decorator -@csrf_exempt def csrf_transfer_monei(request): if request.method == 'GET': try: @@ -208,14 +208,11 @@ def csrf_transfer_monei_api(request,recipent,amount): return redirect('/mitre/9/lab/transaction') else: return redirect ('/mitre/9/lab/transaction') - - # @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 +225,11 @@ def mitre_lab_25(request): @authentication_decorator def mitre_lab_17(request): return render(request, 'mitre/mitre_lab_17.html') +import subprocess def command_out(command): - process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process = subprocess.Popen(command, 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') @@ -243,5 +238,5 @@ def mitre_lab_17_api(request): res = res.decode() 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 + ports = re.findall(pattern, res, re.DOTALL)[0][14:-2].split('\n') + 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..f6ad731 100644 --- a/introduction/playground/A9/api.py +++ b/introduction/playground/A9/api.py @@ -1,33 +1,30 @@ from django.http import JsonResponse -from django.views.decorators.csrf import csrf_exempt - from .main import Log -@csrf_exempt 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) + return JsonResponse({"message": "normal get request", "method": "get"}, status=200) if request.method == "POST": username = request.POST['username'] password = request.POST['password'] L.info(f"POST request with username {username} and password {password}") if username == "admin" and password == "admin": - return JsonResponse({"message":"Loged in successfully", "method":"post"},status = 200) - return JsonResponse({"message":"Invalid credentials", "method":"post"},status = 401) + return JsonResponse({"message": "Loged in successfully", "method": "post"}, status=200) + return JsonResponse({"message": "Invalid credentials", "method": "post"}, status=401) if request.method == "PUT": L.info("PUT request") - return JsonResponse({"message":"success", "method":"put"},status = 200) + return JsonResponse({"message": "success", "method": "put"}, status=200) if request.method == "DELETE": if request.user.is_authenticated: - return JsonResponse({"message":"User is authenticated", "method":"delete"},status = 200) + return JsonResponse({"message": "User is authenticated", "method": "delete"}, status=200) L.error("DELETE request") - return JsonResponse({"message":"permission denied", "method":"delete"},status = 200) + return JsonResponse({"message": "permission denied", "method": "delete"}, status=200) if request.method == "PATCH": L.info("PATCH request") - return JsonResponse({"message":"success", "method":"patch"},status = 200) + 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": "success", "method": "update"}, status=200) + return JsonResponse({"message": "method not allowed"}, status=403) diff --git a/introduction/playground/A9/archive.py b/introduction/playground/A9/archive.py index c9db8fc..b804e9c 100644 --- a/introduction/playground/A9/archive.py +++ b/introduction/playground/A9/archive.py @@ -1,62 +1,31 @@ from django.http import JsonResponse -from django.views.decorators.csrf import csrf_exempt from .main import Log -@csrf_exempt 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) + return JsonResponse({"message": "normal get request", "method": "get"}, status=200) if request.method == "POST": - username = request.POST['username'] - password = request.POST['password'] + username = request.POST.get('username') + password = request.POST.get('password') L.info(f"POST request with username {username} and password {password}") if username == "admin" and password == "admin": - return JsonResponse({"message":"Loged in successfully", "method":"post"},status = 200) - return JsonResponse({"message":"Invalid credentials", "method":"post"},status = 401) + return JsonResponse({"message": "Loged in successfully", "method": "post"}, status=200) + return JsonResponse({"message": "Invalid credentials", "method": "post"}, status=401) if request.method == "PUT": L.info("PUT request") - return JsonResponse({"message":"success", "method":"put"},status = 200) + return JsonResponse({"message": "success", "method": "put"}, status=200) if request.method == "DELETE": if request.user.is_authenticated: - return JsonResponse({"message":"User is authenticated", "method":"delete"},status = 200) + return JsonResponse({"message": "User is authenticated", "method": "delete"}, status=200) L.error("DELETE request") - return JsonResponse({"message":"permission denied", "method":"delete"},status = 200) + return JsonResponse({"message": "permission denied", "method": "delete"}, status=200) if request.method == "PATCH": L.info("PATCH request") - return JsonResponse({"message":"success", "method":"patch"},status = 200) + 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) - - -# ====================================== - -import datetime - - -# f = open('test.log', 'a') --> use this file to log -class Log: - def __init__(self,request): - self.request = request - - def info(self,msg): - now = datetime.datetime.now() - f = open('test.log', 'a') - f.write(f"INFO:{now}:{msg}\n") - f.close() - - def warning(self,msg): - now = datetime.datetime.now() - f = open('test.log', 'a') - f.write(f"WARNING:{now}:{msg}\n") - f.close() - - def error(self,msg): - now = datetime.datetime.now() - f = open('test.log', 'a') - f.write(f"ERROR:{now}:{msg}\n") - f.close() + return JsonResponse({"message": "success", "method": "update"}, status=200) + return JsonResponse({"message": "method not allowed"}, status=403) diff --git a/introduction/static/js/a9.js b/introduction/static/js/a9.js index 9c58b8a..c6bc051 100644 --- a/introduction/static/js/a9.js +++ b/introduction/static/js/a9.js @@ -35,11 +35,14 @@ event3 = function(){ let data = JSON.parse(result); // parse JSON string into object console.log(data.logs); document.getElementById("a9_d3").style.display = 'flex'; - for (var i = 0; i < data.logs.length; i++) { + var ul = document.getElementById("a9_d3"); + ul.innerHTML = ''; // Clear existing list items + data.logs.forEach(log => { var li = document.createElement("li"); - li.innerHTML = data.logs[i]; + var text = document.createTextNode(log); + li.appendChild(text); 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..7145c34 100644 --- a/introduction/templates/Lab/A9/a9_lab.html +++ b/introduction/templates/Lab/A9/a9_lab.html @@ -8,6 +8,7 @@
{{otp}}{{email}}{{code}}
+{{code}}