diff --git a/cmd/fuzz.go b/cmd/fuzz.go index 6a763df..2fb5d2d 100644 --- a/cmd/fuzz.go +++ b/cmd/fuzz.go @@ -15,7 +15,7 @@ var fuzzCmd = &cobra.Command{ Use: "fuzz", Short: "Fuzz URL scheme", RunE: func(cmd *cobra.Command, args []string) error { - var validInputs []string + var validInputs [][]byte var err error base, err := cmd.Flags().GetString("base") @@ -102,7 +102,7 @@ var fuzzCmd = &cobra.Command{ l.Infof("Attached to %s", app) - var lastInput string + var lastInput []byte sess.On("detached", func(reason frida.SessionDetachReason, crash *frida.Crash) { l.Infof("Session detached; reason=%s", reason.String()) @@ -112,7 +112,7 @@ var fuzzCmd = &cobra.Command{ if err != nil { return err } - f.WriteString(lastInput) + f.Write(lastInput) return nil }() if err != nil { @@ -149,13 +149,13 @@ var fuzzCmd = &cobra.Command{ l.Infof("Loaded script") - m := mutator.NewMutator(base, runs, fn, validInputs...) + m := mutator.NewMutator([]byte(base), runs, fn, validInputs...) ch := m.Mutate() for mutated := range ch { - lastInput = mutated.Input + //lastInput = mutated.Input l.Infof("[%s] %s\n", color.New(color.FgCyan).Sprintf("%s", mutated.Mutation), mutated.Input) - _ = script.ExportsCall("fuzz", method, mutated.Input) + //_ = script.ExportsCall("fuzz", method, mutated.Input) if timeout > 0 { time.Sleep(time.Duration(timeout) * time.Second) } diff --git a/cmd/root.go b/cmd/root.go index 27debc6..90e8e3b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -29,13 +29,13 @@ func Execute(sc string) error { return rootCmd.Execute() } -func readInputs(dirPath string) ([]string, error) { +func readInputs(dirPath string) ([][]byte, error) { files, err := os.ReadDir(dirPath) if err != nil { return nil, err } - var validInputs []string + var validInputs [][]byte for _, fl := range files { if fl.IsDir() { @@ -54,13 +54,13 @@ func readInputs(dirPath string) ([]string, error) { if err != nil { return nil, err } - validInputs = append(validInputs, string(data)) + validInputs = append(validInputs, data) } return validInputs, nil } -func crashSHA256(inp string) string { +func crashSHA256(inp []byte) string { h := sha256.New() - h.Write([]byte(inp)) + h.Write(inp) return fmt.Sprintf("%x", h.Sum(nil)) } diff --git a/mutator/helpers.go b/mutator/helpers.go index b793089..c0155e7 100644 --- a/mutator/helpers.go +++ b/mutator/helpers.go @@ -1,8 +1,8 @@ package mutator -func (m *Mutator) getFuzzedInput() string { +func (m *Mutator) getFuzzedInput() []byte { if m.multipleRounds { - if m.lastInput == "" { + if len(m.lastInput) == 0 { m.lastInput = m.fetchInput() } return m.lastInput @@ -10,7 +10,7 @@ func (m *Mutator) getFuzzedInput() string { return m.fetchInput() } -func (m *Mutator) fetchInput() string { +func (m *Mutator) fetchInput() []byte { if m.fuzzIdx == -1 || len(m.validInputs) == 0 { return m.input } diff --git a/mutator/mutations.go b/mutator/mutations.go index 0f2f1f2..1360e24 100644 --- a/mutator/mutations.go +++ b/mutator/mutations.go @@ -1,11 +1,10 @@ package mutator import ( - "strings" - "unicode" + "bytes" ) -type mutateFn func(m *Mutator) string +type mutateFn func(m *Mutator) []byte var mutations = map[string]mutateFn{ "insert": insert, @@ -19,46 +18,60 @@ var mutations = map[string]mutateFn{ } // insert inserts random byte at random location inside the input -func insert(m *Mutator) string { +func insert(m *Mutator) []byte { inp := m.getFuzzedInput() pos := m.r.Intn(len(inp)) - var char byte - for { - c := m.r.Intn(unicode.MaxASCII) - if unicode.IsPrint(rune(c)) { - char = byte(c) - break + char := byte(m.r.Intn(255)) + + res := make([]byte, len(inp)+1) + + k := 0 + + for i := 0; i < len(inp); i++ { + if i == pos { + res[k] = char + res[k+1] = inp[i] + k += 2 + } else { + res[k] = inp[i] + k++ } } - return inp[:pos] + string(char) + inp[pos:] + return res } // del deletes random byte -func del(m *Mutator) string { +func del(m *Mutator) []byte { inp := m.getFuzzedInput() pos := m.r.Intn(len(inp)) - return inp[:pos] + inp[pos+1:] + res := make([]byte, len(inp)-1) + + k := 0 + for i := 0; i < len(inp); i++ { + if i == pos { + continue + } + res[k] = inp[i] + k++ + } + + return res } // substitute substitutes byte at random position with random byte -func substitute(m *Mutator) string { +func substitute(m *Mutator) []byte { inp := m.getFuzzedInput() pos := m.r.Intn(len(inp)) - var char byte - for { - c := m.r.Intn(unicode.MaxASCII) - if unicode.IsPrint(rune(c)) { - char = byte(c) - break - } - } - var res string + char := byte(m.r.Intn(255)) + + res := make([]byte, len(inp)) + for i, c := range inp { if i == pos { - res += string(char) + res[i] = char } else { - res += string(c) + res[i] = c } } return res @@ -66,7 +79,7 @@ func substitute(m *Mutator) string { // byteOp takes random byte and random position inside the string // and do arithmetic operation on them (+, -, *, /) -func byteOp(m *Mutator) string { +func byteOp(m *Mutator) []byte { b := make([]byte, 1) m.r.Read(b) inp := m.getFuzzedInput() @@ -74,21 +87,21 @@ func byteOp(m *Mutator) string { op := m.r.Intn(4) - res := make([]rune, len(inp)) + res := make([]byte, len(inp)) for i, r := range inp { if i == pos { switch op { case 0: - res[i] = r + rune(b[0]) + res[i] = r + b[0] case 1: - res[i] = r - rune(b[0]) + res[i] = r - b[0] case 2: - res[i] = r * rune(b[0]) + res[i] = r * b[0] default: if b[0] != 0 { - res[i] = r / rune(b[0]) + res[i] = r / b[0] } else { - res[i] = r + rune(b[0]) + res[i] = r + b[0] } } } else { @@ -96,12 +109,12 @@ func byteOp(m *Mutator) string { } } - return string(res) + return res } // duplicateRange duplicates random range inside the original string random // number of times -func duplicateRange(m *Mutator) string { +func duplicateRange(m *Mutator) []byte { inp := m.getFuzzedInput() start := m.r.Intn(len(inp)) @@ -115,29 +128,43 @@ func duplicateRange(m *Mutator) string { } rng := inp[start:end] + duplicatedBytes := bytes.Repeat(rng, countOfDuplications) + + res := make([]byte, len(inp)+len(duplicatedBytes)) + + k := 0 + for i := 0; i < end; i++ { + res[k] = inp[i] + k++ + } + + for i := 0; i < len(duplicatedBytes); i++ { + res[k] = duplicatedBytes[i] + k++ + } - res := "" - res += inp[:start] - res += strings.Repeat(rng, countOfDuplications) - res += inp[end:] + for i := end; i < len(inp); i++ { + res[k] = inp[i] + k++ + } return res } // bitFlip flips the bit at random position inside random location inside input -func bitFlip(m *Mutator) string { +func bitFlip(m *Mutator) []byte { inp := m.getFuzzedInput() pos := m.r.Intn(len(inp)) bitPosition := m.r.Intn(8) - res := "" + res := make([]byte, len(inp)) for i, r := range inp { if i == pos { - res += string(r ^ (1 << bitPosition)) + res[i] = r ^ (1 << bitPosition) } else { - res += string(r) + res[i] = r } } @@ -145,19 +172,19 @@ func bitFlip(m *Mutator) string { } // bitmask applies random bitmask on random location inside the string -func bitmask(m *Mutator) string { +func bitmask(m *Mutator) []byte { inp := m.getFuzzedInput() pos := m.r.Intn(len(inp)) bm := m.r.Intn(255) - res := "" + res := make([]byte, len(inp)) for i, r := range inp { if pos == i { - res += string(inp[i] ^ uint8(bm)) + res[i] = inp[i] ^ uint8(bm) } else { - res += string(r) + res[i] = r } } @@ -165,12 +192,12 @@ func bitmask(m *Mutator) string { } // duplicate duplicates original string random number of times (2 < 10) -func duplicate(m *Mutator) string { +func duplicate(m *Mutator) []byte { inp := m.getFuzzedInput() var count int for count = m.r.Intn(10); count < 1; count = m.r.Intn(10) { } - return strings.Repeat(inp, count) + return bytes.Repeat(inp, count) } diff --git a/mutator/mutator.go b/mutator/mutator.go index 89d05fc..b87daa9 100644 --- a/mutator/mutator.go +++ b/mutator/mutator.go @@ -1,14 +1,14 @@ package mutator import ( + "bytes" "math/rand" - "strings" "time" ) -func NewMutator(inp string, runs uint, fnName string, validInputs ...string) *Mutator { +func NewMutator(inp []byte, runs uint, fnName string, validInputs ...[]byte) *Mutator { return &Mutator{ - fuzzIdx: strings.Index(inp, "FUZZ"), + fuzzIdx: bytes.Index(inp, []byte("FUZZ")), baseURL: inp, input: inp, fnName: fnName, @@ -23,18 +23,18 @@ func NewMutator(inp string, runs uint, fnName string, validInputs ...string) *Mu type Mutator struct { fuzzIdx int runs uint - baseURL string - input string - lastInput string + baseURL []byte + input []byte + lastInput []byte fnName string ch chan *Mutated r *rand.Rand - validInputs []string + validInputs [][]byte multipleRounds bool } type Mutated struct { - Input string + Input []byte Mutation string } @@ -42,7 +42,7 @@ func (m *Mutator) Mutate() <-chan *Mutated { go func() { if m.runs > 0 { for i := 0; i < int(m.runs); i++ { - var mutatedInput string + var mutatedInput []byte var method string mut := m.r.Intn(len(mutations) + 1) // run random mutations random number of times @@ -86,7 +86,7 @@ func (m *Mutator) Mutate() <-chan *Mutated { } } else { m.ch <- &Mutated{ - Input: strings.Replace(m.baseURL, "FUZZ", mutatedInput, -1), + Input: bytes.Replace(m.baseURL, []byte("FUZZ"), mutatedInput, -1), Mutation: method, } } @@ -95,7 +95,7 @@ func (m *Mutator) Mutate() <-chan *Mutated { close(m.ch) } else { for { - var mutatedInput string + var mutatedInput []byte var method string mut := m.r.Intn(len(mutations) + 1) if mut == len(mutations) { @@ -138,7 +138,7 @@ func (m *Mutator) Mutate() <-chan *Mutated { } } else { m.ch <- &Mutated{ - Input: strings.Replace(m.baseURL, "FUZZ", mutatedInput, -1), + Input: bytes.Replace(m.baseURL, []byte("FUZZ"), mutatedInput, -1), Mutation: method, } } diff --git a/mutator/post_process.go b/mutator/post_process.go index 140e0cb..172e35a 100644 --- a/mutator/post_process.go +++ b/mutator/post_process.go @@ -5,17 +5,19 @@ import ( "net/url" ) -type applyFn func(input string) string +type applyFn func(input []byte) []byte var applyFunctions = map[string]applyFn{ "url": urlEncode, "base64": base64Encode, } -func urlEncode(input string) string { - return url.QueryEscape(input) +func urlEncode(input []byte) []byte { + return []byte(url.QueryEscape(string(input))) } -func base64Encode(input string) string { - return base64.StdEncoding.EncodeToString([]byte(input)) +func base64Encode(input []byte) []byte { + dst := make([]byte, base64.StdEncoding.EncodedLen(len(input))) + base64.StdEncoding.Encode(dst, input) + return dst }