
ChakraCore 악용 기법
CVE-2016-7190 [0]은 ChakraCore의 Array.map() 함수에서 발생하는 힙 오버플로우로, 인접한 메모리를 덮어쓸 수 있습니다. 임의 읽기-쓰기 액세스를 얻는 주요 아이디어는 먼저 여러 개의 연속된 JavaScript 정수 배열을 할당한 다음, 오버플로우를 악용하여 하나의 배열 크기를 조작하는 것입니다. 그런 다음 이 배열을 활용해 Uint8Array의 기본 주소를 읽거나 쓰고자 하는 임의의 주소로 변경합니다. 이 추가 단계가 필요한 이유는 오버플로우가 오버플로우된 정수 배열 크기의 2배로 제한되기 때문입니다.
이 취약점은 현재 버전의 ChakraCore에 쉽게 다시 도입할 수 있으므로 다양한 익스플로잇 전략을 테스트하는 데 매우 적합합니다 (undo-cve-2016-7190.patch 참조).
이 예제에서는 Uint8Array C++ 객체의 vtable 포인터를 공격자가 제어하는 메모리 주소로 덮어쓰고, Uint8Array 객체의 함수를 호출해 가상 함수가 호출되도록 함으로써 제어 흐름을 하이재킹합니다.
CFI(Control-flow Integrity)는 제어 흐름 하이재킹 공격을 완화하는 방어 기법입니다. CFI의 일반적인 개념은 컴파일 타임에 애플리케이션의 제어 흐름 그래프(CFG)를 계산한 다음, 런타임 검사를 애플리케이션에 삽입하여 제어 흐름이 정적으로 계산된 CFG를 런타임 중에 벗어나지 않도록 보장하는 것입니다. 그러나 웹 브라우저와 같은 애플리케이션이 동적 코드 생성을 지원하는 경우 CFG는 런타임 중에 확장 가능해야 합니다. 이로 인해 여러 가지 문제가 제기됩니다.
연구를 수행하는 동안 우리는 마지막 문제에 집중했습니다. 주요 아이디어는 JIT(Just-In-Time) 컴파일러의 입력(데이터)을 조작하는 것입니다. 그 결과 JIT 컴파일러는 이후 현재 컨텍스트에 통합되고 CFI로 강화되기까지 하는 악성 코드를 생성하게 됩니다. 우리의 연구와 동시에 theori [4]는 JIT 컴파일러의 출력을 조작하여 CFI를 우회하는 방법을 보여주었습니다. 이 공격은 체크섬을 통해 출력의 무결성을 검증함으로써 완화됩니다. 우리는 JIT 컴파일러의 입력을 조작하므로 이로는 우리의 공격을 막을 수 없습니다. 그러나 JIT 컴파일을 다른 프로세스로 아웃소싱함으로써(일명 Arbitrary Code Guard [3]) 두 공격 모두 완화됩니다.
우리는 공격자가 JIT 컴파일러에 대한 데이터 전용 공격을 악용하여 임의의 네이티브 코드를 생성할 수 있음을 시연합니다. 특히 JIT 컴파일러의 중간 표현(IR)을 수정하여 공격자가 제어하는 명령어를 주입합니다. 이후 JIT 컴파일러가 수정된 IR을 기반으로 네이티브 코드를 생성하면 공격자가 제어하는 네이티브 코드가 생성됩니다.
연구가 원래 수행된 시점과 지금 사이에 ChakraCore의 IR이 변경되었습니다. 우리는 이 공격을 포팅하는 작업을 수행하지 않았습니다. 따라서 이 공격을 가지고 실험하려면 다음 바이너리를 사용하십시오.
ACG [3]를 분석하는 동안 우리는 무엇보다도 [1,2]에서 .mrdata 섹션에 저장된 전역 읽기 전용 데이터가 동적으로 생성된 코드를 통합하기 위해 조정되어야 한다는 것을 발견했습니다. 이는 스레드 안전성을 위해 잠금을 사용하는 LdrProtectMrdata() 함수를 통해 수행됩니다. 그러나 .mrdata 섹션은 잠금이 획득되기 전에 먼저 쓰기 가능으로 재매핑됩니다. 흥미롭게도 .mrdata 섹션에는 나중에 이 섹션을 다시 읽기 전용으로 재매핑하는 데 사용되는 .mrdata 섹션의 기본 주소와 크기가 포함되어 있습니다.
공격자는 이를 악용하여 읽기 전용 메모리를 쓰기 가능으로 재매핑할 수 있는 공격 프리미티브를 만들 수 있습니다. 따라서 공격자는 다음 단계를 수행합니다.
이 공격의 결과로 이전에 신뢰되던 데이터, 즉 읽기 전용으로 매핑된 데이터는 신뢰할 수 없게 됩니다. 저희의 개념 증명에서는 _guard_dispatch_icall_fptr에 저장된 CFGuard 검증 함수의 포인터를 변경하여 CFGuard(Control-flow Guard)를 우회하기 위해 이 프리미티브를 악용합니다. 디버거를 연결하고 bypass_cfguard 변수를 변경하면 그 효과를 관찰할 수 있습니다.
[0] https://bugs.chromium.org/p/project-zero/issues/detail?id=923
[1] http://alex-ionescu.com/publications/euskalhack/euskalhack2017-cfg.pdf
[2] https://sites.google.com/site/bingsunsec/dataonlyattack
[3] https://blogs.windows.com/msedgedev/2017/02/23/mitigating-arbitrary-native-code-execution/
[4] http://theori.io/research/chakra-jit-cfg-bypass