WebP Cloud Services Blog

手动编译 libwebp——我们针对 CVE 2023-4863 的缓解方案

· Nova Kwok

CVE-2023-4863 是一个缓冲区溢出漏洞,影响了 libwebp 库,该库是一个用于处理 WebP 图片的开源库,由 Google 开发,好巧不巧,我们的开源组件 WebP Server Go 和 SaaS 服务 WebP Cloud Services 都使用了这个库。

相关的修复 PR: https://github.com/webmproject/libwebp/commit/902bc9190331343b2017211debcec8d2ab87e17a, PR 中尝试解决的问题:

  1. Huffman编码表构建问题:在 VP8L 图像编码/解码库中,存在一个名为BuildHuffmanTable的函数,用于构建 Huffman 编码表。之前的代码中存在一个潜在的问题,即在构建 Huffman 编码表时,有时可能会导致访问超出分配内存范围,这可能导致内存越界写入问题。修复这个问题的方式是对内存分配和访问进行了更严格的控制,以确保不会超出分配的内存范围。

  2. Huffman编码表的内存分配和释放:该补丁还引入了一种新的方式来管理 Huffman 编码表的内存分配和释放。通过引入HuffmanTables结构以及HuffmanTablesSegment结构,它提供了更灵活的内存管理,允许在需要时动态分配更多的内存段,以避免内存不足问题,并确保分配的内存块是连续的。

以上内容由 ChatGPT 生成。

鉴于我们暂时没有在公开的地方找到 PoC ,且我们还没研究透怎么构造一个 PoC,我们的工作重心优先放在了修复上,这个漏洞主要的影响在于 Decode 的部分,所以对于浏览器端而言可能可以通过一个恶意的 WebP 图片造成溢出,对于 WebP Server Go 和 WebP Cloud Services 而言,攻击面应该在于源站上构造一个恶意的 WebP 图片,然后在我们服务尝试 Decode 的时候触发溢出。

修复/缓解方式有两种:

  • 禁用 WebP 作为源站图片,但是我们看了一下在 WebP Cloud Services 上已经有不少用户在使用 WebP 作为源站图片了,所以这个方案不可行
  • 更新 libwebp

目前各个发行版的打包速度还比较慢,在本文编写的时候可以看到以下发行版的修复情况(已经修复的版本是 1.3.2,所以低于这个版本的 libwebp 都可能不包含这个 Fix,但是也有例外情况,就是发行版在老版本上打了 Patch):

Ubuntu 22.04

root@6134fe90ec67:/# apt-cache policy libwebp-dev
libwebp-dev:
  Installed: (none)
  Candidate: 1.2.2-2ubuntu0.22.04.2
  Version table:
     1.2.2-2ubuntu0.22.04.2 500
        500 http://ports.ubuntu.com/ubuntu-ports jammy-updates/main arm64 Packages
        500 http://ports.ubuntu.com/ubuntu-ports jammy-security/main arm64 Packages
     1.2.2-2 500
        500 http://ports.ubuntu.com/ubuntu-ports jammy/main arm64 Packages

根据 https://changelogs.ubuntu.com/changelogs/pool/main/libw/libwebp/libwebp_1.2.2-2ubuntu0.22.04.2/changelog ,这个 Patch 版本包含了 Fix

libwebp (1.2.2-2ubuntu0.22.04.2) jammy-security; urgency=medium

  • SECURITY UPDATE: Heap buffer overflow in BuildHuffmanTable
    • debian/patches/CVE-2023-4863.patch: fix OOB write in BuildHuffmanTable in src/dec/vp8l_dec.c, src/dec/vp8li_dec.h, src/utils/huffman_utils.c, src/utils/huffman_utils.h.
    • CVE-2023-4863

– Marc Deslauriers marc.deslauriers@ubuntu.com Wed, 13 Sep 2023 13:57:14 -0400

Debian 12

root@581497e00c7b:/# apt-cache policy libwebp-dev
libwebp-dev:
  Installed: (none)
  Candidate: 1.2.4-0.2+deb12u1
  Version table:
     1.2.4-0.2+deb12u1 500
        500 http://deb.debian.org/debian-security bookworm-security/main arm64 Packages
     1.2.4-0.2 500
        500 http://deb.debian.org/debian bookworm/main arm64 Packages

根据 https://metadata.ftp-master.debian.org/changelogs//main/libw/libwebp/libwebp_1.2.4-0.2+deb12u1_changelog ,这个 Patch 版本包含了 Fix

libwebp (1.2.2-2ubuntu0.22.04.2) jammy-security; urgency=medium

  • SECURITY UPDATE: Heap buffer overflow in BuildHuffmanTable
    • debian/patches/CVE-2023-4863.patch: fix OOB write in BuildHuffmanTable in src/dec/vp8l_dec.c, src/dec/vp8li_dec.h, src/utils/huffman_utils.c, src/utils/huffman_utils.h.
    • CVE-2023-4863

– Marc Deslauriers marc.deslauriers@ubuntu.com Wed, 13 Sep 2023 13:57:14 -0400

libwebp (1.2.4-0.2+deb12u1) bookworm-security; urgency=medium

  • CVE-2023-4863

– Moritz Mühlenhoff jmm@debian.org Tue, 12 Sep 2023 21:35:44 +0200

Alpine 3.18

/ # cat /etc/issue
Welcome to Alpine Linux 3.18
Kernel \r on an \m (\l)

/ # apk info libwebp-dev
libwebp-dev-1.3.2-r0 description:
Libraries for working with WebP images (development files)

libwebp-dev-1.3.2-r0 webpage:
https://developers.google.com/speed/webp

libwebp-dev-1.3.2-r0 installed size:
160 KiB

Fedora 38

[root@9dd00c8c57b8 ~]# yum info libwebp-devel
Last metadata expiration check: 0:00:37 ago on Sat Sep 16 12:25:45 2023.
Available Packages
Name         : libwebp-devel
Version      : 1.3.1
Release      : 3.fc38
Architecture : aarch64
Size         : 38 k
Source       : libwebp-1.3.1-3.fc38.src.rpm

Arch Linux

[root@bbacbc426a03 ~]# pacman -Ss libwebp
extra/libwebp 1.3.2-1
    WebP library and conversion tools

CentOS Stream 9

[root@69de863356a0 /]# yum info libwebp-devel.x86_64
Last metadata expiration check: 0:01:42 ago on Sat Sep 16 12:35:25 2023.
Available Packages
Name         : libwebp-devel
Version      : 1.2.0
Release      : 7.el9
Architecture : x86_64

OpenSUSE Tumbleweed

9f749cc56bcb:/ # zypper info libwebp-devel
Loading repository data...
Reading installed packages...

Information for package libwebp-devel:
--------------------------------------
Repository     : openSUSE-Tumbleweed-Oss
Name           : libwebp-devel
Version        : 1.3.1-2.1
Arch           : x86_64
Vendor         : openSUSE

我们的修复

鉴于几个发行版基本没有修复,而我们又受到此漏洞的影响,且由于我们是 Multi-stage 的容器化构建,所以前置的 RUN apt update && apt install --no-install-recommends libvips-dev -y && mkdir /build 及以上的部分可能一直会被缓存,导致我们一直没法得到带 Patch 的版本,正好我们也打算升级一下 libwebp 的版本,因此我们需要手动编译新版本的 libwebp

由于我们的服务是跑在容器中的,并且在 Dockerfile 中我们使用了 multi-stage build,那么我们要做的事情只是添加一个 stage,编译成我们所需的 so 文件,然后把最后的这个 so 文件复制到运行的 stage 里

检查so文件

在安装 libwebp 的时候,会自动带上这些依赖:libwebp-dev libwebp7 libwebpdemux2 libwebpmux3

使用 dpkg -L 可以看到 so 文件,最终我们要全部替换掉

root@ee54b6e5dbfa:/# dpkg -L libwebp-dev libwebp7 libwebpdemux2 libwebpmux3 | grep "so"
/usr/lib/aarch64-linux-gnu/libwebp.so
/usr/lib/aarch64-linux-gnu/libwebpdemux.so
/usr/lib/aarch64-linux-gnu/libwebpmux.so
/usr/lib/aarch64-linux-gnu/libwebp.so.7.1.5
/usr/lib/aarch64-linux-gnu/libwebp.so.7
/usr/lib/aarch64-linux-gnu/libwebpdemux.so.2.0.11
/usr/lib/aarch64-linux-gnu/libwebpdemux.so.2
/usr/lib/aarch64-linux-gnu/libwebpmux.so.3.0.10
/usr/lib/aarch64-linux-gnu/libwebpmux.so.3

安装编译前依赖

注意 libgif-dev,少了这个就没法生成 GIF 动图了

apt install -y wget gcc make autoconf automake libtool libgif-dev \
    libjpeg-dev libjpeg62-turbo libjpeg62-turbo-dev libpng-dev libpng-tools libpng16-16 libtiff-dev libtiff6 libtiffxx6

下载源代码并配置编译

mkdir libwebp && mkdir -p /build/usr && mkdir /build/usr/lib/ && cd libwebp &&  \
        wget https://chromium.googlesource.com/webm/libwebp/+archive/refs/heads/1.3.2.tar.gz && \
        tar xf 1.3.2.tar.gz && rm -f 1.3.2.tar.gz && \
        ./autogen.sh && \
        ./configure --prefix=/build/usr --libdir=/build/usr/lib --enable-everything && \
       

./configure 时,由于我们并不真正需要安装到这个系统中,所以我们创建一个临时的 /build 目录,通过 --prefix--libdir 来输出 so 文件到这个目录之中。

配置完成之后会输出如下信息,共享库、encoder、decoder、mux 和 demux 都有,并且 JPEG PNG TIFF和 GIF 的支持都在。这样编译出来的 so 就是我们要的运行时链接库文件。

WebP Configuration Summary
--------------------------

Shared libraries: yes
Static libraries: yes
Threading support: yes
libwebp: yes
libwebpdecoder: yes
libwebpdemux: yes
libwebpmux: yes
libwebpextras: yes

Tools:
cwebp : yes
  Input format support
  ====================
  JPEG : yes
  PNG  : yes
  TIFF : yes
  WIC  : no
dwebp : yes
  Output format support
  =====================
  PNG  : yes
  WIC  : no
GIF support : yes
anim_diff   : yes
gif2webp    : yes
img2webp    : yes
webpmux     : yes
vwebp       : no
webpinfo    : yes
SDL support : no
vwebp_sdl   : no

编译

make && make install

检查一下编译结果

root@ee54b6e5dbfa:~/libwebp# tree /build
/build
`-- usr
    |-- bin
    |   |-- cwebp
    |   |-- dwebp
    |   |-- gif2webp
    |   |-- img2webp
    |   |-- webpinfo
    |   `-- webpmux
    |-- include
    |   `-- webp
    |       |-- decode.h
    |       |-- demux.h
    |       |-- encode.h
    |       |-- mux.h
    |       |-- mux_types.h
    |       |-- sharpyuv
    |       |   |-- sharpyuv.h
    |       |   `-- sharpyuv_csp.h
    |       `-- types.h
    |-- lib
    |   |-- libsharpyuv.a
    |   |-- libsharpyuv.la
    |   |-- libsharpyuv.so -> libsharpyuv.so.0.0.1
    |   |-- libsharpyuv.so.0 -> libsharpyuv.so.0.0.1
    |   |-- libsharpyuv.so.0.0.1
    |   |-- libwebp.a
    |   |-- libwebp.la
    |   |-- libwebp.so -> libwebp.so.7.1.8
    |   |-- libwebp.so.7 -> libwebp.so.7.1.8
    |   |-- libwebp.so.7.1.8
    |   |-- libwebpdecoder.a
    |   |-- libwebpdecoder.la
    |   |-- libwebpdecoder.so -> libwebpdecoder.so.3.1.8
    |   |-- libwebpdecoder.so.3 -> libwebpdecoder.so.3.1.8
    |   |-- libwebpdecoder.so.3.1.8
    |   |-- libwebpdemux.a
    |   |-- libwebpdemux.la
    |   |-- libwebpdemux.so -> libwebpdemux.so.2.0.14
    |   |-- libwebpdemux.so.2 -> libwebpdemux.so.2.0.14
    |   |-- libwebpdemux.so.2.0.14
    |   |-- libwebpmux.a
    |   |-- libwebpmux.la
    |   |-- libwebpmux.so -> libwebpmux.so.3.0.13
    |   |-- libwebpmux.so.3 -> libwebpmux.so.3.0.13
    |   |-- libwebpmux.so.3.0.13
    |   `-- pkgconfig
    |       |-- libsharpyuv.pc
    |       |-- libwebp.pc
    |       |-- libwebpdecoder.pc
    |       |-- libwebpdemux.pc
    |       `-- libwebpmux.pc

复制到最终 stage 中

我们需要先强制删除安装 libvips-dev 时自带的 libwebp 相关包。

dpkg --remove --force-depends libwebp-dev libwebp7 libwebpdemux2 libwebpmux3

然后想办法把 so 文件复制过来。那么问题来了,怎么复制?我们的 docker image 要支持多架构,对于 amd64 来说共享库的目录是 /usr/lib/x86_64-linux-gnu/ 对于 arm64 则是 /usr/lib/aarch64-linux-gnu/

也许你会想,那么还不简单,uname -m 对应架构,没错,可惜 Dockerfile 并不支持这么用,例如如果你想尝试用如下指令的话:

COPY --from=libwebp /build/usr/lib/* /usr/lib/$(uname -m)-linux-gnu/

是不可用的。

那我们投机取巧一下,先复制到临时目录,然后 RUN 就可以啦。要注意 /build/usr/lib/* 结尾的 * 以及最后的 ldconfig 指令。

COPY --from=libwebp /build/usr/lib/* /usr/lib/temp-linux-gnu/
RUN mv /usr/lib/temp-linux-gnu/* /usr/lib/$(uname -m)-linux-gnu/ && ldconfig

完整参考 Dockerfile

FROM golang:1.21-bookworm as builder

ARG IMG_PATH=/opt/pics
ARG EXHAUST_PATH=/opt/exhaust
RUN apt update && apt install --no-install-recommends libvips-dev -y && mkdir /build
COPY go.mod /build
RUN cd /build && go mod download

COPY . /build
RUN cd /build && sed -i "s|.\/pics|${IMG_PATH}|g" config.json  \
    && sed -i "s|\"\"|\"${EXHAUST_PATH}\"|g" config.json  \
    && sed -i 's/127.0.0.1/0.0.0.0/g' config.json  \
    && go build -ldflags="-s -w" -o webp-server .

FROM debian:bookworm-slim as libwebp
RUN apt update && apt install -y wget gcc make autoconf automake libtool libgif-dev \
    libjpeg-dev libjpeg62-turbo libjpeg62-turbo-dev libpng-dev libpng-tools libpng16-16 libtiff-dev libtiff6 libtiffxx6
RUN mkdir libwebp && mkdir -p /build/usr && mkdir /build/usr/lib/ && cd libwebp &&  \
        wget https://chromium.googlesource.com/webm/libwebp/+archive/refs/heads/1.3.2.tar.gz && \
        tar xf 1.3.2.tar.gz && rm -f 1.3.2.tar.gz && \
        ./autogen.sh && \
        ./configure --prefix=/build/usr --libdir=/build/usr/lib --enable-everything && \
        make && make install

FROM debian:bookworm-slim

RUN apt update && apt install --no-install-recommends libvips ca-certificates libjemalloc2 libtcmalloc-minimal4 -y && rm -rf /var/lib/apt/lists/* &&  rm -rf /var/cache/apt/archives/*

# for CVE-2023-4863
RUN dpkg --remove --force-depends libwebp-dev libwebp7 libwebpdemux2 libwebpmux3
COPY --from=libwebp /build/usr/lib/* /usr/lib/temp-linux-gnu/
RUN mv /usr/lib/temp-linux-gnu/* /usr/lib/$(uname -m)-linux-gnu/ && ldconfig

COPY --from=builder /build/webp-server  /usr/bin/webp-server
COPY --from=builder /build/config.json /etc/config.json

WORKDIR /opt
VOLUME /opt/exhaust
CMD ["/usr/bin/webp-server", "--config", "/etc/config.json"]

类似的,我们在 WebP Cloud Services 上也进行了类似的 Patch,减少漏洞对于我们系统的影响。

References

  1. https://news.ycombinator.com/item?id=37478403
  2. https://github.com/webmproject/libwebp/commit/902bc9190331343b2017211debcec8d2ab87e17a

WebP Cloud Services 团队是一个来自上海和赫尔辛堡的三人小团队,由于我们不融资,且没有盈利压力 ,所以我们会坚持做我们认为正确的事情,力求在我们的资源和能力允许范围内尽量把事情做到最好, 同时也会在不影响对外提供的服务的情况下整更多的活,并在我们产品上实践各种新奇的东西。

如果你觉得我们的这个服务有意思或者对我们服务感兴趣,欢迎登录 WebP Cloud Dashboard 来体验,如果你好奇它还有哪些神奇的功能,可以来看看我们的文档 WebP Cloud Services Docs,希望大家玩的开心~


Discuss on Hacker News