[1-day] VirtualBox 3D Acceleration [CVE-2019-2525] 1편

2021. 3. 13. 16:370x0F 1-Day

728x90

이 글은 독학으로 공부한 과정을 잊지 않기 위해 기술하고 있으므로 꽤 깁니다. (제 기억력이 안좋기때문에..) 익스플로잇 코드만 보고 싶은 분은 마지막 블로그만 보시면 됩니다.

 

앞의 블로그에서 Virtual Box 3D Acceleration 관련 공격을 수행하기 전에 알아야 하는 것들에 대해 스스로 판단한 것들을 서술해두었다.

 

본격적으로 취약점을 이해하기 위해 Debugging을 진행해보고자 한다. 

하단의 사진에서 PID는 9958임을 확인할 수 있다.

VirtualBox는 strip 되어있지 않기 때문에 disassembly가 가능하다.

하지만, 취약점이 존재하는 코드는 접근할 수가 없었다.

도대체 왜 접근할 수 없는가? 지금부터 살펴보도록 하자.

 

메인함수의 소스코드 경로는 다음과 같다.

src/VBox/FrontEnd/VirtualBox/src

 

Frontend라는 것은 클라이언트 그 자체라는 것이고, cuUnpack..함수는 src/VBox/HostService에 존재하는중이다.

HostService 관련 모듈을 호출하는 부분을 찾아야 할 것이다. 

 

만약, IDA를 통해 찾고자하는 crUnpackExtendGetAttribLocation 함수를 찾을 수 없다면, 공부한 배경지식인 hgcm_connect를 할 수 있는 무엇인가가 있어야 한다는 것을 뜻한다. 

 

여기서 상당한 시간을 소모하였다. 

IDA를 통해 살펴본 함수 목록은 대략적으로 이랬다.

UIDetailSet
UIDetailGroup
UIAccessibility 

QMap
QList

...

그러므로, 이 FrontEnd코드는 UI에 관련 된 내용만 수록되어있다고 판단했다. 

 

자 우선 VirtualBox 통파일의 리버싱 결과는 참담하였다. 

이제 이러한 생각을 할 수가 있다.

VirtualBox 클라이언트는 그냥 껍데기일 뿐이고, 배경지식 공부할 때 봤던 extern 변수들에 접근하기 위해 libc를 로드하는 부분이 있을 것이다. 

 

바로 이 UI가 VirtualBox 바이너리이다.

 

아는 동생에게 전화를 해서 물어보았다. VirtualBox대신에 VirtualBoxVM을 분석해야한다고 말해주었다. 

바쁜 주말에 전화를 받아주어서 진심으로 고마웠다....

 

마침내.. 해당 부분에 break point를 걸 수 있게 되었다. ^_^

 

FYI  

VirtualBox 어플리케이션은 꽤나 많은 바이너리가 종속되어있다. 저 부분들에 대해 전부 다 이해하게 될 때, VBox 고수가 되어있을 거 같다는 상상을 하면서 다시 분석에 임해보도록 하자...

 

자, 이제 break point를 걸수는 있다. 근데? 어떻게 VirtualBoxVM에 접근할 수 있는지를 알아야 한다. 

또 많은 시간을 써서 분석에 임했다. 

 

친절하게 return_ptr이라고 쓰여있다. return_ptr -> SET_RETURN_PTR 매크로 함수에서 중요하게 사용되는 변수였다.

 

그리고 무작정 running 해보면 다음과 같은 에러가 뜨게 된다.

running 하자마자 VirtualBox Guest Ubuntu가 꺼졌다.

심볼이 없다고 한다. 몇초 고민을 해보게 되었다.

가만.. 심볼이 없는데 브레이크 포인트는 걸렸다.

Virtualbox가 현 Vmware Ubuntu 에서 프로세스 상태로 있기 때문에 브레이크 포인트는 잘 걸린다.

이 상태에서 running을 하게 되면, debugger 입장에서 어라.. 심볼이 없네 뭐하자는거지? 

종료시켜버려야겠다. 하면서 종료를 하게 되는 거 같았다. 

 

continue로 진행해보니, 버퍼가 데이터를 받기 위해 무한 대기 하는 모습을 볼 수 있었다. 

그러므로, 현 VirtualBoxVM에서 crUnpackExtendGetAttribLocation함수에 접근하려면 트리거 되는 역할을 수행하는 무엇인가가 필요하다는 의미가 된다. 

 

crUnpackExtendGetAttribLocation 함수를 직접적으로 호출하는 부분이 VBox.6.0.0 코드에 노출 되지 않아서, 직접 코드 오디팅을 진행하였다. 

 

unpack.py (/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.py) 파일을 보면 이런 형태의 코드가 눈에 띈다.

# Pseudo Code

if pack || extpack in apiutil.ChromiumPros():
...

case CR_EXTEND_OPCODE:
	...
    crUnpackExtendDbg()
    
    ...
    crUnpackExtend()

여기까지 보면, 이러한 상상이 가능하다.

apiutil의 ChromiumPros 메서드에서 특정 값이 있을 경우 OPCODE라는 식별자를 체크하고, 그 식별자 중 CR_EXTEND_OPCODE에 조건이 맞을 시, Unpack하는 과정을 거치게 된다. 

 

배경지식 파트(이전 블로그 글 참조)에서 VBoxSharedCrOpenGL이 중요한 역할을 한다는 것을 알게 되었었다. 

HGCM 프로토콜과 VBoxSharedCrOpenGL을 연계해서 생각해볼 필요가 있다.

 

발표자님의 자료에서 놓친 부분이 있었다.

'Accessible to unprivileged guest user'

 

HGCM 프로토콜은 게스트유저가 액세스 할 수 있다. 

게스트유저가 엑세스할 수 있다면, VBoxSharedCrOpenGL도 강제로 사용할 수 있다! 

 

새로 생기는 질문은 'How can I access the HGCM protocol from the GuestOS?'

궁금증 해결은 오직. 분석뿐이었다. 

 

include/VBox/hgcmsvc.h

connect에 관련된 문장을 뽑아보면 다음과 같다.

513 line : DECLR3CALLBACKMEMBER(int, pfnConnect, (void *pvService, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor,. bool fRestoring));

이 문장은 구조체 안에 포함되어 있었다. 

구조체 : VBOXXHGCMSVCFNTABLE

 

그리고, typedef DECLCALLBACK(int) VBOXHGCMSVCLOAD(VBOXHGCMSVCFNTABLE *ptable)이 서비스 초기화 시켜주는 녀석이었다.

 

서비스 초기화에 대해서는 이제 알았고, VBoxSharedCrOpenGL에 대해 알아보기 위해 VBoxSharedCrOpenGL.so 파일 분석에 들어갔다.

 

Lucky ! 이 부분에서 HGCMConnect 부분을 찾을 수 있었다.

src/VBox/GuestHost/OpenGL/util/vboxhgcm.c

 

VbglR3HGCMConnect 함수 선언은 다음과 같았다.

주석이 정말 친절하다. 

이것으로 'HGCM' 연결을 어떻게 하는지도 머릿속에 담았다. 이제 HGCMConnect를 구현하여 디버깅을 할 수 있게 해야한다.

왜냐하면, 첫 디버깅을 통해 VBoxSharedCrOpenGL 서비스 연결이 되어있지 않으면 메시지큐가 무한루프 도는 것을 확인했기 때문이다.

 

include/VBox/VBoxGuestCoreTypes.h

VBGLIOCHGCMCALL 구조체 부근의 주석을 보면, HGCM 프로토콜은 IOCTL 통신을 하는 것을 알 수 있다.

쉽게 말해서 디바이스 Input / Output에 관여하는 것이라고 보면 된다. 

CALL이 구조체를 쓰면, Connect도 구조체를 쓸 것이다. 

include/VBox/VBoxGuest.h

여기에 관련 된 내용들이 3dpwn 이라는 라이브러리에 이미 구현되어 있었다. 

디버깅을 하기 위해 코드를 작성해보자.

https://github.com/niklasb/3dpwn