how do i redirect fifo to stdin using python either with subprocess or with pwntools?

Viewed 259

As an example I am trying to "imitate" the behaviour of the following sets of commands is bash:

mkfifo named_pipe /challenge/embryoio_level103 < named_pipe & cat > named_pipe

In Python I have tried the following commands:

import os
import subprocess as sp

os.mkfifo("named_pipe",0777) #equivalent to mkfifo in bash..
fw = open("named_pipe",'w')

#at this point the system hangs...

My idea it was to use subprocess.Popen and redirect stdout to fw... next open named_pipe for reading and giving it as input to cat (still using Popen).

I know it is a simple (and rather stupid) example, but I can't manage to make it work..

How would you implement such simple scenario?

1 Answers

Hello fellow pwn college user! I just solved this level :)

open(path, flags) blocks execution. There are many similar stackoverflow Q&As, but I'll reiterate here. A pipe will not pass data until both ends are opened, which is why the process hangs (only 1 end was opened).

If you want to open without blocking, you may do so on certain operating systems (Unix works, Windows doesn't as far as I'm aware) using os.open with the flag os.O_NONBLOCK. I don't know what consequences there are, but be cautious of opening with nonblocking because you may try reading prematurely and there will be nothing to read (possibly leading to error, etc.).

Also, note that using the integer literal 0777 causes a syntax error, so I assume you mean 0o777 (max permissions), where the preceding 0o indicates octal. The default for os.mkfifo is 0o666, which is identical to 0o777 except for the execute flags, which are useless because pipes cannot be executed. Also, be aware that these permissions might not all be granted and when trying to set to 0o666, the permissions may actually be 0o644 (like in my case). I believe this is due to the umask, which can be changed and is used simply for security purposes, but more info can be found elsewhere.

For the blocking case, you can use the package multiprocessing like so:

import os
import subprocess as sp
from multiprocessing import Process

path='named_pipe'
os.mkfifo(path)

def read(): sp.run("cat", stdin=open(path, "r"))
def write(): sp.run(["echo", "hello world"], stdout=open(path, "w"))

if __name__ == "__main__":
    p_read = Process(target=read)
    p_write = Process(target=write)

    p_read.start()
    p_write.start()

    p_read.join()
    p_write.join()

    os.remove(path)

output:

hello world
Related