-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
378 additions
and
201 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package cmd | ||
|
||
import ( | ||
"errors" | ||
"github.com/frida/frida-go/frida" | ||
"github.com/spf13/cobra" | ||
"io" | ||
"os" | ||
"time" | ||
) | ||
|
||
var crashCmd = &cobra.Command{ | ||
Use: "crash", | ||
Short: "Run the application with crash", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
sfile, err := cmd.Flags().GetString("session") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
l.Infof("Reading session file %s", sfile) | ||
s, err := NewSession(sfile) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
crash, err := cmd.Flags().GetString("crash") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
inpf, err := os.Open(crash) | ||
if err != nil { | ||
return err | ||
} | ||
defer inpf.Close() | ||
|
||
input, _ := io.ReadAll(inpf) | ||
|
||
l.Infof("Read %s from crash file", string(input)) | ||
|
||
dev := frida.USBDevice() | ||
if dev == nil { | ||
return errors.New("no USB device detected") | ||
} | ||
defer dev.Clean() | ||
|
||
sess, err := dev.Attach(s.App, nil) | ||
if err != nil { | ||
return err | ||
} | ||
defer sess.Clean() | ||
|
||
l.Infof("Attached to %s", s.App) | ||
|
||
script, err := sess.CreateScript(scriptContent) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
script.On("message", func(message string) { | ||
l.Infof("script output: %s", message) | ||
}) | ||
|
||
if err := script.Load(); err != nil { | ||
return err | ||
} | ||
defer script.Clean() | ||
|
||
l.Infof("Loaded script") | ||
|
||
l.Infof("Sleeping for two seconds and triggering crash") | ||
|
||
time.Sleep(2 * time.Second) | ||
|
||
_ = script.ExportsCall("fuzz", s.Method, string(input)) | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
func init() { | ||
crashCmd.Flags().StringP("session", "s", "", "session path") | ||
crashCmd.Flags().StringP("crash", "c", "", "crash file") | ||
|
||
rootCmd.AddCommand(crashCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package cmd | ||
|
||
import ( | ||
"errors" | ||
"github.com/fatih/color" | ||
"github.com/frida/frida-go/frida" | ||
"github.com/nsecho/furlzz/mutator" | ||
"github.com/spf13/cobra" | ||
"os" | ||
"strings" | ||
"time" | ||
) | ||
|
||
var fuzzCmd = &cobra.Command{ | ||
Use: "fuzz", | ||
Short: "Fuzz URL scheme", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
var validInputs []string | ||
var err error | ||
|
||
base, err := cmd.Flags().GetString("base") | ||
if err != nil { | ||
return err | ||
} | ||
if base == "" { | ||
return errors.New("base URL cannot be empty") | ||
} | ||
|
||
input, err := cmd.Flags().GetString("input") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if input == "" && strings.Contains(base, "FUZZ") { | ||
return errors.New("input directory cannot be empty when using FUZZ keyword") | ||
} | ||
|
||
if strings.Contains(base, "FUZZ") { | ||
validInputs, err = readInputs(input) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
runs, err := cmd.Flags().GetUint("runs") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fn, err := cmd.Flags().GetString("function") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
timeout, err := cmd.Flags().GetUint("timeout") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
method, err := cmd.Flags().GetString("method") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
l.Infof("Fuzzing base URL \"%s\"", base) | ||
if strings.Contains(base, "FUZZ") { | ||
l.Infof("Read %d inputs from %s directory", | ||
len(validInputs), input) | ||
} else { | ||
l.Infof("Fuzzing base URL") | ||
} | ||
|
||
if runs == 0 { | ||
l.Infof("Fuzzing indefinitely") | ||
} else { | ||
l.Infof("Fuzzing with %d mutated inputs", runs) | ||
} | ||
|
||
if timeout != 0 { | ||
l.Infof("Sleeping %d seconds between each fuzz case", timeout) | ||
} | ||
|
||
app, err := cmd.Flags().GetString("app") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if app == "" { | ||
return errors.New("error: app cannot be empty") | ||
} | ||
|
||
dev := frida.USBDevice() | ||
if dev == nil { | ||
return errors.New("no USB device detected") | ||
} | ||
defer dev.Clean() | ||
|
||
sess, err := dev.Attach(app, nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
l.Infof("Attached to %s", app) | ||
|
||
var lastInput string | ||
|
||
sess.On("detached", func(reason frida.SessionDetachReason, crash *frida.Crash) { | ||
l.Infof("Session detached; reason=%s", reason.String()) | ||
out := crashSHA256(lastInput) | ||
err := func() error { | ||
f, err := os.Create(out) | ||
if err != nil { | ||
return err | ||
} | ||
f.WriteString(lastInput) | ||
return nil | ||
}() | ||
if err != nil { | ||
l.Errorf("Error writing crash file: %v", err) | ||
} else { | ||
l.Infof("Written crash to: %s", out) | ||
} | ||
s := Session{ | ||
App: app, | ||
Base: base, | ||
Function: fn, | ||
Method: method, | ||
} | ||
if err := s.WriteToFile(); err != nil { | ||
l.Errorf("Error writing session file: %v", err) | ||
} else { | ||
l.Infof("Written session file") | ||
} | ||
os.Exit(1) | ||
}) | ||
|
||
script, err := sess.CreateScript(scriptContent) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
script.On("message", func(message string) { | ||
l.Infof("script output: %s", message) | ||
}) | ||
|
||
if err := script.Load(); err != nil { | ||
return err | ||
} | ||
|
||
l.Infof("Loaded script") | ||
|
||
m := mutator.NewMutator(base, runs, fn, validInputs...) | ||
ch := m.Mutate() | ||
|
||
for mutated := range ch { | ||
lastInput = mutated.Input | ||
l.Infof("[%s] %s\n", color.New(color.FgCyan).Sprintf("%s", mutated.Mutation), mutated.Input) | ||
_ = script.ExportsCall("fuzz", method, mutated.Input) | ||
if timeout > 0 { | ||
time.Sleep(time.Duration(timeout) * time.Second) | ||
} | ||
} | ||
return nil | ||
}, | ||
} | ||
|
||
func init() { | ||
fuzzCmd.Flags().StringP("app", "a", "Gadget", "Application name to attach to") | ||
fuzzCmd.Flags().StringP("base", "b", "", "base URL to fuzz") | ||
fuzzCmd.Flags().StringP("input", "i", "", "path to input directory") | ||
fuzzCmd.Flags().StringP("function", "f", "", "apply the function to mutated input (url, base64)") | ||
fuzzCmd.Flags().StringP("method", "m", "delegate", "method of opening url (delegate, app)") | ||
fuzzCmd.Flags().UintP("runs", "r", 0, "number of runs") | ||
fuzzCmd.Flags().UintP("timeout", "t", 1, "sleep X seconds between each case") | ||
|
||
rootCmd.AddCommand(fuzzCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package cmd | ||
|
||
import ( | ||
"crypto/sha256" | ||
"fmt" | ||
"github.com/nsecho/furlzz/logger" | ||
"github.com/spf13/cobra" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
var scriptContent string | ||
|
||
var l = *logger.NewLogger() | ||
|
||
var rootCmd = &cobra.Command{ | ||
Use: "furlzz", | ||
Short: "Fuzz iOS URL schemes", | ||
CompletionOptions: cobra.CompletionOptions{ | ||
DisableDefaultCmd: true, | ||
}, | ||
SilenceErrors: true, | ||
SilenceUsage: true, | ||
} | ||
|
||
func Execute(sc string) error { | ||
scriptContent = sc | ||
return rootCmd.Execute() | ||
} | ||
|
||
func readInputs(dirPath string) ([]string, error) { | ||
files, err := os.ReadDir(dirPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var validInputs []string | ||
|
||
for _, fl := range files { | ||
if fl.IsDir() { | ||
continue | ||
} | ||
data, err := func() ([]byte, error) { | ||
f, err := os.Open(filepath.Join(dirPath, fl.Name())) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer f.Close() | ||
|
||
data, _ := io.ReadAll(f) | ||
return data, nil | ||
}() | ||
if err != nil { | ||
return nil, err | ||
} | ||
validInputs = append(validInputs, string(data)) | ||
} | ||
return validInputs, nil | ||
} | ||
|
||
func crashSHA256(inp string) string { | ||
h := sha256.New() | ||
h.Write([]byte(inp)) | ||
return fmt.Sprintf("%x", h.Sum(nil)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"gopkg.in/yaml.v3" | ||
"os" | ||
"time" | ||
) | ||
|
||
func NewSession(sessionFile string) (*Session, error) { | ||
var s Session | ||
|
||
f, err := os.Open(sessionFile) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer f.Close() | ||
|
||
if err := yaml.NewDecoder(f).Decode(&s); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &s, nil | ||
} | ||
|
||
type Session struct { | ||
App string `yaml:"app"` | ||
Base string `yaml:"base"` | ||
Function string `yaml:"fn"` | ||
Method string `yaml:"method"` | ||
} | ||
|
||
func (s *Session) WriteToFile() error { | ||
t := time.Now() | ||
outputFilename := fmt.Sprintf("session_%s", t.Format("2006_01_02_15:04:05")) | ||
|
||
f, err := os.Create(outputFilename) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
|
||
return yaml.NewEncoder(f).Encode(&s) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.