How to find 0days
in the Linux kernel
Andrey Konovalov <andreyknvl@google.com>
PHDays 2017
May 23-24th 2017
Userspace tools
● AddressSanitizer, ThreadSanitizer, MemorySanitizer, ...
○ C/C++/Go bug detectors
○ Detect wide range of bug types (use-after-frees and out-of-bounds, data
races and deadlocks, uninitialized memory uses, undefined behaviors)
○ Built into Clang, GCC and Go compiler
● libFuzzer
○ In-process coverage-guided fuzzing engine for C/C++
● oss-fuzz
○ Continuous fuzzing service for open source software
Finding bugs in the Linux kernel
Dynamic bug finding tools:
● Bug detectors
● Fuzzers
Bug detectors:
KASAN, KTSAN, KMSAN
Kernel Sanitizers
● KASAN (use-after-frees and out-of-bounds)
○ CONFIG_KASAN available upstream since 4.0
● KTSAN (data-races and deadlocks)
○ prototype available at https://github.com/google/ktsan
● KMSAN (uninitialized-memory-use)
○ prototype available at https://github.com/google/kmsan
KASAN report
BUG: KASAN: use-after-free in sctp_id2assoc+0x3a3/0x3b0
net/sctp/socket.c:224
Read of size 8 at addr ffff8800385b6838 by task syz-executor2/5889
Call Trace:
sctp_id2assoc+0x3a3/0x3b0 net/sctp/socket.c:224
sctp_setsockopt_rtoinfo net/sctp/socket.c:2931 [inline]
sctp_setsockopt+0x4b7/0x60f0 net/sctp/socket.c:4018
sock_common_setsockopt+0x95/0xd0 net/core/sock.c:2850
SYSC_setsockopt net/socket.c:1798 [inline]
SyS_setsockopt+0x270/0x3a0 net/socket.c:1777
entry_SYSCALL_64_fastpath+0x1f/0xbe
KASAN report
Allocated by task 5841:
kzalloc include/linux/slab.h:663 [inline]
sctp_association_new+0x114/0x2180 net/sctp/associola.c:309
...
SyS_connect+0x24/0x30 net/socket.c:1569
entry_SYSCALL_64_fastpath+0x1f/0xbe
Freed by task 5882:
kfree+0xe8/0x2b0 mm/slub.c:3882
sctp_association_destroy net/sctp/associola.c:435 [inline]
...
syscall_return_slowpath+0x3ba/0x410 arch/x86/entry/common.c:263
entry_SYSCALL_64_fastpath+0xbc/0xbe
Fuzzer:
syzkaller
syzkaller
● Coverage-guided syscall fuzzer for the Linux kernel
● As of now found over 400 bugs
(https://github.com/google/syzkaller/wiki/Found-Bugs)
● Over 25 CVEs
● At least 3 public local privilege escalation bugs over the
last few months (CVE-2017-7308, CVE-2017-6074,
CVE-2017-2636)
● Can generate C reproducers for found bugs
Coverage-guided fuzzing
(Think AFL or libFuzzer)
void TestOneInput(const char *data, int size) {
/* do something with data */
}
Fuzzer invokes the function with different inputs
Code coverage guiding:
● Corpus of “interesting” inputs
● Mutate and execute inputs from corpus
● If inputs gives new coverage, add it to corpus
Kernel inputs
Kernel
Userspace
syscalls
network ... USB
Syscall descriptions
open(file filename, flags flags[open_flags],
mode flags[open_mode]) fd
read(fd fd, buf buffer[out], count len[buf])
close(fd fd)
open_flags = O_RDONLY, O_WRONLY, O_RDWR, O_APPEND ...
Syscall descriptions:
discriminated syscalls
socket$inet_tcp(
domain const[AF_INET],
type const[SOCK_STREAM],
proto const[0]) sock_tcp
socket$packet(
domain const[AF_PACKET],
type flags[packet_socket_type],
proto const[ETH_P_ALL_BE]) sock_packet
Syscall descriptions:
structs layout
sockaddr_ll {
sll_family const[AF_PACKET, int16]
sll_protocol flags[packet_protocols, int16be]
sll_ifindex ifindex
sll_hatype const[ARPHRD_ETHER, int16]
sll_pkttype int8
sll_halen const[6, int8]
sll_addr mac_addr
pad array[const[0, int8], 2]
}
bind$packet(fd sock_packet, addr ptr[in, sockaddr_ll], addrlen len[addr])
Syscall descriptions:
resources
resource sock[fd]
socket(domain flags[socket_domain], type flags[socket_type],
proto int8) sock
bind(fd sock, addr ptr[in, sockaddr_storage], addrlen len[addr])
resource sock_packet[sock]
socket$packet(domain const[AF_PACKET], type flags[packet_socket_type],
proto const[ETH_P_ALL_BE]) sock_packet
bind$packet(fd sock_packet, addr ptr[in, sockaddr_ll], addrlen len[addr])
Programs
The description allows to generate and mutate "programs" in
the following form:
mmap(&(0x7f0000000000), (0x1000), 0x3, 0x32, -1, 0)
r0 = open(&(0x7f0000000000)="./file0", 0x3, 0x9)
read(r0, &(0x7f0000000000), 42)
close(r0)
Algorithm
1. Start with empty corpus of programs
2. Generate a new program, or choose an existing program
from corpus and mutate it
3. Interpret the program, collect coverage
4. If a syscall covers code that wasn't covered previously,
minimize program and add to corpus
5. Goto 1
External kernel inputs
Kernel
Userspace
syscalls
network ... USB
TUN/TAP
gadgetfs /
functionfs
Demo
Finding 0days in the Linux kernel
1. Setup syzkaller
2. Write syscall descriptions for some previously untested
kernel interface
3. Specify syscalls required to interact with this interface in
the configuration file
4. Run syzkaller and discover bugs
Questions?
https://github.com/google/kasan/wiki
https://github.com/google/syzkaller
kasan-dev@googlegroups.com
syzkaller@googlegroups.com
Andrey Konovalov, andreyknvl@google.com
Dmitry Vyukov, dvyukov@google.com
Backup
Coverage for the Linux kernel
● Available upstream with CONFIG_KCOV
● GCC pass that inserts a function call into every basic block
● kernel debugfs extension that collects and exposes coverage per-thread
__fuzz_coverage();
if (...) {
__fuzz_coverage();
...
}
__fuzz_coverage();
if (...) {
...
}
Process structure
Kernel system call fuzzers
● Trinity (https://github.com/kernelslacker/trinity)
● syzkaller (https://github.com/google/syzkaller)
Other Linux kernel fuzzers
● https://github.com/oracle/kernel-fuzzing
● https://github.com/nccgroup/TriforceLinuxSyscallFuzzer
● http://web.eece.maine.edu/~vweaver/projects/perf_events/f
uzzer/
● https://github.com/schumilo/vUSBf
External Stimulus
Systems calls and external stimulus in the same program:
listen(r0)
emit_ethernet(syn)
emit_ethernet(ack)
r1 = accept(r0)
emit_ethernet(data)
read(r1)
emit_ethernet(rst)
Work in progress; also applicable to USB, ...
Existing system call fuzzers
Trinity in essence:
syscall(rand(), rand(), rand());
Knows argument types, so more like:
syscall(rand(), rand_fd(), rand_addr());
● Tend to find shallow bugs
● Frequently no reproducers

Ищем уязвимости нулевого дня в ядре Linux

  • 1.
    How to find0days in the Linux kernel Andrey Konovalov <andreyknvl@google.com> PHDays 2017 May 23-24th 2017
  • 2.
    Userspace tools ● AddressSanitizer,ThreadSanitizer, MemorySanitizer, ... ○ C/C++/Go bug detectors ○ Detect wide range of bug types (use-after-frees and out-of-bounds, data races and deadlocks, uninitialized memory uses, undefined behaviors) ○ Built into Clang, GCC and Go compiler ● libFuzzer ○ In-process coverage-guided fuzzing engine for C/C++ ● oss-fuzz ○ Continuous fuzzing service for open source software
  • 3.
    Finding bugs inthe Linux kernel Dynamic bug finding tools: ● Bug detectors ● Fuzzers
  • 4.
  • 5.
    Kernel Sanitizers ● KASAN(use-after-frees and out-of-bounds) ○ CONFIG_KASAN available upstream since 4.0 ● KTSAN (data-races and deadlocks) ○ prototype available at https://github.com/google/ktsan ● KMSAN (uninitialized-memory-use) ○ prototype available at https://github.com/google/kmsan
  • 6.
    KASAN report BUG: KASAN:use-after-free in sctp_id2assoc+0x3a3/0x3b0 net/sctp/socket.c:224 Read of size 8 at addr ffff8800385b6838 by task syz-executor2/5889 Call Trace: sctp_id2assoc+0x3a3/0x3b0 net/sctp/socket.c:224 sctp_setsockopt_rtoinfo net/sctp/socket.c:2931 [inline] sctp_setsockopt+0x4b7/0x60f0 net/sctp/socket.c:4018 sock_common_setsockopt+0x95/0xd0 net/core/sock.c:2850 SYSC_setsockopt net/socket.c:1798 [inline] SyS_setsockopt+0x270/0x3a0 net/socket.c:1777 entry_SYSCALL_64_fastpath+0x1f/0xbe
  • 7.
    KASAN report Allocated bytask 5841: kzalloc include/linux/slab.h:663 [inline] sctp_association_new+0x114/0x2180 net/sctp/associola.c:309 ... SyS_connect+0x24/0x30 net/socket.c:1569 entry_SYSCALL_64_fastpath+0x1f/0xbe Freed by task 5882: kfree+0xe8/0x2b0 mm/slub.c:3882 sctp_association_destroy net/sctp/associola.c:435 [inline] ... syscall_return_slowpath+0x3ba/0x410 arch/x86/entry/common.c:263 entry_SYSCALL_64_fastpath+0xbc/0xbe
  • 8.
  • 9.
    syzkaller ● Coverage-guided syscallfuzzer for the Linux kernel ● As of now found over 400 bugs (https://github.com/google/syzkaller/wiki/Found-Bugs) ● Over 25 CVEs ● At least 3 public local privilege escalation bugs over the last few months (CVE-2017-7308, CVE-2017-6074, CVE-2017-2636) ● Can generate C reproducers for found bugs
  • 10.
    Coverage-guided fuzzing (Think AFLor libFuzzer) void TestOneInput(const char *data, int size) { /* do something with data */ } Fuzzer invokes the function with different inputs Code coverage guiding: ● Corpus of “interesting” inputs ● Mutate and execute inputs from corpus ● If inputs gives new coverage, add it to corpus
  • 11.
  • 12.
    Syscall descriptions open(file filename,flags flags[open_flags], mode flags[open_mode]) fd read(fd fd, buf buffer[out], count len[buf]) close(fd fd) open_flags = O_RDONLY, O_WRONLY, O_RDWR, O_APPEND ...
  • 13.
    Syscall descriptions: discriminated syscalls socket$inet_tcp( domainconst[AF_INET], type const[SOCK_STREAM], proto const[0]) sock_tcp socket$packet( domain const[AF_PACKET], type flags[packet_socket_type], proto const[ETH_P_ALL_BE]) sock_packet
  • 14.
    Syscall descriptions: structs layout sockaddr_ll{ sll_family const[AF_PACKET, int16] sll_protocol flags[packet_protocols, int16be] sll_ifindex ifindex sll_hatype const[ARPHRD_ETHER, int16] sll_pkttype int8 sll_halen const[6, int8] sll_addr mac_addr pad array[const[0, int8], 2] } bind$packet(fd sock_packet, addr ptr[in, sockaddr_ll], addrlen len[addr])
  • 15.
    Syscall descriptions: resources resource sock[fd] socket(domainflags[socket_domain], type flags[socket_type], proto int8) sock bind(fd sock, addr ptr[in, sockaddr_storage], addrlen len[addr]) resource sock_packet[sock] socket$packet(domain const[AF_PACKET], type flags[packet_socket_type], proto const[ETH_P_ALL_BE]) sock_packet bind$packet(fd sock_packet, addr ptr[in, sockaddr_ll], addrlen len[addr])
  • 16.
    Programs The description allowsto generate and mutate "programs" in the following form: mmap(&(0x7f0000000000), (0x1000), 0x3, 0x32, -1, 0) r0 = open(&(0x7f0000000000)="./file0", 0x3, 0x9) read(r0, &(0x7f0000000000), 42) close(r0)
  • 17.
    Algorithm 1. Start withempty corpus of programs 2. Generate a new program, or choose an existing program from corpus and mutate it 3. Interpret the program, collect coverage 4. If a syscall covers code that wasn't covered previously, minimize program and add to corpus 5. Goto 1
  • 18.
    External kernel inputs Kernel Userspace syscalls network... USB TUN/TAP gadgetfs / functionfs
  • 19.
  • 20.
    Finding 0days inthe Linux kernel 1. Setup syzkaller 2. Write syscall descriptions for some previously untested kernel interface 3. Specify syscalls required to interact with this interface in the configuration file 4. Run syzkaller and discover bugs
  • 21.
  • 22.
  • 23.
    Coverage for theLinux kernel ● Available upstream with CONFIG_KCOV ● GCC pass that inserts a function call into every basic block ● kernel debugfs extension that collects and exposes coverage per-thread __fuzz_coverage(); if (...) { __fuzz_coverage(); ... } __fuzz_coverage(); if (...) { ... }
  • 24.
  • 25.
    Kernel system callfuzzers ● Trinity (https://github.com/kernelslacker/trinity) ● syzkaller (https://github.com/google/syzkaller)
  • 26.
    Other Linux kernelfuzzers ● https://github.com/oracle/kernel-fuzzing ● https://github.com/nccgroup/TriforceLinuxSyscallFuzzer ● http://web.eece.maine.edu/~vweaver/projects/perf_events/f uzzer/ ● https://github.com/schumilo/vUSBf
  • 27.
    External Stimulus Systems callsand external stimulus in the same program: listen(r0) emit_ethernet(syn) emit_ethernet(ack) r1 = accept(r0) emit_ethernet(data) read(r1) emit_ethernet(rst) Work in progress; also applicable to USB, ...
  • 28.
    Existing system callfuzzers Trinity in essence: syscall(rand(), rand(), rand()); Knows argument types, so more like: syscall(rand(), rand_fd(), rand_addr()); ● Tend to find shallow bugs ● Frequently no reproducers