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 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"]
failSolveLimit = 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"]
failSolveLimit=2

[challenge.env]
apt_deps = ["gcc", "socat"]
Expand Down
180 changes: 176 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"
log "github.com/sirupsen/logrus"
)
Expand All @@ -35,6 +36,116 @@ 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 @@ -102,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 @@ -113,6 +240,7 @@ func challengeInfoHandler(c *gin.Context) {
c.Abort()
return
}


autherr := auth.Authorize(values[1], core.ADMIN)

Expand All @@ -125,7 +253,8 @@ func challengeInfoHandler(c *gin.Context) {
Tags: challengeTags,
Status: challenge.Status,
Ports: challengePorts,
Hints: challenge.Hints,
Hints: hintInfos,
FailSolveLimit: challenge.FailSolveLimit,
Desc: challenge.Description,
Assets: strings.Split(challenge.Assets, core.DELIMITER),
AdditionalLinks: strings.Split(challenge.AdditionalLinks, core.DELIMITER),
Expand All @@ -146,7 +275,8 @@ func challengeInfoHandler(c *gin.Context) {
Tags: challengeTags,
Status: challenge.Status,
Ports: challengePorts,
Hints: challenge.Hints,
Hints: hintInfos,
FailSolveLimit: challenge.FailSolveLimit,
Desc: challenge.Description,
Assets: strings.Split(challenge.Assets, core.DELIMITER),
AdditionalLinks: strings.Split(challenge.AdditionalLinks, core.DELIMITER),
Expand Down Expand Up @@ -261,6 +391,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 @@ -297,6 +444,30 @@ 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 @@ -305,13 +476,15 @@ func challengesInfoHandler(c *gin.Context) {
CreatedAt: challenge.CreatedAt,
Status: challenge.Status,
Ports: challengePorts,
Hints: challenge.Hints,
Hints: hintInfos,
FailSolveLimit: challenge.FailSolveLimit,
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,
}
}

Expand Down Expand Up @@ -636,7 +809,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"`
FailSolveLimit int `json:"failSolveLimit" 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"`
}
Expand All @@ -123,8 +136,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']"`
FailSolveLimit int `json:"failSolveLimit" 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"`
}
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)
}

// Notification route group
Expand Down
Loading