bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: eval '<$(;)' causes Segmentation Fault


From: youheng.lue
Subject: RE: eval '<$(;)' causes Segmentation Fault
Date: Tue, 27 Aug 2024 00:44:31 +0200

>> These still cause the current devel branch to segfault, but (at least 
>> for me, on macOS) only when invoked via argument, as OP directed.  For 
>> example, reading the scripts via stdin avoids the segfault.
Tested on commit cf694865de527e597de5a906643a74037341a431 
I reproduced within a Docker container based on the official Bash development 
image (bash:devel), using Alpine Linux 3.20 with GCC as the compiler. Detailed 
Dockerfile is at the end of the mail.

Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-musl
Compiler: gcc
Compilation CFLAGS: -g -O2
uname output: Linux 839141c7e5ba 6.8.0-40-generic #40~22.04.3-Ubuntu SMP 
PREEMPT_DYNAMIC Tue Jul 30 17:30:19 UTC 2 x86_64 Linux
Machine Type: x86_64-pc-linux-musl

Bash Version: 5.3
Patch Level: 0
Release Status: alpha

POC:
$ cat poc.sh
eval '<${;}'

> The specific case is an empty command containing only a redirection that 
> results in an expansion error read from a script or string.
I can confirm that the error is triggerted in the "execute_null_command" 
function and later containing a redirection.
Specifically the variable `INPUT_STREAM bashinput.location` is both a char 
pointer and an int.


At first it is used as a char pointer in the function "parse_and_execute"

BEFORE 
```
gdb> p bash_input.location.string
$3 = 0x7fb3dc0db3b0 "<${;}"
```
However at shell.c:1758 in the fuction unset_bash_input it gets overwritten to 
a fd:
```
bash_input.location.buffered_fd = -1;
```
which overwrites the last 4 bytes of the pointer with 0xffffffff
Backtrace for unset_bash_input:
► 0   0x5c55279555d3 unset_bash_input+35
   1   0x5c5527986ed8 make_child+952
   2   0x5c552796bb73 execute_null_command+387
   3   0x5c55279703fa execute_command_internal+9786
   4   0x5c55279703fa execute_command_internal+9786
   5   0x5c55279ce969 parse_and_execute+1513


AFTER
```
gdb> p bash_input.location.string
$14 = 0x7fb3ffffffff <error: Cannot access memory at address 0x7fb3ffffffff>
```
This leads to an OOB read, which leads to a segfault at evalstring.c:359
```c
while (*(bash_input.location.string) || parser_expanding_alias ())
```


Security:
   This might pose a security risk, since an attacker might leverage this to 
break out of a restricted shell.
   1. It might be possible for them to get lucky with ASLR and get a useful OOB 
read that doesn't crash.
   2. An attacker controls the size of `bash_input.location.string` which is 
allocated and therefore have some control where the OOB Read happens

Proposed fix:
    I suggest modifying INPUT_STREAM from a union to a struct to prevent 
    the type confusion between pointer and int that leads to the OOB read. 
    This is a theoretical fix and I have not tested it yet, so further 
    validation is required to confirm its effectiveness.


####### Dockerfile ##########
FROM alpine:3.20

# https://git.savannah.gnu.org/cgit/bash.git/log/?h=devel
ENV _BASH_COMMIT cf694865de527e597de5a906643a74037341a431
# optimize asynchronous function invocations; fix for running return from trap 
while sourcing a file; restore completion function if read -e is interrupted
ENV _BASH_VERSION devel-20240815
# prefixed with "_" since "$BASH..." have meaning in Bash parlance

RUN set -eux; \
        \
        apk add --no-cache --virtual .build-deps \
                bison \
                coreutils \
                dpkg-dev dpkg \
                gcc \
                libc-dev \
                make \
                ncurses-dev \
                patch \
                tar \
        ; \
        \
        wget -O bash.tar.gz 
"https://git.savannah.gnu.org/cgit/bash.git/snapshot/bash-$_BASH_COMMIT.tar.gz";;
 \
        \
        mkdir -p /usr/src/bash; \
        tar \
                --extract \
                --file=bash.tar.gz \
                --strip-components=1 \
                --directory=/usr/src/bash \
        ; \
        rm bash.tar.gz; \
        \
        if [ -d bash-patches ]; then \
                apk add --no-cache --virtual .patch-deps patch; \
                for p in bash-patches/*; do \
                        patch \
                                --directory=/usr/src/bash \
                                --input="$(readlink -f "$p")" \
                                --strip=0 \
                        ; \
                        rm "$p"; \
                done; \
                rmdir bash-patches; \
                apk del --no-network .patch-deps; \
        fi; \
        \
# https://lists.gnu.org/archive/html/bug-bash/2023-05/msg00011.html
        { echo '#include <unistd.h>'; echo; cat /usr/src/bash/lib/sh/strscpy.c; 
} > /usr/src/bash/lib/sh/strscpy.c.new; \
        mv /usr/src/bash/lib/sh/strscpy.c.new /usr/src/bash/lib/sh/strscpy.c; \
        \
        cd /usr/src/bash; \
        gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
        ./configure \
                --build="$gnuArch" \
                --enable-readline \
                --with-curses \
# musl does not implement brk/sbrk (they simply return -ENOMEM)
#   bash: xmalloc: locale.c:81: cannot allocate 18 bytes (0 bytes allocated)
                --without-bash-malloc \
        || { \
                cat >&2 config.log; \
                false; \
        }; \
        make -j "$(nproc)"; \
        make install; \
        cd /; \
# delete a few installed bits for smaller image size
        rm -rf \
                /usr/local/share/doc/bash/*.html \
                /usr/local/share/info \
                /usr/local/share/locale \
                /usr/local/share/man \
        ; \
        \
        runDeps="$( \
                scanelf --needed --nobanner --format '%n#p' --recursive 
/usr/local \
                        | tr ',' '\n' \
                        | sort -u \
                        | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { 
next } { print "so:" $1 }' \
        )"; \
        apk add --no-network --virtual .bash-rundeps $runDeps; \
        apk del --no-network .build-deps; \
        \
        [ "$(which bash)" = '/usr/local/bin/bash' ]; \
        bash --version; \
        bash -c 'help' > /dev/null

## BELOW HERE ARE CUSTOM TOOLS USED FOR DEBUGGING
# Install 
RUN set -eux; \
        \
    apk add gdb; \
    apk add git; 

# pwndbg
# RUN set -eux; \
#    apk add nix; \
#    nix shell github:pwndbg/pwndbg --extra-experimental-features nix-command 
--extra-experimental-features flakes
 
-----------
Best regards,
Youheng Lü





reply via email to

[Prev in Thread] Current Thread [Next in Thread]