diff --git a/client/command/README.md b/client/command/README.md index e2bfba0e7d..f2e17d866b 100644 --- a/client/command/README.md +++ b/client/command/README.md @@ -10,4 +10,5 @@ General guidance on the structure of this package: * The root command, and reused code should go into the file named after the command * For example, code shared between the `generate` and the `regenerate` command should go in `generate.go`, and any `regenerate` specific code should go in `regenerate.go` * Command entrypoint functions should have the suffix `Cmd` e.g., `GenerateCmd` is the entrypoint for `generate` + * Command entrypoints should always a function signature of `func (ctx *grumble.Context, con *console.SliverConsoleClient)` * Functions that are only ever exported for other commands to use should go in a `helpers.go`, if the function is used internally and exported follow the guidance above on shared code. diff --git a/client/command/env.go b/client/command/env.go deleted file mode 100644 index 947ea32363..0000000000 --- a/client/command/env.go +++ /dev/null @@ -1,111 +0,0 @@ -package command - -// /* -// Sliver Implant Framework -// Copyright (C) 2019 Bishop Fox - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// */ - -// import ( -// "context" -// "fmt" - -// "github.com/bishopfox/sliver/protobuf/commonpb" -// "github.com/bishopfox/sliver/protobuf/rpcpb" -// "github.com/bishopfox/sliver/protobuf/sliverpb" -// "github.com/desertbit/grumble" -// ) - -// func getEnv(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) { -// session := ActiveSession.Get() -// if session == nil { -// return -// } - -// name := ctx.Args.String("name") -// envInfo, err := rpc.GetEnv(context.Background(), &sliverpb.EnvReq{ -// Name: name, -// Request: ActiveSession.Request(ctx), -// }) - -// if err != nil { -// fmt.Printf(Warn+"Error: %v", err) -// return -// } - -// for _, envVar := range envInfo.Variables { -// fmt.Printf("%s=%s\n", envVar.Key, envVar.Value) -// } -// } - -// func setEnv(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) { -// session := ActiveSession.Get() -// if session == nil { -// return -// } - -// name := ctx.Args.String("name") -// value := ctx.Args.String("value") -// if name == "" || value == "" { -// fmt.Printf(Warn + "Usage: setenv KEY VALUE\n") -// return -// } - -// envInfo, err := rpc.SetEnv(context.Background(), &sliverpb.SetEnvReq{ -// Variable: &commonpb.EnvVar{ -// Key: name, -// Value: value, -// }, -// Request: ActiveSession.Request(ctx), -// }) -// if err != nil { -// fmt.Printf(Warn+"Error: %v", err) -// return -// } -// if envInfo.Response != nil && envInfo.Response.Err != "" { -// fmt.Printf(Warn+"Error: %s", envInfo.Response.Err) -// return -// } -// fmt.Printf(Info+"set %s to %s\n", name, value) -// } - -// func unsetEnv(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) { -// session := ActiveSession.Get() -// if session == nil { -// return -// } - -// name := ctx.Args.String("name") -// if name == "" { -// fmt.Printf(Warn + "Usage: setenv NAME\n") -// return -// } - -// unsetResp, err := rpc.UnsetEnv(context.Background(), &sliverpb.UnsetEnvReq{ -// Name: name, -// Request: ActiveSession.Request(ctx), -// }) - -// if err != nil { -// fmt.Printf(Warn+"Error: %v", err) -// return -// } - -// if unsetResp.Response != nil && unsetResp.Response.Err != "" { -// fmt.Printf(Warn+"Error: %s\n", unsetResp.Response.Err) -// return -// } -// fmt.Printf(Info+"Successfully unset %s\n", name) -// } diff --git a/client/command/environment/README.md b/client/command/environment/README.md new file mode 100644 index 0000000000..20dd1fa8e3 --- /dev/null +++ b/client/command/environment/README.md @@ -0,0 +1,4 @@ +Environment +=========== + +Contains command implementations for manipulating remote environment variables. diff --git a/client/command/environment/get.go b/client/command/environment/get.go new file mode 100644 index 0000000000..b85ae451b1 --- /dev/null +++ b/client/command/environment/get.go @@ -0,0 +1,49 @@ +package environment + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/sliverpb" + "github.com/desertbit/grumble" +) + +func EnvGetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) { + session := con.ActiveSession.Get() + if session == nil { + return + } + + name := ctx.Args.String("name") + envInfo, err := con.Rpc.GetEnv(context.Background(), &sliverpb.EnvReq{ + Name: name, + Request: con.ActiveSession.Request(ctx), + }) + + if err != nil { + con.PrintErrorf("%s\n", err) + return + } + + for _, envVar := range envInfo.Variables { + con.Printf("%s=%s\n", envVar.Key, envVar.Value) + } +} diff --git a/client/command/environment/set.go b/client/command/environment/set.go new file mode 100644 index 0000000000..5f58484761 --- /dev/null +++ b/client/command/environment/set.go @@ -0,0 +1,59 @@ +package environment + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/commonpb" + "github.com/bishopfox/sliver/protobuf/sliverpb" + "github.com/desertbit/grumble" +) + +func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) { + session := con.ActiveSession.Get() + if session == nil { + return + } + + name := ctx.Args.String("name") + value := ctx.Args.String("value") + if name == "" || value == "" { + con.PrintErrorf("Usage: setenv KEY VALUE\n") + return + } + + envInfo, err := con.Rpc.SetEnv(context.Background(), &sliverpb.SetEnvReq{ + Variable: &commonpb.EnvVar{ + Key: name, + Value: value, + }, + Request: con.ActiveSession.Request(ctx), + }) + if err != nil { + con.PrintErrorf("%s\n", err) + return + } + if envInfo.Response != nil && envInfo.Response.Err != "" { + con.PrintErrorf("%s\n", envInfo.Response.Err) + return + } + con.PrintInfof("Set %s to %s\n", name, value) +} diff --git a/client/command/environment/unset.go b/client/command/environment/unset.go new file mode 100644 index 0000000000..c9fc023cb0 --- /dev/null +++ b/client/command/environment/unset.go @@ -0,0 +1,56 @@ +package environment + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/sliverpb" + "github.com/desertbit/grumble" +) + +func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) { + session := con.ActiveSession.Get() + if session == nil { + return + } + + name := ctx.Args.String("name") + if name == "" { + con.PrintErrorf("Usage: setenv NAME\n") + return + } + + unsetResp, err := con.Rpc.UnsetEnv(context.Background(), &sliverpb.UnsetEnvReq{ + Name: name, + Request: con.ActiveSession.Request(ctx), + }) + + if err != nil { + con.PrintErrorf("%s\n", err) + return + } + + if unsetResp.Response != nil && unsetResp.Response.Err != "" { + con.PrintErrorf("%s\n", unsetResp.Response.Err) + return + } + con.PrintInfof("Successfully unset %s\n", name) +} diff --git a/client/command/jobs/README.md b/client/command/jobs/README.md new file mode 100644 index 0000000000..3921833db9 --- /dev/null +++ b/client/command/jobs/README.md @@ -0,0 +1,4 @@ +Jobs +===== + +Implements commands related to starting/killing jobs like `jobs` and starting C2 listeners (`mtls`, `http`, etc.) diff --git a/client/command/jobs/dns.go b/client/command/jobs/dns.go new file mode 100644 index 0000000000..5e22c0028a --- /dev/null +++ b/client/command/jobs/dns.go @@ -0,0 +1,36 @@ +package jobs + +import ( + "context" + "strings" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/desertbit/grumble" +) + +func DNSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) { + + domains := strings.Split(ctx.Flags.String("domains"), ",") + for _, domain := range domains { + if !strings.HasSuffix(domain, ".") { + domain += "." + } + } + + lport := uint16(ctx.Flags.Int("lport")) + + con.PrintInfof("Starting DNS listener with parent domain(s) %v ...\n", domains) + dns, err := con.Rpc.StartDNSListener(context.Background(), &clientpb.DNSListenerReq{ + Domains: domains, + Port: uint32(lport), + Canaries: !ctx.Flags.Bool("no-canaries"), + Persistent: ctx.Flags.Bool("persistent"), + }) + con.Println() + if err != nil { + con.PrintErrorf("%s\n", err) + } else { + con.PrintInfof("Successfully started job #%d\n", dns.JobID) + } +} diff --git a/client/command/jobs/http.go b/client/command/jobs/http.go new file mode 100644 index 0000000000..f0881e8899 --- /dev/null +++ b/client/command/jobs/http.go @@ -0,0 +1,28 @@ +package jobs + +import ( + "context" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/desertbit/grumble" +) + +func HTTPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) { + domain := ctx.Flags.String("domain") + lport := uint16(ctx.Flags.Int("lport")) + + con.PrintInfof("Starting HTTP %s:%d listener ...\n", domain, lport) + http, err := con.Rpc.StartHTTPListener(context.Background(), &clientpb.HTTPListenerReq{ + Domain: domain, + Website: ctx.Flags.String("website"), + Port: uint32(lport), + Secure: false, + Persistent: ctx.Flags.Bool("persistent"), + }) + if err != nil { + con.PrintErrorf("%s\n", err) + } else { + con.PrintInfof("Successfully started job #%d\n", http.JobID) + } +} diff --git a/client/command/jobs/https.go b/client/command/jobs/https.go new file mode 100644 index 0000000000..57928d2572 --- /dev/null +++ b/client/command/jobs/https.go @@ -0,0 +1,56 @@ +package jobs + +import ( + "context" + "io/ioutil" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/desertbit/grumble" +) + +func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) { + domain := ctx.Flags.String("domain") + website := ctx.Flags.String("website") + lport := uint16(ctx.Flags.Int("lport")) + + cert, key, err := getLocalCertificatePair(ctx) + if err != nil { + con.Println() + con.PrintErrorf("Failed to load local certificate %s\n", err) + return + } + + con.PrintInfof("Starting HTTPS %s:%d listener ...\n", domain, lport) + https, err := con.Rpc.StartHTTPSListener(context.Background(), &clientpb.HTTPListenerReq{ + Domain: domain, + Website: website, + Port: uint32(lport), + Secure: true, + Cert: cert, + Key: key, + ACME: ctx.Flags.Bool("lets-encrypt"), + Persistent: ctx.Flags.Bool("persistent"), + }) + con.Println() + if err != nil { + con.PrintErrorf("%s\n", err) + } else { + con.PrintInfof("Successfully started job #%d\n", https.JobID) + } +} + +func getLocalCertificatePair(ctx *grumble.Context) ([]byte, []byte, error) { + if ctx.Flags.String("cert") == "" && ctx.Flags.String("key") == "" { + return nil, nil, nil + } + cert, err := ioutil.ReadFile(ctx.Flags.String("cert")) + if err != nil { + return nil, nil, err + } + key, err := ioutil.ReadFile(ctx.Flags.String("key")) + if err != nil { + return nil, nil, err + } + return cert, key, nil +} diff --git a/client/command/jobs/job.go b/client/command/jobs/job.go deleted file mode 100644 index 610bad0525..0000000000 --- a/client/command/jobs/job.go +++ /dev/null @@ -1,231 +0,0 @@ -package command - -// /* -// Sliver Implant Framework -// Copyright (C) 2019 Bishop Fox - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// */ - -// import ( -// "context" -// "fmt" -// "io/ioutil" -// "os" -// "sort" -// "strings" -// "text/tabwriter" - -// "github.com/bishopfox/sliver/protobuf/clientpb" -// "github.com/bishopfox/sliver/protobuf/commonpb" -// "github.com/bishopfox/sliver/protobuf/rpcpb" - -// // "github.com/bishopfox/sliver/protobuf/sliverpb" - -// "github.com/desertbit/grumble" -// // "github.com/golang/protobuf/proto" -// ) - -// func jobs(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) { -// if ctx.Flags.Int("kill") != -1 { -// killJob(uint32(ctx.Flags.Int("kill")), rpc) -// } else if ctx.Flags.Bool("kill-all") { -// killAllJobs(rpc) -// } else { -// jobs, err := rpc.GetJobs(context.Background(), &commonpb.Empty{}) -// if err != nil { -// fmt.Printf(Warn+"%s", err) -// return -// } -// // Convert to a map -// activeJobs := map[uint32]*clientpb.Job{} -// for _, job := range jobs.Active { -// activeJobs[job.ID] = job -// } -// if 0 < len(activeJobs) { -// printJobs(activeJobs) -// } else { -// fmt.Printf(Info + "No active jobs\n") -// } -// } -// } - -// func killAllJobs(rpc rpcpb.SliverRPCClient) { -// jobs, err := rpc.GetJobs(context.Background(), &commonpb.Empty{}) -// if err != nil { -// fmt.Printf(Warn+"%s\n", err) -// return -// } -// for _, job := range jobs.Active { -// killJob(job.ID, rpc) -// } -// } - -// func killJob(jobID uint32, rpc rpcpb.SliverRPCClient) { -// fmt.Printf(Info+"Killing job #%d ...\n", jobID) -// jobKill, err := rpc.KillJob(context.Background(), &clientpb.KillJobReq{ -// ID: jobID, -// }) -// if err != nil { -// fmt.Printf(Warn+"%s\n", err) -// } else { -// fmt.Printf(Info+"Successfully killed job #%d\n", jobKill.ID) -// } -// } - -// func printJobs(jobs map[uint32]*clientpb.Job) { -// table := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0) -// fmt.Fprintf(table, "ID\tName\tProtocol\tPort\t\n") -// fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", -// strings.Repeat("=", len("ID")), -// strings.Repeat("=", len("Name")), -// strings.Repeat("=", len("Protocol")), -// strings.Repeat("=", len("Port"))) - -// var keys []int -// for _, job := range jobs { -// keys = append(keys, int(job.ID)) -// } -// sort.Ints(keys) // Fucking Go can't sort int32's, so we convert to/from int's - -// for _, k := range keys { -// job := jobs[uint32(k)] -// fmt.Fprintf(table, "%d\t%s\t%s\t%d\t\n", job.ID, job.Name, job.Protocol, job.Port) -// } -// table.Flush() -// } - -// func startMTLSListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) { -// server := ctx.Flags.String("server") -// lport := uint16(ctx.Flags.Int("lport")) - -// fmt.Printf(Info + "Starting mTLS listener ...\n") -// mtls, err := rpc.StartMTLSListener(context.Background(), &clientpb.MTLSListenerReq{ -// Host: server, -// Port: uint32(lport), -// Persistent: ctx.Flags.Bool("persistent"), -// }) -// if err != nil { -// fmt.Printf("\n"+Warn+"%s\n", err) -// } else { -// fmt.Printf("\n"+Info+"Successfully started job #%d\n", mtls.JobID) -// } -// } - -// func startWGListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) { -// lport := uint16(ctx.Flags.Int("lport")) -// nport := uint16(ctx.Flags.Int("nport")) -// keyExchangePort := uint16(ctx.Flags.Int("key-port")) - -// fmt.Printf(Info + "Starting Wireguard listener ...\n") -// wg, err := rpc.StartWGListener(context.Background(), &clientpb.WGListenerReq{ -// Port: uint32(lport), -// NPort: uint32(nport), -// KeyPort: uint32(keyExchangePort), -// Persistent: ctx.Flags.Bool("persistent"), -// }) -// if err != nil { -// fmt.Printf("\n"+Warn+"%s\n", err) -// } else { -// fmt.Printf("\n"+Info+"Successfully started job #%d\n", wg.JobID) -// } -// } - -// func startDNSListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) { - -// domains := strings.Split(ctx.Flags.String("domains"), ",") -// for _, domain := range domains { -// if !strings.HasSuffix(domain, ".") { -// domain += "." -// } -// } - -// lport := uint16(ctx.Flags.Int("lport")) - -// fmt.Printf(Info+"Starting DNS listener with parent domain(s) %v ...\n", domains) -// dns, err := rpc.StartDNSListener(context.Background(), &clientpb.DNSListenerReq{ -// Domains: domains, -// Port: uint32(lport), -// Canaries: !ctx.Flags.Bool("no-canaries"), -// Persistent: ctx.Flags.Bool("persistent"), -// }) -// if err != nil { -// fmt.Printf("\n"+Warn+"%s\n", err) -// } else { -// fmt.Printf("\n"+Info+"Successfully started job #%d\n", dns.JobID) -// } -// } - -// func startHTTPSListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) { -// domain := ctx.Flags.String("domain") -// website := ctx.Flags.String("website") -// lport := uint16(ctx.Flags.Int("lport")) - -// cert, key, err := getLocalCertificatePair(ctx) -// if err != nil { -// fmt.Printf("\n"+Warn+"Failed to load local certificate %v", err) -// return -// } - -// fmt.Printf(Info+"Starting HTTPS %s:%d listener ...\n", domain, lport) -// https, err := rpc.StartHTTPSListener(context.Background(), &clientpb.HTTPListenerReq{ -// Domain: domain, -// Website: website, -// Port: uint32(lport), -// Secure: true, -// Cert: cert, -// Key: key, -// ACME: ctx.Flags.Bool("lets-encrypt"), -// Persistent: ctx.Flags.Bool("persistent"), -// }) -// if err != nil { -// fmt.Printf("\n"+Warn+"%s\n", err) -// } else { -// fmt.Printf("\n"+Info+"Successfully started job #%d\n", https.JobID) -// } -// } - -// func getLocalCertificatePair(ctx *grumble.Context) ([]byte, []byte, error) { -// if ctx.Flags.String("cert") == "" && ctx.Flags.String("key") == "" { -// return nil, nil, nil -// } -// cert, err := ioutil.ReadFile(ctx.Flags.String("cert")) -// if err != nil { -// return nil, nil, err -// } -// key, err := ioutil.ReadFile(ctx.Flags.String("key")) -// if err != nil { -// return nil, nil, err -// } -// return cert, key, nil -// } - -// func startHTTPListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) { -// domain := ctx.Flags.String("domain") -// lport := uint16(ctx.Flags.Int("lport")) - -// fmt.Printf(Info+"Starting HTTP %s:%d listener ...\n", domain, lport) -// http, err := rpc.StartHTTPListener(context.Background(), &clientpb.HTTPListenerReq{ -// Domain: domain, -// Website: ctx.Flags.String("website"), -// Port: uint32(lport), -// Secure: false, -// Persistent: ctx.Flags.Bool("persistent"), -// }) -// if err != nil { -// fmt.Printf(Warn+"%s\n", err) -// } else { -// fmt.Printf(Info+"Successfully started job #%d\n", http.JobID) -// } -// } diff --git a/client/command/jobs/jobs-kill.go b/client/command/jobs/jobs-kill.go new file mode 100644 index 0000000000..a0f7e789ea --- /dev/null +++ b/client/command/jobs/jobs-kill.go @@ -0,0 +1,32 @@ +package jobs + +import ( + "context" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/bishopfox/sliver/protobuf/commonpb" +) + +func JobKillCmd(jobID uint32, con *console.SliverConsoleClient) { + con.PrintInfof("Killing job #%d ...\n", jobID) + jobKill, err := con.Rpc.KillJob(context.Background(), &clientpb.KillJobReq{ + ID: jobID, + }) + if err != nil { + con.PrintErrorf("%s\n", err) + } else { + con.PrintInfof("Successfully killed job #%d\n", jobKill.ID) + } +} + +func killAllJobs(con *console.SliverConsoleClient) { + jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{}) + if err != nil { + con.PrintErrorf("%s\n", err) + return + } + for _, job := range jobs.Active { + JobKillCmd(job.ID, con) + } +} diff --git a/client/command/jobs/jobs.go b/client/command/jobs/jobs.go new file mode 100644 index 0000000000..6880069df1 --- /dev/null +++ b/client/command/jobs/jobs.go @@ -0,0 +1,80 @@ +package jobs + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + "fmt" + "os" + "sort" + "strings" + "text/tabwriter" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/bishopfox/sliver/protobuf/commonpb" + + "github.com/desertbit/grumble" +) + +func JobsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) { + if ctx.Flags.Int("kill") != -1 { + JobKillCmd(uint32(ctx.Flags.Int("kill")), con) + } else if ctx.Flags.Bool("kill-all") { + killAllJobs(con) + } else { + jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{}) + if err != nil { + con.PrintErrorf("%s\n", err) + return + } + // Convert to a map + activeJobs := map[uint32]*clientpb.Job{} + for _, job := range jobs.Active { + activeJobs[job.ID] = job + } + if 0 < len(activeJobs) { + printJobs(activeJobs) + } else { + con.PrintInfof("No active jobs\n") + } + } +} + +func printJobs(jobs map[uint32]*clientpb.Job) { + table := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0) + fmt.Fprintf(table, "ID\tName\tProtocol\tPort\t\n") + fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", + strings.Repeat("=", len("ID")), + strings.Repeat("=", len("Name")), + strings.Repeat("=", len("Protocol")), + strings.Repeat("=", len("Port"))) + + var keys []int + for _, job := range jobs { + keys = append(keys, int(job.ID)) + } + sort.Ints(keys) // Fucking Go can't sort int32's, so we convert to/from int's + + for _, k := range keys { + job := jobs[uint32(k)] + fmt.Fprintf(table, "%d\t%s\t%s\t%d\t\n", job.ID, job.Name, job.Protocol, job.Port) + } + table.Flush() +} diff --git a/client/command/jobs/mtls.go b/client/command/jobs/mtls.go new file mode 100644 index 0000000000..6eb31e5a73 --- /dev/null +++ b/client/command/jobs/mtls.go @@ -0,0 +1,27 @@ +package jobs + +import ( + "context" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/desertbit/grumble" +) + +func MTLSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) { + server := ctx.Flags.String("server") + lport := uint16(ctx.Flags.Int("lport")) + + con.PrintInfof("Starting mTLS listener ...\n") + mtls, err := con.Rpc.StartMTLSListener(context.Background(), &clientpb.MTLSListenerReq{ + Host: server, + Port: uint32(lport), + Persistent: ctx.Flags.Bool("persistent"), + }) + con.Println() + if err != nil { + con.PrintErrorf("%s\n", err) + } else { + con.PrintInfof("Successfully started job #%d\n", mtls.JobID) + } +} diff --git a/client/command/jobs/wg.go b/client/command/jobs/wg.go new file mode 100644 index 0000000000..8dae3458c3 --- /dev/null +++ b/client/command/jobs/wg.go @@ -0,0 +1,28 @@ +package jobs + +import ( + "context" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/desertbit/grumble" +) + +func WGListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) { + lport := uint16(ctx.Flags.Int("lport")) + nport := uint16(ctx.Flags.Int("nport")) + keyExchangePort := uint16(ctx.Flags.Int("key-port")) + + con.PrintInfof("Starting Wireguard listener ...\n") + wg, err := con.Rpc.StartWGListener(context.Background(), &clientpb.WGListenerReq{ + Port: uint32(lport), + NPort: uint32(nport), + KeyPort: uint32(keyExchangePort), + Persistent: ctx.Flags.Bool("persistent"), + }) + if err != nil { + con.PrintErrorf("%s\n", err) + } else { + con.PrintInfof("Successfully started job #%d\n", wg.JobID) + } +}