/*
* Copyright (c) 2017 Richard Palethorpe <rpalethorpe@suse.com>
* Based on repro-compatReleaseEntry.c by NCC group
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Test for CVE-2016-4997
*
* For a full explanation of how the vulnerability works see:
* https://github.com/nccgroup/TriforceLinuxSyscallFuzzer/tree/master/crash_reports/report_compatIpt
*
* The original vulnerability was present in the 32-bit compatibility system
* call, so the test should be compiled with -m32 and run on a 64-bit kernel.
* For simplicities sake the test requests root privliges instead of creating
* a user namespace.
*/
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <limits.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include "tst_test.h"
#include "tst_safe_net.h"
#include "tst_kernel.h"
#define TOO_SMALL_OFFSET 74
#define OFFSET_OVERWRITE 0xFFFF
#define NEXT_OFFSET (sizeof(struct ipt_entry) \
+ sizeof(struct xt_entry_match) \
+ sizeof(struct xt_entry_target))
#define PADDING (OFFSET_OVERWRITE - NEXT_OFFSET)
#ifndef HAVE_STRUCT_XT_ENTRY_MATCH
struct xt_entry_match {
union {
struct {
uint16_t match_size;
char name[29];
uint8_t revision;
} user;
struct {
uint16_t match_size;
void *match;
} kernel;
uint16_t match_size;
} u;
unsigned char data[0];
};
#endif
#ifndef HAVE_STRUCT_XT_ENTRY_TARGET
struct xt_entry_target {
union {
struct {
uint16_t target_size;
char name[29];
uint8_t revision;
} user;
struct {
uint16_t target_size;
void *target;
} kernel;
uint16_t target_size;
} u;
unsigned char data[0];
};
#endif
struct payload {
struct ipt_replace repl;
struct ipt_entry ent;
struct xt_entry_match match;
struct xt_entry_target targ;
char padding[PADDING];
struct xt_entry_target targ2;
};
static void setup(void)
{
if (tst_kernel_bits() == 32 || sizeof(long) > 4)
tst_res(TCONF,
"The vulnerability was only present in 32-bit compat mode");
}
static void run(void)
{
int ret, sock_fd;
struct payload p = { 0 };
sock_fd = SAFE_SOCKET(AF_INET, SOCK_DGRAM, 0);
strncpy(p.match.u.user.name, "icmp", sizeof(p.match.u.user.name));
p.match.u.match_size = OFFSET_OVERWRITE;
p.ent.next_offset = NEXT_OFFSET;
p.ent.target_offset = TOO_SMALL_OFFSET;
p.repl.num_entries = 2;
p.repl.num_counters = 1;
p.repl.size = sizeof(struct payload);
p.repl.valid_hooks = 0;
ret = setsockopt(sock_fd, SOL_IP, IPT_SO_SET_REPLACE,
&p, sizeof(struct payload));
tst_res(TPASS | TERRNO, "We didn't cause a crash, setsockopt returned %d", ret);
}
static struct tst_test test = {
.min_kver = "2.6.32",
.setup = setup,
.test_all = run,
.needs_root = 1,
};