├── .DS_Store
├── .gitignore
├── Caddyfile
├── LICENSE
├── Makefile
├── README.md
├── authentication
├── auth.go
├── login.go
└── register.go
├── backup
└── .gitignore
├── bin
└── .gitignore
├── ctfsrc
├── announcement.go
├── challengeupload.go
├── flagvalidation.go
├── getchallenge.go
├── imexport.go
├── points.go
├── startstop.go
└── teammanagement.go
├── database
└── db.go
├── entities
├── announcements.go
├── backup.go
├── challenge.go
├── flag.go
├── points.go
├── sessions.go
├── startstop.go
└── users.go
├── frontend
├── .gitignore
├── dist_collected
│ ├── CTF.html
│ ├── admin.html
│ ├── assets
│ │ ├── index-357cb75c.css
│ │ ├── index-860e7d9b.js
│ │ ├── index-bbff9fea.js
│ │ └── map.geojson
│ └── index.html
├── index.html
└── src
│ ├── App.svelte
│ ├── app.css
│ ├── lib
│ ├── Admin.svelte
│ └── Index.svelte
│ └── main.js
├── frontend_dev
├── .gitignore
├── .vscode
│ ├── extensions.json
│ └── launch.json
├── README.md
├── astro.config.mjs
├── package.json
├── pnpm-lock.yaml
├── public
│ ├── favicon.svg
│ └── map.geojson
├── src
│ ├── components
│ │ ├── admin.svelte
│ │ ├── ctfstats.svelte
│ │ ├── globe.jsx
│ │ └── login.svelte
│ ├── env.d.ts
│ └── pages
│ │ ├── ctf.astro
│ │ ├── dashboard.astro
│ │ └── index.astro
├── svelte.config.js
├── tailwind.config.cjs
└── tsconfig.json
├── go.mod
├── go.sum
├── main.go
├── servehtml
├── admin.go
├── ctf.go
└── login.go
└── startup.sh
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FADAMIS/ctf-portal/198ac2fdda1c8e6a33ad3272ca3703acb8c42bb4/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | CTFCONTENTS/*
2 | !CTFCONTENTS/.gitignore
3 |
4 | bin/*
5 | !bin/.gitignore
6 |
7 | backup/*
8 | !backup/.gitignore
--------------------------------------------------------------------------------
/Caddyfile:
--------------------------------------------------------------------------------
1 | :80 {
2 | reverse_proxy /api/* localhost:8888
3 | reverse_proxy /* localhost:3000
4 | }
5 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | - go build -o bin/
3 | - sudo setcap 'cap_net_bind_service=+ep' bin/ctf-portal
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CTF Portal
2 |
3 | Backend and frontend of CTF contest portal, originally made for HackDays but is generally usable.
4 |
5 | CTF challenges are stored in CTFCONTENTS directory - every challenge has it's own subdirectory
6 |
7 | ## TODO:
8 | - Finish adding challenges (frontend)
9 | - Add Announcments, count down and leaderboard to gameboard (frontend)
10 |
11 | ---
12 |
13 | **Made with Go and Svelte**
14 |
15 | *Nearly finished, in development*
16 |
17 | Developers:
18 | - Backend - [Fabucik](https://github.com/Fabucik)
19 | - Frontend - [DuckyScr](https://github.com/DuckyScr)
20 |
--------------------------------------------------------------------------------
/authentication/auth.go:
--------------------------------------------------------------------------------
1 | package authentication
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/Fabucik/ctf-portal/database"
7 | "github.com/Fabucik/ctf-portal/entities"
8 | "github.com/gin-gonic/gin"
9 | )
10 |
11 | func IsValidSession(session entities.Session) bool {
12 | /*dbJson, _ := os.ReadFile("./database/session-cookies.json")
13 | var sessions entities.Sessions
14 | json.Unmarshal(dbJson, &sessions)
15 |
16 | for i := 0; i < len(sessions.Sessions); i++ {
17 | if session == sessions.Sessions[i] {
18 | // if session exists BUT is expired, delete the session
19 | if isExpired(session.ExpiresIn) {
20 | sessions.Sessions = append(sessions.Sessions[:i], sessions.Sessions[i+1:]...)
21 | writableJson, _ := json.MarshalIndent(sessions, "", "\t")
22 | os.WriteFile("./database/session-cookies.json", writableJson, 0600)
23 | return false
24 | }
25 |
26 | return true
27 | }
28 | }
29 |
30 | return false*/
31 |
32 | sessions := database.ReadSessions(database.GetOpenedDB())
33 | for i := 0; i < len(sessions.Sessions); i++ {
34 | if session == sessions.Sessions[i] {
35 | return !isExpired(session.ExpiresIn)
36 | }
37 | }
38 |
39 | return false
40 | }
41 |
42 | func IsAdmin(ctx *gin.Context, session entities.Session) bool {
43 | if !IsValidSession(session) || IsValidSession(session) && session.Username != "admin" {
44 | ctx.JSON(http.StatusUnauthorized, gin.H{
45 | "message": "Unauthorized",
46 | })
47 |
48 | return false
49 | }
50 |
51 | return true
52 | }
53 |
--------------------------------------------------------------------------------
/authentication/login.go:
--------------------------------------------------------------------------------
1 | package authentication
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 | "time"
7 |
8 | "github.com/Fabucik/ctf-portal/database"
9 | "github.com/Fabucik/ctf-portal/entities"
10 | "github.com/gin-gonic/gin"
11 | "github.com/google/uuid"
12 | )
13 |
14 | func Login(ctx *gin.Context) {
15 | var credentials entities.User
16 | ctx.Bind(&credentials)
17 |
18 | /*dbJson, _ := os.ReadFile("./database/users.json")
19 | var dbContent entities.Users
20 | json.Unmarshal(dbJson, &dbContent)
21 |
22 | for i := 0; i < len(dbContent.Users); i++ {
23 | // if credentials match, create session
24 | if dbContent.Users[i].Username == credentials.Username && dbContent.Users[i].Password == credentials.Password {
25 | session := createSessionCookie(credentials.Username)
26 | ctx.SetCookie("session", session, 6*60*60, "/", "localhost", false, true)
27 | ctx.JSON(http.StatusOK, gin.H{
28 | "message": "Login successful",
29 | })
30 | return
31 | }
32 | }*/
33 |
34 | allUsers := database.ReadUsers(database.GetOpenedDB())
35 | for i := 0; i < len(allUsers.Users); i++ {
36 | if allUsers.Users[i].Username == credentials.Username && allUsers.Users[i].Password == credentials.Password {
37 | session := createSessionCookie(credentials.Username)
38 | // CHANGE DOMAIN
39 | ctx.SetCookie("session", session, 6*60*60, "/", "localhost", false, true)
40 | ctx.JSON(http.StatusOK, gin.H{
41 | "message": "Login successful",
42 | })
43 | return
44 | }
45 | }
46 |
47 | ctx.JSON(http.StatusUnauthorized, gin.H{
48 | "message": "Login failed",
49 | })
50 | }
51 |
52 | func createSessionCookie(username string) string {
53 |
54 | // create new session with all the parameters
55 | var session entities.Session
56 |
57 | id := uuid.New()
58 | now := time.Now()
59 |
60 | session.ID = id.String()
61 | session.Username = username
62 | session.ExpiresIn = now.Unix() + 6*60*60
63 |
64 | returnSession, _ := json.Marshal(session)
65 |
66 | /*
67 | // read session database, append newly created session and write it back
68 | var sessions entities.Sessions
69 | sessionsJson, _ := os.ReadFile("./database/session-cookies.json")
70 | json.Unmarshal(sessionsJson, &sessions)
71 |
72 | sessions.Sessions = append(sessions.Sessions, session)
73 |
74 | writableJson, _ := json.MarshalIndent(sessions, "", "\t")
75 | os.WriteFile("./database/session-cookies.json", writableJson, 0600)*/
76 |
77 | database.WriteSession(database.GetOpenedDB(), session)
78 |
79 | return string(returnSession)
80 | }
81 |
82 | func isExpired(sessionTime int64) bool {
83 | now := time.Now()
84 |
85 | return sessionTime < now.Unix()
86 | }
87 |
--------------------------------------------------------------------------------
/authentication/register.go:
--------------------------------------------------------------------------------
1 | package authentication
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/Fabucik/ctf-portal/database"
8 | "github.com/Fabucik/ctf-portal/entities"
9 | "github.com/gin-gonic/gin"
10 | "github.com/gin-gonic/gin/binding"
11 | )
12 |
13 | func Register(ctx *gin.Context) {
14 | var newUser entities.User
15 | ctx.ShouldBindBodyWith(&newUser, binding.JSON)
16 |
17 | var session entities.Session
18 | cookie, _ := ctx.Cookie("session")
19 | json.Unmarshal([]byte(cookie), &session)
20 |
21 | isAdmin := IsAdmin(ctx, session)
22 | if !isAdmin {
23 | return
24 | }
25 |
26 | if DoesUserExists(newUser.Username) || newUser.Username == "admin" {
27 | ctx.JSON(http.StatusConflict, gin.H{
28 | "message": "Username already exists",
29 | })
30 |
31 | return
32 | }
33 | /*
34 | // read user database, append new user and write back
35 | userJson, _ := os.ReadFile("./database/users.json")
36 | var users entities.Users
37 | json.Unmarshal(userJson, &users)
38 |
39 | users.Users = append(users.Users, newUser)
40 |
41 | writableJson, _ := json.MarshalIndent(users, "", "\t")
42 | os.WriteFile("./database/users.json", writableJson, 0600)
43 |
44 | // create entries in point database for the newly created user
45 | var userPoints entities.TeamPoints
46 | userPoints.Team = newUser.Username
47 | userPoints.PointAmount = 0
48 |
49 | var db entities.AllPoints
50 | pointDb, _ := os.ReadFile("./database/points.json")
51 | json.Unmarshal(pointDb, &db)
52 |
53 | db.Points = append(db.Points, userPoints)
54 | writableJson, _ = json.MarshalIndent(db, "", "\t")
55 | os.WriteFile("./database/points.json", writableJson, 0600)*/
56 |
57 | database.CreateUser(database.GetOpenedDB(), newUser)
58 |
59 | var teamPoints entities.TeamPoints
60 | teamPoints.Team = newUser.Username
61 | teamPoints.PointAmount = 0
62 | teamPoints.Solved = ""
63 | database.CreateTeamPoints(database.GetOpenedDB(), teamPoints)
64 |
65 | ctx.JSON(http.StatusOK, gin.H{
66 | "message": "Register successful",
67 | })
68 | }
69 |
70 | func DoesUserExists(username string) bool {
71 | /*
72 | dbJson, _ := os.ReadFile("./database/users.json")
73 |
74 | var users entities.Users
75 | json.Unmarshal(dbJson, &users)
76 |
77 | for i := 0; i < len(users.Users); i++ {
78 | if users.Users[i].Username == username {
79 | return true
80 | }
81 | }*/
82 |
83 | allUsers := database.ReadUsers(database.GetOpenedDB())
84 | for i := 0; i < len(allUsers.Users); i++ {
85 | if allUsers.Users[i].Username == username {
86 | return true
87 | }
88 | }
89 |
90 | return false
91 | }
92 |
--------------------------------------------------------------------------------
/backup/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FADAMIS/ctf-portal/198ac2fdda1c8e6a33ad3272ca3703acb8c42bb4/backup/.gitignore
--------------------------------------------------------------------------------
/bin/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FADAMIS/ctf-portal/198ac2fdda1c8e6a33ad3272ca3703acb8c42bb4/bin/.gitignore
--------------------------------------------------------------------------------
/ctfsrc/announcement.go:
--------------------------------------------------------------------------------
1 | package ctfsrc
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/Fabucik/ctf-portal/authentication"
8 | "github.com/Fabucik/ctf-portal/database"
9 | "github.com/Fabucik/ctf-portal/entities"
10 | "github.com/gin-gonic/gin"
11 | )
12 |
13 | func CreateAnnouncement(ctx *gin.Context) {
14 | var session entities.Session
15 | cookie, _ := ctx.Cookie("session")
16 | json.Unmarshal([]byte(cookie), &session)
17 |
18 | if !authentication.IsAdmin(ctx, session) {
19 | return
20 | }
21 |
22 | var announcement entities.Announcement
23 | ctx.Bind(&announcement)
24 |
25 | if DoesAnnouncementExist(announcement) {
26 | ctx.JSON(http.StatusConflict, gin.H{
27 | "message": "Announcement with this ID already exists",
28 | })
29 |
30 | return
31 | }
32 |
33 | /*
34 | var announcements entities.Announcements
35 | announcementDb, _ := os.ReadFile("./database/announcements.json")
36 | json.Unmarshal(announcementDb, &announcements)
37 |
38 | announcements.Announcements = append(announcements.Announcements, announcement)
39 | writableJson, _ := json.MarshalIndent(announcements, "", "\t")
40 | os.WriteFile("./database/announcements.json", writableJson, 0600)*/
41 |
42 | database.CreateAnnouncement(database.GetOpenedDB(), announcement)
43 |
44 | ctx.JSON(http.StatusOK, gin.H{
45 | "message": "Announcement successfully created",
46 | })
47 | }
48 |
49 | func GetAnnouncements(ctx *gin.Context) {
50 | var session entities.Session
51 | cookie, _ := ctx.Cookie("session")
52 | json.Unmarshal([]byte(cookie), &session)
53 |
54 | if !authentication.IsValidSession(session) {
55 | ctx.JSON(http.StatusUnauthorized, gin.H{
56 | "message": "not logged in",
57 | })
58 | return
59 | }
60 |
61 | /*
62 | var announcements entities.Announcements
63 | announcementDb, _ := os.ReadFile("./database/announcements.json")
64 | json.Unmarshal(announcementDb, &announcements)*/
65 |
66 | announcements := database.ReadAnnouncements(database.GetOpenedDB())
67 |
68 | ctx.JSON(http.StatusOK, announcements)
69 | }
70 |
71 | func DeleteAnnouncement(ctx *gin.Context) {
72 | var session entities.Session
73 | cookie, _ := ctx.Cookie("session")
74 | json.Unmarshal([]byte(cookie), &session)
75 |
76 | if !authentication.IsAdmin(ctx, session) {
77 | return
78 | }
79 |
80 | var announcementToDelete entities.Announcement
81 | ctx.Bind(&announcementToDelete)
82 |
83 | /*
84 | var announcements entities.Announcements
85 | announcementDb, _ := os.ReadFile("./database/announcements.json")
86 | json.Unmarshal(announcementDb, &announcements)
87 |
88 | for i := 0; i < len(announcements.Announcements); i++ {
89 | if announcementToDelete.ID == announcements.Announcements[i].ID {
90 | announcements.Announcements = append(announcements.Announcements[:i], announcements.Announcements[i+1:]...)
91 | }
92 | }
93 |
94 | writableJson, _ := json.MarshalIndent(&announcements, "", "\t")
95 | os.WriteFile("./database/announcements.json", writableJson, 0600)*/
96 |
97 | database.DeleteAnnouncement(database.GetOpenedDB(), announcementToDelete)
98 |
99 | ctx.JSON(http.StatusOK, gin.H{
100 | "message": "Successfully deleted announcement",
101 | })
102 | }
103 |
104 | func DoesAnnouncementExist(announcement entities.Announcement) bool {
105 | /*var announcements entities.Announcements
106 | announcementDb, _ := os.ReadFile("./database/announcements.json")
107 | json.Unmarshal(announcementDb, &announcements)*/
108 |
109 | announcements := database.ReadAnnouncements(database.GetOpenedDB())
110 |
111 | for i := 0; i < len(announcements.Announcements); i++ {
112 | if announcement.ID == announcements.Announcements[i].ID {
113 | return true
114 | }
115 | }
116 |
117 | return false
118 | }
119 |
--------------------------------------------------------------------------------
/ctfsrc/challengeupload.go:
--------------------------------------------------------------------------------
1 | package ctfsrc
2 |
3 | import (
4 | "encoding/base64"
5 | "encoding/json"
6 | "fmt"
7 | "net/http"
8 | "os"
9 | "strconv"
10 | "strings"
11 |
12 | "github.com/Fabucik/ctf-portal/authentication"
13 | "github.com/Fabucik/ctf-portal/entities"
14 | "github.com/gin-gonic/gin"
15 | "github.com/gin-gonic/gin/binding"
16 | )
17 |
18 | func CreateChallenge(ctx *gin.Context) {
19 | var challenge entities.Challenge
20 | ctx.ShouldBindBodyWith(&challenge, binding.JSON)
21 |
22 | var session entities.Session
23 | cookie, _ := ctx.Cookie("session")
24 | json.Unmarshal([]byte(cookie), &session)
25 |
26 | isAdmin := authentication.IsAdmin(ctx, session)
27 | if !isAdmin {
28 | return
29 | }
30 |
31 | if strings.Contains(challenge.Name, ";") {
32 | ctx.JSON(http.StatusForbidden, gin.H{
33 | "message": "Cannot use ';' in challenge name",
34 | })
35 |
36 | return
37 | }
38 |
39 | entries, _ := os.ReadDir("CTFCONTENTS")
40 | for _, e := range entries {
41 | if challenge.Name == e.Name() {
42 | ctx.JSON(http.StatusConflict, gin.H{
43 | "message": "challenge's name is taken",
44 | })
45 |
46 | return
47 | }
48 | }
49 |
50 | // all ctf challenges are stored under CTFCONTENTS directory as other directories
51 | os.Mkdir("CTFCONTENTS/"+challenge.Name, 0777)
52 | os.Mkdir("CTFCONTENTS/"+challenge.Name+"/FILES", 0777)
53 |
54 | os.WriteFile("CTFCONTENTS/"+challenge.Name+"/FLAG.TXT", []byte(challenge.Flag), 0600)
55 | os.WriteFile("CTFCONTENTS/"+challenge.Name+"/POINTS.TXT", []byte(strconv.Itoa(challenge.Points)), 0600)
56 | os.WriteFile("CTFCONTENTS/"+challenge.Name+"/DESCRIPTION.TXT", []byte(challenge.Description), 0600)
57 | os.WriteFile("CTFCONTENTS/"+challenge.Name+"/COUNTRY.TXT", []byte(challenge.CountryCode), 0600)
58 |
59 | // write each file to FILES dir
60 | for i := 0; i < len(challenge.Files); i++ {
61 | contents, err := base64.StdEncoding.DecodeString(challenge.Files[i].Base64)
62 | if err != nil {
63 | fmt.Println(err)
64 | }
65 |
66 | f, _ := os.Create("CTFCONTENTS/" + challenge.Name + "/FILES/" + challenge.Files[i].FileName)
67 | defer f.Close()
68 |
69 | f.Write(contents)
70 | }
71 |
72 | ctx.JSON(http.StatusOK, gin.H{
73 | "message": "Upload successful",
74 | })
75 | }
76 |
--------------------------------------------------------------------------------
/ctfsrc/flagvalidation.go:
--------------------------------------------------------------------------------
1 | package ctfsrc
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 | "os"
7 | "strconv"
8 |
9 | "github.com/Fabucik/ctf-portal/authentication"
10 | "github.com/Fabucik/ctf-portal/entities"
11 | "github.com/gin-gonic/gin"
12 | "github.com/gin-gonic/gin/binding"
13 | )
14 |
15 | func ValidateFlag(ctx *gin.Context) {
16 | var flag entities.Flag
17 | ctx.ShouldBindBodyWith(&flag, binding.JSON)
18 |
19 | var session entities.Session
20 | cookie, _ := ctx.Cookie("session")
21 | json.Unmarshal([]byte(cookie), &session)
22 |
23 | if !authentication.IsValidSession(session) {
24 | ctx.JSON(http.StatusUnauthorized, gin.H{
25 | "message": "Not logged in",
26 | })
27 |
28 | return
29 | }
30 |
31 | if !IsCtfStarted() || IsCtfExpired() {
32 | ctx.JSON(http.StatusUnauthorized, gin.H{
33 | "message": "CTF is not yet started",
34 | })
35 |
36 | return
37 | }
38 |
39 | // return not found if challenge (directory) doesnt exist
40 | _, err := os.ReadDir("CTFCONTENTS/" + flag.Challenge)
41 | if err != nil {
42 | ctx.JSON(http.StatusNotFound, gin.H{
43 | "message": "Challenge not found",
44 | })
45 |
46 | return
47 | }
48 |
49 | // read flag and compare it with input
50 | serverFlag, _ := os.ReadFile("CTFCONTENTS/" + flag.Challenge + "/FLAG.TXT")
51 | if string(serverFlag) != flag.Value {
52 | ctx.JSON(http.StatusConflict, gin.H{
53 | "message": "Wrong flag",
54 | })
55 |
56 | return
57 | }
58 |
59 | // assign points
60 | points, _ := os.ReadFile("CTFCONTENTS/" + flag.Challenge + "/POINTS.TXT")
61 | pointsInt, _ := strconv.Atoi(string(points))
62 |
63 | // if flag was already answered, return status forbidden
64 | isSuccess := AssignPoints(pointsInt, session.Username, flag.Challenge)
65 | if !isSuccess {
66 | ctx.JSON(http.StatusForbidden, gin.H{
67 | "message": "Already answered",
68 | })
69 |
70 | return
71 | }
72 |
73 | ctx.JSON(http.StatusOK, gin.H{
74 | "message": "Correct",
75 | })
76 | }
77 |
--------------------------------------------------------------------------------
/ctfsrc/getchallenge.go:
--------------------------------------------------------------------------------
1 | package ctfsrc
2 |
3 | import (
4 | "encoding/base64"
5 | "encoding/json"
6 | "net/http"
7 | "os"
8 | "strconv"
9 |
10 | "github.com/Fabucik/ctf-portal/authentication"
11 | "github.com/Fabucik/ctf-portal/entities"
12 | "github.com/gin-gonic/gin"
13 | )
14 |
15 | func GetChallenges(ctx *gin.Context) {
16 | var session entities.Session
17 | cookie, _ := ctx.Cookie("session")
18 | json.Unmarshal([]byte(cookie), &session)
19 |
20 | if !authentication.IsValidSession(session) {
21 | ctx.JSON(http.StatusUnauthorized, gin.H{
22 | "message": "Not logged in",
23 | })
24 |
25 | return
26 | }
27 |
28 | if !IsCtfStarted() || IsCtfExpired() {
29 | ctx.JSON(http.StatusUnauthorized, gin.H{
30 | "message": "CTF is not yet started",
31 | })
32 |
33 | return
34 | }
35 |
36 | var allChallenges entities.ReturnChallenges
37 |
38 | challengeDirs, _ := os.ReadDir("CTFCONTENTS")
39 | allChallenges.Challenges = make([]entities.ReturnChallenge, len(challengeDirs))
40 |
41 | for i, entry := range challengeDirs {
42 | points, _ := os.ReadFile("CTFCONTENTS/" + entry.Name() + "/POINTS.TXT")
43 | description, _ := os.ReadFile("CTFCONTENTS/" + entry.Name() + "/DESCRIPTION.TXT")
44 | country, _ := os.ReadFile("CTFCONTENTS/" + entry.Name() + "/COUNTRY.TXT")
45 |
46 | allChallenges.Challenges[i].Name = entry.Name()
47 | allChallenges.Challenges[i].Points, _ = strconv.Atoi(string(points))
48 | allChallenges.Challenges[i].Description = string(description)
49 | allChallenges.Challenges[i].CountryCode = string(country)
50 |
51 | challengeFiles, _ := os.ReadDir("CTFCONTENTS/" + entry.Name() + "/FILES")
52 | for _, file := range challengeFiles {
53 | contents, _ := os.ReadFile("CTFCONTENTS/" + entry.Name() + "/FILES/" + file.Name())
54 |
55 | var fileInfo entities.ChallengeFile
56 | fileInfo.FileName = file.Name()
57 | fileInfo.Base64 = base64.StdEncoding.EncodeToString(contents)
58 | allChallenges.Challenges[i].Files = append(allChallenges.Challenges[i].Files, fileInfo)
59 | }
60 | }
61 |
62 | ctx.JSON(http.StatusOK, allChallenges)
63 | }
64 |
--------------------------------------------------------------------------------
/ctfsrc/imexport.go:
--------------------------------------------------------------------------------
1 | package ctfsrc
2 |
3 | import (
4 | "archive/zip"
5 | "encoding/base64"
6 | "encoding/json"
7 | "io"
8 | "net/http"
9 | "os"
10 | "path/filepath"
11 | "strings"
12 | "time"
13 |
14 | "github.com/Fabucik/ctf-portal/authentication"
15 | "github.com/Fabucik/ctf-portal/entities"
16 | "github.com/gin-gonic/gin"
17 | )
18 |
19 | func Export(ctx *gin.Context) {
20 | var session entities.Session
21 | cookie, _ := ctx.Cookie("session")
22 | json.Unmarshal([]byte(cookie), &session)
23 |
24 | if !authentication.IsAdmin(ctx, session) {
25 | return
26 | }
27 |
28 | fileName, err := Zip()
29 | if err != nil {
30 | ctx.JSON(http.StatusInternalServerError, gin.H{
31 | "message": "An error occured while exporting the CTF",
32 | })
33 |
34 | return
35 | }
36 |
37 | zipFile, _ := os.ReadFile("./backup/" + fileName)
38 | encoded := base64.StdEncoding.EncodeToString(zipFile)
39 |
40 | ctx.JSON(http.StatusOK, gin.H{
41 | "message": "OK",
42 | "base64": encoded,
43 | })
44 | }
45 |
46 | func Import(ctx *gin.Context) {
47 | var session entities.Session
48 | cookie, _ := ctx.Cookie("session")
49 | json.Unmarshal([]byte(cookie), &session)
50 |
51 | if !authentication.IsAdmin(ctx, session) {
52 | return
53 | }
54 |
55 | var encoded entities.Backup
56 | ctx.Bind(&encoded)
57 |
58 | decoded, _ := base64.StdEncoding.DecodeString(encoded.Base64)
59 | os.WriteFile("./backup/import.zip", decoded, 0600)
60 |
61 | ctfcontents, _ := os.ReadDir("./CTFCONTENTS")
62 | for _, entry := range ctfcontents {
63 | if entry.Name() == ".gitignore" {
64 | continue
65 | }
66 |
67 | os.RemoveAll("./CTFCONTENTS/" + entry.Name())
68 | }
69 |
70 | err := Unzip("./backup/import.zip")
71 | if err != nil {
72 | ctx.JSON(http.StatusInternalServerError, gin.H{
73 | "message": "An error occured while importing the CTF",
74 | })
75 |
76 | return
77 | }
78 |
79 | ctx.JSON(http.StatusOK, gin.H{
80 | "message": "Successfully imported CTF",
81 | })
82 | }
83 |
84 | func Unzip(src string) error {
85 | reader, err := zip.OpenReader(src)
86 | if err != nil {
87 | return err
88 | }
89 |
90 | defer reader.Close()
91 |
92 | extract := func(file *zip.File, dst string) error {
93 | rc, err := file.Open()
94 | if err != nil {
95 | return err
96 | }
97 |
98 | defer rc.Close()
99 |
100 | path := filepath.Join(dst, file.Name)
101 |
102 | if file.FileInfo().IsDir() {
103 | os.MkdirAll(path, 0777)
104 | } else {
105 | os.MkdirAll(filepath.Dir(path), 0777)
106 |
107 | f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
108 | if err != nil {
109 | return err
110 | }
111 |
112 | defer f.Close()
113 |
114 | _, err = io.Copy(f, rc)
115 | if err != nil {
116 | return err
117 | }
118 | }
119 |
120 | return nil
121 | }
122 |
123 | for _, f := range reader.File {
124 | err := extract(f, "./CTFCONTENTS")
125 | if err != nil {
126 | return err
127 | }
128 | }
129 |
130 | return nil
131 | }
132 |
133 | func Zip() (string, error) {
134 | fileName := "backup-" + time.Now().Format("02_01_2006__15_04_05") + ".zip"
135 | dstFile, err := os.Create("./backup/" + fileName)
136 | if err != nil {
137 | return "", err
138 | }
139 |
140 | zipped := zip.NewWriter(dstFile)
141 | err = filepath.Walk("./CTFCONTENTS", func(filePath string, info os.FileInfo, err error) error {
142 | if info.IsDir() {
143 | return nil
144 | }
145 | if err != nil {
146 | return err
147 | }
148 |
149 | relPath := strings.TrimPrefix(filePath, "CTFCONTENTS")
150 | zipFile, err := zipped.Create(relPath)
151 | if err != nil {
152 | return err
153 | }
154 |
155 | fsFile, err := os.Open(filePath)
156 | if err != nil {
157 | return err
158 | }
159 |
160 | _, err = io.Copy(zipFile, fsFile)
161 | if err != nil {
162 | return err
163 | }
164 |
165 | return nil
166 | })
167 |
168 | if err != nil {
169 | return "", err
170 | }
171 |
172 | zipped.Close()
173 |
174 | return fileName, nil
175 | }
176 |
--------------------------------------------------------------------------------
/ctfsrc/points.go:
--------------------------------------------------------------------------------
1 | package ctfsrc
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 | "strings"
7 |
8 | "github.com/Fabucik/ctf-portal/authentication"
9 | "github.com/Fabucik/ctf-portal/database"
10 | "github.com/Fabucik/ctf-portal/entities"
11 | "github.com/gin-gonic/gin"
12 | )
13 |
14 | func AssignPoints(points int, team string, challenge string) bool {
15 | /* var db entities.AllPoints
16 | dbJson, _ := os.ReadFile("./database/points.json")
17 | json.Unmarshal(dbJson, &db)
18 |
19 | var newPoints entities.TeamPoints
20 | newPoints.Team = team
21 | for i := 0; i < len(db.Points); i++ {
22 | if newPoints.Team == db.Points[i].Team {
23 | // if team already answered the flag return from the function
24 | for j := 0; j < len(db.Points[i].Solved); j++ {
25 | if db.Points[i].Solved[j] == challenge {
26 | return false
27 | }
28 | }
29 |
30 | // update finished flags
31 | newPoints.Solved = append(db.Points[i].Solved, challenge)
32 |
33 | // update points and delete old entry
34 | newPoints.PointAmount = db.Points[i].PointAmount + points
35 | db.Points = append(db.Points[:i], db.Points[i+1:]...)
36 | }
37 | }
38 |
39 | db.Points = append(db.Points, newPoints)
40 |
41 | writableJson, _ := json.MarshalIndent(db, "", "\t")
42 | os.WriteFile("./database/points.json", writableJson, 0600)
43 |
44 | return true*/
45 |
46 | teamPoints := database.ReadTeamPoints(database.GetOpenedDB(), team)
47 | solved := strings.Split(teamPoints.Solved, ";")
48 | for i := 0; i < len(solved); i++ {
49 | if challenge == solved[i] {
50 | return false
51 | }
52 | }
53 |
54 | teamPoints.Team = team
55 | teamPoints.Solved += ";" + challenge
56 | teamPoints.PointAmount += points
57 | database.UpdatePoints(database.GetOpenedDB(), teamPoints)
58 |
59 | return true
60 | }
61 |
62 | func GetAllPoints(ctx *gin.Context) {
63 | var session entities.Session
64 | cookie, _ := ctx.Cookie("session")
65 | json.Unmarshal([]byte(cookie), &session)
66 |
67 | if !authentication.IsValidSession(session) {
68 | ctx.JSON(http.StatusUnauthorized, gin.H{
69 | "message": "Not logged in",
70 | })
71 |
72 | return
73 | }
74 | /*
75 | var allPoints entities.AllPoints
76 | pointDb, _ := os.ReadFile("./database/points.json")
77 | json.Unmarshal(pointDb, &allPoints)
78 |
79 | ctx.JSON(http.StatusOK, allPoints)*/
80 |
81 | allPoints := database.ReadAllPoints(database.GetOpenedDB())
82 | ctx.JSON(http.StatusOK, allPoints)
83 | }
84 |
--------------------------------------------------------------------------------
/ctfsrc/startstop.go:
--------------------------------------------------------------------------------
1 | package ctfsrc
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 | "strconv"
7 | "time"
8 |
9 | "github.com/Fabucik/ctf-portal/authentication"
10 | "github.com/Fabucik/ctf-portal/database"
11 | "github.com/Fabucik/ctf-portal/entities"
12 | "github.com/gin-gonic/gin"
13 | )
14 |
15 | func SetTime(ctx *gin.Context) {
16 | var session entities.Session
17 | cookie, _ := ctx.Cookie("session")
18 | json.Unmarshal([]byte(cookie), &session)
19 |
20 | if !authentication.IsAdmin(ctx, session) {
21 | return
22 | }
23 |
24 | var autoStart entities.AutomaticStart
25 | ctx.Bind(&autoStart)
26 |
27 | /*
28 | var startInfo entities.StartStopInfo
29 | startInfoDb, _ := os.ReadFile("./database/time.json")
30 | json.Unmarshal(startInfoDb, &startInfo)
31 |
32 |
33 | startInfo.Automatic = ctfTime
34 | startInfo.Automatic.IsValid = true
35 | startInfo.Manual.IsValid = false
36 |
37 | writableJson, _ := json.MarshalIndent(startInfo, "", "\t")
38 | os.WriteFile("./database/time.json", writableJson, 0600)*/
39 |
40 | var manualStart entities.ManualStart
41 | manualStart.IsValid = false
42 | autoStart.IsValid = true
43 |
44 | database.WriteAutoStart(database.GetOpenedDB(), autoStart)
45 | database.WriteManStart(database.GetOpenedDB(), manualStart)
46 |
47 | ctx.JSON(http.StatusOK, gin.H{
48 | "message": "Successfully set time",
49 | })
50 | }
51 |
52 | func GetTime(ctx *gin.Context) {
53 | var session entities.Session
54 | cookie, _ := ctx.Cookie("session")
55 | json.Unmarshal([]byte(cookie), &session)
56 |
57 | if !authentication.IsValidSession(session) {
58 | ctx.JSON(http.StatusUnauthorized, gin.H{
59 | "message": "Not logged in",
60 | })
61 |
62 | return
63 | }
64 |
65 | /*
66 | var ctfTime entities.StartStopInfo
67 | timeDb, _ := os.ReadFile("./database/time.json")
68 | json.Unmarshal(timeDb, &ctfTime)*/
69 |
70 | ctfTime := database.ReadAutoStart(database.GetOpenedDB())
71 |
72 | ctx.JSON(http.StatusOK, ctfTime)
73 | }
74 |
75 | func SetManualStartStop(ctx *gin.Context) {
76 | var session entities.Session
77 | cookie, _ := ctx.Cookie("session")
78 | json.Unmarshal([]byte(cookie), &session)
79 |
80 | if !authentication.IsAdmin(ctx, session) {
81 | return
82 | }
83 |
84 | var manualStart entities.ManualStart
85 | ctx.Bind(&manualStart)
86 |
87 | /*
88 | var startInfo entities.StartStopInfo
89 | startInfoDb, _ := os.ReadFile("./database/time.json")
90 | json.Unmarshal(startInfoDb, &startInfo)
91 |
92 | startInfo.Manual = manualStart
93 | startInfo.Manual.IsValid = true
94 | startInfo.Automatic.IsValid = false
95 |
96 | writableJson, _ := json.MarshalIndent(&startInfo, "", "\t")
97 | os.WriteFile("./database/time.json", writableJson, 0600)*/
98 |
99 | var autoStart entities.AutomaticStart
100 | autoStart.IsValid = false
101 | manualStart.IsValid = true
102 |
103 | database.WriteManStart(database.GetOpenedDB(), manualStart)
104 | database.WriteAutoStart(database.GetOpenedDB(), autoStart)
105 |
106 | ctx.JSON(http.StatusOK, gin.H{
107 | "message": "Successfully set time",
108 | "isrunning": strconv.FormatBool(manualStart.IsStarted),
109 | })
110 | }
111 |
112 | func GetManualStartStop(ctx *gin.Context) {
113 | var session entities.Session
114 | cookie, _ := ctx.Cookie("session")
115 | json.Unmarshal([]byte(cookie), &session)
116 |
117 | if !authentication.IsValidSession(session) {
118 | ctx.JSON(http.StatusUnauthorized, gin.H{
119 | "message": "Not logged in",
120 | })
121 |
122 | return
123 | }
124 |
125 | /*
126 | var ctfTime entities.ManualStart
127 | startDb, _ := os.ReadFile("./database/time.json")
128 | json.Unmarshal(startDb, &ctfTime)*/
129 |
130 | manTime := database.ReadManStart(database.GetOpenedDB())
131 |
132 | ctx.JSON(http.StatusOK, manTime)
133 | }
134 |
135 | func IsCtfExpired() bool {
136 | /*
137 | var startInfo entities.StartStopInfo
138 | timeDb, _ := os.ReadFile("./database/time.json")
139 | json.Unmarshal(timeDb, &startInfo)*/
140 |
141 | autoStart := database.ReadAutoStart(database.GetOpenedDB())
142 | manStart := database.ReadManStart(database.GetOpenedDB())
143 |
144 | // if automatic start is used
145 | if autoStart.IsValid {
146 | now := time.Now()
147 | return autoStart.StopTime < now.Unix()
148 | }
149 |
150 | // if manual start is used
151 | return !manStart.IsStarted
152 | }
153 |
154 | func IsCtfStarted() bool {
155 | /*
156 | var startInfo entities.StartStopInfo
157 | timeDb, _ := os.ReadFile("./database/time.json")
158 | json.Unmarshal(timeDb, &startInfo)*/
159 |
160 | autoStart := database.ReadAutoStart(database.GetOpenedDB())
161 | manStart := database.ReadManStart(database.GetOpenedDB())
162 |
163 | // if automatic start is used
164 | if autoStart.IsValid {
165 | now := time.Now()
166 | return autoStart.StartTime < now.Unix()
167 | }
168 |
169 | // if manual start is used
170 | return manStart.IsStarted
171 | }
172 |
--------------------------------------------------------------------------------
/ctfsrc/teammanagement.go:
--------------------------------------------------------------------------------
1 | package ctfsrc
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/Fabucik/ctf-portal/authentication"
8 | "github.com/Fabucik/ctf-portal/database"
9 | "github.com/Fabucik/ctf-portal/entities"
10 | "github.com/gin-gonic/gin"
11 | )
12 |
13 | func GetTeams(ctx *gin.Context) {
14 | var session entities.Session
15 | cookie, _ := ctx.Cookie("session")
16 | json.Unmarshal([]byte(cookie), &session)
17 |
18 | if !authentication.IsAdmin(ctx, session) {
19 | return
20 | }
21 | /*
22 | var users entities.Users
23 | teamDb, _ := os.ReadFile("./database/users.json")
24 | json.Unmarshal(teamDb, &users)*/
25 |
26 | users := database.ReadUsers(database.GetOpenedDB())
27 |
28 | ctx.JSON(http.StatusOK, users)
29 | }
30 |
31 | func DeleteTeam(ctx *gin.Context) {
32 | var session entities.Session
33 | cookie, _ := ctx.Cookie("session")
34 | json.Unmarshal([]byte(cookie), &session)
35 |
36 | if !authentication.IsAdmin(ctx, session) {
37 | return
38 | }
39 |
40 | var userToDelete entities.User
41 | ctx.Bind(&userToDelete)
42 |
43 | /*
44 | var users entities.Users
45 | userDb, _ := os.ReadFile("./database/users.json")
46 | json.Unmarshal(userDb, &users)
47 |
48 | var points entities.AllPoints
49 | pointDb, _ := os.ReadFile("./database/points.json")
50 | json.Unmarshal(pointDb, &points)
51 |
52 | var sessions entities.Sessions
53 | sessionDb, _ := os.ReadFile("./database/session-cookies.json")
54 | json.Unmarshal(sessionDb, &sessions)
55 |
56 | check := 0
57 | for i := 0; i < len(users.Users); i++ {
58 | // cannot delete admin user
59 | if userToDelete.Username == "admin" {
60 | ctx.JSON(http.StatusForbidden, gin.H{
61 | "message": "cannot delete admin",
62 | })
63 | }
64 |
65 | if userToDelete.Username == users.Users[i].Username {
66 | users.Users = append(users.Users[:i], users.Users[i+1:]...)
67 | check++
68 | }
69 | }
70 |
71 | if check == 0 {
72 | ctx.JSON(http.StatusConflict, gin.H{
73 | "message": "User does not exist",
74 | })
75 |
76 | return
77 | }
78 |
79 | for i := 0; i < len(points.Points); i++ {
80 | if userToDelete.Username == points.Points[i].Team {
81 | points.Points = append(points.Points[:i], points.Points[i+1:]...)
82 | }
83 | }
84 |
85 | for i := 0; i < len(sessions.Sessions); i++ {
86 | if userToDelete.Username == sessions.Sessions[i].Username {
87 | sessions.Sessions = append(sessions.Sessions[:i], sessions.Sessions[i+1:]...)
88 | }
89 | }
90 |
91 | writableUserJson, _ := json.MarshalIndent(&users, "", "\t")
92 | os.WriteFile("./database/users.json", writableUserJson, 0600)
93 |
94 | writablePointJson, _ := json.MarshalIndent(&points, "", "\t")
95 | os.WriteFile("./database/points.json", writablePointJson, 0600)
96 |
97 | writableSessionJson, _ := json.MarshalIndent(&sessions, "", "\t")
98 | os.WriteFile("./database/session-cookies.json", writableSessionJson, 0600)*/
99 |
100 | if userToDelete.Username == "admin" {
101 | ctx.JSON(http.StatusForbidden, gin.H{
102 | "message": "cannot delete admin",
103 | })
104 |
105 | return
106 | }
107 |
108 | if !authentication.DoesUserExists(userToDelete.Username) {
109 | ctx.JSON(http.StatusConflict, gin.H{
110 | "message": "User does not exist",
111 | })
112 |
113 | return
114 | }
115 |
116 | database.DeleteUser(database.GetOpenedDB(), userToDelete)
117 |
118 | ctx.JSON(http.StatusOK, gin.H{
119 | "message": "OK",
120 | })
121 | }
122 |
--------------------------------------------------------------------------------
/database/db.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "github.com/Fabucik/ctf-portal/entities"
5 | "gorm.io/driver/postgres"
6 | "gorm.io/gorm"
7 | )
8 |
9 | func InitDB() (*gorm.DB, error) {
10 | dsn := "host=localhost user=fanda dbname=ctfportal port=5432 sslmode=disable TimeZone=Europe/Prague"
11 | db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
12 | if err != nil {
13 | return nil, err
14 | }
15 |
16 | db.AutoMigrate(&entities.User{}, &entities.Announcement{}, &entities.Session{}, &entities.TeamPoints{}, &entities.AutomaticStart{}, &entities.ManualStart{})
17 |
18 | return db, nil
19 | }
20 |
21 | var DB, _ = InitDB()
22 |
23 | func GetOpenedDB() *gorm.DB {
24 | return DB
25 | }
26 |
27 | func CreateUser(db *gorm.DB, user entities.User) {
28 | db.Create(&user)
29 | }
30 |
31 | func UpdateUser(db *gorm.DB, user entities.User) {
32 | db.Save(&user)
33 | }
34 |
35 | func ReadUsers(db *gorm.DB) entities.Users {
36 | var users []entities.User
37 | db.Find(&users)
38 |
39 | var allUsers entities.Users
40 | allUsers.Users = users
41 | return allUsers
42 | }
43 |
44 | func DeleteUser(db *gorm.DB, user entities.User) {
45 | db.Delete(user)
46 | db.Delete(&entities.Session{}, "username = ?", user.Username)
47 | db.Delete(&entities.TeamPoints{}, "team = ?", user.Username)
48 | }
49 |
50 | func CreateAnnouncement(db *gorm.DB, announcement entities.Announcement) {
51 | db.Create(&announcement)
52 | }
53 |
54 | func ReadAnnouncements(db *gorm.DB) entities.Announcements {
55 | var announcements []entities.Announcement
56 |
57 | db.Find(&announcements)
58 |
59 | var allAnnouncements entities.Announcements
60 | allAnnouncements.Announcements = announcements
61 |
62 | return allAnnouncements
63 | }
64 |
65 | func DeleteAnnouncement(db *gorm.DB, announcement entities.Announcement) {
66 | db.Delete(&announcement)
67 | }
68 |
69 | func WriteSession(db *gorm.DB, session entities.Session) {
70 | db.Create(&session)
71 | }
72 |
73 | func ReadSessions(db *gorm.DB) entities.Sessions {
74 | var sessions []entities.Session
75 |
76 | db.Find(&sessions)
77 |
78 | var allSessions entities.Sessions
79 | allSessions.Sessions = sessions
80 | return allSessions
81 | }
82 |
83 | func CreateTeamPoints(db *gorm.DB, teamPoints entities.TeamPoints) {
84 | db.Create(&teamPoints)
85 | }
86 |
87 | // solved will be string split by ;
88 | func UpdatePoints(db *gorm.DB, teamPoints entities.TeamPoints) {
89 | db.Save(&teamPoints)
90 | }
91 |
92 | func ReadAllPoints(db *gorm.DB) entities.AllPoints {
93 | var points []entities.TeamPoints
94 |
95 | db.Find(&points)
96 |
97 | var allPoints entities.AllPoints
98 | allPoints.Points = points
99 | return allPoints
100 | }
101 |
102 | func ReadTeamPoints(db *gorm.DB, team string) entities.TeamPoints {
103 | var points entities.TeamPoints
104 |
105 | db.Last(&points, "team = ?", team)
106 |
107 | return points
108 | }
109 |
110 | func WriteAutoStart(db *gorm.DB, time entities.AutomaticStart) {
111 | db.Save(&time)
112 | }
113 |
114 | func ReadAutoStart(db *gorm.DB) entities.AutomaticStart {
115 | var start entities.AutomaticStart
116 |
117 | db.Last(&start)
118 |
119 | return start
120 | }
121 |
122 | func WriteManStart(db *gorm.DB, time entities.ManualStart) {
123 | db.Save(&time)
124 | }
125 |
126 | func ReadManStart(db *gorm.DB) entities.ManualStart {
127 | var start entities.ManualStart
128 |
129 | db.Last(&start)
130 |
131 | return start
132 | }
133 |
--------------------------------------------------------------------------------
/entities/announcements.go:
--------------------------------------------------------------------------------
1 | package entities
2 |
3 | type Announcement struct {
4 | Message string `json:"message"`
5 | ID int `json:"id"`
6 | }
7 |
8 | type Announcements struct {
9 | Announcements []Announcement `json:"announcements"`
10 | }
11 |
--------------------------------------------------------------------------------
/entities/backup.go:
--------------------------------------------------------------------------------
1 | package entities
2 |
3 | type Backup struct {
4 | Base64 string `json:"base64"`
5 | }
6 |
--------------------------------------------------------------------------------
/entities/challenge.go:
--------------------------------------------------------------------------------
1 | package entities
2 |
3 | type Challenge struct {
4 | Name string `json:"name"`
5 | Files []ChallengeFile `json:"files"`
6 | Flag string `json:"flag"`
7 | Points int `json:"points"`
8 | Description string `json:"description"`
9 | CountryCode string `json:"country"`
10 | }
11 |
12 | type ChallengeFile struct {
13 | FileName string `json:"filename"`
14 | Base64 string `json:"base64"`
15 | }
16 |
17 | type Challenges struct {
18 | Challenges []Challenge `json:"challenges"`
19 | }
20 |
21 | // this will be sent to the client, basically challenge struct stripped by the flag
22 | type ReturnChallenge struct {
23 | Name string `json:"name"`
24 | Files []ChallengeFile `json:"files"`
25 | Points int `json:"points"`
26 | Description string `json:"description"`
27 | CountryCode string `json:"country"`
28 | }
29 |
30 | type ReturnChallenges struct {
31 | Challenges []ReturnChallenge `json:"challenges"`
32 | }
33 |
--------------------------------------------------------------------------------
/entities/flag.go:
--------------------------------------------------------------------------------
1 | package entities
2 |
3 | type Flag struct {
4 | Challenge string `json:"challenge"`
5 | Value string `json:"value"`
6 | }
7 |
--------------------------------------------------------------------------------
/entities/points.go:
--------------------------------------------------------------------------------
1 | package entities
2 |
3 | type TeamPoints struct {
4 | Team string `json:"team"`
5 | PointAmount int `json:"points"`
6 | Solved string `json:"solved"`
7 | ID int `json:"id"`
8 | }
9 |
10 | type AllPoints struct {
11 | Points []TeamPoints `json:"allpoints"`
12 | }
13 |
--------------------------------------------------------------------------------
/entities/sessions.go:
--------------------------------------------------------------------------------
1 | package entities
2 |
3 | type Session struct {
4 | ID string `json:"id"`
5 | Username string `json:"username"`
6 | ExpiresIn int64 `json:"expiresin"`
7 | }
8 |
9 | type Sessions struct {
10 | Sessions []Session `json:"sessions"`
11 | }
12 |
--------------------------------------------------------------------------------
/entities/startstop.go:
--------------------------------------------------------------------------------
1 | package entities
2 |
3 | type AutomaticStart struct {
4 | StartTime int64 `json:"starttime"`
5 | StopTime int64 `json:"stoptime"`
6 | // false if manual start is used
7 | IsValid bool `json:"isvalid"`
8 | ID int `json:"id"`
9 | }
10 |
11 | type ManualStart struct {
12 | IsStarted bool `json:"isstarted"`
13 |
14 | // false if automatic start is used
15 | IsValid bool `json:"isvalid"`
16 | ID int `json:"id"`
17 | }
18 |
19 | // this is used to write both of entries to the "database"
20 | type StartStopInfo struct {
21 | Automatic AutomaticStart `json:"automatic"`
22 | Manual ManualStart `json:"manual"`
23 | }
24 |
--------------------------------------------------------------------------------
/entities/users.go:
--------------------------------------------------------------------------------
1 | package entities
2 |
3 | type User struct {
4 | Username string `json:"username"`
5 | Password string `json:"password"`
6 | ID int `json:"id"`
7 | }
8 |
9 | type Users struct {
10 | Users []User `json:"users"`
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | node_modules
3 | jsconfig.json
4 | package-lock.json
5 | package.json
6 | svelte.config.js
7 | vite.config.js
8 | postcss.config.cjs
9 | tailwind.config.cjs
10 | .vscode/
11 |
--------------------------------------------------------------------------------
/frontend/dist_collected/CTF.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CTF
6 |
7 |
8 |
9 |
10 |
11 |
12 |
244 |
380 |
381 |
382 |
383 | LEADERBOARD:
384 |
385 |
386 | ANNOUNCMENTS:
387 |
388 |
389 |
390 |
391 |
392 |
393 |
Challange Description
394 |
395 |
396 |
397 |
398 |
399 |
611 |
--------------------------------------------------------------------------------
/frontend/dist_collected/admin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + Svelte
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/frontend/dist_collected/assets/index-357cb75c.css:
--------------------------------------------------------------------------------
1 | *,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.ml-10{margin-left:2.5rem}.ml-2{margin-left:.5rem}.ml-5{margin-left:1.25rem}.mr-4{margin-right:1rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-8{margin-top:2rem}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-20{height:5rem}.h-64{height:16rem}.h-9{height:2.25rem}.h-full{height:100%}.h-screen{height:100vh}.w-3\/4{width:75%}.w-32{width:8rem}.w-40{width:10rem}.w-48{width:12rem}.w-52{width:13rem}.w-56{width:14rem}.w-64{width:16rem}.w-7\/12{width:58.333333%}.w-full{width:100%}.w-screen{width:100vw}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.place-content-center{place-content:center}.place-content-start{place-content:start}.place-content-end{place-content:end}.place-items-center{place-items:center}.justify-center{justify-content:center}.overflow-hidden{overflow:hidden}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-violet-500{--tw-border-opacity: 1;border-color:rgb(139 92 246 / var(--tw-border-opacity))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity))}.bg-violet-700{--tw-bg-opacity: 1;background-color:rgb(109 40 217 / var(--tw-bg-opacity))}.bg-violet-800{--tw-bg-opacity: 1;background-color:rgb(91 33 182 / var(--tw-bg-opacity))}.fill-gray-300{fill:#d1d5db}.p-10{padding:2.5rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.pt-7{padding-top:1.75rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity: 1;color:rgb(249 250 251 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.outline-violet-500{outline-color:#8b5cf6}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:border-4:hover{border-width:4px}.hover\:bg-violet-700:hover{--tw-bg-opacity: 1;background-color:rgb(109 40 217 / var(--tw-bg-opacity))}.hover\:bg-violet-900:hover{--tw-bg-opacity: 1;background-color:rgb(76 29 149 / var(--tw-bg-opacity))}@media (min-width: 1024px){.lg\:pl-64{padding-left:16rem}.lg\:pr-64{padding-right:16rem}}#body.svelte-deiqed{background-color:#161616;opacity:.8;background-size:10px 10px;background-image:repeating-linear-gradient(45deg,#2e2e2e 0,#2e2e2e 1px,#040404 0,#010101 50%)}
2 |
--------------------------------------------------------------------------------
/frontend/dist_collected/assets/index-860e7d9b.js:
--------------------------------------------------------------------------------
1 | (function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))a(s);new MutationObserver(s=>{for(const r of s)if(r.type==="childList")for(const h of r.addedNodes)h.tagName==="LINK"&&h.rel==="modulepreload"&&a(h)}).observe(document,{childList:!0,subtree:!0});function l(s){const r={};return s.integrity&&(r.integrity=s.integrity),s.referrerPolicy&&(r.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?r.credentials="include":s.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function a(s){if(s.ep)return;s.ep=!0;const r=l(s);fetch(s.href,r)}})();function J(){}function X(t,e){for(const l in e)t[l]=e[l];return t}function Ye(t){return t()}function De(){return Object.create(null)}function ae(t){t.forEach(Ye)}function Ze(t){return typeof t=="function"}function ge(t,e){return t!=t?e==e:t!==e||t&&typeof t=="object"||typeof t=="function"}function ot(t){return Object.keys(t).length===0}function ze(t){const e={};for(const l in t)l[0]!=="$"&&(e[l]=t[l]);return e}function se(t,e){const l={};e=new Set(e);for(const a in t)!e.has(a)&&a[0]!=="$"&&(l[a]=t[a]);return l}function o(t,e){t.appendChild(e)}function K(t,e,l){t.insertBefore(e,l||null)}function G(t){t.parentNode&&t.parentNode.removeChild(t)}function Me(t,e){for(let l=0;lt.removeEventListener(e,l,a)}function i(t,e,l){l==null?t.removeAttribute(e):t.getAttribute(e)!==l&&t.setAttribute(e,l)}function ie(t,e){for(const l in e)i(t,l,e[l])}function $e(t){return t===""?null:+t}function st(t){return Array.from(t.childNodes)}function et(t,e){e=""+e,t.wholeText!==e&&(t.data=e)}function H(t,e){t.value=e??""}let Be;function ke(t){Be=t}function Te(t,e){const l=t.$$.callbacks[e.type];l&&l.slice().forEach(a=>a.call(this,e))}const we=[],Je=[],Oe=[],Ue=[],it=Promise.resolve();let Le=!1;function at(){Le||(Le=!0,it.then(tt))}function Ne(t){Oe.push(t)}const je=new Set;let be=0;function tt(){if(be!==0)return;const t=Be;do{try{for(;be{Fe.delete(t),a&&(l&&t.d(1),a())}),t.o(e)}else a&&a()}function Se(t,e){const l={},a={},s={$$scope:1};let r=t.length;for(;r--;){const h=t[r],c=e[r];if(c){for(const d in h)d in c||(a[d]=1);for(const d in c)s[d]||(l[d]=c[d],s[d]=1);t[r]=c}else for(const d in h)s[d]=1}for(const h in a)h in l||(l[h]=void 0);return l}function me(t){t&&t.c()}function ce(t,e,l,a){const{fragment:s,after_update:r}=t.$$;s&&s.m(e,l),a||Ne(()=>{const h=t.$$.on_mount.map(Ye).filter(Ze);t.$$.on_destroy?t.$$.on_destroy.push(...h):ae(h),t.$$.on_mount=[]}),r.forEach(Ne)}function ue(t,e){const l=t.$$;l.fragment!==null&&(ae(l.on_destroy),l.fragment&&l.fragment.d(e),l.on_destroy=l.fragment=null,l.ctx=[])}function ft(t,e){t.$$.dirty[0]===-1&&(we.push(t),at(),t.$$.dirty.fill(0)),t.$$.dirty[e/31|0]|=1<{const k=w.length?w[0]:_;return n.ctx&&s(n.ctx[x],n.ctx[x]=k)&&(!n.skip_bound&&n.bound[x]&&n.bound[x](k),f&&ft(t,x)),_}):[],n.update(),f=!0,ae(n.before_update),n.fragment=a?a(n.ctx):!1,e.target){if(e.hydrate){const x=st(e.target);n.fragment&&n.fragment.l(x),x.forEach(G)}else n.fragment&&n.fragment.c();e.intro&&oe(t.$$.fragment),ce(t,e.target,e.anchor,e.customElement),tt()}ke(d)}class ve{$destroy(){ue(this,1),this.$destroy=J}$on(e,l){if(!Ze(l))return J;const a=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return a.push(l),()=>{const s=a.indexOf(l);s!==-1&&a.splice(s,1)}}$set(e){this.$$set&&!ot(e)&&(this.$$.skip_bound=!0,this.$$set(e),this.$$.skip_bound=!1)}}function dt(t){let e,l,a,s,r,h,c=[{xmlns:"http://www.w3.org/2000/svg"},{viewBox:"0 0 24 24"},{width:t[0]},{height:t[0]},{fill:t[1]},{class:s="remixicon "+t[2]},t[3]],d={};for(let n=0;n{e=X(X({},e),ze(n)),l(3,s=se(e,a)),"size"in n&&l(0,r=n.size),"color"in n&&l(1,h=n.color),"class"in n&&l(2,c=n.class)},[r,h,c,s,d]}class lt extends ve{constructor(e){super(),pe(this,e,ht,dt,ge,{size:0,color:1,class:2})}}function mt(t){let e,l,a,s,r,h,c=[{xmlns:"http://www.w3.org/2000/svg"},{viewBox:"0 0 24 24"},{width:t[0]},{height:t[0]},{fill:t[1]},{class:s="remixicon "+t[2]},t[3]],d={};for(let n=0;n{e=X(X({},e),ze(n)),l(3,s=se(e,a)),"size"in n&&l(0,r=n.size),"color"in n&&l(1,h=n.color),"class"in n&&l(2,c=n.class)},[r,h,c,s,d]}class pt extends ve{constructor(e){super(),pe(this,e,gt,mt,ge,{size:0,color:1,class:2})}}function vt(t){let e,l,a,s,r,h,c=[{xmlns:"http://www.w3.org/2000/svg"},{viewBox:"0 0 24 24"},{width:t[0]},{height:t[0]},{fill:t[1]},{class:s="remixicon "+t[2]},t[3]],d={};for(let n=0;n{e=X(X({},e),ze(n)),l(3,s=se(e,a)),"size"in n&&l(0,r=n.size),"color"in n&&l(1,h=n.color),"class"in n&&l(2,c=n.class)},[r,h,c,s,d]}class _t extends ve{constructor(e){super(),pe(this,e,xt,vt,ge,{size:0,color:1,class:2})}}function bt(t){let e,l,a,s,r,h,c=[{xmlns:"http://www.w3.org/2000/svg"},{viewBox:"0 0 24 24"},{width:t[0]},{height:t[0]},{fill:t[1]},{class:s="remixicon "+t[2]},t[3]],d={};for(let n=0;n{e=X(X({},e),ze(n)),l(3,s=se(e,a)),"size"in n&&l(0,r=n.size),"color"in n&&l(1,h=n.color),"class"in n&&l(2,c=n.class)},[r,h,c,s,d]}class yt extends ve{constructor(e){super(),pe(this,e,wt,bt,ge,{size:0,color:1,class:2})}}function Ct(t){let e,l,a,s,r,h,c=[{xmlns:"http://www.w3.org/2000/svg"},{viewBox:"0 0 24 24"},{width:t[0]},{height:t[0]},{fill:t[1]},{class:s="remixicon "+t[2]},t[3]],d={};for(let n=0;n{e=X(X({},e),ze(n)),l(3,s=se(e,a)),"size"in n&&l(0,r=n.size),"color"in n&&l(1,h=n.color),"class"in n&&l(2,c=n.class)},[r,h,c,s,d]}class zt extends ve{constructor(e){super(),pe(this,e,kt,Ct,ge,{size:0,color:1,class:2})}}function Ve(t,e,l){const a=t.slice();return a[47]=e[l],a}function qe(t,e,l){const a=t.slice();return a[39]=e[l],a}function Ge(t,e,l){const a=t.slice();return a[42]=e[l],a}function Re(t,e,l){const a=t.slice();return a[39]=e[l],a}function Tt(t){let e,l,a,s,r,h,c,d,n,f,x,_,w,k,S,B,z,F,M,P,I,L,D,A,T=t[1],v=[];for(let g=0;gDelete',i(l,"class","text-white text-2xl font-mono"),i(s,"type","text"),i(s,"class","w-full h-10 rounded-xl p-2 bg-gray-800 text-gray-50 flex place-content-center"),s.value=r=t[42].name,s.disabled=!0,i(n,"class","text-white text-2xl font-mono"),i(x,"type","text"),i(x,"class","w-full h-10 rounded-xl p-2 bg-gray-800 text-gray-50 flex place-content-center"),x.value=_=t[42].description,x.disabled=!0,i(S,"class","text-white text-2xl font-mono"),i(z,"type","text"),i(z,"class","w-48 h-10 rounded-xl p-2 bg-gray-800 text-gray-50 flex place-content-center text-center"),z.value=F=t[42].points,z.disabled=!0,i(k,"class","ml-2"),i(c,"class","flex"),i(P,"class","text-white text-xl font-mono mt-3"),i(D,"class","w-full flex place-content-end"),i(e,"class","w-full bg-black border border-violet-500 rounded-xl mt-2 p-10 pt-7")},m(v,g){K(v,e,g),o(e,l),o(e,a),o(e,s),o(e,h),o(e,c),o(c,d),o(d,n),o(d,f),o(d,x),o(c,w),o(c,k),o(k,S),o(k,B),o(k,z),o(e,M),o(e,P),o(e,I);for(let b=0;bAdmin page',s=m(),r=u("div"),h=u("div"),c=u("div"),me(d.$$.fragment),n=m(),f=u("h1"),f.textContent="General",x=m(),_=u("div"),me(w.$$.fragment),k=m(),S=u("h1"),S.textContent="Announcment",B=m(),z=u("div"),me(F.$$.fragment),M=m(),P=u("h1"),P.textContent="Challanges",I=m(),L=u("div"),me(D.$$.fragment),A=m(),T=u("h1"),T.textContent="Teams",v=m(),b&&b.c(),i(a,"id","header"),i(a,"class","grid place-content-center w-screen h-20 bg-black outline outline-violet-500"),i(f,"class","text-xl text-gray-300 font-mono"),i(c,"class","flex w-52 transition-all hover:bg-violet-900 p-3 rounded-xl ml-2 mt-10"),i(S,"class","text-xl text-gray-300 font-mono"),i(_,"class","flex w-52 transition-all hover:bg-violet-900 p-3 rounded-xl ml-2 mt-1"),i(P,"class","text-xl text-gray-300 font-mono"),i(z,"class","flex w-52 transition-all hover:bg-violet-900 p-3 rounded-xl ml-2 mt-1"),i(T,"class","text-xl text-gray-300 font-mono"),i(L,"class","flex w-52 transition-all hover:bg-violet-900 p-3 rounded-xl ml-2 mt-1"),i(h,"class","grid place-content-start w-56 h-screen bg-black"),i(r,"class","flex"),i(l,"id","body"),i(l,"class","w-full h-full overflow-hidden svelte-deiqed")},m(C,Q){K(C,e,Q),o(e,l),o(l,a),o(l,s),o(l,r),o(r,h),o(h,c),ce(d,c,null),o(c,n),o(c,f),o(h,x),o(h,_),ce(w,_,null),o(_,k),o(_,S),o(h,B),o(h,z),ce(F,z,null),o(z,M),o(z,P),o(h,I),o(h,L),ce(D,L,null),o(L,A),o(L,T),o(r,v),~g&&V[g].m(r,null),E=!0,U||(R=[j(c,"click",t[12]),j(_,"click",t[13]),j(z,"click",t[14]),j(L,"click",t[15])],U=!0)},p(C,Q){let Y=g;g=ee(C),g===Y?~g&&V[g].p(C,Q):(b&&(ct(),re(V[Y],1,1,()=>{V[Y]=null}),ut()),~g?(b=V[g],b?b.p(C,Q):(b=V[g]=q[g](C),b.c()),oe(b,1),b.m(r,null)):b=null)},i(C){E||(oe(d.$$.fragment,C),oe(w.$$.fragment,C),oe(F.$$.fragment,C),oe(D.$$.fragment,C),oe(b),E=!0)},o(C){re(d.$$.fragment,C),re(w.$$.fragment,C),re(F.$$.fragment,C),re(D.$$.fragment,C),re(b),E=!1},d(C){C&&G(e),ue(d),ue(w),ue(F),ue(D),~g&&V[g].d(),U=!1,ae(R)}}}let nt=0;function Pt(){document.getElementById("import").click()}function At(){fetch("/manualstart",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({isstarted:!0})}).then(t=>{t.status==200?alert("CTF started"):alert("💀")})}function jt(){fetch("/manualstart",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({isstarted:!1})}).then(t=>{t.status==200?alert("CTF stopped"):alert("💀")})}function Lt(t,e,l){let a=[!1,!1,!1,!1],s=[{name:"",password:"",score:0,solved:[]}],r=[],h=[],c=[{name:"",files:h,flag:"",points:0,description:"",country:""}],d="",n="",f,x,_,w,k,S=0;function B(){for(let p=0;pp.json()).then(p=>{l(1,s=p.users)})}function P(){let p=new Date(_+" "+f).getTime()/1e3,N=new Date(w+" "+x).getTime()/1e3;p{te.status==200?alert("Time added"):alert("💀")}):p>N?alert("Start time must be before end time"):p==N?alert("CTF has to run at leats 1 second"):alert("Enter a valid time")}function I(){fetch("/announcement").then(p=>p.json()).then(p=>{S=p.announcements.length}),S==0?S=0:S=S+1,fetch("/announcement",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:k,id:S})}).then(p=>{p.status==200?alert("Announcment posted"):alert("💀")})}function L(){document.getElementById("makeChallangeFile").click(),document.querySelector("#makeChallangeFile").addEventListener("change",N=>{const te=N.target.files[0],ne=new FileReader;ne.onloadend=()=>{const de=ne.result.replace("data:","").replace(/^.+,/,"");h.push({filename:te.name,base64:de}),console.log(c[0])},ne.readAsDataURL(te)}),l(3,h)}function D(){l(4,c),fetch("/upload",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:c[0].name,files:c[0].files,flag:c[0].flag,points:c[0].points,description:c[0].description,country:c[0].country})}).then(p=>{p.status==200?alert("Challange added"):alert("💀")})}function A(){fetch("/register",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:d,password:n})}).then(p=>{p.status==200?alert("Team added"):alert("Team already exists")}).then(p=>{fetch("/teams").then(N=>N.json()).then(N=>{l(1,s=N.users)})})}function T(p){var N=s[p].username;fetch("/teams",{method:"DELETE",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:N})}).then(te=>{te.status==200?alert("Team deleted"):alert("💀")}).then(te=>{fetch("/teams").then(ne=>ne.json()).then(ne=>{l(1,s=ne.users)})})}function v(){_=this.value,l(9,_)}function g(){f=this.value,l(7,f)}function b(){w=this.value,l(10,w)}function E(){x=this.value,l(8,x)}function U(){k=this.value,l(11,k)}function R(){c[0].name=this.value,l(4,c)}function q(){c[0].description=this.value,l(4,c)}function V(){c[0].country=this.value,l(4,c)}function ee(){c[0].flag=this.value,l(4,c)}function C(){c[0].points=$e(this.value),l(4,c)}const Q=()=>T(nt);function Y(){d=this.value,l(5,d)}function fe(){n=this.value,l(6,n)}return[a,s,r,h,c,d,n,f,x,_,w,k,B,z,F,M,P,I,L,D,A,T,v,g,b,E,U,R,q,V,ee,C,Q,Y,fe]}class Nt extends ve{constructor(e){super(),pe(this,e,Lt,Mt,ge,{},null,[-1,-1])}}function Bt(t){let e,l,a;return l=new Nt({}),{c(){e=u("main"),me(l.$$.fragment)},m(s,r){K(s,e,r),ce(l,e,null),a=!0},p:J,i(s){a||(oe(l.$$.fragment,s),a=!0)},o(s){re(l.$$.fragment,s),a=!1},d(s){s&&G(e),ue(l)}}}class Ht extends ve{constructor(e){super(),pe(this,e,null,Bt,ge,{})}}new Ht({target:document.getElementById("app")});
2 |
--------------------------------------------------------------------------------
/frontend/dist_collected/assets/index-bbff9fea.js:
--------------------------------------------------------------------------------
1 | (function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))o(r);new MutationObserver(r=>{for(const l of r)if(l.type==="childList")for(const i of l.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&o(i)}).observe(document,{childList:!0,subtree:!0});function n(r){const l={};return r.integrity&&(l.integrity=r.integrity),r.referrerPolicy&&(l.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?l.credentials="include":r.crossOrigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function o(r){if(r.ep)return;r.ep=!0;const l=n(r);fetch(r.href,l)}})();function $(){}function K(e){return e()}function F(){return Object.create(null)}function E(e){e.forEach(K)}function W(e){return typeof e=="function"}function k(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}function X(e){return Object.keys(e).length===0}function c(e,t){e.appendChild(t)}function z(e,t,n){e.insertBefore(t,n||null)}function M(e){e.parentNode&&e.parentNode.removeChild(e)}function d(e){return document.createElement(e)}function Y(e){return document.createTextNode(e)}function w(){return Y(" ")}function S(e,t,n,o){return e.addEventListener(t,n,o),()=>e.removeEventListener(t,n,o)}function u(e,t,n){n==null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}function Z(e){return Array.from(e.childNodes)}function C(e,t){e.value=t??""}let q;function O(e){q=e}const y=[],H=[],N=[],J=[],ee=Promise.resolve();let T=!1;function te(){T||(T=!0,ee.then(D))}function I(e){N.push(e)}const j=new Set;let _=0;function D(){if(_!==0)return;const e=q;do{try{for(;_{A.delete(e),o&&(n&&e.d(1),o())}),e.o(t)}else o&&o()}function se(e){e&&e.c()}function Q(e,t,n,o){const{fragment:r,after_update:l}=e.$$;r&&r.m(t,n),o||I(()=>{const i=e.$$.on_mount.map(K).filter(W);e.$$.on_destroy?e.$$.on_destroy.push(...i):E(i),e.$$.on_mount=[]}),l.forEach(I)}function R(e,t){const n=e.$$;n.fragment!==null&&(E(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function ie(e,t){e.$$.dirty[0]===-1&&(y.push(e),te(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<{const x=f.length?f[0]:v;return s.ctx&&r(s.ctx[a],s.ctx[a]=x)&&(!s.skip_bound&&s.bound[a]&&s.bound[a](x),b&&ie(e,a)),v}):[],s.update(),b=!0,E(s.before_update),s.fragment=o?o(s.ctx):!1,t.target){if(t.hydrate){const a=Z(t.target);s.fragment&&s.fragment.l(a),a.forEach(M)}else s.fragment&&s.fragment.c();t.intro&&G(e.$$.fragment),Q(e,t.target,t.anchor,t.customElement),D()}O(m)}class V{$destroy(){R(this,1),this.$destroy=$}$on(t,n){if(!W(n))return $;const o=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return o.push(n),()=>{const r=o.indexOf(n);r!==-1&&o.splice(r,1)}}$set(t){this.$$set&&!X(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}function le(e){let t,n,o,r,l,i,p,m,s,b,a,v,f,x,g,P,B;return{c(){t=d("main"),n=d("div"),o=d("div"),r=d("h1"),r.textContent="CTF Login",l=w(),i=d("div"),p=d("label"),p.textContent="team_name",m=w(),s=d("input"),b=w(),a=d("label"),a.textContent="password",v=w(),f=d("input"),x=w(),g=d("button"),g.innerHTML='Login
',u(r,"class","text-gray-300 text-4xl text-center font-bold mb-3 font-mono"),u(p,"class","text-gray-300 font-mono mb-1"),u(p,"for","username"),u(s,"class","rounded-lg h-9 w-full p-2 transition-all bg-black hover:bg-violet-900 text-gray-300"),u(s,"type","text"),u(s,"name","username"),u(s,"id","username"),u(a,"class","text-gray-300 font-mono mt-2 mb-1"),u(a,"for","password"),u(f,"class","rounded-lg h-9 w-full p-2 transition-all bg-black hover:bg-violet-900 text-gray-300"),u(f,"type","password"),u(f,"name","password"),u(f,"id","password"),u(g,"class","bg-gray-900 w-32 h-12 rounded-xl transition-all border-2 hover:border-4 border-violet-500 mt-2"),u(g,"type","submit"),u(i,"class","grid place-items-center"),u(o,"class","w-64"),u(n,"id","body"),u(n,"class","grid w-screen h-screen place-content-center svelte-deiqed")},m(h,L){z(h,t,L),c(t,n),c(n,o),c(o,r),c(o,l),c(o,i),c(i,p),c(i,m),c(i,s),C(s,e[0]),c(i,b),c(i,a),c(i,v),c(i,f),C(f,e[1]),c(i,x),c(i,g),P||(B=[S(s,"input",e[3]),S(f,"input",e[4]),S(g,"click",e[2])],P=!0)},p(h,[L]){L&1&&s.value!==h[0]&&C(s,h[0]),L&2&&f.value!==h[1]&&C(f,h[1])},i:$,o:$,d(h){h&&M(t),P=!1,E(B)}}}function ue(e,t,n){let o,r;function l(){fetch("/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:o,password:r})}).then(m=>{m.status==200?window.location.href="/ctf":alert("Wrong username or password")})}function i(){o=this.value,n(0,o)}function p(){r=this.value,n(1,r)}return[o,r,l,i,p]}class ae extends V{constructor(t){super(),U(this,t,ue,le,k,{})}}function ce(e){let t,n,o;return n=new ae({}),{c(){t=d("main"),se(n.$$.fragment)},m(r,l){z(r,t,l),Q(n,t,null),o=!0},p:$,i(r){o||(G(n.$$.fragment,r),o=!0)},o(r){oe(n.$$.fragment,r),o=!1},d(r){r&&M(t),R(n)}}}class fe extends V{constructor(t){super(),U(this,t,null,ce,k,{})}}new fe({target:document.getElementById("app")});
2 |
--------------------------------------------------------------------------------
/frontend/dist_collected/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + Svelte
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + Svelte
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/frontend/src/App.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/frontend/src/app.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/frontend/src/lib/Admin.svelte:
--------------------------------------------------------------------------------
1 |
271 |
272 |
273 |
276 |
277 |
278 |
279 |
280 |
General
281 |
282 |
283 |
284 |
Announcment
285 |
286 |
287 |
288 |
Challanges
289 |
290 |
291 |
292 |
Teams
293 |
294 |
295 | {#if pages[0] == false && pages[1] == false && pages[2] == false && pages[3] == false}
296 |
Later...
297 | {:else if pages[0] == true}
298 |
299 |
General
300 |
312 |
313 |
314 |
315 |
Manual control:
316 |
317 |
318 |
319 |
320 |
Import/Export Challanges:
321 |
322 |
323 |
324 |
325 |
326 |
327 | {:else if pages[1] == true}
328 |
329 |
Announcment
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 | {:else if pages[2] == true}
339 |
340 |
Challanges
341 |
342 | {#each challanges as challange}
343 |
344 |
Name:
345 |
346 |
356 |
Files:
357 | {#each challange.files as file}
358 |
{file.name}
359 | {/each}
360 |
361 |
362 |
363 |
364 | {/each}
365 |
368 |
369 |
383 |
393 |
Files:
394 | {#each files as file}
395 |
{file.name}
396 | {/each}
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 | {:else if pages[3] == true}
406 |
407 |
Teams
408 |
409 | {#each teams as team}
410 |
411 |
Username:
412 |
413 |
Password:
414 |
415 |
416 |
417 | {teamIndex += 1}
418 |
419 |
420 | {/each}
421 |
424 |
434 |
435 |
436 |
437 |
438 |
439 | {/if}
440 |
441 |
442 |
443 |
--------------------------------------------------------------------------------
/frontend/src/lib/Index.svelte:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
CTF Login
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import './app.css'
2 | import App from './App.svelte'
3 |
4 | const app = new App({
5 | target: document.getElementById('app'),
6 | })
7 |
8 | export default app
9 |
--------------------------------------------------------------------------------
/frontend_dev/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist/
3 |
4 | # generated types
5 | .astro/
6 |
7 | # dependencies
8 | node_modules/
9 |
10 | # logs
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # environment variables
17 | .env
18 | .env.production
19 |
20 | # macOS-specific files
21 | .DS_Store
22 |
--------------------------------------------------------------------------------
/frontend_dev/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["astro-build.astro-vscode"],
3 | "unwantedRecommendations": []
4 | }
5 |
--------------------------------------------------------------------------------
/frontend_dev/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "command": "./node_modules/.bin/astro dev",
6 | "name": "Development server",
7 | "request": "launch",
8 | "type": "node-terminal"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/frontend_dev/README.md:
--------------------------------------------------------------------------------
1 | # Astro Starter Kit: Basics
2 |
3 | ```
4 | npm create astro@latest -- --template basics
5 | ```
6 |
7 | [](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
8 | [](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics)
9 | [](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json)
10 |
11 | > 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
12 |
13 | 
14 |
15 |
16 | ## 🚀 Project Structure
17 |
18 | Inside of your Astro project, you'll see the following folders and files:
19 |
20 | ```
21 | /
22 | ├── public/
23 | │ └── favicon.svg
24 | ├── src/
25 | │ ├── components/
26 | │ │ └── Card.astro
27 | │ ├── layouts/
28 | │ │ └── Layout.astro
29 | │ └── pages/
30 | │ └── index.astro
31 | └── package.json
32 | ```
33 |
34 | Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
35 |
36 | There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
37 |
38 | Any static assets, like images, can be placed in the `public/` directory.
39 |
40 | ## 🧞 Commands
41 |
42 | All commands are run from the root of the project, from a terminal:
43 |
44 | | Command | Action |
45 | | :--------------------- | :----------------------------------------------- |
46 | | `npm install` | Installs dependencies |
47 | | `npm run dev` | Starts local dev server at `localhost:3000` |
48 | | `npm run build` | Build your production site to `./dist/` |
49 | | `npm run preview` | Preview your build locally, before deploying |
50 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
51 | | `npm run astro --help` | Get help using the Astro CLI |
52 |
53 | ## 👀 Want to learn more?
54 |
55 | Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
56 |
--------------------------------------------------------------------------------
/frontend_dev/astro.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'astro/config';
2 | import react from "@astrojs/react";
3 | import tailwind from "@astrojs/tailwind";
4 | import node from "@astrojs/node";
5 |
6 | import svelte from "@astrojs/svelte";
7 |
8 | // https://astro.build/config
9 | export default defineConfig({
10 | integrations: [react(), tailwind(), svelte()],
11 | output: "server",
12 | adapter: node({
13 | mode: "standalone"
14 | })
15 | });
--------------------------------------------------------------------------------
/frontend_dev/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend-dev",
3 | "type": "module",
4 | "version": "0.0.1",
5 | "scripts": {
6 | "dev": "astro dev",
7 | "start": "astro dev",
8 | "build": "astro build",
9 | "preview": "astro preview",
10 | "astro": "astro"
11 | },
12 | "dependencies": {
13 | "@astrojs/node": "^5.1.1",
14 | "@astrojs/react": "^2.1.1",
15 | "@astrojs/svelte": "^2.1.0",
16 | "@astrojs/tailwind": "^3.1.1",
17 | "@types/react": "^18.0.21",
18 | "@types/react-dom": "^18.0.6",
19 | "astro": "^2.2.0",
20 | "d3": "^7.8.4",
21 | "globe.gl": "^2.27.1",
22 | "react": "^18.0.0",
23 | "react-dom": "^18.0.0",
24 | "react-globe.gl": "^2.23.3",
25 | "svelte": "^3.54.0",
26 | "svelte-remixicon": "^1.0.1",
27 | "tailwindcss": "^3.0.24"
28 | },
29 | "devDependencies": {
30 | "tailwindcss-bg-patterns": "^0.2.0"
31 | }
32 | }
--------------------------------------------------------------------------------
/frontend_dev/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/frontend_dev/src/components/admin.svelte:
--------------------------------------------------------------------------------
1 |
279 |
280 |
281 |
284 |
285 |
286 |
287 |
288 |
General
289 |
290 |
291 |
292 |
Announcment
293 |
294 |
295 |
296 |
Challanges
297 |
298 |
299 |
300 |
Teams
301 |
302 |
303 | {#if pages[0] == false && pages[1] == false && pages[2] == false && pages[3] == false}
304 |
Later...
305 | {:else if pages[0] == true}
306 |
307 |
General
308 |
320 |
321 |
322 |
323 |
Manual control:
324 |
325 |
326 |
327 |
328 |
Import/Export Challanges:
329 |
330 |
331 |
332 |
333 |
334 |
335 | {:else if pages[1] == true}
336 |
337 |
Announcment
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 | {:else if pages[2] == true}
347 |
348 |
Challanges
349 |
350 | {#each challanges as challange}
351 |
352 |
Name:
353 |
354 |
364 |
Files:
365 | {#each challange.files as file}
366 |
{file.name}
367 | {/each}
368 |
369 |
370 |
371 |
372 | {/each}
373 |
374 |
375 |
376 |
390 |
400 |
Files:
401 | {#each files as file}
402 |
{file.name}
403 | {/each}
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 | {:else if pages[3] == true}
413 |
414 |
Teams
415 |
416 | {#each teams as team}
417 |
418 |
Username:
419 |
420 |
Password:
421 |
422 |
423 |
424 | {teamIndex += 1}
425 |
426 |
427 | {/each}
428 |
429 |
430 |
440 |
441 |
442 |
443 |
444 |
445 | {/if}
446 |
447 |
448 |
449 |
--------------------------------------------------------------------------------
/frontend_dev/src/components/ctfstats.svelte:
--------------------------------------------------------------------------------
1 |
75 |
76 |
77 |
78 |
79 |
Announcements:
80 | {#each announcements as announcement}
81 | admin: {announcement.message}
82 | {/each}
83 |
84 |
85 |
Leaderboard:
86 | {#each teams as team, order}
87 | {order + 1}#{team.team}{team.points}
88 | {/each}
89 |
90 |
91 |
TIME LEFT
92 |
{days}:{hours}:{minutes}:{seconds}
93 |
94 |
days
95 |
hours
96 |
minutes
97 |
seconds
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/frontend_dev/src/components/globe.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React, { useState, useEffect, createElement } from 'react';
3 | import Globe from 'react-globe.gl';
4 |
5 | let challenges = [];
6 |
7 | function isCountryUsed(country) {
8 | for (let i = 0; i < challenges.length; i++) {
9 | if (country === challenges[i].country.toUpperCase()) {
10 | return true;
11 | }
12 | }
13 | return false;
14 | }
15 |
16 | function fetchChallenges() {
17 | fetch('/api/challenges').then(res => res.json()).then(data => {
18 | if (data.status === 401) {
19 | alert('CTF is not yet started');
20 | }
21 | else {
22 | challenges = data.challenges;
23 | }
24 | });
25 | }
26 |
27 | const types = {
28 | // File Extension MIME Type
29 | 'abs': 'audio/x-mpeg',
30 | 'ai': 'application/postscript',
31 | 'aif': 'audio/x-aiff',
32 | 'aifc': 'audio/x-aiff',
33 | 'aiff': 'audio/x-aiff',
34 | 'aim': 'application/x-aim',
35 | 'art': 'image/x-jg',
36 | 'asf': 'video/x-ms-asf',
37 | 'asx': 'video/x-ms-asf',
38 | 'au': 'audio/basic',
39 | 'avi': 'video/x-msvideo',
40 | 'avx': 'video/x-rad-screenplay',
41 | 'bcpio': 'application/x-bcpio',
42 | 'bin': 'application/octet-stream',
43 | 'bmp': 'image/bmp',
44 | 'body': 'text/html',
45 | 'cdf': 'application/x-cdf',
46 | 'cer': 'application/pkix-cert',
47 | 'class': 'application/java',
48 | 'cpio': 'application/x-cpio',
49 | 'csh': 'application/x-csh',
50 | 'css': 'text/css',
51 | 'dib': 'image/bmp',
52 | 'doc': 'application/msword',
53 | 'dtd': 'application/xml-dtd',
54 | 'dv': 'video/x-dv',
55 | 'dvi': 'application/x-dvi',
56 | 'elf': 'application/x-elf',
57 | 'eot': 'application/vnd.ms-fontobject',
58 | 'eps': 'application/postscript',
59 | 'etx': 'text/x-setext',
60 | 'exe': 'application/octet-stream',
61 | 'gif': 'image/gif',
62 | 'gtar': 'application/x-gtar',
63 | 'gz': 'application/x-gzip',
64 | 'hdf': 'application/x-hdf',
65 | 'hqx': 'application/mac-binhex40',
66 | 'htc': 'text/x-component',
67 | 'htm': 'text/html',
68 | 'html': 'text/html',
69 | 'ief': 'image/ief',
70 | 'jad': 'text/vnd.sun.j2me.app-descriptor',
71 | 'jar': 'application/java-archive',
72 | 'java': 'text/x-java-source',
73 | 'jnlp': 'application/x-java-jnlp-file',
74 | 'jpe': 'image/jpeg',
75 | 'jpeg': 'image/jpeg',
76 | 'jpg': 'image/jpeg',
77 | 'js': 'application/javascript',
78 | 'jsf': 'text/plain',
79 | 'json': 'application/json',
80 | 'jspf': 'text/plain',
81 | 'kar': 'audio/midi',
82 | 'latex': 'application/x-latex',
83 | 'm3u': 'audio/x-mpegurl',
84 | 'mac': 'image/x-macpaint',
85 | 'man': 'text/troff',
86 | 'mathml': 'application/mathml+xml',
87 | 'me': 'text/troff',
88 | 'mid': 'audio/midi',
89 | 'midi': 'audio/midi',
90 | 'mif': 'application/x-mif',
91 | 'mov': 'video/quicktime',
92 | 'movie': 'video/x-sgi-movie',
93 | 'mp1': 'audio/mpeg',
94 | 'mp2': 'audio/mpeg',
95 | 'mp3': 'audio/mpeg',
96 | 'mp4': 'video/mp4',
97 | 'mpa': 'audio/mpeg',
98 | 'mpe': 'video/mpeg',
99 | 'mpeg': 'video/mpeg',
100 | 'mpega': 'audio/x-mpeg',
101 | 'mpg': 'video/mpeg',
102 | 'mpv2': 'video/mpeg2',
103 | 'ms': 'application/x-wais-source',
104 | 'nc': 'application/x-netcdf',
105 | 'oda': 'application/oda',
106 | 'odb': 'application/vnd.oasis.opendocument.database',
107 | 'odc': 'application/vnd.oasis.opendocument.chart',
108 | 'odf': 'application/vnd.oasis.opendocument.formula',
109 | 'odg': 'application/vnd.oasis.opendocument.graphics',
110 | 'odi': 'application/vnd.oasis.opendocument.image',
111 | 'odm': 'application/vnd.oasis.opendocument.text-master',
112 | 'odp': 'application/vnd.oasis.opendocument.presentation',
113 | 'ods': 'application/vnd.oasis.opendocument.spreadsheet',
114 | 'odt': 'application/vnd.oasis.opendocument.text',
115 | 'otg': 'application/vnd.oasis.opendocument.graphics-template',
116 | 'oth': 'application/vnd.oasis.opendocument.text-web',
117 | 'otp': 'application/vnd.oasis.opendocument.presentation-template',
118 | 'ots': 'application/vnd.oasis.opendocument.spreadsheet-template',
119 | 'ott': 'application/vnd.oasis.opendocument.text-template',
120 | 'ogx': 'application/ogg',
121 | 'ogv': 'video/ogg',
122 | 'oga': 'audio/ogg',
123 | 'ogg': 'audio/ogg',
124 | 'otf': 'application/x-font-opentype',
125 | 'spx': 'audio/ogg',
126 | 'flac': 'audio/flac',
127 | 'anx': 'application/annodex',
128 | 'axa': 'audio/annodex',
129 | 'axv': 'video/annodex',
130 | 'xspf': 'application/xspf+xml',
131 | 'pcap': 'application/vnd.tcpdump.pcap',
132 | 'pbm': 'image/x-portable-bitmap',
133 | 'pct': 'image/pict',
134 | 'pdf': 'application/pdf',
135 | 'pgm': 'image/x-portable-graymap',
136 | 'pic': 'image/pict',
137 | 'pict': 'image/pict',
138 | 'pls': 'audio/x-scpls',
139 | 'png': 'image/png',
140 | 'pnm': 'image/x-portable-anymap',
141 | 'pnt': 'image/x-macpaint',
142 | 'ppm': 'image/x-portable-pixmap',
143 | 'ppt': 'application/vnd.ms-powerpoint',
144 | 'pps': 'application/vnd.ms-powerpoint',
145 | 'ps': 'application/postscript',
146 | 'psd': 'image/vnd.adobe.photoshop',
147 | 'qt': 'video/quicktime',
148 | 'qti': 'image/x-quicktime',
149 | 'qtif': 'image/x-quicktime',
150 | 'ras': 'image/x-cmu-raster',
151 | 'rdf': 'application/rdf+xml',
152 | 'rgb': 'image/x-rgb',
153 | 'rm': 'application/vnd.rn-realmedia',
154 | 'roff': 'text/troff',
155 | 'rtf': 'application/rtf',
156 | 'rtx': 'text/richtext',
157 | 'sfnt': 'application/font-sfnt',
158 | 'sh': 'application/x-sh',
159 | 'shar': 'application/x-shar',
160 | 'sit': 'application/x-stuffit',
161 | 'snd': 'audio/basic',
162 | 'src': 'application/x-wais-source',
163 | 'sv4cpio': 'application/x-sv4cpio',
164 | 'sv4crc': 'application/x-sv4crc',
165 | 'svg': 'image/svg+xml',
166 | 'svgz': 'image/svg+xml',
167 | 'swf': 'application/x-shockwave-flash',
168 | 't': 'text/troff',
169 | 'tar': 'application/x-tar',
170 | 'tcl': 'application/x-tcl',
171 | 'tex': 'application/x-tex',
172 | 'texi': 'application/x-texinfo',
173 | 'texinfo': 'application/x-texinfo',
174 | 'tif': 'image/tiff',
175 | 'tiff': 'image/tiff',
176 | 'tr': 'text/troff',
177 | 'tsv': 'text/tab-separated-values',
178 | 'ttf': 'application/x-font-ttf',
179 | 'txt': 'text/plain',
180 | 'ulw': 'audio/basic',
181 | 'ustar': 'application/x-ustar',
182 | 'vxml': 'application/voicexml+xml',
183 | 'xbm': 'image/x-xbitmap',
184 | 'xht': 'application/xhtml+xml',
185 | 'xhtml': 'application/xhtml+xml',
186 | 'xls': 'application/vnd.ms-excel',
187 | 'xml': 'application/xml',
188 | 'xpm': 'image/x-xpixmap',
189 | 'xsl': 'application/xml',
190 | 'xslt': 'application/xslt+xml',
191 | 'xul': 'application/vnd.mozilla.xul+xml',
192 | 'xwd': 'image/x-xwindowdump',
193 | 'vsd': 'application/vnd.visio',
194 | 'wav': 'audio/x-wav',
195 | 'wbmp': 'image/vnd.wap.wbmp',
196 | 'wml': 'text/vnd.wap.wml',
197 | 'wmlc': 'application/vnd.wap.wmlc',
198 | 'wmls': 'text/vnd.wap.wmlsc',
199 | 'wmlscriptc': 'application/vnd.wap.wmlscriptc',
200 | 'wmv': 'video/x-ms-wmv',
201 | 'woff': 'application/font-woff',
202 | 'woff2': 'application/font-woff2',
203 | 'wrl': 'model/vrml',
204 | 'wspolicy': 'application/wspolicy+xml',
205 | 'z': 'application/x-compress',
206 | 'zip': 'application/zip'
207 | };
208 |
209 | export default function InterfaceGlobe() {
210 | const [isHover, setIsHover] = useState(false);
211 | const [geoData, setGeoData] = useState([]);
212 | const [hoverD, setHoverD] = useState(null);
213 |
214 | const handleMouseEnter = () => {
215 | setIsHover(true);
216 | };
217 |
218 | const handleMouseLeave = () => {
219 | setIsHover(false);
220 | };
221 |
222 | useEffect(() => {
223 | fetch('./map.geojson').then(res => res.json()).then(data => setGeoData(data.features));
224 | }, []);
225 |
226 | function flagSubmit() {
227 | let flag = document.getElementById('challengeFlag').value;
228 | let challange = document.getElementById('challengeName').innerHTML;
229 | fetch('/api/validate', {
230 | method: 'POST',
231 | headers: {
232 | 'Content-Type': 'application/json',
233 | },
234 | body: JSON.stringify({
235 | challenge: challange,
236 | value: flag
237 | })
238 | })
239 | .then(response => response.status)
240 | .then(status => {
241 | if (status === 200) {
242 | alert("Correct Flag!");
243 | } else if (status === 403) {
244 | alert("Already anwsered!");
245 | } else if (status === 409) {
246 | alert("Wrong Flag!");
247 | } else {
248 | alert("Something went wrong!");
249 | }
250 |
251 | })
252 | }
253 |
254 | function download() {
255 | let files = []
256 | for (let i = 0; i < challenges.length; i++) {
257 | if (challenges[i].name === document.getElementById('challengeName').innerHTML) {
258 | for (let j = 0; j < challenges[i].files.length; j++) {
259 | files.push({ filename : challenges[i].files[j].filename, base64 : challenges[i].files[j].base64 });
260 | }
261 | }
262 | }
263 |
264 | if (files == []) {
265 | return
266 | }
267 |
268 | for (let i = 0; i < files.length; i++) {
269 | var downloadLink = document.createElement("a");
270 | var type = "";
271 | for (let j = 0; j < types.length; j++) {
272 | if (files[i].filename.includes(types[i].extension)) {
273 | type = types[i].type;
274 | }
275 | }
276 | downloadLink.href = "data:" + type + ";base64," + files[i].base64;
277 | downloadLink.download = files[i].filename;
278 | downloadLink.click();
279 | }
280 | }
281 |
282 | const handlePolygonHover = (polygon) => {
283 | setHoverD(polygon);
284 | }
285 |
286 | function getChallengeByCountry(country) {
287 | for (let i = 0; i < challenges.length; i++) {
288 | if (country === challenges[i].country.toUpperCase()) {
289 | return challenges[i];
290 | }
291 | }
292 | }
293 |
294 | function handlePolygonClick(d) {
295 | if (isCountryUsed(d.properties.ISO_A2) === true) {
296 | let challenge = getChallengeByCountry(d.properties.ISO_A2);
297 | document.getElementById('challengeBlock').style.display = 'block';
298 | document.getElementById('challengeName').innerHTML = challenge.name;
299 | document.getElementById('challengeCountry').innerHTML = d.properties.ADMIN;
300 | document.getElementById('challengePoints').innerHTML = challenge.points + ' points';
301 | document.getElementById('challengeDescription').innerHTML = challenge.description;
302 | addEventListener('click', () => {
303 | if(event.target.id !== 'challengeBlock' && event.target.className !== 'challengeInfo') {
304 | document.getElementById('challengeBlock').style.display = 'none';
305 | }
306 | });
307 | }
308 | else {
309 | document.getElementById('challengeBlock').style.display = 'none';
310 | }
311 | }
312 |
313 | return (
314 |
315 |
d.properties.ISO_A2 !== 'AQ')}
319 | polygonCapColor={d => isCountryUsed(d.properties.ISO_A2) ? '#3d2473' : 'rgb(22, 22, 22)'}
320 | polygonSideColor = {() => 'rgb(93, 52, 179)'}
321 | polygonStrokeColor = {() => '#5D34B3'}
322 | onPolygonHover={handlePolygonHover}
323 | polygonAltitude={d => (hoverD && d.properties.ADMIN === hoverD.properties.ADMIN ? 0.04 : 0.02)}
324 | polygonsTransitionDuration={150}
325 | onPolygonClick={d => handlePolygonClick(d)}
326 | />
327 |
328 |
329 |
330 |
331 |
Challenge
332 |
333 |
334 |
335 |
336 |
points: 0
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 | );
345 | }
346 |
347 | fetchChallenges();
348 | setInterval(fetchChallenges, 2*60*1000)
349 |
--------------------------------------------------------------------------------
/frontend_dev/src/components/login.svelte:
--------------------------------------------------------------------------------
1 |
33 |
34 |
35 |
36 |
CTF Login
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
56 |
--------------------------------------------------------------------------------
/frontend_dev/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/frontend_dev/src/pages/ctf.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Stats from '../components/ctfstats.svelte';
3 | import InterfaceGlobe from '../components/globe.jsx';
4 |
5 | ---
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/frontend_dev/src/pages/dashboard.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Admin from "../components/admin.svelte";
3 | ---
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/frontend_dev/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Login from '../components/login.svelte';
3 | ---
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/frontend_dev/svelte.config.js:
--------------------------------------------------------------------------------
1 | import { vitePreprocess } from '@astrojs/svelte';
2 |
3 | export default {
4 | preprocess: vitePreprocess(),
5 | };
6 |
--------------------------------------------------------------------------------
/frontend_dev/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
4 | theme: {
5 | extend: {
6 | colors: {
7 | black1: "#000",
8 | },
9 | },
10 | patterns: {
11 | colors: {
12 | black1: "#000",
13 | },
14 | opacities: {
15 | 100: "1",
16 | 80: ".80",
17 | 60: ".60",
18 | 40: ".40",
19 | 20: ".20",
20 | 10: ".10",
21 | 5: ".05",
22 | },
23 | sizes: {
24 | 1: "0.25rem",
25 | 2: "0.5rem",
26 | 4: "1rem",
27 | 6: "1.5rem",
28 | 8: "2rem",
29 | 16: "4rem",
30 | 20: "5rem",
31 | 24: "6rem",
32 | 32: "8rem"
33 | },
34 | },
35 | },
36 | plugins: [
37 | require('tailwindcss-bg-patterns'),
38 | ],
39 | }
40 |
--------------------------------------------------------------------------------
/frontend_dev/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/base",
3 | "compilerOptions": {
4 | "jsx": "react-jsx",
5 | "jsxImportSource": "react"
6 | }
7 | }
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/Fabucik/ctf-portal
2 |
3 | go 1.20
4 |
5 | require (
6 | github.com/bytedance/sonic v1.8.3 // indirect
7 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
8 | github.com/gin-contrib/gzip v0.0.6 // indirect
9 | github.com/gin-contrib/sse v0.1.0 // indirect
10 | github.com/gin-gonic/gin v1.9.0 // indirect
11 | github.com/go-playground/locales v0.14.1 // indirect
12 | github.com/go-playground/universal-translator v0.18.1 // indirect
13 | github.com/go-playground/validator/v10 v10.11.2 // indirect
14 | github.com/goccy/go-json v0.10.0 // indirect
15 | github.com/google/uuid v1.3.0 // indirect
16 | github.com/jackc/pgpassfile v1.0.0 // indirect
17 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
18 | github.com/jackc/pgx/v5 v5.3.1 // indirect
19 | github.com/jinzhu/inflection v1.0.0 // indirect
20 | github.com/jinzhu/now v1.1.5 // indirect
21 | github.com/json-iterator/go v1.1.12 // indirect
22 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect
23 | github.com/leodido/go-urn v1.2.2 // indirect
24 | github.com/mattn/go-isatty v0.0.17 // indirect
25 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
26 | github.com/modern-go/reflect2 v1.0.2 // indirect
27 | github.com/pelletier/go-toml/v2 v2.0.7 // indirect
28 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
29 | github.com/ugorji/go/codec v1.2.10 // indirect
30 | golang.org/x/arch v0.2.0 // indirect
31 | golang.org/x/crypto v0.9.0 // indirect
32 | golang.org/x/net v0.10.0 // indirect
33 | golang.org/x/sys v0.8.0 // indirect
34 | golang.org/x/text v0.9.0 // indirect
35 | google.golang.org/protobuf v1.28.1 // indirect
36 | gopkg.in/yaml.v3 v3.0.1 // indirect
37 | gorm.io/driver/postgres v1.5.0 // indirect
38 | gorm.io/gorm v1.25.1 // indirect
39 | )
40 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
2 | github.com/bytedance/sonic v1.8.3 h1:pf6fGl5eqWYKkx1RcD4qpuX+BIUaduv/wTm5ekWJ80M=
3 | github.com/bytedance/sonic v1.8.3/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
4 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
5 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
6 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
7 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10 | github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
11 | github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
12 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
13 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
14 | github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
15 | github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
16 | github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
17 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
18 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
19 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
20 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
21 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
22 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
23 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
24 | github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
25 | github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
26 | github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
27 | github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
28 | github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
29 | github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
30 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
31 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
32 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
33 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
34 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
35 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
36 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
37 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
38 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
39 | github.com/jackc/pgx/v5 v5.3.0/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
40 | github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
41 | github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
42 | github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
43 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
44 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
45 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
46 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
47 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
48 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
49 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
50 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
51 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
52 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
53 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
54 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
55 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
56 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
57 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
58 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
59 | github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4=
60 | github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ=
61 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
62 | github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
63 | github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
64 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
65 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
66 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
67 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
68 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
69 | github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
70 | github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
71 | github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
72 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
73 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
74 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
75 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
76 | github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
77 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
78 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
79 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
80 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
81 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
82 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
83 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
84 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
85 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
86 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
87 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
88 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
89 | github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
90 | github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
91 | github.com/ugorji/go/codec v1.2.10 h1:eimT6Lsr+2lzmSZxPhLFoOWFmQqwk0fllJJ5hEbTXtQ=
92 | github.com/ugorji/go/codec v1.2.10/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
93 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
94 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
95 | golang.org/x/arch v0.2.0 h1:W1sUEHXiJTfjaFJ5SLo0N6lZn+0eO5gWD1MFeTGqQEY=
96 | golang.org/x/arch v0.2.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
97 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
98 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
99 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
100 | golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
101 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
102 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
103 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
104 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
105 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
106 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
107 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
108 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
109 | golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
110 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
111 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
112 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
113 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
114 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
115 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
116 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
117 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
118 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
119 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
120 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
121 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
122 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
123 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
124 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
125 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
126 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
127 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
128 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
129 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
130 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
131 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
132 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
133 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
134 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
135 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
136 | golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
137 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
138 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
139 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
140 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
141 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
142 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
143 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
144 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
145 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
146 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
147 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
148 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
149 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
150 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
151 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
152 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
153 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
154 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
155 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
156 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
157 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
158 | gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U=
159 | gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A=
160 | gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
161 | gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64=
162 | gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
163 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
164 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/Fabucik/ctf-portal/authentication"
5 | "github.com/Fabucik/ctf-portal/ctfsrc"
6 | "github.com/gin-gonic/gin"
7 | "github.com/gin-contrib/gzip"
8 | )
9 |
10 | func main() {
11 | gin.SetMode(gin.ReleaseMode)
12 | server := gin.Default()
13 | server.Use(gzip.Gzip(gzip.DefaultCompression))
14 |
15 | server.LoadHTMLFiles("frontend/dist_collected/index.html", "frontend/dist_collected/admin.html", "frontend/dist_collected/CTF.html")
16 | server.Static("/assets", "./frontend/dist_collected/assets")
17 |
18 | //authentication stuff
19 | server.POST("/api/register", authentication.Register)
20 | server.POST("/api/login", authentication.Login)
21 |
22 | // challenge upload
23 | server.POST("/api/upload", ctfsrc.CreateChallenge)
24 |
25 | // flag validation
26 | server.POST("/api/validate", ctfsrc.ValidateFlag)
27 |
28 | // lists challenges
29 | server.GET("/api/challenges", ctfsrc.GetChallenges)
30 |
31 | // team management
32 | server.GET("/api/teams", ctfsrc.GetTeams)
33 | server.DELETE("/api/teams", ctfsrc.DeleteTeam)
34 |
35 | // returns json with all teams and their points
36 | server.GET("/api/points", ctfsrc.GetAllPoints)
37 |
38 | // ctf start and stop
39 | server.POST("/api/timedstart", ctfsrc.SetTime)
40 | server.GET("/api/timedstart", ctfsrc.GetTime)
41 | server.POST("/api/manualstart", ctfsrc.SetManualStartStop)
42 | server.GET("/api/manualstart", ctfsrc.GetManualStartStop)
43 |
44 | // announcement stuff
45 | server.POST("/api/announcement", ctfsrc.CreateAnnouncement)
46 | server.GET("/api/announcement", ctfsrc.GetAnnouncements)
47 | server.DELETE("/api/announcement", ctfsrc.DeleteAnnouncement)
48 |
49 | // importing and exporting CTF challenges
50 | server.GET("/api/backup", ctfsrc.Export)
51 | server.POST("/api/backup", ctfsrc.Import)
52 |
53 | server.Run(":8888")
54 | }
55 |
--------------------------------------------------------------------------------
/servehtml/admin.go:
--------------------------------------------------------------------------------
1 | package servehtml
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/Fabucik/ctf-portal/authentication"
8 | "github.com/Fabucik/ctf-portal/entities"
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | func AdminHTML(ctx *gin.Context) {
13 | var session entities.Session
14 | cookie, _ := ctx.Cookie("session")
15 | json.Unmarshal([]byte(cookie), &session)
16 |
17 | if !authentication.IsAdmin(ctx, session) {
18 | return
19 | }
20 |
21 | ctx.HTML(http.StatusOK, "admin.html", gin.H{})
22 | }
23 |
--------------------------------------------------------------------------------
/servehtml/ctf.go:
--------------------------------------------------------------------------------
1 | package servehtml
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/Fabucik/ctf-portal/authentication"
8 | "github.com/Fabucik/ctf-portal/entities"
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | func CtfHTML(ctx *gin.Context) {
13 | var session entities.Session
14 | cookie, _ := ctx.Cookie("session")
15 | json.Unmarshal([]byte(cookie), &session)
16 |
17 | if !authentication.IsValidSession(session) {
18 | ctx.JSON(http.StatusUnauthorized, gin.H{
19 | "message": "Not logged in",
20 | })
21 |
22 | return
23 | }
24 |
25 | ctx.HTML(http.StatusOK, "CTF.html", gin.H{})
26 | }
27 |
--------------------------------------------------------------------------------
/servehtml/login.go:
--------------------------------------------------------------------------------
1 | package servehtml
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | func LoginHTML(ctx *gin.Context) {
10 | ctx.HTML(http.StatusOK, "index.html", gin.H{})
11 | }
12 |
--------------------------------------------------------------------------------
/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # PATH TO THIS REPO
4 | cd $USER/home/ctf-portal
5 |
6 | bin/ctf-portal
--------------------------------------------------------------------------------