It is a dynamically linked ELF 64-bit executable. Now, let’s look at its protections before we actually start.

1

2

3

4

5

6

7

$checksec./srnr

[*]"/mnt/hgfs/ctf/Redpwn/Pwn/Stop, ROP, n', Roll/srnr"

Arch:amd64-64-little

RELRO:Full RELRO

Stack:No canary found

NX:NX enabled

PIE:No PIE(0x400000)

The stack is not executable.

Let’s decompile its main function to understand the executable.

1

2

3

4

5

6

7

8

9

10

11

12

13

int__cdecl main(intargc,constchar**argv,constchar**envp)

{

charbuf;// [sp+7h] [bp-9h]@1

intfd[2];// [sp+8h] [bp-8h]@1

setbuf(stdout,0LL);

setbuf(stdin,0LL);

setbuf(stderr,0LL);

printf("[#] number of bytes: ",0LL);

*(_QWORD*)fd=get_int();

read(fd[0],&buf,0x186A0uLL);

return0;

}

It actually allows us to select the file descriptor parameter for the following read function call. We can simply send 0 as the file descriptor to make it read input from stdin.

Since it reads up to 0x1860 bytes and the buffer is located at bp-9h, there is a buffer overflow issue where we can overwrite the return address after 17 bytes of padding.

In order to be able to call execve(“/bin/sh”, NULL, NULL), we need to set rdi to the address of a “/bin/sh” string and clear both rsi and rdx registers. Luckily, the binary already contains the “/bin/sh” string so that we don’t need to inject it.

Since r14 and r15 are already zero, this gadget actually clears rdx and rsi for us. If we look at the register values, we see that rdi is already zero. Thus, we can use r13 to set a 32-bit address to rdi. We will use it to assign “/bin//sh” string’s address to rdi. Also, notice that rbx was already zero which means we can call arbitrary functions by controlling r12.

I have found the following gadget to modify r12 and r13:

1

0x000000000040081c:pop r12;pop r13;pop r14;pop r15;ret

However, we need a pointer of an address to assign to r12. I found the following string in the binary to overcome this issue:

1

.rodata:0000000000400C52aZudb'%zu',0

This string is actually used by get_int function which calls __isoc99_scanf to read an integer. We can simply use this to write an address to somewhere in the .data section or in the .bss section.

As you can see above, the address 0x602000 is writable. We need to set rdi to format string and set rsi to our buffer. We can use the following gadgets for this purpose:

1

2

0x0000000000400823:poprdi; ret

0x0000000000400821:poprsi; pop r15 ; ret

The only thing left is to find a way to set rax to 59. However, I couldn’t find any gadget for this purpose, but we know that get_int reads an integer from stdin and returns it. Therefore, we can call it to modify rax.