HackIt, SolveIt and SmashCTF (III) – HTML5 DRM – Conflicto ideológico


DRM y HTML5. EME (Encrypted Media Extensions). Hay que empaparse algo sobre estos temas para resolver el nivel. EME ofrece un API que permite a las aplicaciones web interactuar con sistemas de protección de contenido para poder reproducir audio o video cifrado. El famoso DRM en HTML5, algo que muchos consideran una aberración (la web nació para ser abierta, no para ofrecer contenidos cerrados). Pero… ahí está el API. Y es precisamente lo que hay que intentar resolver. Básicamente el cliente tiene una etiqueta video. Al pulsar el play se visualizan 26 segundos. Pero a partir de ahí, todo está negro. Parece que el video webm está protegido. En el código vemos que en un momento dado se hace una petición de licencia a un servidor license, que nos envía la clave para desproteger el webm.

Pero esa petición sólo se puede hacer si rellenamos los bytes que faltan… esos bytes forman parte de la solución al sudoku que nos han puesto debajo del vídeo. ¿Qué hacer cuando tengamos la clave de desprotección del vídeo? Visualizarlo en el navegador 🙂 ¿Y después? Bueno, eso lo veremos enseguida… Vayamos por partes. Lo primero es solucionar el sudoku. Lo siguiente es automatizar el proceso de meter los números en las casillas del sudoku (hacerlo a mano es un infierno).
Solucionar el sudoku es fácil. Entramos en sudoku-solutions.com, metemos los datos y pulsamos en check…
Vaya, tiene 9 soluciones posibles. No podía ser tan fácil …

Para no perder tiempo tecleando cada una de ellas, podemos automatizar el proceso. Abrimos la consola JavaScript y tecleamos:

var sudoku = $("#su input")
var s ="852931647473862159961547283318476925549328761726159834637294518194685372285713496"
for (var i = 0; i < sudoku.length; i++){ sudoku[i].value = s.charAt(i); }
$("video")[0].needs_reload = 1;

Por cierto, en ese código ya va la solución correcta 🙂 La última línea informa al navegador que el sudoku ha cambiado y debe leer sus datos. Bien, todo preparado. Pulsamos play y vemos que pasamos del segundo 26. Es un trailer de «Inception». Hay una serie de fotogramas que muestran pixels distorsionados. Seguramente porque se haya introducido por ahí algún string que no debería estar… Habrá que bajar el webm, descifrarlo y abrirlo más o menos por esa parte, para ver de qué string se trata.

¿Pero cómo obtenemos la clave de descodificación del webm? (el navegador la conoce, pero necesitamos aislarla…) ¿Por cierto, cuántas claves habrá? Vamos allá.

Abrimos main.js y metemos un punto de ruptura en la línea 71

     }).then(function(e) {
                var n = (e = new Uint8Array(e)).slice(0, 12);
                return window.crypto.subtle.decrypt({
                    name: "AES-GCM",
                    iv: n,
                    tagLength: 128
                }, r, e.slice(12))
            }).then(function(e) {
breakpoint --->                return t.target.update(e)
            })

En e tendremos la clave. Ojo, veremos que el breakpoint se ejecuta dos veces, y necesitaremos apuntar ambas claves (una es para cifrar el vídeo y otra para cifrar el audio). Creo recordar que no era «tan sencillo», sino que había que convertir el formato de las claves obtenidas en «e» con una línea como

atob(String.fromCharCode.apply(null, new Uint8Array(e)))

y a continuación extraer las claves de 16 bytes con un script como el siguiente (una de las claves era w-UHS…):

var b64string = "w-UHS56ogAQacZLNj1TpqA" ;
var buf = Buffer.from(b64string, 'base64');
var fs = require('fs');
fs.writeFile("key.txt", buf,  "binary",function(err) {
    if(err) {
        console.log(err);
    } else {
        console.log("The file was saved!");
    }
});

Momento de descargar el vídeo (cifrado) y descifrar. ¿Cómo desciframos? Bien, sabemos la clave y tenemos el vídeo cifrado. Nos falta saber cómo se cifró. Investigando un poco, nos encontramos con la utilidad webm_crypt de las webm-tools. Tiene una dependencia con libwebm, pero siguiendo las instrucciones de compilación del anterior enlace, lo podremos obtener sin problemas (en Linux, en macOS no iba).

Desciframos con :

$ webm_crypt -i input.webm -o decrypted.webm -decrypt -audio_options base_file=clave1 -video_options base_file=clave2

Y por fin, podremos abrir el fichero decrypted.webm (por ejemplo, con vlc…)

o con strings (!)

$ strings -n 10 decrypted.webm

(Nota: -n 10 = dame los strings ASCII de decrypted.webm que puedas visualizar, siempre y cuando esos strings sean de longitud mayor o igual a 10)

Y analizando la salida de strings, veremos la clave para el siguiente nivel 🙂

PD: creo que hay una herramienta que te permite pasar como input un vídeo webm y una marca de tiempo (hh:mm:ss) y te da como salida el contenido del fotograma de esa marca de tiempo. Lo cual te evitaría el uso de strings (o lo facilitaría). Pero eso lo dejo para que la gente de W0pr, navarparty o Barcelona92 nos lo cuenten en los comentarios.

Un comentario en «HackIt, SolveIt and SmashCTF (III) – HTML5 DRM – Conflicto ideológico»

  1. Efectivamente, nosotros utilizamos mpv para sacar la marca de tiempo exacta en la que vimos el fotograma corrupto, en 2:18:527 si no recuerdo mal, luego usamos webm_info (con -a) para obtener el offset y tamaño del fotograma corrupto, luego lo extrajimos con dd y al pasar strings a ese archivo ya teníamos la clave 😉

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.