Skip to content

Unsafe use of the return value of snprintf() and out-of-bounds memory access in RIOT /sys/shell/cmds/vfs.c

Low
Teufelchen1 published GHSA-frp5-4gfp-84j3 Apr 26, 2024

Package

RIOT

Affected versions

<= 2023.10

Patched versions

None

Description

Summary

I spotted an unsafe use of the return value of snprintf() that leads to an out-of-bounds memory access in RIOT source code at:
https://github.com/RIOT-OS/RIOT/blob/master/sys/shell/cmds/vfs.c#L747-L749

Details

The snprintf() API function returns the number of characters (excluding the terminating NUL byte) which would have been written to the destination string if enough space had been available. If an attacker is able to craft input so that the final string would become larger than bs, they can exploit this bug to cause a buffer overflow.

Please refer to the marked lines below:

static void _write_block(int fd, unsigned bs, unsigned i)
{
    char block[bs];
    char *buf = block;

    buf += snprintf(buf, bs, "|%03u|", i); // VULN: snprintf() returns the total length of the string it tried to create, which may be larger than bs

    memset(buf, _get_char(i), &block[bs] - buf); // VULN: buf can point past the block buffer, in addition &block[bs] - buf would be negative and become a large unsigned value
    block[bs - 1] = '\n';

    vfs_write(fd, block, bs);
}

PoC

I put together a quick proof-of-concept to demonstrate this issue:

raptor@blumenkraft Research % cat ret1.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static char _get_char(unsigned i)
{
    i %= 62; /* a-z, A-Z, 0..9, -> 62 characters */

    if (i < 10) {
        return '0' + i;
    }
    i -= 10;

    if (i <= 'z' - 'a') {
        return 'a' + i;
    }
    i -= 1 + 'z' - 'a';

    return 'A' + i;
}

static void _write_block(unsigned bs, unsigned i)
{
    char block[bs];
    char *buf = block;

    buf += snprintf(buf, bs, "|%03u|", i); // VULN: unsafe use of return value

    memset(buf, _get_char(i), &block[bs] - buf); // VULN: oob memory write, size becomes a large unsigned value
    block[bs - 1] = '\n';
}

int main(int argc, char **argv)
{
	if (argc < 3)
		return 1;

	_write_block(atoi(argv[1]), atoi(argv[2]));

	return 0;
}
raptor@blumenkraft Research % make ret1
cc     ret1.c   -o ret1
raptor@blumenkraft Research % ./ret1 5 10
raptor@blumenkraft Research % ./ret1 5 1000000
zsh: segmentation fault  ./ret1 5 1000000

Impact

If the input above is attacker-controlled and crosses a security boundary, the impact of the buffer overflow vulnerability could range from denial of service to (less likely in this case) arbitrary code execution.

Severity

Low

CVE ID

No known CVE

Credits