Pwnablekr_Toddlers Bottle_uaf

ssh로 접속하여 실행하는 문제이다. sftp로 소스 코드와 프로그램을 다운받아 살펴보았다.

cpp 소스 코드가 주어지며 클래스가 정의되어 있다. main에서는 선택한 메뉴에 따라 클래스의 virtual 함수를 호출하거나, 힙을 할당한 뒤 쓰기하거나, 인스턴스를 delete 한다.

클래스 virtual 함수를 이용했다는 점에 주목하자. CPP에서 virtual 함수를 이용하기 위해서는 vtable을 이용한다. 이는 힙에 생성된 클래스에 잡힌다.

위 코드를 빌드한 어셈블리를 살펴보았다. vtable에 접근하여 함수 주소를 얻고 호출한다. vtable은 인스턴스 맨 위에 있는 포인터를 통해 접근한다.

위 코드에서는 vtable에 접근한 뒤, vtable 시작 주소+8에 있는 내용을 읽어 호출하고 있다. 이는 코드가 vtable의 두 번째에 있는 introduction 함수를 호출하기 때문이다.

위 이미지는 rax에 vtable의 주소가 있는 상태에서 출력해 본 결과이다. vtable+8 즉, vtable[1]이 가리키는 곳은 introduction이고, vtable[0]이 가리키는 곳은 shell임을 확인할 수 있다.

vtable의 내용을 조작하거나 인스턴스 맨 위의 vtable 포인터를 조작하여 조작된 vtable을 가리키면 원하는 함수를 호출시킬 수 있다. 이미 있는 vtable을 조작해보도록 하자.

함수를 호출할 때 vtable[1]을 호출하므로, 오브젝트에 저장되는 vtable 주소-8을 끼워넣으면 shell 함수를 호출할 수 있다.

case 3을 통해 delete를 호출하면 mw 인스턴스가 삭제된다. 이 때 mw 인스턴스를 생성하기 위해 할당되었던 힙 메모리가 합쳐지게 된다.

delete 이후 다시 적당한 크기의 힙을 할당하면 방금 전 delete한 힙 위치를 재활용한다. 적당한 크기란, 재활용될 힙의 크기보다 작은 크기를 의미한다. 더 나중에 freew가 사용했던 힙부터 재할당되고, 다시 할당 시 m 이 있던 위치가 재할당된다. mw 모두 조작하기 위해서 두 번 할당하였다. (2번 메뉴를 두번 사용)

파일을 이용하여 페이로드를 입력해야 하므로 파일에 저장해 둔다. vtable 주소에서 8을 뺀 주소를 입력한다.

free 후에 after를 두 번 호출하여 mw의 vtable 포인터를 조작하고, 다시 use를 하면 조작된 vtable을 참조하여 함수를 호출하게 된다.

그리하여 shell 함수가 호출된다.

플래그는 yay_f1ag_aft3r_pwning 이다.


tags: writeup, pwnable, elf file, linux, cpp lang, heap, use after free, memory corruption, vtable, cpp virtual