diff options
-rw-r--r-- | README.md | 22 | ||||
-rw-r--r-- | src/challenge.go | 23 | ||||
-rw-r--r-- | src/http.go | 21 | ||||
-rw-r--r-- | src/main.go | 5 | ||||
-rw-r--r-- | src/seed.go | 86 |
5 files changed, 157 insertions, 0 deletions
diff --git a/README.md b/README.md index 433f658..89b2017 100644 --- a/README.md +++ b/README.md @@ -18,3 +18,25 @@ This executable needs some parameters to work properly: | `-username` | No | Name of our user, as used e.g. in salutation. No length or charset limitation. *Default: Player 1* | | `-accessCode` | Yes | Access code for the user. *Default: AllYourCodesAreBelongToUs* | | `-sessionSalt` | Yes | Variable to salt the session token generator with. | +| `-seedFile` | Yes | JSON file to read challenge information from. | + +## Seed file + +The seed file should be of the following format: + +``` +{ + "challenges": [ + { + "name": "Evil Service", + "description": "A meaningful and funny description", + "flag": "CIRCUS[IS_REALLY_COOL]", + "container": "challenge-evilservice", + "category": "Miscellaneous" + }, + { + "name": "Insecure Stuff", + [...] + ] +} +``` diff --git a/src/challenge.go b/src/challenge.go new file mode 100644 index 0000000..a1f9893 --- /dev/null +++ b/src/challenge.go @@ -0,0 +1,23 @@ +package main + +type Challenge struct { + Name string + Description string + Flag string // this should never leave the server + Container string // this could, but is not required as well + Category string +} + +type StrippedChallenge struct { + Name string `json:"name"` + Description string `json:"description"` + Category string `json:"category"` +} + +func stripChallenge(c Challenge) (StrippedChallenge) { + return StrippedChallenge{ + Name: c.Name, + Description: c.Description, + Category: c.Category, + } +} diff --git a/src/http.go b/src/http.go index 9f97352..1ddebe5 100644 --- a/src/http.go +++ b/src/http.go @@ -8,6 +8,7 @@ import ( "strings" "io/ioutil" "time" + "log" ) var ( @@ -26,6 +27,7 @@ func runHTTPServer() (error) { r.HandleFunc("/login", loginGetHandler).Methods("GET") r.HandleFunc("/login", loginPostHandler).Methods("POST") r.HandleFunc("/logout", logoutHandler).Methods("GET") + r.HandleFunc("/api/getChallenges", getChallengesHandler).Methods("GET") address := fmt.Sprintf(":%d", *port) return http.ListenAndServe(address, r) @@ -141,3 +143,22 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } + +func getChallengesHandler(w http.ResponseWriter, r *http.Request) { + session, cookieNotFoundError := r.Cookie("session") + + if cookieNotFoundError != nil || !isValidSession(session.Value) { + // either no session cookie found, or it contains an invalid session token. Redirect. + http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) + } else { + // valid session token found, send out JSON array containing all challenges + json, jsonErr := generateJSONFromChallenges() + + if jsonErr == nil { + w.Write([]byte(json)) + } else { + log.Println(jsonErr) + w.WriteHeader(500) + } + } +} diff --git a/src/main.go b/src/main.go index 519943a..8ca13a7 100644 --- a/src/main.go +++ b/src/main.go @@ -10,8 +10,13 @@ func main() { registerHTTPFlags() registerSessionFlags() registerCredentialsFlags() + registerSeedFlags() flag.Parse() + // Read challenges from file + getChallengesFromSeedFile() + generateJSONFromChallenges() + // Run HTTP server log.Fatalln(runHTTPServer()) } \ No newline at end of file diff --git a/src/seed.go b/src/seed.go new file mode 100644 index 0000000..8256929 --- /dev/null +++ b/src/seed.go @@ -0,0 +1,86 @@ +package main + +import ( + "flag" + "io/ioutil" + "log" + "encoding/json" +) + +var ( + challenges = []Challenge{} + seedFilePath* string +) + +func registerSeedFlags() { + seedFilePath = flag.String("seedFile", "/etc/companion.json", "Path to seedfile") +} + +// Read a given seed file and return their containing challenges +func readSeedFile(path string) ([]Challenge) { + var jsonContents map[string]interface{} + + // Read file + rawContents, readError := ioutil.ReadFile(path) + if readError != nil { + log.Printf("Failed to read seed file at %s: %s", *seedFilePath, readError.Error()) + return nil + } + + // Convert JSON String to map + unmarshalError := json.Unmarshal(rawContents, &jsonContents) + if unmarshalError != nil { + log.Printf("Failed to parse JSON in seed file at %s: %s", *seedFilePath, unmarshalError.Error()) + return nil + } + + tmpChallenges := []Challenge{} + + // Iterate over challenges + for index, challengeObject := range jsonContents["challenges"].([]interface{}) { + challenge := challengeObject.(map[string]interface{}) + // add our parsed challenge to array + + name, nameOK := challenge["name"].(string) + desc, descOK := challenge["description"].(string) + flag, flagOK := challenge["flag"].(string) + cont, contOK := challenge["container"].(string) + category, categoryOK := challenge["category"].(string) + + if nameOK && descOK && flagOK && contOK && categoryOK { + tmpChallenges = append(tmpChallenges, Challenge{ + Name: name, + Description: desc, + Flag: flag, + Container: cont, + Category: category, + }) + } else { + log.Printf("Ignoring challenge at position %d: Not all values are parseable (name: %b, desc: %b, flag: %b, container: %b, category: %b", index, nameOK, descOK, flagOK, contOK, categoryOK) + } + } + + return tmpChallenges +} + +// Read the file we set up using flags and store the returned challenge array +func getChallengesFromSeedFile() { + tmpChallenges := readSeedFile(*seedFilePath) + + if tmpChallenges != nil { + challenges = tmpChallenges + } +} + +// Generate a JSON string from the stored challenges, stripped from content which might spoil the user +func generateJSONFromChallenges() (string, error) { + // To avoid leakage of container name or the flag towards the user, we need to strip the challenges + var strippedChallenges []StrippedChallenge + + for _, challenge := range challenges { + strippedChallenges = append(strippedChallenges, stripChallenge(challenge)) + } + + marshalled, marshalError := json.Marshal(strippedChallenges) + return string(marshalled), marshalError +} |