Sunteți pe pagina 1din 12

CTF Una-al-mes 

COCHE FANTÁSTICO. Episodio 3

Con la nueva release del firmware de Kitt cambió el sistema de desbloqueo. Después de
unos cuantos intentos fallidos el sistema se ha bloqueado y necesitamos introducir un
código de seguridad. Lamentablemente, el becario que desarrolló el firmware está de de
baja porque se rompió el radio y no coge el teléfono. Debes encontrar la forma de generar
un código válido.

http://34.253.120.147:31337

Info: La flag tiene el formato UAM{md5 del string encontrado}

Resolución

Abrimos la página.

Después de buscar en los fuentes de la web y posibles vulnerabilidades, y no encontrar


ninguna pista adicional, pasamos a enumerar:

gobuster -e -u http://34.253.120.147:31337 -w /usr/share/wordlists/dirb/common.txt

=====================================================
Gobuster v2.0.1 OJ Reeves (@TheColonial)
=====================================================
[+] Mode : dir
[+] Url/Domain : http://34.253.120.147:31337/
[+] Threads : 10
[+] Wordlist : /usr/share/wordlists/dirb/common.txt
[+] Status codes : 200,204,301,302,307,403
[+] Expanded : true
[+] Timeout : 10s
=====================================================
2020/01/15 13:09:11 Starting gobuster
=====================================================
http://34.253.120.147:31337/flag (Status: 200)
http://34.253.120.147:31337/seccode (Status: 302)
=====================================================
2020/01/15 13:10:14 Finished
=====================================================

http://34.253.120.147:31337/flag (Status: 200) :

UAM{aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1mc3hWbzZXcUk4dw==}

base64

UAM{https://www.youtube.com/watch?v=fsxVo6WqI8w}

Mediante el segundo enlace, descargamos el programa seccode y analizamos

​ ot@kali:~/Desktop# file seccode


ro

seccode: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32,
BuildID[sha1]=7dae0b820e98b8d7a7b4299af23de7523953fe29, not strippe​d

Probamos:

root@kali:~/Desktop# ./seccode

Usage: ./seccode <key> <seed>,

root@kali:~/Desktop# ./seccode 1111 1111

Invalid key.

Tendremos que buscar la key correcta. Abrimos con Ghidra y decompilamos “main”

undefined8 main(int param_1,undefined8 *param_2)

{
char *__s;
char cVar1;
int iVar2;
int iVar3;
undefined8 uVar4;
size_t sVar5;
uint uVar6;
long in_FS_OFFSET;
uint local_60;
int local_5c;
int local_58;
code *local_50;
byte abStack40 [8];
long local_20;

local_20 = *(long *)(in_FS_OFFSET + 0x28);


​ if (param_1 < 3) {
printf("Usage: %s <key> <seed>\n",*param_2);
uVar4 = 1;
}
else {
​ __s = (char *)param_2[1];
sVar5 = strlen(__s);
if (sVar5 == 8) {
local_60 = 0;
while( true ) {
sVar5 = strlen(__s);
if (sVar5 <= (ulong)(long)(int)local_60) break;
iVar2 = (int)local_60 / 2;
iVar3 = tolower((int)__s[(int)local_60]);
cVar1 = (char)iVar3;
if ((cVar1 < '0') || ('8' < cVar1)) {
local_5c = (int)cVar1 + -0x57;
}
else {
local_5c = (int)cVar1 + -0x30;
}
if ((local_60 & 1) == 0) {
abStack40[iVar2] = abStack40[iVar2] & 0xf;
abStack40[iVar2] = abStack40[iVar2] | (byte)(local_5c << 4);
}
else {
abStack40[iVar2] = abStack40[iVar2] & 0xf0;
abStack40[iVar2] = abStack40[iVar2] | (byte)local_5c & 0xf;
}
local_60 = local_60 + 1;
}
mprotect(FUN_00403000,0x1000,6);
local_58 = 0;
​ local_50 = FUN_00403000;
while (local_50 < &LAB_0040333a) {
uVar6 = (uint)(local_58 >> 0x1f) >> 0x1e;
*(byte *)local_50 = abStack40[(int)((local_58 + uVar6 & 3) - uVar6)] ^ (byte)*local_50;
local_50 = local_50 + 1;
local_58 = local_58 + 1;
}
FUN_00402000((char *)param_2[2]);
​ iVar2 = strcmp(&DAT_0040300d,"UAM");
if (iVar2 == 0) {
uVar4 = FUN_00403000();
printf("Codigo: %lu\n",uVar4);
}
else {
puts("Incorrect key :(");
}
uVar4 = 0;
}
else {
puts("Invalid key");
uVar4 = 1;
}
}
if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return uVar4;
}

Están resaltadas las partes más interesantes del código.

En este primer vistazo, ya podemos determinar la longitud de la “key”, 8 caracteres.

__s = (char *)param_2[1];


sVar5 = strlen(__s);
if (sVar5 == 8) {

root@kali:~/Desktop# ./seccode 11111111 1111

Incorrect key :(

Para entender mejor el código, nos ayudamos de edb.

root@kali:~/Desktop# edb --run seccode 11111111 1111

while( true ) {
sVar5 = strlen(__s);
if (sVar5 <= (ulong)(long)(int)local_60) break;
iVar2 = (int)local_60 / 2;
iVar3 = tolower((int)__s[(int)local_60]);
cVar1 = (char)iVar3;
if ((cVar1 < '0') || ('8' < cVar1)) {
local_5c = (int)cVar1 + -0x57;
}
else {
local_5c = (int)cVar1 + -0x30;
}
if ((local_60 & 1) == 0) {
abStack40[iVar2] = abStack40[iVar2] & 0xf;
abStack40[iVar2] = abStack40[iVar2] | (byte)(local_5c << 4);
}
else {
abStack40[iVar2] = abStack40[iVar2] & 0xf0;
abStack40[iVar2] = abStack40[iVar2] | (byte)local_5c & 0xf;
}
local_60 = local_60 + 1;
}
Esta parte de código está localizada en 00402187-00402269.

Tras varias ejecuciones, cambiando la key de entrada y poniendo un breakpoint en


00402272 (fin bucle), observamos que se copia a una zona de memoria (rbp-0x20) el “valor
“ del string de entrada:

Para 11111111:

Para AB112233:

Pasamos a analizar la siguiente parte del código


local_58 = 0;
​ local_50 = FUN_00403000;
while (local_50 < &LAB_0040333a) {
uVar6 = (uint)(local_58 >> 0x1f) >> 0x1e;
*(byte *)local_50 = abStack40[(int)((local_58 + uVar6 & 3) - uVar6)] ^ (byte)*local_50;
local_50 = local_50 + 1;
local_58 = local_58 + 1;
}

abStack40 -> es la zona de memoria donde se coloca el valor del string, parámetro key.

Lo que realiza es un xor de key introducida con los valores 0x403000 - 0x40333a

Posteriormente:

FUN_00402000((char *)param_2[2]); // algo con el segundo parámetro ->Seed


​ iVar2 = strcmp(&DAT_0040300d,"UAM");​ /
if (iVar2 == 0) {
uVar4 = FUN_00403000();
printf("Codigo: %lu\n",uVar4);
}
else {
puts("Incorrect key :(");
}

Luego, compara la dirección de memoria 0x40300d tras el XOR realizado con la key de
entrada contra la string “UAM”. Para solucionarlo tenemos que:

​ 0040300d 64 ?? 64h d
0040300e 76 ?? 76h v
0040300f 7a ?? 7Ah z
00403010 4b ?? 4Bh K

​ 004033fc 55 ?? 55h U
004033fd 41 ?? 41h A
004033fe 4d ?? 4Dh M
004033ff 00 ?? 00h

d ⊕ k1 = ‘U’ => k1 = 64 ⊕ 55 = k1 = 31 => 1


d ⊕ k2 = ‘A’ => k2 = 76 ⊕ 41 = k2 = 37 => 7
d ⊕ k3 = ‘M’ => k3 = 7a ⊕ 4d = k3 = 37 => 7
d ⊕ k4 = ‘’ => k4 = 4b ⊕ 00 = k4 = 4b => K

Probamos la key encontrada “3137374b”

root@kali:~# .​/seccode 3137374b 11111

Incorrect key :(
Algo no va del todo bien, revisamos, volcamos en hexadecimal, la zona de memoria de
(00403000-00403339) y realizamos el XOR en ​CyberChef​, ​comprobamos que el error se
produce por el desplazamiento al realizar el XOR. Al empezar en 403000, cuando llega a la
dirección 0040300d, la key no se aplica como necesitamos (4 caracteres 3137374b):

1e 79 be d2 03 b2 db 57 03 b8 4a 9f 7e 64 76 7a 4b

la solución, desplazar la key a 4b313737, y ya podemos ver en el volcado la palabra UAM y


algo de texto al final del​ ​volcado​.

Ahora seguimos el pseudocódigo, cuando la comprobación es correcta:

​ iVar2 = strcmp(&DAT_0040300d,"UAM");​ /
if (iVar2 == 0) {
uVar4 = ​FUN_00403000();
printf("Codigo: %lu\n",uVar4);
}

Lo que realiza es una llamada a 403000, es decir, una vez realizado el xor y la key es
correcta, interpreta la zona de memoria como opcodes, pasando a ser de zona de
ejecución.

En edb, podemos ver como queda el código, si ejecutamos paso a paso, otro detalle a tener
en cuenta, sobreescribe la zona de ejecución con nop, borrando algunas instrucciones (xor).
Para poder analizar el código, podemos utilizar Ghidra, que incorpora un decompilador muy
bueno. Para ello, utilizamos un editor hexadecimal de ficheros, y sustituimos la parte
correspondiente al volcado de memoria (00403000-00403339) por los valores después del
XOR, teniendo en cuenta en la sustitución del ​volcado de memoria los valores de las
imágenes anteriores (nop). Después abrimos en Ghidra y en esas direcciones ya aparece el
pseudocódigo en C.
ulong FUN_00403000(ulong param_1)

{
ulong uVar1;
time_t tVar2;
time_t tVar3;
ulong uVar4;
float fVar5;
float fVar6;
ulong local_60;
ulong local_48;
ulong local_40;
ulong local_38;
ulong local_30;

/* WARNING: Read-only address (ram,0x00403034) is written */


uRam0000000000403034 = 0xa879ebc3;
local_48 = 0;
local_40 = 1;
local_38 = 1;
local_60 = param_1;
if (param_1 < 0x10000) {
local_60 = 0x10000;
}
local_30 = 0;
tVar2 = time((time_t *)0x0);
while( true ) {
uVar4 = local_30 + 1;
if (local_60 <= local_30) break;
​ local_48 = (local_38 * 4 + local_40 * 6) % 0x1337733b;
​ local_38 = local_40​;
​ local_40 = local_48;
local_30 = uVar4;
if ((uVar4 & 0xffffff) == 0) {
tVar3 = time((time_t *)0x0);
uVar1 = (long)(((tVar3 - tVar2) * local_60) / uVar4) / 0x3c;
if ((long)uVar4 < 0) {
uVar4 = uVar4 >> 1 | (ulong)((uint)uVar4 & 1);
fVar5 = (float)uVar4 + (float)uVar4;
}
else {
fVar5 = (float)uVar4;
uVar4 = uVar1;
}
if ((long)local_60 < 0) {
uVar4 = local_60 >> 1 | (ulong)((uint)local_60 & 1);
fVar6 = (float)uVar4 + (float)uVar4;
}
else {
fVar6 = (float)local_60;
}
printf((char *)(double)((fVar5 * 100.00000000) / fVar6),
"\r%.2f%% - Tiempo restante: %lu minutos...",uVar1,uVar4);
fflush(stdout);
}
}
printf("\r \r");
​ return local_48 % 100000;
}

Resumiendo utiliza la el parámetro “​seed” c​ omo límite para ir generando los términos de
una serie, y da como resultado el último término, ( “capado” -> mod 100000). Las otras
variables, sólo se utilizan para ir dando el porcentaje del proceso y una estimación del
tiempo.

Serie:

F(n) = ( 6* F(n-1) + 4 * F(n-2) ) % 0x1337733b

En los Hints encontrados en el ​volcado de memoria​ tenemos:

HINT -> ​“Obtener posición de una serie recurrente usando matrices: “

https://www.geeksforgeeks.org/matrix-exponentiation/

Para la serie:

F(n) = a*F(n-1) + b*F(n-2) + c*F(n-3)

El término n-esimo, se puede calcular:

| F(n) | = [ | a b c | ] ^ (n-2) * | F(2) |


| F(n-1) | [ | 1 0 0 | ] | F(1) |
| F(n-2) | [ | 0 1 0 | ] | F(0) |

En nuestro caso, a =6, b = 4, c = 0, F(0) = 0 , F(1) = 1, F(2) = 1

F(n) = 6*F(n-1) + 4*F(n-2) + 0*F(n-3)

| F(n) | = [ | 6 4 0 | ] ^ (n-2) * | F(2) |


| F(n-1) | [ | 1 0 0 | ] | F(1) |
| F(n-2) | [ | 0 1 0 | ] | F(0) |

La misma página nos da un pequeño programa de ejemplo, que podemos utilizar como
base, modificamos para ajustarlo a este caso concreto y añadiendo el cálculo modular,
utilizando las propiedades indicadas en:

HINT => “Exponenciar una matriz aplicando módulo:”

https://stackoverflow.com/a/26284607/1756928
or a ​matrix​ ​M​. This comes from the following two fundamental identities, which are
valid for integers ​x​ and ​y​:

(x+y) mod p = ([x mod p]+[y mod p]) mod p ​# All additions can be done on
numbers *modulo p*

(x*y) mod p = ([x mod p]*[y mod p]) mod p ​# All multiplications can be done
on numbers *modulo p*

# Python3 program to find value of f(n)


# where f(n) is defined as F(n) = F(n-1) + F(n-2) + F(n-3), n >= 3
# Base Cases : F(0) = 0, F(1) = 1, F(2) = 1
def​ multiply(a, b):
mul = [[​0​ ​for​ x ​in​ ​range​(​3​)]
​for​ y ​in​ ​range​(​3​)];
​for​ i ​in​ ​range​(​3​):
​for​ j ​in​ ​range​(​3​):
mul[i][j] = ​0​;
​for​ k ​in​ ​range​(​3​):
mul[i][j] += (a[i][k] * b[k][j])​ % ​0x1337733b​;
​for​ i ​in​ ​range​(​3​):
​for​ j ​in​ ​range​(​3​):
a[i][j] = mul[i][j]; ​# Updating our matrix
​return​ a;
# Recursive Function to compute F raise # to power n-2.
def​ power(F, n):
​ M = [[6, 4, 0], [1, 0, 0], [0, 1, 0]];
​# Multiply it with initial values i.e
​# with F(0) = 0, F(1) = 1, F(2) = 1
​if​ (n == ​1​):
​return​ (F[​0​][​0​] + F[​0​][​1​]) ​% ​0x1337733b​;
power(F, ​int​(n / ​2​));
F = multiply(F, F);
​if​ (n % ​2​ != ​0​):
F = multiply(F, M);
​return​ (F[​0​][​0​] + F[​0​][​1​])​ % 0x1337733b​;

def​ findNthTerm(n):
​ F = [[6​,​ 4, 0], [1, 0, 0], [0, 1, 0]];
​return​ power(F, n - ​2​);
# Driver code
n = ​284885361295​; ​#​Seed, código desbloqueo indicado en la web
x = findNthTerm(​n+2​); ​#​desplazamiento del término n para ajustarlo a seccode
print​(​"F("​+​str​(n)+​") is"​, x);
print​(​"F("​+​str​(n)+​") is"​, x % ​100000​);

Probamos en ​https://repl.it/languages/python3

Aquí, hay que comentar que para comprobar funcionaba correctamente, se probaron con
los valores obtenidos en seccode y el script en python, descubriendo que el programa nos
devolvía dos términos por delante de la semilla n introducida.

./seccode 4b313737 ​65536


Codigo: ​21905

./seccode 4b313737 65537


Codigo: 46539

./seccode 4b313737 65538


Codigo: 61439

Python3:
F(65536) is 35986092
F(65536) is 86092

F(65537) is 167480131
F(65537) is 80131

F(65538) is 181621905
F(​65538​) is ​21905

Probamos en la web, ponemos la semilla, en este caso ​209717987314

F(209717987314) is 19826609
F(209717987314) is ​26609

Correcto!
Esta es tu flag: UAM{b4f2741f6f27497193288faf2cf70399}

UAM{b4f2741f6f27497193288faf2cf70399}
Found : julianwashere

@bicacaro

S-ar putea să vă placă și