Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Socket dataset: Workaround for bogus dereference in kernel 5.x #15771

Merged
merged 4 commits into from
Jan 23, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions x-pack/auditbeat/module/system/socket/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,14 +534,16 @@ type udpSendMsgCall struct {
LPort uint16 `kprobe:"lport"`
RPort uint16 `kprobe:"rport"`
AltRPort uint16 `kprobe:"altrport"`
// SIPtr is the struct sockaddr_in pointer.
SIPtr uintptr `kprobe:"siptr"`
// SIAF is the address family in (struct sockaddr_in*)->sin_family.
SIAF uint16 `kprobe:"siaf"`
}

func (e *udpSendMsgCall) asFlow() flow {
raddr, rport := e.RAddr, e.RPort
if raddr == 0 {
if e.SIPtr == 0 || e.SIAF != unix.AF_INET {
raddr = e.AltRAddr
}
if rport == 0 {
rport = e.AltRPort
}
return flow{
Expand Down Expand Up @@ -586,14 +588,16 @@ type udpv6SendMsgCall struct {
LPort uint16 `kprobe:"lport"`
RPort uint16 `kprobe:"rport"`
AltRPort uint16 `kprobe:"altrport"`
// SI6Ptr is the struct sockaddr_in6 pointer.
SI6Ptr uintptr `kprobe:"si6ptr"`
// Si6AF is the address family field ((struct sockaddr_in6*)->sin6_family)
SI6AF uint16 `kprobe:"si6af"`
}

func (e *udpv6SendMsgCall) asFlow() flow {
raddra, raddrb, rport := e.RAddrA, e.RAddrB, e.RPort
if raddra == 0 && raddrb == 0 {
if e.SI6Ptr == 0 || e.SI6AF != unix.AF_INET6 {
raddra, raddrb = e.AltRAddrA, e.AltRAddrB
}
if rport == 0 {
rport = e.AltRPort
}
return flow{
Expand Down
116 changes: 116 additions & 0 deletions x-pack/auditbeat/module/system/socket/guess/deref.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

// +build linux,386 linux,amd64

package guess

import (
"encoding/hex"
"os"
"syscall"

"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/x-pack/auditbeat/module/system/socket/helper"
"github.com/elastic/beats/x-pack/auditbeat/tracing"
)

/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty comment block.


*/

func init() {
if err := Registry.AddGuess(&guessDeref{}); err != nil {
panic(err)
}
}

const (
flagName = "NULL_PTR_DEREF_IS_GARBAGE"
envVar = "AUDITBEAT_SYSTEM_SOCKET_CHECK_DEREF"
)

type guessDeref struct {
ctx Context
}

func (g *guessDeref) Condition(ctx Context) (bool, error) {
v := os.Getenv(envVar)
return v == "1", nil
}

// Name of this guess.
func (g *guessDeref) Name() string {
return "guess_deref"
}

// Provides returns the names of discovered variables.
func (g *guessDeref) Provides() []string {
return []string{
flagName,
}
}

// Requires declares the variables required to run this guess.
func (g *guessDeref) Requires() []string {
return []string{
"SYS_UNAME",
}
}

// Probes returns a kretprobe on prepare_creds that dumps the first bytes
// pointed to by the return value, which is a struct cred.
func (g *guessDeref) Probes() ([]helper.ProbeDef, error) {
return []helper.ProbeDef{
{
Probe: tracing.Probe{
Type: tracing.TypeKProbe,
Name: "guess_null_ptr_deref",
Address: "{{.SYS_UNAME}}",
Fetchargs: helper.MakeMemoryDump("{{.P1}}", 0, credDumpBytes),
},
Decoder: tracing.NewDumpDecoder,
},
}, nil
}

// Prepare is a no-op.
func (g *guessDeref) Prepare(ctx Context) error {
g.ctx = ctx
return nil
}

// Terminate is a no-op.
func (g *guessDeref) Terminate() error {
return nil
}

func (g *guessDeref) MaxRepeats() int {
return 1000
}

// Extract receives the struct cred dump and discovers the offsets.
func (g *guessDeref) Extract(ev interface{}) (common.MapStr, bool) {
raw := ev.([]byte)
if len(raw) != credDumpBytes {
return nil, false
}
for _, val := range raw {
if val != 0 {
g.ctx.Log.Errorf("Found non-empty memory:\n%s", hex.Dump(raw))
//return nil, false
}
}
return nil, true
}

// Trigger invokes the SYS_ACCESS syscall:
// int access(const char *pathname, int mode);
// The function call will return an error due to path being NULL, but it will
// have invoked prepare_creds before argument validation.
func (g *guessDeref) Trigger() error {
var ptr *syscall.Utsname
syscall.Uname(ptr)
return nil
}
4 changes: 2 additions & 2 deletions x-pack/auditbeat/module/system/socket/kprobes.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ var sharedKProbes = []helper.ProbeDef{
Probe: tracing.Probe{
Name: "udp_sendmsg_in",
Address: "udp_sendmsg",
Fetchargs: "sock={{.UDP_SENDMSG_SOCK}} size={{.UDP_SENDMSG_LEN}} laddr=+{{.INET_SOCK_LADDR}}({{.UDP_SENDMSG_SOCK}}):u32 lport=+{{.INET_SOCK_LPORT}}({{.UDP_SENDMSG_SOCK}}):u16 raddr=+{{.SOCKADDR_IN_ADDR}}(+0({{.UDP_SENDMSG_MSG}})):u32 rport=+{{.SOCKADDR_IN_PORT}}(+0({{.UDP_SENDMSG_MSG}})):u16 altraddr=+{{.INET_SOCK_RADDR}}({{.UDP_SENDMSG_SOCK}}):u32 altrport=+{{.INET_SOCK_RPORT}}({{.UDP_SENDMSG_SOCK}}):u16",
Fetchargs: "sock={{.UDP_SENDMSG_SOCK}} size={{.UDP_SENDMSG_LEN}} laddr=+{{.INET_SOCK_LADDR}}({{.UDP_SENDMSG_SOCK}}):u32 lport=+{{.INET_SOCK_LPORT}}({{.UDP_SENDMSG_SOCK}}):u16 raddr=+{{.SOCKADDR_IN_ADDR}}(+0({{.UDP_SENDMSG_MSG}})):u32 siptr=+0({{.UDP_SENDMSG_MSG}}) siaf=+{{.SOCKADDR_IN_AF}}(+0({{.UDP_SENDMSG_MSG}})):u16 rport=+{{.SOCKADDR_IN_PORT}}(+0({{.UDP_SENDMSG_MSG}})):u16 altraddr=+{{.INET_SOCK_RADDR}}({{.UDP_SENDMSG_SOCK}}):u32 altrport=+{{.INET_SOCK_RPORT}}({{.UDP_SENDMSG_SOCK}}):u16",
},
Decoder: helper.NewStructDecoder(func() interface{} { return new(udpSendMsgCall) }),
},
Expand Down Expand Up @@ -455,7 +455,7 @@ var ipv6KProbes = []helper.ProbeDef{
Probe: tracing.Probe{
Name: "udpv6_sendmsg_in",
Address: "udpv6_sendmsg",
Fetchargs: "sock={{.UDP_SENDMSG_SOCK}} size={{.UDP_SENDMSG_LEN}} laddra={{.INET_SOCK_V6_LADDR_A}}({{.UDP_SENDMSG_SOCK}}){{.INET_SOCK_V6_TERM}} laddrb={{.INET_SOCK_V6_LADDR_B}}({{.UDP_SENDMSG_SOCK}}){{.INET_SOCK_V6_TERM}} lport=+{{.INET_SOCK_LPORT}}({{.UDP_SENDMSG_SOCK}}):u16 raddra=+{{.SOCKADDR_IN6_ADDRA}}(+0({{.UDP_SENDMSG_MSG}})):u64 raddrb=+{{.SOCKADDR_IN6_ADDRB}}(+0({{.UDP_SENDMSG_MSG}})):u64 rport=+{{.SOCKADDR_IN6_PORT}}(+0({{.UDP_SENDMSG_MSG}})):u16 altraddra={{.INET_SOCK_V6_RADDR_A}}({{.UDP_SENDMSG_SOCK}}){{.INET_SOCK_V6_TERM}} altraddrb={{.INET_SOCK_V6_RADDR_B}}({{.UDP_SENDMSG_SOCK}}){{.INET_SOCK_V6_TERM}} altrport=+{{.INET_SOCK_RPORT}}({{.UDP_SENDMSG_SOCK}}):u16",
Fetchargs: "sock={{.UDP_SENDMSG_SOCK}} size={{.UDP_SENDMSG_LEN}} laddra={{.INET_SOCK_V6_LADDR_A}}({{.UDP_SENDMSG_SOCK}}){{.INET_SOCK_V6_TERM}} laddrb={{.INET_SOCK_V6_LADDR_B}}({{.UDP_SENDMSG_SOCK}}){{.INET_SOCK_V6_TERM}} lport=+{{.INET_SOCK_LPORT}}({{.UDP_SENDMSG_SOCK}}):u16 raddra=+{{.SOCKADDR_IN6_ADDRA}}(+0({{.UDP_SENDMSG_MSG}})):u64 raddrb=+{{.SOCKADDR_IN6_ADDRB}}(+0({{.UDP_SENDMSG_MSG}})):u64 rport=+{{.SOCKADDR_IN6_PORT}}(+0({{.UDP_SENDMSG_MSG}})):u16 altraddra={{.INET_SOCK_V6_RADDR_A}}({{.UDP_SENDMSG_SOCK}}){{.INET_SOCK_V6_TERM}} altraddrb={{.INET_SOCK_V6_RADDR_B}}({{.UDP_SENDMSG_SOCK}}){{.INET_SOCK_V6_TERM}} altrport=+{{.INET_SOCK_RPORT}}({{.UDP_SENDMSG_SOCK}}):u16 si6ptr=+0({{.UDP_SENDMSG_MSG}}) si6af=+{{.SOCKADDR_IN6_AF}}(+0({{.UDP_SENDMSG_MSG}})):u16",
},
Decoder: helper.NewStructDecoder(func() interface{} { return new(udpv6SendMsgCall) }),
},
Expand Down
74 changes: 70 additions & 4 deletions x-pack/auditbeat/module/system/socket/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/joeshaw/multierror"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"golang.org/x/sys/unix"

"github.com/elastic/beats/libbeat/beat"
"github.com/elastic/beats/x-pack/auditbeat/module/system/socket/dns"
Expand Down Expand Up @@ -148,11 +149,9 @@ func TestUDPOutgoingSinglePacketWithProcess(t *testing.T) {
Sock: sock,
Size: 123,
LAddr: lAddr,
RAddr: rAddr,
AltRAddr: 0,
AltRAddr: rAddr,
LPort: lPort,
RPort: rPort,
AltRPort: 0,
AltRPort: rPort,
},
&inetReleaseCall{Meta: meta(1234, 1235, 17), Sock: sock},
&doExit{Meta: meta(1234, 1234, 18)},
Expand Down Expand Up @@ -293,6 +292,14 @@ func ipv4(ip string) uint32 {
return tracing.MachineEndian.Uint32(netIP)
}

func ipv6(ip string) (hi uint64, lo uint64) {
netIP := net.ParseIP(ip).To16()
if netIP == nil {
panic("bad ip")
}
return tracing.MachineEndian.Uint64(netIP[:]), tracing.MachineEndian.Uint64(netIP[8:])
}

func feedEvents(evs []event, st *state, t *testing.T) error {
for idx, ev := range evs {
t.Logf("Delivering event %d: %s", idx, ev.String())
Expand Down Expand Up @@ -515,3 +522,62 @@ func TestDNSTracker(t *testing.T) {
}.Run(t)
})
}

func TestUDPSendMsgAltLogic(t *testing.T) {
const expectedIPv4 = "6 probe=0 pid=1234 tid=1235 udp_sendmsg(sock=0x0, size=0, 10.11.12.13:1010 -> 10.20.30.40:1234)"
const expectedIPv6 = "6 probe=0 pid=1234 tid=1235 udpv6_sendmsg(sock=0x0, size=0, [fddd::bebe]:1010 -> [fddd::cafe]:1234)"
t.Run("ipv4 non-connected", func(t *testing.T) {
ev := udpSendMsgCall{
Meta: meta(1234, 1235, 6),
LAddr: ipv4("10.11.12.13"),
LPort: be16(1010),
RAddr: ipv4("10.20.30.40"),
RPort: be16(1234),
AltRAddr: ipv4("192.168.255.255"),
AltRPort: be16(555),
SIPtr: 0x7fffffff,
SIAF: unix.AF_INET,
}
assert.Equal(t, expectedIPv4, ev.String())
})
t.Run("ipv4 connected", func(t *testing.T) {
ev := udpSendMsgCall{
Meta: meta(1234, 1235, 6),
LAddr: ipv4("10.11.12.13"),
LPort: be16(1010),
RAddr: ipv4("192.168.255.255"),
RPort: be16(555),
AltRAddr: ipv4("10.20.30.40"),
AltRPort: be16(1234),
}
assert.Equal(t, expectedIPv4, ev.String())
})
t.Run("ipv6 non-connected", func(t *testing.T) {
ev := udpv6SendMsgCall{
Meta: meta(1234, 1235, 6),
LPort: be16(1010),
RPort: be16(1234),
AltRPort: be16(555),
SI6Ptr: 0x7fffffff,
SI6AF: unix.AF_INET6,
}
ev.LAddrA, ev.LAddrB = ipv6("fddd::bebe")
ev.RAddrA, ev.RAddrB = ipv6("fddd::cafe")
ev.AltRAddrA, ev.AltRAddrB = ipv6("fddd::bad:bad")
assert.Equal(t, expectedIPv6, ev.String())
})

t.Run("ipv6 connected", func(t *testing.T) {
ev := udpv6SendMsgCall{
Meta: meta(1234, 1235, 6),
LPort: be16(1010),
RPort: be16(555),
AltRPort: be16(1234),
}
ev.LAddrA, ev.LAddrB = ipv6("fddd::bebe")
ev.RAddrA, ev.RAddrB = ipv6("fddd::bad:bad")
ev.AltRAddrA, ev.AltRAddrB = ipv6("fddd::cafe")
assert.Equal(t, expectedIPv6, ev.String())
})

}