From ad26e7cd3a32acf02d0011b5da961e5a3ebfeb34 Mon Sep 17 00:00:00 2001 From: Nima HeydariNasab Date: Fri, 24 Mar 2023 01:45:30 +0330 Subject: [PATCH 1/9] Get ExposedPorts from Config if it does not exist in ContainerConfig --- docker_challenges/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docker_challenges/__init__.py b/docker_challenges/__init__.py index 190ef35..91a7f1d 100644 --- a/docker_challenges/__init__.py +++ b/docker_challenges/__init__.py @@ -282,8 +282,11 @@ def get_unavailable_ports(docker): def get_required_ports(docker, image): r = do_request(docker, f'/images/{image}/json?all=1') - result = r.json()['ContainerConfig']['ExposedPorts'].keys() - return result + result = r.json() + ports = (result['ContainerConfig']['ExposedPorts']\ + if 'ExposedPorts' in result['ContainerConfig']\ + else result['Config']['ExposedPorts']).keys() + return ports def create_container(docker, image, team, portbl): From 993227f0b8d7245e6c4d6139bf16464210c9f550 Mon Sep 17 00:00:00 2001 From: Nima HeydariNasab Date: Fri, 24 Mar 2023 23:16:59 +0330 Subject: [PATCH 2/9] Add feature to specify which ports to publish per challenge image --- docker_challenges/__init__.py | 27 ++++++++++++++++++++++++++- docker_challenges/assets/create.html | 6 ++++++ docker_challenges/assets/create.js | 5 +++++ docker_challenges/assets/update.html | 7 +++++++ docker_challenges/assets/update.js | 6 ++++++ 5 files changed, 50 insertions(+), 1 deletion(-) diff --git a/docker_challenges/__init__.py b/docker_challenges/__init__.py index 91a7f1d..ccf209c 100644 --- a/docker_challenges/__init__.py +++ b/docker_challenges/__init__.py @@ -315,7 +315,8 @@ def create_container(docker, image, team, portbl): return [] host = docker.hostname URL_TEMPLATE = '%s://%s' % (prefix, host) - needed_ports = get_required_ports(docker, image) + challenge = DockerChallenge.query.filter_by(docker_image=image).first() + needed_ports = challenge.ports.split(',') if challenge.ports else get_required_ports(docker, image) team = hashlib.md5(team.encode("utf-8")).hexdigest()[:10] container_name = "%s_%s" % (image.split(':')[1], team) assigned_ports = dict() @@ -425,6 +426,7 @@ def read(challenge): 'name': challenge.name, 'value': challenge.value, 'docker_image': challenge.docker_image, + 'ports': challenge.ports, 'description': challenge.description, 'category': challenge.category, 'state': challenge.state, @@ -539,6 +541,7 @@ class DockerChallenge(Challenges): __mapper_args__ = {'polymorphic_identity': 'docker'} id = db.Column(None, db.ForeignKey('challenges.id'), primary_key=True) docker_image = db.Column(db.String(128), index=True) + ports = db.Column(db.String(128), nullable=True) # API @@ -675,6 +678,27 @@ def get(self): }, 400 +docker_ports_namespace = Namespace("docker", description='Endpoint to retrieve docker image exposed ports') + + +@docker_ports_namespace.route("", methods=['POST', 'GET']) +class DockerPortsAPI(Resource): + """ + This is for creating Docker Challenges. The purpose of this API is to populate the Docker Image exposed ports Select form + object in the Challenge Creation Screen. + """ + + @admins_only + def get(self): + image = request.args.get('image') + docker = DockerConfig.query.filter_by(id=1).first() + exposed_ports = list(get_required_ports(docker, image)) + return { + 'success': True, + 'data': exposed_ports + } + + def load(app): app.db.create_all() CHALLENGE_CLASSES['docker'] = DockerChallengeType @@ -682,6 +706,7 @@ def load(app): define_docker_admin(app) define_docker_status(app) CTFd_API_v1.add_namespace(docker_namespace, '/docker') + CTFd_API_v1.add_namespace(docker_ports_namespace, '/docker_ports') CTFd_API_v1.add_namespace(container_namespace, '/container') CTFd_API_v1.add_namespace(active_docker_namespace, '/docker_status') CTFd_API_v1.add_namespace(kill_container, '/nuke') diff --git a/docker_challenges/assets/create.html b/docker_challenges/assets/create.html index 6106b5e..dd9d493 100644 --- a/docker_challenges/assets/create.html +++ b/docker_challenges/assets/create.html @@ -15,6 +15,12 @@ +
+ + +
{% endblock %} {% block type %} diff --git a/docker_challenges/assets/create.js b/docker_challenges/assets/create.js index 51ae118..a318f4f 100644 --- a/docker_challenges/assets/create.js +++ b/docker_challenges/assets/create.js @@ -22,5 +22,10 @@ CTFd.plugin.run((_CTFd) => { } }); }); + $("#docker_image").on("change", function() { + $.getJSON("/api/v1/docker_ports?image=" + this.value, function(result) { + $("#ports_text").text("Published Ports, Separated With Comma (Allowed Values: " + result["data"].join(", ") + "):"); + }); + }); }); }); \ No newline at end of file diff --git a/docker_challenges/assets/update.html b/docker_challenges/assets/update.html index 878ac05..444a697 100644 --- a/docker_challenges/assets/update.html +++ b/docker_challenges/assets/update.html @@ -13,9 +13,16 @@ +
+ + +
{% endblock %} {% block footer %} {% endblock %} \ No newline at end of file diff --git a/docker_challenges/assets/update.js b/docker_challenges/assets/update.js index 524afb4..ac9a86a 100644 --- a/docker_challenges/assets/update.js +++ b/docker_challenges/assets/update.js @@ -7,6 +7,12 @@ CTFd.plugin.run((_CTFd) => { $("#dockerimage_select").append($("