
TeamViewer 사용자-커널 권한 상승 PoC. CVE-2024-7479 및 CVE-2024-7481. ZDI-24-1289 및 ZDI-24-1290. TV-2024-1006.
이 저장소에는 권한 없는 사용자가 시스템에 임의의 커널 드라이버를 로드할 수 있게 하는 TeamViewer 취약점의 익스플로잇 개념 증명이 포함되어 있습니다. 이 취약점을 보고하고 책임 있게 공개하기 위해 조정해 주신 Zero Day Initiative에 감사드립니다.

이러한 취약점으로 이어지게 된 연구에 대한 자세한 내용은 제 블로그의 다음 3부작 시리즈에서 확인할 수 있습니다. 이 글에서는 이러한 취약점을 더 자세히 다루고, 그 과정에서 제가 실패했던 부분도 보여줍니다. 3부가 가장 재미있습니다 :P.
익스플로잇 동영상은 여기에서 볼 수 있습니다:
SYSTEM 서비스 IPC에 연결할 때 유효한 TeamViewer 클라이언트를 스푸핑(블로그에 설명된 간단한 인증만 수행)할 수 있게 되면 임의의 드라이버 설치를 트리거할 수 있었습니다. TeamViewer는 설치 중인 드라이버의 서명을 검증하지 않았습니다.
따라서 TeamViewer 덕분에 USER에서 KERNEL로의 권한 상승이 가능했습니다.
가장 좋은 방법 중 하나는 잘 알려진 기법인 *BYOD(Bring Your Own Vulnerable Driver)*를 사용하여 서명된 유효한 드라이버를 Windows 커널에 로드한 다음, 이를 악용하여 임의 프로세스의 토큰을 권한 있는 토큰으로 변경하는 등 사용자 수준에서 권한 있는 작업을 수행하는 것입니다.
TeamViewer가 시스템에 설치되면 SYSTEM 권한으로 실행되는 서비스인 TeamViewer_service.exe가 생성됩니다.
이 서비스는 클라이언트가 일부 작업을 수행하도록 도와주는 헬퍼입니다. 따라서 클라이언트는 상승된 권한으로 실행되지 않으며 일부 작업은 서비스에 위임됩니다.
서비스(IPC)와의 통신은 소켓(Overlapped I/O 및 IoCompletionPort 사용)을 통해 구현됩니다. 기본적으로 TeamViewer SYSTEM 서비스는 localhost의 5939/tcp에서 수신 대기합니다.
TeamViewer는 클라이언트가 드라이버 설치나 서명 검사 등을 요청할 때 전송하는 매개변수를 필터링하지 않습니다.
그래서 아이디어는 이렇습니다. TV 클라이언트를 스푸핑하여 VPN 드라이버 설치를 요청하되 다른 INF를 지정하는 것입니다. 저는 TeamViewer의 원래 INF를 재사용했지만, 권한이 없는 다른 경로에 두고 "악성" 드라이버를 _teamviewervpn.sys_로 이름을 바꿨습니다. 이는 원래 INF가 대상으로 하는 드라이버 이름이기 때문입니다.
이는 TeamViewer의 Changes require administrative rights on this computer 옵션도 우회합니다.
이 확인은 GUI를 통해서만 유효합니다. 권한 없는 사용자가 버튼을 클릭하면 TeamViewer 옵션이 비활성화되기 때문입니다. 그러나 소켓에 연결하여 임의 드라이버 로드를 수행하는 것은 가능합니다.
이 익스플로잇은 버전에 따라 다릅니다. 클라이언트가 자신의 PID와 버전 외에 다른 데이터를 지정하는 IPC 메시지 때문입니다. 클라이언트 버전은 SYSTEM 서비스의 버전과 일치해야 합니다. 익스플로잇은 공격 대상이 되는 TeamViewer_service.exe 버전에 맞게 Main.cpp의 (140~143행)을 수정해야 합니다.
그래서 기본적으로 우리는 TeamViewer 클라이언트를 스푸핑하여 SYSTEM 서비스에 연결하고 임의 드라이버 설치를 요청합니다. TeamViewer 서비스는 이를 커널에 친절하게 로드해 줍니다.
TeamViewer에는 처음 발견한 메시지(VPN 드라이버 설치를 클릭할 때 발생)와 매우 유사한 또 다른 IPC 메시지가 있습니다. 이 다른 메시지는 프린터 드라이버를 설치하기 위한 것입니다.
따라서 본질적으로 CVE-2024-7479와 CVE-2024-7481은 동일하지만 TeamViewer가 동일한 실수를 두 번 저질렀습니다. 메시지는 다르지만 매우 유사합니다. 둘은 서로 다른 IPC Method Id를 가집니다.
결과는 동일하며, 임의 드라이버를 로드할 수 있습니다.