Linuxの指紋

dual display

前回の妄想、NotePCの外部Displayに、巨大なTVを使えないか? ターゲットがWindows10なら、HDMIなケーブルを買ってくれば、ちょちょいのちょい で行くはず。TV側の扱いは、シャープ方面に詳しい取扱説明書が有ったので、読んでみた。

うるさく言ってたのは、認証が通ったちゃんとしたケーブルを使えって事。安いケーブルだと、画像がボケたり、場合によっては映らない事があるとな。4Kな画像を映そうとすると、帯域が16Gも必要とか。

そんな訳なんで、100均には影も形もなかったよ。何も知らない素人が買ってって、映らない、ケーブル壊れてると、クレームされるのが恐いんで、置くのを自粛したんだろう?

さて、ケーブルはどこに? ちゃんとした家電量販店なら置いてあるのは分かってるけど、ホームセンターにも有るかな? まて、ブルーレイCDに繋ぐには繋がりで、レンタルCD屋にもありそうだな。散歩の方向を変えて、偵察してくるか。日経Linux買いますか?HDMIケーブル買いますかだな。

で、Windows10方面はいいんだけど、リナ方面はどうなってる? Windows7改めDebian(32)なマシンも有るからなあ。少し事情を調べておくか。

デュアルディスプレイを使う

Linuxで複数モニタを使うときのメモ

debian:tmp$ xrandr
Screen 0: minimum 320 x 200, current 1366 x 768, maximum 8192 x 8192
LVDS-1 connected primary 1366x768+0+0 (normal left inverted right x axis y axis)
 345mm x 194mm
   1366x768      59.97*+  50.00
   1360x768      59.80    59.96
    :
   320x180       59.84    59.32
VGA-1 disconnected (normal left inverted right x axis y axis)
HDMI-1 disconnected (normal left inverted right x axis y axis)
DP-1 disconnected (normal left inverted right x axis y axis)
DP-2 disconnected (normal left inverted right x axis y axis)

ThinkPad SL510 には、こんなに映像系が用意されてるの?

gui版のツール lxrandr が、既に入っていたよ。 Windows + 17 Display's なんて言う、楽しい事をやった人を発見。尊敬しますよ。

tcpdump file

前回はtcpdumpをgdb観光した。ソースをざっと辿ってみると、tcpdumpって、ご本尊がlibpcapで、それを操るshellみたいな物っていう風に感じた。tcpdumpは、パケットの録音と再生装置。CUIで見せてくれるのがtcpdumpで、GUIで見せてくれるのがwiresharkだ。

録音したデータは、共通規格なんで、どこでも使える。

debian:tmp$ file OBLOG
OBLOG: pcap capture file, microsecond ts (little-endian) - version 2.4 (Ethernet, capture length 262144)
vbox$ file OBLOG
OBLOG: tcpdump capture file (little-endian) - version 2.4 (Ethernet, capture length 262144)

VMwareのネットに繋がってるOpenBSDからDebinaに向かってsshした時の、取っ掛かりをDebian側で録音したもの。同一ログをOpenBSD側で確認すると、tcpdumpが生成したと言ってきた。

このファイルの構造ってどうなってるのだろうか? そんなのlibpcapのソース嫁。じゃ、あんまりなので、リバースエンジニアリングぽい事をしてみるか。

vbox$ grep tcpdump /etc/magic
# (We call them "tcpdump capture file(s)" for now, as "tcpdump" is
0       ubelong         0xa1b2c3d4      tcpdump capture file (big-endian)
0       ulelong         0xa1b2c3d4      tcpdump capture file (little-endian)

まずは、fileコマンドが使っているデータベースを検索。あれ? シグネチャーが同一じゃんと一瞬思っちゃったけど、型を見ると、ubelongとulelongの違いが有る。ストリーム表現した時に、並びが違うから、判定出来るよとな。それにしても、何の捻りも無いシグネチャーだな。zip('abcd', '1234') とわね。

vbox$ hexdump OBLOG | head -2
0000000    c3d4    a1b2    0002    0004    0000    0000    0000    0000
0000010    0000    0004    0001    0000    aae5    60bd    59aa    0009

little-endianってdumpした時、素直に読めないから嫌いだぞ >Intel

次に2、4とかはバージョン番号だろう(多分)。後、fileコマンドで、caputure length 262144なんてのが有る。これもヘッダーに埋め込まれいるんだろう。262144ってHexにすると幾つになる?

vbox$ gdb -q
(gdb) p/x 262144
$1 = 0x40000

これも、それっぽいのが、dump結果の2行目に表われていますなあ。後はソース嫁。この場合、tcpdumpのそれを見ても、見つからないはず。libpcapを見れ。

libpcap/pcap.h

/*
 * The first record in the file contains saved values for some
 * of the flags used in the printout phases of tcpdump.
 * Many fields here are 32 bit ints so compilers won't insert unwanted
 * padding; these files need to be interchangeable across architectures.
 */
struct pcap_file_header {
        bpf_u_int32 magic;
        u_short version_major;
        u_short version_minor;
        bpf_int32 thiszone;     /* gmt to local correction */
        bpf_u_int32 sigfigs;    /* accuracy of timestamps */
        bpf_u_int32 snaplen;    /* max length saved portion of each pkt */
        bpf_u_int32 linktype;   /* data link type (DLT_*) */
};

面倒かけないように32bitのint単位で扱うよとな。

wireshark

外見から分かるのは、上記ぐらいかな。後は懇切丁寧にパケットを分解してくれるwiresharkに登場願おう。データが表示されたら、全部展開しておいて、それをプレーンなテキストとしてファイルに落とせば、レポートが出来上がるよ。

ether frame

Frame 1: 78 bytes on wire (624 bits), 78 bytes captured (624 bits)
    Encapsulation type: Ethernet (1)
    Arrival Time: Jun  7, 2021 14:13:09.612778000 JST
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1623042789.612778000 seconds
     :
Ethernet II, Src: Vmware_59:e8:ba (00:0c:29:59:e8:ba), Dst: Vmware_4d:18:2d (00:0c:29:4d:18:2d)
    Destination: Vmware_4d:18:2d (00:0c:29:4d:18:2d)
    Source: Vmware_59:e8:ba (00:0c:29:59:e8:ba)
    Type: IPv4 (0x0800)

Frame 1ってなってる所はwiresharkがまとめてくれたサマリーだ。注目は到着日時。それとepochにした時間。おっと、epochを人間が分かる日に直してる。

vbox$ gdb -q
(gdb) p/x 1623042789
$1 = 0x60bdaae5
(gdb) q
vbox$ hexdump OBLOG | grep 60bd
0000010    0000    0004    0001    0000    aae5    60bd    59aa    0009
0000070    4811    0000    0000    aae5    60bd    59be    0009    004a

packet毎に、到着日時を記録してるんだな。まあ、logですから。 次は、 イーサネットフレームだな。

MacAddressと下位のプロトコル(IPv4)を指定してる。MacAddressは、ベンダーがVmwareってちゃんと知ってるよ。VMwarePlayerのconfigファイル、OpenBSD.vmxに当てってみると

ethernet0.connectionType = "nat"
ethernet0.virtualDev = "e1000"
ethernet0.generatedAddress = "00:0c:29:59:e8:ba"

MACaddressって、偽装が簡単だな。ルーターで、MACアドレスでフィルタリングするのは、気休めか。

IP header

次は、 IPv4 だな。注目は

Differentiated Services Field: 0x48 (DSCP: AF21, ECN: Not-ECT)
Flags: 0x4000, Don't fragment
Fragment offset: 0
Time to live: 64
Protocol: TCP (6)

フラグメントしないでね。64回ルーターを通ったら消滅してね。更に荷物があるけど、それはTCPだからね。

TCP header

最後は、 Transmission Control Protocol

1011 .... = Header Length: 44 bytes (11)
Flags: 0x002 (SYN)
Window size value: 16384
 :
Options: (24 bytes), Maximum segment size, No-Operation (NOP), No-Operation (NOP), SACK permitted, No-Operation (NOP), Window scale, No-Operation (NOP), No-Operation (NOP), Timestamps
    TCP Option - Maximum segment size: 1460 bytes
    TCP Option - No-Operation (NOP)
    TCP Option - No-Operation (NOP)
    TCP Option - SACK permitted
    TCP Option - No-Operation (NOP)
    TCP Option - Window scale: 6 (multiply by 64)
    TCP Option - No-Operation (NOP)
    TCP Option - No-Operation (NOP)
    TCP Option - Timestamps: TSval 2780959048, TSecr 0
        Kind: Time Stamp Option (8)
        Length: 10
        Timestamp value: 2780959048
        Timestamp echo reply: 0
[Timestamps]
    [Time since first frame in this TCP stream: 0.000000000 seconds]
    [Time since previous frame in this TCP stream: 0.000000000 seconds]

tcpdumpでは、下記のように表示したよ。

vbox$ tcpdump -tt -r OBLOG
tcpdump: WARNING: snaplen raised from 116 to 262144
1623042789.612778 aa.bb.cc.128.33778 > aa.bb.cc.129.ssh: S 2874817133:2874817133(0) win 16384 \ 
<mss 1460,nop,nop,sackOK,nop,wscale 6,nop,nop,timestamp 2780959048 0> (DF) [tos 0x48]

tcpdump by gdb

OpenBSDがデフォで用意してるtcpdumpはgdbから正しく機能しなかった(応答が無くなる)。普通のtcpdumpはどうかと試してみる。

(gdb) bt
   :
#8  0x140a7d74 in print_packet (user=0xcf7f9b18 "", h=0xcf7f9968, sp=0x5acda002\
 "") at ./tcpdump.c:3075
#9  0x059c0907 in pcap_offline_read (p=0x4641ea00, cnt=-1, callback=0x140a7d00 \
<print_packet>, user=0xcf7f9b18 "") at /usr/src/lib/libpcap/savefile.c:336
#10 0x059a7911 in pcap_loop (p=0x4641ea00, cnt=-1, callback=0x140a7d00 <print_p\
acket>, user=0xcf7f9b18 "") at /usr/src/lib/libpcap/pcap.c:69
#11 0x140a5175 in main (argc=3, argv=0xcf7fa134) at ./tcpdump.c:2524

こんな具合にちゃんと動いているな。 色々ggしてみたが、手がかり無し。

tcpdump -o

あちこにに手を出してしまったけど、OpenBSDのtcpdumpに組み込まれている、OSの推測方法を探ってみる。前回目星を付けておいた。

print-tcp.p

/* OS Fingerprint */
         :
                head = pf_osfp_fingerprint_hdr(ip, ip6, tp);
        if (head) {
                int prev = 0;
                printf(" (src OS:");

指紋の鑑定は、 pf_osfp_fingerprint_hdr で、やっているみたいだ。Makefileを見ると

# TCP OS Fingerprinting
.PATH: ${.CURDIR}/../../sys/net
.PATH: ${.CURDIR}/../../sbin/pfctl
SRCS+=  pf_osfp.c pfctl_osfp.c

こんな風に、部外者を招聘してる。何とKernelで使われているネット関係のエリアになるやつだ。カーネル側のコードとユーザーランド側と混在してて、使い分けている。

struct pf_osfp_enlist *
pf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6,
    const struct tcphdr *tcp)
      :
        if ((fpresult = pf_osfp_find(&pf_osfp_list, &fp,
            PF_OSFP_MAXTTL_OFFSET)))
                return (&fpresult->fp_oses);
        return (NULL);
}

この関数の中で、渡されて来たパケットデータから分析に必要な部分を取り出して、fpにまとめる。 pf_osfp_list の方は、台帳だ。一致してたら、それを返すって事だ。

pfctl_osfp.c には、/etc/pf.osを読み込むルーチンが置いてあるなあ。

pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
  :
                if (GET_INT(window, &w_mod, "window size", T_DC|T_MSS|T_MTU|
                    T_MOD, 0xffff) ||
                    GET_INT(ttl, NULL, "ttl", 0, 0xff) ||
                    GET_INT(df, NULL, "don't fragment frag", 0, 1) ||
                    GET_INT(psize, &p_mod, "overall packet size", T_MOD|T_DC,
                    8192) ||
                    GET_STR(tcpopts, "TCP Options", 1) ||
                    GET_STR(class, "OS class", 1) ||
                    GET_STR(version, "OS version", 0) ||
                    GET_STR(subtype, "OS subtype", 0) ||
                    GET_STR(desc, "OS description", 2))

こんな風に読み込んで、検索に都合のよい形に変形してる。

pf.os(5)

  # tcpdump -s128 -c1 -nv 'tcp[13] == 2'
  03:13:48.118526 10.0.0.1.3377 > 10.0.0.2.80: S [tcp sum ok] \
      534596083:534596083(0) win 57344 <mss 1460> (DF) [tos 0x10] \
      (ttl 64, id 11315, len 44)

almost translates into the following fingerprint

  57344:64:1:44:M1460:  exampleOS:1.0::exampleOS 1.0

これ、指紋台帳への登録方法。基本の貴だ。これだけでは、心元ないので、tcpoptsで詳細を追加しとけ。例が、pf.osの現場に載ってた。

linuxの指紋

ここまで分かれば、OS推測用のデータベース /etc/pf.os を更新するのは容易であろう。以前録音、じゃなくて録パケットしたログから、必要な情報を炙り出してみる。最初はOpenBSD用ね。

ob$ tcpdump -t -v -r OBLOG
tcpdump: WARNING: snaplen raised from 116 to 262144
aa.bb.cc.128.33778 > aa.bb.cc.129.ssh: S [tcp sum ok] 2874817133:2874817133(0) win 16384 <mss 1460,nop,nop,sackOK,nop,wscale 6,nop,nop,timestamp 2780959048 0> (DF) [tos 0x48] (ttl 64, id 17994, len 64)

このデータを登録しればいいんだけど、先客に

16384:64:1:64:M*,N,N,S,N,W6,N,N,T:      OpenBSD:6.1::OpenBSD 6.1

こんなのが居る。翻訳結果も同様になった(と言う事は、6.1の頃から全く代わりが無いと言う事だ)。tcpoptionsのmssが、どうでもいいよってなってるけど、そこを無理して現在の数値1460に設定してみるか。

マッチは、最初から見ていくので、少なくとも、上記のデータよりも手前の方に書いておく必要が有る。

16384:64:1:64:M1460,N,N,S,N,W6,N,N,T:      OpenBSD:6.9::OpenBSD 6.9
ob$ tcpdump -t -o -r OBLOG
tcpdump: WARNING: snaplen raised from 116 to 262144
aa.bb.cc.128.33778 > aa.bb.cc.129.ssh: S (src OS: OpenBSD 6.9) 2874817133:2874817133(0) win 16384 <mss 1460,nop,nop,sackOK,nop,wscale 6,nop,nop,timestamp 2780959048 0> (DF) [tos 0x48]

成功した。じゃ、いよいよlinuxからのパケットの収録をする。ターゲットの素性は、こんな奴だった。この情報、台帳登録時に必要になるからね。

sakae@pen:~$ uname -a
Linux pen 4.19.0-16-amd64 #1 SMP Debian 4.19.181-1 (2021-03-19) x86_64 GNU/Linux

録パケ開始。sshでOpenBSDにlogin。指紋を採取された事を知らない、哀れなLinux

ob# tcpdump -s128 -w LINLOG 'tcp[13] == 2'
tcpdump: listening on em0, link-type EN10MB
^C
169 packets received by filter
0 packets dropped by kernel

ちゃんとget出来たか、再生してみる。

ob$ tcpdump -r LINLOG -v -t -o
tcpdump: WARNING: snaplen raised from 116 to 128
aa.bb.cc.129.53392 > aa.bb.cc.128.ssh: S [tcp sum ok] (src OS: unknown) 4214038903:4214038903(0) win 64240 <mss 1460,sackOK,timestamp 3988811210 0,nop,wscale 7> (DF) (ttl 64, id 32018, len 60)

そして、登録情報

64240:64:1:60:M1460,S,T,N,W7:   Linux:4.19::Linux 4.19(debian)

検証大事大事だよ

ob$ tcpdump -r LINLOG -t -o
tcpdump: WARNING: snaplen raised from 116 to 128
aa.bb.cc.129.53392 > aa.bb.cc.128.ssh: S (src OS: Linux 4.19) 4214038903:4214038903(0) win 64240 <mss 1460,sackOK,timestamp 3988811210 0,nop,wscale 7> (DF)

これで、嘘は付けないぞ。なんてのは、裏側を知らない人。簡単にサツの裏をかけるぞ。例えば、IP Headerの中に埋め込まれているTTLが指紋の一部として採用されてる。

ob$ sysctl -a | grep ttl
net.inet.ip.ttl=64
 :

sysctlを使って簡単に変更出来ちゃうからね。OpenBSDでは64だけど、Windowsなんてのは太っ腹に128に設定されてる。

また、ルーターを渡り歩いて来るパケットは、ルーター色にパケットが変色しちゃうんで、この機構の使い所は限定されてしまうぞ。

何か良いアイデアが有ったら、 p0f v3 (version 3.09b) に、コンタクトをお願い致します。


This year's Index

Home