LoginSignup
16
8

More than 1 year has passed since last update.

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

Posted at

はじめに

Python Advent Calendar 2021 25日目:christmas_tree:です。

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

本記事はペネトレーションテストなど、ユーティリティとして使うための方法を踏まえて、ソケット通信の仕組みについて解説しています。

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

ソケット通信の仕組み

ソケットは、コンピュータがOSを介してネットワーク通信を行うために標準化された仕組みです。

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

また、ネットワーク通信を行うためのインターフェースとして、同じコンピュータ上の他のプロセスとも通信(プロセス間通信)を行ったり、リアルタイムなどで通信を行うWeb開発などで使用されています。

OSI参照モデルでは、セッション層の位置付けとして、トランスポート層の構造を決定します。基本的にTCPで使用されるストリームソケットと、UDPで使用されるデータストリームソケットに大別されます。

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

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

ソケットプログラミング

Pythonでソケットプログラミングを行う場合、socketモジュールを使用します。

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

TCPクライアント

  • tcp_client.py
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
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

[*] Listening on 0.0.0.0 80

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

python3 tcp_cleint.py -host localhost -p 80

b'ACK!'

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

[*] 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

おわりに

Pythonは万能ナイフです。

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

参考

16
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
8