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