Konfiguracja Frida w iOS

Reading time: 9 minutes

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks

Instalacja Frida

Kroki do zainstalowania Frida na urządzeniu z Jailbreakiem:

  1. Otwórz aplikację Cydia/Sileo.
  2. Przejdź do Zarządzaj -> Źródła -> Edytuj -> Dodaj.
  3. Wprowadź "https://build.frida.re" jako URL.
  4. Przejdź do nowo dodanego źródła Frida.
  5. Zainstaluj pakiet Frida.

Jeśli używasz Corellium, musisz pobrać wersję Frida z https://github.com/frida/frida/releases (frida-gadget-[yourversion]-ios-universal.dylib.gz) i rozpakować oraz skopiować do lokalizacji dylib, o którą prosi Frida, np.: /Users/[youruser]/.cache/frida/gadget-ios.dylib

Po zainstalowaniu możesz użyć na swoim PC polecenia frida-ls-devices i sprawdzić, czy urządzenie się pojawia (twój PC musi mieć do niego dostęp).
Wykonaj również frida-ps -Uia, aby sprawdzić działające procesy telefonu.

Frida bez urządzenia z Jailbreakiem i bez patchowania aplikacji

Sprawdź ten wpis na blogu o tym, jak używać Frida na urządzeniach bez Jailbreaka bez patchowania aplikacji: https://mrbypass.medium.com/unlocking-potential-exploring-frida-objection-on-non-jailbroken-devices-without-application-ed0367a84f07

Instalacja klienta Frida

Zainstaluj frida tools:

bash
pip install frida-tools
pip install frida

Z zainstalowanym serwerem Frida i działającym oraz podłączonym urządzeniem, sprawdź, czy klient działa:

bash
frida-ls-devices  # List devices
frida-ps -Uia     # Get running processes

Frida Trace

bash
# Functions
## Trace all functions with the word "log" in their name
frida-trace -U <program> -i "*log*"
frida-trace -U <program> -i "*log*" | swift demangle # Demangle names

# Objective-C
## Trace all methods of all classes
frida-trace -U <program> -m "*[* *]"

## Trace all methods with the word "authentication" from classes that start with "NE"
frida-trace -U <program> -m "*[NE* *authentication*]"

# Plug-In
## To hook a plugin that is momentarely executed prepare Frida indicating the ID of the Plugin binary
frida-trace -U -W <if-plugin-bin> -m '*[* *]'

Pobierz wszystkie klasy i metody

  • Auto uzupełnianie: Wystarczy wykonać frida -U <program>
  • Pobierz wszystkie dostępne klasy (filtruj według ciągu)
/tmp/script.js
// frida -U <program> -l /tmp/script.js

var filterClass = "filterstring"

if (ObjC.available) {
for (var className in ObjC.classes) {
if (ObjC.classes.hasOwnProperty(className)) {
if (!filterClass || className.includes(filterClass)) {
console.log(className)
}
}
}
} else {
console.log("Objective-C runtime is not available.")
}
  • Pobierz wszystkie metody z klasy (filtruj według ciągu)
/tmp/script.js
// frida -U <program> -l /tmp/script.js

var specificClass = "YourClassName"
var filterMethod = "filtermethod"

if (ObjC.available) {
if (ObjC.classes.hasOwnProperty(specificClass)) {
var methods = ObjC.classes[specificClass].$ownMethods
for (var i = 0; i < methods.length; i++) {
if (!filterMethod || methods[i].includes(filterClass)) {
console.log(specificClass + ": " + methods[i])
}
}
} else {
console.log("Class not found.")
}
} else {
console.log("Objective-C runtime is not available.")
}
  • Wywołaj funkcję
javascript
// Find the address of the function to call
const func_addr = Module.findExportByName("<Prog Name>", "<Func Name>")
// Declare the function to call
const func = new NativeFunction(
func_addr,
"void",
["pointer", "pointer", "pointer"],
{}
)

var arg0 = null

// In this case to call this function we need to intercept a call to it to copy arg0
Interceptor.attach(wg_log_addr, {
onEnter: function (args) {
arg0 = new NativePointer(args[0])
},
})

// Wait untill a call to the func occurs
while (!arg0) {
Thread.sleep(1)
console.log("waiting for ptr")
}

var arg1 = Memory.allocUtf8String("arg1")
var txt = Memory.allocUtf8String("Some text for arg2")
wg_log(arg0, arg1, txt)

console.log("loaded")

Frida Fuzzing

Frida Stalker

From the docs: Stalker to silnik śledzenia Fridy. Umożliwia śledzenie wątków, przechwytując każdą funkcję, każdy blok, a nawet każdą instrukcję, która jest wykonywana.

Masz przykład implementacji Frida Stalker w https://github.com/poxyran/misc/blob/master/frida-stalker-example.py

To kolejny przykład, aby dołączyć Frida Stalker za każdym razem, gdy funkcja jest wywoływana:

javascript
console.log("loading")
const wg_log_addr = Module.findExportByName("<Program>", "<function_name>")
const wg_log = new NativeFunction(
wg_log_addr,
"void",
["pointer", "pointer", "pointer"],
{}
)

Interceptor.attach(wg_log_addr, {
onEnter: function (args) {
console.log(`logging the following message: ${args[2].readCString()}`)

Stalker.follow({
events: {
// only collect coverage for newly encountered blocks
compile: true,
},
onReceive: function (events) {
const bbs = Stalker.parse(events, {
stringify: false,
annotate: false,
})
console.log(
"Stalker trace of write_msg_to_log: \n" +
bbs.flat().map(DebugSymbol.fromAddress).join("\n")
)
},
})
},
onLeave: function (retval) {
Stalker.unfollow()
Stalker.flush() // this is important to get all events
},
})

caution

To jest interesujące z punktu widzenia debugowania, ale do fuzzingu, ciągłe .follow() i .unfollow() jest bardzo nieefektywne.

Fpicker

fpicker to zestaw narzędzi do fuzzingu oparty na Frida, który oferuje różne tryby fuzzingu do fuzzingu w procesie, takie jak tryb AFL++ lub tryb pasywnego śledzenia. Powinien działać na wszystkich platformach obsługiwanych przez Frida.

bash
# Get fpicker
git clone https://github.com/ttdennis/fpicker
cd fpicker

# Get Frida core devkit and prepare fpicker
wget https://github.com/frida/frida/releases/download/16.1.4/frida-core-devkit-16.1.4-[yourOS]-[yourarchitecture].tar.xz
# e.g. https://github.com/frida/frida/releases/download/16.1.4/frida-core-devkit-16.1.4-macos-arm64.tar.xz
tar -xf ./*tar.xz
cp libfrida-core.a libfrida-core-[yourOS].a #libfrida-core-macos.a

# Install fpicker
make fpicker-[yourOS] # fpicker-macos
# This generates ./fpicker

# Install radamsa (fuzzer generator)
brew install radamsa
  • Przygotuj FS:
bash
# From inside fpicker clone
mkdir -p examples/wg-log # Where the fuzzing script will be
mkdir -p examples/wg-log/out # For code coverage and crashes
mkdir -p examples/wg-log/in # For starting inputs

# Create at least 1 input for the fuzzer
echo Hello World > examples/wg-log/in/0
  • Skrypt Fuzzer (examples/wg-log/myfuzzer.js):
examples/wg-log/myfuzzer.js
// Import the fuzzer base class
import { Fuzzer } from "../../harness/fuzzer.js"

class WGLogFuzzer extends Fuzzer {
constructor() {
console.log("WGLogFuzzer constructor called")

// Get and declare the function we are going to fuzz
var wg_log_addr = Module.findExportByName(
"<Program name>",
"<func name to fuzz>"
)
var wg_log_func = new NativeFunction(
wg_log_addr,
"void",
["pointer", "pointer", "pointer"],
{}
)

// Initialize the object
super("<Program nane>", wg_log_addr, wg_log_func)
this.wg_log_addr = wg_log_addr // We cannot use "this" before calling "super"

console.log("WGLogFuzzer in the middle")

// Prepare the second argument to pass to the fuzz function
this.tag = Memory.allocUtf8String("arg2")

// Get the first argument we need to pass from a call to the functino we want to fuzz
var wg_log_global_ptr = null
console.log(this.wg_log_addr)
Interceptor.attach(this.wg_log_addr, {
onEnter: function (args) {
console.log("Entering in the function to get the first argument")
wg_log_global_ptr = new NativePointer(args[0])
},
})

while (!wg_log_global_ptr) {
Thread.sleep(1)
}
this.wg_log_global_ptr = wg_log_global_ptr
console.log("WGLogFuzzer prepare ended")
}

// This function is called by the fuzzer with the first argument being a pointer into memory
// where the payload is stored and the second the length of the input.
fuzz(payload, len) {
// Get a pointer to payload being a valid C string (with a null byte at the end)
var payload_cstring = payload.readCString(len)
this.payload = Memory.allocUtf8String(payload_cstring)

// Debug and fuzz
this.debug_log(this.payload, len)
// Pass the 2 first arguments we know the function needs and finally the payload to fuzz
this.target_function(this.wg_log_global_ptr, this.tag, this.payload)
}
}

const f = new WGLogFuzzer()
rpc.exports.fuzzer = f
  • Skompiluj fuzzer:
bash
# From inside fpicker clone
## Compile from "myfuzzer.js" to "harness.js"
frida-compile examples/wg-log/myfuzzer.js -o harness.js
  • Wywołaj fuzzer fpicker za pomocą radamsa:
bash
# Indicate fpicker to fuzz a program with the harness.js script and which folders to use
fpicker -v --fuzzer-mode active -e attach -p <Program to fuzz> -D usb -o examples/wg-log/out/ -i examples/wg-log/in/ -f harness.js --standalone-mutator cmd --mutator-command "radamsa"
# You can find code coverage and crashes in examples/wg-log/out/

caution

W tym przypadku nie restartujemy aplikacji ani nie przywracamy stanu po każdym ładunku. Więc, jeśli Frida znajdzie awarię, następne dane wejściowe po tym ładunku mogą również spowodować awarię aplikacji (ponieważ aplikacja jest w niestabilnym stanie), nawet jeśli dane wejściowe nie powinny spowodować awarii aplikacji.

Co więcej, Frida będzie przechwytywać sygnały wyjątków iOS, więc gdy Frida znajdzie awarię, prawdopodobnie raporty o awariach iOS nie będą generowane.

Aby temu zapobiec, na przykład, moglibyśmy zrestartować aplikację po każdej awarii Frida.

Logi i awarie

Możesz sprawdzić konsolę macOS lub log cli, aby sprawdzić logi macOS.
Możesz również sprawdzić logi z iOS za pomocą idevicesyslog.
Niektóre logi będą pomijać informacje, dodając <private>. Aby pokazać wszystkie informacje, musisz zainstalować jakiś profil z https://developer.apple.com/bug-reporting/profiles-and-logs/, aby włączyć te prywatne informacje.

Jeśli nie wiesz, co zrobić:

sh
vim /Library/Preferences/Logging/com.apple.system.logging.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Enable-Private-Data</key>
<true/>
</dict>
</plist>

killall -9 logd

Możesz sprawdzić awarie w:

  • iOS
  • Ustawienia → Prywatność → Analiza i ulepszenia → Dane analityczne
  • /private/var/mobile/Library/Logs/CrashReporter/
  • macOS:
  • /Library/Logs/DiagnosticReports/
  • ~/Library/Logs/DiagnosticReports

warning

iOS przechowuje tylko 25 awarii tej samej aplikacji, więc musisz to wyczyścić, inaczej iOS przestanie tworzyć awarie.

Frida Android Tutorials

{{#ref}} ../android-app-pentesting/frida-tutorial/ {{#endref}}

References

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks