Home Homebrew Prosper Together: exploit userland per PS4 e PS5 con codice arbitrario via...

Prosper Together: exploit userland per PS4 e PS5 con codice arbitrario via salvataggi e LuaJIT in Don’t Starve Together

431
0

Il developer earthonion ci invita a scoprire un nuovo exploit userland chiamato Prosper Together, progettato per funzionare su PS4 e PS5 sfruttando la versione Console Edition di Don’t Starve Together.

Questo progetto si distingue nella scena homebrew perché utilizza un approccio accessibile e ingegnoso: invece di basarsi su vulnerabilità di sistema complesse, sfrutta il sistema di salvataggi del gioco per ottenere l’esecuzione di codice arbitrario direttamente in ambiente userland.

Il cuore dell’exploit risiede nella modifica dei file di salvataggio attraverso uno script Python, che consente di iniettare codice Lua appositamente costruito all’interno dei metadati del gioco.

Una volta importato il salvataggio sulla console e avviata una partita, il codice viene eseguito e sfrutta alcune caratteristiche di LuaJIT per uscire dal contesto sandbox previsto dagli sviluppatori, accedendo così all’ambiente globale del gioco.

Da qui si apre una fase molto più avanzata, in cui vengono costruite primitive di memoria come addrof e fakeobj. Queste tecniche permettono di leggere e scrivere direttamente nella memoria della console, offrendo un controllo significativo sul comportamento del sistema.

Grazie a questo livello di accesso, è possibile creare catene ROP e invocare syscall del sistema FreeBSD, trasformando di fatto il gioco in un punto di ingresso per operazioni ben più profonde rispetto a quelle normalmente consentite.

Un ulteriore elemento interessante è la componente di rete: una volta attivo, l’exploit apre una connessione TCP sulla porta 9066, consentendo l’invio di payload Lua in tempo reale da un PC collegato alla stessa rete locale.

Questo rende possibile eseguire comandi remoti, avviare servizi come un server FTP, oppure sviluppare strumenti di debug e automazione direttamente sulla console.

Dal punto di vista pratico, l’utilizzo richiede pochi elementi essenziali: una copia compatibile del gioco, Python per generare il salvataggio modificato, una connessione di rete locale e un salvataggio di partenza da alterare.

È fondamentale sottolineare che Prosper Together non rappresenta un jailbreak completo, poiché non fornisce accesso al kernel. Tuttavia, proprio per questo, si rivela uno strumento estremamente utile per lo sviluppo e il testing in ambiente userland.

Come funziona

  1. Iniezione del salvataggiomake_save.py inietta codice Lua nei file .meta di un salvataggio di DST
  2. Escape dalla sandbox – Il payload è racchiuso in loadstring([[...]])() che evade RunInSandbox di DST nell’ambiente globale _G.
  3. Primitive – La manipolazione del bytecode di LuaJIT fornisce fakeobj/addrof, che avviano lettura/scrittura arbitraria, poi call e syscall basate su ROP.
  4. TCP loader – Un server socket sulla porta 9066 accetta ed esegue script Lua a runtime

Avvio rapido

Costruire il salvataggio

bash

cd save-gen
python3 make_save.py
# Output: build_dst/savedata.dat

Andare a hostare una partita

Inviare payload a runtime

Una volta eseguito il gioco con il salvataggio iniettato, il TCP loader resterà in ascolto sulla porta 9066. Inviare qualsiasi script Lua: cat payloads/ftp_server.lua | nc -q 0 <ps5_ip> 9066

Oppure eseguire one-liner: echo 'return eboot_base' | nc -q 0 <ps5_ip> 9066

Riferimento API

Tutte le primitive sono esportate come globali. Inviare script tramite il TCP loader sulla porta 9066.

Lettura memoria

read_bytes_abs(addr, count)table o nil

Legge byte grezzi da un indirizzo assoluto. Restituisce una tabella di valori byte {b1, b2, ...} oppure nil in caso di errore.

bash

local bytes = read_bytes_abs(0x19838000, 4)
-- bytes = {127, 69, 76, 70} (ELF magic)

read_u32_abs(addr)number o nil

Legge un intero unsigned a 32 bit (little-endian).

bash

local magic = read_u32_abs(eboot_base)
-- magic = 0x464C457F (ELF magic)

read_u64_abs(addr)lo, hi o nil, nil

Legge un valore a 64 bit come due metà da 32 bit.

bash

local lo, hi = read_u64_abs(eboot_base)
-- lo = 32 bit bassi, hi = 32 bit alti
-- valore completo = hi * 0x100000000 + lo

hexread(addr, count)string

Dump esadecimale della memoria. Restituisce byte hex separati da spazio oppure "ERR".

bash

local dump = hexread(eboot_base, 16)
-- "7f 45 4c 46 02 01 01 09 00 00 00 00 00 00 00 00"

Scrittura memoria

write_double(addr, value)boolean

Scrive un double IEEE 754 da 8 byte a un indirizzo. Il valore deve essere un numero Lua. Restituisce true in caso di successo.

bash

write_double(some_addr, 0) -- scrive 8 byte a zero (0.0 come double)
write_double(some_addr, 1.5) -- scrive il double 1.5

Nota: Questo scrive un float a doppia precisione, non un intero grezzo. Per scrivere pattern di byte raw, usare fakeobj per creare un valore con la rappresentazione di bit desiderata, poi passalo a write_double.

get_str_addr(s)number

Ottiene l’indirizzo di memoria dell’oggetto GCstr di una stringa Lua. Il contenuto reale della stringa inizia all’offset +24.

bash

local buf = string.rep("\0", 256)
local buf_addr = get_str_addr(buf) + 24 -- puntatore a 256 byte zero

Questo è il metodo principale per allocare buffer di memoria per le syscall:

bash

-- Alloca un buffer e ottiene il suo indirizzo
local my_buf = string.rep("\0", 4096)
local my_buf_ptr = get_str_addr(my_buf) + 24

-- Usa come argomento syscall
syscall(3, fd, my_buf_ptr, 4096) -- read(fd, buf, 4096)

Manipolazione oggetti

addrof(obj)number o nil

Ottiene l’indirizzo di memoria di un oggetto Lua (table, function, userdata, ecc..).

bash

local addr = addrof(TheNet)
-- addr = 0x12345678

fakeobj(hi32, addr)value o nil

Crea un valore Lua falso da componenti raw di puntatore taggato. hi32 sono i 32 bit alti (type tag), addr sono i 32 bit bassi (puntatore).

bash

-- Crea un oggetto stringa falso all’indirizzo 0xDEADBEEF
local fake_str = fakeobj(0xFFFD8000, 0xDEADBEEF)

-- Crea un oggetto table falso
local fake_tab = fakeobj(0xFFFA0000, some_addr)

Tag comuni:

TagTipo
0xFFFD8000string
0xFFFA0000table
0lightuserdata / numero (bit bassi)

Chiamare funzioni

call(func_lo, func_hi, a1, a2, a3, a4, a5, a6)ret_lo, ret_hi

Chiama una funzione arbitraria a un indirizzo assoluto. Fino a 6 argomenti (rdi, rsi, rdx, rcx, r8, r9). Argomenti e valore di ritorno sono divisi in coppie 32 bit lo/hi. Passa argomenti a 64 bit come tabelle {lo, hi}.

bash

-- Chiama una funzione a un indirizzo noto
local ret_lo, ret_hi = call(func_lo, func_hi, arg1, arg2)

-- Esempio: call memcpy(dst, src, len)
local memcpy_lo = libc_base_lo + 0x32600
local memcpy_hi = libc_base_hi
call(memcpy_lo, memcpy_hi, dst_addr, src_addr, length)

-- Esempio: call con 6 argomenti
local ret_lo, ret_hi = call(func_lo, func_hi, a1, a2, a3, a4, a5, a6)

Syscall

syscall(num, a1, a2, a3, a4, a5, a6)ret_lo, ret_hi

Esegue una syscall FreeBSD con fino a 6 argomenti. Restituisce il risultato grezzo; ret_lo >= 0x80000000 indica errore (ritornato -1 dal kernel).

bash

-- getpid (syscall 20)
local pid = syscall(20)

-- open (syscall 5)
local path = "/dev/notification0\0"
local path_addr = get_str_addr(path) + 24
local fd = syscall(5, path_addr, 0, 0) -- open(path, O_RDONLY, 0)

-- read (syscall 3)
local buf = string.rep("\0", 256)
local buf_addr = get_str_addr(buf) + 24
local n = syscall(3, fd, buf_addr, 256)

-- write (syscall 4)
local msg = "hello\n"
local msg_addr = get_str_addr(msg) + 24
syscall(4, fd, msg_addr, #msg)

-- close (syscall 6)
syscall(6, fd)

-- socket (syscall 97)
local sock = syscall(97, 2, 1, 0) -- AF_INET, SOCK_STREAM, 0

-- setsockopt (5 argomenti)
local enable = "\x01\x00\x00\x00\x00\x00\x00\x00"
local enable_ptr = get_str_addr(enable) + 24
syscall(105, sock_fd, 0xFFFF, 4, enable_ptr, 4)

-- mmap (6 argomenti)
local addr_lo, addr_hi = syscall(477, 0, 0x4000, 3, 0x1022, -1, 0)

Numeri syscall comuni:

NumeroNome
3read
4write
5open
6close
20getpid
30accept
37kill
97socket
104bind
105setsockopt
106listen
188stat
209poll
272getdents
477mmap

Controllo errori

Gli errori syscall restituiscono -1 (lo stub libkernel gestisce il carry flag). In pratica, controlla ret_lo >= 0x80000000:

bash

local fd = syscall(5, path_addr, 0, 0)
if not fd or fd >= 0x80000000 then
-- errore
end

Notifiche

notify(message)

Invia una notifica popup di sistema PS5.

notify("Hello from Lua!")

Logging

rlog(tag, message)

Log nella barra annunci in-game e nella socket TCP (se connessa).

bash

rlog("info", "qualcosa è successo")
-- mostra: [info] qualcosa è successo

prosper_log(message)

Log nella console a schermo di Prosper Together.

prosper_log("FTP server avviato sulla porta 1337")

Globali

Globale   TipoDescrizione
eboot_baseNumeroIndirizzo base dell’eseguibile del gioco
regionStringa"US" o "EU"
libc_base_loNumeroIndirizzo base libc (32 bit bassi)
libc_base_hiNumeroIndirizzo base libc (32 bit alti)
libkernel_base_loNumeroBase libkernel (32 bit bassi)
libkernel_base_hiNumeroBase libkernel (32 bit alti)
syscall_loNumeroIndirizzo stub syscall (32 bit bassi)
syscall_hiNumeroIndirizzo stub syscall (32 bit alti)
my_ipStringaIndirizzo IP PS5 rilevato (es. "192.168.0.101")

Utility

Shutdown()

Termina il processo del gioco (chiama kill(getpid(), SIGKILL)).

Shutdown()

Payload

FTP Server (payloads/ftp_server.lua)

Server FTP completo sulla porta 1337. Eseguire la connessione con qualsiasi client FTP (FileZilla, lftp, ecc..).

cat payloads/ftp_server.lua | nc -q 0 <ps5_ip> 9066

Configurazione FileZilla: Gestione siti > Generale > Crittografia: “Usa solo FTP semplice (non sicuro)”

Supporta: USER, PASS, PWD, CWD, CDUP, LIST, RETR, STOR, PASV, PORT, TYPE, SIZE, DELE, MKD, RMD, RNFR, RNTO, REST, SITE CHMOD, FEAT, SYST, QUIT

Esempio completo

bash

-- Leggi header ELF
local magic = hexread(eboot_base, 16)
prosper_log("ELF: " .. magic)

-- Ottieni PID
local pid = syscall(20)
prosper_log("PID: " .. tostring(pid))

-- Leggi un file
local path = "/savedata0/savedata.dat\0"
local path_ptr = get_str_addr(path) + 24
local fd = syscall(5, path_ptr, 0, 0)
if fd and fd < 0x80000000 then
local buf = string.rep("\0", 128)
local buf_ptr = get_str_addr(buf) + 24
local n = syscall(3, fd, buf_ptr, 128)
prosper_log("Letti " .. tostring(n) .. " byte")
prosper_log(hexread(buf_ptr, 32))
syscall(6, fd)
end

-- Invia una notifica
notify("Exploit in esecuzione! PID=" .. tostring(pid))

Download: savedata.zip

Download: Source code Prosper Together

Alcune parti di questo articolo sono state generate con l’aiuto dell’intelligenza artificiale.

🔥 Prodotti in promozione e articoli più venduti: PS4

Vedi altri prodotti PS4

Questo articolo contiene link affiliati a Amazon. Se acquisti tramite questi link, potrei guadagnare una commissione senza costi aggiuntivi per te.

Ultimo aggiornamento 2026-05-13 / Link di affiliazione / Immagini da Amazon Product Advertising API

LASCIA UN COMMENTO

Per favore inserisci il tuo commento!
Per favore inserisci il tuo nome qui
Captcha verification failed!
Punteggio utente captcha non riuscito. Ci contatti per favore!

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.