From a60c529229c7ce6a86e6e76b1e7dcd477ac76c1a Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 9 Apr 2017 02:30:13 +0200 Subject: [PATCH] Separating challenges --- .../owasp/webgoat/plugins/PluginResource.java | 5 +- .../owasp/webgoat/plugins/PluginsLoader.java | 19 +-- .../Assignment1.java} | 5 +- .../Challenge1.java} | 8 +- .../Assignment2.java} | 5 +- .../webgoat/plugin/challenge2/Challenge2.java | 39 ++++++ .../plugin/{ => challenge2}/ShopEndpoint.java | 2 +- .../plugin/challenge3/Assignment3.java | 109 +++++++++++++++++ .../webgoat/plugin/challenge3/Challenge3.java | 112 ++++-------------- .../src/main/resources/html/Challenge1.html | 59 +++++++++ .../src/main/resources/html/Challenge2.html | 111 +++++++++++++++++ .../src/main/resources/html/Challenge3.html | 57 +++++++++ .../src/main/resources/html/Challenge4.html | 111 +++++++++++++++++ .../resources/i18n/WebGoatLabels.properties | 3 + 14 files changed, 535 insertions(+), 110 deletions(-) rename webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/{Challenge1.java => challenge1/Assignment1.java} (94%) rename webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/{Challenge.java => challenge1/Challenge1.java} (79%) rename webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/{Challenge2.java => challenge2/Assignment2.java} (87%) create mode 100644 webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge2/Challenge2.java rename webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/{ => challenge2}/ShopEndpoint.java (97%) create mode 100644 webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge3/Assignment3.java create mode 100644 webgoat-lessons/challenge/src/main/resources/html/Challenge1.html create mode 100644 webgoat-lessons/challenge/src/main/resources/html/Challenge2.html create mode 100644 webgoat-lessons/challenge/src/main/resources/html/Challenge3.html create mode 100644 webgoat-lessons/challenge/src/main/resources/html/Challenge4.html diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginResource.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginResource.java index c84f07da2..73e3ee03b 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginResource.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginResource.java @@ -8,7 +8,6 @@ import org.owasp.webgoat.lessons.NewLesson; import java.net.URL; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; /** @@ -24,8 +23,8 @@ public class PluginResource { private final URL location; private final List classes; - public Optional getLesson() { - return classes.stream().filter(c -> c.getSuperclass() == NewLesson.class).findFirst(); + public List getLessons() { + return classes.stream().filter(c -> c.getSuperclass() == NewLesson.class).collect(Collectors.toList()); } public List> getEndpoints() { diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java index 50a255381..594b185ef 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java @@ -67,13 +67,18 @@ public class PluginsLoader { List lessons = Lists.newArrayList(); for (PluginResource plugin : findPluginResources()) { try { - Class lessonClazz = plugin.getLesson() - .orElseThrow(() -> new PluginLoadingFailure("Plugin resource does not contain lesson")); - NewLesson lesson = (NewLesson) lessonClazz.newInstance(); - List> assignments = plugin.getAssignments(); - lesson.setAssignments(createAssignment(assignments)); - lessons.add(lesson); - pluginEndpointPublisher.publish(plugin.getEndpoints()); + plugin.getLessons().forEach(c -> { + NewLesson lesson = null; + try { + lesson = (NewLesson) c.newInstance(); + } catch (Exception e) { + log.error("Error while loading:" + c, e); + } + List> assignments = plugin.getAssignments(); + lesson.setAssignments(createAssignment(assignments)); + lessons.add(lesson); + pluginEndpointPublisher.publish(plugin.getEndpoints()); + }); } catch (Exception e) { log.error("Error in loadLessons: ", e); } diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/Challenge1.java b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge1/Assignment1.java similarity index 94% rename from webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/Challenge1.java rename to webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge1/Assignment1.java index 2b2a4f1d4..a05fcafab 100644 --- a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/Challenge1.java +++ b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge1/Assignment1.java @@ -1,8 +1,9 @@ -package org.owasp.webgoat.plugin; +package org.owasp.webgoat.plugin.challenge1; import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.plugin.Flag; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @@ -42,7 +43,7 @@ import static org.owasp.webgoat.plugin.SolutionConstants.PASSWORD; * @since August 11, 2016 */ @AssignmentPath("/challenge/1") -public class Challenge1 extends AssignmentEndpoint { +public class Assignment1 extends AssignmentEndpoint { @RequestMapping(method = RequestMethod.POST) public diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/Challenge.java b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge1/Challenge1.java similarity index 79% rename from webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/Challenge.java rename to webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge1/Challenge1.java index be24197ca..86364d124 100644 --- a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/Challenge.java +++ b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge1/Challenge1.java @@ -1,4 +1,4 @@ -package org.owasp.webgoat.plugin; +package org.owasp.webgoat.plugin.challenge1; import com.google.common.collect.Lists; import org.owasp.webgoat.lessons.Category; @@ -10,7 +10,7 @@ import java.util.List; * @author nbaars * @since 3/21/17. */ -public class Challenge extends NewLesson { +public class Challenge1 extends NewLesson { @Override public Category getDefaultCategory() { @@ -29,11 +29,11 @@ public class Challenge extends NewLesson { @Override public String getTitle() { - return "challenge.title"; + return "challenge1.title"; } @Override public String getId() { - return "Challenge"; + return "Challenge1"; } } diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/Challenge2.java b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge2/Assignment2.java similarity index 87% rename from webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/Challenge2.java rename to webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge2/Assignment2.java index 87ece4df9..d46535589 100644 --- a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/Challenge2.java +++ b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge2/Assignment2.java @@ -1,8 +1,9 @@ -package org.owasp.webgoat.plugin; +package org.owasp.webgoat.plugin.challenge2; import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.plugin.Flag; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @@ -17,7 +18,7 @@ import static org.owasp.webgoat.plugin.SolutionConstants.SUPER_COUPON_CODE; * @since 4/6/17. */ @AssignmentPath("/challenge/2") -public class Challenge2 extends AssignmentEndpoint { +public class Assignment2 extends AssignmentEndpoint { @RequestMapping(method = RequestMethod.POST) public diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge2/Challenge2.java b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge2/Challenge2.java new file mode 100644 index 000000000..94b1cf58b --- /dev/null +++ b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge2/Challenge2.java @@ -0,0 +1,39 @@ +package org.owasp.webgoat.plugin.challenge2; + +import com.google.common.collect.Lists; +import org.owasp.webgoat.lessons.Category; +import org.owasp.webgoat.lessons.NewLesson; + +import java.util.List; + +/** + * @author nbaars + * @since 3/21/17. + */ +public class Challenge2 extends NewLesson { + + @Override + public Category getDefaultCategory() { + return Category.CHALLENGE; + } + + @Override + public List getHints() { + return Lists.newArrayList(); + } + + @Override + public Integer getDefaultRanking() { + return 10; + } + + @Override + public String getTitle() { + return "challenge2.title"; + } + + @Override + public String getId() { + return "Challenge2"; + } +} diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/ShopEndpoint.java b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge2/ShopEndpoint.java similarity index 97% rename from webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/ShopEndpoint.java rename to webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge2/ShopEndpoint.java index aae31c098..12c929492 100644 --- a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/ShopEndpoint.java +++ b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge2/ShopEndpoint.java @@ -1,4 +1,4 @@ -package org.owasp.webgoat.plugin; +package org.owasp.webgoat.plugin.challenge2; import com.beust.jcommander.internal.Lists; import lombok.AllArgsConstructor; diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge3/Assignment3.java b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge3/Assignment3.java new file mode 100644 index 000000000..5d44247dd --- /dev/null +++ b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge3/Assignment3.java @@ -0,0 +1,109 @@ +package org.owasp.webgoat.plugin.challenge3; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.EvictingQueue; +import org.joda.time.DateTime; +import org.owasp.webgoat.assignments.AssignmentEndpoint; +import org.owasp.webgoat.assignments.AssignmentPath; +import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.plugin.Flag; +import org.owasp.webgoat.session.WebSession; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Unmarshaller; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.Collection; + +import static org.springframework.http.MediaType.ALL_VALUE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.web.bind.annotation.RequestMethod.GET; +import static org.springframework.web.bind.annotation.RequestMethod.POST; + +/** + * @author nbaars + * @since 4/8/17. + */ +@AssignmentPath("/challenge/3") +public class Assignment3 extends AssignmentEndpoint { + + @Autowired + private WebSession webSession; + private static final EvictingQueue comments = EvictingQueue.create(100); + + static { + comments.add(new Comment("webgoat", DateTime.now().toString(), "Silly cat....")); + comments.add(new Comment("guest", DateTime.now().toString(), "I think I will use this picture in one of my projects.")); + comments.add(new Comment("guest", DateTime.now().toString(), "Lol!! :-).")); + } + + @RequestMapping(method = GET, produces = APPLICATION_JSON_VALUE) + @ResponseBody + public Collection retrieveComments() { + return comments; + } + + @RequestMapping(method = POST, consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) + @ResponseBody + public AttackResult createNewComment(@RequestBody String commentStr, @RequestHeader("Content-Type") String contentType) throws Exception { + Comment comment = null; + AttackResult attackResult = failed().build(); + if (APPLICATION_JSON_VALUE.equals(contentType)) { + comment = parseJson(commentStr); + comment.setDateTime(DateTime.now().toString()); + comment.setUser(webSession.getUserName()); + } + if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) { + comment = parseXml(commentStr); + comment.setDateTime(DateTime.now().toString()); + comment.setUser(webSession.getUserName()); + } + if (comment != null) { + comments.add(comment); + if (checkSolution(comment)) { + attackResult = success().feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(2)).build(); + } + } + + return attackResult; + } + + private boolean checkSolution(Comment comment) { + if (comment.getComment().contains("Congratulations you may now collect your flag")) { + comment.setComment("Congratulations to " + webSession.getUserName() + " for finding the flag!!"); + return true; + } + return false; + } + + public static Comment parseXml(String xml) throws Exception { + JAXBContext jc = JAXBContext.newInstance(Comment.class); + + XMLInputFactory xif = XMLInputFactory.newFactory(); + xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true); + xif.setProperty(XMLInputFactory.IS_VALIDATING, false); + + xif.setProperty(XMLInputFactory.SUPPORT_DTD, true); + XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(xml)); + + Unmarshaller unmarshaller = jc.createUnmarshaller(); + return (Comment) unmarshaller.unmarshal(xsr); + } + + private Comment parseJson(String comment) { + ObjectMapper mapper = new ObjectMapper(); + try { + return mapper.readValue(comment, Comment.class); + } catch (IOException e) { + return new Comment(); + } + } + + +} + diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge3/Challenge3.java b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge3/Challenge3.java index d1a92dd84..91a05d4ea 100644 --- a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge3/Challenge3.java +++ b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/plugin/challenge3/Challenge3.java @@ -1,109 +1,39 @@ package org.owasp.webgoat.plugin.challenge3; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.EvictingQueue; -import org.joda.time.DateTime; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentPath; -import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.plugin.Flag; -import org.owasp.webgoat.session.WebSession; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; +import com.google.common.collect.Lists; +import org.owasp.webgoat.lessons.Category; +import org.owasp.webgoat.lessons.NewLesson; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamReader; -import java.io.IOException; -import java.io.StringReader; -import java.util.Collection; - -import static org.springframework.http.MediaType.ALL_VALUE; -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import static org.springframework.web.bind.annotation.RequestMethod.GET; -import static org.springframework.web.bind.annotation.RequestMethod.POST; +import java.util.List; /** * @author nbaars - * @since 4/8/17. + * @since 3/21/17. */ -@AssignmentPath("/challenge/3") -public class Challenge3 extends AssignmentEndpoint { +public class Challenge3 extends NewLesson { - @Autowired - private WebSession webSession; - private static final EvictingQueue comments = EvictingQueue.create(100); - - static { - comments.add(new Comment("webgoat", DateTime.now().toString(), "Silly cat....")); - comments.add(new Comment("guest", DateTime.now().toString(), "I think I will use this picture in one of my projects.")); - comments.add(new Comment("guest", DateTime.now().toString(), "Lol!! :-).")); + @Override + public Category getDefaultCategory() { + return Category.CHALLENGE; } - @RequestMapping(method = GET, produces = APPLICATION_JSON_VALUE) - @ResponseBody - public Collection retrieveComments() { - return comments; + @Override + public List getHints() { + return Lists.newArrayList(); } - @RequestMapping(method = POST, consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) - @ResponseBody - public AttackResult createNewComment(@RequestBody String commentStr, @RequestHeader("Content-Type") String contentType) throws Exception { - Comment comment = null; - AttackResult attackResult = failed().build(); - if (APPLICATION_JSON_VALUE.equals(contentType)) { - comment = parseJson(commentStr); - comment.setDateTime(DateTime.now().toString()); - comment.setUser(webSession.getUserName()); - } - if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) { - comment = parseXml(commentStr); - comment.setDateTime(DateTime.now().toString()); - comment.setUser(webSession.getUserName()); - } - if (comment != null) { - comments.add(comment); - if (checkSolution(comment)) { - attackResult = success().feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(2)).build(); - } - } - - return attackResult; + @Override + public Integer getDefaultRanking() { + return 10; } - private boolean checkSolution(Comment comment) { - if (comment.getComment().contains("Congratulations you may now collect your flag")) { - comment.setComment("Congratulations to " + webSession.getUserName() + " for finding the flag!!"); - return true; - } - return false; + @Override + public String getTitle() { + return "challenge3.title"; } - public static Comment parseXml(String xml) throws Exception { - JAXBContext jc = JAXBContext.newInstance(Comment.class); - - XMLInputFactory xif = XMLInputFactory.newFactory(); - xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true); - xif.setProperty(XMLInputFactory.IS_VALIDATING, false); - - xif.setProperty(XMLInputFactory.SUPPORT_DTD, true); - XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(xml)); - - Unmarshaller unmarshaller = jc.createUnmarshaller(); - return (Comment) unmarshaller.unmarshal(xsr); + @Override + public String getId() { + return "Challenge3"; } - - private Comment parseJson(String comment) { - ObjectMapper mapper = new ObjectMapper(); - try { - return mapper.readValue(comment, Comment.class); - } catch (IOException e) { - return new Comment(); - } - } - - } - diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge1.html b/webgoat-lessons/challenge/src/main/resources/html/Challenge1.html new file mode 100644 index 000000000..f2baa0d78 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/html/Challenge1.html @@ -0,0 +1,59 @@ + + + + +
+
+
+ +
+
+
+
+
+
+ +
+
+
+ +
+ + +
+
+ + +
+ +
+
+
+ +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+ + + \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge2.html b/webgoat-lessons/challenge/src/main/resources/html/Challenge2.html new file mode 100644 index 000000000..8d3cea4c8 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/html/Challenge2.html @@ -0,0 +1,111 @@ + + + + +
+
+
+ +
+
+ + +
+
+
+ + +
+ +
+ +
+
+

Samsung Galaxy S8

+
Samsung · + (124421 reviews) +
+ +
+ PRICE +
+

US $899

+ +
+
+ COLOR +
+
+
+
+
+
+
+
+ CAPACITY +
+
+
64 GB
+
128 GB
+
+
+
+
+ QUANTITY +
+
+
+ +
+
+
+ +
+
+ CHECKOUT CODE +
+ + + +
+ +
+ +
+ Like
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge3.html b/webgoat-lessons/challenge/src/main/resources/html/Challenge3.html new file mode 100644 index 000000000..a5e0ebdf9 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/html/Challenge3.html @@ -0,0 +1,57 @@ + + + + +
+
+
+ +
+
+ + +
+
+
+
+
+ user profile image +
+
+
+ John Doe + uploaded a photo. +
+
24 days ago
+
+
+ +
+ image post +
+ +
+ +
+ +
+ + +
+
+
+
+
+ \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge4.html b/webgoat-lessons/challenge/src/main/resources/html/Challenge4.html new file mode 100644 index 000000000..c095de87a --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/html/Challenge4.html @@ -0,0 +1,111 @@ + + + + +
+
+
+ +
+
+ + +
+
+
+ + +
+ +
+ +
+
+

Samsung Galaxy S8

+
Samsung · + (124421 reviews) +
+ +
+ PRICE +
+

US $899

+ +
+
+ COLOR +
+
+
+
+
+
+
+
+ CAPACITY +
+
+
64 GB
+
128 GB
+
+
+
+
+ QUANTITY +
+
+
+ +
+
+
+ +
+
+ CHECKOUT CODE +
+ + + +
+ +
+ +
+ Like
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties index 7a0256c24..f5bc601a3 100644 --- a/webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties @@ -1,2 +1,5 @@ challenge.title=WebGoat Challenge +challenge1.title=Admin lost password +challenge2.title=Get it for free +challenge3.title=Photo comments challenge.solved=Congratulations, you solved the challenge. Here is your flag: {0}