mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 08:04:14 -10:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f67e96e1de | ||
|
|
1301cf6027 | ||
|
|
ce72711bc3 | ||
|
|
b732ce1daa | ||
|
|
218e3c53b1 |
No files matched your search
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
Sliver Implant Framework
|
||||||
|
Copyright (C) 2023 Bishop Fox
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package filesystem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/bishopfox/sliver/client/command/settings"
|
||||||
|
"github.com/bishopfox/sliver/client/console"
|
||||||
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MountCmd - Print information about mounted filesystems
|
||||||
|
func MountCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
|
if session == nil && beacon == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mount, err := con.Rpc.Mount(context.Background(), &sliverpb.MountReq{
|
||||||
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
con.PrintErrorf("%s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
os := getOS(session, beacon)
|
||||||
|
if mount.Response != nil && mount.Response.Async {
|
||||||
|
con.AddBeaconCallback(mount.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||||
|
err = proto.Unmarshal(task.Response, mount)
|
||||||
|
if err != nil {
|
||||||
|
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
PrintMount(os, mount, con)
|
||||||
|
})
|
||||||
|
con.PrintAsyncResponse(mount.Response)
|
||||||
|
} else {
|
||||||
|
PrintMount(os, mount, con)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||||
|
if session != nil {
|
||||||
|
return session.OS
|
||||||
|
} else if beacon != nil {
|
||||||
|
return beacon.OS
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func reduceSpaceMetric(numberOfBytes float64) string {
|
||||||
|
units := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
||||||
|
|
||||||
|
if numberOfBytes < 1 {
|
||||||
|
return fmt.Sprintf("0 %s", units[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
base := 1024.0
|
||||||
|
|
||||||
|
exp := math.Floor(math.Log(numberOfBytes) / math.Log(base))
|
||||||
|
index := int(math.Min(exp, float64(len(units)-1)))
|
||||||
|
divisor := math.Pow(base, float64(index))
|
||||||
|
|
||||||
|
value := numberOfBytes / divisor
|
||||||
|
|
||||||
|
return fmt.Sprintf("%.2f %s", value, units[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintMount - Print a table containing information on mounted filesystems
|
||||||
|
func PrintMount(os string, mount *sliverpb.Mount, con *console.SliverConsoleClient) {
|
||||||
|
if mount.Response != nil && mount.Response.Err != "" {
|
||||||
|
con.PrintErrorf("%s\n", mount.Response.Err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tw := table.NewWriter()
|
||||||
|
tw.SetStyle(settings.GetTableStyle(con))
|
||||||
|
|
||||||
|
switch os {
|
||||||
|
case "windows":
|
||||||
|
tw.AppendHeader(table.Row{"Volume", "Volume Type", "Mount Point", "Label", "Filesystem", "Used Space", "Free Space", "Total Space"})
|
||||||
|
case "darwin":
|
||||||
|
fallthrough
|
||||||
|
case "linux":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
tw.AppendHeader(table.Row{"Source", "Mount Point", "Mount Root", "Filesystem Type", "Options", "Total Space"})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mountPoint := range mount.Info {
|
||||||
|
row := mountRow(os, mountPoint)
|
||||||
|
tw.AppendRow(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.PaginateTable(tw, 0, false, false, con)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mountRow(os string, mountInfo *sliverpb.MountInfo) table.Row {
|
||||||
|
var row table.Row
|
||||||
|
|
||||||
|
switch os {
|
||||||
|
case "windows":
|
||||||
|
row = table.Row{mountInfo.VolumeName,
|
||||||
|
mountInfo.VolumeType,
|
||||||
|
mountInfo.MountPoint,
|
||||||
|
mountInfo.Label,
|
||||||
|
mountInfo.FileSystem,
|
||||||
|
reduceSpaceMetric(float64(mountInfo.UsedSpace)),
|
||||||
|
reduceSpaceMetric(float64(mountInfo.FreeSpace)),
|
||||||
|
reduceSpaceMetric(float64(mountInfo.TotalSpace)),
|
||||||
|
}
|
||||||
|
case "darwin":
|
||||||
|
fallthrough
|
||||||
|
case "linux":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
row = table.Row{mountInfo.VolumeName,
|
||||||
|
mountInfo.MountPoint,
|
||||||
|
mountInfo.Label,
|
||||||
|
mountInfo.VolumeType,
|
||||||
|
mountInfo.MountOptions,
|
||||||
|
reduceSpaceMetric(float64(mountInfo.TotalSpace)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return row
|
||||||
|
}
|
||||||
@@ -79,6 +79,7 @@ var (
|
|||||||
consts.PsExecStr: psExecHelp,
|
consts.PsExecStr: psExecHelp,
|
||||||
consts.BackdoorStr: backdoorHelp,
|
consts.BackdoorStr: backdoorHelp,
|
||||||
consts.SpawnDllStr: spawnDllHelp,
|
consts.SpawnDllStr: spawnDllHelp,
|
||||||
|
consts.MountStr: mountHelp,
|
||||||
|
|
||||||
consts.WebsitesStr: websitesHelp,
|
consts.WebsitesStr: websitesHelp,
|
||||||
consts.ScreenshotStr: screenshotHelp,
|
consts.ScreenshotStr: screenshotHelp,
|
||||||
@@ -379,6 +380,9 @@ On Windows, escaping is disabled. Instead, '\\' is treated as path separator.`
|
|||||||
[[.Bold]]About:[[.Normal]] (Windows Only) Executes the .NET assembly in a child process.
|
[[.Bold]]About:[[.Normal]] (Windows Only) Executes the .NET assembly in a child process.
|
||||||
`
|
`
|
||||||
|
|
||||||
|
mountHelp = `[[.Bold]]Command:[[.Normal]] mount
|
||||||
|
[[.Bold]]About:[[.Normal]] Displays information about mounted drives on the system, including mount point, space metrics, and filesystem.`
|
||||||
|
|
||||||
executeShellcodeHelp = `[[.Bold]]Command:[[.Normal]] execute-shellcode [local path to raw shellcode]
|
executeShellcodeHelp = `[[.Bold]]Command:[[.Normal]] execute-shellcode [local path to raw shellcode]
|
||||||
[[.Bold]]About:[[.Normal]] Executes the given shellcode in the implant's process.
|
[[.Bold]]About:[[.Normal]] Executes the given shellcode in the implant's process.
|
||||||
|
|
||||||
|
|||||||
@@ -1090,6 +1090,20 @@ func SliverCommands(con *client.SliverConsoleClient) console.Commands {
|
|||||||
|
|
||||||
carapace.Gen(memfilesRmCmd).PositionalCompletion(carapace.ActionValues().Usage("memfile file descriptor"))
|
carapace.Gen(memfilesRmCmd).PositionalCompletion(carapace.ActionValues().Usage("memfile file descriptor"))
|
||||||
|
|
||||||
|
mountCmd := &cobra.Command{
|
||||||
|
Use: consts.MountStr,
|
||||||
|
Short: "Get information on mounted filesystems",
|
||||||
|
Long: help.GetHelpFor([]string{consts.MountStr}),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
filesystem.MountCmd(cmd, con, args)
|
||||||
|
},
|
||||||
|
GroupID: consts.FilesystemHelpGroup,
|
||||||
|
}
|
||||||
|
Flags("", false, mountCmd, func(f *pflag.FlagSet) {
|
||||||
|
f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds")
|
||||||
|
})
|
||||||
|
sliver.AddCommand(mountCmd)
|
||||||
|
|
||||||
// [ Network ] ---------------------------------------------
|
// [ Network ] ---------------------------------------------
|
||||||
|
|
||||||
ifconfigCmd := &cobra.Command{
|
ifconfigCmd := &cobra.Command{
|
||||||
|
|||||||
@@ -219,6 +219,7 @@ const (
|
|||||||
ChmodStr = "chmod"
|
ChmodStr = "chmod"
|
||||||
ChownStr = "chown"
|
ChownStr = "chown"
|
||||||
ChtimesStr = "chtimes"
|
ChtimesStr = "chtimes"
|
||||||
|
MountStr = "mount"
|
||||||
|
|
||||||
MemfilesStr = "memfiles"
|
MemfilesStr = "memfiles"
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/implant/sliver/procdump"
|
"github.com/bishopfox/sliver/implant/sliver/procdump"
|
||||||
|
"github.com/bishopfox/sliver/implant/sliver/mount"
|
||||||
"github.com/bishopfox/sliver/implant/sliver/taskrunner"
|
"github.com/bishopfox/sliver/implant/sliver/taskrunner"
|
||||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
@@ -71,6 +72,7 @@ var (
|
|||||||
sliverpb.MsgReconfigureReq: reconfigureHandler,
|
sliverpb.MsgReconfigureReq: reconfigureHandler,
|
||||||
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
|
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||||
sliverpb.MsgProcessDumpReq: dumpHandler,
|
sliverpb.MsgProcessDumpReq: dumpHandler,
|
||||||
|
sliverpb.MsgMountReq: mountHandler,
|
||||||
sliverpb.MsgGrepReq: grepHandler,
|
sliverpb.MsgGrepReq: grepHandler,
|
||||||
|
|
||||||
// Wasm Extensions - Note that execution can be done via a tunnel handler
|
// Wasm Extensions - Note that execution can be done via a tunnel handler
|
||||||
@@ -124,6 +126,27 @@ func dumpHandler(data []byte, resp RPCResponse) {
|
|||||||
resp(data, err)
|
resp(data, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mountHandler(data []byte, resp RPCResponse) {
|
||||||
|
mountReq := &sliverpb.MountReq{}
|
||||||
|
err := proto.Unmarshal(data, mountReq)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mountData, err := mount.GetMountInformation()
|
||||||
|
mountResp := &sliverpb.Mount{
|
||||||
|
Info: mountData,
|
||||||
|
Response: &commonpb.Response{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
mountResp.Response.Err = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err = proto.Marshal(mountResp)
|
||||||
|
resp(data, err)
|
||||||
|
}
|
||||||
|
|
||||||
func taskHandler(data []byte, resp RPCResponse) {
|
func taskHandler(data []byte, resp RPCResponse) {
|
||||||
var err error
|
var err error
|
||||||
task := &sliverpb.TaskReq{}
|
task := &sliverpb.TaskReq{}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/implant/sliver/extension"
|
"github.com/bishopfox/sliver/implant/sliver/extension"
|
||||||
|
"github.com/bishopfox/sliver/implant/sliver/mount"
|
||||||
"github.com/bishopfox/sliver/implant/sliver/priv"
|
"github.com/bishopfox/sliver/implant/sliver/priv"
|
||||||
"github.com/bishopfox/sliver/implant/sliver/procdump"
|
"github.com/bishopfox/sliver/implant/sliver/procdump"
|
||||||
"github.com/bishopfox/sliver/implant/sliver/registry"
|
"github.com/bishopfox/sliver/implant/sliver/registry"
|
||||||
@@ -86,6 +87,7 @@ var (
|
|||||||
sliverpb.MsgRegistryDeleteKeyReq: regDeleteKeyHandler,
|
sliverpb.MsgRegistryDeleteKeyReq: regDeleteKeyHandler,
|
||||||
sliverpb.MsgRegistrySubKeysListReq: regSubKeysListHandler,
|
sliverpb.MsgRegistrySubKeysListReq: regSubKeysListHandler,
|
||||||
sliverpb.MsgRegistryListValuesReq: regValuesListHandler,
|
sliverpb.MsgRegistryListValuesReq: regValuesListHandler,
|
||||||
|
sliverpb.MsgMountReq: mountHandler,
|
||||||
|
|
||||||
// Generic
|
// Generic
|
||||||
sliverpb.MsgPing: pingHandler,
|
sliverpb.MsgPing: pingHandler,
|
||||||
@@ -736,6 +738,28 @@ func getPrivsHandler(data []byte, resp RPCResponse) {
|
|||||||
resp(data, err)
|
resp(data, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mountHandler(data []byte, resp RPCResponse) {
|
||||||
|
mountReq := &sliverpb.MountReq{}
|
||||||
|
|
||||||
|
err := proto.Unmarshal(data, mountReq)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mountData, err := mount.GetMountInformation()
|
||||||
|
mountResp := &sliverpb.Mount{
|
||||||
|
Info: mountData,
|
||||||
|
Response: &commonpb.Response{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
mountResp.Response.Err = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err = proto.Marshal(mountResp)
|
||||||
|
resp(data, err)
|
||||||
|
}
|
||||||
|
|
||||||
// Extensions
|
// Extensions
|
||||||
|
|
||||||
func registerExtensionHandler(data []byte, resp RPCResponse) {
|
func registerExtensionHandler(data []byte, resp RPCResponse) {
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// +linux
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetMountInformation() ([]*sliverpb.MountInfo, error) {
|
||||||
|
mountInfo := make([]*sliverpb.MountInfo, 0)
|
||||||
|
|
||||||
|
file, err := os.Open("/proc/self/mountinfo")
|
||||||
|
if err != nil {
|
||||||
|
return mountInfo, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
|
||||||
|
// Extract fields according to the /proc/self/mountinfo format
|
||||||
|
// https://man7.org/linux/man-pages/man5/proc.5.html
|
||||||
|
mountRoot := fields[3]
|
||||||
|
mountPoint := fields[4]
|
||||||
|
mountOptions := fields[5]
|
||||||
|
mountType := fields[len(fields)-3]
|
||||||
|
mountSource := fields[len(fields)-2]
|
||||||
|
|
||||||
|
// Get mount information using statfs
|
||||||
|
var stat syscall.Statfs_t
|
||||||
|
err := syscall.Statfs(mountPoint, &stat)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var mountData sliverpb.MountInfo
|
||||||
|
|
||||||
|
mountData.Label = mountRoot
|
||||||
|
mountData.MountPoint = mountPoint
|
||||||
|
mountData.VolumeType = mountType
|
||||||
|
mountData.VolumeName = mountSource
|
||||||
|
mountData.MountOptions = mountOptions
|
||||||
|
mountData.TotalSpace = stat.Blocks * uint64(stat.Bsize)
|
||||||
|
mountInfo = append(mountInfo, &mountData)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return mountInfo, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sliver Implant Framework
|
||||||
|
Copyright (C) 2023 Bishop Fox
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
pathLength = windows.MAX_PATH + 1
|
||||||
|
volumePathsLength = 32769 // 32K + 1 for a null terminator
|
||||||
|
universalNameInfoLevel = 1
|
||||||
|
remoteNameInfoLevel = 2
|
||||||
|
remoteNameMaxLength = 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
type UniversalNameInfo struct {
|
||||||
|
UniversalName [remoteNameMaxLength]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
var driveTypeMap = map[uint32]string{
|
||||||
|
0: "Unknown",
|
||||||
|
1: "Root Path invalid (no volume mounted for path)",
|
||||||
|
2: "Removable",
|
||||||
|
3: "Fixed disk",
|
||||||
|
4: "Remote / network drive",
|
||||||
|
5: "CD-ROM",
|
||||||
|
6: "RAM disk",
|
||||||
|
}
|
||||||
|
|
||||||
|
func findAllVolumes() ([]string, error) {
|
||||||
|
var volumes []string
|
||||||
|
|
||||||
|
volumeGuid := make([]uint16, pathLength)
|
||||||
|
handle, err := windows.FindFirstVolume(&volumeGuid[0], pathLength)
|
||||||
|
defer windows.FindVolumeClose(handle)
|
||||||
|
if err != nil {
|
||||||
|
return volumes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
volumes = append(volumes, windows.UTF16ToString(volumeGuid))
|
||||||
|
|
||||||
|
// Keep gathering volumes until we cannot find anymore
|
||||||
|
for {
|
||||||
|
err = windows.FindNextVolume(handle, &volumeGuid[0], pathLength)
|
||||||
|
if err != nil {
|
||||||
|
if err == windows.ERROR_NO_MORE_FILES {
|
||||||
|
// Then we are done
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
return volumes, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
volumes = append(volumes, windows.UTF16ToString(volumeGuid))
|
||||||
|
}
|
||||||
|
|
||||||
|
return volumes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findMountPointsForVolume(volumeName string) ([]string, error) {
|
||||||
|
const nullByte uint16 = 0
|
||||||
|
var volumeMounts []string
|
||||||
|
|
||||||
|
mountsForVolume := make([]uint16, volumePathsLength)
|
||||||
|
var returnedInformationLength uint32 = 0
|
||||||
|
volNameUint, err := windows.UTF16PtrFromString(volumeName)
|
||||||
|
if err != nil {
|
||||||
|
return volumeMounts, err
|
||||||
|
}
|
||||||
|
err = windows.GetVolumePathNamesForVolumeName(volNameUint, &mountsForVolume[0], volumePathsLength, &returnedInformationLength)
|
||||||
|
if err != nil {
|
||||||
|
if err == windows.ERROR_MORE_DATA {
|
||||||
|
// Then we need to make the buffer bigger and try again
|
||||||
|
// We will try one more time
|
||||||
|
mountsForVolume := make([]uint16, returnedInformationLength)
|
||||||
|
err = windows.GetVolumePathNamesForVolumeName(volNameUint, &mountsForVolume[0], volumePathsLength, &returnedInformationLength)
|
||||||
|
if err != nil {
|
||||||
|
return volumeMounts, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return volumeMounts, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Because the buffer contains multiple strings separated by a null terminator, we will read the
|
||||||
|
buffer in chunks and separate the strings
|
||||||
|
*/
|
||||||
|
volumeMounts = splitStringBuffer(mountsForVolume)
|
||||||
|
|
||||||
|
return volumeMounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitStringBuffer(buffer []uint16) []string {
|
||||||
|
bufferString := string(utf16.Decode(buffer))
|
||||||
|
return strings.Split(strings.TrimRight(bufferString, "\x00"), "\x00")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllDrives() ([]string, error) {
|
||||||
|
var logicalDrives []string
|
||||||
|
|
||||||
|
// Get necessary buffer size
|
||||||
|
n, err := windows.GetLogicalDriveStrings(0, nil)
|
||||||
|
driveStrings := make([]uint16, n)
|
||||||
|
_, err = windows.GetLogicalDriveStrings(n, &driveStrings[0])
|
||||||
|
if err != nil {
|
||||||
|
return logicalDrives, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logicalDrives = splitStringBuffer(driveStrings)
|
||||||
|
|
||||||
|
return logicalDrives, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDriveType(driveSpec string) string {
|
||||||
|
driveUTF16, err := windows.UTF16PtrFromString(driveSpec)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
driveTypeValue := windows.GetDriveType(driveUTF16)
|
||||||
|
|
||||||
|
if driveType, ok := driveTypeMap[driveTypeValue]; ok {
|
||||||
|
return driveType
|
||||||
|
} else {
|
||||||
|
return driveTypeMap[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUniversalName(driveSpec string) (string, error) {
|
||||||
|
mpr := windows.NewLazyDLL("mpr.dll")
|
||||||
|
wNetGetUniversalNameWProc := mpr.NewProc("WNetGetUniversalNameW")
|
||||||
|
|
||||||
|
driveUTF16, err := windows.UTF16PtrFromString(driveSpec)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var universalNameBuffer UniversalNameInfo
|
||||||
|
var bufferSize = uint32(unsafe.Sizeof(universalNameBuffer))
|
||||||
|
|
||||||
|
returnCode, _, err := wNetGetUniversalNameWProc.Call(
|
||||||
|
uintptr(unsafe.Pointer(driveUTF16)),
|
||||||
|
uintptr(universalNameInfoLevel),
|
||||||
|
uintptr(unsafe.Pointer(&universalNameBuffer)),
|
||||||
|
uintptr(unsafe.Pointer(&bufferSize)),
|
||||||
|
)
|
||||||
|
|
||||||
|
if returnCode != 0 {
|
||||||
|
// Then there was an error
|
||||||
|
return "", fmt.Errorf("%v", windows.Errno(returnCode).Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferStrings := splitStringBuffer(universalNameBuffer.UniversalName[:])
|
||||||
|
// There is some junk returned at the beginning of the structure. The actual UNC path starts after the first null terminator.
|
||||||
|
if len(bufferStrings) > 1 {
|
||||||
|
return bufferStrings[1], nil
|
||||||
|
} else {
|
||||||
|
return "", errors.New("No valid data returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDiskStats(driveSpec string) (uint64, uint64, uint64, error) {
|
||||||
|
var freeSpace uint64 = 0
|
||||||
|
var usedSpace uint64 = 0
|
||||||
|
var totalSpace uint64 = 0
|
||||||
|
|
||||||
|
driveUTF16, err := windows.UTF16PtrFromString(driveSpec)
|
||||||
|
if err != nil {
|
||||||
|
return freeSpace, usedSpace, totalSpace, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = windows.GetDiskFreeSpaceEx(driveUTF16, nil, &totalSpace, &freeSpace)
|
||||||
|
if err != nil {
|
||||||
|
return freeSpace, usedSpace, totalSpace, err
|
||||||
|
}
|
||||||
|
|
||||||
|
usedSpace = totalSpace - freeSpace
|
||||||
|
|
||||||
|
return freeSpace, usedSpace, totalSpace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDriveInfo(driveSpec string) (string, string) {
|
||||||
|
// Get label and filesystem type
|
||||||
|
driveUTF16, err := windows.UTF16PtrFromString(driveSpec)
|
||||||
|
if err != nil {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeNameBuffer := make([]uint16, pathLength)
|
||||||
|
fileSystemNameBuffer := make([]uint16, pathLength)
|
||||||
|
|
||||||
|
err = windows.GetVolumeInformation(driveUTF16, &volumeNameBuffer[0], pathLength, nil, nil, nil, &fileSystemNameBuffer[0], pathLength)
|
||||||
|
if err != nil {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return windows.UTF16ToString(volumeNameBuffer), windows.UTF16ToString(fileSystemNameBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func constructDriveVolumeMap() (map[string]string, error) {
|
||||||
|
driveVolumeMap := make(map[string]string)
|
||||||
|
|
||||||
|
volumes, err := findAllVolumes()
|
||||||
|
if err != nil {
|
||||||
|
return driveVolumeMap, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, volume := range volumes {
|
||||||
|
mountPoints, err := findMountPointsForVolume(volume)
|
||||||
|
if err != nil {
|
||||||
|
return driveVolumeMap, err
|
||||||
|
}
|
||||||
|
for _, mountPoint := range mountPoints {
|
||||||
|
driveVolumeMap[mountPoint] = volume
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return driveVolumeMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMountInformation() ([]*sliverpb.MountInfo, error) {
|
||||||
|
mountInfo := make([]*sliverpb.MountInfo, 0)
|
||||||
|
|
||||||
|
logicalDrives, err := getAllDrives()
|
||||||
|
if err != nil {
|
||||||
|
return mountInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
driveVolumeMap, err := constructDriveVolumeMap()
|
||||||
|
if err != nil {
|
||||||
|
return mountInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, drive := range logicalDrives {
|
||||||
|
var mountData sliverpb.MountInfo
|
||||||
|
mountData.MountPoint = drive
|
||||||
|
mountData.VolumeType = getDriveType(drive)
|
||||||
|
if mountData.VolumeType == driveTypeMap[4] {
|
||||||
|
// Then this is a network drive, so let's figure out the UNC path
|
||||||
|
networkPath, err := getUniversalName(drive)
|
||||||
|
if err != nil {
|
||||||
|
return mountInfo, err
|
||||||
|
}
|
||||||
|
mountData.VolumeName = networkPath
|
||||||
|
} else {
|
||||||
|
mountData.VolumeName = driveVolumeMap[drive]
|
||||||
|
}
|
||||||
|
|
||||||
|
mountData.Label, mountData.FileSystem = getDriveInfo(drive)
|
||||||
|
free, used, total, err := getDiskStats(drive)
|
||||||
|
if err != nil {
|
||||||
|
return mountInfo, err
|
||||||
|
}
|
||||||
|
mountData.UsedSpace = used
|
||||||
|
mountData.FreeSpace = free
|
||||||
|
mountData.TotalSpace = total
|
||||||
|
mountInfo = append(mountInfo, &mountData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mountInfo, nil
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.31.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v4.25.0
|
// protoc v3.15.8
|
||||||
// source: clientpb/client.proto
|
// source: clientpb/client.proto
|
||||||
|
|
||||||
package clientpb
|
package clientpb
|
||||||
@@ -5681,8 +5681,7 @@ func (x *MsfStager) GetFile() *commonpb.File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetSystemReq - Client request to the server which is translated into
|
// GetSystemReq - Client request to the server which is translated into
|
||||||
//
|
// InvokeSystemReq when sending to the implant.
|
||||||
// InvokeSystemReq when sending to the implant.
|
|
||||||
type GetSystemReq struct {
|
type GetSystemReq struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -5755,8 +5754,7 @@ func (x *GetSystemReq) GetRequest() *commonpb.Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MigrateReq - Client request to the server which is translated into
|
// MigrateReq - Client request to the server which is translated into
|
||||||
//
|
// InvokeMigrateReq when sending to the implant.
|
||||||
// InvokeMigrateReq when sending to the implant.
|
|
||||||
type MigrateReq struct {
|
type MigrateReq struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.31.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v4.25.0
|
// protoc v3.15.8
|
||||||
// source: commonpb/common.proto
|
// source: commonpb/common.proto
|
||||||
|
|
||||||
package commonpb
|
package commonpb
|
||||||
@@ -131,9 +131,8 @@ func (x *Request) GetSessionID() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Response - Common fields used in all gRPC responses. Note that the Err field
|
// Response - Common fields used in all gRPC responses. Note that the Err field
|
||||||
//
|
// only used when the implant needs to return an error to the server.
|
||||||
// only used when the implant needs to return an error to the server.
|
// Client<->Server comms should use normal gRPC error handling.
|
||||||
// Client<->Server comms should use normal gRPC error handling.
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.31.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v4.25.0
|
// protoc v3.15.8
|
||||||
// source: dnspb/dns.proto
|
// source: dnspb/dns.proto
|
||||||
|
|
||||||
package dnspb
|
package dnspb
|
||||||
@@ -87,10 +87,12 @@ func (DNSMessageType) EnumDescriptor() ([]byte, []int) {
|
|||||||
return file_dnspb_dns_proto_rawDescGZIP(), []int{0}
|
return file_dnspb_dns_proto_rawDescGZIP(), []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: DNS is very space sensitive so certain fields are re-purposed
|
|
||||||
// depending on the DNSMessageType as noted below:
|
|
||||||
//
|
//
|
||||||
// [Type TOTP]: ID field is used for the TOTP code
|
//NOTE: DNS is very space sensitive so certain fields are re-purposed
|
||||||
|
//depending on the DNSMessageType as noted below:
|
||||||
|
//
|
||||||
|
//[Type TOTP]: ID field is used for the TOTP code
|
||||||
|
//
|
||||||
type DNSMessage struct {
|
type DNSMessage struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
|||||||
+659
-653
File diff suppressed because it is too large.
Load diff
@@ -176,6 +176,7 @@ service SliverRPC {
|
|||||||
rpc MemfilesList(sliverpb.MemfilesListReq) returns (sliverpb.Ls);
|
rpc MemfilesList(sliverpb.MemfilesListReq) returns (sliverpb.Ls);
|
||||||
rpc MemfilesAdd(sliverpb.MemfilesAddReq) returns (sliverpb.MemfilesAdd);
|
rpc MemfilesAdd(sliverpb.MemfilesAddReq) returns (sliverpb.MemfilesAdd);
|
||||||
rpc MemfilesRm(sliverpb.MemfilesRmReq) returns (sliverpb.MemfilesRm);
|
rpc MemfilesRm(sliverpb.MemfilesRmReq) returns (sliverpb.MemfilesRm);
|
||||||
|
rpc Mount(sliverpb.MountReq) returns (sliverpb.Mount);
|
||||||
rpc ProcessDump(sliverpb.ProcessDumpReq) returns (sliverpb.ProcessDump);
|
rpc ProcessDump(sliverpb.ProcessDumpReq) returns (sliverpb.ProcessDump);
|
||||||
rpc RunAs(sliverpb.RunAsReq) returns (sliverpb.RunAs);
|
rpc RunAs(sliverpb.RunAsReq) returns (sliverpb.RunAs);
|
||||||
rpc Impersonate(sliverpb.ImpersonateReq) returns (sliverpb.Impersonate);
|
rpc Impersonate(sliverpb.ImpersonateReq) returns (sliverpb.Impersonate);
|
||||||
|
|||||||
+382
-524
File diff suppressed because it is too large.
Load diff
@@ -342,6 +342,9 @@ const (
|
|||||||
|
|
||||||
// MsgGrepReq - Request to grep for data
|
// MsgGrepReq - Request to grep for data
|
||||||
MsgGrepReq
|
MsgGrepReq
|
||||||
|
|
||||||
|
// MsgMountReq - Request filesystem mounts
|
||||||
|
MsgMountReq
|
||||||
)
|
)
|
||||||
|
|
||||||
// Constants to replace enums
|
// Constants to replace enums
|
||||||
@@ -588,8 +591,12 @@ func MsgNumber(request proto.Message) uint32 {
|
|||||||
case *GrepReq:
|
case *GrepReq:
|
||||||
return MsgGrepReq
|
return MsgGrepReq
|
||||||
|
|
||||||
|
case *MountReq:
|
||||||
|
return MsgMountReq
|
||||||
|
|
||||||
case *MemfilesListReq:
|
case *MemfilesListReq:
|
||||||
return MsgMemfilesListReq
|
return MsgMemfilesListReq
|
||||||
|
|
||||||
case *MemfilesAddReq:
|
case *MemfilesAddReq:
|
||||||
return MsgMemfilesAddReq
|
return MsgMemfilesAddReq
|
||||||
case *MemfilesAdd:
|
case *MemfilesAdd:
|
||||||
|
|||||||
+2118
-1838
File diff suppressed because it is too large.
Load diff
@@ -327,6 +327,28 @@ message Grep {
|
|||||||
commonpb.Response Response = 9;
|
commonpb.Response Response = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message MountReq {
|
||||||
|
commonpb.Request Request = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MountInfo {
|
||||||
|
string VolumeName = 1;
|
||||||
|
string VolumeType = 2;
|
||||||
|
string MountPoint = 3;
|
||||||
|
string Label = 4;
|
||||||
|
string FileSystem = 5;
|
||||||
|
uint64 UsedSpace = 6;
|
||||||
|
uint64 FreeSpace = 7;
|
||||||
|
uint64 TotalSpace = 8;
|
||||||
|
string MountOptions = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Mount {
|
||||||
|
repeated MountInfo Info = 1;
|
||||||
|
|
||||||
|
commonpb.Response Response = 9;
|
||||||
|
}
|
||||||
|
|
||||||
message ProcessDumpReq {
|
message ProcessDumpReq {
|
||||||
int32 Pid = 1;
|
int32 Pid = 1;
|
||||||
int32 Timeout = 2;
|
int32 Timeout = 2;
|
||||||
@@ -340,6 +362,7 @@ message ProcessDump {
|
|||||||
commonpb.Response Response = 9;
|
commonpb.Response Response = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
message RunAsReq {
|
message RunAsReq {
|
||||||
string Username = 1;
|
string Username = 1;
|
||||||
string ProcessName = 2;
|
string ProcessName = 2;
|
||||||
|
|||||||
@@ -239,3 +239,13 @@ func (rpc *Server) Grep(ctx context.Context, req *sliverpb.GrepReq) (*sliverpb.G
|
|||||||
}
|
}
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mount - Get information on mounted filesystems
|
||||||
|
func (rpc *Server) Mount(ctx context.Context, req *sliverpb.MountReq) (*sliverpb.Mount, error) {
|
||||||
|
resp := &sliverpb.Mount{Response: &commonpb.Response{}}
|
||||||
|
err := rpc.GenericHandler(req, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
Reference in new issue
Block a user