Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2019-16784-POC — PyInstaller CVE-2019-16783에 대한 개념 증명 익스플로잇 | Kitploit
도구/GitHubGitHub/ckrielle/cve-2019-16784-poc
Privilege EscalationVulnerability AnalysisExploitationLearning & EducationPayload DevelopmentBinary ExploitationLabs & Practice
GitHubckrielle/cve-2019-16784-poc

CVE-2019-16784-POC

PyInstaller CVE-2019-16783에 대한 개념 증명 익스플로잇

저장소 보기
2년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2019-16784 POC

이것은 Windows PyInstaller 버전 < 3.6의 취약점에 대한 제 POC입니다. 이 취약점은 --onefile 옵션에 존재합니다. 공격자는 PyInstaller 바이너리에서 사용하는 파이썬 인터프리터 DLL이 임포트하는 DLL을 하이재킹하여 명령 실행 및 가능한 LPE를 달성할 수 있습니다. 취약점과 익스플로잇 과정에 대한 간단한 설명이 뒤따릅니다. 취약점을 발견하고 그 발견에 대해 PagedOut #3 (pdf의 55페이지)에 기고해 주신 Alter Solutions께 감사드립니다.

취약점

이 취약점은 실행된 바이너리의 런타임에 PyInstaller가 생성하는 디렉토리의 취약한 생성 때문에 발생했습니다. PyInstaller는 사용자의 임시 디렉토리에 _MEIPIDX 디렉토리를 구성하고, python 인터프리터 DLL(PE 실행 파일에 패킹된 파이썬 코드를 실행하는 데 사용됨)과 같은 다양한 항목을 그 안에 넣습니다.

이 프로세스의 문제는 NT AUTHORITY\SYSTEM에 대해 구성된 디렉토리가 C:\Windows\Temp였다는 것이었으며, 이로 인해 누군가가 이를 추측하고 그 안에 쓸 수 있었습니다. 따라서 예를 들어 python 인터프리터가 실행될 때 DLL 하이재킹이 가능했습니다. 다음은 취약점을 수정한 커밋입니다: 여기. 표준 API 함수에만 의존하는 대신, 개발자들은 디렉토리 생성을 더 잘 제어하기 위해 자체 함수를 구현했습니다.

익스플로잇 과정

이것은 제 첫 번째 POC였기 때문에, 진행하면서 마주친 몇 가지 문제를 논의하겠습니다.

테스트 환경 설정

첫째, 이 POC를 위한 환경을 설정하는 것은 특별히 어렵지 않았습니다. 올바른 패키지 버전만 있으면 되었기 때문입니다. 하지만 설치했을 때, 충돌이 발생했습니다.

root@kitploit:~
Traceback (most recent call last):
  File "c:\users\ckrielle\appdata\local\programs\python\python38\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
...
  File "c:\users\ckrielle\appdata\local\programs\python\python38\lib\site-packages\PyInstaller\building\utils.py", line 653, in <genexpr>
    strip_paths_in_code(const_co, new_filename)
  File "c:\users\ckrielle\appdata\local\programs\python\python38\lib\site-packages\PyInstaller\building\utils.py", line 660, in strip_paths_in_code
    return code_func(co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize,
TypeError: an integer is required (got type bytes)

잠시 검색한 후, 제 Python 버전이 문제라는 것을 알게 되었습니다 (저는 3.8.10을 사용하고 있습니다). 제 Python 버전 때문에 다른 3.8.x 버전을 설치하기가 어려웠습니다. 설치 시 설치 프로그램이 제 Python38 버전을 발견하고 오류를 반환했기 때문입니다. 또한 다른 3.8 버전을 빌드하려고 시도했지만, Visual Studio 2015가 필요했던 반면 저는 2022를 가지고 있어서 실패했습니다. 결국 Python 3.7.5를 다운로드하기로 결정했고, 이 버전은 완벽하게 작동했습니다. 그래서 3.7 버전용 가상 환경을 생성했습니다.

환경 설정은 이미 설정되어 있는 경우부터 올바르게 설정하는 데 많은 시간이 걸릴 수 있는 경우까지 다양하다는 것을 이해했습니다. 중요하기는 하지만, 실제로 타겟을 익스플로잇하는 재미를 빼앗을 수도 있습니다.

익스플로잇 작성

우리의 익스플로잇 과정에는 두 단계가 있습니다: PyInstaller로 패키징된 프로세스의 디렉토리를 찾고, 우리의 익스플로잇 DLL을 그곳에 쓰는 것입니다. 첫 번째 부분의 경우, 표준 WINAPI 함수(CreateToolhelp32Snapshot, Process32First, Process32Next)를 통해 PID를 찾을 수 있습니다. 이것은 원활하게 작동했습니다. 그 다음, _MEI 디렉토리의 마지막 번호를 찾아야 합니다. 원본 익스플로잇은 GetFileAttributesA 함수를 사용하고 반환된 상태 코드가 FILE_ATTRIBUTE_DIRECTORY인지 확인하는 것을 제안합니다. 그러나 이것은 저에게 작동하지 않았습니다. 제 해결책은 모든 디렉토리 후보에 파일을 생성하고, 파일이 성공적으로 생성되면 디렉토리가 존재하는 것으로 간주하는 것이었습니다. 어떤 이유로 GetFileAttributesA에서 파일에 대해 반환된 상태가 FILE_ATTRIBUTE_ARCHIVE였으므로, 이를 확인합니다. 계속하기 전에, PID를 검색하는 동안의 루프는 타겟 프로세스가 실행될 때 DLL을 주입하여 로딩이 발생하기 전에 준비되도록 하기 위한 것입니다.

DLL의 경우, python 인터프리터(python37.dll)가 임포트하는 DLL을 하이재킹해야 합니다. 인터프리터가 임포트하는 DLL을 확인하려면 PE-Bear로 DLL을 열 수 있습니다. 임포트된 시스템 DLL 중 하나는 version.dll입니다. 따라서 우리만의 DLL을 생성하고, Windows 로더의 검색 순서를 악용하여 PyInstaller로 패키징된 프로세스의 임시 디렉토리에 배치할 수 있습니다. 그렇게 하면, DLL을 임포트하려고 할 때 올바른 DLL 대신 악성 DLL을 임포트하게 됩니다.

python37.dll Imports

그러나 이렇게 하면 작동하지 않습니다. 그 이유는 python 인터프리터가 version.dll에서 호출하는 함수(특히 이미지의 VerQueryValueW)의 임포트가 해결되지 않기 때문입니다. 따라서 프로그램이 충돌합니다. 이 문제를 해결하기 위해 DLL 프록시를 수행해야 합니다. 간단히 말해, 악성 DLL이 원래 DLL의 함수를 내보내도록 구성하고, 원래 DLL을 이름을 바꿔 가져와서 로드한 후 그곳에서 함수를 호출할 수 있게 합니다. 따라서 익스플로잇의 경우, 코드 실행을 달성할 수 있는 DllMain이 있는 악성 DLL을 컴파일합니다. 모든 version.dll 함수를 내보내고, 원래 시스템 version2.dll을 복사하여 같은 디렉토리에 배치합니다. 그러면 version.dll이 함수 호출을 version2.dll로 전달할 수 있습니다. 이 DLL들을 준비한 후, 프로세스의 디렉토리에 복사하고 코드 실행을 기다리면 됩니다. 더 나은 DLL 프록시/하이재킹 설명을 위해 링크된 글을 읽어보세요.

Malicious version.dll Exported Functions

돌이켜 보면 이 단계들은 모두 쉽고 간단하지만, POC를 개발하는 동안에는 그렇지 않았습니다. 얼마 전까지는 익스플로잇 과정을 이해하는 데 시간이 걸렸고, 코딩을 시작하면서 더 이해하게 되었습니다. DLL의 경우, Alter Solutions POC 저장소에 암시된 방식으로 직접 컴파일하려고 시도했습니다. 그들은 코드 실행을 위한 별도의 DLL과 프록시를 위한 다른 DLL을 가지고 있었으며, 런타임에 payload.dll을 로드했습니다(DllMain은 로딩 시 호출됨). 그러나 제대로 컴파일할 수 없었습니다. 결국 위의 글을 읽은 후, DLLProxyProject 저장소를 사용하기로 결정했습니다. 이 저장소는 제가 원하는 DLL을 컴파일해 주었고, 일반적으로 프록시 목적의 DLL을 생성하는 데 사용할 수 있습니다. DLLMain.cpp 파일과 exports.h 헤더 파일을 복사하여 컴파일하려고 시도했습니다. 실제로 실행되었지만, 오류가 발생했습니다:

root@kitploit:~
Fatal Python error: init_sys_streams: can't initialize sys standard streams
OSError: [WinError 6] The handle is invalid

Current thread 0x00002ea8 (most recent call first):

이 오류는 Utils.cpp 파일로 피할 수 있을 수 있어서, DLL 컴파일을 위해 그 프로젝트를 그대로 사용하기로 결정했습니다 (완전히 직접 했더라면 더 좋았겠지만요).

타겟 익스플로잇

환경을 설정한 후 (psexec를 사용하여 NT AUTHORITY\SYSTEM 셸을 얻음), 익스플로잇을 실행하고 타겟 바이너리를 실행하여 관리자 권한으로 코드 실행을 얻었습니다.

POC Success

마무리

이것은 매우 좋은 경험이었고, 기꺼이 겪은 일입니다. 다른 CVE에 대한 더 많은 POC를 만들고 싶으며, 이번은 완벽한 첫 번째 타겟이었습니다. 취약점 설명을 읽는 것은 흥미로웠습니다. 직접 POC를 구현하는 주요 어려움은 저자가 설명하지 않은 부분(의도적이든 아니든)을 채우고, 읽고 있는 내용을 이해하는 것임을 깨달았기 때문입니다. 간단한 취약점이었기 때문에 이해하는 데 많은 노력을 기울이지 않았습니다. 가까운 시일 내에 몇 가지 더 수행한 후, 메모리 손상 취약점에 대한 POC를 시도해 볼 것입니다.

도구 다운로드