Skip to content

Commit

Permalink
send binary name as token payload
Browse files Browse the repository at this point in the history
  • Loading branch information
ursachec committed Jan 15, 2023
1 parent 1d578c4 commit 760f95b
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 1 deletion.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ clean:
.PHONY: fmt
fmt:
go fmt

.PHONY: test
test:
go test -v ./...
48 changes: 47 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ package main

import (
"bytes"
"encoding/base32"
"encoding/binary"
"errors"
"flag"
"fmt"
"golang.org/x/sys/unix"
"log"
"math/rand"
"net"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"

"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/perf"
Expand All @@ -28,11 +32,53 @@ type bpfEvent struct {
Pathname [MaxPathnameLength]uint8
}

// add custom data to token using the format specified at:
// https://docs.canarytokens.org/guide/dns-token.html#encoding-additional-information-in-your-token
// ```
// Base32 encode your data, and remove any padding '=' characters
// Insert periods (.) after every 63-bytes
// Append the magic string '.G'+<2-random-digits>+'.' (e.g. '.G12.' or '.G83.')
// ```
func hostnameWithPayload(hostname string, payload string, r *rand.Rand) string {
if len(payload) == 0 {
return hostname
}
data := []byte(payload)
dst := make([]byte, base32.StdEncoding.EncodedLen(len(data)))
base32.StdEncoding.Encode(dst, data)
trimmed := strings.TrimRight(string(dst), "=")

chunkSize := 63
var sb strings.Builder
for i, r := range []rune(trimmed) {
sb.WriteRune(r)
if i != 0 && i != 1 && (i+1)%chunkSize == 0 {
sb.WriteRune('.')
}
}
encodedPayloadWithSeparator := sb.String()

magicString := fmt.Sprintf(".G%d%d", r.Intn(10), r.Intn(10))
hostNameWithMagicString := strings.Join([]string{magicString, hostname}, ".")

maxHostnameLength := 253
maxPayloadSize := maxHostnameLength - len(hostNameWithMagicString)
if len(encodedPayloadWithSeparator) <= maxPayloadSize {
return encodedPayloadWithSeparator + hostNameWithMagicString
}
return encodedPayloadWithSeparator[0:maxPayloadSize] + hostNameWithMagicString
}

// send DNS requests
func consumer(canaryHostname string, link <-chan string, done chan<- bool) {
rs := rand.NewSource(time.Now().UnixNano())
r := rand.New(rs)

for pathname := range link {
log.Printf("triggering DNS token for: %s", pathname)
_, err := net.LookupIP(canaryHostname)
base := filepath.Base(pathname)
hostnameWithData := hostnameWithPayload(canaryHostname, base, r)
_, err := net.LookupIP(hostnameWithData)
if err != nil {
fmt.Fprintf(os.Stderr, "DNS request failed: %v\n", err)
}
Expand Down
62 changes: 62 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"math/rand"
"regexp"
"testing"
"time"
)

func TestHostnameWithPayloadSimplestInvocation(t *testing.T) {
rs := rand.NewSource(time.Now().UnixNano())
r := rand.New(rs)

hostname := "nosuchtokennonononononono.canarytokens.com"
res := hostnameWithPayload(hostname, "", r)
want := hostname
if res != want {
t.Fatalf("strings not equal. res: `%s` | want: `%s`", res, want)
}
}

func TestHostnameWithPayload(t *testing.T) {
testCases := []struct {
hostname string
payload string
regexStr string
}{
{"nosuchtokennonononononono.canarytokens.com", "", "nosuchtokennonononononono.canarytokens.com"},
{"nosuchtokennonononononono.canarytokens.com", "whoami", "^[a-zA-Z0-9]+\\.G[0-9][0-9]\\.nosuchtokennonononononono.canarytokens.com"},

//$ (export STR=$(python3 -c "print('dot'.join(str(i) for i in range(0, 10)), end='')") && export B32=$(echo $STR|base32 -w 0|tr -d =) && echo $STR $(echo $STR|wc -c) $B32 $(echo $B32|wc -c))
//0dot1dot2dot3dot4dot5dot6dot7dot8dot9 38 GBSG65BRMRXXIMTEN52DGZDPOQ2GI33UGVSG65BWMRXXIN3EN52DQZDPOQ4QU 62
{"nosuchtokennonononononono.canarytokens.com", "0dot1dot2dot3dot4dot5dot6dot7dot8dot9", "^[a-zA-Z0-9]+\\.G[0-9][0-9]\\.nosuchtokennonononononono.canarytokens.com"},

//$ (export STR=$(python3 -c "print('dot'.join(str(i) for i in range(0, 16)), end='')") && export B32=$(echo $STR|base32 -w 0|tr -d =) && echo $STR $(echo $STR|wc -c) $B32 $(echo $B32|wc -c))
//0dot1dot2dot3dot4dot5dot6dot7dot8dot9dot10dot11dot12dot13dot14dot15 68 GBSG65BRMRXXIMTEN52DGZDPOQ2GI33UGVSG65BWMRXXIN3EN52DQZDPOQ4WI33UGEYGI33UGEYWI33UGEZGI33UGEZWI33UGE2GI33UGE2QU 110
{"nosuchtokennonononononono.canarytokens.com", "0dot1dot2dot3dot4dot5dot6dot7dot8dot9dot10dot11dot12dot13dot14dot15", "^[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.G[0-9][0-9]\\.nosuchtokennonononononono.canarytokens.com"},

//$ (export STR=$(python3 -c "print('dot'.join(str(i) for i in range(0, 20)), end='')") && export B32=$(echo $STR|base32 -w 0|tr -d =) && echo $STR $(echo $STR|wc -c) $B32 $(echo $B32|wc -c))
//0dot1dot2dot3dot4dot5dot6dot7dot8dot9dot10dot11dot12dot13dot14dot15dot16dot17dot18dot19 88 GBSG65BRMRXXIMTEN52DGZDPOQ2GI33UGVSG65BWMRXXIN3EN52DQZDPOQ4WI33UGEYGI33UGEYWI33UGEZGI33UGEZWI33UGE2GI33UGE2WI33UGE3GI33UGE3WI33UGE4GI33UGE4QU 142
{"nosuchtokennonononononono.canarytokens.com", "0dot1dot2dot3dot4dot5dot6dot7dot8dot9dot10dot11dot12dot13dot14dot15dot16dot17dot18dot19", "^[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.G[0-9][0-9]\\.nosuchtokennonononononono.canarytokens.com"},

//$ (export STR=$(python3 -c "print('dot'.join(str(i) for i in range(0, 35)), end='')") && export B32=$(echo $STR|base32 -w 0|tr -d =) && echo $STR $(echo $STR|wc -c) $B32 $(echo $B32|wc -c))
//0dot1dot2dot3dot4dot5dot6dot7dot8dot9dot10dot11dot12dot13dot14dot15dot16dot17dot18dot19dot20dot21dot22dot23dot24dot25dot26dot27dot28dot29dot30dot31dot32dot33dot34 163 GBSG65BRMRXXIMTEN52DGZDPOQ2GI33UGVSG65BWMRXXIN3EN52DQZDPOQ4WI33UGEYGI33UGEYWI33UGEZGI33UGEZWI33UGE2GI33UGE2WI33UGE3GI33UGE3WI33UGE4GI33UGE4WI33UGIYGI33UGIYWI33UGIZGI33UGIZWI33UGI2GI33UGI2WI33UGI3GI33UGI3WI33UGI4GI33UGI4WI33UGMYGI33UGMYWI33UGMZGI33UGMZWI33UGM2AU 262
{"nosuchtokennonononononono.canarytokens.com", "0dot1dot2dot3dot4dot5dot6dot7dot8dot9dot10dot11dot12dot13dot14dot15dot16dot17dot18dot19dot20dot21dot22dot23dot24dot25dot26dot27dot28dot29dot30dot31dot32dot33dot34", "^[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.G[0-9][0-9]\\.nosuchtokennonononononono.canarytokens.com"},
}

maxTotalLength := 253
rs := rand.NewSource(41414141)
r := rand.New(rs)

for _, testCase := range testCases {
res := hostnameWithPayload(testCase.hostname, testCase.payload, r)
matched, _ := regexp.MatchString(testCase.regexStr, res)
if !matched {
t.Errorf("Result did not match regex. res `%s`, regex: `%s`, hostname: `%s`, payload: `%s`", res, testCase.regexStr, testCase.hostname, testCase.payload)
}
if len(res) > maxTotalLength {
t.Errorf("Result string larger than expected max length. res: `%s`, hostname: `%s`, payload: `%s`", res, testCase.hostname, testCase.payload)
}
}
}

0 comments on commit 760f95b

Please sign in to comment.