Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Code coverage profiles and other test artifacts
*.out
coverage.*
*.coverprofile
profile.cov

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
go.work.sum

# env file
.env

# Editor/IDE
# .idea/
.vscode/
node_modules/
# macOS
.DS_Store
# dev
_agent.js
24 changes: 20 additions & 4 deletions cmd/fuzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/fatih/color"
"github.com/nsecho/furlzz/internal/config"
"os"
"path/filepath"
"regexp"
Expand All @@ -14,9 +12,12 @@ import (
"strings"
"time"

"github.com/fatih/color"
"github.com/frida/frida-go/frida"
"github.com/nsecho/furlzz/mutator"
"github.com/spf13/cobra"

"github.com/nsecho/furlzz/internal/config"
"github.com/nsecho/furlzz/mutator"
)

var (
Expand All @@ -36,6 +37,11 @@ var fuzzCmd = &cobra.Command{
return err
}

debug, err := cmd.Flags().GetBool("debug")
if err != nil {
return err
}

var cfg config.Config
f, err := os.Open(configPath)
if err != nil {
Expand Down Expand Up @@ -193,7 +199,7 @@ var fuzzCmd = &cobra.Command{
delegateName = fuzzMap["delegate"].(string)
}

_ = script.ExportsCall("setup_fuzz", method, uiapp, delegateName, sceneName)
_ = script.ExportsCall("setup_fuzz", method, uiapp, delegateName, sceneName, debug)

l.Infof("Finished fuzz setup")

Expand All @@ -208,10 +214,19 @@ var fuzzCmd = &cobra.Command{
case mutated := <-ch:
lastInput = mutated.Input
l.Infof("[%s] %s\n", color.New(color.FgCyan).Sprintf("%s", mutated.Mutation), mutated.Input)

_ = script.ExportsCall("fuzz", cfg.Type, mutated.Input)

if cfg.Timeout > 0 {
time.Sleep(time.Duration(cfg.Timeout) * time.Second)
}

// Check if script has new coverage blocks
has, ok := script.ExportsCall("has_new_blocks").(bool)
if ok && has {
l.Infof("New blocks found, continuing fuzzing...")
mut.HandleNewCoverage(mutated.MutatedInputs)
}
}
}

Expand Down Expand Up @@ -275,6 +290,7 @@ func spawnApp(dev frida.DeviceInt, app string, toSpawn bool, sTimeout uint) erro

func init() {
fuzzCmd.Flags().StringP("config", "c", "furlzz.json", "Path to config file")
fuzzCmd.Flags().BoolP("debug", "d", true, "Enable debug output (useful for coverage)")

rootCmd.AddCommand(fuzzCmd)
}
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/frida/frida-go v0.12.0 h1:3cONEUvLTGvna67VmcVr/4Lle8Ec3rsDgi2rEOPbdmg=
github.com/frida/frida-go v0.12.0/go.mod h1:OyRIp58wuTcGggI6ztakXcHkTXrt5WfD9yi3pq6QyGw=
github.com/frida/frida-go v0.13.1 h1:RN097XqOvKAb3wnYocSJzCC+KMvc6Ks8ePbIrReu00w=
github.com/frida/frida-go v0.13.1/go.mod h1:O8Dg1YBGfQsBEL1a8x3GURw/JllJrcuvg78ga2OgdM4=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
Expand Down
8 changes: 4 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package main
import (
_ "embed"
"fmt"
"github.com/frida/frida-go/frida"
"github.com/nsecho/furlzz/cmd"
"io/ioutil"
"os"
"os/exec"
"path/filepath"

"github.com/frida/frida-go/frida"
"github.com/nsecho/furlzz/cmd"
)

const (
Expand Down Expand Up @@ -73,7 +73,7 @@ func main() {
sc = bundle
}
} else {
scriptContent, err = ioutil.ReadFile(filepath.Join(tempDir, agentFilename))
scriptContent, err = os.ReadFile(filepath.Join(tempDir, agentFilename))
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read agent: %v\n", err)
os.Exit(1)
Expand Down
24 changes: 24 additions & 0 deletions mutator/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"io"
"os"
"path/filepath"
"slices"
)

func (m *Mutator) getFuzzedInput(set string) string {
Expand All @@ -17,10 +18,33 @@ func (m *Mutator) getFuzzedInput(set string) string {
}

func (m *Mutator) fetchInput(set string) string {
m.mux.RLock()
defer m.mux.RUnlock()

k := m.r.Intn(len(m.inputSets[set]))
return m.inputSets[set][k]
}

func (m *Mutator) addCorpus(set, value string) {
m.mux.Lock()
defer m.mux.Unlock()

if set == "" || value == "" {
return
}

if m.inputSets != nil {
if slices.Contains(m.inputSets[set], value) {
return
}

if _, e := m.inputSets[set]; !e {
m.inputSets[set] = []string{}
}
m.inputSets[set] = append(m.inputSets[set], value)
}
}

func readCrashes(app string) ([]string, error) {
files, _ := filepath.Glob("fcrash_*_*")

Expand Down
21 changes: 17 additions & 4 deletions mutator/mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"math/rand"
"strings"
"sync"
"time"
)

Expand Down Expand Up @@ -40,6 +41,7 @@ func NewMutator(inp, app string, runs uint, fnName string, ignoreCrashes bool, i
}

type Mutator struct {
mux sync.RWMutex
fuzzIdx int
runs uint
baseURL string
Expand All @@ -57,8 +59,9 @@ type Mutator struct {
}

type Mutated struct {
Input string
Mutation string
Input string
Mutation string
MutatedInputs []string
}

func (m *Mutator) Close() {
Expand Down Expand Up @@ -152,9 +155,19 @@ func (m *Mutator) mutateAndSend() bool {
}

m.ch <- &Mutated{
Input: inp,
Mutation: method,
Input: inp,
Mutation: method,
MutatedInputs: mutatedInputs,
}
m.lastInput = strings.Join(mutatedInputs, "")
return true
}

func (m *Mutator) HandleNewCoverage(mutatedInputs []string) {
if len(mutatedInputs) == 0 {
return
}
for i, input := range mutatedInputs {
m.addCorpus(fmt.Sprintf("FUZZ%d", i+1), input)
}
}
46 changes: 46 additions & 0 deletions mutator/mutator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package mutator

import (
"log"
"testing"
)

func TestHandleNewCoverage(t *testing.T) {
initialCorpus := map[string][]string{
"FUZZ1": {"test"},
"FUZZ2": {"123"},
}

m := NewMutator(
"user=FUZZ1 pass=FUZZ2",
"myapp",
11,
"url",
false,
initialCorpus,
)
defer m.Close()

ch := m.Mutate()

count := 0
for {
select {
case mutated := <-ch:
if mutated == nil {
return
}

t.Log("Mutated Input:", mutated.Input)
if count%5 == 0 {
// simulate has new coverage path
log.Println("Simulate has new coverage path")
m.HandleNewCoverage(mutated.MutatedInputs)
log.Println("corpus", m.inputSets)
}
count++
case <-m.quit:
return
}
}
}
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "furlzz",
"version": "1.0.0",
"type":"module",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"build": "frida-compile -S -c script.ts -o _agent.js"
},
"repository": {
"type": "git",
Expand All @@ -17,5 +17,8 @@
"homepage": "https://github.com/NSEcho/furlzz#readme",
"dependencies": {
"frida-objc-bridge": "^8.0.4"
},
"devDependencies": {
"@types/frida-gum": "^19.0.0"
}
}
}
Loading
Loading