Commit a21a98d3 authored by Dominik Rosiek's avatar Dominik Rosiek

Merge branch 'master' of gitlab.telemabk.pl:jifwin/CTF

parents 99821629 70a1e723
......@@ -85,8 +85,10 @@ textTasks:
flags:
- value: "KYN2016_KominiarkaAtrybutemHakera"
description: "Umiesz się bawić"
points: 1
- value: "AGH_sdfg1f1"
description: "flaga 1"
points: 2
- name: "Inne"
text: "Wszystko inne"
......@@ -95,6 +97,7 @@ textTasks:
flags:
- value: "KYN2016_UmiemCzytacRegulaminy"
description: "Wiem w co gram"
points: 3
- name: "UI"
text: "Znajdz flagi w jułaju"
......@@ -103,16 +106,22 @@ textTasks:
flags:
- value: "KYN2016_TervetullutKotisivuilleni"
description: "Tytuł"
points: 4
- value: "KYN2016_ToNieprawda"
description: "Poezja"
points: 5
- value: "KYN2016_EmacsemPrzezSendmaila"
description: "Konsola"
points: 6
- value: "KYN2016_SzukajDalejAZnajdzieszWiecej"
description: "QR"
points: 7
- value: "KYN2016_BROWAR"
description: "Rebus"
points: 8
- value: "KYN2016_RUAHACKER?"
description: "Tunel"
points: 9
webTasks:
......@@ -123,8 +132,10 @@ webTasks:
flags:
- value: "KYN2016_WartoSieGlowic"
description: "Dodanie nagłówka"
points: 5
- value: "KYN2016_PatriInterentu"
description: "Remote File Include"
points: 3
- name: "Zadanie testowe nr 7"
description: "Opis zadania testowego. Usunąć w wersji produkcyjnej."
......@@ -133,6 +144,7 @@ webTasks:
flags:
- value: "KYN2016_OpenYourEyes"
description: "Kod źródłowy"
points: 2
- name: "Wiadomość od Alicji"
description: "Alicja przesłała Ci wiadomość. Niestety część wiadomości jest nieczytelna, została ona zastąpiona znakami '@'. Na szczęście, za pomocą innego kanału komunikacji Alicja przesłała Ci hash md5 wiadomości, który wynosi: 047998436f086d08317249e14c5bd8c0"
......@@ -141,6 +153,7 @@ webTasks:
flags:
- value: "KYN2016_87365101329846397649561156187035341"
description: "MD5"
points: 7
- name: "Kryptografia"
description: "Z kryptografią sptykamy się codziennie. Ale czy na wiemy o niej wystarczająco wiele?. Kilka zadań z kryptografii dla rozruszania szarych komórek..."
......@@ -149,12 +162,16 @@ webTasks:
flags:
- value: "KYN2016_DoubleEncoded"
description: "Kombinacja kodów"
points: 2
- value: "KYN2016_AveJaAveCezar"
description: "Cezar"
points: 3
- value: "KYN2016_SlowoOdOrganizatora"
description: "Wiadomość od organizatorów"
points: 9
- value: "KYN2016_OupsChybaCosTuJest"
description: "Komentarz w archiwum"
points: 1
- name: "Znowu kotki, pieski, krówki..."
description: "Strona została w końcu zbudowana i wystawiona do sieci, ale czy na pewno jest bezpieczna?"
......@@ -163,5 +180,16 @@ webTasks:
flags:
- value: "KYN2016_UzywamFantazyji"
description: "Niezwykły tytuł"
points: 2
- value: "KYN2016_LaoreetAliquet"
description: "SQLInjection"
points: 3
- name: "Debugging task to remove"
description: "To nie jest zadanie"
url: "http://localhost:8082/"
level: 9
flags:
- value: "flaga 1"
description: "flaga 1 desc"
points: 100
......@@ -85,8 +85,10 @@ textTasks:
flags:
- value: "KYN2016_KominiarkaAtrybutemHakera"
description: "Umiesz się bawić"
points: 1
- value: "AGH_sdfg1f1"
description: "flaga 1"
points: 2
- name: "Inne"
text: "Wszystko inne"
......@@ -95,6 +97,7 @@ textTasks:
flags:
- value: "KYN2016_UmiemCzytacRegulaminy"
description: "Wiem w co gram"
points: 3
- name: "UI"
text: "Znajdz flagi w jułaju"
......@@ -103,16 +106,22 @@ textTasks:
flags:
- value: "KYN2016_TervetullutKotisivuilleni"
description: "Tytuł"
points: 4
- value: "KYN2016_ToNieprawda"
description: "Poezja"
points: 5
- value: "KYN2016_EmacsemPrzezSendmaila"
description: "Konsola"
points: 6
- value: "KYN2016_SzukajDalejAZnajdzieszWiecej"
description: "QR"
points: 7
- value: "KYN2016_BROWAR"
description: "Rebus"
points: 8
- value: "KYN2016_RUAHACKER?"
description: "Tunel"
points: 9
webTasks:
......@@ -123,8 +132,10 @@ webTasks:
flags:
- value: "KYN2016_WartoSieGlowic"
description: "Dodanie nagłówka"
points: 5
- value: "KYN2016_PatriInterentu"
description: "Remote File Include"
points: 3
- name: "Zadanie testowe nr 7"
description: "Opis zadania testowego. Usunąć w wersji produkcyjnej."
......@@ -133,6 +144,7 @@ webTasks:
flags:
- value: "KYN2016_OpenYourEyes"
description: "Kod źródłowy"
points: 2
- name: "Wiadomość od Alicji"
description: "Alicja przesłała Ci wiadomość. Niestety część wiadomości jest nieczytelna, została ona zastąpiona znakami '@'. Na szczęście, za pomocą innego kanału komunikacji Alicja przesłała Ci hash md5 wiadomości, który wynosi: 047998436f086d08317249e14c5bd8c0"
......@@ -141,6 +153,7 @@ webTasks:
flags:
- value: "KYN2016_87365101329846397649561156187035341"
description: "MD5"
points: 7
- name: "Kryptografia"
description: "Z kryptografią sptykamy się codziennie. Ale czy na wiemy o niej wystarczająco wiele?. Kilka zadań z kryptografii dla rozruszania szarych komórek..."
......@@ -149,12 +162,16 @@ webTasks:
flags:
- value: "KYN2016_DoubleEncoded"
description: "Kombinacja kodów"
points: 2
- value: "KYN2016_AveJaAveCezar"
description: "Cezar"
points: 3
- value: "KYN2016_SlowoOdOrganizatora"
description: "Wiadomość od organizatorów"
points: 9
- value: "KYN2016_OupsChybaCosTuJest"
description: "Komentarz w archiwum"
points: 1
- name: "Znowu kotki, pieski, krówki..."
description: "Strona została w końcu zbudowana i wystawiona do sieci, ale czy na pewno jest bezpieczna?"
......@@ -163,5 +180,7 @@ webTasks:
flags:
- value: "KYN2016_UzywamFantazyji"
description: "Niezwykły tytuł"
points: 2
- value: "KYN2016_LaoreetAliquet"
description: "SQLInjection"
points: 3
\ No newline at end of file
package com.telephoners.krakyournet.ctf.beans;
import com.fasterxml.jackson.annotation.JsonIgnore;
public class Flag {
private String value;
private String description;
private int points;
public Flag() {
}
public Flag(String value, String description) {
public Flag(String value, String description, int points) {
this.value = value;
this.description = description;
this.points = points;
}
@JsonIgnore
public String getValue() {
return value;
}
@JsonIgnore
public void setValue(String value) {
this.value = value;
}
......@@ -29,4 +35,14 @@ public class Flag {
{
this.description = description;
}
public int getPoints()
{
return points;
}
public void setPoints(int points)
{
this.points = points;
}
}
package com.telephoners.krakyournet.ctf.beans.tasks;
import com.telephoners.krakyournet.ctf.beans.User;
import org.eclipse.jetty.http.HttpMethod;
import java.util.Map;
public class TaskRequestContext
{
private String httpMethod; //todo: use class
private HttpMethod httpMethod;
private User user;
private String path;
private String body;
private Map<String, String> headers;
public TaskRequestContext withHttpMethod(String httpMethod)
public TaskRequestContext withHttpMethod(HttpMethod httpMethod)
{
this.httpMethod = httpMethod;
return this;
......@@ -40,7 +41,7 @@ public class TaskRequestContext
return this;
}
public String getHttpMethod()
public HttpMethod getHttpMethod()
{
return httpMethod;
}
......
package com.telephoners.krakyournet.ctf.beans.tasks;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import com.telephoners.krakyournet.ctf.helpers.StreamUtils;
import org.eclipse.jetty.http.HttpMethod;
import org.glassfish.jersey.server.ContainerRequest;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.MultivaluedMap;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Singleton
public class TaskRequestContextBuilder
{
private ApplicationConfiguration applicationConfiguration;
@Inject
public TaskRequestContextBuilder(ApplicationConfiguration applicationConfiguration)
{
this.applicationConfiguration = applicationConfiguration;
}
private Map<String, String> extractProxiedHeaders(MultivaluedMap<String, String> headers)
{
Set<String> proxiedHeaders = applicationConfiguration.getProxiedHeaders();
return headers.entrySet()
.stream()
.filter(header -> proxiedHeaders.contains(header.getKey()))
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().get(0) //todo: check
));
}
public TaskRequestContext from(User user, String path, ContainerRequestContext containerRequestContext) throws IOException
{
String fullPath = path;
TaskRequestContext taskRequestContext = new TaskRequestContext();
if (user != null) {
taskRequestContext.withUser(user);
}
if (path != null) {
taskRequestContext.withPath(path);
String httpMethod = containerRequestContext.getMethod();
String query = ((ContainerRequest) containerRequestContext).getRequestUri().getQuery();
if (query != null) {
fullPath += "?" + query;
}
taskRequestContext.withPath(fullPath);
if(httpMethod.equals("GET")) {
taskRequestContext.withHttpMethod(HttpMethod.GET);
}
else if (httpMethod.equals("POST")) {
taskRequestContext.withHttpMethod(HttpMethod.POST);
String body = StreamUtils.readStream(containerRequestContext.getEntityStream());
taskRequestContext.withBody(body);
}
else {
throw new IllegalStateException("Invalid http method");
}
Map<String, String> proxiedHeaders = extractProxiedHeaders(containerRequestContext.getHeaders());
taskRequestContext.withHeaders(proxiedHeaders);
}
return taskRequestContext;
}
}
......@@ -2,23 +2,29 @@ package com.telephoners.krakyournet.ctf.beans.tasks;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.telephoners.krakyournet.ctf.beans.Flag;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.helpers.StreamUtils;
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.mongodb.morphia.annotations.Entity;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Entity("tasks")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class WebTask extends Task
{
private String url;
private static final String KYN_HEADER_NAME = "KYN_2016"; //todo
public WebTask(String name, int level, String description, List<Flag> flags, String url)
{
......@@ -30,30 +36,35 @@ public class WebTask extends Task
{
}
@Override
public TaskResponse getTaskResponse(TaskRequestContext taskRequestContext) throws IOException
//todo: avoid converting in both ways
private Map<String, String> buildHeadersMap(Header[] headers)
{
String url = getUrl() + taskRequestContext.getPath();
//todo: header in
CloseableHttpResponse response = proxyRequest(url, taskRequestContext.getUser(), null);
String text = StreamUtils.readStream(response.getEntity().getContent());
//todo: header out
TaskResponse taskResponse = new TaskResponse(text, null);//todo: build with
return taskResponse;
return Stream.of(headers)
.collect(Collectors.toMap(
Header::getName,
Header::getValue
));
}
private Header[] buildHeadersArray(Map<String, String> headers)
{
List<BasicHeader> headersList = headers.entrySet().stream() //todo: dirty casting
.map(headerEntry -> new BasicHeader(headerEntry.getKey(), headerEntry.getValue()))
.collect(Collectors.toList());
Header[] headersArray = new Header[headersList.size()];
return headersList.toArray(headersArray);
}
/*
public TaskResponse getTaskResponse(User user, String path, ContainerRequestContext context) throws IOException
@Override
public TaskResponse getTaskResponse(TaskRequestContext taskRequestContext) throws IOException
{
String kynHeaderValue = context.getHeaderString(KYN_HEADER_NAME);
CloseableHttpResponse response = proxyRequest(url, user, kynHeaderValue);
String proxiedUrl = getTaskUrl() + taskRequestContext.getPath();
CloseableHttpResponse response = proxyRequest(proxiedUrl, taskRequestContext);
String text = StreamUtils.readStream(response.getEntity().getContent());
Header kynHeader = response.getFirstHeader(KYN_HEADER_NAME);
return new TaskResponse(text, kynHeader != null ? kynHeader.getValue() : null); //todo: needs refactorig
return new TaskResponse(text, buildHeadersMap(response.getAllHeaders()));
}
*/
public String getUrl()
public String getTaskUrl()
{
return url;
}
......@@ -63,12 +74,23 @@ public class WebTask extends Task
this.url = url;
}
private CloseableHttpResponse proxyRequest(String url, User user, String kynHeaderValue) throws IOException
private CloseableHttpResponse proxyRequest(String url, TaskRequestContext taskRequestContext) throws IOException
{
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpget = new HttpGet(url);
httpget.setHeader("CTF-User", user.getName()); //todo: is it necessary?
httpget.setHeader("KYN_2016", kynHeaderValue); //todo: move to configuration
return httpClient.execute(httpget);
HttpMethod httpMethod = taskRequestContext.getHttpMethod();
if (httpMethod.equals(HttpMethod.GET)) {
HttpGet httpget = new HttpGet(url);
httpget.setHeaders(buildHeadersArray(taskRequestContext.getHeaders()));
return httpClient.execute(httpget);
} else if (httpMethod.equals(HttpMethod.POST)) {
HttpPost httpPost = new HttpPost(url);
httpPost.setHeaders(buildHeadersArray(taskRequestContext.getHeaders()));
httpPost.setEntity(new ByteArrayEntity(taskRequestContext.getBody().getBytes("UTF-8")));
return httpClient.execute(httpPost);
} else {
throw new IllegalStateException("Invalid http method");
}
}
}
package com.telephoners.krakyournet.ctf.repositories;
import com.telephoners.krakyournet.ctf.beans.Flag;
import com.telephoners.krakyournet.ctf.beans.Solution;
import com.telephoners.krakyournet.ctf.beans.Team;
import com.telephoners.krakyournet.ctf.beans.tasks.Task;
......@@ -26,7 +27,7 @@ public class SolutionsRepository extends Repository<Solution>
this.tasksRepository = tasksRepository;
}
public Map<Integer, List<String>> getTeamSolutions(Team team)
public Map<Integer, List<Flag>> getTeamSolutions(Team team)
{
//todo: do not identify team by name? conflict in team ids?
return datastore.createQuery(Solution.class)
......@@ -45,7 +46,7 @@ public class SolutionsRepository extends Repository<Solution>
.collect(Collectors.toMap(
taskSolutions -> taskSolutions.getKey().getLevel(),
taskSolutions -> taskSolutions.getValue().stream()
.map(solution -> solution.getFlag().getDescription()).collect(Collectors.toList())
.map(Solution::getFlag).collect(Collectors.toList())
));
}
......@@ -59,10 +60,10 @@ public class SolutionsRepository extends Repository<Solution>
public List<Integer> getCompletedTasks(Team team)
{
Map<Integer, List<String>> teamSolutions = getTeamSolutions(team);
Map<Integer, List<Flag>> teamSolutions = getTeamSolutions(team);
return tasksRepository.getAll().stream()
.filter(task -> {
List<String> teamTaskSolutions = teamSolutions.get(task.getLevel());
List<Flag> teamTaskSolutions = teamSolutions.get(task.getLevel());
return teamTaskSolutions != null && teamTaskSolutions.size() == task.getFlags().size();
})
.map(Task::getLevel)
......
......@@ -76,7 +76,7 @@ public class SolutionsResource
@GET
@Path("/all")
public Map<String, Map<Integer, List<String>>> getTeamsSolutions()
public Map<String, Map<Integer, List<Flag>>> getTeamsSolutions()
{
return teamsRepository.getAll()
.stream()
......
......@@ -3,12 +3,10 @@ package com.telephoners.krakyournet.ctf.resources;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.beans.tasks.Task;
import com.telephoners.krakyournet.ctf.beans.tasks.TaskRequestContext;
import com.telephoners.krakyournet.ctf.beans.tasks.TaskRequestContextBuilder;
import com.telephoners.krakyournet.ctf.beans.tasks.TaskResponse;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import com.telephoners.krakyournet.ctf.helpers.StreamUtils;
import com.telephoners.krakyournet.ctf.repositories.TasksRepository;
import io.dropwizard.auth.Auth;
import org.glassfish.jersey.server.ContainerRequest;
import javax.inject.Inject;
import javax.inject.Singleton;
......@@ -18,109 +16,64 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Singleton
@Path(value = "/task")
public class TaskResource
{
private final ApplicationConfiguration applicationConfiguration;
private final TasksRepository tasksRepository;
private final TaskRequestContextBuilder taskRequestContextBuilder;
@Inject
public TaskResource(ApplicationConfiguration applicationConfiguration, TasksRepository tasksRepository)
public TaskResource(TasksRepository tasksRepository, TaskRequestContextBuilder taskRequestContextBuilder)
{
this.applicationConfiguration = applicationConfiguration;
this.tasksRepository = tasksRepository;
this.taskRequestContextBuilder = taskRequestContextBuilder;
}
//todo: name
private TaskResponse getTaskResponse(int taskLevel, TaskRequestContext taskRequestContext) throws IOException
private Response buildResponse(TaskResponse taskResponse)
{
Task task = tasksRepository.getByLevel(taskLevel);
return task.getTaskResponse(taskRequestContext);
Response.ResponseBuilder responseBuilder = Response.ok();
responseBuilder.entity(taskResponse.getText());
taskResponse.getHeaders()
.entrySet()
.stream()
.forEach(headerEntry -> {
responseBuilder.header(headerEntry.getKey(), headerEntry.getValue());
});
return responseBuilder.build();
}
private Map<String, String> extractProxiedHeaders(MultivaluedMap<String, String> headers) {
Set<String> proxiedHeaders = applicationConfiguration.getProxiedHeaders();
return headers.entrySet()
.stream()
.filter(header -> proxiedHeaders.contains(header.getKey()))
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().get(0) //todo: check
));
private Response handleRequest(User user,
int taskLevel,
String path,
ContainerRequestContext containerRequestContext) throws IOException
{
Task task = tasksRepository.getByLevel(taskLevel);
TaskRequestContext taskRequestContext = taskRequestContextBuilder.from(user, path, containerRequestContext);
return buildResponse(task.getTaskResponse(taskRequestContext));
}
@Path("{task_level}/{path: .*}")
@GET
public Response getTaskGet(@Auth User user,
final @PathParam("task_level") int taskLevel,
final @PathParam("path") String path,
@Context ContainerRequestContext containerRequestContext) throws IOException
final @PathParam("task_level") int taskLevel,
final @PathParam("path") String path,
@Context ContainerRequestContext containerRequestContext) throws IOException
{
String fullPath = path;
String query = ((ContainerRequest)containerRequestContext).getRequestUri().getQuery();
if(query != null) {
fullPath += query;
}
Map<String, String> headers = extractProxiedHeaders(containerRequestContext.getHeaders());//todo: inline
TaskRequestContext taskRequestContext = new TaskRequestContext()
.withHttpMethod("POST")
.withUser(user)
.withPath(fullPath)
.withHeaders(headers);
TaskResponse taskResponse = getTaskResponse(taskLevel, taskRequestContext);
Response.ResponseBuilder responseBuilder = Response.ok()
.entity(taskResponse.getText());
taskResponse.getHeaders().entrySet()
.stream()
.forEach(headerEntry -> responseBuilder.header(headerEntry.getKey(), headerEntry.getValue()));
return responseBuilder.build();
return handleRequest(user, taskLevel, path, containerRequestContext);
}
@Path("{task_level}/{path: .*}")
@POST
public Response getTaskPost(@Auth User user,
final @PathParam("task_level") int taskLevel,
final @PathParam("path") String path,
@Context ContainerRequestContext containerRequestContext) throws IOException
final @PathParam("task_level") int taskLevel,
final @PathParam("path") String path,
@Context ContainerRequestContext containerRequestContext) throws IOException
{
String fullPath = path;
String query = ((ContainerRequest)containerRequestContext).getRequestUri().getQuery();
if(query != null) {
fullPath += query;
}
String body = StreamUtils.readStream(containerRequestContext.getEntityStream());
//todo: TaskContextFrom
Map<String, String> headers = extractProxiedHeaders(containerRequestContext.getHeaders());//todo: inline
TaskRequestContext taskRequestContext = new TaskRequestContext()
.withHttpMethod("POST")
.withUser(user)
.withPath(fullPath)
.withBody(body)
.withHeaders(headers);
TaskResponse taskResponse = getTaskResponse(taskLevel, taskRequestContext);
Response.ResponseBuilder responseBuilder = Response.ok()
.entity(taskResponse.getText());
taskResponse.getHeaders().entrySet()
.stream()
.forEach(headerEntry -> responseBuilder.header(headerEntry.getKey(), headerEntry.getValue()));
return responseBuilder.build();
return handleRequest(user, taskLevel, path, containerRequestContext);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment