はじめに
前回はサーバーを自作し、Proxmox を導入して仮想化しました。
seekt.hatenablog.com
今回は、遊ぶための環境をサーバー上に作ったのでそのメモを残したいと思います。
今回作った環境
こんな環境を作りました。上段がホームネットワーク上の物理マシン (Windows10) で、中段と下段が Proxmox 上のVMです。
の4つのVMを Proxmox 上に立てて動かしています。
用途・使い方
用途・使い方はこんな感じです。
- クライアントからサーバに TCP または UDP の通信をする
- 攻撃端末は通信の盗聴・改ざんをする
- Windowsからクライアントおよびサーバを動かせるようにする
- 踏み台サーバでクライアントおよびサーバの負荷監視を行えるようにする
ここでは、それぞれのVMの設定について簡単にまとめます。
ParrotOS
ParrotOS は、攻撃端末として使います。基本的にGUIで運用しますが、WindowsとSSH接続できるようにします。
インストール
公式サイト から ISO イメージをダウンロードして設定します。ここでは、Security Edition の ISO イメージを使っています。
VMのスペックはこんな感じにしています。
初期設定
sudo apt update
sudo apt -y upgrade
realtek-rtl8188-dkms
でエラーが出るので、エラーの解決をします。
community.parrotsec.org
sudo apt purge realtek-rtl8188eus-dkms
sudo parrot-upgrade
sudo apt autoremove
sudo parrot-upgrade
SSHを有効にします。
sudo apt install ssh
sudo systemctl start ssh
sudo systemctl enable ssh
必要なパッケージのインストール
通信の盗聴や改ざんには Python の Scapy と NetfilterQueue を使うので、必要なパッケージをインストールします。
パッケージのインストール
sudo apt install python3-venv
mkdir .venvs
python3 -m venv ~/.venvs/venv39
source ~/.venvs/venv39/bin/activate
(venv39) pip install --upgrade pip
(venv39) pip install scapy NetfilterQueue
テスト
(venv39) python
>>> import scapy
>>> from netfilterqueue import NetfilterQueue
(Ctrl + D)
(venv39) deactivate
もし NetfilterQueue でうまくいかなかったら、以下のコマンドを実行してもう一度試す。
sudo apt install build-essential python-dev python3-dev libnetfilter-queue-dev
アップデートの自動化
crontab を用います。具体的には、/etc/crontab
に以下を追記します。
00 3 * * * root apt update && apt -y upgrade && apt -y autoremove
毎日朝3時に root
権限でアップデートが行われる1。
インストール
公式サイト から ISO イメージをダウンロードして設定します。ここでは、Ubuntu 22.04 を使っています。
VMのスペックはこんな感じにしています。
初期設定
sudo apt update
sudo apt -y upgrade
SSHを有効にします。
sudo apt install ssh
sudo systemctl start ssh
sudo systemctl enable ssh
GUI は負荷が大きいので、CUI で運用することにする。
sudo systemctl set-default multi-user.target
reboot
アップデートの自動化
Parrot と同じ方法でアップデートを自動化する。
Fedora (Client, Server)
インストール
公式サイト から ISO イメージをダウンロードして設定します。ここでは、Fedora Server をダウンロードしました。
VMのスペック・設定はこんな感じにしています。
- CPU
- Memory
- Storage
- インストール時は
192.168.0.0/24
のみ接続し、インストール後に 192.168.50.0/24
を追加する。
- root ユーザを作る
初期設定
yum update
nmtui
をインストールする
yum install NetworkManager-tui
nmtui
で 192.168.50.0/24
のIP を固定 (下図は server の例)。
また、192.168.0.0/24
のネットワーク接続を切っておきます (nmtui
で deactivate する)。
クライアント - サーバ間の通信
www.oreilly.co.jp
この本を参考に作っています。
UDP のクライアントとサーバを作りました。
UDP クライアント
クライアント側で動かすプログラムです。
import socket
MESSAGE_SIZE = 1024
dstip = '192.168.50.3'
dstport = 12590
dst_addr = (dstip, dstport)
udp_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_client.sendto(b'ABCDEF', dst_addr)
udp_client.close()
サーバ側で動かすプログラムです。
import socket
MESSAGE_SIZE = 1024
host = '192.168.50.3'
port = 12590
addr = (host, port)
def main():
sock = socket.socket(socket.AF_INET, type=socket.SOCK_DGRAM)
sock.bind(addr)
print(f'[*] Listening on {host}:{port}')
while True:
try:
rcv_data, client_addr = sock.recvfrom(MESSAGE_SIZE)
print(f'[*] Received: {rcv_data.decode(encoding="utf-8")} from {client_addr[0]}:{client_addr[1]}')
print('\n')
except KeyboardInterrupt:
print(f'[*] Finished')
sock.close()
break
if __name__ == "__main__":
main()
TCP のクライアントとサーバを作りました。
TCP クライアント
クライアント側で動かすプログラムです。
import socket
MESSAGE_SIZE = 1024
host = '192.168.50.3'
port = 12600
addr = (host, port)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(addr)
client.send(b'ZYXWVU')
response = client.recv(MESSAGE_SIZE)
print(response.decode())
client.close()
サーバ側で動かすプログラムです。
import socket
import threading
MESSAGE_SIZE = 1024
host = '192.168.50.3'
port = 12600
addr = (host, port)
def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(addr)
server.listen(5)
print(f'[*] Listening on {host}:{port}')
while True:
try:
client, address = server.accept()
print(f'[*] Accepted connection from {address[0]}:{address[1]}')
client_handler = threading.Thread(target=handle_client, args=(client,))
client_handler.start()
except KeyboardInterrupt:
print(f'[*] Finished')
break
def handle_client(client_socket):
with client_socket as sock:
request = sock.recv(MESSAGE_SIZE)
print(f'[*] Received: {request.decode("utf-8")}')
print('\n')
sock.send(b'ACK')
if __name__ == "__main__":
main()
通信のテスト
通信を行う前に、サーバ側のポートを開けます。今回は、12590/udp
と 12600/tcp
を開けます。
サーバ側 (192.168.50.3
) に root
でログインして、以下コマンドでポートを開けます。
# firewall-cmd --add-port=12590/udp
# firewall-cmd --add-port=12600/tcp
youtu.be
通信を実行している様子を動画にしました。
サーバ側 (右側) のプログラムを実行してからクライアント側 (左側) のプログラムを実行すると、サーバ側がメッセージを受け取る様子が見て取れます。
通信を定期的に行う
サーバを起動した状態で、クライアントのプログラムを数秒おきに実行すると、定期的な通信を模擬できます。
例えば、5秒おきに UDP の通信をしたいときは、
watch -n 5 python3 udp_client_sample.py
とすれば良いです。
負荷監視ログの取得
踏み台からクライアントとサーバの負荷監視ログを取得しようと思います。今回は、毎秒の vmstat
のログを1時間とるようにしました。
こんなスクリプトを書けば実現できます。
サーバのログを取得するスクリプト (obtain_log_server.sh
)
#!/bin/bash
HOST=192.168.50.3
USER=guest
ssh ${USER}@${HOST} < log_command.sh > ./log/log_server_`date "+%Y%m%d%H%M%S"`.vmstat
クライアントのログを取得するスクリプト (obtain_log_client.sh
)
#!/bin/bash
HOST=192.168.50.4
USER=guest
ssh ${USER}@${HOST} < log_command.sh > ./log/log_client_`date "+%Y%m%d%H%M%S"`.vmstat
共通で使うスクリプト (log_command.sh
, 上2つのスクリプトと同じディレクトリに置く)
#/bin/bash
vmstat -n -t 1 3600
./obtain_log_server.sh
でサーバの、./obtain_log_client.sh
でクライアントのログが取得できます。
やっていることは、SSH 接続先で log_command.sh
を実行して、踏み台の log
ディレクトリに log_client_YYYYmmddHHMMSS.vmstat
として保存するということです。
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- -----timestamp-----
r b swpd free buff cache si so bi bo in cs us sy id wa st JST
0 0 0 1478852 1668 313252 0 0 3 1 41 42 0 0 99 0 0 2022-09-04 11:17:21
0 0 0 1478852 1668 313252 0 0 0 0 120 116 0 0 100 0 0 2022-09-04 11:17:22
0 0 0 1478852 1668 313252 0 0 0 0 102 104 0 0 99 0 0 2022-09-04 11:17:23
0 0 0 1478852 1668 313252 0 0 0 0 95 96 0 1 99 0 0 2022-09-04 11:17:24
0 0 0 1478852 1668 313252 0 0 0 20 103 106 0 1 99 0 0 2022-09-04 11:17:25
0 0 0 1478852 1668 313252 0 0 0 0 92 92 0 1 100 0 0 2022-09-04 11:17:26
こんな感じのログが取れます。
まとめ
ここでは、
- 遊ぶための環境について
- クライアント - サーバ間の通信について
- 踏み台サーバからのクライアントおよびサーバの負荷監視について
書きました。
時間があるときに通信の盗聴とか改ざんとかもこの環境で試そうと思っています。