mosers
/
eaas-vde-proxy
Archiviert
1
0
Fork 0

Added filter for NBNS/NBDS [Close #19]

main
Simon Moser vor 3 Jahren
Ursprung ee257888d7
Commit 65f47aaaa8
Signiert von: mosers
GPG-Schlüssel-ID: 96B3365A234B500C

@ -1,246 +0,0 @@
// based on https://github.com/aler9/landiscover/blob/main/layer-nbns.go (MIT License)
package layer_nbns
import (
"encoding/binary"
"fmt"
"strings"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
type layerNbns struct {
layers.BaseLayer
TransactionID uint16
IsResponse bool
Opcode uint8
Truncated bool
Recursion bool
Broadcast bool
Questions []nbnsQuestion
Answers []nbnsAnswer
AuthorityCount uint16
AdditionalCount uint16
}
func NewLayerNbns() *layerNbns {
return &layerNbns{}
}
type nbnsQuestion struct {
Query string
Type uint16
Class uint16
}
type nbnsAnswer struct {
Query string
Type uint16
Class uint16
TTL uint32
Names []nbnsAnswerName
}
type nbnsAnswerName struct {
Name string
Type uint8
Flags uint16
}
func (l *layerNbns) DecodeFromBytes(data []byte) error {
l.BaseLayer = layers.BaseLayer{Contents: data}
if len(data) < 12 {
return fmt.Errorf("invalid packet")
}
l.TransactionID = binary.BigEndian.Uint16(data[0:2])
l.IsResponse = (data[3] >> 7) == 0x01
l.Opcode = (data[3] >> 3) & 0x0F
l.Truncated = (data[3] >> 1) == 0x01
l.Recursion = data[3] == 0x01
l.Broadcast = (data[4] >> 4) == 0x01
questionCount := binary.BigEndian.Uint16(data[4:6])
answerCount := binary.BigEndian.Uint16(data[6:8])
l.AuthorityCount = binary.BigEndian.Uint16(data[8:10])
l.AdditionalCount = binary.BigEndian.Uint16(data[10:12])
pos := 12
if questionCount > 0 {
return fmt.Errorf("is question, unsupported")
}
l.Questions = nil
for i := uint16(0); i < questionCount; i++ {
q := nbnsQuestion{}
var read int
q.Query, read = dnsQueryDecode(data, pos)
if read <= 0 {
return fmt.Errorf("invalid string")
}
pos += read
q.Type = binary.BigEndian.Uint16(data[pos+0 : pos+2])
q.Class = binary.BigEndian.Uint16(data[pos+2 : pos+4])
pos += 4
l.Questions = append(l.Questions, q)
}
l.Answers = nil
for i := uint16(0); i < answerCount; i++ {
a := nbnsAnswer{}
var read int
a.Query, read = dnsQueryDecode(data, pos)
if read <= 0 {
return fmt.Errorf("invalid string")
}
pos += read
a.Type = binary.BigEndian.Uint16(data[pos+0 : pos+2])
a.Class = binary.BigEndian.Uint16(data[pos+2 : pos+4])
a.TTL = binary.BigEndian.Uint32(data[pos+4 : pos+8])
dataLen := binary.BigEndian.Uint16(data[pos+8 : pos+10])
pos += 10
if a.Type == 0x21 { // NB_STAT
pos2 := pos
nameCount := data[pos2]
pos2++
for j := uint8(0); j < nameCount; j++ {
a.Names = append(a.Names, nbnsAnswerName{
Name: strings.TrimSuffix(string(data[pos2:pos2+15]), " "),
Type: data[pos2+15],
Flags: binary.BigEndian.Uint16(data[pos2+16 : pos2+18]),
})
pos2 += 18
}
}
pos += int(dataLen)
l.Answers = append(l.Answers, a)
}
// TODO: Decode Authorities and Additional Information
return nil
}
func (l *layerNbns) SerializeTo(b gopacket.SerializeBuffer) error {
data, err := b.AppendBytes(12)
if err != nil {
panic(err)
}
binary.BigEndian.PutUint16(data[0:2], l.TransactionID)
if l.IsResponse {
data[3] |= 0x01 << 7
}
data[3] |= l.Opcode << 3
if l.Truncated {
data[3] |= 0x01 << 1
}
if l.Recursion {
data[3] |= 0x01
}
if l.Broadcast {
data[4] |= 0x01 << 4
}
binary.BigEndian.PutUint16(data[4:6], uint16(len(l.Questions)))
binary.BigEndian.PutUint16(data[6:8], uint16(len(l.Answers)))
binary.BigEndian.PutUint16(data[8:10], l.AuthorityCount)
binary.BigEndian.PutUint16(data[10:12], l.AdditionalCount)
for _, q := range l.Questions {
enc := dnsQueryEncode(q.Query)
data, err := b.AppendBytes(len(enc) + 4)
if err != nil {
panic(err)
}
copy(data[:len(enc)], enc)
data = data[len(enc):]
binary.BigEndian.PutUint16(data[0:2], q.Type)
binary.BigEndian.PutUint16(data[2:4], q.Class)
}
// TODO: encode answers, authorities and additional informations
return nil
}
// Source: https://github.com/aler9/landiscover/blob/main/utils.go
// <size>part<size>part
func dnsQueryDecode(data []byte, start int) (string, int) {
var read []byte
toread := uint8(0)
pos := start
for ; true; pos++ {
if pos >= len(data) { // decoding terminated before null character
return "", -1
}
if data[pos] == 0x00 {
if toread > 0 { // decoding terminated before part parsing
return "", -1
}
break // query correctly decoded
}
if toread == 0 { // we need a size or pointer
if len(read) > 0 { // add separator
read = append(read, '.')
}
if (data[pos] & 0xC0) == 0xC0 { // pointer
ptr := int(binary.BigEndian.Uint16(data[pos:pos+2]) & 0x3FFF)
pos++ // skip next byte
substr, subread := dnsQueryDecode(data, ptr)
if subread <= 0 {
return "", -1
}
read = append(read, []byte(substr)...)
break // query correctly decoded
} else { // size
toread = data[pos]
}
} else { // byte inside part
read = append(read, data[pos])
toread--
}
}
return string(read), pos + 1 - start
}
func dnsQueryEncode(in string) []byte {
tmp := strings.Split(in, ".")
l := 0
for _, part := range tmp {
bpart := []byte(part)
l++
l += len(bpart)
}
l++
ret := make([]byte, l)
i := 0
for _, part := range tmp {
bpart := []byte(part)
ret[i] = uint8(len(bpart))
i++
copy(ret[i:], bpart)
i += len(bpart)
}
ret[i] = uint8(0)
return ret
}

@ -12,7 +12,6 @@ import (
"net" "net"
"os" "os"
"proxy/cmd" "proxy/cmd"
nbns "proxy/layer-nbns"
. "proxy/util" . "proxy/util"
"strconv" "strconv"
"time" "time"
@ -37,6 +36,7 @@ var DHCPState dhcp4.MessageType
var DHCPCandidate net.IP var DHCPCandidate net.IP
var UId string var UId string
var Wireshark bool var Wireshark bool
var Hostname string
// Start the two plugs and run two concurrent forward methods // Start the two plugs and run two concurrent forward methods
func main() { func main() {
@ -52,6 +52,7 @@ func main() {
pidFile := flag.String("pidfile", "", "Location to write the pid to") pidFile := flag.String("pidfile", "", "Location to write the pid to")
logFile := flag.String("logfile", "", "Location to write output to") logFile := flag.String("logfile", "", "Location to write output to")
wireshark := flag.Bool("wireshark", false, "Whether to write all traffic to /tmp") wireshark := flag.Bool("wireshark", false, "Whether to write all traffic to /tmp")
hostname := flag.String("hostname", "", "Set a windows hostname to filter for in binary payloads")
flag.Parse() flag.Parse()
log.SetLevel(log.Level(*logLvl)) log.SetLevel(log.Level(*logLvl))
OldMAC, _ = net.ParseMAC(*oldMAC) OldMAC, _ = net.ParseMAC(*oldMAC)
@ -60,6 +61,7 @@ func main() {
NewIP = net.ParseIP(*newIP).To4() NewIP = net.ParseIP(*newIP).To4()
Passthrough = *passthrough Passthrough = *passthrough
Wireshark = *wireshark Wireshark = *wireshark
Hostname = *hostname
UId = GenerateUId(*sockProxy) UId = GenerateUId(*sockProxy)
log.SetFormatter(&log.TextFormatter{ log.SetFormatter(&log.TextFormatter{
DisableTimestamp: true, DisableTimestamp: true,
@ -175,17 +177,10 @@ func pipeForward(prefix string) {
log.Error(prefix, "Error setting network layer for UDP checksum", err) log.Error(prefix, "Error setting network layer for UDP checksum", err)
} }
if udpPacket.SrcPort == 137 && udpPacket.DstPort == 137 { if (udpPacket.SrcPort == 137 && udpPacket.DstPort == 137) || // NBNS
log.Info(prefix, "Trying to decode NBNS payload") (udpPacket.SrcPort == 138 && udpPacket.DstPort == 138) { // NBDS
l := nbns.NewLayerNbns() log.Info(prefix, "Filtering NBNS/NBDS payload")
if err := l.DecodeFromBytes(udpPacket.Payload); err != nil { filterPayload(prefix, &udpPacket.Payload)
log.Warning("Error decoding NBNS: ", err)
}
print(l)
}
if udpPacket.SrcPort == 138 && udpPacket.DstPort == 138 {
log.Info(prefix, "NBDS payload, not implemented (yet)")
} }
} }
} }
@ -227,6 +222,33 @@ func pipeForward(prefix string) {
} }
} }
func filterPayload(prefix string, payload *[]byte) {
// Populate slices with values to on vm and network side
vmVals := [][]byte{OldMAC}
netVals := [][]byte{NewMAC}
if OldIP != nil && !OldIP.Equal(NewIP) {
vmVals = append(vmVals, OldIP)
netVals = append(netVals, NewIP)
}
if Hostname != "" {
vmVals = append(vmVals, []byte(Hostname), DnsQueryEncode(Hostname))
netVals = append(netVals, []byte(UId), DnsQueryEncode(UId))
}
// Choose for which slice to search for and which to replace it with
searchVals := vmVals
replaceVals := netVals
if prefix == cmd.In {
searchVals = netVals
replaceVals = vmVals
}
// Replace all occurencies of each slice value
for i, search := range searchVals {
*payload = bytes.Replace(*payload, search, replaceVals[i], -1)
}
}
// filterIP checks whether an IP target selected from src and dst equals a given value. If yes, it is changed // filterIP checks whether an IP target selected from src and dst equals a given value. If yes, it is changed
func filterIP(prefix string, dst interface{}, src interface{}, context gopacket.LayerType) { func filterIP(prefix string, dst interface{}, src interface{}, context gopacket.LayerType) {
var target interface{} var target interface{}

@ -3,6 +3,7 @@ package util
import ( import (
"crypto/rand" "crypto/rand"
"encoding/base32" "encoding/base32"
"encoding/binary"
"github.com/google/gopacket" "github.com/google/gopacket"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/google/gopacket/pcapgo" "github.com/google/gopacket/pcapgo"
@ -31,9 +32,9 @@ func WritePcap(file string, data []byte) {
defer r.Flush() defer r.Flush()
ci := gopacket.CaptureInfo{ ci := gopacket.CaptureInfo{
Timestamp: time.Now(), Timestamp: time.Now(),
CaptureLength: len(data), CaptureLength: len(data),
Length: len(data), Length: len(data),
} }
err = r.WritePacket(ci, data) err = r.WritePacket(ci, data)
@ -50,7 +51,7 @@ func WritePIDFile(filename string) {
if filename == "" { if filename == "" {
return return
} }
if err := ioutil.WriteFile(filename, []byte(strconv.Itoa(os.Getpid()) + "\n"), 0644); err != nil { if err := ioutil.WriteFile(filename, []byte(strconv.Itoa(os.Getpid())+"\n"), 0644); err != nil {
log.Errorf("Error writing PID file %s", filename) log.Errorf("Error writing PID file %s", filename)
} }
} }
@ -78,7 +79,7 @@ func GenerateXID() []byte {
if _, err := rand.Read(buf); err != nil { if _, err := rand.Read(buf); err != nil {
log.Error("Error generating random xid") log.Error("Error generating random xid")
return []byte{36, 23, 250, 224} // chosen by fair dice roll return []byte{36, 23, 250, 224} // chosen by fair dice roll
// guaranteed to be random --> https://xkcd.com/221/ // guaranteed to be random --> https://xkcd.com/221/
} }
return buf return buf
} }
@ -124,3 +125,74 @@ func (c If) IP(a, b net.IP) net.IP {
} }
return b return b
} }
// Source: https://github.com/aler9/landiscover/blob/main/utils.go
// <size>part<size>part
func DnsQueryDecode(data []byte, start int) (string, int) {
var read []byte
toread := uint8(0)
pos := start
for ; true; pos++ {
if pos >= len(data) { // decoding terminated before null character
return "", -1
}
if data[pos] == 0x00 {
if toread > 0 { // decoding terminated before part parsing
return "", -1
}
break // query correctly decoded
}
if toread == 0 { // we need a size or pointer
if len(read) > 0 { // add separator
read = append(read, '.')
}
if (data[pos] & 0xC0) == 0xC0 { // pointer
ptr := int(binary.BigEndian.Uint16(data[pos:pos+2]) & 0x3FFF)
pos++ // skip next byte
substr, subread := DnsQueryDecode(data, ptr)
if subread <= 0 {
return "", -1
}
read = append(read, []byte(substr)...)
break // query correctly decoded
} else { // size
toread = data[pos]
}
} else { // byte inside part
read = append(read, data[pos])
toread--
}
}
return string(read), pos + 1 - start
}
func DnsQueryEncode(in string) []byte {
tmp := strings.Split(in, ".")
l := 0
for _, part := range tmp {
bpart := []byte(part)
l++
l += len(bpart)
}
l++
ret := make([]byte, l)
i := 0
for _, part := range tmp {
bpart := []byte(part)
ret[i] = uint8(len(bpart))
i++
copy(ret[i:], bpart)
i += len(bpart)
}
ret[i] = uint8(0)
return ret
}