Linux Kernel CVE-2017-18017 の Poc を書いて検証した

表題の通り CVE-2017-18017 の PoC を書いてどのような影響があるのを検証・観察した

⚠️

一年以上前に修正パッチが出ている CVE です

CVE の Description

The tcpmss_mangle_packet function in net/netfilter/xt_TCPMSS.c in the Linux kernel before 4.11, and 4.9.x before 4.9.36, allows remote attackers to cause a denial of service (use-after-free and memory corruption) or possibly have unspecified other impact by leveraging the presence of xt_TCPMSS in an iptables action.

iptablesTCPMSS ターゲット ( --set-mss または --clamp-mss-to-pmtu ) を使っているホストに、細工した TCPヘッダを投げつけると use-after-free やメモリの破壊等々が起こせる

CVSS3 の Base Score が 9.8 と高い 🔥

修正パッチ

パッチは以下の通りです

From 2638fd0f92d4397884fd991d8f4925cb3f081901 Mon Sep 17 00:00:00 2001
From: Eric Dumazet <edumazet@google.com>
Date: Mon, 3 Apr 2017 10:55:11 -0700
Subject: netfilter: xt_TCPMSS: add more sanity tests on tcph->doff

Denys provided an awesome KASAN report pointing to an use
after free in xt_TCPMSS

I have provided three patches to fix this issue, either in xt_TCPMSS or
in xt_tcpudp.c. It seems xt_TCPMSS patch has the smallest possible
impact.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Denys Fedoryshchenko <nuclearcat@nuclearcat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/xt_TCPMSS.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c
index 27241a767f17..c64aca611ac5 100644
--- a/net/netfilter/xt_TCPMSS.c
+++ b/net/netfilter/xt_TCPMSS.c
@@ -104,7 +104,7 @@ tcpmss_mangle_packet(struct sk_buff *skb,
    tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
    tcp_hdrlen = tcph->doff * 4;
 
-  if (len < tcp_hdrlen)
+   if (len < tcp_hdrlen || tcp_hdrlen < sizeof(struct tcphdr))
        return -1;
 
    if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
@@ -152,6 +152,10 @@ tcpmss_mangle_packet(struct sk_buff *skb,
    if (len > tcp_hdrlen)
        return 0;
 
+   /* tcph->doff has 4 bits, do not wrap it to 0 */
+   if (tcp_hdrlen >= 15 * 4)
+       return 0;
+
    /*
     * MSS Option not found ?! add it..
     */
-- 
cgit 1.2-0.3.lf.el7

分析 🔎

  • tcp_hdrlen のサイズを任意に細工したTCPセグメントを送りつけると、パッチで修正した箇所のコードを通過して不具合を再現できると推測がつく
  • TCP ヘッダの仕様をぜんぜん覚えてなかなったので、復習し直しながら SOCK_RAW のソケットで試した

CVE の PoC

リモート攻撃が容易に可能となる PoC なので非公開 (いつも非公開ですいません)

再現

修正パッチを読むと「 KASAN = Kernel Address Sanitizer を使って use-after-free を検知した」とあったので、v4.9.35 で CONFIG_KASAN を有効にしたカーネルで PoC を実行して、KASAN のログを出す事で再現をとったものとする

f:id:hiboma:20180929113027p:plain

iptables の設定をあれこれすれば、VM 一台で再現が可能。細工したTCPセグメントを一つ飛ばすだけで KASAN のログがわらわらと出た

[   60.958790] BUG: KASAN: use-after-free in tcpmss_mangle_packet+0x2aa/0x920 [xt_TCPMSS] at addr ffff880067f72002
[   60.969337] Read of size 1 by task ****/1167
[   60.975820] Object at ffff880067f72000, in cache vm_area_struct size: 192 👈 ( kmalloc-32 や kmalloc-128 が出ているログもあった ) 
[   60.989569] Allocated:
[   60.994387] PID = 1101
[   61.000590] Freed:
[   61.003538] PID = 1102
[   61.005466] Memory state around the buggy address:
[   61.010213]  ffff880067f71f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[   61.016000]  ffff880067f71f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[   61.024256] >ffff880067f72000: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[   61.029370]                    ^
[   61.032932]  ffff880067f72080: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
[   61.040788]  ffff880067f72100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[   61.048892] ==================================================================

slab-out-of-bounds も同時に検知していた

Sep 28 10:37:20 localhost kernel: BUG: KASAN: slab-out-of-bounds in tcpmss_mangle_packet+0x283/0x920 [xt_TCPMSS] at addr ffff880064660e80
Sep 28 10:37:20 localhost kernel: Read of size 1 by task ****/1174
Sep 28 10:37:20 localhost kernel: CPU: 3 PID: 1174 Comm: **** Tainted: G           O    4.9.35 #4
Sep 28 10:37:20 localhost kernel: Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
Sep 28 10:37:20 localhost kernel: ffff880064e0f338 ffffffff8a11dd06 ffff88006a003080 ffff880064660c80
Sep 28 10:37:20 localhost kernel: ffff880064e0f360 ffffffff89f03ce1 ffffed000c8cc1d0 ffff88006a003080
Sep 28 10:37:20 localhost kernel: 0000000000000000 ffff880064e0f3e8 ffffffff89f03f8a ffffffff8aca6e03
Sep 28 10:37:20 localhost kernel: Call Trace:
Sep 28 10:37:20 localhost kernel: [<ffffffff8a11dd06>] dump_stack+0x63/0x8d
Sep 28 10:37:20 localhost kernel: [<ffffffff89f03ce1>] kasan_object_err+0x21/0x70
Sep 28 10:37:20 localhost kernel: [<ffffffff89f03f8a>] kasan_report.part.1+0x20a/0x4f0
Sep 28 10:37:20 localhost kernel: [<ffffffff89e64279>] ? pm_qos_get_value.part.5+0x6/0x6
Sep 28 10:37:20 localhost kernel: [<ffffffffc08c8393>] ? tcpmss_mangle_packet+0x283/0x920 [xt_TCPMSS] 🔥
Sep 28 10:37:20 localhost kernel: [<ffffffff89f03043>] ? save_stack+0xa3/0xd0
Sep 28 10:37:20 localhost kernel: [<ffffffff89f045a6>] kasan_report+0x26/0x30
Sep 28 10:37:20 localhost kernel: [<ffffffff89f02997>] __asan_load1+0x47/0x50
Sep 28 10:37:20 localhost kernel: [<ffffffffc08c8393>] tcpmss_mangle_packet+0x283/0x920 [xt_TCPMSS]
Sep 28 10:37:20 localhost kernel: [<ffffffff8a625521>] ? inet_sendmsg+0xe1/0x130
Sep 28 10:37:20 localhost kernel: [<ffffffffc08c8110>] ? tcpmss_tg4_check+0x110/0x110 [xt_TCPMSS]
Sep 28 10:37:20 localhost kernel: [<ffffffff8a71e46b>] ? entry_SYSCALL64_slow_path+0x25/0x25
Sep 28 10:37:20 localhost kernel: [<ffffffff8a5ae35a>] ? tcp_mt+0x2ca/0x530
Sep 28 10:37:20 localhost kernel: [<ffffffff89c4ff02>] ? __mcheck_cpu_init_generic+0xd2/0xf0
Sep 28 10:37:20 localhost kernel: [<ffffffff8a5ae090>] ? udp_mt_check+0x40/0x40
Sep 28 10:37:20 localhost kernel: [<ffffffff8a558280>] ? dst_alloc+0x90/0xd0
Sep 28 10:37:20 localhost kernel: [<ffffffff8a5af254>] ? rt_dst_alloc+0x54/0x1b0
Sep 28 10:37:20 localhost kernel: [<ffffffff8a5b2ba4>] ? __ip_route_output_key_hash+0x684/0x1110
Sep 28 10:37:20 localhost kernel: [<ffffffff8a5b3c53>] ? ip_route_output_flow+0x23/0x60
Sep 28 10:37:20 localhost kernel: [<ffffffff8a60a8ec>] ? raw_sendmsg+0x61c/0x1180
Sep 28 10:37:20 localhost kernel: [<ffffffff8a625521>] ? inet_sendmsg+0xe1/0x130
Sep 28 10:37:20 localhost kernel: [<ffffffff8a517354>] ? sock_sendmsg+0x74/0x80
Sep 28 10:37:20 localhost kernel: [<ffffffff8a517d3f>] ? SYSC_sendto+0x22f/0x270
Sep 28 10:37:20 localhost kernel: [<ffffffff8a518ede>] ? SyS_sendto+0xe/0x10
Sep 28 10:37:20 localhost kernel: [<ffffffff89c03803>] ? do_syscall_64+0xe3/0x230
Sep 28 10:37:20 localhost kernel: [<ffffffff8a71e46b>] ? entry_SYSCALL64_slow_path+0x25/0x25
Sep 28 10:37:20 localhost kernel: [<ffffffffc08c8a9f>] tcpmss_tg4+0x6f/0x10e [xt_TCPMSS]
Sep 28 10:37:20 localhost kernel: [<ffffffffc06c99ca>] ipt_do_table+0x67a/0x790 [ip_tables]
Sep 28 10:37:20 localhost kernel: [<ffffffffc06c9350>] ? ip_tables_net_init+0x20/0x20 [ip_tables]
Sep 28 10:37:20 localhost kernel: [<ffffffffc06c984f>] ? ipt_do_table+0x4ff/0x790 [ip_tables]
Sep 28 10:37:20 localhost kernel: [<ffffffffc0798060>] ? iptable_mangle_net_exit+0x60/0x60 [iptable_mangle]
Sep 28 10:37:20 localhost kernel: [<ffffffffc07980b4>] iptable_mangle_hook+0x54/0x210 [iptable_mangle]
Sep 28 10:37:20 localhost kernel: [<ffffffffc0798060>] ? iptable_mangle_net_exit+0x60/0x60 [iptable_mangle]
Sep 28 10:37:20 localhost kernel: [<ffffffff8a5a4840>] nf_iterate+0xb0/0xd0
Sep 28 10:37:20 localhost kernel: [<ffffffff8a5a4923>] nf_hook_slow+0xc3/0x140
Sep 28 10:37:20 localhost kernel: [<ffffffff8a5a4860>] ? nf_iterate+0xd0/0xd0
Sep 28 10:37:20 localhost kernel: [<ffffffffc0798253>] ? iptable_mangle_hook+0x1f3/0x210 [iptable_mangle]
Sep 28 10:37:20 localhost kernel: [<ffffffff8a5c3215>] ip_output+0x1c5/0x1e0
Sep 28 10:37:20 localhost kernel: [<ffffffff8a5c3050>] ? ip_mc_output+0x490/0x490
Sep 28 10:37:20 localhost kernel: [<ffffffff8a5c1c10>] ? __ip_flush_pending_frames.isra.45+0x120/0x120
Sep 28 10:37:20 localhost kernel: [<ffffffff8a60b084>] raw_sendmsg+0xdb4/0x1180
Sep 28 10:37:20 localhost kernel: [<ffffffff8a60a2d0>] ? compat_raw_getsockopt+0x70/0x70
Sep 28 10:37:20 localhost kernel: [<ffffffff89ccfb0d>] ? SYSC_kill+0x15d/0x300
Sep 28 10:37:20 localhost kernel: [<ffffffff89c03803>] ? do_syscall_64+0xe3/0x230
Sep 28 10:37:20 localhost kernel: [<ffffffff8a71e46b>] ? entry_SYSCALL64_slow_path+0x25/0x25
Sep 28 10:37:20 localhost kernel: [<ffffffff89f033e5>] ? memcpy+0x45/0x50
Sep 28 10:37:20 localhost kernel: [<ffffffff8a29bb58>] ? tty_insert_flip_string_fixed_flag+0xa8/0x110
Sep 28 10:37:20 localhost kernel: [<ffffffff8a29b6f3>] ? tty_flip_buffer_push+0x63/0x70
Sep 28 10:37:20 localhost kernel: [<ffffffff89f038b9>] ? kasan_slab_free+0x89/0xc0
Sep 28 10:37:20 localhost kernel: [<ffffffff8a609010>] ? ip4_datagram_connect+0x50/0x50
Sep 28 10:37:20 localhost kernel: [<ffffffff89f1893c>] ? lock_page_memcg+0x2c/0xc0
Sep 28 10:37:20 localhost kernel: [<ffffffff89f173cb>] ? unlock_page_memcg+0x2b/0x80
Sep 28 10:37:20 localhost kernel: [<ffffffff89d2b310>] ? __wake_up_bit+0x80/0xe0
Sep 28 10:37:20 localhost kernel: [<ffffffff89d2b290>] ? init_wait_entry+0x70/0x70
Sep 28 10:37:20 localhost kernel: [<ffffffff89e66a4f>] ? unlock_page+0x3f/0x50
Sep 28 10:37:20 localhost kernel: [<ffffffff8a625521>] inet_sendmsg+0xe1/0x130
Sep 28 10:37:20 localhost kernel: [<ffffffff8a625440>] ? inet_recvmsg+0x1d0/0x1d0
Sep 28 10:37:20 localhost kernel: [<ffffffff8a517354>] sock_sendmsg+0x74/0x80
Sep 28 10:37:20 localhost kernel: [<ffffffff8a517d3f>] SYSC_sendto+0x22f/0x270
Sep 28 10:37:20 localhost kernel: [<ffffffff8a517b10>] ? SYSC_connect+0x220/0x220
Sep 28 10:37:20 localhost kernel: [<ffffffff89ebc427>] ? handle_mm_fault+0x447/0x1cb0
Sep 28 10:37:20 localhost kernel: [<ffffffff89ccf835>] ? group_send_sig_info+0x35/0x40
Sep 28 10:37:20 localhost kernel: [<ffffffff89c02510>] ? exit_to_usermode_loop+0xc0/0xc0
Sep 28 10:37:20 localhost kernel: [<ffffffff89c86be2>] ? __do_page_fault+0x552/0x610
Sep 28 10:37:20 localhost kernel: [<ffffffff8a518ed0>] ? SyS_getpeername+0x10/0x10
Sep 28 10:37:20 localhost kernel: [<ffffffff8a518ede>] SyS_sendto+0xe/0x10
Sep 28 10:37:20 localhost kernel: [<ffffffff89c03803>] do_syscall_64+0xe3/0x230
Sep 28 10:37:20 localhost kernel: [<ffffffff8a71e46b>] entry_SYSCALL64_slow_path+0x25/0x25
Sep 28 10:37:20 localhost kernel: Object at ffff880064660c80, in cache kmalloc-512 size: 512
Sep 28 10:37:20 localhost kernel: Allocated:
Sep 28 10:37:20 localhost kernel: PID = 1174
Sep 28 10:37:20 localhost kernel: save_stack_trace+0x1b/0x20
Sep 28 10:37:20 localhost kernel: save_stack+0x43/0xd0
Sep 28 10:37:20 localhost kernel: kasan_kmalloc+0xad/0xe0
Sep 28 10:37:20 localhost kernel: kasan_slab_alloc+0x12/0x20
Sep 28 10:37:20 localhost kernel: __kmalloc_node_track_caller+0x153/0x260
Sep 28 10:37:20 localhost kernel: __kmalloc_reserve.isra.38+0x31/0xa0
Sep 28 10:37:20 localhost kernel: __alloc_skb+0xe0/0x330
Sep 28 10:37:20 localhost kernel: alloc_skb_with_frags+0x7a/0x2a0
Sep 28 10:37:20 localhost kernel: sock_alloc_send_pskb+0x3ce/0x410
Sep 28 10:37:20 localhost kernel: sock_alloc_send_skb+0x18/0x20
Sep 28 10:37:20 localhost kernel: raw_sendmsg+0x9b0/0x1180
Sep 28 10:37:20 localhost kernel: inet_sendmsg+0xe1/0x130
Sep 28 10:37:20 localhost kernel: sock_sendmsg+0x74/0x80
Sep 28 10:37:20 localhost kernel: SYSC_sendto+0x22f/0x270
Sep 28 10:37:20 localhost kernel: SyS_sendto+0xe/0x10
Sep 28 10:37:20 localhost kernel: do_syscall_64+0xe3/0x230
Sep 28 10:37:20 localhost kernel: return_from_SYSCALL_64+0x0/0x6a
Sep 28 10:37:20 localhost kernel: Freed:
Sep 28 10:37:20 localhost kernel: PID = 642
Sep 28 10:37:20 localhost kernel: save_stack_trace+0x1b/0x20
Sep 28 10:37:20 localhost kernel: save_stack+0x43/0xd0
Sep 28 10:37:20 localhost kernel: kasan_slab_free+0x73/0xc0
Sep 28 10:37:20 localhost kernel: kfree+0x93/0x1a0
Sep 28 10:37:20 localhost kernel: vt_do_kdsk_ioctl+0x1d4/0x4e0
Sep 28 10:37:20 localhost kernel: vt_ioctl+0x14e3/0x18c0
Sep 28 10:37:20 localhost kernel: tty_ioctl+0x3c1/0x11f0
Sep 28 10:37:20 localhost kernel: do_vfs_ioctl+0x144/0x970
Sep 28 10:37:20 localhost kernel: SyS_ioctl+0x79/0x90
Sep 28 10:37:20 localhost kernel: do_syscall_64+0xe3/0x230
Sep 28 10:37:20 localhost kernel: return_from_SYSCALL_64+0x0/0x6a
Sep 28 10:37:20 localhost kernel: Memory state around the buggy address:
Sep 28 10:37:20 localhost kernel: ffff880064660d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Sep 28 10:37:20 localhost kernel: ffff880064660e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Sep 28 10:37:20 localhost kernel: >ffff880064660e80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
Sep 28 10:37:20 localhost kernel:                   ^
Sep 28 10:37:20 localhost kernel: ffff880064660f00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
Sep 28 10:37:20 localhost kernel: ffff880064660f80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb

やったね J( 'ー`)し kasan

感想

  • リモートから攻撃されうる CVE ≒ ネットワークスタックに苦手意識があったので、修正パッチサイズが小さいものを選び出して試行錯誤していたが、非常に勉強になった。
  • 具体的には、RAW ソケット や TCP や IP の理解が足りなかったのでヘッダの意味を調べたり、任意の値に改変して送信する方法を既存の実装から探ったりしていた
  • KASAN の強力さも理解できた。別の use-after-free や out-of-bounds の再現をする際にも使ってみよう

TCPMSS の --set-mss や --clamp-mss-to-pmtu の経験値がゼロなのだが、Path MTU Discovery を解決するものとあるので Linux でルータを構成してたりすると使うものなのかな? ここら、詳しい人いたら助言が欲しいところ