• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1External network fuzzing for Linux kernel
2=========================================
3
4syzkaller has support for external fuzzing of the network stack.
5This is achieved by using the [TUN/TAP](https://www.kernel.org/doc/Documentation/networking/tuntap.txt) interface.
6It allows to set up a virtual network interface and send packets to the kernel as they are being received from an external network.
7This triggers the same paths as a real packet delivered through a real network interface (except for the driver layer).
8
9You need to enable the `CONFIG_TUN` kernel config to enable external network fuzzing.
10See `initialize_tun()` in [executor/common_linux.h](/executor/common_linux.h) for the exact way the virtual interface is set up.
11
12The template descriptions can be found in [sys/linux/vnet.txt](/sys/linux/vnet.txt).
13At this moment there are 2 fake syscalls: `syz_emit_ethernet` and `syz_extract_tcp_res`.
14The first one externally sends a packet through the virtual interface.
15The second one tries to externally receive a packet back and parse TCP sequence numbers from it for use in subseqent packets.
16There are many protocols or protocol extensions that are not described yet, so the additions are welcome!
17
18Since fuzzing may be done in mutiple executor proccesses within the same VM instance, we need a way to isolate the virtual networks for different executors.
19Right now this is done by creating one virtual interface per executor and assigning different MAC, IPv4 and IPv6 addresses to each of these interfaces.
20Then the template descriptions make use of the `proc` type to generate proper addresses for each executor.
21
22Since many network protocols require checksum fields to be embedded into packets, there's a support for describing such fields.
23There's a `csum` type, which right now supports two different kinds of checksumming:
24[the Internet checksum](https://tools.ietf.org/html/rfc1071): `csum[parent, inet, int16be]`,
25and TCP-like pseudo header checksum: `csum[tcp_packet, pseudo, IPPROTO_TCP, int16be]`.
26The checksums are computed and embedded right before emitting a packet though the virtual interface.
27There's also a nice feature: when syzkaller generates a C reproducer, it generates code to compute checksums in runtime as well.
28
29By using `syz_emit_ethernet` and `syz_extract_tcp_res` the following syzkaller program is able to establish a TCP connection over IPv4:
30
31```
32mmap(&(0x7f0000000000/0x10000)=nil, (0x10000), 0x3, 0x32, 0xffffffffffffffff, 0x0)
33r0 = socket$inet_tcp(0x2, 0x1, 0x0)
34bind$inet(r0, &(0x7f0000001000)={0x2, 0x0, @empty=0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, 0x10)
35listen(r0, 0x5)
36syz_emit_ethernet(0x36, &(0x7f0000002000)={@local={[0xaa, 0xaa, 0xaa, 0xaa, 0xaa], 0x0}, @random="4c6112cc15d8", [], {{0x800, @ipv4={{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x6, 0x0, @remote={0xac, 0x14, 0x0, 0xbb}, @local={0xac, 0x14, 0x0, 0xaa}, {[]}}, @tcp={{0x1, 0x0, 0x42424242, 0x42424242, 0x0, 0x0, 0x5, 0x2, 0x0, 0x0, 0x0, {[]}}, {""}}}}}})
37syz_extract_tcp_res(&(0x7f0000003000)={<r1=>0x42424242, <r2=>0x42424242}, 0x1, 0x0)
38syz_emit_ethernet(0x38, &(0x7f0000004000)={@local={[0xaa, 0xaa, 0xaa, 0xaa, 0xaa], 0x0}, @remote={[0xbb, 0xbb, 0xbb, 0xbb, 0xbb], 0x0}, [], {{0x800, @ipv4={{0x5, 0x4, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x6, 0x0, @remote={0xac, 0x14, 0x0, 0xbb}, @local={0xac, 0x14, 0x0, 0xaa}, {[]}}, @tcp={{0x1, 0x0, r2, r1, 0x0, 0x0, 0x5, 0x10, 0x0, 0x0, 0x0, {[]}}, {"0c10"}}}}}})
39r3 = accept$inet(r0, &(0x7f0000005000)={0x0, 0x0, @multicast1=0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, &(0x7f0000006000)=0x10)
40```
41
42```
43Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
44tcp        0      0 0.0.0.0:20000           0.0.0.0:*               LISTEN      5477/a.out
45tcp        2      0 172.20.0.170:20000      172.20.0.187:20001      ESTABLISHED 5477/a.out
46```
47
48Similar program for IPv6:
49
50```
51mmap(&(0x7f0000000000/0x10000)=nil, (0x10000), 0x3, 0x32, 0xffffffffffffffff, 0x0)
52r0 = socket$inet6_tcp(0xa, 0x1, 0x0)
53bind$inet6(r0, &(0x7f0000000000)={0xa, 0x1, 0x0, @empty={[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, 0x0}, 0x1c)
54listen(r0, 0x5)
55syz_emit_ethernet(0x4a, &(0x7f0000001000)={@local={[0xaa, 0xaa, 0xaa, 0xaa, 0xaa], 0x0}, @random="de895db1468d", [], {{0x86dd, @ipv6={0x0, 0x6, "a228af", 0x14, 0x6, 0x0, @remote={0xfe, 0x80, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], 0x0, 0xbb}, @local={0xfe, 0x80, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], 0x0, 0xaa}, {[], @tcp={{0x0, 0x1, 0x42424242, 0x42424242, 0x0, 0x0, 0x5, 0x2, 0x0, 0x0, 0x0, {[]}}, {""}}}}}}})
56syz_extract_tcp_res(&(0x7f0000002000)={<r1=>0x42424242, <r2=>0x42424242}, 0x1, 0x0)
57syz_emit_ethernet(0x4a, &(0x7f0000003000)={@local={[0xaa, 0xaa, 0xaa, 0xaa, 0xaa], 0x0}, @random="de895db1468d", [], {{0x86dd, @ipv6={0x0, 0x6, "a228af", 0x14, 0x6, 0x0, @remote={0xfe, 0x80, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], 0x0, 0xbb}, @local={0xfe, 0x80, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], 0x0, 0xaa}, {[], @tcp={{0x0, 0x1, r2, r1, 0x0, 0x0, 0x5, 0x10, 0x0, 0x0, 0x0, {[]}}, {""}}}}}}})
58r3 = accept$inet6(r0, &(0x7f0000004000)={0x0, 0x0, 0x0, @empty={[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, 0x0}, &(0x7f0000005000)=0x1c)
59```
60
61```
62Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
63tcp6       0      0 :::20001                :::*                    LISTEN      5527/a.out
64tcp6       0      0 fe80::aa:20001          fe80::bb:20000          ESTABLISHED 5527/a.out
65```
66