Path: csiph.com!xmission!news.snarked.org!news.linkpendium.com!news.linkpendium.com!panix!usenet.stanford.edu!not-for-mail From: Corbin Souffrant Newsgroups: gnu.bash.bug Subject: Re: Use-After-Free in Bash Date: Tue, 30 Oct 2018 16:54:09 -0700 Lines: 173 Approved: bug-bash@gnu.org Message-ID: References: NNTP-Posting-Host: lists.gnu.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit X-Trace: usenet.stanford.edu 1540948923 27687 208.118.235.17 (31 Oct 2018 01:22:03 GMT) X-Complaints-To: action@cs.stanford.edu To: bug-bash@gnu.org Envelope-to: bug-bash@gnu.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:to:references:from:message-id:date:user-agent:mime-version :in-reply-to:content-transfer-encoding:content-language; bh=2KtkDRfC6xN3eMuaBrtgG+mFalDx2JWl96czyDqbc3w=; b=Q+9wtnSz8vHPFXUgEaj35/8goKiExBuPeU7uz8SJKJsrfXGnpyw6QowqzdvOY7rizv YLeA1zbHB/bL6BThkM144j1kLVW15PXUQ14ztnUevHMFuQwQHxn3rfYMJMq0O3DyEyQg IxYdZ58yRAKBCbpkX9aBtQKegjIseKM0zBtdnS/xHYZJC5oDou4bo6EvmiKQ7iKr8S7G brjUvDRqzipZV9Pisjw2HydJIo6tBqvtG8bXi0LrnoMZyGNboKIN4/MGeyCHsuoSIN7a /kvxj/oAWopOiXYDtqBbVEizokCFrLwrcV+Iknz7ZoHHbpvOYL7Q26/cOwpGRgiPQqck EuQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-transfer-encoding :content-language; bh=2KtkDRfC6xN3eMuaBrtgG+mFalDx2JWl96czyDqbc3w=; b=COSJYwDHRu/IipGfhjQIuzeov8ZuXvB/duZdbh5KmrnpcL9ndYUFx2czMPcMNF++RQ S4uAwcRC4/tH8/gS9f7XohFlfS1zdSFtVba9fxA4Yre/hZj/srjhaq3GpHchznGnQZRj awgBEs/ce984tqp1m8Q12PVcPoAmAYyg8WDa8slBtZP6UMuqj7Qb5VSH52QkUnSLwWiU jJlRcjm1YK9K6ZefTIW+Zc+WBYGDmHjQsU8JDZ2VzuD2SybZ1zUp1gbr291HqshhrczN nz8M6M3yTxeLblafRs2RUnK/N+YkmvsW5jENBoZJgzE7wMqEjtmqEaKhIoelkVeTX7LM fjKg== X-Gm-Message-State: AGRZ1gKRrlhOZP5MWTb9G2yIlOKTEO+ersI5Ne00x05YKpHG+TCZI6BC 8T/DrBzt5x7MOjmVPYhrb5NfPEW7 X-Google-Smtp-Source: AJdET5fv70EmPKaD8SCKIwT5KOx53xmARwNMNrKt77cxrR3pZHZGqWrnlrBkfwzaaRr6OeaIFnoOwQ== X-Received: by 2002:a17:902:b692:: with SMTP id c18-v6mr793913pls.191.1540943698728; Tue, 30 Oct 2018 16:54:58 -0700 (PDT) User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:60.0) Gecko/20100101 Thunderbird/60.2.1 In-Reply-To: Content-Language: en-US X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:4864:20::62f X-Mailman-Approved-At: Tue, 30 Oct 2018 21:22:00 -0400 X-BeenThere: bug-bash@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Bug reports for the GNU Bourne Again SHell List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Xref: csiph.com gnu.bash.bug:14749 I emailed with Chet today and got approval to post the writeup here. He has already applied the patch. Thanks again for the fast response! -Corbin Use After Free Writeup: In bash <3.2 using ^C while in a large brace expansion would slowly eat memory with no way to ^C, so in bash 3.2 (2006-10-11) they introduced the ability to ^C. However with this, they introduced a memory leak (that is noted in the source code). In bash 4.4-beta (2015-10-12), a patch for this was introduced which caused the following use-after-free that exists in all versions of the code since. The following writeup used bash-4.4.18.tar.gz    2018-01-30 16:15    9.0M from the URL http://ftp.gnu.org/gnu/bash/bash-4.4.18.tar.gz Version checking: ./bash --version GNU bash, version 4.4.18(2)-release (x86_64-unknown-linux-gnu) First run the following and ^C the execution immediately: [loliponi@loliponi bash-4.4.18]$ for i in {0..7777777}; do echo $i; done ^C[1]    29966 segmentation fault (core dumped)  ./bash Before jumping into the debug, I will note that I made two modifications to the build to make the debug easier to follow, but it works without these changes: 1) Change CFLAGS optimatzation flags to -O0 2) Modify lib/sh/stringvec.c:83 (strvec_flush()) to "volatile register int i;" After opening up the coredump in gdb I ran bt to see the current stack frames. (gdb) bt #0  0x000055c5e3eecb07 in internal_free (mem=0xdfdfdfdfdfdfdfdf, file=0x55c5e3f065a8 "stringvec.c", line=89,     flags=) at malloc.c:858 #1  0x000055c5e3ea2d31 in sh_xfree (string=0xdfdfdfdfdfdfdfdf, file=0x55c5e3f065a8 "stringvec.c", line=89)     at xmalloc.c:221 #2  0x000055c5e3ec5657 in strvec_flush (array=array@entry=0x55c5e5d2d008) at stringvec.c:89 #3  0x000055c5e3ec569e in strvec_dispose (array=0x55c5e5d2d008) at stringvec.c:99 #4  0x000055c5e3e8e5f6 in mkseq (start=0, end=7777777, incr=1, type=1, width=0) at braces.c:439 The interesting behavior happens because strvec_flush called free() with a bad value, so lets find out why... (gdb) f 2 #2  0x000055c5e3ec5657 in strvec_flush (array=array@entry=0x55c5e5d2d008) at stringvec.c:89 89        free (array[i]); The currrent iteration in the loop when I hit ^C (gdb) info local i = 5297567 (5297567 * 8) + 0x55c5e5d2d008 = 55C5E8597D00 The contents of the array. Note that at exactly the expected offset, we see a corresponding set of 0xdfdfdfdf where the crash occured. This appears to be some kind of guard page or zero'd out memory. On some systems, we are not seeing this memory being set to zero'd value, leading to a potential compromise with a use after free vulnerability, due to the fact that an arbitrary address left in stale memory via a heap spray would lead to a free that could be controlled to point at a function pointer that the attacker would allocate over with a new malicious address. (gdb) x/8wx 0x55c5e5d2d008 0x55c5e5d2d008:    0xe5cb42d8    0x000055c5    0xe5cb42e8 0x000055c5 0x55c5e5d2d018:    0xe5cb42f8    0x000055c5    0xe5cb4308 0x000055c5 [...] (gdb) x/8wx 0x55c5e8597cf0 0x55c5e8597cf0:    0xf3ed2fe8    0x000055c5    0xf3ed3008 0x000055c5 0x55c5e8597d00:    0xdfdfdfdf    0xdfdfdfdf    0xdfdfdfdf 0xdfdfdfdf Code segments for clarity: braces.c contains the function mkseq which calls strvec_dispose on char **result if an interrupt is triggered while it is building the array. static char ** mkseq (start, end, incr, type, width)      intmax_t start, end, incr;      int type, width; {   intmax_t n, prevn;   int i, j, nelem;   char **result, *t; [...] do { #if defined (SHELL)       if (ISINTERRUPT) {           strvec_dispose (result);           result = (char **)NULL; } QUIT; #endif [...] }   while (1); [...] } In lib/sh/stringvec.c, strvec_dispose() calls strvec_flush() in the same file. void strvec_dispose (array)      char **array; {   if (array == 0) return;   strvec_flush (array);   free (array); } Finally strvec_flush() iterates through all the values in the array and falsely assumes that unallocated entries will be set to 0, leading to the bug. void strvec_flush (array)      char **array; {   register int i;   if (array == 0) return;   for (i = 0; array[i]; i++)     free (array[i]); } A patch for this bug would involve either zero'ing out allocated memory when it is retrieved or marking where the last entry was written. This is an example of a patch that would fix the issue. I wasn't sure if I should diff it with 5.0 alpha or 4.4.18, so I diff'd it with 4.4.18 and here is where the code changes. braces.c:L439 result[i] = (char *)0; // Signal to free where the current position is. strvec_dispose (result); $ diff braces.c braces.c.new 438a439 >           result[i] = (char *)0; // Signal to free where the current position is. On 10/30/2018 12:31 PM, Corbin Souffrant wrote: > Hello, > > I found a reproducible use-after-free in every version of Bash from > 4.4-5.0beta, that could potentially be used to escape restricted mode. I > say potentially, because I can get it to crash in restricted mode, but I > haven't gone through the effort of attempting to heap spray to overwrite > function pointers. > > I read in previous threads that you don't consider most crashes in Bash to > be security issues, but before I posted something to the public mailing > list, I wanted to be sure that this was the correct place to do so. If not, > who should I email? I have a writeup, with repro and patch that I think > should work. :) > > Thanks! > Corbin Souffrant > > >