CVE: CVE-2021-30836

Tested Versions:

  • webkitGTK2.32.0

Product URL(s):

Description of the vulnerability

In order to show how we can reproduce it, let’s open poc.html in webkitgtk version 2.32.0 within Ubuntu.

Alternatively, you may want to use my docker script to build. Source code of build.sh

docker build . -t webkit_asan
docker run -it  --name=webkit2.32.0 webkit_asan /bin/bash

Source code of Dockerfile

FROM ubuntu:18.04
MAINTAINER mipu94
RUN echo ${WEBKIT_VERSION}
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get -y update && \
    apt-get install -y wget \
    cmake \
    bison \
    git \
    unzip \
    xz-utils \
    apache2 \
    llvm-7 \ 
    clang-7 \
    libclang-7-dev \
    tzdata \
    sed \ 
    ruby

WORKDIR /root/

# install ninja
RUN wget https://github.com/ninja-build/ninja/releases/download/v1.10.0/ninja-linux.zip \
 && unzip ninja-linux.zip \
 && mv ninja /usr/local/bin/ \
 && rm ninja-linux.zip

# install clang
RUN wget https://prereleases.llvm.org/10.0.0/rc3/clang+llvm-10.0.0-rc3-x86_64-linux-gnu-ubuntu-18.04.tar.xz \
 && tar xvf clang+llvm-10.0.0-rc3-x86_64-linux-gnu-ubuntu-18.04.tar.xz \
 && mv clang+llvm-10.0.0-rc3-x86_64-linux-gnu-ubuntu-18.04 clang

ENV WEBKIT_VERSION="2.32.0" \
    FUZZ_TYPE="address"
RUN  apt-get update --fix-missing
# download webkit
RUN wget https://webkitgtk.org/releases/webkitgtk-${WEBKIT_VERSION}.tar.xz && \
    tar xvf webkitgtk-${WEBKIT_VERSION}.tar.xz && \
    cd webkitgtk-${WEBKIT_VERSION} && \
    printf 'y\n' | ./Tools/gtk/install-dependencies 


WORKDIR /root/webkitgtk-${WEBKIT_VERSION}
# patch asan build
#address, memory
ENV ASAN_TYPE=address  
RUN sed -i 's~COMMAND CC=${CMAKE_C_COMPILER} CFLAGS=-Wno-deprecated-declarations LDFLAGS=~COMMAND CC=${CMAKE_C_COMPILER} CFLAGS=\\\"-Wno-deprecated-declarations -fsanitize=address\\\" LDFLAGS=\\\"-fsanitize=address\\\"~g' Source/WebKit/PlatformGTK.cmake

RUN sed -i 's~COMMAND CC=${CMAKE_C_COMPILER} CFLAGS=-Wno-deprecated-declarations~COMMAND CC=${CMAKE_C_COMPILER} CFLAGS=\\\"-Wno-deprecated-declarations -fsanitize=address\\\"~g' Source/WebKit/PlatformGTK.cmake

RUN sed -i 's~LDFLAGS="${INTROSPECTION_ADDITIONAL_LDFLAGS}"~LDFLAGS=\\\"${INTROSPECTION_ADDITIONAL_LDFLAGS} -fsanitize=address\\\"~g' Source/WebKit/PlatformGTK.cmake
ENV CC="$BUILD_PATH/clang/bin/clang" \
      CXX="$BUILD_PATH/clang/bin/clang++" \
      CFLAGS="-fsanitize=$ASAN_TYPE" \
      CXXFLAGS="-fsanitize=$ASAN_TYPE" \
      LDFLAGS="-fsanitize=$ASAN_TYPE" \
      ASAN_OPTIONS="detect_leaks=0"
RUN mkdir mybuild && cd mybuild && cmake \
      -DCMAKE_BUILD_TYPE=Release  \
      -DCMAKE_INSTALL_PREFIX=/usr \
      -DCMAKE_SKIP_RPATH=ON       \
      -DPORT=GTK                  \
      -DLIB_INSTALL_DIR=/usr/lib  \
      -DUSE_LIBHYPHEN=OFF         \
      -DENABLE_MINIBROWSER=ON     \
      -DENABLE_GAMEPAD=OFF        \
      -DUSE_WOFF2=OFF             \
      -DUSE_WPE_RENDERER=OFF      \
      -DENABLE_BUBBLEWRAP_SANDBOX=OFF \
-Wno-dev -G Ninja -DCMAKE_C_COMPILER="/root/clang/bin/clang" -DCMAKE_CXX_COMPILER="/root/clang/bin/clang++" .. && ninja && ninja install

RUN cp -rf mybuild /root/webkitASAN

CMD ["/bin/bash"]
    # ./build.sh
    # export DISPLAY=:1000
    # Xvfb :1000 -screen 0 1920x1080x24 &
    # DISPLAY=:1000 LD_LIBRARY_PATH=/root/webkitASAN/lib /root/webkitASAN/bin/MiniBrowser ~/poc.html

In order to understand the root cause of this, let’s take a deeper look at the poc.html Source code of poc.html

<script>
    cdmwZ22 = new GainNode(new AudioContext(), {});
    uGexE85 = cdmwZ22.connect(cdmwZ22, 0, 0);
    uGexE85.disconnect(uGexE85, 0, 1);
</script>

Source Code:

webkitgtk-2.30.3\Source\WebCore\animation\KeyframeEffect.cpp:1102

// Source/WebCore/Modules/webaudio/AudioNode.cpp:160
AudioNodeInput* AudioNode::input(unsigned i)
{
    if (i < m_inputs.size())
        return m_inputs[i].get();
    return nullptr; // if i >= size return null
}

// Source/WebCore/Modules/webaudio/AudioNode.cpp:307
ExceptionOr<void> AudioNode::disconnect(AudioNode& destinationNode, unsigned outputIndex, unsigned inputIndex)
{
    ASSERT(isMainThread());
    BaseAudioContext::AutoLocker locker(context());
    if (outputIndex >= numberOfOutputs())
        return Exception { IndexSizeError, "output index is out of bounds"_s };
    if (outputIndex >= destinationNode.numberOfInputs())
        return Exception { IndexSizeError, "input index is out of bounds"_s };
    auto* output = this->output(outputIndex);
    auto* input = destinationNode.input(inputIndex); // (1) call to AudioNode::input -> return null
    if (!output->isConnectedTo(*input))
        return Exception { InvalidAccessError, "The given destination is not connected"_s };
    input->disconnect(output);
    updatePullStatus();
    return { };
}

The crash happend because |destinationNode.input|(1) returned a null pointer, but there is no nullpointer check on this |input| before it calls into disconnect.

Backtrace

gdb-peda$ backtrace 
#0  0x00007f144a52c974 in WebCore::AudioNodeInput::disconnect(WebCore::AudioNodeOutput*) (this=this@entry=0x0, output=output@entry=0x7f1433e55870) at ../Source/WebCore/Modules/webaudio/AudioNodeInput.cpp:71
#1  0x00007f144a52d0c9 in WebCore::AudioNode::disconnect(WebCore::AudioNode&, unsigned int, unsigned int) (this=this@entry=0x7f144c6fa750, destinationNode=..., outputIndex=outputIndex@entry=0x0, inputIndex=0x1)
    at ../Source/WebCore/Modules/webaudio/AudioNode.cpp:323
#2  0x00007f144b6a4a52 in WebCore::jsAudioNodePrototypeFunction_disconnect5Body (castedThis=<optimized out>, callFrame=<optimized out>, lexicalGlobalObject=0x7f1433e3a068) at DerivedSources/WebCore/JSAudioNode.cpp:489
#3  0x00007f144b6a4a52 in WebCore::jsAudioNodePrototypeFunction_disconnectOverloadDispatcher (castedThis=<optimized out>, callFrame=<optimized out>, lexicalGlobalObject=0x7f1433e3a068)
    at DerivedSources/WebCore/JSAudioNode.cpp:554
#4  0x00007f144b6a4a52 in WebCore::IDLOperation<WebCore::JSAudioNode>::call<WebCore::jsAudioNodePrototypeFunction_disconnectOverloadDispatcher> (operationName=0x7f144b8ea010 "disconnect", callFrame=..., lexicalGlobalObject=...)
    at ../Source/WebCore/bindings/js/JSDOMOperation.h:53
#5  0x00007f144b6a4a52 in WebCore::jsAudioNodePrototypeFunction_disconnect(JSC::JSGlobalObject*, JSC::CallFrame*) (lexicalGlobalObject=0x7f1433e3a068, callFrame=<optimized out>) at DerivedSources/WebCore/JSAudioNode.cpp:561
#6  0x00007effbffff1d8 in  ()
#7  0x00007fff002e6cc0 in  ()
#8  0x00007f144668ddb8 in llint_op_call () at /home/test/webkitgtk-2.32.0/Source/JavaScriptCore/llint/LowLevelInterpreter.asm:1093
#9  0x0000000000000000 in  ()

Timeline

  • 2021-06-09 Reported to Vendor
  • 2021-12-14 Vendor fixed and assign CVE