Índice

0 - Introducción
1 - Análisis del Log TS
   1.1 - Análisis Inicial
   1.2 - Paquetes de Audio
   1.3 - ECMs
2 - Análisis del firmware de la tarjeta
   2.1 - Formato ECM
   2.2 - Procesado CW
   2.3 - Análisis Debilidades
3 - Descifrando el stream
   3.1 - Bruteforce
   3.2 - Crear EEprom equivalente
   3.3 - Descifrar Audio
   3.4 - Flag

0 - Introducción

Enlace al reto: 32C3 CTF 2015: carder-500

El reto nos dice que debemos descifrar un stream DVB-T para obtener la clave y nos da un stream TS y un firmware de una card, como pista nos muestra una foto de una tarjeta en la que pone "Fun4"

Para resolverlo yo he utilizado algunos programas propios que he ido haciendo a lo largo de los años y el lenguaje C# pero se puede realizar con otras herramientas y utilizando el lenguaje de programación que prefiramos

Además de conocimientos sobre ASM también será de bastante utilidad tener algunos conocimientos básicos sobre el estandar DVB y DVB-CA, recomiendo revisar alguna documentación sobre estos estandares:
https://en.wikipedia.org/wiki/MPEG_transport_stream
https://en.wikipedia.org/wiki/Conditional_access
https://en.wikipedia.org/wiki/Common_Scrambling_Algorithm
https://www.dvb.org/resources/public/standards/a38_dvb-si_specification.pdf

Vamos a ello...

1 - Análisis del Log TS

Lo primero vamos a analizar el stream DVB-T. Para ello yo he utilizado un programa propio, pero se pueden encontrar por internet varios con muchas opciones y más profesionales, por ejemplo el TSReader o el DVBSnoop..

1.1 - Análisis Inicial

Análisis Incial de Paquetes:

PIDDescripciónOtrosN° PaquetesN° Paquetes Iniciales
0000PAT 16741674
0010NIT 33473347
0011SDT 25102510
0012EIT 16741674
0014TDT 105105
0401PMTSID 0001 - rrr! (32C3 CTF)837837
0402AudioSID 0001 - rrr! (32C3 CTF)9779700
0403PMTSID 0002 - ffm! (32C3 CTF)837837
0404ECM (0B00)SID 0002 - ffm! (32C3 CTF)626626
0405AudioSID 0002 - ffm! (32C3 CTF)9779699
1FFFEmpty 8054610

Descripción:

En el estandar DVB se utiliza el algoritmo CSA para cifrar los datos de audio y/o video. Generalmente las claves a utilizar con el algoritmo CSA (las llamaremos CW) se actualizan cada pocos segundos y se envían cifradas dentro de las ECMs (salvo en sistemas como BISS en el que se utiliza una clave fija y no se envían ECMs).

Para el reto, nos centraremos en los PID 0404 y 0405 en los que tenemos el audio cifrado y las ECMs para poder descifrarlo.

1.2 - Paquetes de Audio (0405)

Siguiendo el estandar DVB, la cabecera de los paquetes TS nos da cierta información. Vemos paquetes cifrados con la CW par y otros con la CW impar. Algunos tienen Adaptation Field y otros no. Además podemos ver en algunos paquetes el flag de inicio de la trama PES.

Algunos Ejemplos de cabeceras:

Primer Paquete Inicial Cifrado con clave Impar

474405F0 075000007B0C7E00 751555EB48033A7D37E375CA57090AFD474970291C8A83153BA8B929F0400596052C312C9892E8A45048BD4A5712F3460DCFE9CC43C8981DAE5C11D40FF5ACC9263D90E247FE42987FB0C64A6D8060923D01FE5780654459AC02DD0537FD40729A0715983A449FC0E6601BD09FE67136D3F06AB88CD2D5F42D9F1572814E7A493EC48941F1E8E8CE94114EEC930F018DD0D4BA92481B6D0CCDA402BC5526810813C6A11FD033AE1EB61C2335F53DE22C (Header) (Adaptation Field) (Data)

Primer Paquete Inicial Cifrado con clave Par

474405B6 075000015529FE81 C4B8715DA78BA62721415BC995357AC21AEFB341B133B24A03CF1A0011F1D76DDA2687334478AC41EE9F38AE71814C9BD96E3BEA2812F47DC898C078FD6D5CE949EEDDA243A0853CD60C819FB923F2E619879DCD3F4B7CF9132E66C883E03E28020CF910C06ABFF42E76F980A1FF17026D6A77E44B19EDF73A19108ABAF01FF17863A5B9E2C81F1B171B1604FE80CB416F4A154FA4463D34AE1779D9BE0B6CA8A5CE5EF202EB7927CD68AC853D71C8E3 (Header) (Adaptation Field) (Data)

1.3 - ECMs (0404)

80B08AFFFFC30000 00 0C9B810F65AAF0A936640B97F4FCAA0A91D43EE512CF4481BB02E377CBC39FCF4651207D4628FE3046E8ACDBF8BFF0FE18E2AC81943E9CE5E8814198497A6B0252DC04A06DFDA77A4FBBFC7E9D3DAA8A23DFD06E529D200AF56DE63421BED5BEB74B80BE4AA3C8D7F0C98B525B2552AD341C431985709C60B4C704200DF0D08E 227EE00D 80B08AFFFFC30000 01 DB92F699EA651B4858ED4742BB2B56341CC3970FDC6F350DCCFC0AAAEC290E8E544634361CB08CA21CD8412665274A5AE8D6C1F8232F9D3C2C222018111B469257846D2CAFBCA23528BFCF18DCEEA6B178E2BDF3C3796600FBACA9AC437C15537EDB957B989667EC7D564F6A214B41C43961054D7623A8F3AF68242B8B7A267F 25FB858B 80B08AFFFFC30000 00 E2A596C3246AA4A9069B3215B5942598F5C36E4BCFB2DA82B8452771164FDB5A1E0CAB44153A48B284A2503FABB6419595EBE11D099120C67301BDF98E16BFAB6ACB055586900FC62E60D83D1405445918285EB6ED2E7CF296A19F2F2DA6F03D37464AAD7809E9F244476C4C2E509EB69B3BA9227BB5DF9C6CDC5694BEE04B44 47745F68 80B08AFFFFC30000 01 58E5085774E0E88AB799B07580FF120B127499C15E7BDA6F5A9EDB785FC01C16646A3DB9069C55997C6BE6021486F839DC1C228142A80BF64802112A062385AF5C270BBE642AD786AA4C6496C92E16571A5D2906E0289B7E1E531837DF4ED9DEDC68FC1CA5AE7866CAF653EC949F5A679AD054A388F47DCEA2F4BBCFA83C94B3 93B95743 ...

Vemos que las ECMs están formadas por

2 - Análisis del firmware de la tarjeta

El consejo nos muestra una foto de una tarjeta Funcard 4. Buscando información sobre esta tarjeta rápidamente encontraremos que tiene un procesador AT90S8515 y una eeprom AT24C256. Suficiente para comenzar a desensamblar y ver como funciona.

Nota: Sin el consejo, también deberíamos ser capaces de reconocer el procesador. Mirando el comienzo del firmware podemos ver xxC0xxC0xxC0xxC0 (13 veces xxC0). Parece una tabla con 13 saltos (una tabla de vectores de interrupción). Asi que, ¿que procesadores de los habitualmente usados en smartcards tiene la tabla de vectores en la posición 0000? ST...no, SLE...no, Texas... no, Motorola... no, ATMEL...si!! Podemos probar a desensamblar usando cualquier AVR y confirmar que funciona.

Datasheet AT90S8515

2.1 - Formato ECM

Analizando el desensamblado veremos que el formato de las ECMs debe ser el siguiente:

DD A2 00 00 F0 14 8E FE XX XX XX XX XX XX XX XX YY DATA...(0x80)...DATA ZZ ZZ ZZ ZZ

Cabecera:

Cuerpo:

Desensamblado:

ROM:02FC _cmdA2_ECM: ; CODE XREF: Main_Function:cmdA2 ROM:02FC lds r24, header4_LEN ROM:02FE cpi r24, 3 ROM:02FF brcc cmdA2_lenOK ROM:0300 rjmp send6700 ROM:0301 ROM:0301 cmdA2_lenOK: ; CODE XREF: Main_Function+188 ROM:0301 mov r16, r28 ROM:0302 mov r17, r29 ROM:0303 subi r16, -0x21 ROM:0304 sbci r17, -1 ROM:0305 mov r14, r28 ROM:0306 mov r15, r29 ROM:0307 ldi r27, 0x24 ROM:0308 add r14, r27 ROM:0309 adc r15, r1 ROM:030A ROM:030A cmdA2_loopRead3: ; CODE XREF: Main_Function+19C ROM:030A rcall sendACK_COM ROM:030B rcall readByte ROM:030C mov r30, r16 ROM:030D mov r31, r17 ROM:030E st Z+, r24 ROM:030F mov r16, r30 ROM:0310 mov r17, r31 ROM:0311 cp r30, r14 ROM:0312 cpc r31, r15 ROM:0313 brne cmdA2_loopRead3 ROM:0314 ldd r24, Y+0x21 ROM:0315 cpi r24, 0x14 ; 14 ROM:0316 breq loc_318 ROM:0317 rjmp send6700 ROM:0318 ROM:0318 loc_318: ; CODE XREF: Main_Function+19F ROM:0318 ldd r20, Y+0x22 ROM:0319 mov r18, r20 ROM:031A ldi r19, 0 ROM:031B lds r24, header4_LEN ROM:031D ldi r25, 0 ROM:031E sbiw r24, 2 ROM:031F cp r18, r24 ; header[4] - 2 ROM:0320 cpc r19, r25 ROM:0321 breq loc_323 ROM:0322 rjmp send6700 ROM:0323 ROM:0323 loc_323: ; CODE XREF: Main_Function+1AA ROM:0323 cpi r20, 0x8E ; 8E ROM:0324 breq loc_326 ROM:0325 rjmp send6700 ROM:0326 ROM:0326 loc_326: ; CODE XREF: Main_Function+1AD ROM:0326 ldd r24, Y+0x23 ROM:0327 andi r24, 0xFD ROM:0328 breq loc_32A ROM:0329 rjmp send6700 ROM:032A ROM:032A loc_32A: ; CODE XREF: Main_Function+1B1 ROM:032A ldi r17, 8 ROM:032B ROM:032B cmdA2_loopRead8: ; CODE XREF: Main_Function+1B7 ROM:032B rcall sendACK_COM ROM:032C rcall readByte ROM:032D subi r17, 1 ; XX XX XX XX XX XX XX XX ROM:032E brne cmdA2_loopRead8 ROM:032F rcall sendACK_COM ROM:0330 rcall readByte ; YY (odd/even flag) ROM:0331 mov r14, r24 ROM:0332 ldi r16, 0x76 ROM:0333 ldi r17, 0 ROM:0334 ROM:0334 cmdA2_loopRead80: ; CODE XREF: Main_Function+1C8 ROM:0334 ldi r27, 0 ROM:0335 cpi r16, 0xF6 ROM:0336 cpc r17, r27 ROM:0337 breq loc_340 ROM:0338 rcall sendACK_COM ROM:0339 rcall readByte ; 0x80 DATA ROM:033A mov r26, r16 ROM:033B mov r27, r17 ROM:033C st X+, r24 ; Store from 0076 to 00F5 (buffer) ROM:033D mov r16, r26 ROM:033E mov r17, r27 ROM:033F rjmp cmdA2_loopRead80 ROM:0340 ; --------------------------------------------------------------------------- ROM:0340 ROM:0340 loc_340: ; CODE XREF: Main_Function+1C0 ROM:0340 rcall sendACK_COM ROM:0341 rcall readByte ; ZZ ROM:0342 rcall sendACK_COM ROM:0343 rcall readByte ; ZZ ROM:0344 rcall sendACK_COM ROM:0345 rcall readByte ; ZZ ROM:0346 rcall sendACK_COM ROM:0347 rcall readByte ; ZZ

Descargar desensamblado completo: firmware.lst

2.2 - Procesado CW

Una vez recibida la ECM la tarjeta calcula la CW de la siguiente forma:

  1. Se comienza con un seed conocido de 16 bytes (valores del 00 al 0F)
  2. Se realizan 0x100 permutaciones en este seed de acuerdo a los 0x80 bytes de la ECM (Conocidos) y a los 0x80 bytes de la eeprom (Desconocidos)
  3. Con los 8 primeros bytes del seed resultante y los bytes de la ECM se calcula la CW.

Desensamblado:

ROM:0330 rcall readByte ; YY (odd/even flag) ROM:0331 mov r14, r24 ROM:0332 ldi r16, 0x76 ROM:0333 ldi r17, 0 ROM:0334 ROM:0334 cmdA2_loopRead80: ; CODE XREF: Main_Function+1C8 ROM:0334 ldi r27, 0 ROM:0335 cpi r16, 0xF6 ROM:0336 cpc r17, r27 ROM:0337 breq loc_340 ROM:0338 rcall sendACK_COM ROM:0339 rcall readByte ; 0x80 DATA ROM:033A mov r26, r16 ROM:033B mov r27, r17 ROM:033C st X+, r24 ; Store from 0076 to 00F5 (buffer) ROM:033D mov r16, r26 ROM:033E mov r17, r27 ROM:033F rjmp cmdA2_loopRead80 ... ... ROM:034A ldi r20, 0x80 ; Len = 80 ROM:034B ldi r22, 0 ROM:034C ldi r23, 0 ; Src = 0000 ROM:034D ldi r24, 0xF6 ROM:034E ldi r25, 0 ; Dest = 00F6 (buffer + 0x80) ROM:034F rcall Read_Eeprom ... ... ROM:0353 loc_353: ; CODE XREF: Main_Function+1DA ROM:0353 ldi r24, 0x60 ROM:0354 rcall sendByte ROM:0355 mov r30, r28 ROM:0356 mov r31, r29 ROM:0357 adiw r30, 1 ; Zreg = Yreg+1 = seed ROM:0358 ldi r24, 0 ; i=0 ROM:0359 ROM:0359 loopGenerateSeed: ; CODE XREF: Main_Function+1E5 ROM:0359 st Z+, r24 ; seed[i] = i ROM:035A subi r24, -1 ; i++ ROM:035B cpi r24, 0x10 ROM:035C brne loopGenerateSeed ROM:035D ldi r24, 0x60 ROM:035E rcall sendByte ROM:035F mov r8, r1 ; i=0 ROM:0360 ROM:0360 loopMixSeed: ; CODE XREF: Main_Function+205 ROM:0360 ; Main_Function+20A ROM:0360 mov r30, r8 ROM:0361 ldi r31, 0 ROM:0362 subi r30, -0x76 ROM:0363 sbci r31, -1 ROM:0364 ld r24, Z ; buffer[i] ROM:0365 mov r25, r24 ROM:0366 swap r25 ROM:0367 andi r25, 0xF ; HN = (buffer[i] >> 4) & 0x0F ROM:0368 ldi r26, 1 ROM:0369 ldi r27, 0 ROM:036A add r26, r28 ROM:036B adc r27, r29 ROM:036C add r26, r25 ROM:036D adc r27, r1 ROM:036E ld r25, X ; XX = seed[HN] ROM:036F andi r24, 0xF ; LB = buffer[i] & 0x0F ROM:0370 ldi r30, 1 ROM:0371 ldi r31, 0 ROM:0372 add r30, r28 ROM:0373 adc r31, r29 ROM:0374 add r30, r24 ROM:0375 adc r31, r1 ROM:0376 ld r24, Z ; YY = seed[LN] ROM:0377 st X, r24 ; seed[LN] = XX ROM:0378 st Z, r25 ; seed[HN] = YY ROM:0379 inc r8 ; i++ ROM:037A mov r24, r8 ROM:037B andi r24, 0xF ROM:037C brne loopMixSeed ROM:037D tst r8 ROM:037E breq loc_382 ROM:037F ldi r24, 0x60 ROM:0380 rcall sendByte ROM:0381 rjmp loopMixSeed ROM:0382 ROM:0382 loc_382: ; CODE XREF: Main_Function+207 ROM:0382 mov r15, r1 ROM:0383 ldi r20, 3 ROM:0384 ROM:0384 loop_Prepare_offset_CW: ; CODE XREF: Main_Function+210 ROM:0384 lsl r14 ; offsetCW = OddEvenFlag << 3 ROM:0385 rol r15 ROM:0386 dec r20 ROM:0387 brne loop_Prepare_offset_CW ROM:0388 ldi r30, 0x11 ROM:0389 ldi r31, 0 ROM:038A add r30, r28 ROM:038B adc r31, r29 ROM:038C add r14, r30 ROM:038D adc r15, r31 ; r14:r15 = Pointer to CW ROM:038E ldi r16, 0 ROM:038F ldi r17, 0 ; r16:r17 = i = 0 ROM:0390 mov r7, r1 ROM:0391 ROM:0391 loopGenerateCW: ; CODE XREF: Main_Function+246 ROM:0391 mov r24, r16 ROM:0392 andi r24, 3 ROM:0393 cpi r24, 3 ; if (i & 3) == 3 (If is CW checksum Byte) ROM:0394 breq loc_3B1 ROM:0395 ldi r24, 0x60 ROM:0396 rcall sendByte ROM:0397 mov r26, r28 ROM:0398 mov r27, r29 ROM:0399 adiw r26, 1 ROM:039A ldi r18, 0 ROM:039B mov r24, r16 ROM:039C mov r25, r17 ROM:039D subi r24, -0x76 ROM:039E sbci r25, -1 ROM:039F ROM:039F loopCalcCWByte: ; CODE XREF: Main_Function+235 ROM:039F ld r30, X+ ; seed[i] ROM:03A0 ldi r31, 0 ROM:03A1 ldi r19, 3 ROM:03A2 ROM:03A2 loop: ; CODE XREF: Main_Function+22E ROM:03A2 lsl r30 ; seed[i] >> 3 ROM:03A3 rol r31 ROM:03A4 dec r19 ROM:03A5 brne loop ROM:03A6 add r30, r24 ; Pointer to buffer + i + (seed[i] >> 3) + i ROM:03A7 adc r31, r25 ROM:03A8 ld r19, Z ; YY = buffer[i + (seed[i] >> 3)] ROM:03A9 eor r18, r19 ; XX ^= YY ROM:03AA cp r26, r10 ROM:03AB cpc r27, r11 ROM:03AC brne loopCalcCWByte ROM:03AD mov r24, r18 ROM:03AE add r24, r7 ; temporal checksum ROM:03AF mov r7, r18 ; r7 = Calculated Byte ROM:03B0 rjmp StoreCWByte ROM:03B1 ROM:03B1 loc_3B1: ; CODE XREF: Main_Function+21D ROM:03B1 ldi r24, 0 ROM:03B2 ROM:03B2 StoreCWByte: ; CODE XREF: Main_Function+239 ROM:03B2 mov r30, r14 ROM:03B3 mov r31, r15 ROM:03B4 add r30, r16 ROM:03B5 adc r31, r17 ROM:03B6 st Z, r7 ; CW[i] = r7 ROM:03B7 subi r16, -1 ROM:03B8 sbci r17, -1 ; i++ ROM:03B9 cpi r16, 8 ROM:03BA cpc r17, r1 ROM:03BB breq endCW ROM:03BC mov r7, r24 ; r7 = checksum ROM:03BD rjmp loopGenerateCW

Descargar desensamblado completo: firmware.lst

Código C#:

byte[] buffer = new byte[0x100]; for (int i = 0; i < 0x80; i++) buffer[i] = ecmdata[i]; //Known for (int i = 0; i < 0x80; i++) buffer[i + 0x80] = eepromData[i]; //Unknown byte[] seed = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F} for (int i = 0; i < 0x100; i++) { int a = buffer[i] & 0x0F; int b = buffer[i] >> 4; byte tmp = seed[a]; seed[a] = seed[b]; seed[b] = tmp; } byte[] cw = new byte[8]; for (int i = 0; i < 8; i++) { if ((i % 4) != 3) //No checksum Byte { for (int j = 0; j < 8; j++) cw[i] ^= buffer[i + (seed[j] << 3)]; } else //Checksum Byte { cw[i] = (byte)(cw[i - 1] + cw[i - 2] + cw[i - 3]); } }

2.3 - Análisis Debilidades

En este algoritmo podemos encontrar 3 debilidades que nos permitirán descifrar el stream sin conocer los datos de la eeprom de la tarjeta.

1° Vulnerabilidad

Para calcular una clave de 48 bits (+16 de checksums) está utilizando un seed de menor complejidad. En este caso 8 bytes de un grupo de 16 bytes conocidos, esto nos da

16! --- = 16*15*14*13*12*11*10*9 = 518.918.400 = 0x1EEE1100 posibilidades 8!

Es una cantidad razonable para realizar bruteforce

2° Vulnerabilidad

Aunque la longitud de los datos de eeprom (clave desconocida) son 0x80 bytes. El algoritmo utilizado hace que tenga una complejidad equivalente a 8 bytes. Ya que permutar muchas veces los mismos datos no tiene sentido y lo único que importa es la posición final que se puede conseguir con tan solo 8 permutaciones.

3ª Vulnerabilidad

El algoritmo utilizado nos permite deducir la clave a partir de una CW. Haciendo bruteforce a una CW podremos calcular un contenido de eeprom válido para cualquier ECM.

3 - Descifrando el stream

3.1 - Bruteforce

1 - Cogemos la Primera ECM

80B0 8A FFFFC30000 00 0C9B810F65AAF0A936640B97F4FCAA0A91D43EE512CF4481BB02E377CBC39FCF4651207D4628FE3046E8ACDBF8BFF0FE18E2AC81943E9CE5E8814198497A6B0252DC04A06DFDA77A4FBBFC7E9D3DAA8A23DFD06E529D200AF56DE63421BED5BEB74B80BE4AA3C8D7F0C98B525B2552AD341C431985709C60B4C704200DF0D08E 227EE00D

2 - Es una ECM Par (el byte 8 es 00) asi que buscamos el primer inicio de audio cifrado con CW Par

474405B6 075000015529FE81 C4B8715DA78BA62721415BC995357AC21AEFB341B133B24A03CF1A0011F1D76DDA2687334478AC41EE9F38AE71814C9BD96E3BEA2812F47DC898C078FD6D5CE949EEDDA243A0853CD60C819FB923F2E619879DCD3F4B7CF9132E66C883E03E28020CF910C06ABFF42E76F980A1FF17026D6A77E44B19EDF73A19108ABAF01FF17863A5B9E2C81F1B171B1604FE80CB416F4A154FA4463D34AE1779D9BE0B6CA8A5CE5EF202EB7927CD68AC853D71C8E3

Por ser un inicio de una trama PES sabemos que los primeros bytes de de este paquete descifrado debe ser 00 00 01 C0 (mirando los paquetes de audio en claro del canal del PID 0401 podemos confirmar esto)... asi que ya tenemos lo necesario para realizar un bruteforce.


3 - Hacemos bruteforce del seed utilizando la primera ecm y el paquete de audio

byte[] encrypted_audio = {0xC4, 0xB8, 0x71, 0x5D, 0xA7, 0x8B, 0xA6, 0x27, 0x21, 0x41, 0x5B, 0xC9 }; //The first 12 bytes are enough to decrypt 4 bytes byte[] ecmdata = {0x0C, 0x9B, 0x81, 0x0F, 0x65, 0xAA, 0xF0, 0xA9, 0x36, 0x64, 0x0B, 0x97, 0xF4, 0xFC, 0xAA, 0x0A, 0x91, 0xD4, 0x3E, 0xE5, 0x12, 0xCF, 0x44, 0x81, 0xBB, 0x02, 0xE3, 0x77, 0xCB, 0xC3, 0x9F, 0xCF, 0x46, 0x51, 0x20, 0x7D, 0x46, 0x28, 0xFE, 0x30, 0x46, 0xE8, 0xAC, 0xDB, 0xF8, 0xBF, 0xF0, 0xFE, 0x18, 0xE2, 0xAC, 0x81, 0x94, 0x3E, 0x9C, 0xE5, 0xE8, 0x81, 0x41, 0x98, 0x49, 0x7A, 0x6B, 0x02, 0x52, 0xDC, 0x04, 0xA0, 0x6D, 0xFD, 0xA7, 0x7A, 0x4F, 0xBB, 0xFC, 0x7E, 0x9D, 0x3D, 0xAA, 0x8A, 0x23, 0xDF, 0xD0, 0x6E, 0x52, 0x9D, 0x20, 0x0A, 0xF5, 0x6D, 0xE6, 0x34, 0x21, 0xBE, 0xD5, 0xBE, 0xB7, 0x4B, 0x80, 0xBE, 0x4A, 0xA3, 0xC8, 0xD7, 0xF0, 0xC9, 0x8B, 0x52, 0x5B, 0x25, 0x52, 0xAD, 0x34, 0x1C, 0x43, 0x19, 0x85, 0x70, 0x9C, 0x60, 0xB4, 0xC7, 0x04, 0x20, 0x0D, 0xF0, 0xD0, 0x8E}; byte[] decrypted_audio = new byte[4]; for (byte b1 = 0; b1 < 0x10; b1++) { for (byte b2 = 0; b2 < 0x10; b2++) { if (b1 == b2) continue; for (byte b3 = 0; b3 < 0x10; b3++) { if ((b1 == b3) || (b2 == b3)) continue; for (byte b4 = 0; b4 < 0x10; b4++) { if ((b1 == b4) || (b2 == b4) || (b3 == b4)) continue; for (byte b5 = 0; b5 < 0x10; b5++) { if ((b1 == b5) || (b2 == b5) || (b3 == b5) || (b4 == b5)) continue; for (byte b6 = 0; b6 < 0x10; b6++) { if ((b1 == b6) || (b2 == b6) || (b3 == b6) || (b4 == b6) || (b5 == b6)) continue; for (byte b7 = 0; b7 < 0x10; b7++) { if ((b1 == b7) || (b2 == b7) || (b3 == b7) || (b4 == b7) || (b5 == b7) || (b6 == b7)) continue; for (byte b8 = 0; b8 < 0x10; b8++) { if ((b1 == b8) || (b2 == b8) || (b3 == b8) || (b4 == b8) || (b5 == b8) || (b6 == b8) || (b7 == b8)) continue; byte[] seed = { b1, b2, b3, b4, b5, b6, b7, b8 }; byte[] cw = new byte[8]; for (int i = 0; i < 8; i++) { if ((i % 4) != 3) { for (int j = 0; j < 8; j++) cw[i] ^= ecmdata[i + (seed[j] << 3)]; } else { cw[i] = (byte)(cw[i - 1] + cw[i - 2] + cw[i - 3]); } } CSA.decryptsoft(cw, encrypted_audio, decrypted_audio); //It's a standard CSA decryption but limited to first 4 bytes instead of the complete packet, to speed up the bruteforce if ((res[0] == 0x00) && (res[1] == 0x00) && (res[2] == 0x01)) { tresul.Text = "Bingo! CW=" + bytetostring(cw) + " Seed= " + bytetostring(seed) + " Audio=" + bytetostring(res); return; } } } } } } } } }

Y tras unos minutos... Bingo! CW=836219FE3C242989 Seed=02050607080B0C0F Audio=000001C0


4 - Comprobamos que efectivamente la CW funciona y descifra el paquete completo

000001C009D48080052100137109FFF5C2C4FFFFFFFFFFFFFFFFFFF52D35D4FDD556CA4A4BA8D55450FBB0BAFD775D7616E97577553494596D96D75D77DD6E195F85EB56E587AB9CCDC8056DE4FFA2A5B82C3D5A9ECF6989642CA21F195E132CDAEAC91BE01E80D7E955DB28E60910CF32AA697E029C957665BEA7D7D445AA0F4D22DF156D8F547DBCB396CE314F24F4DD069DB485CA66D18B1260F40DE7F569BDB13A8741DBEF41450B92A7636C5AB5

3.2 - Crear EEprom equivalente

5 - Sabemos el Seed resultante, asique podemos calcular una funcion equivalente a todas los intercambios que provocan los bytes de EEPROM

A-Calculamos el Seed tras las permutaciones producidos por el byte de la ECM

byte[] buffer = {0x0C, 0x9B, 0x81, 0x0F, 0x65, 0xAA, 0xF0, 0xA9, 0x36, 0x64, 0x0B, 0x97, 0xF4, 0xFC, 0xAA, 0x0A, 0x91, 0xD4, 0x3E, 0xE5, 0x12, 0xCF, 0x44, 0x81, 0xBB, 0x02, 0xE3, 0x77, 0xCB, 0xC3, 0x9F, 0xCF, 0x46, 0x51, 0x20, 0x7D, 0x46, 0x28, 0xFE, 0x30, 0x46, 0xE8, 0xAC, 0xDB, 0xF8, 0xBF, 0xF0, 0xFE, 0x18, 0xE2, 0xAC, 0x81, 0x94, 0x3E, 0x9C, 0xE5, 0xE8, 0x81, 0x41, 0x98, 0x49, 0x7A, 0x6B, 0x02, 0x52, 0xDC, 0x04, 0xA0, 0x6D, 0xFD, 0xA7, 0x7A, 0x4F, 0xBB, 0xFC, 0x7E, 0x9D, 0x3D, 0xAA, 0x8A, 0x23, 0xDF, 0xD0, 0x6E, 0x52, 0x9D, 0x20, 0x0A, 0xF5, 0x6D, 0xE6, 0x34, 0x21, 0xBE, 0xD5, 0xBE, 0xB7, 0x4B, 0x80, 0xBE, 0x4A, 0xA3, 0xC8, 0xD7, 0xF0, 0xC9, 0x8B, 0x52, 0x5B, 0x25, 0x52, 0xAD, 0x34, 0x1C, 0x43, 0x19, 0x85, 0x70, 0x9C, 0x60, 0xB4, 0xC7, 0x04, 0x20, 0x0D, 0xF0, 0xD0, 0x8E}; byte[] seed = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F} for (int i = 0; i < 0x80; i++) { int a = buffer[i] & 0x0F; int b = buffer[i] >> 4; byte tmp = seed[a]; seed[a] = seed[b]; seed[b] = tmp; } tresul.Text = "Seed después de las permutaciones de la ECM: " + bytetostring(seed);

-> Seed después de las permutaciones de la ECM: 0908030E0407020F0B000D0A01050C06

B-Vemos los 8 cambios equivalentes a los 0x80 de los bytes de EEPROM

byte[] equivalentMixingEEP = {6, 13, 15, 5, 1, 8, 14, 7};

6 - Probamos a calcular la CW sin BruteForce

byte[] buffer = {0x0C, 0x9B, 0x81, 0x0F, 0x65, 0xAA, 0xF0, 0xA9, 0x36, 0x64, 0x0B, 0x97, 0xF4, 0xFC, 0xAA, 0x0A, 0x91, 0xD4, 0x3E, 0xE5, 0x12, 0xCF, 0x44, 0x81, 0xBB, 0x02, 0xE3, 0x77, 0xCB, 0xC3, 0x9F, 0xCF, 0x46, 0x51, 0x20, 0x7D, 0x46, 0x28, 0xFE, 0x30, 0x46, 0xE8, 0xAC, 0xDB, 0xF8, 0xBF, 0xF0, 0xFE, 0x18, 0xE2, 0xAC, 0x81, 0x94, 0x3E, 0x9C, 0xE5, 0xE8, 0x81, 0x41, 0x98, 0x49, 0x7A, 0x6B, 0x02, 0x52, 0xDC, 0x04, 0xA0, 0x6D, 0xFD, 0xA7, 0x7A, 0x4F, 0xBB, 0xFC, 0x7E, 0x9D, 0x3D, 0xAA, 0x8A, 0x23, 0xDF, 0xD0, 0x6E, 0x52, 0x9D, 0x20, 0x0A, 0xF5, 0x6D, 0xE6, 0x34, 0x21, 0xBE, 0xD5, 0xBE, 0xB7, 0x4B, 0x80, 0xBE, 0x4A, 0xA3, 0xC8, 0xD7, 0xF0, 0xC9, 0x8B, 0x52, 0x5B, 0x25, 0x52, 0xAD, 0x34, 0x1C, 0x43, 0x19, 0x85, 0x70, 0x9C, 0x60, 0xB4, 0xC7, 0x04, 0x20, 0x0D, 0xF0, 0xD0, 0x8E}; byte[] seed = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F} for (int i = 0; i < 0x80; i++) { int a = buffer[i] & 0x0F; int b = buffer[i] >> 4; byte tmp = seed[a]; seed[a] = seed[b]; seed[b] = tmp; } byte[] finalSeed = new byte[8]; //We need only 8 bytes for (int i = 0; i < 8; i++) { finalSeed[i] = seed[equivalentMixingEEP[i]]; } byte[] cw = new byte[8]; for (int i = 0; i < 8; i++) { if ((i % 4) != 3) { for (int j = 0; j < 8; j++) cw[i] ^= buffer[i + (finalSeed[j] << 3)]; } else { cw[i] = (byte)(cw[i - 1] + cw[i - 2] + cw[i - 3]); } } tresul.Text = "CW: " + bytetostring(cw);

-> CW: 836219FE3C242989

3.3 - Descifrar Audio

6 - Sabiendo descifrar la ECM... podemos hacer una emu completa y descifrar el audio :)

private byte[] decryptECM(byte[] buffer) { byte[] seed = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F} for (int i = 0; i < 0x80; i++) { int a = buffer[i] & 0x0F; int b = buffer[i] >> 4; byte tmp = seed[a]; seed[a] = seed[b]; seed[b] = tmp; } byte[] finalSeed = new byte[8]; //We need only 8 bytes for (int i = 0; i < 8; i++) { finalSeed[i] = seed[equivalentMixingEEP[i]]; } byte[] cw = new byte[8]; for (int i = 0; i < 8; i++) { if ((i % 4) != 3) { for (int j = 0; j < 8; j++) cw[i] ^= buffer[i + (finalSeed[j] << 3)]; } else { cw[i] = (byte)(cw[i - 1] + cw[i - 2] + cw[i - 3]); } } return cw; } private byte[] getBytes(byte[] data, int offset, int len) { byte[] result = new byte[len]; for (int i = 0; i < len; i++) result[i] = data[offset + i]; return result; } private void decryptTS() { Stream instream = File.OpenRead("transponder.ts"); BinaryReader bufin = new BinaryReader(instream); byte[] tsFile = new byte[instream.Length]; bufin.Read(tsFile, 0, tsFile.Length); bufin.Close(); instream.Close(); FileStream stream = new FileStream("audio_decrypted.mpa", FileMode.Create, FileAccess.Write); BinaryWriter writer = new BinaryWriter(stream); bool writeResult = false; byte flagECM = 0xFF; byte[] cwEven = new byte[8]; byte[] cwOdd = new byte[8]; for (int pos = 0; pos < tsFile.Length; pos += 188) { byte[] ts = getBytes(tsFile, pos, 188); ushort PID = (ushort)(((ts[1] << 8) + ts[2]) & 0x1FFF); switch (PID) { case 0x0404: //ECM { if (ts[13] != flagECM) { flagECM = ts[13]; if (ts[13] == 0) cwEven = decryptECM(getBytes(ts, 14, 0x80)); else if (ts[13] == 1) cwOdd = decryptECM(getBytes(ts, 14, 0x80)); } } break; case 0x0405: //Audio { int AFsize = 0; if ((ts[3] & 0x20) > 0) //There is AF { AFsize = ts[4] + 1; } byte[] data = getBytes(ts, 4 + AFsize, 0xBC - 4 - AFsize); byte[] result = new byte[data.Length]; if ((ts[3] & 0x40) == 0) CSA.decrypt(cwEven, data, result); else CSA.decrypt(cwOdd, data, result); if ((result[0] == 0x00) && (result[1] == 0x00) && (result[2] == 0x01) && (result[3] == 0xC0)) writeResult = true; if (writeResult) //We wait the first initial packet decrypted correctly to write the result in the file { writer.Write(result); } } break; default: //Ignore other PIDs break; } } writer.Close(); stream.Close(); }

3.4 - Flag

7 - Podemos escuchar el fichero audio_decrypted.mpa:

Descargar audio audio_decrypted.zip

Congratulations, the flag is 32C3_youre_listening_to_flagfm_radio