Skip to content

Commit

Permalink
fixed trigger out of bounds panic, test all strategies w/ pcap file (…
Browse files Browse the repository at this point in the history
…file not included) (#7)

* fixed trigger out of bounds panic, test all strategies w/ pcap file
  • Loading branch information
garmr-ulfr authored Mar 2, 2024
1 parent 9997753 commit 4935a8c
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 140 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/geneva
/geneva.exe
profile.cov
/internal/testdata/input.pcap
28 changes: 17 additions & 11 deletions actions/tamper_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ var (
"dataofs": TCPFieldDataOff,
"flags": TCPFieldFlags,
"window": TCPFieldWindow,
"urgent": TCPFieldUrgent,
"urgptr": TCPFieldUrgent,
"chksum": TCPFieldChecksum,
"options-eol": TCPOptionEol,
"options-nop": TCPOptionNop,
Expand All @@ -253,8 +253,8 @@ var (

// tcpOptionLengths is a map of TCP options to the length of their data field.
tcpOptionLengths = map[TCPField]int{
TCPOptionEol: 0,
TCPOptionNop: 0,
TCPOptionEol: 1,
TCPOptionNop: 1,
TCPOptionMss: 2,
TCPOptionWscale: 1,
TCPOptionSackok: 0, // the geneva team has this listed as 0, so at most the data is deleted
Expand Down Expand Up @@ -313,14 +313,20 @@ func NewTCPTamperAction(ta TamperAction) (*TCPTamperAction, error) {
case field == TCPLoad:
gen.vBytes = []byte(ta.NewValue)
default:
val, err := strconv.ParseUint(ta.NewValue, 10, 32)
if err != nil {
return nil, fmt.Errorf(
"%w: %q is not a valid value for field %q",
ErrInvalidTamperRule,
ta.NewValue,
ta.Field,
)
var (
val uint64
err error
)
if ta.NewValue != "" {
val, err = strconv.ParseUint(ta.NewValue, 10, 32)
if err != nil {
return nil, fmt.Errorf(
"%w: %q is not a valid value for field %q",
ErrInvalidTamperRule,
ta.NewValue,
ta.Field,
)
}
}

gen.vUint = uint32(val)
Expand Down
74 changes: 44 additions & 30 deletions geneva_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,57 @@ package geneva_test
import (
"testing"

"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/getlantern/geneva"
"github.com/getlantern/geneva/strategy"
)

var examples = []string{
"[TCP:flags:PA]-duplicate(tamper{TCP:dataofs:replace:10}(tamper{TCP:chksum:corrupt},),)-|",
"[TCP:flags:PA]-duplicate(tamper{TCP:dataofs:replace:10}(tamper{IP:ttl:replace:10},),)-|",
"[TCP:flags:PA]-duplicate(tamper{TCP:dataofs:replace:10}(tamper{TCP:ack:corrupt},),)-|",
"[TCP:flags:PA]-duplicate(tamper{TCP:options-wscale:corrupt}(tamper{TCP:dataofs:replace:8},),)-|",
"[TCP:flags:PA]-duplicate(tamper{TCP:load:corrupt}(tamper{TCP:chksum:corrupt},),)-|",
"[TCP:flags:PA]-duplicate(tamper{TCP:load:corrupt}(tamper{IP:ttl:replace:8},),)-|",
"[TCP:flags:PA]-duplicate(tamper{TCP:load:corrupt}(tamper{TCP:ack:corrupt},),)-|",
"[TCP:flags:S]-duplicate(,tamper{TCP:load:corrupt})-|",
"[TCP:flags:PA]-duplicate(tamper{IP:len:replace:64},)-|",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:R}(tamper{TCP:chksum:corrupt},))-|",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:R}(tamper{IP:ttl:replace:10},))-|",
"[TCP:flags:A]-duplicate(,tamper{TCP:options-md5header:corrupt}(tamper{TCP:flags:replace:R},))-|",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:RA}(tamper{TCP:chksum:corrupt},))-|",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:RA}(tamper{IP:ttl:replace:10},))-|",
"[TCP:flags:A]-duplicate(,tamper{TCP:options-md5header:corrupt}(tamper{TCP:flags:replace:R},))-|",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:FRAPUEN}(tamper{TCP:chksum:corrupt},))-|",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:FREACN}(tamper{IP:ttl:replace:10},))-|",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:FRAPUN}(tamper{TCP:options-md5header:corrupt},))-|",
"[TCP:flags:PA]-fragment{tcp:8:False}-| [TCP:flags:A]-tamper{TCP:seq:corrupt}-|",
"[TCP:flags:PA]-fragment{tcp:8:True}(,fragment{tcp:4:True})-|",
"[TCP:flags:PA]-fragment{tcp:-1:True}-|",
"[TCP:flags:PA]-duplicate(tamper{TCP:flags:replace:F}(tamper{IP:len:replace:78},),)-|",
"[TCP:flags:S]-duplicate(tamper{TCP:flags:replace:SA},)-|",
"[TCP:flags:PA]-tamper{TCP:options-uto:corrupt}-|",
}

func TestNewStrategy(t *testing.T) {
t.Parallel()

for i, s := range examples {
for i, s := range geneva.Strategies {
_, err := geneva.NewStrategy(s)
if err != nil {
t.Errorf("failed to parse strategy %d %q: %v", i, s, err)
assert.NoError(t, err, "failed to parse strategy %d %q", i, s)
}
}

func TestApplyAllStrategies(t *testing.T) {
t.Skip("no input pcap file")

t.Parallel()

t.Log("reading pcap file")

packets := []gopacket.Packet{}
handle, err := pcap.OpenOffline("internal/testdata/input.pcap")
require.NoError(t, err, "failed to open pcap file")

packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {
packets = append(packets, packet)
}

t.Log("parsing strategies")

strategies := []*strategy.Strategy{}
for _, s := range geneva.Strategies {
strat, err := geneva.NewStrategy(s)
assert.NoError(t, err, "failed to parse strategy %q", s)
strategies = append(strategies, strat)
}

t.Log("applying strategies")

for _, s := range strategies {
for _, p := range packets {
p := gopacket.NewPacket(p.Data(), layers.LayerTypeEthernet, gopacket.Default)
_, err := s.Apply(p, strategy.DirectionOutbound)
assert.NoError(t, err, "strategy: %s\n%v", s, p)
}
}
}
67 changes: 67 additions & 0 deletions strategies.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package geneva

var Strategies = []string{
"[TCP:flags:PA]-duplicate(tamper{TCP:dataofs:replace:10}(tamper{TCP:chksum:corrupt},),)-| \\/",
"[TCP:flags:PA]-duplicate(tamper{TCP:dataofs:replace:10}(tamper{IP:ttl:replace:10},),)-| \\/",
"[TCP:flags:PA]-duplicate(tamper{TCP:dataofs:replace:10}(tamper{TCP:ack:corrupt},),)-| \\/",
"[TCP:flags:PA]-duplicate(tamper{TCP:options-wscale:corrupt}(tamper{TCP:dataofs:replace:8},),)-| \\/",
"[TCP:flags:PA]-duplicate(tamper{TCP:load:corrupt}(tamper{TCP:chksum:corrupt},),)-| \\/",
"[TCP:flags:PA]-duplicate(tamper{TCP:load:corrupt}(tamper{IP:ttl:replace:8},),)-| \\/",
"[TCP:flags:PA]-duplicate(tamper{TCP:load:corrupt}(tamper{TCP:ack:corrupt},),)-| \\/",
"[TCP:flags:S]-duplicate(,tamper{TCP:load:corrupt})-| \\/",
"[TCP:flags:PA]-duplicate(tamper{IP:len:replace:64},)-| \\/",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:R}(tamper{TCP:chksum:corrupt},))-| \\/",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:R}(tamper{IP:ttl:replace:10},))-| \\/",
"[TCP:flags:A]-duplicate(,tamper{TCP:options-md5header:corrupt}(tamper{TCP:flags:replace:R},))-| \\/",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:RA}(tamper{TCP:chksum:corrupt},))-| \\/",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:RA}(tamper{IP:ttl:replace:10},))-| \\/",
"[TCP:flags:A]-duplicate(,tamper{TCP:options-md5header:corrupt}(tamper{TCP:flags:replace:R},))-| \\/",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:FRAPUEN}(tamper{TCP:chksum:corrupt},))-| \\/",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:FREACN}(tamper{IP:ttl:replace:10},))-| \\/",
"[TCP:flags:A]-duplicate(,tamper{TCP:flags:replace:FRAPUN}(tamper{TCP:options-md5header:corrupt},))-| \\/",
"[TCP:flags:PA]-fragment{tcp:8:False}-| [TCP:flags:A]-tamper{TCP:seq:corrupt}-| \\/",
"[TCP:flags:PA]-fragment{tcp:8:True}(,fragment{tcp:4:True})-| \\/",
"[TCP:flags:PA]-fragment{tcp:-1:True}-| \\/",
"[TCP:flags:PA]-duplicate(tamper{TCP:flags:replace:F}(tamper{IP:len:replace:78},),)-| \\/",
"[TCP:flags:S]-duplicate(tamper{TCP:flags:replace:SA},)-| \\/",
"[TCP:flags:PA]-tamper{TCP:options-uto:corrupt}-| \\/",

"[TCP:options-sackok:]-tamper{TCP:dataofs:replace:7}-| \\/",
"[TCP:options-sack::4]-fragment{tcp:-1:False}-| \\/",
"[TCP:options-nop:]-tamper{TCP:urgptr:corrupt}(tamper{TCP:options-eol:corrupt},)-| \\/",
"[TCP:options-nop:]-fragment{ip:-1:True:9}(drop,)-| \\/",
"[TCP:urgptr:0]-duplicate-| \\/",
"[TCP:dataofs:10:3]-tamper{TCP:options-mss:replace:17484}(fragment{tcp:-1:False},)-| \\/",
"[TCP:options-sack:]-tamper{TCP:window:corrupt}(tamper{TCP:options-eol:corrupt},)-| \\/",
"[TCP:options-altchksum:]-duplicate(fragment{ip:-1:False},)-| \\/",
"[TCP:options-altchksumopt:]-fragment{tcp:-1:True}-| \\/",
"[TCP:dataofs:8]-duplicate(duplicate,)-| \\/",
"[TCP:options-md5header:]-duplicate-| \\/",
"[TCP:options-md5header:]-fragment{tcp:-1:False}-| [TCP:options-wscale:7]-drop-| \\/",
"[TCP:options-uto:]-duplicate(,tamper{TCP:load:replace:y0qgai1woz})-| \\/",
"[TCP:options-sackok::1]-tamper{TCP:window:replace:120}(tamper{TCP:ack:corrupt},)-| \\/",
"[TCP:load:]-tamper{TCP:options-uto:corrupt}(fragment{tcp:-1:False},)-| [TCP:options-uto:]-tamper{TCP:options-mss:replace:}-| \\/",
"[TCP:options-wscale:]-tamper{TCP:options-nop:corrupt}(tamper{TCP:options-altchksum:replace:},)-| \\/",
"[TCP:options-sack::1]-tamper{TCP:chksum:replace:22170}-| \\/",
"[TCP:load:]-fragment{tcp:-1:False}(tamper{TCP:urgptr:replace:29},)-| \\/",
"[TCP:urgptr:0]-fragment{tcp:-1:False}(duplicate,tamper{TCP:options-sackok:replace:})-| \\/",
"[TCP:options-eol:]-tamper{TCP:window:corrupt}-| \\/",
"[TCP:options-uto:]-tamper{TCP:options-altchksum:replace:90}(duplicate(tamper{TCP:options-sack:replace:},),)-| \\/",
"[TCP:options-altchksumopt:]-tamper{TCP:options-uto:replace:}(tamper{TCP:load:corrupt}(fragment{tcp:-1:True}(,drop),),)-| \\/",
"[TCP:options-altchksumopt:]-fragment{tcp:-1:False}(drop,duplicate)-| \\/",
"[TCP:options-eol:]-tamper{TCP:urgptr:corrupt}(duplicate,)-| \\/",
"[TCP:options-sack:]-duplicate(tamper{TCP:urgptr:corrupt}(fragment{tcp:-1:False},),)-| \\/",
"[TCP:options-nop::2]-fragment{tcp:46:True}(fragment{tcp:-1:True},)-| \\/",
"[TCP:chksum:26741]-duplicate(tamper{TCP:options-timestamp:replace:},)-| \\/",
"[TCP:options-uto:]-duplicate(,tamper{TCP:options-nop:replace:})-| \\/",
"[TCP:options-altchksum:]-tamper{TCP:options-wscale:replace:248}(tamper{TCP:options-sackok:replace:},)-| \\/",
"[TCP:options-sackok:]-duplicate(duplicate(,tamper{TCP:load:replace:GET%20/%3Fq%3Dultrasurf%20HTTP/1.1%0D%0AHost%3A%2023.88.46.143%3A4228%0D%0AUser-Agent%3A%20python-requests/2.23.0%0D%0AAccept-Encoding%3A%20gzip%2C%20deflate%0D%0AAccept%3A%20%2A/%2A%0D%0AConnection%3A%20keep-alive%0D%0A%0D%0A}),duplicate)-| \\/",
"[TCP:options-sackok::1]-fragment{tcp:29:False:14}(tamper{TCP:options-timestamp:replace:4263716593},)-| \\/",
"[TCP:options-altchksumopt:]-tamper{TCP:options-nop:corrupt}(duplicate,)-| \\/",

// Can't find where these Strategies came from. They are not in the geneva paper or the geneva team's repo.
// "[TCP:reserved:0]-fragment{tcp:-1:True}(,tamper{TCP:options-uto:replace:})-| \\/",
// "[TCP:options-nop:]-tamper{TCP:load:replace:}(tamper{TCP:reserved:corrupt}(tamper{TCP:options-sackok:corrupt},),)-| \\/",
// "[TCP:options-sack:]-fragment{tcp:-1:True}(,tamper{TCP:reserved:replace:1})-| \\/",
// "[TCP:reserved:0]-duplicate(tamper{TCP:options-sackok:corrupt},)-| \\/",
}
61 changes: 0 additions & 61 deletions strategies.txt

This file was deleted.

13 changes: 2 additions & 11 deletions triggers/ip_trigger.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package triggers

import (
"bytes"
"fmt"
"net"
"strconv"
Expand Down Expand Up @@ -110,17 +111,7 @@ func (t *IPTrigger) Matches(pkt gopacket.Packet) (bool, error) {
case IPFieldDestAddress:
return ipLayer.DstIP.Equal(net.ParseIP(t.value)), nil
case IPFieldPayload:
if len(ipLayer.Payload) < len(t.value) {
return false, nil
}

for i, r := range []byte(t.value) {
if r != ipLayer.Payload[i] {
return false, nil
}
}

return true, nil
return bytes.Equal(ipLayer.Payload, []byte(t.value)), nil
}

// The rest of the triggers work on numbers.
Expand Down
26 changes: 3 additions & 23 deletions triggers/tcp_trigger.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package triggers

import (
"bytes"
"errors"
"fmt"
"math"
Expand Down Expand Up @@ -182,17 +183,7 @@ func matchTCPOption(field TCPField, value string, tcpLayer *layers.TCP) (bool, e

for _, opt := range tcpLayer.Options {
if opt.OptionType == optKind {
if len(opt.OptionData) < len(value) {
return false, nil
}

for i, b := range opt.OptionData {
if b != []byte(value)[i] {
return false, nil
}
}

return true, nil
return bytes.Equal(opt.OptionData, []byte(value)), nil
}
}

Expand All @@ -210,18 +201,7 @@ func (t *TCPTrigger) Matches(pkt gopacket.Packet) (bool, error) {
case TCPFieldFlags:
return matchField(t.value, tcpLayer), nil
case TCPFieldPayload:
if len(tcpLayer.Payload) < len(t.value) {
return false, nil
}

for i, r := range []byte(t.value) {
if r != tcpLayer.Payload[i] {
return false, nil
}
}

return true, nil

return bytes.Equal(tcpLayer.Payload, []byte(t.value)), nil
case TCPFieldOptionEOL, TCPFieldOptionNOP, TCPFieldOptionMSS, TCPFieldOptionWScale,
TCPFieldOptionSackOk, TCPFieldOptionSack, TCPFieldOptionTimestamp,
TCPFieldOptionAltChecksum, TCPFieldOptionAltChecksumOpt, TCPFieldOptionMD5Header,
Expand Down
14 changes: 10 additions & 4 deletions triggers/trigger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import (
"reflect"
"testing"

"github.com/getlantern/geneva/internal/scanner"
"github.com/getlantern/geneva/triggers"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"

"github.com/getlantern/geneva/internal/scanner"
"github.com/getlantern/geneva/triggers"
)

func TestParseTrigger(t *testing.T) {
Expand Down Expand Up @@ -215,8 +216,13 @@ func TestIPTriggers(t *testing.T) {
{"src-invalid", "src", "192.168.1.48", false},
{"dst-valid", "dst", "192.168.2.1", true},
{"dst-invalid", "dst", "192.168.1.3", false},
{"load-valid", "load", "\xee\x3a\x00", true},
{"load-invalid", "load", "\xee\x3a\x00\f7", false},
{
"load-valid",
"load",
"\xee\x3a\x00\x16\x6b\x8b\xad\x49\x9f\x7b\x50\xae\x80\x18\x08\x0a\x61\x41\x00\x00\x01\x01\x08\x0a\x8b\xc1\xd9\x53\x28\xbf\x41\x06\x53\x53\x48\x2d\x32\x2e\x30\x2d\x4f\x70\x65\x6e\x53\x53\x48\x5f\x38\x2e\x31\x0d\x0a",
true,
},
{"load-invalid", "load", "\xee\x3a\x00\xf7", false},
}

for _, tc := range tt {
Expand Down

0 comments on commit 4935a8c

Please sign in to comment.