Skip to content
Merged

CLI #41

Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ GREEN := \033[0;32m
PURPLE := \033[0;35m
RESET := \033[0m
RED := \033[0;31m
JACFARM_API_KEY := $(shell openssl rand -hex 32)
JACFARM_API_KEY := $(shell openssl rand -hex 16)
ADMIN_PASS := $(shell openssl rand -hex 8)


Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
</p>
<p align="center">
<a href="https://github.com/Jacute/JacFARM/actions"><img src="https://github.com/Jacute/JacFARM/actions/workflows/tests.yml/badge.svg" alt="CI Status"></a>
<a href="https://codecov.io/gh/Jacute/JacFARM"><img alt="Codecov" src="https://codecov.io/gh/Jacute/JacFARM/branch/master/graph/badge.svg"></a>
<a href='https://badge.coveralls.io/github/Jacute/JacFARM?branch=master'><img src='https://badge.coveralls.io/repos/github/Jacute/JacFARM/badge.svg?branch=master' alt='Coverage Status' /></a>
<a href="https://github.com/Jacute/JacFARM/releases"><img alt="Release" src="https://img.shields.io/github/v/release/Jacute/JacFARM"></a>
</p>
Expand Down Expand Up @@ -46,6 +45,7 @@ make clean-all
## Features

- Uploading exploits in ui
- Uploading exploits in cli
- Real-time configuration farm options like number of concurrently running exploits, the size of the flag sending batch, team ip addresses, etc
- The ability to [change the plugin for sending flags to jury](./docs/flag_sender/flag_sender.md).
- There are already two sending plugins: [forcad_http](./workers/flag_sender/plugins/forcad_http/client.go) and [saarctf_tcp](./workers/flag_sender/plugins/saarctf_tcp/client.go).
Expand All @@ -61,7 +61,7 @@ make clean-all

### Client

- **Frontend** - ui for
- **Frontend** (./jacfarm-frontend) - ui for
- viewing flags with any filters
- adding exploits of different types via '+' button
- deleting or updating exploits by right mouse button
Expand All @@ -71,14 +71,14 @@ make clean-all

![](./docs/img/frontend.png)

- **start_exploit.py** - python cli tool for starting exploits on local machine (TODO)
- **CLI** (./cli) - tool for starting exploits on local machine

### Server

- **Exploit Runner** - a worker that launches exploits on all teams. [More details](./docs/exploit_runner/exploit_runner.md)
- **Flag Sender** - a worker that sends flags to jury using *Plugins*. [More details](./docs/flag_sender/flag_sender.md)
- **JacFARM API** - API for frontend and cli start_exploit.py.
- **Config Loader** - loads config into db from config.yml on start. Next configuration editing is available through the frontend.
- **Exploit Runner** (./workers/exploit_runner) - a worker that launches exploits on all teams. [More details](./docs/exploit_runner/exploit_runner.md)
- **Flag Sender** (./workers/flag_sender) - a worker that sends flags to jury using *Plugins*. [More details](./docs/flag_sender/flag_sender.md)
- **JacFARM API** (./jacfarm-api) - API for frontend and cli start_exploit.py.
- **Config Loader** (./workers/config_loader) - loads config into db from config.yml on start. Next configuration editing is available through the frontend.

#### Plugins

Expand All @@ -90,4 +90,4 @@ make clean-all

### Arch Diagram

![](./docs/img/diagram.jpg)
![](./docs/img/diagram.png)
44 changes: 44 additions & 0 deletions cli/cmd/cli/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import (
"flag"
"fmt"
"jacfarmcli/internal/cli"
"log/slog"
"os"
"os/signal"
"syscall"

"github.com/jacute/prettylogger"
)

func main() {
args, err := cli.ParseArgs()
if err != nil {
flag.Usage()
os.Exit(1)
}
err = cli.ValidateArgs(args)
if err != nil {
fmt.Println(err.Error())
flag.Usage()
os.Exit(2)
}

log := slog.New(prettylogger.NewColoredHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))

sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

log.Info("running script")
err = cli.Run(args, log)
if err != nil {
os.Exit(2)
}

sign := <-sigCh

log.Info("stopping script", slog.String("signal", sign.String()))
}
19 changes: 19 additions & 0 deletions cli/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module jacfarmcli

go 1.25.4

require (
github.com/jacute/prettylogger v0.0.7
github.com/stretchr/testify v1.11.1
go.uber.org/mock v0.6.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.18.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
25 changes: 25 additions & 0 deletions cli/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/jacute/prettylogger v0.0.7 h1:inKCDEJ42j31hNVB6wAYZWOrc7E4QJ//x2hcR0LRhrg=
github.com/jacute/prettylogger v0.0.7/go.mod h1:3lynOiaGfyYdX6g8mz6cEg9CyLBZSTnPWwXdeQlao2w=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
103 changes: 103 additions & 0 deletions cli/internal/cli/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package cli

import (
"errors"
"flag"
"fmt"
jacfarm_client "jacfarmcli/internal/clients/jacfarm"
"jacfarmcli/internal/worker"
"log/slog"
"os"
"time"

"github.com/jacute/prettylogger"
)

var (
ErrUsage = errors.New("usage error")
)

type Args struct {
Addr string
FlagRe string
Token string
Port int
ExecutablePath string
Timeout int
AttackPeriod int
MaxConcurrentExploits int
}

func ParseArgs() (*Args, error) {
var args Args

flag.IntVar(&args.Timeout, "t", 5, "timeout for http client (in seconds)")
flag.StringVar(&args.Token, "a", "", "JacFARM auth token")
flag.IntVar(&args.AttackPeriod, "period", 5, "attack period (in seconds)")
flag.IntVar(&args.MaxConcurrentExploits, "c", 50, "max concurrent exploits in one time")
flag.IntVar(&args.Port, "p", 15050, "jacfarm port")
flag.StringVar(&args.FlagRe, "f", "[A-Z0-9]{31}=", "flag regex")

flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage:\n")
fmt.Fprintf(os.Stderr, " %s [flags] <addr> <executable>\n\n", os.Args[0])
fmt.Fprintf(os.Stderr, "Example:\n")
fmt.Fprintf(os.Stderr, " %s -a TOKEN -p 15050 localhost ./exploit\n\n", os.Args[0])
fmt.Fprintf(os.Stderr, "Flags:\n")
flag.PrintDefaults()
}
flag.Parse()

if args.Token == "" {
return nil, flag.ErrHelp
}

rest := flag.Args()
if len(rest) < 1 {
return nil, flag.ErrHelp
}

args.Addr = rest[0]
args.ExecutablePath = rest[1]

return &args, nil
}

func ValidateArgs(args *Args) error {
if _, err := os.Stat(args.ExecutablePath); os.IsNotExist(err) {
return err
}
return nil
}

// Run starts the worker
// Non-block function
func Run(args *Args, log *slog.Logger) error {
const op = "cli.Run"

client, err := jacfarm_client.New(
args.Addr,
args.Token,
jacfarm_client.WithCustomPort(args.Port),
jacfarm_client.WithTimeout(time.Duration(args.Timeout)*time.Second),
)
if err != nil {
log.Error("error creating jacfarm client", prettylogger.Err(err))
return fmt.Errorf("%s: error creating jacfarm client %w", op, err)
}
w, err := worker.New(
client, log,
args.ExecutablePath,
args.FlagRe,
worker.WithAttackPeriod(time.Duration(args.AttackPeriod)*time.Second),
worker.WithMaxConcurrentExploits(args.MaxConcurrentExploits),
)
if err != nil {
log.Error("error creating worker", prettylogger.Err(err))
return fmt.Errorf("%s: error creating worker %e", op, err)
}

w.Run()

return nil
}
Loading
Loading