babyreeee
4 minutos de lectura
Se nos proporciona un binario llamado chall
:
$ file chall
chall: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=151528987cd274999ec93665ef2d6a7678c5107b, for GNU/Linux 3.2.0, stripped
Si lo ejecutamos, nos pregunta por la flag:
$ ./chall
Hello! Welcome to SEETF. Please enter the flag.
SEE{asdf}
Flag wrong. Try again.
Vamos a abrir el binario en Ghidra para analizar el código en C descompilado. Esta es la función main
. Puede parecer muy compleja porque hay muchas asignaciones:
undefined8 main() {
char *pcVar1;
undefined4 *puVar2;
size_t length;
ulong i;
ulong i_copy;
char flag_input[128];
undefined4 local_d8[4];
undefined4 local_c8;
undefined4 uStack196;
undefined4 uStack192;
undefined4 uStack188;
undefined4 local_b8;
undefined4 uStack180;
undefined4 uStack176;
undefined4 uStack172;
undefined4 local_a8;
undefined4 uStack164;
undefined4 uStack160;
undefined4 uStack156;
undefined4 local_98;
undefined4 uStack148;
undefined4 uStack144;
undefined4 uStack140;
undefined4 local_88;
undefined4 uStack132;
undefined4 uStack128;
undefined4 uStack124;
undefined4 local_78;
undefined4 uStack116;
undefined4 uStack112;
undefined4 uStack108;
undefined4 local_68;
undefined4 uStack100;
undefined4 uStack96;
undefined4 uStack92;
undefined4 local_58;
undefined4 uStack84;
undefined4 uStack80;
undefined4 uStack76;
undefined4 local_48;
undefined4 uStack68;
undefined4 uStack64;
undefined4 uStack60;
undefined4 local_38;
undefined4 uStack52;
undefined4 uStack48;
undefined4 uStack44;
undefined4 local_28;
undefined4 uStack36;
undefined4 uStack32;
undefined4 uStack28;
undefined4 local_18;
undefined4 uStack20;
undefined4 uStack16;
undefined4 uStack12;
byte i_byte;
puts("Hello! Welcome to SEETF. Please enter the flag.");
local_d8[0] = 0x98;
local_d8[1] = 0x8b;
local_d8[2] = 0x88;
local_d8[3] = 0xc3;
local_c8 = 0x71;
uStack196 = 0xb6;
uStack192 = 0x7e;
uStack188 = 0xa3;
local_b8 = 0x72;
uStack180 = 0xbb;
uStack176 = 0x73;
uStack172 = 0x7d;
local_a8 = 0x7a;
uStack164 = 0xa9;
uStack160 = 0x74;
uStack156 = 0x73;
local_98 = 0x68;
uStack148 = 0xa4;
uStack144 = 0xb6;
uStack140 = 0x6e;
local_88 = 0x62;
uStack132 = 0xbc;
uStack128 = 0x61;
uStack124 = 0x61;
local_78 = 0x62;
uStack116 = 0xb3;
uStack112 = 0x67;
uStack108 = 0xbc;
local_68 = 0x61;
uStack100 = 0x6b;
uStack96 = 0xb8;
uStack92 = 0xb5;
local_58 = 0x56;
uStack84 = 0x54;
uStack80 = 0x89;
uStack76 = 0x55;
local_48 = 0x8c;
uStack68 = 0x50;
uStack64 = 0x5b;
uStack60 = 0x51;
local_38 = 0x53;
uStack52 = 0x54;
uStack48 = 0x5d;
uStack44 = 0x5e;
local_28 = 0x50;
uStack36 = 0x86;
uStack32 = 0x89;
uStack28 = 0x89;
local_18 = 0x48;
uStack20 = 0x4f;
uStack16 = 0x49;
uStack12 = 0xf1;
fgets(flag_input, 0x80, stdin);
length = strlen(flag_input);
if (length == 53) {
puts("Good work! Your flag is the correct size.");
puts("On to the flag check itself...");
length = strlen(flag_input);
i = 0;
do {
i_copy = i & 0xffffffff;
if (length - 1 == i) {
puts("Success! Go get your points, champ.");
return 0;
}
pcVar1 = flag_input + i;
puVar2 = local_d8 + i;
i_byte = (byte) i;
i = i + 1;
} while ((byte) *puVar2 == (byte) (*pcVar1 + 0x45U ^ i_byte));
printf("Flag check failed at index: %d", i_copy);
} else {
printf("Flag wrong. Try again.");
}
return 1;
}
Nótese que la flag tiene que tener 53 caracteres (realmente 52, porque el último carácter representa el salto de línea):
$ python3 -c 'print("A" * 47)'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$ ./chall
Hello! Welcome to SEETF. Please enter the flag.
SEE{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}
Good work! Your flag is the correct size.
On to the flag check itself...
Flag check failed at index: 4
Aquí hay dos posibilidades para resolver el reto. Una es probar cada carácter (por ejemplo, para el índice 4) de la flag hasta ver el mensaje "Flag check failed at index: 5"
. Luego, podemos construir la flag carácter a carácter.
Sin embargo, resulta más sencillo invertir el algoritmo de cifrado (que utiliza XOR), ya que sabemos cómo se toma nuestra entrada de datos y cómo se compara con la flag cifrada.
Este es el script que soluciona el reto:
#!/usr/bin/env python3
flag_enc = [
0x98,
0x8b,
0x88,
0xc3,
0x71,
0xb6,
0x7e,
0xa3,
0x72,
0xbb,
0x73,
0x7d,
0x7a,
0xa9,
0x74,
0x73,
0x68,
0xa4,
0xb6,
0x6e,
0x62,
0xbc,
0x61,
0x61,
0x62,
0xb3,
0x67,
0xbc,
0x61,
0x6b,
0xb8,
0xb5,
0x56,
0x54,
0x89,
0x55,
0x8c,
0x50,
0x5b,
0x51,
0x53,
0x54,
0x5d,
0x5e,
0x50,
0x86,
0x89,
0x89,
0x48,
0x4f,
0x49,
0xf1
]
for i, c in enumerate(flag_enc):
print(chr(((c ^ i) - 0x45)), end='')
$ python3 solve.py
SEE{0n3_5m411_573p_81d215e8b81ae10f1c08168207fba396}
El script completo se puede encontrar aquí: solve.py
.