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
  • Loading branch information
garmr-ulfr committed Mar 1, 2024
1 parent 9997753 commit 7bdc7fa
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 136 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
72 changes: 42 additions & 30 deletions geneva_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,55 @@ 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.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

0 comments on commit 7bdc7fa

Please sign in to comment.