Relapse: come funziona il jailbreak della PS5 fino al firmware 13.60?

Analisi della catena di exploit, dal browser al kernel, con il codice della release ufficiale.

7 min di lettura#exploit#kernel#ps5Read in English →
INDICE · 8 SEZIONI

Benvenuti nel primissimo post del mio blog personale: è un piacere avervi qui.

Ho deciso di iniziare quest'avventura e di mettere per iscritto le idee curiose che mi vengono in mente, per tenerne traccia in futuro e per aprire un dialogo su questioni che mi stanno a cuore, soprattutto di sicurezza informatica.

L'ho fatto anche per riprendere il contatto perso con la sintassi italiana (al giorno d'oggi chi non scrive le mail con l'aiuto dell'IA?).

Grazie ancora per il tempo dedicatomi.

GR


A fine settembre 2026 è stato pubblicato su GitHub Relapse, una catena di exploit open source che consente il jailbreak della PlayStation 5 sui firmware dal 7.00 al 13.60. Non servono modifiche hardware. Tutto parte da una pagina web aperta nel browser di sistema della console.

In questo articolo vediamo insieme come è costruito Relapse partendo dal codice della release ufficiale: quali fasi compongono la catena, cosa fa ciascuna e quali limiti bisogna tenere presenti. L'obiettivo è capire cosa succede dietro le quinte.

Piccolo easter egg: il nome, in inglese, significa "ricaduta", e richiama Lapse, un exploit del kernel pubblicato nel 2025 per i firmware più vecchi.

Panoramica della catena

Relapse lavora in due stadi, uno dopo l'altro, e si conclude con l'avvio di un ELF loader:

  1. Stadio browser. Sfrutta alcuni bug di JavaScriptCore, il motore JavaScript di WebKit, per ottenere lettura e scrittura arbitraria della memoria del processo del browser.
  2. Stadio kernel. Partendo da questo accesso, sfrutta una race condition nel kernel. Ottiene così lettura e scrittura della memoria del kernel, i privilegi di root e l'uscita dalla sandbox.
  3. ELF loader. A catena completata resta in ascolto sulla porta 9021 un loader che permette di eseguire software homebrew.
DIAGRAMMAmermaid
flowchart TD
    A["Pagina aperta<br/>nel browser della PS5"] --> B{"Firmware<br/>supportato?"}
    B -- no --> X["Esecuzione interrotta<br/>con un messaggio"]
    B -- sì --> C["Stadio 1: browser<br/>WebKit / JavaScriptCore"]
    C --> D["Stadio 2: kernel<br/>race condition su aio_multi_wait"]
    D --> E["ELF loader<br/>in ascolto sulla porta 9021"]
    E --> F["Homebrew, emulatori,<br/>strumenti, Linux"]

    classDef browser stroke:#e0a030,stroke-width:2px
    classDef kernel stroke:#e0607e,stroke-width:2px
    classDef ok stroke:#3fa66b,stroke-width:2px
    class C browser
    class D kernel
    class E,F ok

Il README del progetto riassume la catena così:

Dal README

Browser stage uses JSC info leaks and a structured clone object pool mismatch to corrupt a typedarray. The kernel stage combines a address leak with an aio_multi_wait uaf race to establish kernel r/w.

Nelle prossime sezioni cerchiamo di capire queste righe, scomponendole in passaggi più semplici.

Il controllo del firmware

Prima di fare qualsiasi altra cosa, la pagina verifica su quale console e su quale firmware sta girando. Per farlo legge lo User-Agent, la stringa con cui il browser si identifica ai siti. Su PS5 lo User-Agent contiene anche la versione del firmware, che viene confrontata con l'elenco delle versioni supportate:

jssrc/firmware.js
const supportedFirmware = [
  "13.60", "13.42", "13.40", "13.20", "13.00",
  "12.70", "12.60", "12.40", "12.20", "12.02", "12.00",
  "11.60", "11.20", "11.00",
  "10.60", "10.40", "10.20", "10.01", "10.00",
  "9.60", "9.40", "9.20", "9.00",
  "8.60", "8.40", "8.20", "8.00",
  "7.61", "7.60", "7.40", "7.20", "7.01", "7.00",
];

// Lo User-Agent della PS5 contiene, ad esempio, "PlayStation 5/13.60"
const firmwareUserAgent = navigator.userAgent;
const firmwareMatch = /PlayStation 5\/(\d+\.\d+)/.exec(firmwareUserAgent);
const firmwareVersion = firmwareMatch ? firmwareMatch[1] : "";

Se la console non è una PS5, o se il firmware non è nell'elenco, l'esecuzione si ferma con un messaggio esplicito:

js
rejection() {
  if (!firmwareUserAgent.includes("PlayStation 5")) return "PlayStation 5 Required";
  if (!firmwareVersion) return "FW version not found";
  if (!supportedFirmware.includes(firmwareVersion)) return `FW ${firmwareVersion} is not supported`;
  return null;
}

Le versioni sono elencate una per una perché da un firmware all'altro cambiano le posizioni di funzioni e strutture interne. Nella cartella offsets/ c'è un file per ciascuna delle 33 versioni supportate, e la pagina carica quello della console in uso.

Scheda Relapse
Firmware supportati dal 7.00 al 13.60 (33 versioni)
Console PS5 e, secondo voci circolate soprattutto su X, anche PS5 Pro
Patch di Sony firmware 14.00
Persistenza nessuna, va rieseguito dopo ogni riavvio

Stadio 1: il browser

Il browser della PS5 si basa su WebKit, lo stesso motore di Safari, noto al grande pubblico come browser predefinito di macOS e iOS, e il codice JavaScript viene eseguito da JavaScriptCore (JSC). È un componente molto complesso, ottimizzato per le prestazioni, e proprio per questo offre una superficie d'attacco ampia.

Relapse combina due tipi di bug:

  • info leak: falle che lasciano trapelare informazioni interne del motore, come gli indirizzi di memoria in cui si trovano certi oggetti;
  • un errore nella clonazione strutturata (structured clone), il meccanismo con cui il browser fa copie fedeli degli oggetti JavaScript. In determinate condizioni, durante la copia, il motore associa un oggetto a quello sbagliato.

Per rendere l'idea, pensate a una fotocopiatrice che, mentre copia un quaderno, scambia tra loro due pagine. So che sembra un esempio forzato, ma non me ne venivano in mente altri. Qui la "pagina" ottenuta per errore è un typed array, una struttura che dà accesso diretto a un'area di memoria. Una volta corrotto, il typed array può essere fatto puntare ovunque: si ottengono così lettura e scrittura arbitraria nella memoria del processo del browser.

Un dettaglio interessante riguarda gli indirizzi. Il tipo Number di JavaScript è in virgola mobile e oltre 2⁵³ perde precisione, mentre gli indirizzi di memoria della console sono a 64 bit. Per questo il codice li rappresenta come due metà da 32 bit, con una piccola classe dedicata:

jssrc/utils/int64.js (estratto semplificato)
export function int64(low = 0, hi = 0) {
  this.low = low >>> 0;   // 32 bit bassi
  this.hi  = hi  >>> 0;   // 32 bit alti
}

int64.prototype.add32 = function (value) {
  const low = (this.low + value) >>> 0;
  const hi  = (this.hi + (low < this.low ? 1 : 0)) >>> 0; // gestione del riporto
  return new int64(low, hi);
};

È una normale somma con riporto, che serve a lavorare su indirizzi a 64 bit con un tipo numerico che non può rappresentarli con precisione.

L'intera sequenza è orchestrata da src/site.js:

jssrc/site.js (accorciato)
async function run() {
  const rejection = window.firmware.rejection();
  if (rejection) throw new Error(rejection);   // firmware non supportato

  writeLog("Credits: Sonic_Iso, Jordy, ntfargo, ufm42, Dr. Yenyen, ...", "info");
  writeLog(`Firmware: ${window.fw_str}`, "info");

  const primitive = await getPrimitive();      // stadio 1: browser
  await import("./relapse_exploit.js");
  await main(primitive);                       // stadio 2: kernel
}

Un piccolo flex da parte degli sviluppatori: superato il controllo del firmware, la prima cosa che la pagina scrive a schermo sono i crediti di chi ha lavorato al progetto.

Stadio 2: il kernel

Il controllo del browser da solo non basta. Il processo del browser è confinato in una sandbox e non ha accesso alle risorse di sistema. Per superare questo limite bisogna compromettere il kernel, il nucleo del sistema operativo della PS5, che deriva da FreeBSD (nostalgia, eh?).

Relapse usa due elementi:

  1. un address leak, che rivela la posizione in memoria di alcune strutture del kernel;
  2. una race condition che provoca un use-after-free in aio_multi_wait, una funzione del sottosistema di I/O asincrono.

Si parla di race condition quando l'esito di un'operazione dipende dall'ordine in cui due thread eseguono le proprie istruzioni. L'use-after-free, invece, è una vulnerabilità ancora oggi devastante, che nasce soprattutto dalla libertà con cui chi programma gestisce la memoria: un thread continua a usare un oggetto che un altro thread ha già liberato.

DIAGRAMMAmermaid
sequenceDiagram
    participant A as Thread A
    participant O as Oggetto nel kernel
    participant B as Thread B
    A->>O: usa l'oggetto
    B->>O: libera l'oggetto (free)
    Note over O: la memoria è stata liberata,<br/>ma A conserva il riferimento
    A->>O: continua a usare il riferimento
    Note over A,O: use-after-free

Quando la race va a buon fine, lo stadio kernel ottiene lettura e scrittura nella memoria del kernel. Da lì si arriva ai privilegi di root e all'uscita dalla sandbox.

È anche la fase più delicata della catena, come avverte lo stesso README:

Dal README

The kernel exploit may hang or panic the console, so reboot before trying again if that happens.

Una race condition, per sua natura, non riesce sempre. Può capitare che la console si blocchi o vada in kernel panic: in quel caso va riavviata prima di riprovare.

Il risultato: l'ELF loader sulla porta 9021

Se entrambi gli stadi vanno a buon fine, la catena si chiude con questi messaggi:

jssrc/main.js (accorciato)
if (result.payloads) {
  log("kernel exploit complete", "info");
  log("elfldr is listening on port 9021", "info");
} else {
  log("kernel chain complete: root and sandbox escape are active", "info");
}

elfldr è un ELF loader, un servizio che resta in ascolto sulla porta 9021 della console e riceve programmi in formato ELF, lo standard degli eseguibili nei sistemi derivati da Unix come quello della PS5. Da questo momento la console può eseguire software homebrew: emulatori, strumenti, perfino Linux. La cartella payloads/ della release contiene i due componenti usati di default, cioè il loader e il binario dell'exploit del kernel.

Cosa compare a schermo

Per chi lo esegue, tutto il processo si riduce a una pagina nera su cui compaiono, una dopo l'altra, le righe di log:

DIAGRAMMAmermaid
sequenceDiagram
    participant U as Utente
    participant P as Pagina
    participant K as Console
    U->>P: apre la pagina nel browser
    P->>P: controllo del firmware
    P-->>U: [+] Credits, Agent, Firmware
    P-->>U: [*] Starting WebKit exploit
    Note over P: se il browser si blocca,<br/>si ricarica la pagina
    P-->>U: [+] ARW ready
    P->>K: avvio dello stadio kernel
    Note over K: in caso di blocco o panic,<br/>riavvio e nuovo tentativo
    K-->>U: [+] kernel exploit complete
    K-->>U: [+] elfldr is listening on port 9021

ARW ready sta per arbitrary read/write e indica che lo stadio browser è riuscito. Secondo il README, questo primo stadio può richiedere più tentativi: se il browser si blocca, basta sempre ricaricare la pagina.

Stato attuale: patch e firmware successivi

DIAGRAMMAmermaid
flowchart LR
    A["7.00 → 13.60<br/>Relapse funziona"] --> B["14.00<br/>Sony chiude la falla"]
    B --> C["14.00 / 14.10<br/>Prolapse: annunciato,<br/>codice non pubblico"]
    C --> D["14.20<br/>secondo i report,<br/>chiusa anche questa"]

    classDef ok stroke:#3fa66b,stroke-width:2px
    classDef chiuso stroke:#e0607e,stroke-width:2px
    classDef incerto stroke:#e0a030,stroke-width:2px,stroke-dasharray:5 4
    class A ok
    class B,D chiuso
    class C incerto

Quando Relapse è uscito, Sony aveva già distribuito da alcune settimane il firmware 14.00, che chiude la vulnerabilità. Sulla PS5 non esiste un downgrade ufficiale, quindi chi ha aggiornato non può tornare a una versione vulnerabile.

Ne nasce un compromesso evidente: restare al 13.60 permette di usare Relapse, ma i giochi più recenti chiedono già il 14.00. Per i firmware 14.00 e 14.10 è stato annunciato Prolapse, il cui codice però non è pubblico. Secondo i report, Sony avrebbe chiuso anche quella vulnerabilità con il firmware 14.20, distribuito a fine ottobre.

Conclusioni

Relapse non si basa su un'unica vulnerabilità ma su una catena di problemi distinti: un errore nella clonazione degli oggetti di JavaScriptCore, alcune fughe di informazioni e una race condition nel kernel. Presi uno per uno sono difetti isolati. Messi insieme, permettono di superare tutte le protezioni di una console progettata per restare chiusa.

È un ottimo esempio di come si studia la sicurezza di un dispositivo. Ogni stadio della catena corrisponde a una difesa che ha ceduto, e rafforzarne anche una sola sarebbe bastato a interrompere tutto.

Alla fine è proprio come si dice nel nostro ambiente: la catena è forte quanto il suo anello più debole.

Alla prossima.


Fonti

Scritto da Giovanni Rapa · 29.10.2026#exploit#kernel#ps5

Ti è servito, o ho sbagliato qualcosa?

Scrivimi: leggo tutto e, se serve, correggo l'articolo.

Rispondi via email

Commenti

via GitHub Discussions · giscus

I commenti stanno su GitHub Discussions. Caricandoli, il tuo browser si collega a giscus.app e a GitHub.

Serve un account GitHub. Preferisci di no? Rispondimi via email.