Grab some popcorn 'cuz we're here to talk about curl-bash pipelines on Linux. Personally I don't like them for maintenance reasons (how do you remove things you installed like this?), and I want to nag my fellows to not casually run a wall-of-script from the Internet.
Interactive vs non-interactive shells
When a bash shell is started at login it's considered interactive and expects to be attached to a TTY. When we run curl https://example.com/script.sh | bash
the bash process started is non-interactive and expects no human input. We can confirm a bash process' interactivity either by looking at the process table (run ps
) or by checking the builtin variable $-
(the shell's starting arguments) for an 'i' character.
PIDs, Parents and "Siblings"
Part of the classic Unix process model is that a process has a parent that created it and may have child processes that it created. For the purpose of this exercise let's call processes that were created by the same immediate parent process "siblings".
We can find the current PID's parent with ps -p $$ -o ppid -h
. Running a wide-ranging ps
and grepping for a common parent PID will show us all processes started by the same parent PID as our shell.
Seeing connections in procfs
Now that we have a list of all a PID's siblings we can cat-out procfs to see if any of those processes are connected to a known HTTP port.
$ cat /proc/21456/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:2328 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 264000 1 0000000000000000 100 0 0 10 0
1: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 264692 1 0000000000000000 100 0 0 10 0
2: 0100007F:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 48214 1 0000000000000000 100 0 0 10 0
3: F71CFE0A:0016 224EFBCF:FBC3 01 00000034:00000000 01:00000015 00000000 0 0 67266130 3 0000000000000000 21 4 17 10 54
4: F71CFE0A:9EE0 856EC7B9:01BB 08 00000000:00000001 02:000012E3 00000000 150001 1 67376561 2 0000000000000000 20 4 0 10 -1
5: F71CFE0A:88D8 0200FE0A:0035 06 00000000:00000000 03:0000028A 00000000 0 0 0 3 0000000000000000
6: F71CFE0A:89C8 0200FE0A:0035 06 00000000:00000000 03:00001073 00000000 0 0 0 3 0000000000000000
7: F71CFE0A:960E 98A27032:01BB 01 00000000:00000000 02:000008BC 00000000 0 0 67384497 2 0000000000000000 26 4 6 10 -1
8: F71CFE0A:DC02 71529D36:270D 01 00000000:00000000 00:00000000 00000000 0 0 67343092 1 0000000000000000 20 4 1 10 -1
This looks pretty-intense but we're only interested in the last 4 characters (TCP port) of the third column (remote host). These values are all in sigh hex, so it will be fastest to just grep for a few well-known ports like 0050
(80), 01BB
(443), 1F90
(8080 and 20FB
(8443)
Putting it all together
Now that we can detect non-interactive shell's children connecting to a well-known web port and suspend their process how do we get this functionality deployed? Let's save our final shell script as /usr/local/bin/blocker.sh:
if [[ $(grep "$(cat /proc/$$/cmdline | tr '\000' "\n" | tail -1)$" /etc/shells) ]]
then
echo $- | grep -qv i && ps awwwx -ocmd,pid,ppid | grep "$(ps -p $$ -o ppid -h)$" | while read p
do
sib=$(echo "$p" | awk '{ print $(NF-1) }')
egrep -q ":0050|:01BB|:1F90|:20FB" /proc/$sib/net/tcp 2>/dev/null && kill -SIGSTOP $sib 2>/dev/null &&\
echo "If you ๐ง๐๐๐ก๐ก๐ฎ want to execute some rando script from the Internet type ctrl-Z and then ๐ณ๐ด"
done | uniq
fi
and set $BASH_ENV=/usr/local/bin/blocker.sh
to execute our script at startup of every non-interactive bash shell.
As an example, here's this script pausing an install of Omnitruck
; curl -s https://omnitruck.chef.io/install.sh | bash
If you ๐ง๐๐๐ก๐ก๐ฎ want to execute some rando script from the Internet type ctrl-Z and then ๐ณ๐ด
[1]+ Stopped curl -s https://omnitruck.chef.io/install.sh | bash
Top comments (0)