Pythonで学ぶサイバーセキュリティプログラミング

# はじめに
Python Advent Calendar 2021 25日目:christmas_tree:です。

機械学習でデファクトスタンダードとなっているPythonは、セキュリティとも相性が良くスクリプトなどで使用されてハッカー達に好まれています。

本記事は[ペネトレーションテスト](https://ja.wikipedia.org/wiki/%E3%83%9A%E3%83%8D%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%86%E3%82%B9%E3%83%88)など、ユーティリティとして使うための方法を踏まえて、ソケット通信の仕組みについて解説しています。

Pythonのバージョンは3.9.4を使用。

## ソケット通信の仕組み
[ソケット](https://ja.wikipedia.org/wiki/%E3%82%BD%E3%82%B1%E3%83%83%E3%83%88_(BSD))は、コンピュータがOSを介してネットワーク通信を行うために標準化された仕組みです。

ソケットの仕組みを用いてTCP/IPの通信を行い、HTTPなど上位のアプリケーションプロトコルを使用することができます。

また、ネットワーク通信を行うためのインターフェースとして、同じコンピュータ上の他のプロセスとも通信[（プロセス間通信）](https://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E9%96%93%E9%80%9A%E4%BF%A1)を行ったり、リアルタイムなどで通信を行うWeb開発などで使用されています。

[OSI参照モデル](https://ja.wikipedia.org/wiki/OSI%E5%8F%82%E7%85%A7%E3%83%A2%E3%83%87%E3%83%AB)では、セッション層の位置付けとして、トランスポート層の構造を決定します。基本的にTCPで使用されるストリームソケットと、UDPで使用されるデータストリームソケットに大別されます。

なお、rawソケットの場合、ICMPを制御することができます。OSによって、どのソケットタイプをカバーしているか異なるため、低レイヤのソケットプラグラミングを実装する場合は、OSの仕様を確認した方が良いでしょう。

悪意のあるアプリケーションの場合、rawソケットを使用して、IPアドレスの偽装などトリッキーなネットワーク通信を行うこともできます。

## ソケットプログラミング
Pythonでソケットプログラミングを行う場合、[socket](https://docs.python.org/ja/3/library/socket.html)モジュールを使用します。

以下にソケットの仕組みを用いて、クライアントとサーバでプログラムを実行し、通信の仕組みを理解します。

### TCPクライアント

- tcp_client.py

```python
import argparse
import socket

def main():
    target_host = get_args()
    server_connect(target_host)

def get_args():
    parser = argparse.ArgumentParser() 
    parser.add_argument('-host', help='target host')
    parser.add_argument('-p', type=int, help='target port')
    args = parser.parse_args()

    return args

def server_connect(host):
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((host.host, host.p))
    client.send(b"GET / HTTP/1.1\r\nHost: host.host\r\n\r\n")
    response = client.recv(4096)
    
    print(response)

if __name__ == '__main__':
    main()
```

### TCPサーバ

- tcp_server.py


```python
import argparse
import socket
import threading

def main():
    local_host = get_args()
    server_start(local_host)

def get_args():
    parser = argparse.ArgumentParser() 
    parser.add_argument('-i', help='bind ip')
    parser.add_argument('-p', type=int, help='bind port')
    args = parser.parse_args()

    return args

def server_start(host):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((host.i, host.p))
    server.listen(5)

    print(f"[*] Listening on {host.i} {host.p}") 

    while True:
        client,addr = server.accept()
        
        print(f"[*] Accepted connection from: {addr[0]} {addr[1]}") 

        client_handler = threading.Thread(target=handle_client, args=(client,))
        client_handler.start()
    
def handle_client(client_socket):
    request = client_socket.recv(1024)

    print(f"[*] Received: {request}")

    client_socket.send(b"ACK!")
    client_socket.close() 

if __name__ == '__main__':
    main()
```

## ソケット通信の検証
上記、プログラムを実行して、ソケット通信を確認します。

ターミナルなどを起動し、サーバとなる`tcp_server.py`を実行します。
以下のコマンド実行後、コンソールに80番ポートが起動した出力が確認できます。

`# python3 tcp_server.py -i 0.0.0.0 -p 80`

```console
[*] Listening on 0.0.0.0 80
```

次に、クライアントとなる`tcp_client.py`を実行します。
以下のコマンド実行後、サーバ側からレスポンスとして、ACK!の文字列が帰ってきます。

`python3 tcp_cleint.py -host localhost -p 80`

```console
b'ACK!'
```

再度、サーバ側のコンソールを見ると、クライアントの56855ポートから、GETメソッドのHTTPリクエストが行われていることが確認できます。

```console
[*] Listening on 0.0.0.0 80
[*] Accepted connection from: 127.0.0.1 56855
[*] Received: b'GET / HTTP/1.1\r\nHost: host.host\r\n\r\n'
```

Wiresharkで通信をキャプチャして見てみると、サーバ側のレスポンスはHTTPではなく、TCPプロトコルを用いてデータを返していることが確認できます。

![スクリーンショット 2021-12-14 0.19.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/d9ca92c6-381a-e9c8-50b6-5b90ae234131.png)

# おわりに
Pythonは万能ナイフです。

netcatがインストールされれていない環境にて、Pythonを用いて代用することができます。

## 参考
- [ソケットの概念について](https://www.ibm.com/docs/ja/zos/2.3.0?topic=concepts-understanding-sockets)
- [ソケットの仕組み](https://www.ibm.com/docs/ja/i/7.3?topic=programming-how-sockets-work)
- [ソケットの基本的な使用](https://docs.oracle.com/cd/E19455-01/806-2730/6jbu725kj/index.html)