Skip to content

ラボ 4.2: Linux からのラテラルムーブメント

オンライン演習

接続: この演習を実施するには、クラス内ネットワーク(560A VPN 経由)に接続している必要があります

目的

  • -L および -D を含む、ラテラルムーブメントのためのさまざまな SSH オプションに習熟する
  • 特に既存のシェルアクセスをアップグレードする際の、SSH 鍵ベース認証について学ぶ
  • ピボットのための proxychains の使用についてよりよく理解する

ウォークスルービデオ

ラボのセットアップ

使用する VM:

  • Linux
  • Windows

Linux VM から 10.130.10.10 に ping できることを確認してください:

コマンド

ping -c 4 10.130.10.10

想定される結果

sec560@560vm:~$ ping -c 4 10.130.10.10
PING 10.130.10.10 (10.130.10.10) 56(84) bytes of data.
64 bytes from 10.130.10.10: icmp_seq=1 ttl=63 time=52.4 ms
64 bytes from 10.130.10.10: icmp_seq=2 ttl=63 time=51.2 ms
64 bytes from 10.130.10.10: icmp_seq=3 ttl=63 time=61.8 ms
64 bytes from 10.130.10.10: icmp_seq=4 ttl=63 time=46.1 ms

--- 10.130.10.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3006ms
rtt min/avg/max/mdev = 46.105/52.867/61.785/5.662 ms

ラボ - 手順

1: ターゲットの特定

このクラスで以前学んだように、10.130.10.21 と 10.130.11.13 システムはどちらも Windows です。これらのホストのどちらが TCP ポート 445 でリッスンしている(かつアクセス可能である)かを確認しましょう。

Nmap を使用して、どのシステムがポート 445 でリッスンしているかを特定します。

コマンド

nmap -n -Pn -p 445 10.130.10.21 10.130.11.13

想定される結果

sec560@560vm:~$ nmap -n -Pn -p 445 10.130.10.21 10.130.11.13
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-21 20:12 UTC
Nmap scan report for 10.130.10.21
Host is up (0.046s latency).

PORT    STATE SERVICE
445/tcp open  microsoft-ds

Nmap scan report for 10.130.11.13
Host is up.

PORT    STATE    SERVICE
445/tcp filtered microsoft-ds

Nmap done: 2 IP addresses (2 hosts up) scanned in 0.49 seconds

10.130.11.13 の TCP ポート 445 にアクセスできないことがわかります。少なくとも VPN 経由での現在の位置からはアクセスできません。

2: 対話型シェルと単一コマンドのための SSH

まず、SSH 自体に慣れましょう。ssh に必要なオプションは、ターゲット(ホスト名または IP)のみです(例: ssh 10.130.10.22)。デフォルトでは、ssh クライアントは現在のローカルユーザーのユーザー名をターゲットのユーザー名として使用します。別のユーザー名(alex など)でログインしたい場合は、ssh -l alex 10.130.10.22 または、より一般的には ssh alex@10.130.10.22 とすることができます。

10.130.10.22 は Linux システムであり、まず SSH 自体についての経験を積んだ後、10.130.11.0/24 サブネットに到達するためのピボットポイントとして使用します。

ユーザーアカウントに既に鍵ベース認証が設定されており、秘密鍵がキャッシュされているか、クライアント側でパスワードを必要としない場合、パスワードを入力する必要はありません。ただし、私たちのユースケースでは、ユーザー名 bgreen には関連するパスワード Password1 があります(これは以前に見たことがあります)。

Linux 仮想マシンからユーザー名 bgreen として 10.130.10.22 に SSH 接続することから始めましょう:

コマンド

ssh bgreen@10.130.10.22

想定される結果

Warning: Permanently added '10.130.10.22' (ECDSA) to the list of known hosts.
bgreen@10.130.10.22's password: 

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

bgreen@jump02:~$ 

注意

この SSH サーバーに初めて接続する場合、既知のホストのリストにサーバーを追加することについての警告が表示されます。これは正常で予想される動作です。プロンプトが表示されたら yes と入力して Enter キーを押してください。

プロンプトが表示されたら、上記のハイライトされた行に示されているように、ユーザー名 bgreen のパスワードとして Password1 を入力します。

次に、このリモートホストについてさらに詳しく調べるために、いくつかのコマンドを実行しましょう:

コマンド

lsb_release -a
id
sudo -l -l

想定される結果

bgreen@jump02:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 24.04 LTS
Release:    24.04
Codename:   noble
bgreen@jump02:~$ id
uid=1000(bgreen) gid=1000(bgreen) groups=1000(bgreen)
bgreen@jump02:~$ sudo -l -l
[sudo] password for bgreen: 
Sorry, user bgreen may not run sudo on jump02.

プロンプトが表示されたら、sudo -l -l コマンドのために Password1 のパスワードを入力します。

新しいユーザーとして Linux マシンにアクセスした際、最初のコマンドの 1 つは通常 sudo -l -l であるべきです。委任された権限でコマンドを実行する代わりに、sudo -l -l はそのユーザーが実行できるコマンドをリストします。表示されたコマンドは、man ページや GTFOBins などのサイトを含め、通常調査する必要があります。

ユーザー bgreen は管理者ではありませんが、それでも利用可能な攻撃面は重要です。jump02 に TCP 接続を行わせることができるため、基本的にはその経由でトラフィックをルーティングできます。残りは単なる詳細に過ぎません。

jump02 システムが TCP ポート 445 で 10.130.11.13 に到達できるかどうかを確認することから始めましょう。VPN 経由ではこのホストに直接到達できないことを思い出してください!

コマンド

nc -v -z 10.130.11.13 445

想定される結果

bgreen@jump02:~$ nc -v -z 10.130.11.13 445
Connection to 10.130.11.13 445 port [tcp/microsoft-ds] succeeded!

すばらしい!これで、jump02 に制限付きユーザー(bgreen)として認証できること、そして jump02 が直接到達できない 10.130.11.0/24 サブネットに到達できることがわかりました。

ここでは、このセッションを exit しましょう:

コマンド

exit

想定される結果

bgreen@jump02:~$ exit
logout
Connection to 10.130.10.22 closed.
sec560@560vm:~$ 

次に、ssh 自体のあまり知られていない機能をいくつか示しましょう。対話型シェルを実行するためによく使用されますが、ssh は通常、宛先マシンで任意のコマンドを開始できます。単一のコマンドを実行することから始めましょう:

コマンド

ssh bgreen@10.130.10.22 id

想定される結果

sec560@560vm:~$ ssh bgreen@10.130.10.22 id
bgreen@10.130.10.22's password: 
uid=1000(bgreen) gid=1000(bgreen) groups=1000(bgreen)
sec560@560vm:~$ 

プロンプトが表示されたら、再度パスワード(Password1)を入力します。ここでは、id コマンドの出力が表示され、実行され、通常の出力が表示されてから終了します。

bgreen のパスワードを何度も何度も入力するよう求められるのは確かにうっとうしいですよね?次はそれを修正しましょう。

まず、SEC560 Linux 仮想マシンに独自の SSH キーペアを設定しましょう:

コマンド

ssh-keygen

想定される結果

sec560@560vm:~$ ssh-keygen
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/sec560/.ssh/id_ed25519): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/sec560/.ssh/id_ed25519
Your public key has been saved in /home/sec560/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:ivREOAd9ldTB1xVYt9c6uOB9buNW3Oa6PoAlXtcgisA sec560@560vm
The key's randomart image is:
+--[ED25519 256]--+
|    .o   oo+..o+=|
|     oE . . +.o =|
|    o oo . . o +o|
|     +  . o o...o|
|    . . S..=..o..|
|   . + . .oo.. .=|
|    . o   . o..+ |
|             o= .|
|             =*= |
+----[SHA256]-----+

各プロンプトで Enter キーを押し、デフォルトを受け入れます。

これで、自由に共有できる公開鍵(/home/sec560/.ssh/id_ed25519.pub)と、通常は秘密にしておく必要がある秘密鍵(/home/sec560/.ssh/id_ed25519)で構成される SSH キーペアができました。

ユーザーの ~/.ssh フォルダに authorized_keys というファイルがある場合、そのプレーンテキストファイルにリストされている公開鍵は、対応する秘密鍵で自動的にログオンできます。つまり、id_rsa.pub の内容を authorized_keys ファイルに追加すると、パスワードプロンプトなしで Linux 仮想マシンに SSH 接続できます!

試してみましょう:

コマンド

cat /home/sec560/.ssh/id_ed25519.pub >> /home/sec560/.ssh/authorized_keys
sudo service ssh start
ssh localhost "echo hello; sleep 1; echo from; sleep 1; echo inside; sleep 1; echo SSH; exit"

想定される結果

sec560@slingshot:~$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
sec560@slingshot:~$ sudo service ssh start
sec560@slingshot:~$ ssh localhost "echo hello; sleep 1; echo from; sleep 1; echo inside; sleep 1; echo SSH; exit"
hello
from
inside
SSH

注意

SSH 経由で Linux VM に接続するのはこれが初めてである可能性が高いため、別のホスト鍵の警告が表示されます。プロンプトが表示されたら yes と入力して Enter キーを押してください。

通常どおりセミコロンでコマンドを連結できますが、; などの特殊文字はローカルシェルによって解釈されないように引用符内で実行することをお勧めします。

ただし、このプロセスを手動で行う代わりに、便利なスクリプト(ssh-copy-id)を使用して、公開鍵を 10.130.10.22 に自動的に追加しましょう。aproposssh-copy-id コマンドの有用な 1 行説明を提供します。

コマンド

apropos ssh-copy-id 
ssh-copy-id bgreen@10.130.10.22

想定される結果

sec560@560vm:~$ apropos ssh-copy-id 
ssh-copy-id (1)      - use locally available keys to authorise logins on a remote machine
sec560@560vm:~$ ssh-copy-id bgreen@10.130.10.22
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
bgreen@10.130.10.22's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'bgreen@10.130.10.22'"
and check to make sure that only the key(s) you wanted were added.

ハイライトされた行でプロンプトが表示されたら、最後に Password1 を入力します。これで SSH キーファイルが配置され、SSH を使用するユーティリティではパスワードプロンプトなしで自動的にログオンできます!

まず、単一のコマンドとして ip -br a を非対話的に実行して、パスワードプロンプトなしでログインできることを検証しましょう:

コマンド

ssh bgreen@10.130.10.22 "ip -br a"

想定される結果

sec560@560vm:~$ ssh bgreen@10.130.10.22 "ip -br a"
lo               UNKNOWN        127.0.0.1/8 ::1/128 
ens3             UP             10.130.10.22/24 metric 100 fe80::454:e0ff:fe83:1249/64 
docker0          DOWN           172.17.0.1/16

次に、scp(これも鍵ベース認証を使用します)でこれをテストして、/etc/passwd をコピーしてみましょう:

コマンド

scp bgreen@10.130.10.22:/etc/passwd /tmp/10.130.10.22_passwd.txt

想定される結果

sec560@560vm:~$ scp bgreen@10.130.10.22:/etc/passwd /tmp/10.130.10.22_passwd.txt
passwd                                             100% 1893    17.1KB/s   00:00  

すばらしい!bgreen のパスワードをさらに要求されることなく、そのファイルをコピーできました。

3: ピボットのための SSH(単一ポートリダイレクト)

次に、ピボットのために SSH 自体を使用しましょう。-L を使用した**ローカル**ポートリダイレクトから始めます。SSH クライアント(私たちの Linux VM)は新しいポートでリッスンし、SSH サーバーの観点から宛先サーバーとポートに SSH サーバーによってルーティングされます。

では、SSH トンネルをセットアップしましょう。前の手順からパスワードなし認証がまだ設定されているため、これはすぐに完了するはずです。

コマンド

ssh -L 7777:10.130.11.13:445 bgreen@10.130.10.22

想定される結果

sec560@560vm:~$ ssh -L 7777:10.130.11.13:445 bgreen@10.130.10.22
Last login: Wed Aug 21 20:13:17 2024 from 10.254.252.3
bgreen@jump02:~$ 

情報

Last login: の後の出力は異なります。

SSH 経由でのローカルポートリダイレクトの完全な構文は -L local_ip:local_port:server_ip:server_port で、次のように分解されます:

  • local_ip: SSH クライアントでリッスンする IP アドレス(空白のままにすると 127.0.0.1 がデフォルト)
  • local_port: SSH セッションが開いている間、SSH クライアントでリッスンする TCP ポート
  • server_ip: SSH サーバーが接続をトンネリングする IP アドレス
  • server_port: SSH サーバーが接続をトンネリングする TCP ポート

server_ipserver_port が SSH サーバー自体で実行されているサービスであることは一般的ですが、必ずしもそうである必要はありません。私たちの場合、10.130.11.13 は SSH サーバー(10.130.10.22)_経由で_アクセス可能なリモートマシンです。

bgreen ユーザーは Linux システム上の特権アカウントではありません。これにより、この攻撃はさらに素晴らしいものになります。

Warning

リダイレクトされたポートを利用可能に保つためには、SSH クライアントを開いたままにする必要があります。10.130.10.22 での SSH セッションを**終了せず**、次の手順のために新しいターミナルウィンドウまたはタブを開いてください。

次の手順のために新しいターミナルウィンドウを開いてください!

SSH セッションがアクティブである限り、ローカルポート 7777 はリッスンしています。これを確認しましょう:

コマンド

ss -tlnp

想定される結果

sec560@560vm:~$ ss -tlnp
State  Recv-Q   Send-Q      Local Address:Port     Peer Address:Port                                   
LISTEN 0        128             127.0.0.1:7777          0.0.0.0:*      users:(("ssh",pid=13442,fd=5))  
LISTEN 0        128         127.0.0.53%lo:53            0.0.0.0:*                                      
LISTEN 0        128               0.0.0.0:22            0.0.0.0:*                                      
LISTEN 0        128                     *:80                  *:*                                      
LISTEN 0        128                  [::]:22               [::]:*  

ss 出力では、SSH プロセスがローカル TCP ポート 7777 でリッスンしていることがわかります。SSH は既存の SSH セッションと同じ暗号化を使用してそのトラフィックをトンネリングし、宛先マシン(10.130.11.13)は SSH サーバー(10.130.10.22)から来るトラフィックとして認識します。

間もなく Impacket についてさらに詳しく説明しますが、今のところは 1 つのユーティリティを使用して 10.130.11.13 ホストに関する情報を表示します:

コマンド

DumpNTLMInfo.py localhost -port 7777

想定される結果

sec560@560vm:~$ DumpNTLMInfo.py localhost -port 7777
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Defaulting to SMB protocol.
[*] Using target: localhost, IP: localhost, Port: 7777, Protocol: SMB
[+] SMBv1 Enabled   : False
[+] Prefered Dialect: SMB 3.0
[+] Server Security : SIGNING_ENABLED (not required)
[+] Max Read Size   : 8.0 MB (8388608 bytes)
[+] Max Write Size  : 8.0 MB (8388608 bytes)
[+] Current Time    : 2024-09-26 18:57:41.439910+00:00
[+] Name            : SQL02
[+] Domain          : HIBOXY
[+] DNS Domain Name : hiboxy.com
[+] DNS Host Name   : sql02.hiboxy.com
[+] OS              : Windows NT 10.0 Build 20348
[+] Null Session    : False

ここでは、SSH がローカルのリスニングポート 7777 を sql02.hiboxy.com の SMB サーバーにトンネリングしていることがわかります。認証なしでも、最新の SMB サーバーから多くの情報を取得できることは印象的です!

ssh -L はおそらく理解しやすいですが、SSH サーバーが認識できる_単一_のサービスをリダイレクトします。複数のサービスをリダイレクトするために -L を複数回指定できますが、すぐに面倒になります。代わりに、次は ssh -D に焦点を当てて、SSH サーバーを通じて_任意の_トラフィックをリダイレクトするプロキシを作成します。

jump02 での SSH セッションを exit してください:

コマンド

exit

想定される結果

bgreen@jump02:~$ exit
logout
Connection to 10.130.10.22 closed.

4: ピボットのための SSH(動的 SOCKS プロキシ)

ssh -DDynamic SOCKS プロキシで、本質的には VPN です。VPN サービス(SOCKS バージョン 5)は SSH クライアントでリッスンしますが、SOCKS プロキシサービスを使用すると SSH サーバーの観点からブラウズします。

構文は -L よりもシンプルで、ポート番号のみが必要です:

コマンド

ssh bgreen@10.130.10.22 -D 1080

想定される結果

sec560@560vm:~$ ssh bgreen@10.130.10.22 -D 1080
Last login: Wed Aug 21 20:24:09 2024 from 10.254.252.3
bgreen@jump02:~$ 

情報

Last login: の後の出力は異なります。

Warning

リダイレクトされたポートを利用可能に保つためには、SSH クライアントを開いたままにする必要があります。10.130.10.22 での SSH セッションを**終了せず**、次の手順のために新しいターミナルウィンドウまたはタブを開いてください。

次の手順のために新しいターミナルウィンドウを開いてください!

前と同様に、新しいターミナルウィンドウで新しいリスニングポートを確認します:

コマンド

ss -tlnp | grep 1080

想定される結果

sec560@560vm:~$ ss -tlnp | grep 1080
LISTEN 0      128        127.0.0.1:1080       0.0.0.0:*    users:(("ssh",pid=68249,fd=5))
LISTEN 0      128            [::1]:1080          [::]:*    users:(("ssh",pid=68249,fd=4))  

一部のツールは、curl --socks5 127.0.0.1:1080Firefox のように、SOCKS プロキシを直接使用するように設定できます。一部のツールは connect() システムコールを使用し、これは proxychains ユーティリティがすぐに使用するようにインターセプトできます。一部のツールは独自の生のトラフィックを送信し、一般的にプロキシを通して強制するのは困難です。例えば、masscanSYN ステルススキャンを実行する際の Nmap などです。

ここでは、proxychains を使用した場合と使用しない場合の Nmap スキャンの結果を比較しましょう。

まず、10.130.11.13 に直接到達できないことを確認するために、まず proxychains を使用せずに Nmap を実行しましょう:

コマンド

nmap -sT 10.130.11.13 -p 445,1433 -Pn -n -oG - | grep Ports:

想定される結果

sec560@560vm:~$ nmap -sT 10.130.11.13 -p 445,1433 -Pn -n -oG - | grep Ports:
Host: 10.130.11.13 ()   Ports: 445/filtered/tcp//microsoft-ds///, 1433/filtered/tcp//ms-sql-s///

次に、Nmap が connect() システムコールを使用しない(代わりに生のパケットを送信する)ため、proxychains が SYN ステルススキャンからの connect() システムコールをインターセプトできないことを示します:

コマンド

sudo proxychains nmap -sS 10.130.11.13 -p 445,1433 -Pn -n -oG - | grep Ports:

想定される結果

sec560@560vm:~$ sudo proxychains nmap -sS 10.130.11.13 -p 445,1433 -Pn -n -oG - | grep Ports:
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/local/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 4.17
Host: 10.130.11.13 ()   Ports: 445/filtered/tcp//microsoft-ds///, 1433/filtered/tcp//ms-sql-s///

ここでは、インターセプトされたトラフィックを示す proxychains からの [proxychains] 行(出力に strict chain が含まれる)は表示されません。代わりに Nmap はトラフィックを直接送信し(プロキシをバイパス)、結果は依然としてフィルタリングされています。

次に、proxychains 設定(proxychains がローカルポート 1080 の SOCKS プロキシを通じて connect() システムコールをプッシュする場所)を確認し、次に proxychains-sT を使用した connect() システムコールで Nmap を使用します:

コマンド

tail -n 2 /etc/proxychains.conf
proxychains nmap -sT 10.130.11.13 -p 445,1433 -Pn -n -oG - | grep Ports:

想定される結果

sec560@560vm:~$ tail -n 2 /etc/proxychains.conf
proxychains nmap -sT 10.130.11.13 -p 445,1433 -Pn -n -oG - | grep Ports:
socks4 127.0.0.1 1080

[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/local/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain  ...  127.0.0.1:1080  ...  10.130.11.13:445  ...  OK
[proxychains] Strict chain  ...  127.0.0.1:1080  ...  10.130.11.13:1433  ...  OK
Host: 10.130.11.13 ()   Ports: 445/open/tcp//microsoft-ds///, 1433/open/tcp//ms-sql-s/

Nmap からの各接続は proxychains からステータス行([proxychains] で始まる)を取得することに注意してください。

重要なことに、10.130.11.13 のポート 445 と 1433 が現在開いていることもわかります!

SOCKS プロキシでできることは他にもたくさんあります。ほとんどのエンドポイント防御は、疑わしくないバイナリのネットワークトラフィックをインターセプトするように設定されていません。SSH 自体(信頼されたバイナリ)を使用することで、アウトバウンドファイアウォールが SSH 制御トラフィックを許可する限り、プロキシされたトラフィックは一般的に疑わしいとは見なされません。

ほとんどの組織はエンドポイント防御(AV、EDR、XDR など)に依存しており、ネットワークトラフィックの監視にはあまり労力をかけていません。エンドポイント防御ソフトウェアのないシステムから操作できる場合、それらの組織が最初のアラートを取得することさえ困難です。

ただし、ここでは SSH セッションを閉じてラボを終了しましょう。

コマンド

killall ssh

想定される結果

sec560@slingshot:~$ killall ssh

まとめ

ご覧のとおり、SSH は防御者や管理者にとって優れているだけでなく、攻撃者にとっても優れています!クラス全体を通じて SSH を使い続けます。