Skip to content

Commit f30143a

Browse files
authored
feat: extend attack api endpoints (#162)
* attack api endpoints * update tailwind config
1 parent 68e4ded commit f30143a

File tree

7 files changed

+94
-56
lines changed

7 files changed

+94
-56
lines changed

cmd/reaper/main.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,9 @@ func main() {
9696
api.Get("/endpoints", h.GetEndpoints)
9797
api.Get("/endpoints/:id", h.GetEndpoint)
9898
api.Post("/attack", h.CreateAttack)
99-
// api.Get("/attacks", h.GetAttacks)
100-
// api.Get("/attacks/:id", h.GetAttack)
99+
api.Get("/attacks", h.GetAttacks)
100+
api.Get("/attacks/:id", h.GetAttack)
101+
api.Get("/attacks/:id/results", h.GetAttackResults)
101102
api.Delete("/attack/:id/results", h.DeleteAttackResults)
102103
// fuzz
103104
// automate

internal/database/config.go

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func Migrate() {
5050
&models.Endpoint{},
5151
&models.Request{},
5252
&models.Response{},
53+
&models.FuzzAttack{},
5354
&models.FuzzResult{},
5455
&models.Report{},
5556
&models.AgentSession{},

internal/database/models/model.go

+18-37
Original file line numberDiff line numberDiff line change
@@ -108,51 +108,32 @@ type Response struct {
108108

109109
// TODO: clean up this mess
110110
type FuzzAttack struct {
111-
ID uint `json:"id" gorm:"primaryKey"`
112-
Type string `json:"type"` // header, body, param
113-
Headers []FuzzAttackHeader `json:"headers"`
114-
Keys []FuzzAttackKey `json:"keys"`
115-
Params []FuzzAttackParam `json:"params"`
116-
CreatedAt time.Time `json:"created_at"`
117-
UpdatedAt time.Time `json:"updated_at"`
118-
}
119-
120-
type FuzzAttackHeader struct {
121-
ID uint `json:"id" gorm:"primaryKey"`
122-
FuzzAttackID uint `json:"fuzz_attack_id" gorm:"foreignKey:ID"`
123-
CreatedAt time.Time `json:"created_at"`
124-
UpdatedAt time.Time `json:"updated_at"`
125-
}
126-
127-
type FuzzAttackKey struct {
128-
ID uint `json:"id" gorm:"primaryKey"`
129-
FuzzAttackID uint `json:"fuzz_attack_id" gorm:"foreignKey:ID"`
130-
CreatedAt time.Time `json:"created_at"`
131-
UpdatedAt time.Time `json:"updated_at"`
111+
ID uint `json:"id" gorm:"primaryKey"`
112+
Type string `json:"type"` // header, body, param
113+
Headers string `json:"headers"`
114+
Keys string `json:"keys"`
115+
Params string `json:"params"`
116+
Status string `json:"status"`
117+
CreatedAt time.Time `json:"created_at"`
118+
UpdatedAt time.Time `json:"updated_at"`
132119
}
133120

134-
type FuzzAttackParam struct {
121+
type FuzzResult struct {
135122
ID uint `json:"id" gorm:"primaryKey"`
136123
FuzzAttackID uint `json:"fuzz_attack_id" gorm:"foreignKey:ID"`
124+
Hostname string `json:"hostname"`
125+
IpAddress string `json:"ip_address"`
126+
Port string `json:"port"`
127+
Scheme string `json:"scheme"`
128+
URL string `json:"url"`
129+
Endpoint string `json:"endpoint"`
130+
Request string `json:"request"`
131+
Response string `json:"response"`
132+
StatusCode int `json:"status_code"`
137133
CreatedAt time.Time `json:"created_at"`
138134
UpdatedAt time.Time `json:"updated_at"`
139135
}
140136

141-
type FuzzResult struct {
142-
ID uint `json:"id" gorm:"primaryKey"`
143-
// FuzzAttackID uint `json:"fuzz_attack_id" gorm:"foreignKey:ID"`
144-
Hostname string `json:"hostname"`
145-
IpAddress string `json:"ip_address"`
146-
Port string `json:"port"`
147-
Scheme string `json:"scheme"`
148-
URL string `json:"url"`
149-
Endpoint string `json:"endpoint"`
150-
Request string `json:"request"`
151-
Response string `json:"response"`
152-
CreatedAt time.Time `json:"created_at"`
153-
UpdatedAt time.Time `json:"updated_at"`
154-
}
155-
156137
type Report struct {
157138
ID uint `json:"id" gorm:"primaryKey"`
158139
Domain string `json:"domain"`

internal/handlers/endpoint.go

+45-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package handlers
33
import (
44
"log/slog"
55
"strconv"
6+
"strings"
67

78
"github.com/gofiber/fiber/v2"
89

@@ -71,14 +72,56 @@ func (h *Handler) CreateAttack(c *fiber.Ctx) error {
7172
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "endpoint not found"})
7273
}
7374

75+
attack := &models.FuzzAttack{
76+
Type: "param",
77+
Params: strings.Join(atk.Params, ","),
78+
}
79+
h.db.Create(attack)
80+
7481
go func() {
75-
err := fuzz.CreateAttack(endpoint.Hostname, atk.Params, h.pool, h.db, 100, 1000, 10)
82+
err := fuzz.CreateAttack(attack.ID, endpoint.Hostname, atk.Params, h.pool, h.db, 100, 1000, 10)
7683
if err != nil {
7784
slog.Error("[create attack]", "msg", "error creating attack", "error", err)
7885
}
7986
}()
8087

81-
return c.JSON(fiber.Map{"status": "ok"})
88+
return c.JSON(attack)
89+
}
90+
91+
func (h *Handler) GetAttacks(c *fiber.Ctx) error {
92+
attacks := []models.FuzzAttack{}
93+
h.db.Find(&attacks)
94+
95+
return c.JSON(attacks)
96+
}
97+
98+
func (h *Handler) GetAttack(c *fiber.Ctx) error {
99+
id, err := strconv.Atoi(c.Params("id"))
100+
if err != nil {
101+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
102+
}
103+
104+
attack := models.FuzzAttack{}
105+
h.db.First(&attack, id)
106+
107+
return c.JSON(attack)
108+
}
109+
110+
func (h *Handler) GetAttackResults(c *fiber.Ctx) error {
111+
id, err := strconv.Atoi(c.Params("id"))
112+
if err != nil {
113+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
114+
}
115+
116+
limit, err := strconv.Atoi(c.Query("limit", "50"))
117+
if err != nil {
118+
limit = 50
119+
}
120+
121+
results := []models.FuzzResult{}
122+
h.db.Where("fuzz_attack_id = ?", id).Limit(limit).Find(&results)
123+
124+
return c.JSON(results)
82125
}
83126

84127
func (h *Handler) DeleteAttackResults(c *fiber.Ctx) error {

internal/tools/agent/agent.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ func (manager *AgentManager) runAgent() {
303303

304304
// Run the BOLA attack
305305
manager.sendAgentMessage("Running the attack...")
306-
err := fuzz.CreateAttack(function_model.Domain, excludedKeys, manager.Pool, manager.DB, 0, 0, 0)
306+
// TODO: get the attack id
307+
err := fuzz.CreateAttack(0, function_model.Domain, excludedKeys, manager.Pool, manager.DB, 0, 0, 0)
307308
if err != nil {
308309
slog.Error("Failed to create fuzz attack", "error", err)
309310
}

internal/tools/fuzz/simple_fuzzer_example.go

+22-11
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const (
3535
// TODO: add rate limit
3636
)
3737

38-
func CreateAttack(hostname string, params []string, ws *websocket.Pool, db *gorm.DB, min, max, maxSuccess int) error {
38+
func CreateAttack(attackID uint, hostname string, params []string, ws *websocket.Pool, db *gorm.DB, min, max, maxSuccess int) error {
3939
slog.Info("Creating fuzz attack")
4040

4141
// Defaults
@@ -94,7 +94,7 @@ func CreateAttack(hostname string, params []string, ws *websocket.Pool, db *gorm
9494
defer func() { <-semaphore }()
9595

9696
fuzzedReq := createFuzzedRequest(&req, key, value)
97-
status, err := sendRequest(fuzzedReq, ws, db)
97+
status, err := sendRequest(fuzzedReq, ws, db, attackID)
9898
if err != nil {
9999
slog.Error("Failed to send fuzzed request", "error", err)
100100
} else {
@@ -133,6 +133,15 @@ workerLoop:
133133
// Wait for active workers to complete
134134
wg.Wait()
135135

136+
attack := models.FuzzAttack{}
137+
db.First(&attack, attackID)
138+
if successCount > 0 {
139+
attack.Status = "success"
140+
} else {
141+
attack.Status = "completed"
142+
}
143+
db.Save(&attack)
144+
136145
msg := &types.AttackCompleteMessage{
137146
Type: types.MessageTypeAttackComplete,
138147
}
@@ -178,7 +187,7 @@ func createFuzzedRequest(originalReq *models.Request, key string, value int) *ht
178187
return req
179188
}
180189

181-
func sendRequest(req *http.Request, ws *websocket.Pool, db *gorm.DB) (status int, err error) {
190+
func sendRequest(req *http.Request, ws *websocket.Pool, db *gorm.DB, attackID uint) (status int, err error) {
182191
client := &http.Client{}
183192
resp, err := client.Do(req)
184193
if err != nil {
@@ -216,14 +225,16 @@ func sendRequest(req *http.Request, ws *websocket.Pool, db *gorm.DB) (status int
216225

217226
// Create a FuzzResult and save it to the database
218227
fuzzResult := &models.FuzzResult{
219-
Hostname: req.URL.Hostname(),
220-
IpAddress: req.RemoteAddr,
221-
Port: req.URL.Port(),
222-
Scheme: req.URL.Scheme,
223-
URL: req.URL.String(),
224-
Endpoint: req.URL.Path,
225-
Request: string(requestHeaders) + "\n" + string(requestBody),
226-
Response: string(responseBody),
228+
FuzzAttackID: attackID,
229+
Hostname: req.URL.Hostname(),
230+
IpAddress: req.RemoteAddr,
231+
Port: req.URL.Port(),
232+
Scheme: req.URL.Scheme,
233+
URL: req.URL.String(),
234+
Endpoint: req.URL.Path,
235+
Request: string(requestHeaders) + "\n" + string(requestBody),
236+
Response: string(responseBody),
237+
StatusCode: resp.StatusCode,
227238
}
228239
res := db.Create(fuzzResult)
229240
if res.Error != nil {

ui/tailwind.config.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
const animate = require("tailwindcss-animate")
1+
import animate from "tailwindcss-animate";
22

33
/** @type {import('tailwindcss').Config} */
4-
module.exports = {
4+
export default {
55
darkMode: ["class"],
66
safelist: ["dark"],
77
prefix: "",
@@ -92,4 +92,4 @@ module.exports = {
9292
},
9393
},
9494
plugins: [animate],
95-
}
95+
}

0 commit comments

Comments
 (0)