Skip to content

Commit 88c5ff9

Browse files
authored
Merge pull request #41 from Jacute/dev
CLI
2 parents 9f6b7b0 + eb2df61 commit 88c5ff9

37 files changed

Lines changed: 1271 additions & 132 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ GREEN := \033[0;32m
55
PURPLE := \033[0;35m
66
RESET := \033[0m
77
RED := \033[0;31m
8-
JACFARM_API_KEY := $(shell openssl rand -hex 32)
8+
JACFARM_API_KEY := $(shell openssl rand -hex 16)
99
ADMIN_PASS := $(shell openssl rand -hex 8)
1010

1111

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
</p>
1414
<p align="center">
1515
<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>
16-
<a href="https://codecov.io/gh/Jacute/JacFARM"><img alt="Codecov" src="https://codecov.io/gh/Jacute/JacFARM/branch/master/graph/badge.svg"></a>
1716
<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>
1817
<a href="https://github.com/Jacute/JacFARM/releases"><img alt="Release" src="https://img.shields.io/github/v/release/Jacute/JacFARM"></a>
1918
</p>
@@ -46,6 +45,7 @@ make clean-all
4645
## Features
4746

4847
- Uploading exploits in ui
48+
- Uploading exploits in cli
4949
- Real-time configuration farm options like number of concurrently running exploits, the size of the flag sending batch, team ip addresses, etc
5050
- The ability to [change the plugin for sending flags to jury](./docs/flag_sender/flag_sender.md).
5151
- 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).
@@ -61,7 +61,7 @@ make clean-all
6161

6262
### Client
6363

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

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

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

7676
### Server
7777

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

8383
#### Plugins
8484

@@ -90,4 +90,4 @@ make clean-all
9090

9191
### Arch Diagram
9292

93-
![](./docs/img/diagram.jpg)
93+
![](./docs/img/diagram.png)

cli/cmd/cli/main.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"jacfarmcli/internal/cli"
7+
"log/slog"
8+
"os"
9+
"os/signal"
10+
"syscall"
11+
12+
"github.com/jacute/prettylogger"
13+
)
14+
15+
func main() {
16+
args, err := cli.ParseArgs()
17+
if err != nil {
18+
flag.Usage()
19+
os.Exit(1)
20+
}
21+
err = cli.ValidateArgs(args)
22+
if err != nil {
23+
fmt.Println(err.Error())
24+
flag.Usage()
25+
os.Exit(2)
26+
}
27+
28+
log := slog.New(prettylogger.NewColoredHandler(os.Stdout, &slog.HandlerOptions{
29+
Level: slog.LevelInfo,
30+
}))
31+
32+
sigCh := make(chan os.Signal, 1)
33+
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
34+
35+
log.Info("running script")
36+
err = cli.Run(args, log)
37+
if err != nil {
38+
os.Exit(2)
39+
}
40+
41+
sign := <-sigCh
42+
43+
log.Info("stopping script", slog.String("signal", sign.String()))
44+
}

cli/go.mod

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module jacfarmcli
2+
3+
go 1.25.4
4+
5+
require (
6+
github.com/jacute/prettylogger v0.0.7
7+
github.com/stretchr/testify v1.11.1
8+
go.uber.org/mock v0.6.0
9+
)
10+
11+
require (
12+
github.com/davecgh/go-spew v1.1.1 // indirect
13+
github.com/fatih/color v1.17.0 // indirect
14+
github.com/mattn/go-colorable v0.1.13 // indirect
15+
github.com/mattn/go-isatty v0.0.20 // indirect
16+
github.com/pmezard/go-difflib v1.0.0 // indirect
17+
golang.org/x/sys v0.18.0 // indirect
18+
gopkg.in/yaml.v3 v3.0.1 // indirect
19+
)

cli/go.sum

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
4+
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
5+
github.com/jacute/prettylogger v0.0.7 h1:inKCDEJ42j31hNVB6wAYZWOrc7E4QJ//x2hcR0LRhrg=
6+
github.com/jacute/prettylogger v0.0.7/go.mod h1:3lynOiaGfyYdX6g8mz6cEg9CyLBZSTnPWwXdeQlao2w=
7+
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
8+
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
9+
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
10+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
11+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
12+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
13+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
14+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
15+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
16+
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
17+
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
18+
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
19+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
20+
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
21+
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
22+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
23+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
24+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
25+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

cli/internal/cli/cli.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package cli
2+
3+
import (
4+
"errors"
5+
"flag"
6+
"fmt"
7+
jacfarm_client "jacfarmcli/internal/clients/jacfarm"
8+
"jacfarmcli/internal/worker"
9+
"log/slog"
10+
"os"
11+
"time"
12+
13+
"github.com/jacute/prettylogger"
14+
)
15+
16+
var (
17+
ErrUsage = errors.New("usage error")
18+
)
19+
20+
type Args struct {
21+
Addr string
22+
FlagRe string
23+
Token string
24+
Port int
25+
ExecutablePath string
26+
Timeout int
27+
AttackPeriod int
28+
MaxConcurrentExploits int
29+
}
30+
31+
func ParseArgs() (*Args, error) {
32+
var args Args
33+
34+
flag.IntVar(&args.Timeout, "t", 5, "timeout for http client (in seconds)")
35+
flag.StringVar(&args.Token, "a", "", "JacFARM auth token")
36+
flag.IntVar(&args.AttackPeriod, "period", 5, "attack period (in seconds)")
37+
flag.IntVar(&args.MaxConcurrentExploits, "c", 50, "max concurrent exploits in one time")
38+
flag.IntVar(&args.Port, "p", 15050, "jacfarm port")
39+
flag.StringVar(&args.FlagRe, "f", "[A-Z0-9]{31}=", "flag regex")
40+
41+
flag.Usage = func() {
42+
fmt.Fprintf(os.Stderr, "Usage:\n")
43+
fmt.Fprintf(os.Stderr, " %s [flags] <addr> <executable>\n\n", os.Args[0])
44+
fmt.Fprintf(os.Stderr, "Example:\n")
45+
fmt.Fprintf(os.Stderr, " %s -a TOKEN -p 15050 localhost ./exploit\n\n", os.Args[0])
46+
fmt.Fprintf(os.Stderr, "Flags:\n")
47+
flag.PrintDefaults()
48+
}
49+
flag.Parse()
50+
51+
if args.Token == "" {
52+
return nil, flag.ErrHelp
53+
}
54+
55+
rest := flag.Args()
56+
if len(rest) < 1 {
57+
return nil, flag.ErrHelp
58+
}
59+
60+
args.Addr = rest[0]
61+
args.ExecutablePath = rest[1]
62+
63+
return &args, nil
64+
}
65+
66+
func ValidateArgs(args *Args) error {
67+
if _, err := os.Stat(args.ExecutablePath); os.IsNotExist(err) {
68+
return err
69+
}
70+
return nil
71+
}
72+
73+
// Run starts the worker
74+
// Non-block function
75+
func Run(args *Args, log *slog.Logger) error {
76+
const op = "cli.Run"
77+
78+
client, err := jacfarm_client.New(
79+
args.Addr,
80+
args.Token,
81+
jacfarm_client.WithCustomPort(args.Port),
82+
jacfarm_client.WithTimeout(time.Duration(args.Timeout)*time.Second),
83+
)
84+
if err != nil {
85+
log.Error("error creating jacfarm client", prettylogger.Err(err))
86+
return fmt.Errorf("%s: error creating jacfarm client %w", op, err)
87+
}
88+
w, err := worker.New(
89+
client, log,
90+
args.ExecutablePath,
91+
args.FlagRe,
92+
worker.WithAttackPeriod(time.Duration(args.AttackPeriod)*time.Second),
93+
worker.WithMaxConcurrentExploits(args.MaxConcurrentExploits),
94+
)
95+
if err != nil {
96+
log.Error("error creating worker", prettylogger.Err(err))
97+
return fmt.Errorf("%s: error creating worker %e", op, err)
98+
}
99+
100+
w.Run()
101+
102+
return nil
103+
}

0 commit comments

Comments
 (0)