-
-
Save stingerIO/7f9213004aa57bf8f5c7bc3974c49e3d to your computer and use it in GitHub Desktop.
iOS URL Scheme Fuzzer
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
/* | |
* Modified from: https://codeshare.frida.re/@dki/ios-url-scheme-fuzzing/ | |
* Supports iOS 12 and 13, iPadOS 13! | |
* USAGE: frida -U -l fuzzer.js SpringBoard | |
* | |
* iOS URL Scheme Fuzzing | |
* | |
* Open the specified URL | |
* openURL("somescheme://test"); | |
* | |
* Fuzz a particular URL - use {0} as placeholder for insertion points | |
* fuzz("somescheme://somepath?param={0}"); | |
* | |
* List all crash logs matching a particular string | |
* listCrashLogs("MyApp"); | |
* | |
* | |
* You'll typically want to call openURL for the target scheme once before | |
* fuzzing to dismiss the prompt that appears the first time | |
* | |
* openURL("somescheme://test"); | |
* fuzzStrings.push("somefancyfuzzstring"); | |
* fuzz("somescheme://test/{0}"); | |
* | |
*/ | |
// have Springboard open a URL with whatever handler has claimed it | |
function openURL(url) { | |
var w = ObjC.classes.LSApplicationWorkspace.defaultWorkspace(); | |
var toOpen = ObjC.classes.NSURL.URLWithString_(url); | |
return w.openSensitiveURL_withOptions_(toOpen, null); | |
} | |
// emulate single home button press (WORKS ONLY WITH SpringBoard) | |
function homeSinglePress() { | |
var version = ObjC.classes.UIDevice.currentDevice().systemVersion().toString(); | |
if (version.startsWith("9.")) { // iOS 9 | |
ObjC.schedule(ObjC.mainQueue, function() { | |
ObjC.classes.SBUIController.sharedInstance().clickedMenuButton(); | |
}); | |
} else if (version.startsWith("10.") || version.startsWith("11.")) { // iOS 10//11 | |
ObjC.schedule(ObjC.mainQueue, function() { | |
ObjC.classes.SBUIController.sharedInstance().handleHomeButtonSinglePressUp(); | |
}); | |
} else if (version.startsWith("12.") || version.startsWith("13.")) { // iOS 12//13 | |
ObjC.schedule(ObjC.mainQueue, function() { | |
ObjC.classes.SBUIController.sharedInstance().handleHomeButtonSinglePressUp(); | |
}); | |
} else { | |
console.log("Untested on this device firmware version! :("); | |
} | |
} | |
// check for crash logs and move them to /tmp/ if they exist | |
// can result in false positives if the appname is a substring of another app | |
function listCrashLogs(appname) { | |
var match = appname + "*.ips"; | |
var pred = ObjC.classes.NSPredicate.predicateWithFormat_('SELF like "' + match + '"'); | |
var fm = ObjC.classes.NSFileManager.defaultManager(); | |
var dirlist = fm.contentsOfDirectoryAtPath_error_("/private/var/mobile/Library/Logs/CrashReporter", NULL); | |
var results = dirlist.filteredArrayUsingPredicate_(pred); | |
if (results.count() > 0) { | |
for (var i = 0; i < results.count(); i++) { | |
var src = results.objectAtIndex_(i).toString(); | |
console.log(src); | |
} | |
} | |
return results.count(); | |
} | |
// check for crash logs and move them to /tmp/ if they exist | |
// can result in false positives if the appname is a substring of another app | |
function moveCrashLogs(appname) { | |
var match = appname + "*.ips"; | |
var pred = ObjC.classes.NSPredicate.predicateWithFormat_('SELF like "' + match + '"'); | |
var fm = ObjC.classes.NSFileManager.defaultManager(); | |
var dirlist = fm.contentsOfDirectoryAtPath_error_("/private/var/mobile/Library/Logs/CrashReporter", NULL); | |
var results = dirlist.filteredArrayUsingPredicate_(pred); | |
if (results.count() > 0) { | |
for (var i = 0; i < results.count(); i++) { | |
var src = results.objectAtIndex_(i).toString(); | |
console.log('Moving ' + src); | |
var result = fm.moveItemAtPath_toPath_error_("/private/var/mobile/Library/Logs/CrashReporter/" + src, "/tmp/" + src, NULL); | |
console.log('Result: ' + result); | |
} | |
return true; | |
} | |
return false; | |
} | |
// https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format | |
// this is what happens when i port things from python D: | |
if (!String.format) { | |
String.format = function(format) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
return format.replace(/{(\d+)}/g, function(match, number) { | |
return typeof args[number] != 'undefined' ? | |
args[number] : | |
match; | |
}); | |
}; | |
} | |
// add/remove default fuzz strings here, or at the command line | |
var fuzzStrings = ["0", | |
"1", | |
"-1", | |
"null", | |
"nil", | |
"99999999999999999999999999999999999", | |
Array(257).join("A"), | |
Array(1026).join("A"), | |
Array(4096).join("5"), | |
Array(9096).join("8"), | |
"'", | |
"%20d", | |
"%20n", | |
"%20x", | |
"%20s" | |
]; | |
fuzzStrings.iter = function() { | |
var index = 0; | |
var data = this; | |
return { | |
next: function() { | |
return { | |
value: data[index], | |
done: index++ == (data.length - 1) | |
}; | |
}, | |
hasNext: function() { | |
return index < data.length; | |
} | |
} | |
}; | |
// fuzz a url for a registered scheme | |
// use {0} for placeholders: blah://test/{0} | |
function fuzz(appname, url) { | |
function Fuzzer(url, appname, iter) { | |
this.url = url; | |
this.appname = appname; | |
this.iter = iter; | |
} | |
Fuzzer.prototype.checkForCrash = function(done) { | |
// background in case it is still running | |
homeSinglePress(); | |
// check for a crash | |
if (listCrashLogs(this.appname) > 0) { | |
console.log("Crashed!"); | |
} | |
else { | |
console.log("OK!"); | |
} | |
// fuzz next url | |
if (!done) { | |
this.fuzz(); | |
} | |
}; | |
Fuzzer.prototype.fuzz = function() { | |
var term = this.iter.next(); | |
// create the url | |
var fuzzedURL = String.format(this.url, term.value); | |
// this should launch the app | |
if (openURL(fuzzedURL)) { | |
console.log("Opened URL: " + fuzzedURL); | |
} else { | |
console.log("URL refused by SpringBoard: " + fuzzedURL); | |
} | |
// wait a few seconds before backgrounding | |
ObjC.classes.NSThread.sleepForTimeInterval_(3); | |
this.checkForCrash(term.done); | |
}; | |
console.log("Watching for crashes from " + appname + "..."); | |
// start by clearing any current logs | |
if (moveCrashLogs(appname)) { | |
console.log("Moved one or more logs to /tmp/ before fuzzing!"); | |
} | |
else { | |
console.log("No logs were moved."); | |
} | |
// get iterator for fuzz strings | |
var iter = fuzzStrings.iter(); | |
var fuzzer = new Fuzzer(url, appname, iter); | |
fuzzer.fuzz(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment