Denial of service in VCFtools 0.1.16
Loginsoft-2018-1007
August 29, 2018
CWE
CWE-20: Improper Input Validation
Product Details
VCFtools is a suite of functions for use on genetic variation data in the form of VCF and BCF files. The tools provided will be used mainly to summarize data, run calculations on data, filter out data, and convert data into other useful file formats.
URL: https://vcftools.github.io/
Vulnerable Versions
VCFtools 0.1.16
Vulnerability Details
A Denial of service was discovered in VCFtools 0.1.16 version.
SYNOPSIS
``` N_allele = n_allele_info >> 16; [1] N_info = n_allele_info & (uint32_t)65535; ALT_pos = line_pos; ``` ``` void bcf_entry::set_ALT(const int n_allele) { ALT.resize(n_allele-1); [2] ```
Vcftools binary while parsing an BCF file type, it calls the function variant_file::write_stats() which gets all the entries from the object & then attempts to parse those entries calling bcf_entry::parse_basic_entry(). Inside there’s a computation being done to calculate the value of number of allele `N_allele` that is by using `n_allele_info ` & right sifting it by 16 [1], the resultant value is then being passed to bcf_entry::set_ALT() . bcf_entry::set_ALT() responsible for setting the alleles. The value of N_allele is then passed to ALT.resize(), which decrements the value by 1 [2].
In case the value of n_allele_info is zero, the value of N_allele would be zero, which when passed to ALT_resize would then be subtract by 1, leaving a negative number ( -1). Whenever an invalid size is passed to vector::resize, it throws an std::length_error exception, internally calling abort(), raising a SIGABRT signal.
Fix: As a part of fix, a bound check is added to confirm if the number of alleles is greater than zero.
+ if (n_allele <= 0)
+ LOG.error(“Number of alleles must be positive.”);
ALT.resize(n_allele-1);
Commit: cdc24330ec86dff246152cde38ebb45b93035432
Analysis
gef➤ p n_allele $1 = 0x0 gef➤ ptype ALT type = std::vector<std::string>
Backtrace
gef➤ bt #0 0x00007ffff6f7b428 in __GI_raise (sig=sig@entry=0x6) at ../sysdeps/unix/sysv/linux/raise.c:54 #1 0x00007ffff6f7d02a in __GI_abort () at abort.c:89 #2 0x00007ffff78bd8f7 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #3 0x00007ffff78c3a46 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #4 0x00007ffff78c3a81 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #5 0x00007ffff78c3cb4 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #6 0x00007ffff78bf7a9 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #7 0x000000000041d08b in std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::_M_check_len (__s=0x4b6531 "vector::_M_fill_insert", __n=0xffffffffffffffff, this=0x70c348) at /usr/include/c++/5/bits/stl_vector.h:1425 #8 std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::_M_fill_insert (this=this@entry=0x70c348, __position=__position@entry=non-dereferenceable iterator for std::vector, __n=__n@entry=0xffffffffffffffff, __x="") at /usr/include/c++/5/bits/vector.tcc:489 #9 0x00000000004156e2 in std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::insert (__x="", __n=0xffffffffffffffff, __position=..., this=0x70c348) at /usr/include/c++/5/bits/stl_vector.h:1073 #10 std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::resize (__x=<error reading variable: access outside bounds of object referenced via synthetic pointer>, __new_size=0xffffffffffffffff, this=0x70c348) at /usr/include/c++/5/bits/stl_vector.h:716 #11 bcf_entry::set_ALT (this=this@entry=0x70bf00, n_allele=0x0) at bcf_entry_setters.cpp:13 #12 0x000000000040d7f6 in bcf_entry::parse_basic_entry (this=0x70bf00, parse_ALT=<optimized out>, parse_FILTER=<optimized out>, parse_INFO=<optimized out>) at bcf_entry.cpp:115 #13 0x0000000000475a6a in variant_file::write_stats (this=this@entry=0x709580, params=...) at variant_file_output.cpp:5416 #14 0x0000000000406436 in main (argc=<optimized out>, argv=<optimized out>) at vcftools.cpp:58 gef➤ i r rax 0x0 0x0 rbx 0x7071a8 0x7071a8 rcx 0x7ffff6f7b428 0x7ffff6f7b428 rdx 0x6 0x6 rsi 0xdcc 0xdcc rdi 0xdcc 0xdcc rbp 0x6d4b00 0x6d4b00 <stderr@@GLIBC_2.2.5> rsp 0x7fffffffcdb8 0x7fffffffcdb8 r8 0x7ffff730c770 0x7ffff730c770 r9 0x7ffff7fd9740 0x7ffff7fd9740 r10 0x8 0x8 r11 0x246 0x246 r12 0x70bce0 0x70bce0 r13 0xffffffffffffffff 0xffffffffffffffff r14 0x7fffffffd050 0x7fffffffd050 r15 0x70c348 0x70c348 rip 0x7ffff6f7b428 0x7ffff6f7b428 <__GI_raise+56> eflags 0x246 [ PF ZF IF ] cs 0x33 0x33 ss 0x2b 0x2b ds 0x0 0x0 es 0x0 0x0 fs 0x0 0x0 gs 0x0 0x0
Proof of Concept
vcftools --bcf $POC
Timeline
Vendor Disclosure: 2018-08-29
Patch Release: 2018-08-30
Public Disclosure: 2018-08-31
Credit
Discovered by ACE Team – Loginsoft