Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Maximum Attempt Limit to Submit Flag #370

Open
wants to merge 19 commits into
base: multiple_servers
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ site/
ubuntu-bionic-18.04-cloudimg-console.log
target/
.vscode/
vendor
vendor/
20 changes: 14 additions & 6 deletions _examples/simple/beast.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
[author]
name = "fristonio"
email = "[email protected]"
ssh_key = "ssh-rsa AAAAB3NzaC1y"
name = "hi"
email = "[email protected]"
ssh_key = "hi"

[challenge.metadata]
name = "simple"
flag = "BACKDOOR{SAMPLE_FLAG}"
type = "bare"
hints = ["simple_hint_1", "simple_hint_2"]
points = 100

[[challenge.metadata.hints]]
text = "simple_hint_1"
points = 10

[[challenge.metadata.hints]]
text = "simple_hint_2"
points = 20

[challenge.env]
apt_deps = ["gcc", "socat"]
setup_scripts = ["setup.sh"]
run_cmd = "socat tcp-l:10001,fork,reuseaddr exec:./pwn"
ports = [10001]
run_cmd = "socat tcp-l:10005,fork,reuseaddr exec:./pwn"
ports = [10005]
1 change: 1 addition & 0 deletions _examples/web-php/beast.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ name = "web-php"
flag = "BACKDOOR{SAMPLE_WEB_FLAG}"
type = "web:php:7.1:cli"
hints = ["web_hint_1", "web_hint_2"]
max_attempt_limit = 3

[challenge.env]
ports = [10002]
Expand Down
2 changes: 2 additions & 0 deletions _examples/xinetd-service/beast.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ name = "xinetd-service"
flag = "CTF{sample_flag}"
type = "service"
hints = ["xinetd_hint_1", "xinetd_hint_2"]
preReqs = ["simple", "web-php"]
max_attempt_limit=2

[challenge.env]
apt_deps = ["gcc", "socat"]
Expand Down
179 changes: 175 additions & 4 deletions api/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
cfg "github.com/sdslabs/beastv4/core/config"
"github.com/sdslabs/beastv4/core/database"
"github.com/sdslabs/beastv4/core/utils"
coreUtils "github.com/sdslabs/beastv4/core/utils"
"github.com/sdslabs/beastv4/pkg/auth"
fileUtils "github.com/sdslabs/beastv4/utils"
log "github.com/sirupsen/logrus"
Expand All @@ -36,6 +37,115 @@ func usedPortsInfoHandler(c *gin.Context) {
})
}

func hintHandler(c *gin.Context) {
hintIDStr := c.Param("hintID")

if hintIDStr == "" {
c.JSON(http.StatusBadRequest, HTTPErrorResp{
Error: "Hint ID cannot be empty",
})
return
}

hintID, err := strconv.Atoi(hintIDStr)

if err != nil {
c.JSON(http.StatusBadRequest, HTTPPlainResp{
Message: "Hint Id format invalid",
})
return
}

username, err := coreUtils.GetUser(c.GetHeader("Authorization"))
if err != nil {
c.JSON(http.StatusUnauthorized, HTTPErrorResp{
Error: "Unauthorized user",
})
return
}

user, err := database.QueryFirstUserEntry("username", username)
if err != nil {
c.JSON(http.StatusUnauthorized, HTTPErrorResp{
Error: "Unauthorized user",
})
return
}

if user.Status == 1 {
c.JSON(http.StatusUnauthorized, HTTPErrorResp{
Error: "Banned user",
})
return
}

// Fetch hint details
hint, err := database.GetHintByID(uint(hintID))
if err != nil {
if err.Error() == "not_found" {
c.JSON(http.StatusNotFound, HTTPErrorResp{
Error: "Hint not found",
})
} else {
c.JSON(http.StatusInternalServerError, HTTPErrorResp{
Error: "DATABASE ERROR while processing the request",
})
}
return
}

// Check if the user has already taken the hint
hasTakenHint, err := database.UserHasTakenHint(user.ID, uint(hintID))
if err != nil {
c.JSON(http.StatusInternalServerError, HTTPErrorResp{
Error: "DATABASE ERROR while checking hint usage",
})
return
}

if c.Request.Method == "GET" {
if hasTakenHint {
c.JSON(http.StatusOK, HintResponse{
Description: hint.Description,
Points: hint.Points,
})
} else {
c.JSON(http.StatusOK, HintResponse{
Description: "Hint is not taken yet",
Points: hint.Points,
})
}
return
}

if hasTakenHint {
// If hint already taken, just return the description
c.JSON(http.StatusOK, HTTPPlainResp{
Message: hint.Description,
})
return
}

// Save user hint if not already taken
if err := database.SaveUserHint(user.ID, hint.ChallengeID, hint.HintID); err != nil {
if err.Error() == "Not enough points to take this hint" {
c.JSON(http.StatusForbidden, HTTPErrorResp{
Error: "You don't have enough points to take this hint",
})
return
}
c.JSON(http.StatusInternalServerError, HTTPErrorResp{
Error: "DATABASE ERROR while saving the hint usage",
})
return
}

// Return the hint description after successfully taking it
c.JSON(http.StatusOK, HTTPPlainResp{
Message: hint.Description,
})
}

// Returns information about a challenge
// @Summary Returns all information about the challenges.
// @Description Returns all information about the challenges by the challenge name.
Expand Down Expand Up @@ -103,6 +213,22 @@ func challengeInfoHandler(c *gin.Context) {
challengeTags[index] = tags.TagName
}

hints, err := database.QueryHintsByChallengeID(challenge.ID)
if err != nil {
c.JSON(http.StatusInternalServerError, HTTPErrorResp{
Error: "DATABASE ERROR while fetching hints.",
})
return
}

hintInfos := make([]HintInfo, len(hints))
for i, hint := range hints {
hintInfos[i] = HintInfo{
ID: hint.HintID,
Points: hint.Points,
}
}

authHeader := c.GetHeader("Authorization")

values := strings.Split(authHeader, " ")
Expand All @@ -126,7 +252,8 @@ func challengeInfoHandler(c *gin.Context) {
Tags: challengeTags,
Status: challenge.Status,
Ports: challengePorts,
Hints: challenge.Hints,
Hints: hintInfos,
MaxAttemptLimit: challenge.MaxAttemptLimit,
Desc: challenge.Description,
Assets: strings.Split(challenge.Assets, core.DELIMITER),
AdditionalLinks: strings.Split(challenge.AdditionalLinks, core.DELIMITER),
Expand All @@ -148,7 +275,8 @@ func challengeInfoHandler(c *gin.Context) {
Tags: challengeTags,
Status: challenge.Status,
Ports: challengePorts,
Hints: challenge.Hints,
Hints: hintInfos,
MaxAttemptLimit: challenge.MaxAttemptLimit,
Desc: challenge.Description,
Assets: strings.Split(challenge.Assets, core.DELIMITER),
AdditionalLinks: strings.Split(challenge.AdditionalLinks, core.DELIMITER),
Expand Down Expand Up @@ -264,6 +392,23 @@ func challengesInfoHandler(c *gin.Context) {

availableChallenges := make([]ChallengeInfoResp, len(challenges))

// Get user ID from token
username, err := coreUtils.GetUser(c.GetHeader("Authorization"))
if err != nil {
c.JSON(http.StatusUnauthorized, HTTPErrorResp{
Error: "Unauthorized user",
})
return
}

user, err := database.QueryFirstUserEntry("username", username)
if err != nil {
c.JSON(http.StatusUnauthorized, HTTPErrorResp{
Error: "Unauthorized user",
})
return
}

for index, challenge := range challenges {
users, err := database.GetRelatedUsers(&challenge)
if err != nil {
Expand Down Expand Up @@ -300,6 +445,31 @@ func challengesInfoHandler(c *gin.Context) {
challengeTags[index] = tags.TagName
}

// Get hints for this challenge
hints, err := database.QueryHintsByChallengeID(challenge.ID)
if err != nil {
log.Error(err)
c.JSON(http.StatusInternalServerError, HTTPErrorResp{
Error: "DATABASE ERROR while processing the request.",
})
return
}

hintInfos := make([]HintInfo, len(hints))
for i, hint := range hints {
hintInfos[i] = HintInfo{
ID: hint.HintID,
Points: hint.Points,
}
}
// Get previous tries for the current user and challenge
previousTries, err := database.GetUserPreviousTries(user.ID, challenge.ID)
if err != nil {
log.Error(err)
previousTries = 0

}

availableChallenges[index] = ChallengeInfoResp{
Name: challenge.Name,
ChallId: challenge.ID,
Expand All @@ -308,13 +478,15 @@ func challengesInfoHandler(c *gin.Context) {
CreatedAt: challenge.CreatedAt,
Status: challenge.Status,
Ports: challengePorts,
Hints: challenge.Hints,
Hints: hintInfos,
MaxAttemptLimit: challenge.MaxAttemptLimit,
Desc: challenge.Description,
Points: challenge.Points,
Assets: strings.Split(challenge.Assets, core.DELIMITER),
AdditionalLinks: strings.Split(challenge.AdditionalLinks, core.DELIMITER),
SolvesNumber: challSolves,
Solves: challengeUser,
PreviousTries: previousTries,
DeployedLink: challenge.ServerDeployed,
}
}
Expand Down Expand Up @@ -640,7 +812,6 @@ func submissionsHandler(c *gin.Context) {
c.JSON(http.StatusInternalServerError, HTTPErrorResp{
Error: "DATABASE ERROR while fetching user details.",
})
return
}
if len(challenge) == 0 {
continue
Expand Down
2 changes: 1 addition & 1 deletion api/manage.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ func manageUploadHandler(c *gin.Context) {
Assets: config.Challenge.Metadata.Assets,
AdditionalLinks: config.Challenge.Metadata.AdditionalLinks,
Ports: config.Challenge.Env.Ports,
Hints: config.Challenge.Metadata.Hints,
PreReqs: config.Challenge.Metadata.PreReqs,
Desc: config.Challenge.Metadata.Description,
Points: config.Challenge.Metadata.Points,
})
Expand Down
18 changes: 16 additions & 2 deletions api/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ type UserSolveResp struct {
SolvedAt time.Time `json:"solvedAt"`
}

type HintInfo struct {
ID uint `json:"id"`
Points uint `json:"points"`
}

type HintResponse struct {
Description string `json:"description" example:"This is a hint"`
Points uint `json:"points" example:"10"`
}

type ChallengeInfoResp struct {
Name string `json:"name" example:"Web Challenge"`
ChallId uint `json:"id" example:"0"`
Expand All @@ -107,12 +117,15 @@ type ChallengeInfoResp struct {
AdditionalLinks []string `json:"additionalLinks" example:"['http://link1.abc:8080','http://link2.abc:8081']"`
CreatedAt time.Time `json:"createdAt"`
Status string `json:"status" example:"deployed"`
MaxAttemptLimit int `json:"maxAttemptLimit" example:"5"`
PreReqs []string `json:"preReqs" example:"['web-php','simple']"`
Ports []uint32 `json:"ports" example:[3001, 3002]`
Hints string `json:"hints" example:"Try robots"`
Hints []HintInfo `json:"hints"`
Desc string `json:"description" example:"A simple web challenge"`
Points uint `json:"points" example:"50"`
SolvesNumber int `json:"solvesNumber" example:"100"`
Solves []UserSolveResp `json:"solves"`
PreviousTries int `json:"previous_tries" example:"3"`
DynamicFlag bool `json:"dynamicFlag" example:"true"`
Flag string `json:"flag"`
DeployedLink string `json:"deployedLink" example:"beast.sdslabs.co"`
Expand All @@ -124,8 +137,9 @@ type ChallengePreviewResp struct {
Tags []string `json:"tags" example:"['pwn','misc']"`
Assets []string `json:"assets" example:"['image1.png', 'zippy.zip']"`
AdditionalLinks []string `json:"additionalLinks" example:"['http://link1.abc:8080','http://link2.abc:8081']"`
MaxAttemptLimit int `json:"maxAttemptLimit" example:"5"`
PreReqs []string `json:"preReqs" example:"['web-php','simple']"`
Aryan51203 marked this conversation as resolved.
Show resolved Hide resolved
Ports []uint32 `json:"ports" example:[3001, 3002]`
Hints []string `json:"hints" example:"Try robots"`
Desc string `json:"description" example:"A simple web challenge"`
Points uint `json:"points" example:"50"`
DeployedLink string `json:"deployedLink" example:"beast.sdslabs.co"`
Expand Down
2 changes: 2 additions & 0 deletions api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ func initGinRouter() *gin.Engine {
infoGroup.GET("/users", getAllUsersInfoHandler)
infoGroup.GET("/submissions", submissionsHandler)
infoGroup.GET("/tags", tagHandler)
infoGroup.GET("/hint/:hintID", hintHandler)
infoGroup.POST("/hint/:hintID", hintHandler)
infoGroup.GET("/download", serveAssets)
}

Expand Down
Loading