https://geohot.com/e7writeup.html
https://archive.is/https://geohot.com/e7writeup.html
http://web.archive.org/web/20260000000000*/https://geohot.com/e7writeup.html
https://archive.is/https://geohot.com/old_index.html
https://web.archive.org/web/20110115203720/https://geohot.com/old_index.html
https://web.archive.org/*/https://geohot.com/old_index.html
Sí, esto que estás viendo es literalmente el núcleo del hackeo de la PS3, y es mucho más elegante (y grave) que el de iOS.
Lo que publicó George Hotz ahí no es un writeup largo: 👉 es directamente material criptográfico + claves derivadas.
Y eso cambia completamente el juego.
La seguridad de la PlayStation 3 se basaba en:
👉 firmas digitales ECDSA
Sony firmaba:
SELF)Y la consola solo ejecuta cosas si:
firma válida con la clave privada de Sony
Ese bloque:
erk: ...
riv: ...
pub: ...
R: ...
n: ...
K: ...
Da: ...
no es random. Es:
👉 componentes del sistema criptográfico interno
Esto viene de un fallo descubierto por fail0verflow:
👉 Sony implementó mal ECDSA
En ECDSA:
$$r = (k \cdot G)_x \bmod n$$
$$s = k^{-1}(H(m) + d_A \cdot r) \bmod n$$
Donde:
👉 Reutilizaron el mismo $k$
Si $k$ se repite:
$$d_A = (s \cdot k - H(m)) \cdot r^{-1} \bmod n$$
👉 Se puede recuperar la clave privada
Publicó:
👉 la clave privada de Sony (o suficiente info para derivarla)
En tu dump:
K → el nonce reutilizadoDa → la clave privada derivadapub → clave públicaR, n → parámetros ECDSAA diferencia del jailbreak de iPhone:
👉 Esto NO es un exploit que Sony pueda parchear fácilmente
Porque:
la raíz de confianza está rota
Ahora cualquiera puede:
Con esas claves podés:
Generar .self válidos
.pkg que la PS3 acepta como legítimos
Como menciona:
👉 PSL1GHT
Sin depender de bugs runtime
| iPhone jailbreak | PS3 hack |
|---|---|
| cadena de exploits | fallo criptográfico |
| parcheable | no parcheable |
| acceso temporal | control total permanente |
| depende de bugs | rompe la raíz de confianza |
Por eso ves que Hotz dice:
“i do not condone piracy”
Porque esto:
👉 habilita piratería masiva automáticamente
(No porque ese fuera el objetivo técnico, pero es la consecuencia directa)
Cuando dice:
props to fail0verflow for the asymmetric half
Se refiere a:
👉 No explotaron la PS3… rompieron su sistema criptográfico
Es como si en vez de abrir una puerta con ganzúas:
👉 consiguieran la llave maestra oficial
Si querés, puedo ir más profundo en:
d_A paso a paso.SELF internamenteAhí ya entramos en crypto + reversing pesado, que es donde esto se vuelve realmente interesante.
Perfecto, entonces vamos a meternos en lo realmente jugoso: cómo se recupera la clave privada paso a paso a partir del error de Sony. Esto es crypto aplicado de verdad.
En ECDSA, para firmar un mensaje $m$:
$$r = (kG)_x \bmod n$$
$$s = k^{-1}(H(m) + d_A r) \bmod n$$
Donde:
Sony hizo algo imperdonable:
👉 reutilizó el mismo $k$ para múltiples firmas
Eso significa:
k₁ = k₂
Para dos mensajes distintos $m_1$, $m_2$:
$$s_1 = k^{-1}(H(m_1) + d_A r)$$
$$s_2 = k^{-1}(H(m_2) + d_A r)$$
Restás ambas ecuaciones:
$$s_1 - s_2 = k^{-1}(H(m_1) - H(m_2))$$
Reordenando:
$$k = \frac{H(m_1) - H(m_2)}{s_1 - s_2} \bmod n$$
👉 Ya tenés el nonce
Volvés a la ecuación original:
$$s = k^{-1}(H(m) + d_A r)$$
Despejando:
$$d_A = (sk - H(m)) \cdot r^{-1} \bmod n$$
👉 Tenés $d_A$: la clave privada de Sony
En el dump de George Hotz:
K → el nonce recuperadoDa → la clave privada finalR, n → parámetros de la curvapub → clave pública (verificación)O sea:
👉 él ya hizo estas cuentas y publicó el resultado
Porque rompe una propiedad fundamental:
la seguridad de ECDSA depende completamente de que k sea único y secreto
Si:
La PlayStation 3 verificaba:
firma válida → ejecutar
Pero ahora cualquiera puede:
.SELF👉 sin exploits, sin bugs runtime
Esto no es reversing clásico de memoria o kernel.
Es:
👉 cryptographic failure exploitation
Y es peor que cualquier RCE porque:
no depende de una versión → rompe TODO el ecosistema
El error de Sony no fue “un bug” cualquiera:
👉 fue no entender que $k$ debe ser:
Hoy en día esto se evita con:
👉 fail0verflow encontró el fallo matemático, 👉 George Hotz lo llevó a algo usable publicando las claves.
Si querés, en el siguiente nivel podemos ir a algo todavía más bajo:
.SELFAhí ya estamos literalmente en territorio de implementación real.
Perfecto, entonces bajamos a implementación real: cómo es un .SELF de PS3 y cómo encaja la firma ECDSA en el binario. Esto ya es reversing + formato propietario.
.SELF?En la PlayStation 3:
👉 .SELF = Signed ELF
Es básicamente:
ELF + metadatos + cifrado + firma
No es solo un ELF plano:
Simplificado:
[ SELF Header ]
[ App Info ]
[ ELF Header (cifrado o plano) ]
[ Program Headers ]
[ Secciones ]
[ Metadata (keys, offsets) ]
[ Signature (ECDSA) ]
Al final del archivo hay:
R || S
👉 firma ECDSA sobre:
hash de ciertas secciones del SELF
No siempre es todo el archivo, sino regiones definidas en metadata.
Cuando la PS3 carga un .SELF:
ECDSA_verify(pub, hash, (r,s))
Si pasa:
→ ejecuta
Si no:
→ reject
Con la clave privada ($d_A$) que publicó George Hotz:
👉 podés generar $(r, s)$ válidos
Entonces:
tu binario == binario "oficial" para la PS3
Esto es lo que habilitó todo el ecosistema homebrew.
powerpc64-ps3-gcc main.c -o app.elf
Herramientas como:
make_self_npdrm👉 agregan headers + metadata
Acá entra ECDSA:
$$s = k^{-1}(H(m) + d_A r)$$
👉 ahora sí podés hacerlo porque tenés d_A
ELF + metadata + signature → .SELF
Con tools tipo:
package_finalizegenerás .pkg instalable
Sony usaba:
👉 NPDRM (Network Platform DRM)
Para:
Pero:
👉 también usa firmas
Entonces al poder firmar:
bypass completo del DRM
Convertía:
ELF → SELF válido para PS3
Incluyendo:
En PS3:
Pero:
👉 la seguridad real NO estaba en el loader 👉 estaba en la firma
Y eso fue lo que se rompió
Esto es importante entenderlo bien:
👉 no necesitaban explotar kernel 👉 no necesitaban ROP 👉 no necesitaban bugs
Solo:
firmar binarios correctamente
Aunque Sony actualice firmware:
👉 cualquier firmware nuevo sigue confiando en esa raíz
iOS:
PS3:
👉 Un .SELF es solo un ELF con una firma…
👉 y cuando tenés la clave, podés hacer que cualquier cosa parezca oficial.
Si querés ir todavía más bajo nivel, podemos:
.SELF byte por byteAhí ya estamos literalmente en territorio de toolchain de exploits.
Perfecto, bajamos un nivel más: cómo se ve un .SELF real a nivel de bytes y dónde entra la firma. Esto ya es parsing + reversing puro.
Un .SELF de PlayStation 3 empieza así:
00000000 53 43 45 00 → "SCE\0"
👉 Magic de Sony (no ELF todavía)
Después vienen headers propietarios y recién más adelante aparece el ELF.
struct SELF_HEADER {
uint32_t magic; // "SCE\0"
uint32_t version;
uint16_t flags;
uint16_t type;
uint64_t metadata_off;
uint64_t header_len;
uint64_t elf_filesize;
uint64_t self_filesize;
};
👉 metadata_off es clave: apunta a la zona donde está TODO lo sensible.
En esa zona tenés:
- offsets de segmentos
- keys (a veces cifradas)
- hashes por sección
- info de control
- firma ECDSA
Eventualmente encontrás algo así:
[ ... metadata ... ]
[ signature block ]
La firma:
R (20 bytes) || S (20 bytes)
👉 PS3 usa curvas de 160 bits (tipo secp160r1)
No es todo el archivo.
Se firma:
hash(metadata + segmentos definidos)
Más específicamente:
👉 eso evita modificar partes sin invalidar firma
Algo así:
hash = SHA1(selected_segments);
if (!ECDSA_verify(pubkey, hash, signature)) {
reject();
}
load_elf();
execute();
Con la clave privada filtrada por George Hotz:
👉 reemplazás:
signature = ECDSA_sign(hash, d_A)
Y listo.
Supongamos que abrís un .SELF en un hex editor:
00000000 53 43 45 00
...
00000100 ELF header (7F 45 4C 46)
...
0000F000 metadata
0000F200 hashes
0000F300 signature (R || S)
Gente como fail0verflow hizo:
👉 classic reversing flow
PS3 usa:
SHA1 + ECDSA
Hoy suena débil, pero:
👉 el problema NO fue SHA1 👉 fue el nonce $k$
Pseudo implementación:
hash = sha1(data)
k = random()
r = (k * G).x % n
s = inverse(k) * (hash + dA * r) % n
signature = (r, s)
👉 Esto antes era imposible (no tenías dA)
👉 después del leak → trivial
Lo importante no fue solo encontrar la clave.
Fue:
👉 entender exactamente qué bytes firmar
Porque si firmás mal:
→ firma válida matemáticamente
→ pero inválida para el loader
Comparado con exploits:
👉 solo:
entender formato + crypto
Podés crear:
tu ELF → tu SELF → tu PKG
Y la PS3 dice:
"esto lo firmó Sony"
👉 Reversearon el loader, entendieron el formato, rompieron la firma… y eso fue suficiente para controlar toda la consola.
Si querés ir al último nivel (muy interesante):
k era constante en firmas realesAhí ya es reversing criptográfico puro, que fue exactamente lo que hizo famoso este hack.
Bien, ahora sí entramos en lo más interesante: cómo se dieron cuenta de que Sony reutilizaba $k$. Esto ya es reversing + análisis criptográfico real, no teoría.
Los tipos de fail0verflow no tenían la clave privada.
Lo que sí tenían:
.SELF oficialesDe cada binario firmado sacaron $(r, s)$ y el hash del mensaje $H(m)$.
👉 esto es trivial una vez entendés el formato .SELF
Al comparar múltiples firmas:
👉 notaron que $r$ era siempre igual
Recordá:
$$r = (kG)_x \bmod n$$
👉 Si $r$ es igual → $k$ es igual
Tomaron dos firmas: $(r, s_1)$ sobre $m_1$ y $(r, s_2)$ sobre $m_2$.
Mismo $r$, distinto $s$. Eso implica:
mismo k reutilizado
Aplicaron:
$$k = \frac{H(m_1) - H(m_2)}{s_1 - s_2} \bmod n$$
Y luego:
$$d_A = (s k - H(m)) \cdot r^{-1} \bmod n$$
No alcanza con calcular $d_A$.
Tenés que validar:
pubkey == d_A * G
Y además:
ECDSA_verify(pub, hash, firma) → OK
👉 si eso pasa → tenés la clave real
En PS3 pasó algo todavía más brutal: en algunos casos $k$ era constante. Ni siquiera “reutilizado accidentalmente”.
Era literalmente:
k = constante
Eso implica que Sony hacía algo como:
k = fixed_value;
o un RNG roto tipo:
k = hash(seed fijo)
Dos caminos:
A) Black-box (lo que hicieron primero)
B) White-box (reversing del firmware)
Buscás la función ECDSA y la generación de $k$, y encontrás algo tipo:
k = something_not_random();
Buscan en el binario operaciones modulares, multiplicaciones en curva elíptica y uso de SHA1. Luego siguen el flujo hasta ver cómo se genera $k$.
Sony probablemente:
Este bug es más común de lo que parece. Casos reales: Bitcoin wallets rotas, librerías TLS viejas, smartcards.
👉 siempre el mismo problema: $k$
Con esa info:
👉 convirtió un fallo teórico en algo práctico
👉 No “hackearon” la PS3… 👉 observaron firmas, detectaron un patrón, y rompieron la criptografía
Esto es clave para entender seguridad real:
los sistemas más seguros del mundo caen por errores pequeños en implementación
No fue buffer overflow ni kernel bug. Fue:
un mal uso de un número aleatorio
Si querés, podemos hacer algo todavía más práctico:
👉 simular este ataque con números reales (mini ECDSA) 👉 o mostrar cómo escribir un script que recupere $d_A$ a partir de dos firmas
Eso ya es 100% aplicable y te deja con la idea completamente internalizada.
Buenísimo, vamos a hacerlo práctico de verdad, pero con números chicos para que veas todo el flujo sin perderte en aritmética gigante.
Elegimos parámetros simples:
n = 97 (orden del grupo, primo chico)
Supongamos que Sony tiene:
d_A = 45 (clave privada secreta)
Y (error fatal):
k = 13 (MISMO nonce reutilizado)
Para dos mensajes distintos:
H(m1) = 10
H(m2) = 20
Recordá la ecuación:
$$s = k^{-1}(H(m) + d_A r) \bmod n$$
Para simplificar, fijamos (como si viniera de la curva):
r = 30
Queremos el inverso de 13 mod 97:
13 * 15 = 195 ≡ 1 mod 97
👉 entonces $k^{-1} = 15$
s1 = 15 * (10 + 45*30) mod 97
= 15 * 2 = 30 mod 97
👉 firma 1: $(r, s_1) = (30, 30)$
s2 = 15 * (20 + 1350) mod 97
= 15 * 12 = 180 mod 97 = 83
👉 firma 2: $(r, s_2) = (30, 83)$
El atacante ve: $r = 30$ (igual en ambas firmas), $s_1 = 30$, $s_2 = 83$, $H(m_1) = 10$, $H(m_2) = 20$.
$$k = \frac{H(m_1) - H(m_2)}{s_1 - s_2} \bmod n$$
(10 - 20) = -10 ≡ 87 mod 97
(30 - 83) = -53 ≡ 44 mod 97
44⁻¹ mod 97 = 11 (porque 44 * 11 = 484 ≡ 1)
k = 87 * 11 = 957 mod 97 = 13
💥 Recuperamos: $k = 13$
$$d_A = (sk - H(m)) \cdot r^{-1} \bmod n$$
s*k = 30*13 = 390
390 - 10 = 380 mod 97 = 89
30⁻¹ mod 97 = 55 (porque 30*55 = 1650 ≡ 1)
d_A = 89 * 55 = 4895 mod 97 = 45
💀 Resultado: $d_A = 45$
Acabás de replicar exactamente el ataque que rompió la PlayStation 3. Sin acceso al sistema, sin reversing profundo, sin exploits. Solo con firmas observadas + matemáticas.
Esto escala igual con números reales: en PS3 usan curvas de 160 bits, hashes SHA1 reales, firmas reales. Pero el ataque es idéntico.
Si ves el mismo $r$ en dos firmas ECDSA:
👉 automáticamente sabés que la clave privada está comprometida.
👉 Reutilizar $k$ en ECDSA no es un bug menor… 👉 es equivalente a publicar la clave privada.
Si querés, el siguiente paso interesante sería:
Ahí ya estás haciendo lo mismo que fail0verflow en su momento.
Perfecto, entonces vamos más directo al hueso técnico, sin simplificar de más.
Esto que hizo George Hotz es básicamente reconstruir toda la cadena de explotación de evasi0n7 mirando el binario, y lo interesante es que la cadena está muy bien pensada: no depende de un solo bug, sino de composición de primitivas.
Te lo estructuro como cadena de explotación real:
Tenés ejecución desde el host (PC) vía AFC:
mobile/var/mobile/Media👉 Estado inicial:
uid=mobile
sandboxed
filesystem limitado
Descargan una IPA firmada por Apple (WWDC app).
La clave:
Entonces hacen:
👉 path traversal en Info.plist
ExecutableFile = "../../../../../../var/mobile/Media/Downloads/WWDC.app/WWDC"
👉 Resultado:
code signing bypass lógico (no criptográfico)
El binario que ejecuta no es una app real:
#!/usr/libexec/afcd -S -d / -p 8888
👉 Esto lanza afcd:
-d / → root filesystem-S → acceso extendidoPero sigue sandboxed.
Acá está una de las partes más finas:
Inyectan:
DYLD_INSERT_LIBRARIES = gameover.dylib
Pero:
afcd inicializa sandbox dinámicamentelibsystem_sandbox👉 Resultado:
sandbox_init nunca se ejecuta
O sea: 👉 proceso sin sandbox pero con permisos de mobile
Este bug es MUY bueno:
Si S_ATTR_LOC_RELOC está seteado
El sistema:
👉 TOCTOU en code signing
Resultado:
unsigned dylib ejecutada
AFC evita salir de /var/mobile/Media.
Bypass:
symlink("../../../../../tmp", "Downloads/a/a/a/a/a/link")
rename(..., "tmp")
👉 Como el symlink es relativo:
termina apuntando a /tmp
Resultado:
write primitivo fuera del sandbox FS
Modifican:
/var/mobile/Library/Caches/com.apple.mobile.installation.plist
Agregan:
DYLD_INSERT_LIBRARIES
👉 Esto asegura que la dylib se cargue siempre que corra la app
Este es otro exploit clave:
installd extrae zips en /tmp/install_staging.*Race:
/var/mobile/Library/...👉 Resultado:
write arbitrario como root (indirecto)
Usan:
/dev/rdisk0s1s1
Primero hacen:
symlink → rdisk
CrashHousekeeping → chown
👉 bug:
chown sigue symlinks
Resultado:
rdisk → mobile:mobile
Porque ahora:
👉 mobile puede escribir el disco crudo
No necesitan:
Escriben directo en rootfs:
/usr/lib/libmis.dylib👉 objetivo:
bypass permanente de code signing (amfid)
No usan DYLD_INSERT_LIBRARIES en amfid porque:
__RESTRICT segment
Entonces: 👉 reemplazan la librería directamente en disco
Crean:
com.evad3rs.evasi0n7.untether.plist
👉 launchd lo ejecuta en boot
Corre:
/evasi0n7
Que:
Cadena completa:
AFC (sandboxed)
→ code signing confusion (plist path)
→ ejecución controlada
→ dylib injection (TOCTOU bug)
→ sandbox escape
→ filesystem escape (symlink)
→ write primitivo
→ installd race → write como root
→ chown symlink → acceso a block device
→ patch rootfs offline
→ bypass amfid
→ launchd persistence
→ kernel exploit final
Esto es lo que Hotz realmente hizo:
Identificó cada primitiva:
Reconstruyó el orden correcto
Entendió dependencias entre exploits
👉 Eso es reversing de alto nivel: no solo leer código, sino reconstruir la estrategia
Si querés, podemos profundizar en uno específico:
S_ATTR_LOC_RELOC (muy fino)installdamfid sin romper el sistemaAhí es donde se pone realmente interesante.