Skip to content

Commit

Permalink
wip: armory cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
moloch-- committed Dec 27, 2021
1 parent adbe7c7 commit 5c3fd8c
Showing 5 changed files with 563 additions and 15 deletions.
31 changes: 18 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
@@ -13,16 +13,21 @@ GO_VERSION = $(shell $(GO) version)
VERSION ?= $(shell git describe --abbrev=0)
COMPILED_AT = $(shell date +%s)
RELEASES_URL = https://api.github.com/repos/BishopFox/sliver/releases
PKG = github.com/bishopfox/sliver/client/version
CLIENT_ASSETS_PKG = github.com/bishopfox/sliver/client/assets
ARMORY_PUB_KEY = RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L
ARMORY_REPO_URL =
VERSION_PKG = github.com/bishopfox/sliver/client/version
GIT_DIRTY = $(shell git diff --quiet|| echo 'Dirty')
GIT_COMMIT = $(shell git rev-parse HEAD)
LDFLAGS = -ldflags "-s -w \
-X $(PKG).Version=$(VERSION) \
-X \"$(PKG).GoVersion=$(GO_VERSION)\" \
-X $(PKG).CompiledAt=$(COMPILED_AT) \
-X $(PKG).GithubReleasesURL=$(RELEASES_URL) \
-X $(PKG).GitCommit=$(GIT_COMMIT) \
-X $(PKG).GitDirty=$(GIT_DIRTY)"
-X $(VERSION_PKG).Version=$(VERSION) \
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUB_KEY) \
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"


#
@@ -70,12 +75,12 @@ ifeq ($(MAKECMDGOALS), linux)
# Redefine LDFLAGS to add the static part
LDFLAGS = -ldflags "-s -w \
-extldflags '-static' \
-X $(PKG).Version=$(VERSION) \
-X \"$(PKG).GoVersion=$(GO_VERSION)\" \
-X $(PKG).CompiledAt=$(COMPILED_AT) \
-X $(PKG).GithubReleasesURL=$(RELEASES_URL) \
-X $(PKG).GitCommit=$(GIT_COMMIT) \
-X $(PKG).GitDirty=$(GIT_DIRTY)"
-X $(VERSION_PKG).Version=$(VERSION) \
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY)"
endif

#
63 changes: 63 additions & 0 deletions client/assets/armories.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package assets

/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
)

const (
armoryConfigFileName = "armories.json"
)

var (
DefaultArmoryPublicKey string
DefaultArmoryRepoURL string

defaultArmoryConfig = &ArmoryConfig{
PublicKey: DefaultArmoryPublicKey,
RepoURL: DefaultArmoryRepoURL,
}
)

type ArmoryConfig struct {
PublicKey string `json:"public_key"`
RepoURL string `json:"repo_url"`
}

// GetArmoriesConfig - The parsed armory config file
func GetArmoriesConfig() []*ArmoryConfig {
armoryConfigPath := filepath.Join(GetRootAppDir(), armoryConfigFileName)
if _, err := os.Stat(armoryConfigPath); os.IsNotExist(err) {
return []*ArmoryConfig{defaultArmoryConfig}
}
data, err := ioutil.ReadFile(armoryConfigPath)
if err != nil {
return []*ArmoryConfig{defaultArmoryConfig}
}
var armoryConfigs []*ArmoryConfig
err = json.Unmarshal(data, &armoryConfigs)
if err != nil {
return []*ArmoryConfig{defaultArmoryConfig}
}
return append(armoryConfigs, defaultArmoryConfig)
}
3 changes: 1 addition & 2 deletions client/command/alias/install.go
Original file line number Diff line number Diff line change
@@ -22,7 +22,6 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"

"github.com/AlecAivazis/survey/v2"
@@ -154,7 +153,7 @@ func installArtifact(aliasGzFilePath string, installPath, artifactPath string, c
if err != nil {
return err
}
localArtifactPath := filepath.Join(installPath, path.Clean("/"+artifactPath))
localArtifactPath := filepath.Join(installPath, util.ResolvePath(artifactPath))
artifactDir := filepath.Dir(localArtifactPath)
if _, err := os.Stat(artifactDir); os.IsNotExist(err) {
os.MkdirAll(artifactDir, 0o700)
198 changes: 198 additions & 0 deletions client/command/armory/armory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package armory

/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import (
"errors"
"net/url"
"sync"
"time"

"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)

type ArmoryIndex struct {
Aliases []*ArmoryPackage `json:"aliases"`
Extensions []*ArmoryPackage `json:"extensions"`
}

type ArmoryPackage struct {
Name string `json:"name"`
CommandName string `json:"command_name"`
RepoURL string `json:"repo_url"`
PublicKey string `json:"public_key"`
}

type ArmoryHTTPConfig struct {
SkipCache bool
ProxyURL *url.URL
Timeout time.Duration
DisableTLSValidation bool
}

type armoryIndexCacheEntry struct {
Fetched time.Time
Index ArmoryIndex
LastErr error
}

type armoryPkgCacheEntry struct {
Fetched time.Time
Pkg ArmoryPackage
LastErr error
}

var (
// public key -> armoryCacheEntry
indexCache = sync.Map{}
// public key -> armoryPkgCacheEntry
pkgCache = sync.Map{}

cacheTime = time.Hour // This will kill a download if exceeded so needs to be large
defaultTimeout = 15 * time.Minute
)

// ArmoryCmd - The main armory command
func ArmoryCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
armoriesConfig := assets.GetArmoriesConfig()
con.PrintInfof("Fetching %d armory index(es) ...", len(armoriesConfig))
clientConfig := parseArmoryHTTPConfig()
indexes := fetchIndexes(armoriesConfig, clientConfig)
if 0 < len(indexes) {
con.PrintInfof("Fetching package information ...", len(armoriesConfig))
fetchPackageSignatures(indexes, clientConfig)
}

}

func parseArmoryHTTPConfig() ArmoryHTTPConfig {
return ArmoryHTTPConfig{
SkipCache: false,
ProxyURL: nil,
Timeout: defaultTimeout,
DisableTLSValidation: false,
}
}

func fetchIndexes(armoryConfigs []*assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) []ArmoryIndex {
wg := &sync.WaitGroup{}
for _, armoryConfig := range armoryConfigs {
wg.Add(1)
go fetchIndex(armoryConfig, clientConfig, wg)
}
wg.Wait()
indexes := []ArmoryIndex{}
indexCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(armoryIndexCacheEntry)
if cacheEntry.LastErr == nil {
indexes = append(indexes, cacheEntry.Index)
}
return true
})
return indexes
}

func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig, wg *sync.WaitGroup) {
defer wg.Done()
cacheEntry, ok := indexCache.Load(armoryConfig.PublicKey)
if ok {
cached := cacheEntry.(*armoryIndexCacheEntry)
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.SkipCache {
return
}
}

armoryResult := &armoryIndexCacheEntry{}
defer func() {
armoryResult.Fetched = time.Now()
indexCache.Store(armoryConfig.PublicKey, armoryResult)
}()

repoURL, err := url.Parse(armoryConfig.RepoURL)
if err != nil {
armoryResult.LastErr = err
return
}
if repoURL.Scheme != "https" && repoURL.Scheme != "http" {
armoryResult.LastErr = errors.New("invalid URL scheme")
return
}

var index *ArmoryIndex
if indexParser, ok := indexParsers[repoURL.Hostname()]; ok {
index, err = indexParser(armoryConfig, clientConfig)
} else {
index, err = DefaultArmoryIndexParser(armoryConfig, clientConfig)
}
armoryResult.Index = *index
armoryResult.LastErr = err
}

func fetchPackageSignatures(indexes []ArmoryIndex, clientConfig ArmoryHTTPConfig) {
wg := &sync.WaitGroup{}
for _, index := range indexes {
for _, armoryPkg := range index.Extensions {
wg.Add(1)
go fetchPackageSignature(wg, armoryPkg.RepoURL, armoryPkg.PublicKey, clientConfig)
}
for _, armoryPkg := range index.Aliases {
wg.Add(1)
go fetchPackageSignature(wg, armoryPkg.RepoURL, armoryPkg.PublicKey, clientConfig)
}
}
wg.Wait()
}

func fetchPackageSignature(wg *sync.WaitGroup, rawRepoURL string, rawPublicKey string, clientConfig ArmoryHTTPConfig) {
defer wg.Done()
cacheEntry, ok := pkgCache.Load(rawPublicKey)
if ok {
cached := cacheEntry.(*armoryIndexCacheEntry)
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.SkipCache {
return
}
}

var pkgCacheEntry *armoryPkgCacheEntry
defer func() {
pkgCacheEntry.Fetched = time.Now()
indexCache.Store(rawPublicKey, pkgCacheEntry)
}()

repoURL, err := url.Parse(rawRepoURL)
if err != nil {
pkgCacheEntry.LastErr = err
return
}
if repoURL.Scheme != "https" && repoURL.Scheme != "http" {
pkgCacheEntry.LastErr = errors.New("invalid URL scheme")
return
}

var pkg *ArmoryPackage
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
pkg, err = pkgParser(rawRepoURL, rawPublicKey, true, clientConfig)
} else {
pkg, err = DefaultArmoryPkgParser(rawRepoURL, rawPublicKey, true, clientConfig)
}
pkgCacheEntry.Pkg = *pkg
pkgCacheEntry.LastErr = err
}
Loading

0 comments on commit 5c3fd8c

Please sign in to comment.