[Python] Pythonとセキュリティ - ②Pythonで作るポートスキャニングツール

#はじめに
前回([[Python] Pythonとセキュリティ - ②Pythonで作るポートスキャニングツール](https://qiita.com/CyberFortress/items/0c18f0d773ad51ba9b78))でPythonの概要や特徴について調べてみた。
今回はPythonを利用した簡単なポートスキャニングツールを作ってみよう。
また、ツールを利用することで不用意に動作しているサービスを確認して対応することが出来る。

**情報を収集する行為である「Banner Grabbing」になるため、許可を得ていない対象に実施するのは犯罪です。当該の記事で問題が発生した場合、弊社では一切責任を負い兼ねますのでご了承ください。**

#Port Scan(ポートスキャン)とは？
ポートスキャンは対象のサーバーもしくはネットワーク機器などに対してオープンしているポートを検索し、識別することである。オープンしているポートを確認することで、対象から起動しているサービスの確認ができ、攻撃者にとってはポートスキャンが攻撃のための事前準備とも言える。

**Port Scanの種類は以下のものがある**

- UDP Port Scan
- TCP Port Scan
- Connect Scan
    - Half-open(SYN) Scan
    - FIN / NULL / Xmas Scan

#Port Scan種類別特徴
##UDP Port Scan
UDP Scanの場合は、UDPプロトコルを利用してポートをスキャンする。ポートがオープンされている場合、対象からは応答はないが、クローズされている場合は、ICMPメッセージ(Destination Unreachable, Port Unreachable)の応答がある。但し、UDP Scanはルータやファイアウォールからパケットが損失される可能性が高いため、信頼性は低い。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570011/b5ce0ad8-0bfc-ff99-328c-d76c3302f050.png)

##TCP Connect Scan(TCP Open Scan)
connect()関数を利用して、対象と3 Way-Handshakingを結び、オープンされているポートを確認するスキャン。信頼性が高くて、rootと権限がなくてもスキャンを行えるが、スキャンの速度が遅く、ログが残る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570011/230aef4d-799b-f70e-5510-0e87d78c2fd0.png)

##TCP Half-Open Scan(SYN Stealth Scan)
SYNスキャンとも呼ばれるHalf-Open Scanは、TCP Connect Scanとは違って、3 Way-Handshakingで完全なセッションは結ばず、SYNパケットのみ利用してポートを確認する。ログは残らないが、実施のため、root権限が必要である。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570011/809f0be7-7d0e-311e-a222-41a8907be85a.png)

>**root権限が必要な理由?**
TCPプロトコルのヘッダーの制御ビットの設定が必要なため、root権限じゃないとSYNスキャンは実行できない。
>**ログが残らない理由?**
対象からSYN/ACKの応答を受信したら、応答としてACKではなく、RSTに設定したパケットを送信する。
RSTのパケットのため、通信が強制に終了されてしまい、通信設定が最後まで(セッションを結ぶ)行わなかったため、システムにログが残らない可能性が高い。

##FIN/NULL/Xmas Scan
三つのスキャンはFlagをFINに設定するTCP FIN Scan, 何も設定しないNULL Scan, FIN, PSH, HUGを同時に設定するXmas Scanがある。
それぞれ正常な通信ではないため、ログが残らない、対象がUNIX/Linux環境のみ使える、また、Open/Filter/エラーの結果が不明である特徴を持っている。
###FIN Scan
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570011/a042aff8-dedf-4894-ad8e-9437da0849c7.png)
###NULL Scan
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570011/f955c128-ca9a-4a9a-96fe-e1c16f2c08fd.png)
###Xmas Scan
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570011/28f58f68-041c-9238-fb2d-1f1e63cc8b36.png)

#socketモジュールを利用してPythonでScanningツールを作ってみよう
socketモジュールをインポート後、connect()関数を利用し、IPとPort番号を指定したら、TCP通信を行う。send(), recv()関数を利用してデータの送信、受信が可能である。

```port_scanning.py
import socket
s = socket.socket()
s.connect(('IPアドレス', ポート番号))
s.close()
```
```「結果」port_scanning.py
#portがオープンされている場合
>> 
#portがオープンされていない場合
Traceback (most recent call last):
  File "C:/~", line 3, in <module>
    s.connect(('127.0.0.1', 23))
ConnectionRefusedError: [WinError 10061] 対象のコンピューターによって拒否されたため、接続できませんでした。
```
エラーを解決するために、簡単な方法で「try」と「except」文を利用して成功と失敗に対してそれぞれ区別する。

```port_scanning.py
import socket
try:
s = socket.socket()
s.connect(('IPアドレス', ポート番号))
    print('success')
    s.close()
except:
    print('fail')
```

```「結果」port_scanning.py
#portがオープンされている場合
>> success
#portがオープンされていない場合
>> fail
```
roop文を利用して範囲内のポート番号を自動で確認するように作る。

```port_scanning.py
import socket
for port in range(1,101):
    try:
        s = socket.socket()
        s.connect(('IPアドレス', port))
        print('オープンされているポート:%d' % port)
        s.close()
        
    except: pass
```

```「結果」port_scanning.py
>> オープンされているポート:22
>> オープンされているポート:80
```
1から100までのポートを確認するのは、時間がかかるので、Pythonのリストデータ型を利用して、主に使用されているポートのみスキャンしてみよう。

**良く使用されるポート**
20, 21(FTP) / 22(SSH) / 23(Telnet) / 25(SMTP) / 53(DNS) / 80(HTTP) / 110(POP3) / 123(NTP) / 443(HTTPS) / 1433(MSSQL) / 3306(MYSQL) / 1521(ORACLE) / 8080(ORACLE, TOMCAT) / 3389(RDP)

```port_scanning.py
import socket
ports = [20, 21, 22, 23, 25, 53, 80, 110, 123, 443, 1433, 3306, 1521, 8080, 3389]
for port in ports:
    try:
        s = socket.socket()
        s.connect(('IPアドレス', port))
        print('オープンされているポート:%d' % port)
        s.close()
        
    except: pass
```

```「結果」port_scanning.py
>> オープンされているポート:80
>> オープンされているポート:443
>> オープンされているポート:3306
>> オープンされているポート:8080
```
input()関数を利用して、ホストアドレスを入力し、ポートスキャニングを実施するコードを作ってみよう。

```port_scanning.py
import socket
ports = [20, 21, 22, 23, 25, 53, 80, 110, 123, 443, 1433, 3306, 1521, 8080, 3389]
host = input('IPアドレス：')
for port in ports:
    try:
        s = socket.socket()
        s.connect((host, port))
        print('オープンされているポート:%d' % port)
        s.close()
        
    except: pass
```

```「結果」port_scanning.py
>> IPアドレス：127.0.0.1
>> オープンされているポート:80
>> オープンされているポート:443
>> オープンされているポート:3306
>> オープンされているポート:8080
```

#まとめ
今回はPythonのsokectライブラリを利用して簡単なポートスキャニングツールを作ってみたが、これを実務的に使うためには色々修正が必要である。
但し、ポートスキャンの原理、またPythonで基本的なポートスキャンのツールが作れるので、担当者及び管理者はこの投稿を参考して、より安全なネットワーク環境の構築に役に立てればと思っている。

#記事まとめ

2020年02月14日 - [:sunny:[Python] Pythonとセキュリティ - ①Pythonとは](https://qiita.com/CyberFortress/items/9b95fa3ee0f82c90d62f)