いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-332 (go-packetメモ-16)(FTPの情報を表示)

関連記事

Goメモ-306 (go-packetメモ-01)(ネットワークインターフェースを表示) - いろいろ備忘録日記

Goメモ-307 (go-packetメモ-02)(流れるパケットをキャプチャする)(OpenLive) - いろいろ備忘録日記

Goメモ-308 (go-packetメモ-03)(pcapファイルを読み込み)(OpenOffline) - いろいろ備忘録日記

Goメモ-309 (go-packetメモ-04)(BPFフィルタを設定)(SetBPFFilter) - いろいろ備忘録日記

Goメモ-310 (go-packetメモ-05)(*pcap.Packetの中身を表示) - いろいろ備忘録日記

Goメモ-311 (go-packetメモ-06)(*layers.Ethernetの情報を表示) - いろいろ備忘録日記

Goメモ-312 (go-packetメモ-07)(*layers.ARPの情報を表示) - いろいろ備忘録日記

Goメモ-313 (go-packetメモ-08)(*layers.ICMPv4の情報を表示) - いろいろ備忘録日記

Goメモ-314 (go-packetメモ-09)(*layers.IPv4の情報を表示) - いろいろ備忘録日記

Goメモ-315 (go-packetメモ-10)(*layers.TCPの情報を表示) - いろいろ備忘録日記

Goメモ-316 (go-packetメモ-11)(*layers.UDPの情報を表示) - いろいろ備忘録日記

Goメモ-317 (go-packetメモ-12)(アプリケーションレイヤーの情報を表示) - いろいろ備忘録日記

Goメモ-321 (go-packetメモ-13)(HTTPの情報を表示) - いろいろ備忘録日記

Goメモ-323 (go-packetメモ-14)(DNSの情報を表示) - いろいろ備忘録日記

Goメモ-324 (go-packetメモ-15)(DNSSECの情報を表示) - いろいろ備忘録日記

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。忘れないうちにメモメモ。。。

GoでWireSharkやtcpdumpのようにパケットを直接見たいときなどに利用できるライブラリに

github.com

pkg.go.dev

というのがあります。

今まで使ったこと無かったのですが、使うと面白かったので自分用のメモ代わりにちょこちょこ残しておこうと思います。

Linux (Ubuntu) 上で遊んでいますので、Windowsの場合はWinPcap (WireSharkをインストールするときについでにインストールできたはず)が必要になると思います。

インストール

libpcap が必要ですので、以下でインストールします。

$ sudo apt install libpcap-dev

あと、tcpdumpが入っていない場合は以下もついでに入れておきます。(これはオプショナルです)

$ sudo apt install tcpdump

あと、FTPサーバが無ければインストールして簡易で立てましょう。

$ sudo apt-get update
$ sudo apt-get install -y vsftpd

試してみる

使い方に関しては、上に挙げている go-packet の godoc の方に詳しく書かれています。

今回は、FTPの中身を見てみます。

アプリケーションレイヤーを取得して、ペイロードが取れれば後はそれをFTPだと解釈してやれば良いはずです。

main.go

// Package main is the example of FTP using go-packet.
package main

import (
    "fmt"
    "log"
    "os"
    "os/signal"
    "time"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
)

var (
    appLog = log.New(os.Stderr, "", 0)
)

func main() {
    if err := run(); err != nil {
        panic(err)
    }
}

func run() error {
    const (
        device      = "lo"
        filter      = "tcp and port 21"
        snapshotLen = int32(1600)
        promiscuous = false
        timeout     = 1 * time.Second
    )

    defer func() { appLog.Println("DONE") }()

    // --------------------------------------
    // Open capture handle
    // --------------------------------------
    var (
        handle *pcap.Handle
        err    error
    )

    handle, err = pcap.OpenLive(device, snapshotLen, promiscuous, timeout)
    if err != nil {
        return fmt.Errorf("error open handle: %w", err)
    }
    defer handle.Close()

    // --------------------------------------
    // Apply capture filter (optional)
    // --------------------------------------
    if filter != "" {
        err = handle.SetBPFFilter(filter)
        if err != nil {
            return fmt.Errorf("error apply filter: %w", err)
        }
    }

    // --------------------------------------
    // Set signal handler
    // --------------------------------------
    var (
        doneCh = make(chan struct{})
        sigCh  = make(chan os.Signal, 1)
    )

    signal.Notify(sigCh, os.Interrupt)

    // --------------------------------------
    // Make packet source and display.
    // --------------------------------------
    var (
        dataSource   gopacket.PacketDataSource = handle
        decoder      gopacket.Decoder          = handle.LinkType()
        packetSource *gopacket.PacketSource    = gopacket.NewPacketSource(dataSource, decoder)
        packetCh     <-chan gopacket.Packet    = packetSource.Packets()
    )
    appLog.Println("START")

LOOP:
    for {
        select {
        case <-sigCh:
            close(doneCh)
            break LOOP
        case p, ok := <-packetCh:
            if !ok {
                break LOOP
            }

            if err = see(p); err != nil {
                return err
            }
        }
    }

    return nil
}

func see(p gopacket.Packet) error {
    //
    // レイヤーを確認
    //
    tcpLayer := p.Layer(layers.LayerTypeTCP)
    if tcpLayer == nil {
        return nil
    }

    //
    // ペイロードを取得
    //
    var (
        tcp     = tcpLayer.(*layers.TCP)
        payload = tcp.LayerPayload()
    )

    if len(payload) == 0 {
        appLog.Printf("[Src Port       ] %v", tcp.SrcPort)
        appLog.Printf("[Dst Port       ] %v", tcp.DstPort)
        appLog.Printf("[Seq Number     ] %v", tcp.Seq)
        appLog.Printf("[Ack Number     ] %v", tcp.Ack)
        appLog.Printf("[Window Size    ] %v", tcp.Window)
        appLog.Printf("[TCP Flags - SYN] %v", tcp.SYN)
        appLog.Printf("[TCP Flags - ACK] %v", tcp.ACK)
        appLog.Printf("[TCP Flags - PSH] %v", tcp.PSH)
        appLog.Printf("[TCP Flags - RST] %v", tcp.RST)
        appLog.Printf("[TCP Flags - FIN] %v", tcp.FIN)
        appLog.Printf("[Checksum       ] %v", tcp.Checksum)
        appLog.Printf("[Urgent Pointer ] %v", tcp.Urgent)
        appLog.Println("------------------------------------")
        return nil
    }

    //
    // ペイロードを文字列に変換してFTPとしての解析を試みる
    //
    var (
        payloadStr = string(payload)
    )
    defer func() { appLog.Println("------------------------------------") }()

    appLog.Printf("[FTP] %s\n", payloadStr)

    return nil
}

Taskfile.yml

タスク定義は以下のような感じ。

  applayer-ftp:
    desc: See FTP info
    dir: cmd/applayer/ftp
    cmds:
      - cmd: echo Please start the FTP server in advance.
        silent: true
      - go build -o gopacket-ftp main.go
      - sudo ./gopacket-ftp &
      - cmd: read
        silent: true
      - sudo pkill gopacket-ftp

以下、Gitpod上で実行してみた結果です。

vsftpを臨時で立てて、それとやり取りしているパケットを見ます。

$ sudo apt-get update
$ sudo apt-get install -y vsftpd

設定ファイルを調整。ローカルでしか使わないのでセキュリティは無視です。

/etc/vsftpd.conf

anonymous_enable=YES
local_enable=YES
write_enable=YES
local_umask=022
chroot_local_user=NO

起動します。

$ sudo mkdir -p /var/run/vsftpd/empty
$ sudo vsftpd /etc/vsftpd.conf

別のターミナルを起動して

$ task applayer-ftp

もう一つ別のターミナルを起動して

$ sudo apt-get install -y lftp
$ lftp -u anonymous, anonymous@localhost
lftp anonymous@localhost:~> ls
lftp anonymous@localhost:/> bye

そうすると、taskを実行しているターミナルに以下のように出力されます。

[Src Port       ] 52584
[Dst Port       ] 21(ftp)
[Seq Number     ] 3285394225
[Ack Number     ] 0
[Window Size    ] 43690
[TCP Flags - SYN] true
[TCP Flags - ACK] false
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] false
[Checksum       ] 65072
[Urgent Pointer ] 0
------------------------------------
[Src Port       ] 21(ftp)
[Dst Port       ] 52584
[Seq Number     ] 2228490639
[Ack Number     ] 3285394226
[Window Size    ] 43690
[TCP Flags - SYN] true
[TCP Flags - ACK] true
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] false
[Checksum       ] 65072
[Urgent Pointer ] 0
------------------------------------
[Src Port       ] 52584
[Dst Port       ] 21(ftp)
[Seq Number     ] 3285394226
[Ack Number     ] 2228490640
[Window Size    ] 342
[TCP Flags - SYN] false
[TCP Flags - ACK] true
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] false
[Checksum       ] 65064
[Urgent Pointer ] 0
------------------------------------
[FTP] 220 (vsFTPd 3.0.5)

------------------------------------
[Src Port       ] 52584
[Dst Port       ] 21(ftp)
[Seq Number     ] 3285394226
[Ack Number     ] 2228490660
[Window Size    ] 342
[TCP Flags - SYN] false
[TCP Flags - ACK] true
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] false
[Checksum       ] 65064
[Urgent Pointer ] 0
------------------------------------
[FTP] FEAT

------------------------------------
[Src Port       ] 21(ftp)
[Dst Port       ] 52584
[Seq Number     ] 2228490660
[Ack Number     ] 3285394232
[Window Size    ] 342
[TCP Flags - SYN] false
[TCP Flags - ACK] true
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] false
[Checksum       ] 65064
[Urgent Pointer ] 0
------------------------------------
[FTP] 211-Features:

------------------------------------
[FTP]  EPRT

------------------------------------
[FTP]  EPSV

------------------------------------
[Src Port       ] 52584
[Dst Port       ] 21(ftp)
[Seq Number     ] 3285394232
[Ack Number     ] 2228490682
[Window Size    ] 342
[TCP Flags - SYN] false
[TCP Flags - ACK] true
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] false
[Checksum       ] 65064
[Urgent Pointer ] 0
------------------------------------
[FTP]  MDTM

------------------------------------
[FTP]  PASV

------------------------------------
[FTP]  REST STREAM

------------------------------------
[FTP]  SIZE

------------------------------------
[Src Port       ] 52584
[Dst Port       ] 21(ftp)
[Seq Number     ] 3285394232
[Ack Number     ] 2228490717
[Window Size    ] 342
[TCP Flags - SYN] false
[TCP Flags - ACK] true
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] false
[Checksum       ] 65064
[Urgent Pointer ] 0
------------------------------------
[FTP]  TVFS

------------------------------------
[FTP] 211 End

------------------------------------
[Src Port       ] 52584
[Dst Port       ] 21(ftp)
[Seq Number     ] 3285394232
[Ack Number     ] 2228490731
[Window Size    ] 342
[TCP Flags - SYN] false
[TCP Flags - ACK] true
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] false
[Checksum       ] 65064
[Urgent Pointer ] 0
------------------------------------
[FTP] AUTH TLS

------------------------------------
[FTP] 530 Please login with USER and PASS.

------------------------------------
[FTP] USER anonymous

------------------------------------
[FTP] 331 Please specify the password.

------------------------------------
[FTP] PASS 

------------------------------------
[FTP] 230 Login successful.

------------------------------------
[FTP] PWD

------------------------------------
[FTP] 257 "/" is the current directory

------------------------------------
[FTP] PASV

------------------------------------
[FTP] 227 Entering Passive Mode (127,0,0,1,209,75).

------------------------------------
[FTP] LIST

------------------------------------
[FTP] 150 Here comes the directory listing.

------------------------------------
[FTP] 226 Directory send OK.

------------------------------------
[Src Port       ] 52584
[Dst Port       ] 21(ftp)
[Seq Number     ] 3285394282
[Ack Number     ] 2228490979
[Window Size    ] 342
[TCP Flags - SYN] false
[TCP Flags - ACK] true
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] false
[Checksum       ] 65064
[Urgent Pointer ] 0
------------------------------------
[FTP] QUIT

------------------------------------
[FTP] 221 Goodbye.

------------------------------------
[Src Port       ] 21(ftp)
[Dst Port       ] 52584
[Seq Number     ] 2228490993
[Ack Number     ] 3285394288
[Window Size    ] 342
[TCP Flags - SYN] false
[TCP Flags - ACK] true
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] true
[Checksum       ] 65064
[Urgent Pointer ] 0
------------------------------------
[Src Port       ] 52584
[Dst Port       ] 21(ftp)
[Seq Number     ] 3285394288
[Ack Number     ] 2228490994
[Window Size    ] 342
[TCP Flags - SYN] false
[TCP Flags - ACK] true
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] true
[Checksum       ] 65064
[Urgent Pointer ] 0
------------------------------------
[Src Port       ] 21(ftp)
[Dst Port       ] 52584
[Seq Number     ] 2228490994
[Ack Number     ] 3285394289
[Window Size    ] 342
[TCP Flags - SYN] false
[TCP Flags - ACK] true
[TCP Flags - PSH] false
[TCP Flags - RST] false
[TCP Flags - FIN] false
[Checksum       ] 65064
[Urgent Pointer ] 0
------------------------------------

task: [applayer-ftp] sudo pkill gopacket-ftp

ちゃんとFTPのやり取りが見えていますね。

リポジトリ

上のサンプルなどは、以下のリポジトリでアップしています。ご参考までに。

github.com

参考情報

Goのおすすめ書籍

Go言語による並行処理

Go言語による並行処理

Amazon


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。