ろうとるがPythonを扱う、、（その20：Scapyで改ざん）

# Scapyでデータを改ざんしてみる
[「ろうとるがPythonを扱う、、（その19：Scapy Bridgeで遅延やパケットDrop）」](https://qiita.com/infinite1oop/items/83cbd8f8cbd671022588)の続きである。ネットワークブリッジをもうけて、そのブリッジでデータを改ざんする。

## ネットワーク構成
下記のようなもの。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/42b3080f-b98e-1c94-a0d9-c9cd70bba2e2.png)
VirtualBoxの３つのVMを利用。中央のVM(Lubuntu)ネットワークブリッジ、両端は同一ネットワークセグメントに属する。

## ソースコード
### 出だし
```python
from scapy.all import *
import select

sub_data = '1234567890'
```
- 改ざん後のデータ（置き換えるもの）

### Ethernetフレーム生成
```python
def recreate_frame(eth_header, ip_packet):
    # Removal Check-Sum
    if ip_packet.haslayer(TCP):
        del ip_packet.getlayer(TCP).chksum
    if ip_packet.haslayer(UDP):
        del ip_packet.getlayer(UDP).chksum
    if ip_packet.haslayer(ICMP):
        del ip_packet.getlayer(ICMP).chksum
    del ip_packet.chksum

    # Recreation Ethernet
    result = Ether(dst=eth_header.dst, src=eth_header.src, type=eth_header.type)/IP(ip_packet.build())
    return result
```
- 改ざん後のEthernetフレーム作成
- Ethernetヘッダはオリジナルのまま利用
- IPレイヤ以上のチェックサム削除および再生成
- 下記サイト参照
    - [Python2 + Scapyで、Raspberry Pi 2 Model B をNAT/NAPTルータ化してみた #router_jisaku](https://thinkami.hatenablog.com/entry/2018/01/07/121824)
    - [How to calculate a packet checksum without sending it?](https://stackoverflow.com/questions/5953371/how-to-calculate-a-packet-checksum-without-sending-it)

### メイン
[前回](https://qiita.com/infinite1oop/items/83cbd8f8cbd671022588)利用した”bridge_and_sniff”ではなく、下記サイトの”select”実装を利用した。
- [Scapy + Python2 + I/O多重化・ブロッキングI/O系モジュールにて、Raspberry Pi 2 Model Bをブリッジ化する](https://thinkami.hatenablog.com/entry/2018/01/04/225048)

```python
### Main ###
# Layer2 socket
eth0_socket = conf.L2socket(iface='enp0s3')
eth1_socket = conf.L2socket(iface='enp0s8')

# Socket Dictionary
next_socket = {
    eth0_socket: eth1_socket,
    eth1_socket: eth0_socket,
}
while True:
    # select()
    readable_sockets, _, _ = select.select([eth0_socket, eth1_socket], [], [])

    for s in readable_sockets:
        # Data receive
        p = s.recv()

        if p:
            if p.haslayer(Raw):
                p.show()
                eth_frame = p.getlayer(Ether)
                ip_pkt = p.getlayer(IP)
                length = len(p['Raw'].load)
                m = length%10
                n = int(length/10)
                data = ''
                for i in range(n):
                    data += sub_data
                data += sub_data[0:m-1]
                data += '\n'
                p['Raw'].load = data.encode('ascii')
                ip_pkt = p.getlayer(IP)
                new_frame = recreate_frame(eth_frame, ip_pkt)
                next_socket[s].send(new_frame)
            else:
                next_socket[s].send(p.original) # そのまま送信
```
- L2ソケットの定義（eth0_socket、eth1_socket）
- ブリッジの定義（L2ソケットどうしをつなげる）
- 待受け（select）
- データ受信（p）
- 改ざん対象は、TCP/UDP/ICMPなどでデータを持つもの（p.haslayer(Raw)がTrue）
- EthernetヘッダおよびIPデータの取得
- 置き換えるデータは、出だしで定義した”sub_data”の内容
    - 同じ文字数分置き換える（TCPシーケンス維持のため）
    - 上述した”recreate_frame()”により、Ethernetフレーム作成
    - データ送信

## 検証
両端VM間で、netcat(nc)を実行。上述したネットワーク構成では、ncのClientが”10.1.1.1”、ncのServerが”10.1.1.2”とになる。

### コマンド実行結果
Clientでのnc実行。
```
$ echo alfjlakdflak | nc 10.1.1.2 1111
```
Serverでのnc待受け。
```
$ nc -l -p 1111
123456789012
```
改ざんされている（置き換わっている）のがわかる。

### ネットワークキャプチャデータ
Clientの結果。
![Client.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/09fc4bb6-4e86-af5e-409a-b838770f4ad6.png)
Serverの結果。
![Server.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/0b2e940d-aed5-6be3-ab9f-e078672a32f8.png)
こちらでも、改ざんされている（置き換わっている）のがわかる。

## 最後に
おそらく、改ざんとしては、初歩の初歩の初歩の初歩レベルであろう。

EOF