hacktricks/macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing
2024-04-10 15:33:28 +00:00
..
arm64-basic-assembly.md Translated ['binary-exploitation/heap/README.md', 'binary-exploitation/h 2024-04-10 15:33:28 +00:00
introduction-to-x64.md GitBook: No commit message 2024-04-06 18:35:30 +00:00
README.md Translated ['exploiting/tools/README.md', 'macos-hardening/macos-securit 2024-03-28 08:47:57 +00:00

App macOS - Ispezione, debug e Fuzzing

Impara l'hacking su AWS da zero a esperto con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Analisi Statica

otool

otool -L /bin/ls #List dynamically linked libraries
otool -tv /bin/ps #Decompile application

objdump

{% codice overflow="wrap" %}

objdump -m --dylibs-used /bin/ls #List dynamically linked libraries
objdump -m -h /bin/ls # Get headers information
objdump -m --syms /bin/ls # Check if the symbol table exists to get function names
objdump -m --full-contents /bin/ls # Dump every section
objdump -d /bin/ls # Dissasemble the binary
objdump --disassemble-symbols=_hello --x86-asm-syntax=intel toolsdemo #Disassemble a function using intel flavour

jtool2

Lo strumento può essere utilizzato come sostituto di codesign, otool e objdump, e fornisce alcune funzionalità aggiuntive. Scaricalo qui o installalo con brew.

# Install
brew install --cask jtool2

jtool2 -l /bin/ls # Get commands (headers)
jtool2 -L /bin/ls # Get libraries
jtool2 -S /bin/ls # Get symbol info
jtool2 -d /bin/ls # Dump binary
jtool2 -D /bin/ls # Decompile binary

# Get signature information
ARCH=x86_64 jtool2 --sig /System/Applications/Automator.app/Contents/MacOS/Automator

# Get MIG information
jtool2 -d __DATA.__const myipc_server | grep MIG

Codesign / ldid

{% hint style="danger" %} Codesign può essere trovato in macOS mentre ldid può essere trovato in iOS {% endhint %}

# Get signer
codesign -vv -d /bin/ls 2>&1 | grep -E "Authority|TeamIdentifier"

# Check if the apps contents have been modified
codesign --verify --verbose /Applications/Safari.app

# Get entitlements from the binary
codesign -d --entitlements :- /System/Applications/Automator.app # Check the TCC perms

# Check if the signature is valid
spctl --assess --verbose /Applications/Safari.app

# Sign a binary
codesign -s <cert-name-keychain> toolsdemo

# Get signature info
ldid -h <binary>

# Get entitlements
ldid -e <binary>

# Change entilements
## /tmp/entl.xml is a XML file with the new entitlements to add
ldid -S/tmp/entl.xml <binary>

SuspiciousPackage

SuspiciousPackage è uno strumento utile per ispezionare i file .pkg (installatori) e vedere cosa c'è dentro prima di installarli.
Questi installatori hanno script bash preinstall e postinstall che gli autori di malware di solito sfruttano per persistere il malware.

hdiutil

Questo strumento consente di montare le immagini disco Apple (.dmg) per ispezionarle prima di eseguire qualsiasi operazione:

hdiutil attach ~/Downloads/Firefox\ 58.0.2.dmg

Obiettivo-C

Metadati

{% hint style="danger" %} Si noti che i programmi scritti in Objective-C mantengono le loro dichiarazioni di classe quando vengono compilati in binari Mach-O. Tali dichiarazioni di classe includono il nome e il tipo di: {% endhint %}

  • La classe
  • I metodi della classe
  • Le variabili di istanza della classe

È possibile ottenere queste informazioni utilizzando class-dump:

class-dump Kindle.app

Chiamata di funzione

Quando una funzione viene chiamata in un binario che utilizza Objective-C, il codice compilato invece di chiamare direttamente quella funzione, chiamerà objc_msgSend. Questo a sua volta chiamerà la funzione finale:

I parametri che questa funzione si aspetta sono:

  • Il primo parametro (self) è "un puntatore che punta all'istanza della classe che deve ricevere il messaggio". O più semplicemente, è l'oggetto su cui il metodo viene invocato. Se il metodo è un metodo di classe, questo sarà un'istanza dell'oggetto della classe (nel suo complesso), mentre per un metodo di istanza, self punterà a un'istanza istanziata della classe come oggetto.
  • Il secondo parametro, (op), è "il selettore del metodo che gestisce il messaggio". Di nuovo, in modo più semplice, questo è semplicemente il nome del metodo.
  • I parametri rimanenti sono eventuali valori richiesti dal metodo (op).

Scopri come ottenere facilmente queste informazioni con lldb in ARM64 in questa pagina:

{% content-ref url="arm64-basic-assembly.md" %} arm64-basic-assembly.md {% endcontent-ref %}

x64:

Argomento Registro (per) objc_msgSend
1° argomento rdi self: oggetto su cui il metodo viene invocato
2° argomento rsi op: nome del metodo
3° argomento rdx 1° argomento per il metodo
4° argomento rcx 2° argomento per il metodo
5° argomento r8 3° argomento per il metodo
6° argomento r9 4° argomento per il metodo
7°+ argomento

rsp+
(nello stack)

5°+ argomento per il metodo

Swift

Con i binari Swift, poiché c'è compatibilità con Objective-C, a volte è possibile estrarre le dichiarazioni utilizzando class-dump ma non sempre.

Con i comandi jtool -l o otool -l è possibile trovare diverse sezioni che iniziano con il prefisso __swift5:

jtool2 -l /Applications/Stocks.app/Contents/MacOS/Stocks
LC 00: LC_SEGMENT_64              Mem: 0x000000000-0x100000000    __PAGEZERO
LC 01: LC_SEGMENT_64              Mem: 0x100000000-0x100028000    __TEXT
[...]
Mem: 0x100026630-0x100026d54        __TEXT.__swift5_typeref
Mem: 0x100026d60-0x100027061        __TEXT.__swift5_reflstr
Mem: 0x100027064-0x1000274cc        __TEXT.__swift5_fieldmd
Mem: 0x1000274cc-0x100027608        __TEXT.__swift5_capture
[...]

Puoi trovare ulteriori informazioni sull'informazione memorizzata in questa sezione in questo post del blog.

Inoltre, i binari Swift potrebbero avere simboli (ad esempio le librerie devono memorizzare i simboli in modo che le sue funzioni possano essere chiamate). I simboli di solito contengono le informazioni sul nome della funzione e sugli attributi in modo poco chiaro, quindi sono molto utili e ci sono "demanglers" che possono ottenere il nome originale:

# Ghidra plugin
https://github.com/ghidraninja/ghidra_scripts/blob/master/swift_demangler.py

# Swift cli
swift demangle

Binari compressi

  • Controllare l'alta entropia
  • Controllare le stringhe (se non ci sono stringhe comprensibili, il binario potrebbe essere compresso)
  • Il pacchetto UPX per MacOS genera una sezione chiamata "__XHDR"

Analisi dinamica

{% hint style="warning" %} Nota che per eseguire il debug dei binari, SIP deve essere disabilitato (csrutil disable o csrutil enable --without debug) oppure è necessario copiare i binari in una cartella temporanea e rimuovere la firma con codesign --remove-signature <percorso-binario> o consentire il debug del binario (puoi utilizzare questo script) {% endhint %}

{% hint style="warning" %} Nota che per strumentalizzare i binari di sistema (come cloudconfigurationd) su macOS, SIP deve essere disabilitato (rimuovere solo la firma non funzionerà). {% endhint %}

Log unificati

MacOS genera molti log che possono essere molto utili durante l'esecuzione di un'applicazione per capire cosa sta facendo.

Inoltre, ci sono alcuni log che conterranno il tag <private> per nascondere alcune informazioni identificabili dall'utente o dal computer. Tuttavia, è possibile installare un certificato per divulgare queste informazioni. Segui le spiegazioni da qui.

Hopper

Pannello sinistro

Nel pannello sinistro di Hopper è possibile vedere i simboli (Etichette) del binario, l'elenco delle procedure e delle funzioni (Proc) e le stringhe (Str). Queste non sono tutte le stringhe ma quelle definite in diverse parti del file Mac-O (come cstring o objc_methname).

Pannello centrale

Nel pannello centrale è possibile vedere il codice disassemblato. E puoi vederlo come disassemblaggio grezzo, come grafico, come decompilato e come binario cliccando sull'icona rispettiva:

Facendo clic con il tasto destro su un oggetto di codice è possibile vedere i riferimenti da/a quell'oggetto o persino cambiarne il nome (questo non funziona nel pseudocodice decompilato):

Inoltre, nella parte centrale in basso è possibile scrivere comandi python.

Pannello destro

Nel pannello destro è possibile vedere informazioni interessanti come la cronologia di navigazione (così sai come sei arrivato alla situazione attuale), il grafo delle chiamate dove puoi vedere tutte le funzioni che chiamano questa funzione e tutte le funzioni che questa funzione chiama, e le informazioni sulle variabili locali.

dtrace

Consente agli utenti di accedere alle applicazioni a un livello estremamente basso e fornisce un modo per gli utenti di tracciare programmi e persino modificare il loro flusso di esecuzione. Dtrace utilizza sonde che sono posizionate in tutto il kernel e si trovano in posizioni come l'inizio e la fine delle chiamate di sistema.

DTrace utilizza la funzione dtrace_probe_create per creare una sonda per ogni chiamata di sistema. Queste sonde possono essere attivate nel punto di ingresso e di uscita di ogni chiamata di sistema. L'interazione con DTrace avviene tramite /dev/dtrace che è disponibile solo per l'utente root.

{% hint style="success" %} Per abilitare Dtrace senza disabilitare completamente la protezione SIP, è possibile eseguire in modalità di ripristino: csrutil enable --without dtrace

È anche possibile dtrace o dtruss i binari che hai compilato. {% endhint %}

Le sonde disponibili di dtrace possono essere ottenute con:

dtrace -l | head
ID   PROVIDER            MODULE                          FUNCTION NAME
1     dtrace                                                     BEGIN
2     dtrace                                                     END
3     dtrace                                                     ERROR
43    profile                                                     profile-97
44    profile                                                     profile-199

Il nome della sonda è composto da quattro parti: il provider, il modulo, la funzione e il nome (fbt:mach_kernel:ptrace:entry). Se non si specifica una parte del nome, Dtrace applicherà quella parte come un carattere jolly.

Per configurare DTrace per attivare le sonde e specificare quali azioni eseguire quando vengono attivate, dovremo utilizzare il linguaggio D.

È possibile trovare una spiegazione più dettagliata e ulteriori esempi su https://illumos.org/books/dtrace/chp-intro.html

Esempi

Eseguire man -k dtrace per elencare gli script DTrace disponibili. Esempio: sudo dtruss -n binary

  • In linea
#Count the number of syscalls of each running process
sudo dtrace -n 'syscall:::entry {@[execname] = count()}'
  • script
syscall:::entry
/pid == $1/
{
}

#Log every syscall of a PID
sudo dtrace -s script.d 1234
syscall::open:entry
{
printf("%s(%s)", probefunc, copyinstr(arg0));
}
syscall::close:entry
{
printf("%s(%d)\n", probefunc, arg0);
}

#Log files opened and closed by a process
sudo dtrace -s b.d -c "cat /etc/hosts"
syscall:::entry
{
;
}
syscall:::return
{
printf("=%d\n", arg1);
}

#Log sys calls with values
sudo dtrace -s syscalls_info.d -c "cat /etc/hosts"

dtruss

dtruss -c ls #Get syscalls of ls
dtruss -c -p 1000 #get syscalls of PID 1000

ktrace

Puoi utilizzare questo anche con SIP attivato

ktrace trace -s -S -t c -c ls | grep "ls("

ProcessMonitor

ProcessMonitor è uno strumento molto utile per controllare le azioni correlate ai processi che un processo sta eseguendo (ad esempio, monitorare quali nuovi processi un processo sta creando).

SpriteTree

SpriteTree è uno strumento che stampa le relazioni tra i processi.
È necessario monitorare il tuo Mac con un comando come sudo eslogger fork exec rename create > cap.json (il terminale che lo avvia richiede FDA). E poi puoi caricare il json in questo strumento per visualizzare tutte le relazioni:

FileMonitor

FileMonitor consente di monitorare gli eventi dei file (come creazione, modifiche ed eliminazioni) fornendo informazioni dettagliate su tali eventi.

Crescendo

Crescendo è uno strumento GUI con l'aspetto e la sensazione che gli utenti di Windows potrebbero conoscere da Procmon di Microsoft Sysinternal. Questo strumento consente di avviare e interrompere la registrazione di vari tipi di eventi, consente di filtrare questi eventi per categorie come file, processo, rete, ecc., e fornisce la funzionalità per salvare gli eventi registrati in un formato json.

Apple Instruments

Apple Instruments fanno parte degli strumenti per sviluppatori di Xcode, utilizzati per monitorare le prestazioni dell'applicazione, identificare perdite di memoria e tracciare l'attività del filesystem.

fs_usage

Consente di seguire le azioni eseguite dai processi:

fs_usage -w -f filesys ls #This tracks filesystem actions of proccess names containing ls
fs_usage -w -f network curl #This tracks network actions

TaskExplorer

Taskexplorer è utile per vedere le librerie utilizzate da un binario, i file che sta utilizzando e le connessioni di rete.
Controlla anche i processi binari su virustotal e mostra informazioni sul binario.

PT_DENY_ATTACH

In questo post sul blog puoi trovare un esempio su come debuggare un demone in esecuzione che utilizza PT_DENY_ATTACH per impedire il debug anche se SIP era disabilitato.

lldb

lldb è lo strumento di debugging binario di macOS de facto.

lldb ./malware.bin
lldb -p 1122
lldb -n malware.bin
lldb -n malware.bin --waitfor

Puoi impostare il flavor di intel quando usi lldb creando un file chiamato .lldbinit nella tua cartella home con la seguente riga:

settings set target.x86-disassembly-flavor intel

{% hint style="warning" %} All'interno di lldb, eseguire il dump di un processo con process save-core {% endhint %}

(lldb) ComandoDescrizione
run (r)Avvia l'esecuzione, che continuerà senza interruzioni fino a quando non viene raggiunto un punto di interruzione o il processo termina.
continue (c)Continua l'esecuzione del processo in debug.
nexti (n / ni)Esegue l'istruzione successiva. Questo comando salterà le chiamate alle funzioni.
stepi (s / si)Esegue l'istruzione successiva. A differenza del comando nexti, questo comando entrerà nelle chiamate alle funzioni.
finish (f)Esegue il resto delle istruzioni nella funzione corrente ("frame") e si ferma.
control + cSospende l'esecuzione. Se il processo è stato avviato (r) o continuato (c), questo farà sì che il processo si fermi ...dovunque si stia attualmente eseguendo.
breakpoint (b)

b main #Qualsiasi funzione chiamata main

b <binname>`main #Funzione principale del binario

b set -n main --shlib <lib_name> #Funzione principale del binario indicato

b -[NSDictionary objectForKey:]

b -a 0x0000000100004bd9

br l #Elenco dei punti di interruzione

br e/dis <num> #Abilita/Disabilita il punto di interruzione

breakpoint delete <num>

help

help breakpoint #Ottieni aiuto sul comando breakpoint

help memory write #Ottieni aiuto per scrivere nella memoria

reg

reg read

reg read $rax

reg read $rax --format <formato>

reg write $rip 0x100035cc0

x/s <reg/memory addressMostra la memoria come stringa terminata da null.
x/i <reg/memory addressMostra la memoria come istruzione assembly.
x/b <reg/memory addressMostra la memoria come byte.
print object (po)

Stamperà l'oggetto referenziato dal parametro

po $raw

{

dnsChanger = {

"affiliate" = "";

"blacklist_dns" = ();

Nota che la maggior parte delle API o metodi Objective-C di Apple restituiscono oggetti e quindi dovrebbero essere visualizzati tramite il comando "print object" (po). Se po non produce un output significativo, utilizzare x/b

memorymemory read 0x000....
memory read $x0+0xf2a
memory write 0x100600000 -s 4 0x41414141 #Scrivi AAAA in quell'indirizzo
memory write -f s $rip+0x11f+7 "AAAA" #Scrivi AAAA nell'indirizzo
disassembly

dis #Disassembla la funzione corrente

dis -n <nomefunzione> #Disassembla la funzione

dis -n <nomefunzione> -b <basename> #Disassembla la funzione
dis -c 6 #Disassembla 6 righe
dis -c 0x100003764 -e 0x100003768 # Da un indirizzo all'altro
dis -p -c 4 # Inizia a disassemblare dall'indirizzo corrente

parrayparray 3 (char **)$x1 # Controlla l'array di 3 componenti nel registro x1

{% hint style="info" %} Quando si chiama la funzione objc_sendMsg, il registro rsi contiene il nome del metodo come stringa terminata da null ("C"). Per stampare il nome tramite lldb fare:

(lldb) x/s $rsi: 0x1000f1576: "startMiningWithPort:password:coreCount:slowMemory:currency:"

(lldb) print (char*)$rsi:
(char *) $1 = 0x00000001000f1576 "startMiningWithPort:password:coreCount:slowMemory:currency:"

(lldb) reg read $rsi: rsi = 0x00000001000f1576 "startMiningWithPort:password:coreCount:slowMemory:currency:" {% endhint %}

Analisi Anti-Dinamica

Rilevamento delle VM

  • Il comando sysctl hw.model restituisce "Mac" quando l'host è un MacOS ma qualcosa di diverso quando si tratta di una VM.
  • Giocando con i valori di hw.logicalcpu e hw.physicalcpu alcuni malware cercano di rilevare se si tratta di una VM.
  • Alcuni malware possono anche rilevare se la macchina è basata su VMware in base all'indirizzo MAC (00:50:56).
  • È anche possibile scoprire se un processo viene debuggato con un codice semplice come:
  • if(P_TRACED == (info.kp_proc.p_flag & P_TRACED)){ //processo in fase di debug }
  • È possibile anche invocare la chiamata di sistema ptrace con il flag PT_DENY_ATTACH. Questo impedisce a un debugger di attaccare e tracciare.
  • È possibile verificare se la funzione sysctl o ptrace viene importata (ma il malware potrebbe importarla dinamicamente)
  • Come indicato in questo articolo, “Sconfiggere le tecniche anti-debug: varianti di ptrace su macOS” :
    Il messaggio Processo # uscito con status = 45 (0x0000002d) è di solito un segno rivelatore che il target di debug sta utilizzando PT_DENY_ATTACH

Fuzzing

ReportCrash

ReportCrash analizza i processi che si bloccano e salva un report di blocco su disco. Un report di blocco contiene informazioni che possono aiutare uno sviluppatore a diagnosticare la causa di un blocco.
Per le applicazioni e altri processi che si eseguono nel contesto di avvio per utente singolo, ReportCrash viene eseguito come LaunchAgent e salva i report di blocco nella cartella ~/Library/Logs/DiagnosticReports/ dell'utente.
Per i daemon, altri processi che si eseguono nel contesto di avvio di sistema e altri processi privilegiati, ReportCrash viene eseguito come LaunchDaemon e salva i report di blocco nella cartella /Library/Logs/DiagnosticReports del sistema.

Se ti preoccupano i report di blocco che vengono inviati ad Apple, puoi disabilitarli. In caso contrario, i report di blocco possono essere utili per capire come si è bloccato un server.

#To disable crash reporting:
launchctl unload -w /System/Library/LaunchAgents/com.apple.ReportCrash.plist
sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.ReportCrash.Root.plist

#To re-enable crash reporting:
launchctl load -w /System/Library/LaunchAgents/com.apple.ReportCrash.plist
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.ReportCrash.Root.plist

Sonno

Durante il fuzzing in un MacOS è importante non permettere al Mac di andare in modalità di sospensione:

Disconnessione SSH

Se stai facendo fuzzing tramite una connessione SSH è importante assicurarsi che la sessione non vada inattiva. Modifica il file sshd_config con:

  • TCPKeepAlive Yes
  • ClientAliveInterval 0
  • ClientAliveCountMax 0
sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist
sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist

Gestori Interni

Controlla la seguente pagina per scoprire come puoi individuare quale app è responsabile di gestire lo schema o protocollo specificato:

{% content-ref url="../macos-file-extension-apps.md" %} macos-file-extension-apps.md {% endcontent-ref %}

Enumerazione dei Processi di Rete

dtrace -n 'syscall::recv*:entry { printf("-> %s (pid=%d)", execname, pid); }' >> recv.log
#wait some time
sort -u recv.log > procs.txt
cat procs.txt

Oppure utilizza netstat o lsof

Libgmalloc

{% code overflow="wrap" %}

lldb -o "target create `which some-binary`" -o "settings set target.env-vars DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib" -o "run arg1 arg2" -o "bt" -o "reg read" -o "dis -s \$pc-32 -c 24 -m -F intel" -o "quit"

{% endcode %}

Fuzzers

AFL++

Funziona per strumenti CLI

Litefuzz

Funziona con gli strumenti GUI di macOS. Nota che alcune app macOS hanno requisiti specifici come nomi file unici, l'estensione corretta, la necessità di leggere i file dalla sandbox (~/Library/Containers/com.apple.Safari/Data)...

Alcuni esempi:

{% code overflow="wrap" %}

# iBooks
litefuzz -l -c "/System/Applications/Books.app/Contents/MacOS/Books FUZZ" -i files/epub -o crashes/ibooks -t /Users/test/Library/Containers/com.apple.iBooksX/Data/tmp -x 10 -n 100000 -ez

# -l : Local
# -c : cmdline with FUZZ word (if not stdin is used)
# -i : input directory or file
# -o : Dir to output crashes
# -t : Dir to output runtime fuzzing artifacts
# -x : Tmeout for the run (default is 1)
# -n : Num of fuzzing iterations (default is 1)
# -e : enable second round fuzzing where any crashes found are reused as inputs
# -z : enable malloc debug helpers

# Font Book
litefuzz -l -c "/System/Applications/Font Book.app/Contents/MacOS/Font Book FUZZ" -i input/fonts -o crashes/font-book -x 2 -n 500000 -ez

# smbutil (using pcap capture)
litefuzz -lk -c "smbutil view smb://localhost:4455" -a tcp://localhost:4455 -i input/mac-smb-resp -p -n 100000 -z

# screensharingd (using pcap capture)
litefuzz -s -a tcp://localhost:5900 -i input/screenshared-session --reportcrash screensharingd -p -n 100000

{% endcode %}

Ulteriori informazioni sul fuzzing di MacOS

Riferimenti

Impara l'hacking di AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks: