Compare commits

...
Author SHA1 Message Date
Ronan Kervella 8a5970c3da Merge branch 'master' into c2profile_update_bugfix 2024-05-03 09:21:40 -07:00
Ronan Kervella 2eb2ac8121 Merge pull request #1659 from C-Sto/bugfix-extensions
fix extension registration, run, and load flows
2024-05-03 09:21:29 -07:00
Ronan Kervella daf7d404b1 Merge branch 'master' into c2profile_update_bugfix 2024-05-03 09:21:23 -07:00
Ronan Kervella c73b183f74 Merge branch 'master' into bugfix-extensions 2024-05-03 08:32:44 -07:00
Ronan Kervella 7564312f5e Merge pull request #1666 from BishopFox/v1.6.0/fix-alias-hostproc
Fix Default Linux Host Process for Aliases
2024-05-03 08:32:22 -07:00
Raf 6134b78afb Fix default linux host process for aliases 2024-05-03 08:24:32 -04:00
Ronan Kervella 3db9946608 Merge branch 'master' into bugfix-extensions 2024-05-02 14:11:02 -07:00
Joe ef09058498 Merge pull request #1664 from BishopFox/v1.6.0/ls-rootdir
Adding root directory entry to `ls` command
2024-05-02 16:06:46 -05:00
Tim Makram Ghatas 331faeb8ef fix commandline paramameters for profile staging 2024-05-02 20:30:33 +02:00
Tim Makram Ghatas 4ae195e3eb fix c2 profile update issue with headers, cookies and segments 2024-05-02 19:09:46 +02:00
Ronan Kervella f8f8ba5864 Merge branch 'master' into bugfix-extensions 2024-05-02 09:59:25 -07:00
Raf 216e3b9759 Adds an entry in the directory listing returned by ls for the root directory ('.'). 2024-05-02 12:12:32 -04:00
Joe f1bc5074f9 Merge pull request #1663 from BishopFox/docs/compile-from-source
Add to index
2024-05-01 21:40:10 -05:00
Joe 1b5d3436eb Merge pull request #1662 from BishopFox/docs/compile-from-source
Improve compile from source docs
2024-05-01 21:23:20 -05:00
Joe 9397fcb4ae Merge pull request #1661 from BishopFox/fix_stage_encoder
fix merge error for stage encoders
2024-05-01 20:11:35 -05:00
Joe 04c6c3f800 Merge pull request #1658 from C-Sto/bugfix-extensionnoargs
fix: handle noargs case
2024-05-01 20:08:17 -05:00
C-Sto 7842a6c4c6 add init back in, handle nils/zero values more elegantly 2024-05-02 08:49:21 +08:00
Tim Makram Ghatas 6c815a4432 fix merge error for stage encoders 2024-05-02 02:06:54 +02:00
C-Sto 81358e31bb fix extension registration, run, and load flows 2024-05-01 12:31:19 +08:00
C-Sto 7bea772dbd fix: handle noargs case 2024-05-01 12:15:33 +08:00
7 changed files with 231 additions and 46 deletions

No files matched your search

+1 -1
View File
@@ -57,7 +57,7 @@ var (
defaultHostProc = map[string]string{
"windows": windowsDefaultHostProc,
"linux": windowsDefaultHostProc,
"linux": linuxDefaultHostProc,
"darwin": macosDefaultHostProc,
}
)
+101 -33
View File
@@ -99,6 +99,7 @@ type ExtCommand struct {
Arguments []*extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"`
DependsOn string `json:"depends_on"`
Init string `json:"init"`
Manifest *ExtensionManifest
}
@@ -371,38 +372,50 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
}
extensionList = extList.Names
}
depLoaded := false
//extensionList contains all *implant* loaded extensions. Is a sha256 sum of the relevant file.
//get the file hash to compare against implant extensions later
toberunfilepath, err := ext.getFileForTarget(goos, goarch)
if err != nil {
return err
}
//we need to check the dependent if it's a bof
if ext.DependsOn != "" {
//verify extension is in loaded list
if extension, found := loadedExtensions[ext.DependsOn]; found {
toberunfilepath, err = extension.getFileForTarget(goos, goarch)
if err != nil {
return err
}
} else {
// handle error
return fmt.Errorf("attempted to load non-existing extension: %s", ext.DependsOn)
}
}
//todo, maybe cache these values somewhere
toberunfiledata, err := os.ReadFile(toberunfilepath)
if err != nil {
return err
}
if len(toberunfiledata) == 0 {
//read an empty file, bail out
return errors.New("read empty extension file content")
}
bd := sha256.Sum256(toberunfiledata)
toberunhash := hex.EncodeToString(bd[:])
for _, extName := range extensionList {
if !depLoaded {
if extN, ok := loadedExtensions[cmd.Name()]; ok && extName == extN.DependsOn {
depLoaded = true
}
}
//check if the file suffixes are the same (should only be relevant to the same extension package), and don't load the file if it's already there
extN := loadedExtensions[cmd.Name()]
if extN != nil {
//confirm we haven't already loaded the file
for _, extf := range extN.Files {
if strings.HasSuffix(binPath, extf.Path) {
//file already exists, no need to load it
return nil
}
}
}
if extN, ok := loadedExtensions[cmd.Name()]; ok && extN.CommandName == extName {
//check if extension we are trying to run (ext.CommandName or ext.DependsOn, for bofs) is already loaded
if extName == toberunhash {
//exists on the other side, exit early
return nil
}
}
// Extension not found, let's load it
if filepath.Ext(binPath) == ".o" {
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
// Auto load the coff loader if we have it
if !depLoaded {
if errLoad := loadDep(goos, goarch, ext.DependsOn, cmd, con); errLoad != nil {
return errLoad
}
}
return nil
// BOFs are not loaded by the DLL loader, but we need to load the loader (more load more good)
if ext.DependsOn != "" {
return loadDep(goos, goarch, ext.DependsOn, cmd, con)
}
binData, err := os.ReadFile(binPath)
if err != nil {
@@ -414,17 +427,45 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
return nil
}
func registerExtension(goos string, _ *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverClient) error {
func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverClient) error {
//set extension name to a hash of the data to avoid loading more than one instance
bd := sha256.Sum256(binData)
name := hex.EncodeToString(bd[:])
sess, beac := con.ActiveTarget.GetInteractive()
ctrl := make(chan bool)
//first time run of an extension will require some waiting depending on the size
if sess != nil {
msg := fmt.Sprintf("Sending %s to implant ...", ext.CommandName)
con.SpinUntil(msg, ctrl)
}
//don't block if we are in beacon mode
if beac != nil && sess == nil {
go func() {
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: name,
Data: binData,
OS: goos,
Init: ext.Init,
Request: con.ActiveTarget.Request(cmd),
})
if err != nil {
con.PrintErrorf("Error registering extension: %s\n", err)
}
if registerResp.Response != nil && registerResp.Response.Err != "" {
con.PrintErrorf("Error registering extension: %s\n", errors.New(registerResp.Response.Err))
}
}()
return nil
}
//session mode (hopefully)
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: name,
Data: binData,
OS: goos,
//Init: ext.Init, ?
Name: name,
Data: binData,
OS: goos,
Request: con.ActiveTarget.Request(cmd),
})
ctrl <- true
<-ctrl
if err != nil {
return err
}
@@ -525,6 +566,33 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
}
bd := sha256.Sum256(extdata)
name := hex.EncodeToString(bd[:])
if isBOF {
//if we are using a bof, we are actually calling the coffloader extension - so get the file from dep ref and use that shasum
if extension, found := loadedExtensions[ext.DependsOn]; found {
dep, err := extension.getFileForTarget(goos, goarch)
if err != nil {
con.PrintErrorf("could not get file for extension %s", ext.DependsOn)
return
}
depdata, err := os.ReadFile(dep)
if err != nil {
con.PrintErrorf("dep read file error: %s\n", err)
return
}
if len(depdata) == 0 {
con.PrintErrorf("read empty file: %s\n", dep)
return
}
bd = sha256.Sum256(depdata)
name = hex.EncodeToString(bd[:])
} else {
// handle error
con.PrintErrorf("attempted to load non-existing extension: %s", ext.DependsOn)
return
}
}
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
Name: name,
Export: entryPoint,
@@ -579,7 +647,7 @@ func getExtArgs(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]b
missingRequiredArgs := make([]string, 0)
// If we have an extension that expects a single string, but more than one has been parsed, combine them
if len(ext.Arguments) == 1 && strings.Contains(ext.Arguments[0].Type, "string") {
if len(ext.Arguments) == 1 && strings.Contains(ext.Arguments[0].Type, "string") && len(args) > 0 {
// The loop below will only read the first element of args because ext.Arguments is 1
args[0] = strings.Join(args, " ")
}
+22
View File
@@ -184,6 +184,28 @@ func Commands(con *console.SliverClient) []*cobra.Command {
}
profilesCmd.AddCommand(profilesNewCmd)
profilesStageCmd := &cobra.Command{
Use: consts.StageStr,
Short: "Generate implant from a profile and encode or encrypt it",
Args: cobra.ExactArgs(1),
Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.StageStr}),
Run: func(cmd *cobra.Command, args []string) {
ProfilesStageCmd(cmd, con, args)
},
}
flags.Bind("profiles", false, profilesStageCmd, func(f *pflag.FlagSet) {
f.StringP("save", "s", "", "directory/file to the binary to")
f.StringP("name", "n", "", "Implant name")
f.StringP("aes-encrypt-key", "k", "", "AES Encryption Key")
f.StringP("aes-encrypt-iv", "i", "", "AES Encryption IV")
f.StringP("rc4-encrypt-key", "r", "", "RC4 encryption key")
f.BoolP("prepend-size", "p", false, "Prepend stage size")
f.StringP("compress", "c", "", "Compress stage (zlib, gzip, deflate9 or deflate)")
})
carapace.Gen(profilesStageCmd).PositionalCompletion(ProfileNameCompleter(con))
profilesCmd.AddCommand(profilesStageCmd)
// Session flags and completions.
coreImplantFlags("session", profilesNewCmd)
compileImplantFlags("session", profilesNewCmd)
+4
View File
@@ -108,6 +108,7 @@ var (
// Profiles
consts.ProfilesStr + sep + consts.NewStr: newProfileHelp,
consts.ProfilesStr + sep + consts.GenerateStr: generateProfileHelp,
consts.ProfilesStr + sep + consts.StageStr: generateProfileStageHelp,
// Reactions
consts.ReactionStr: reactionHelp,
@@ -249,6 +250,9 @@ settings. Generated implants will still have per-binary certificates/obfuscation
generateProfileHelp = `[[.Bold]]Command:[[.Normal]] generate [name] <options>
[[.Bold]]About:[[.Normal]] Generate an implant from a saved profile (see 'profiles new --help').`
generateProfileStageHelp = `[[.Bold]]Command:[[.Normal]] stage [name] <options>
[[.Bold]]About:[[.Normal]] Generate and encrypt or encode an implant from a saved profile (see 'profiles stage --help').`
msfHelp = `[[.Bold]]Command:[[.Normal]] msf [--lhost] <options>
[[.Bold]]About:[[.Normal]] Execute a metasploit payload in the current process.`
+30 -6
View File
@@ -155,7 +155,7 @@ func dirListHandler(data []byte, resp RPCResponse) {
path, filter := determineDirPathFilter(targetPath)
dir, files, err := getDirList(path)
dir, rootDirEntry, files, err := getDirList(path)
// Convert directory listing to protobuf
timezone, offset := time.Now().Zone()
@@ -166,6 +166,19 @@ func dirListHandler(data []byte, resp RPCResponse) {
dirList.Exists = false
}
dirList.Files = []*sliverpb.FileInfo{}
rootDirInfo, err := rootDirEntry.Info()
if err == nil && filter == "" {
// We should not get an error because we created the DirEntry object from the FileInfo object
dirList.Files = append(dirList.Files, &sliverpb.FileInfo{
Name: ".", // Cannot use the name from the FileInfo / DirEntry because that is the name of the directory
Size: rootDirInfo.Size(),
ModTime: rootDirInfo.ModTime().Unix(),
Mode: rootDirInfo.Mode().String(),
Uid: getUid(rootDirInfo),
Gid: getGid(rootDirInfo),
IsDir: rootDirInfo.IsDir(),
})
}
var match bool = false
var linkPath string = ""
@@ -226,19 +239,30 @@ func dirListHandler(data []byte, resp RPCResponse) {
resp(data, err)
}
func getDirList(target string) (string, []fs.DirEntry, error) {
func getDirList(target string) (string, fs.DirEntry, []fs.DirEntry, error) {
dir, err := filepath.Abs(target)
if err != nil {
// {{if .Config.Debug}}
log.Printf("dir list failed to construct path %s", err)
// {{end}}
return "", nil, err
return "", nil, nil, err
}
if _, err := os.Stat(dir); !os.IsNotExist(err) {
if rootInfo, err := os.Stat(dir); !os.IsNotExist(err) {
/*
We could place the entry for the directory itself
at the beginning of the returned slice of DirEntry
objects, but then it is not clear if that is the
root directory or a directory / file in the root
directory with the same name as the root directory.
Using WalkDir is not great here because you cannot
tell it to not be recursive, so we will be wasting
cycles telling it to skip directories and files
*/
files, err := os.ReadDir(dir)
return dir, files, err
return dir, fs.FileInfoToDirEntry(rootInfo), files, err
}
return dir, []fs.DirEntry{}, errors.New("directory does not exist")
return dir, nil, []fs.DirEntry{}, errors.New("directory does not exist")
}
func rmHandler(data []byte, resp RPCResponse) {
+15 -2
View File
@@ -31,8 +31,8 @@ import (
"time"
"unsafe"
"github.com/bishopfox/sliver/implant/sliver/procdump"
"github.com/bishopfox/sliver/implant/sliver/mount"
"github.com/bishopfox/sliver/implant/sliver/procdump"
"github.com/bishopfox/sliver/implant/sliver/taskrunner"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -190,7 +190,7 @@ func memfilesListHandler(_ []byte, resp RPCResponse) {
pid := os.Getpid()
path := fmt.Sprintf("/proc/%d/fd/", pid)
dir, files, err := getDirList(path)
dir, rootDirEntry, files, err := getDirList(path)
// Convert directory listing to protobuf
timezone, offset := time.Now().Zone()
@@ -201,6 +201,19 @@ func memfilesListHandler(_ []byte, resp RPCResponse) {
dirList.Exists = false
}
dirList.Files = []*sliverpb.FileInfo{}
rootDirInfo, err := rootDirEntry.Info()
if err == nil {
// We should not get an error because we created the DirEntry object from the FileInfo object
dirList.Files = append(dirList.Files, &sliverpb.FileInfo{
Name: ".", // Cannot use the name from the FileInfo / DirEntry because that is the name of the directory
Size: rootDirInfo.Size(),
ModTime: rootDirInfo.ModTime().Unix(),
Mode: rootDirInfo.Mode().String(),
Uid: getUid(rootDirInfo),
Gid: getGid(rootDirInfo),
IsDir: rootDirInfo.IsDir(),
})
}
for _, dirEntry := range files {
//log.Printf("File: %s\n", dirEntry.Name())
+58 -4
View File
@@ -436,20 +436,74 @@ func SaveHTTPC2Config(httpC2Config *clientpb.HTTPC2Config) error {
func HTTPC2ConfigUpdate(newConf *clientpb.HTTPC2Config, oldConf *clientpb.HTTPC2Config) error {
clientID, _ := uuid.FromString(oldConf.ImplantConfig.ID)
c2Config := models.HTTPC2ConfigFromProtobuf(newConf)
err := Session().Where(&models.ImplantConfig{
err := Session().Where(&models.HttpC2PathSegment{
HttpC2ImplantConfigID: clientID,
}).Delete(&models.HttpC2PathSegment{})
if err.Error != nil {
return err.Error
}
err = Session().Where(&models.ImplantConfig{
ID: clientID,
}).Updates(c2Config.ImplantConfig)
if err != nil {
if err.Error != nil {
return err.Error
}
for _, segment := range c2Config.ImplantConfig.PathSegments {
segment.HttpC2ImplantConfigID = clientID
err = Session().Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(&segment)
if err.Error != nil {
return err.Error
}
}
serverID, _ := uuid.FromString(oldConf.ServerConfig.ID)
err = Session().Where(&models.HttpC2Cookie{
HttpC2ServerConfigID: serverID,
}).Delete(&models.HttpC2Cookie{})
if err.Error != nil {
return err.Error
}
err = Session().Where(&models.HttpC2Header{
HttpC2ServerConfigID: serverID,
}).Delete(&models.HttpC2Header{})
if err.Error != nil {
return err.Error
}
serverID, _ := uuid.FromString(oldConf.ImplantConfig.ID)
err = Session().Where(&models.HttpC2ServerConfig{
ID: serverID,
}).Updates(c2Config.ServerConfig)
if err != nil {
if err.Error != nil {
return err.Error
}
for _, cookie := range c2Config.ServerConfig.Cookies {
cookie.HttpC2ServerConfigID = serverID
err = Session().Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(&cookie)
if err.Error != nil {
return err.Error
}
}
for _, header := range c2Config.ServerConfig.Headers {
header.HttpC2ServerConfigID = serverID
err = Session().Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(&header)
if err.Error != nil {
return err.Error
}
}
return nil
}