Skip to content

Commit

Permalink
Allow to intercept more kernel connections (#513)
Browse files Browse the repository at this point in the history
* Allow to intercept some kernel connections

Some connections are initiated from kernel space, like WireGuard
VPNs (#454), NFS or SMB connections (#502) and ip tunnels (#500).

Note: This feature is complete for x86_64, WIP for aarch64, and not supported for armhf and i386
#513 (comment)

More information regarding this change: #493
  • Loading branch information
gustavo-iniguez-goya authored Sep 22, 2021
1 parent 2c1acdb commit 0526b84
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 17 deletions.
12 changes: 12 additions & 0 deletions daemon/procmon/details.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ func (p *Process) setCwd(cwd string) {
p.CWD = cwd
}

func (p *Process) readComm() error {
data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", p.ID))
if err != nil {
return err
}
p.Comm = core.Trim(string(data))
return nil
}

func (p *Process) readCwd() error {
link, err := os.Readlink(fmt.Sprintf("/proc/%d/cwd", p.ID))
if err != nil {
Expand Down Expand Up @@ -74,6 +83,9 @@ func (p *Process) readPath() error {

func (p *Process) readCmdline() {
if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", p.ID)); err == nil {
if len(data) == 0 {
return
}
for i, b := range data {
if b == 0x00 {
data[i] = byte(' ')
Expand Down
40 changes: 24 additions & 16 deletions daemon/procmon/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"time"

"github.com/evilsocket/opensnitch/daemon/core"
"github.com/evilsocket/opensnitch/daemon/log"
"github.com/evilsocket/opensnitch/daemon/procmon/audit"
)
Expand Down Expand Up @@ -86,9 +87,6 @@ func GetPIDFromINode(inode int, inodeKey string) int {
// If it exists in /proc, a new Process{} object is returned with the details
// to identify a process (cmdline, name, environment variables, etc).
func FindProcess(pid int, interceptUnknown bool) *Process {
if interceptUnknown && pid == -100 {
return NewProcess(-100, "Linux kernel")
}
if interceptUnknown && pid < 0 {
return NewProcess(0, "")
}
Expand All @@ -115,22 +113,32 @@ func FindProcess(pid int, interceptUnknown bool) *Process {
return proc
}
}

linkName := fmt.Sprint("/proc/", pid, "/exe")
if _, err := os.Lstat(linkName); err != nil {
// if the PID dir doesn't exist, the process may have exited or be a kernel connection
// XXX: can a kernel connection exist without an entry in ProcFS?
if core.Exists(fmt.Sprint("/proc/", pid)) == false {
log.Debug("PID can't be read /proc/", pid)
return nil
}

if link, err := os.Readlink(linkName); err == nil {
proc := NewProcess(pid, link)

proc.readCmdline()
proc.readCwd()
proc.readEnv()
proc.cleanPath()
linkName := fmt.Sprint("/proc/", pid, "/exe")
link, err := os.Readlink(linkName)
proc := NewProcess(pid, link)
proc.readCmdline()
proc.readCwd()
proc.readEnv()
proc.cleanPath()

if len(proc.Args) == 0 {
proc.readComm()
proc.Args = make([]string, 0)
proc.Args = append(proc.Args, proc.Comm)
}

addToActivePidsCache(uint64(pid), proc)
return proc
// If the link to the binary can't be read, the PID may be of a kernel task
if err != nil || proc.Path == "" {
proc.Path = "Kernel connection"
}
return nil

addToActivePidsCache(uint64(pid), proc)
return proc
}
1 change: 1 addition & 0 deletions daemon/procmon/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type procStatm struct {
// Process holds the details of a process.
type Process struct {
ID int
Comm string
Path string
Args []string
Env map[string]string
Expand Down
64 changes: 63 additions & 1 deletion ebpf_prog/opensnitch.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
//uncomment if building on x86_32
//#define OPENSNITCH_x86_32

#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/version.h>
#include <uapi/linux/bpf.h>
#include <uapi/linux/tcp.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <net/sock.h>
#include <net/udp_tunnel.h>
#include <net/inet_sock.h>

#define MAPSIZE 12000
Expand Down Expand Up @@ -97,6 +100,7 @@ struct udpv6_value_t{
u64 counter;
}__attribute__((packed));


// on x86_32 "struct sock" is arranged differently from x86_64 (at least on Debian kernels).
// We hardcode offsets of IP addresses.
struct sock_on_x86_32_t {
Expand Down Expand Up @@ -224,7 +228,6 @@ int kprobe__tcp_v4_connect(struct pt_regs *ctx)
return 0;
};


SEC("kretprobe/tcp_v4_connect")
int kretprobe__tcp_v4_connect(struct pt_regs *ctx)
{
Expand Down Expand Up @@ -253,6 +256,7 @@ int kretprobe__tcp_v4_connect(struct pt_regs *ctx)
tcp_value.uid = bpf_get_current_uid_gid() & 0xffffffff;
tcp_value.counter = *val;
bpf_map_update_elem(&tcpMap, &tcp_key, &tcp_value, BPF_ANY);

u64 newval = *val + 1;
bpf_map_update_elem(&tcpcounter, &zero_key, &newval, BPF_ANY);
bpf_map_delete_elem(&tcpsock, &pid_tgid);
Expand All @@ -274,6 +278,7 @@ int kprobe__tcp_v6_connect(struct pt_regs *ctx)
bpf_map_update_elem(&tcpv6sock, &pid_tgid, &skp, BPF_ANY);
return 0;
};

SEC("kretprobe/tcp_v6_connect")
int kretprobe__tcp_v6_connect(struct pt_regs *ctx)
{
Expand Down Expand Up @@ -309,6 +314,7 @@ int kretprobe__tcp_v6_connect(struct pt_regs *ctx)
tcpv6_value.uid = bpf_get_current_uid_gid() & 0xffffffff;
tcpv6_value.counter = *val;
bpf_map_update_elem(&tcpv6Map, &tcpv6_key, &tcpv6_value, BPF_ANY);

u64 newval = *val + 1;
bpf_map_update_elem(&tcpv6counter, &zero_key, &newval, BPF_ANY);
bpf_map_delete_elem(&tcpv6sock, &pid_tgid);
Expand Down Expand Up @@ -359,6 +365,7 @@ int kprobe__udp_sendmsg(struct pt_regs *ctx)
udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff;
udp_value.counter = *counterVal;
bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY);

u64 newval = *counterVal + 1;
bpf_map_update_elem(&udpcounter, &zero_key, &newval, BPF_ANY);
}
Expand Down Expand Up @@ -427,6 +434,61 @@ int kprobe__udpv6_sendmsg(struct pt_regs *ctx)

};

SEC("kprobe/iptunnel_xmit")
int kprobe__iptunnel_xmit(struct pt_regs *ctx)
{
#ifdef OPENSNITCH_x86_32
// TODO
return 0;
#else
struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM3(ctx);
u32 src = (u32)PT_REGS_PARM4(ctx);
u32 dst = (u32)PT_REGS_PARM5(ctx);
#endif

u16 sport = 0;
unsigned char *head;
u16 pkt_hdr;
__builtin_memset(&head, 0, sizeof(head));
__builtin_memset(&pkt_hdr, 0, sizeof(pkt_hdr));
bpf_probe_read(&head, sizeof(head), &skb->head);
bpf_probe_read(&pkt_hdr, sizeof(pkt_hdr), &skb->transport_header);
struct udphdr *udph;
__builtin_memset(&udph, 0, sizeof(udph));

udph = (struct udphdr *)(head + pkt_hdr);
bpf_probe_read(&sport, sizeof(sport), &udph->source);
sport = (sport >> 8) | ((sport << 8) & 0xff00);

struct udp_key_t udp_key;
struct udp_value_t udp_value;
u32 zero_key = 0;
__builtin_memset(&udp_key, 0, sizeof(udp_key));
__builtin_memset(&udp_value, 0, sizeof(udp_value));

bpf_probe_read(&udp_key.sport, sizeof(udp_key.sport), &sport);
bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &udph->dest);
bpf_probe_read(&udp_key.saddr, sizeof(udp_key.saddr), &src);
bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &dst);

u64 *counterVal = bpf_map_lookup_elem(&udpcounter, &zero_key);
if (counterVal == NULL){return 0;}

struct udp_value_t *lookedupValue = bpf_map_lookup_elem(&udpMap, &udp_key);
u64 pid = bpf_get_current_pid_tgid() >> 32;
if ( lookedupValue == NULL || lookedupValue->pid != pid) {
udp_value.pid = pid;
udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff;
udp_value.counter = *counterVal;
bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY);
u64 newval = *counterVal + 1;
bpf_map_update_elem(&udpcounter, &zero_key, &newval, BPF_ANY);
}

return 0;

};

// debug only: increment key's value by 1 in map "bytes"
void increment(u32 key){
u32 *lookedupValue = bpf_map_lookup_elem(&bytes, &key);
Expand Down

0 comments on commit 0526b84

Please sign in to comment.