[News/Hacking Exploit Issue]
CVE-2020-1380 : 최근 수정 된 IE 제로 데이 분석

2020. 8. 27. 09:50


Microsoft는 Internet Explorer 11, 특히 CVE-2020-1380 을 대상으로하는 제로 데이 취약점 1 개를 패치 했습니다 . Internet Explorer의 JavaScript 엔진 인 jscript9.dll의 UAF (use-after-free) 버그입니다. 지난 몇 년 동안 Internet Explorer에 대한 제로 데이 공격은 일반적으로 vbscript.dll 및 jscript.dll을 악용하여 쉘 코드를 실행하는 것을 관찰했습니다. 이번에는 대상이 jscript9.dll로 변경되고 최신 JavaScript 엔진의 JIT (Just-In-Time) 엔진을 사용하여 버그를 트리거했기 때문에 jscrtip9.dll JIT 엔진에 들어가 근본 원인을 파악하기로 결정했습니다. CVE-2020-1380의.

Jscirpt9.dll 실행 파이프 라인 개요

Jscript9.dll은 Internet Explorer 9부터 jscript.dll을 대체하는 데 사용되는 기본 JavaScript 엔진입니다. 그림 1은 jscript9.dll의 실행 파이프 라인을 보여줍니다.

그림 1. jscript9.dll 실행 파이프 라인

일반적으로 jscript9.dll에서 JavaScript 소스 코드를 실행하는 5 가지 주요 단계가 있습니다.

  1. 파서는 JavaScript 소스 코드를 구문 분석하여 AST (추상 구문 트리)를 가져옵니다.
  2. ByteCodeGenerator는 AST를 순회하고 ByteCode를 생성합니다.
  3. 인터프리터는 ByteCode를 실행하는 가상 머신입니다. 유형 정보와 같은 프로필 데이터는 ByteCode 실행시 수집됩니다.
  4. for 루프에서와 같이 일부 코드 조각이 여러 번 호출되면 인터프리터는 ByteCode 및 프로필 데이터를 백엔드 JIT (Just-In-Time) 엔진으로 전송하여 기계어 코드를 생성 한 다음 ByteCode 진입 점을 생성 된 항목으로 대체합니다. 기계 코드.
  5. 기계어 코드가 실행될 때 어떤 상태가 프로파일 가정을 깨 뜨리면 기계어 코드는 안전 문제를 피하기 위해 ByteCode를 다시 실행하도록 통역사에게 구제 조치됩니다.

기계 코드 찾기

취약점에 대해 이야기하기 전에 먼저 JIT 엔진에 의해 생성 된 기계어 코드를 찾아야합니다. for 루프는 일반적으로 인터프리터에서 JIT 엔진을 호출합니다. 그림 2는 JIT를 트리거 할 수있는 간단한 JavaScript 코드를 보여줍니다.

그림 2. JIT를 트리거하는 간단한 JavaScript 코드 스니펫

루프 수가 일부 임계 값 (그림 3의 코드 스 니펫에서 0x32)보다 크면 루프 본문과 내부 호출 함수 인 opt 가 백엔드 JIT 엔진 작업 큐로 전송되어 최적화 된 기계어 코드를 생성합니다.

그림 3. 프론트 엔드 JIT 트리거의 코드 스니펫

백엔드 JIT 엔진 스레드는 대기열에서 작업을 가져오고 마지막으로 jscript9! Func :: Codegen을 호출하여 최적화 된 기계 코드를 생성합니다.

그림 4. 백엔드 JIT 코드 생성의 코드 스 니펫

백엔드 JIT 엔진은 IR (중간 표현) 빌드, 인라인, CFG (제어 흐름 그래프), 데이터 흐름 분석, 하위 할당, 레지스터 할당, 레이아웃, 인코딩 등과 같은 최적화 된 기계 코드를 생성하기 위해 여러 단계를 거칩니다.

그림 5. Func :: Codegen의 코드 스니펫

최적화 된 기계 코드가 생성되면 ByteCode 루프 본문을 대체하는 데 사용됩니다. 다음에 for 루프가 호출되면 대신 Js :: InterpreterStackFrame :: CallLoopBody 함수에서 기계어 코드가 호출됩니다.

그림 6. 루프 본문 기계어 코드

마지막으로 루프 본문 기계 코드는 내부 호출 함수 opt의 기계 코드를 호출합니다. 여기에서 볼 수 있습니다.

그림 7. Opt 기능 기계어 코드

CVE-2020-1380의 근본 원인 분석

CVE-2020-1380의 PoC는 그림 8에 나와 있습니다.

그림 8. CVE-2020-1380의 PoC

다음 단계는 버그를 유발할 수 있습니다.

  1. for 루프는 opt 함수를 JIT 엔진으로 보냅니다.
  2. opt 함수에서 "인수 연산"의 세 줄은 value2 를 value1로 설정할 수 있으며 Float32Array 첫 번째 요소 arr [0]은 value1에 의해 설정됩니다.
  3. opt 함수가 JIT 엔진으로 전송 된 후 인수 value2를 정수 0x1337에서 valueOf 콜백 함수 가있는 개체로 변경합니다 .
  4. opt 함수의 마지막 호출에서는 'flag'인수가 0으로 설정되어 있으므로 기본 블록 인 "if (flag == 1)"가 실행되지 않고 value2가 arr [0]으로 설정됩니다. 객체가 value2를 대체하기 때문에 암시 적 유형 변환이 발생하고 valueOf 콜백 함수가 기계어 코드에서 호출 될 수 있습니다.

JavaScript는 유형 또는 속성을 암시 적으로 변환 할 수있는 동적 언어입니다. 확인없이 JavaScript를 암시 적으로 직접 호출하는 생성 된 기계어 코드는 신뢰할 수 없습니다. Jscript9.dll은 ExecuteImplicitCall 함수를 사용하여 JavaScript 암시 적 호출을 안전하게 만듭니다.

먼저“arguments operation”의 세 줄을“arguments [0] = value2”로 변경하여 동일한 효과를냅니다. 그림 9는 생성 된 JIT 코드 스 니펫을 보여줍니다.

그림 9. JIT 코드 스니펫

유형 변환 함수 jscript9! Js :: JavascriptConversion :: ToFloat_Helper가 호출되기 전에 일부 값은 주소 0x140F3F68 및 0x140F3E86에 별도로 저장된 플래그로 설정됩니다. 0x140F3F68에 저장된 플래그는 ImplicitCallFlags이고 0x140F3E86에 저장된 다른 플래그는 DisableImplicitFlags입니다. DisableImplicitFlags는 3 (DisableImplicitCallFlag | DisableImplicitExceptionFlag)으로 설정됩니다. 즉, valueOf와 같은 JavaScript 코드에서 암시 적 호출을 호출 할 때 float 로의 형식 변환이 허용되지 않습니다.

Js :: JavascriptConversion :: ToFloat_Helper 함수는 입력 유형을 확인하여 선택할 유형 변환 경로를 결정합니다. value2가 객체이기 때문에 Js :: DynamicObject :: ToPrimitive가 호출되고 마지막으로 ExecuteImplicitCall이 호출됩니다.

그림 9. JIT 코드 스니펫

유형 변환 함수 jscript9! Js :: JavascriptConversion :: ToFloat_Helper가 호출되기 전에 일부 값은 주소 0x140F3F68 및 0x140F3E86에 별도로 저장된 플래그로 설정됩니다. 0x140F3F68에 저장된 플래그는 ImplicitCallFlags이고 0x140F3E86에 저장된 다른 플래그는 DisableImplicitFlags입니다. DisableImplicitFlags는 3 (DisableImplicitCallFlag | DisableImplicitExceptionFlag)으로 설정됩니다. 즉, valueOf와 같은 JavaScript 코드에서 암시 적 호출을 호출 할 때 float 로의 형식 변환이 허용되지 않습니다.

Js :: JavascriptConversion :: ToFloat_Helper 함수는 입력 유형을 확인하여 선택할 유형 변환 경로를 결정합니다. value2가 객체이기 때문에 Js :: DynamicObject :: ToPrimitive가 호출되고 마지막으로 ExecuteImplicitCall이 호출됩니다.

그림 10. Js :: DynamicObject :: ToPrimitive의 코드 스니펫

ExecuteImplicitCall은 DisableImplicitFlags를 확인합니다. 값이 0이 아니면 JavaScript 암시 적 호출이 호출되지 않고 undefined를 직접 반환합니다. 마지막으로, 기계어 코드는 Interpreter에게 구제 조치되고 Interpreter에서 암시 적 호출을 안전하게 호출합니다.

그림 11. ExecuteImplicitCall의 코드 스 니펫

그러나“arguments operation”의 세 줄이“arguments [0] = value2”를 대체하는 데 사용되면 생성 된 기계 코드가 DisableImplicitFlags를 설정하지 않고 Js :: JavascriptConversion :: ToFloat_Helper를 직접 호출하는 것을 볼 수 있습니다.

그림 12. JIT 코드 스니펫

마지막으로, 암시 적 호출 valueOf는 기계 코드에서 직접 호출됩니다. 공격자는이 비 확인 콜백 기회를 사용하여 작업자 스레드에 의해 TypedArray의 ArrayBuffer 메모리를 중성화하는 것과 같은 UAF 취약점을 트리거 할 수 있습니다.

그림 13. UAF 트리거의 코드 스니펫

3 줄의 "인수 연산"이 DisableImplicitFlags 설정 기계 코드를 제거 할 수있는 이유는 제 호기심을 자극했습니다. 백엔드 JIT GlobOpt 단계에서 arguments [0]의 유형 추론 오류가 근본 원인이라고 생각합니다. JIT 엔진은 인수 유형 [0]을 변경하는 데 사용할 수있는 Array.prototype.push의 부작용을 알지 못합니다. 이 유형 추론 오류 문제를 방지하려면 Array.prototype.push 작업 후에 arguments [0] 유형을 종료해야합니다.

Array.prototype.splice를 사용하거나 Float64Array를 사용하여 Js :: JavascriptConversion :: ToNumber_Helper의 변환 경로를 호출하는 것과 같은 다른 메서드가이 버그를 트리거 할 수 있습니다. 이 버그는 8 월 패치에서도 수정되었습니다.

그림 14. Float64Array의 크래시 스냅샷

결론

지난 몇 년 동안 Internet Explorer를 대상으로하는 제로 데이 공격은 일반적으로 vbscrpt.dll 및 jscript.dll 취약성을 악용했습니다. CVE-2020-1380은 jscript9.dll의 JIT 엔진을 대상으로하기 때문에 특별합니다. JIT 취약성은 V8, JavascriptCore, Spidermonkey 및 Chakra와 같은 최신 JavaScript 엔진의 일반적인 문제입니다. 아마도 공격자들은 이제 Internet Explorer의 JIT 엔진을 표적으로 삼고있을 것입니다.

원본 글 링크 : https://www.trendmicro.com/en_us/research/20/h/cve-2020-1380-analysis-of-recently-fixed-ie-zero-day.html?fbclid=IwAR0LUIrIGpIJ5LzursshpexYsbKB2WL_zkR-Geck7RTsBR28GEUzfj-zep0