Skip to content

Commit

Permalink
New feature initiated. Challenge Section unlocked
Browse files Browse the repository at this point in the history
- All the section should be dockerized.
- No need to create individual endpoint for challenge
- Now its easy to add challenge just by adding in the db
- DB is not pushed in database, so the challenges are stored in a json
file at `pygoat/challenge/challenge.json`
- For populating challenges to db, use the command `python3 manage.py
populate_challenge`
  • Loading branch information
RupakBiswas-2304 committed Mar 3, 2023
1 parent 624aa04 commit eb60468
Show file tree
Hide file tree
Showing 22 changed files with 1,191 additions and 7 deletions.
780 changes: 780 additions & 0 deletions app.log

Large diffs are not rendered by default.

File renamed without changes.
16 changes: 16 additions & 0 deletions challenge/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.contrib import admin
from .models import Challenge, UserChallenge
# Register your models here.

@admin.register(Challenge)
class ChallengeAdmin(admin.ModelAdmin):
list_display = ('name', 'docker_image', 'start_port', 'end_port', 'point')
search_fields = ('name', 'docker_image')
empty_value_display = '-empty-'


@admin.register(UserChallenge)
class UserChallengeAdmin(admin.ModelAdmin):
list_display = ('user', 'challenge', 'container_id', 'port', 'no_of_attempt', 'is_solved')
search_fields = ('user__username', 'challenge__name')
empty_value_display = '-empty-'
6 changes: 6 additions & 0 deletions challenge/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class ChallengeConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'challenge'
12 changes: 12 additions & 0 deletions challenge/challenge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"name":"do-it-fast",
"description":"You want flag ?\nThis webside gives you ?\nyeah, just need to login and grab fast",
"docker_image": "rupak2001/do-it-fast",
"docker_port": 5050,
"start_port" : 5000,
"end_port" : 5500,
"flag": "flag{do_it_fast}",
"point" : 100
}
]
22 changes: 22 additions & 0 deletions challenge/management/commands/populate_challenge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import json
from django.core.management.base import BaseCommand, CommandError
from challenge.models import Challenge


class Command(BaseCommand):
help = "Populates the database with some test data"

def handle(self, *args, **options):
try:
filename = "challenge/challenge.json"
with open(filename, "r") as f:
data = json.load(f)
for challenge in data:
try:
Challenge.objects.create(**challenge).save()
except:
pass
except FileNotFoundError:
raise CommandError("File not found: {}".format(filename))


47 changes: 47 additions & 0 deletions challenge/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Generated by Django 4.0.4 on 2023-03-03 06:33

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Challenge',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100)),
('description', models.TextField()),
('docker_image', models.CharField(max_length=100)),
('start_port', models.IntegerField()),
('end_port', models.IntegerField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('flag', models.CharField(max_length=100)),
('point', models.IntegerField()),
],
),
migrations.CreateModel(
name='UserChallenge',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('container_id', models.CharField(max_length=100)),
('port', models.IntegerField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('is_live', models.BooleanField(default=False)),
('no_of_attempt', models.IntegerField(default=0)),
('is_solved', models.BooleanField(default=False)),
('challenge', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='challenge.challenge')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
19 changes: 19 additions & 0 deletions challenge/migrations/0002_challenge_docker_port.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.0.4 on 2023-03-03 06:45

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('challenge', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='challenge',
name='docker_port',
field=models.IntegerField(default=8000),
preserve_default=False,
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 4.0.4 on 2023-03-03 12:11

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('challenge', '0002_challenge_docker_port'),
]

operations = [
migrations.AlterField(
model_name='challenge',
name='docker_image',
field=models.CharField(max_length=100, unique=True),
),
migrations.AlterField(
model_name='challenge',
name='id',
field=models.AutoField(primary_key=True, serialize=False, unique=True),
),
migrations.AlterField(
model_name='challenge',
name='name',
field=models.CharField(max_length=100, unique=True),
),
]
Empty file.
46 changes: 46 additions & 0 deletions challenge/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from django.db import models
from django.contrib.auth.models import User

# Create your models here
class Challenge(models.Model):
id = models.AutoField(primary_key=True, unique=True)
name = models.CharField(max_length=100, unique=True)
description = models.TextField()
docker_image = models.CharField(max_length=100, unique=True)
docker_port = models.IntegerField()
start_port = models.IntegerField()
end_port = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
flag = models.CharField(max_length=100)
point = models.IntegerField()

def __str__(self):
return self.name

# overwriting default save method
def save(self, *args, **kwargs):
if self.start_port > self.end_port:
raise Exception("Start port should be less than end port")
# Here flag need to be hashed
super(Challenge, self).save(*args, **kwargs)

class UserChallenge(models.Model):
"""
This is a mapping of user to challenge with proper progress tracking
This also allows us to reuse the created container for the user
"""
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
challenge = models.ForeignKey(Challenge, on_delete=models.CASCADE)
container_id = models.CharField(max_length=100)
port = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_live = models.BooleanField(default=False)
no_of_attempt = models.IntegerField(default=0)
is_solved = models.BooleanField(default=False)
port = models.IntegerField()

def __str__(self):
return f"{self.user.username} - {self.challenge.name}"
8 changes: 8 additions & 0 deletions challenge/templates/chal-not-found.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends "introduction/base.html" %}
{% block content %}
<div class="content">

<h1>Challenge not found</h1>

</div>
{% endblock content %}
81 changes: 81 additions & 0 deletions challenge/templates/challenge.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{% extends "introduction/base.html" %}
{% block content %}
<div class="content">

<div class="title">
<h2>{{ chal.name }}</h2>
</div>
<div class="box" pre>
<pre>{{ chal.description }}</pre>
</div>
<br>
<button type="submit" class="btn btn-primary" id="cbtn-1" onclick="handleonclick()">Start Challenge</button>
<button type="submit" class="btn btn-primary" id="cbtn-2" onclick="handleRedirect()" style="display:none">Go to
Challenge </button>

</div>
<script>
var button1 = document.getElementById("cbtn-1");
var button2 = document.getElementById("cbtn-2");
var url = null;
const csrf_token = "{{ csrf_token|safe }}";

function handleonclick() {
var requestOptions = {
method: 'POST',
redirect: 'follow',
headers: {'X-CSRFToken': csrf_token},
};

fetch("/challenge/{{chal.name}}", requestOptions)
.then(response => {
console.log(response);
if (response.status === 200) {
return response.json();
} else {
throw new Error('Something went wrong on api server!');
}
})
.then(result => {
console.log(result);
button2.style.display = "revert";
button1.textContent = "Stop Challenge";
button1.onclick = handleStopChallenge;
url = result.endpoint;
})
.catch(error => console.log('error', error));
}

function handleStopChallenge() {
var requestOptions = {
method: 'DELETE',
redirect: 'follow',
headers: {'X-CSRFToken': csrf_token},
};
fetch("/challenge/{{chal.name}}", requestOptions)
.then(response => {
console.log(response);
if (response.status === 200) {
return response.json();
} else {
throw new Error('Something went wrong on api server!');
}
})
.then(result => {
console.log('Container stoped')
button1.textContent = "Start Challenge";
button1.onclick = handleonclick;
button2.style.display = "none";
})
.catch(error => console.log('error', error));
}

function handleRedirect() {
window.location.href = url;
}
</script>
</body>

</html>

{% endblock content %}
3 changes: 3 additions & 0 deletions challenge/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
6 changes: 6 additions & 0 deletions challenge/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.urls import path,include
from .views import *

urlpatterns = [
path('<str:challenge>', DoItFast.as_view(), name='do-it-fast'),
]
10 changes: 10 additions & 0 deletions challenge/utility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import socket

def get_free_port(START_PORT, END_PORT, HOST="localhost"):
for port in range(START_PORT, END_PORT):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
result = s.connect_ex((HOST, port))
if result == 111:
print(f"Port {port} is avilable")
return port
return None
Loading

0 comments on commit eb60468

Please sign in to comment.