Wins blog

글로벌 정보보안 파트너! Global Security  No.1 윈스는 국가대표 정보보안 기업에서 글로벌 강소기업으로 도약합니다.

보안 정보

앞 내용 보기 다음 내용 보기
취약점 정보Adobe Flash Player ByteArray Use-After-Free 취약점 분석
작성일 2015-09-24 조회 2987

Adobe Flash Player ByteArray Use-After-Free 취약점 분석 (CVE-2015-5119) 

 

 

1. 개요

 

2015년 7월, 이탈리아의 스파이웨어 개발 업체인 “해킹팀(Hacking Team)”이 공격을 당하면서 약 400GB의 내부자료가 토렌트로 유출되었다. 유출된 자료에는 스파이웨어를 유포하기 위한 여러 소프트웨어의 익스플로잇이 포함되어 있었으며, 그 중 일부는 제로데이로 최신 버전의 소프트웨어에서 실행이 가능하였다. 특히나 플래시 취약점은 공식 패치가 발표도 되기전에 익스플로잇 킷(Angler, Nuclear, Neutrino 등)이나 다른 공격자들에게 악용되는 사태가 발생하였다. 이번 보고서에서는 첫 번째로 유출되어 SNS에 이슈가 된 플래시 취약점인 CVE-2015-5119에 대하여 분석을 진행하였다. 

 

 

2. 취약점 분석

 

CVE-2015-5119는 ByteArray 클래스에 존재하는 Use-After-Free 취약점이다. 해당 취약점은 해킹팀이 러시아 해커(Vitaly Toropov)로부터 2014년 2월경에 약 $40k (한화 4700만원)에 구매한 것으로 알려져 있다.

 

[그림 1] CVE-2015-5119

 

취약점이 발생하는 원인은 다음과 같다. ByteArray 클래스는 “ba[0] = free;”와 같이 배열에 클래스를 사용하는 것을 허용한다. 
ByteArray 배열에 객체 타입의 자료형이 오게 될 경우, 실제로는 해당 클래스의 valueOf 메소드를 호출하여 리턴값을 가져오게 되는데 메소드의 처리과정에서 기존의 ByteArray 배열의 사이즈를 변경하게 될 경우, AVM 내부에서 배열의 재할당이 이루어지고, 이전의 배열은 해제(free)된다. 

 

그러나 valueOf가 실행된 뒤 배열에 값을 입력하는 과정에서 해당 포인터는 재할당된 배열이 아닌 이전에 해제된 배열의 포인터를 참조하게 되어 Use-After-Free가 발생하게 된다.

 

실제로 내부에서 처리되는 과정은 다음과 같다.

 

[그림 2] ByteArrayObject::setUintProperty

 

[그림 2]의 ByteArrayObject::setUintProperty 메소드는 배열의 인덱스에 정수형의 값을 입력할 때 호출되는 함수로 취약점과 관련된 주요 함수이다. 해당 함수가 어떻게 동작하는 지를 확인하기 위해서 [그림 1]의 POC 코드에서처럼 “ba[0] = 0x41”를 실행해보면 다음과 같이 처리된다.

 


[그림 3] ByteArray::operator[]

 

setUintProperty 내부에서 첫 번째로 호출되는 함수는 ByteArray 객체(068a7410)에서 배열의 실제 주소를 얻어온다. [그림 3]은 연산자의 마지막 처리부분으로 객체의 오프셋 +0x8에 위치한 실제 배열의 주소(068a5058)를 리턴값(eax)으로 넘기는 것을 확인할 수 있다.

 

[그림 4] ByteArray 클래스 구조

 

ByteArray 클래스의 구조는 공개된 AVM 소스에서 확인할 수 있다.

 


[그림 5] ByteArrayObject::setUintProperty

 

리턴된 배열의 주소는 esi 레지스터에 저장되고 배열에 입력할 값을 얻어오기 위해 AvmCore::integer 함수가 호출된다. 마지막 라인을 확인해보면 해당 함수의 리턴값은 POC코드에서 사용하는 0x41이다. 

 


[그림 6] ByteArrayObject::setUintProperty

 

마지막으로 기존의 esi에 저장된 배열주소에 리턴값(eax)을 쓰게된다.

 

다음은 취약점을 발생시키기 위해 “ba[0] = free(POC 클래스)”가 처리되는 과정이다. 우선 처음 실행과정은 동일하게 esi 레지스터에 배열의 주소가 저장된 뒤, AvmCore::Integer 함수가 호출이 된다. 하지만 AvmCore::Integer가 실행이 되는 과정에서 앞서 언급했듯이 POC클래스의 valueOf 메소드 내부에서 배열의 크기를 변경한다. 

 


[그림 7] ByteArray::UnprotectedSetLengthCommon

 

ByteArray의 크기변경을 처리하기 위해서 내부에서 UnprotectedSetLengthCommon 메소드가 호출이 되고, 해당 메소드의 내부에서 ByteArray::Grower 클래스의 SetLengthCommon 메소드가 호출이 된다. [그림 7]에서 확인할 수 있듯이, SetLengthCommon의 첫 번째 인자는 새로운 배열의 크기(88)이다.

 

[그림 8] ByteArray::Grower::ReallocBackingStore

 

SetLengthCommon 메소드의 내부 처리과정에서는 변경된 크기를 수용할 버퍼를 재할당한다. [그림 8]에서는 MMgc::NewTaggedArray를 통해 버퍼를 할당하여 새로운 배열의 주소(06f51000) 얻어오는 부분이다.

 


[그림 9] ByteArray::Grower::ReallocBackingStore

 

[그림 9]에서는 새로 할당된 주소가 기존의 ByteArray 객체(068a7410)에 위치한 이전 배열주소(+0x8)를 대신해서 값이 변경되는 것을 확인할 수 있다.

 

[그럼 10] ByteArray::Grower::SetLengthCommon

 

ByteArray::Grower::SetLengthCommon 메소드의 마지막 처리 파트에서는 ByteArray 객체의 크기(+0x10)도 새롭게 할당된 크기로 변경되는 것을 확인할 수 있다.

 

[그림 11] ByteArray::UnprotectedSetLengthCommon

 

ByteArray::Grower 클래스의 SetLengthCommon 메소드가 호출되어 메모리 재할당이 모두 처리된후에는 Grower 클래스의 소멸자가 호출이 된다.

 


[그림 12] ByteArray::Grower::~Grower

 

[그림 12]에서 확인할 수 있듯이 Grower의 소멸자에서는 크기가 변경되기 전의 배열(068a5058)에 대한 해제가 이루어지게 된다. 

 


[그림 13] ByteArrayObject::setUintProperty

 

마지막으로 [그림13] setUintProperty 메소드에서 AvmCore::Integer의 호출이 끝이 나고 POC 스크립트에서 valueOf 메소드의 리턴값 0xFF가 eax 레지스터에 전달되었다. 그 후, 배열에 리턴값을 쓰게 되는데 esi 레지스터에 저장된 배열 주소는 AvmCore::Integer 메소드가 처리되기전 저장한 배열 주소, 즉 해제된 배열의 주소이다. 결과적으로 setUintProperty는 해제된 메모리에 값을 쓰게 되는 것을 확인할 수 있다.

 

 

3. 익스플로잇

 

취약점의 핵심은 해제된 메모리에 임의 바이트(valueOf 리턴) 값을 쓸 수 있다는 점이다. 유출된 소스코드에서는 취약점을 익스플로잇 하기 위하여 해제된 메모리에 벡터(Vector) 오브젝트를 할당하여 해당 객체의 길이(length)를 수정하는 방법을 사용하였다.

 


[그림 14] MyClass.as, TryExpl 메소드

 

[그림 14]의 TryExpl 메소드는 취약점을 이용하기 위하여 메모리를 구성하는 부분이다. 54~55번째 라인에서 ByteArray의 크기를 0xfa0으로 할당하는 것을 확인할 수 있다.

 

[그림 15] MyClass.as, TryExpl 메소드

 

[그림 15]는 메모리 구성 이후, 실제로 취약점을 트리거하는 부분으로 ByteArray 배열의 연산자에 MyClass 오브젝트를 생성하는 것을 확인할 수 있다.

 


[그림 16] MyClass.as, valueOf 메소드

 

[그림 16]은 MyClass 클래스의 valueOf 메소드가 선언된 부분이다. 해당 클래스의 내부에서 ByteArray의 크기를 0x1100으로 변경하여 이전 배열이 내부에서 해제(free)되게 된다. 그 후, 여러개의 Vector.를 메모리에 할당한다. 이때 벡터는 해제된 메모리의 위치에 할당 된다. 마지막으로 해당 메소드는 0x40을 리턴한 뒤 종료된다.

 


[그림 17] ByteArrayObject::setUintProperty

 

이때 내부에서 처리되는 과정은 [그림 17]에서처럼 setUintProperty 함수에서 확인할 수 있는데, 해제된 메모리에 기존의 값이 0x3f0으로, Vector.의 길이가 위치하고 있는 것을 확인할 수 있다. 그 후, valueOf의 리턴값인 0x40이  길이의 상위 1바이트에 덮어씌워지게 되어 길이가 0x40003f0으로 바뀌게 된다. 


취약점 트리거 이후, 길이가 변경된 벡터를 이용하면 이전에는 접근할 수 없는 메모리 영역에 임의로 읽기/쓰기가 가능하게 된다. 해당 트릭은 최근 플래시 취약점을 익스플로잇 할 때 사용하는 전형적인 방법으로 ASLR이나 DEP, CFG 등 메모리 보호기법에 대한 우회가 쉽게 이루어질 수 있다.

 

[그림 18] MyClass.as, TryExpl 메소드

 

[그림 18]에서도 확인할 수 있듯이, 취약점 트리거가 끝난 후 루프를 돌면서 길이가 0x3f0이 아닌 벡터를 찾는 것을 볼 수 있다. 이후 소스코드의 88 ~ 100번 라인에서 조작된 벡터를 사용하여 코드 실행을 시도한다.

 

 

4. 패치


[그림 19] 어도비 보안 업데이트 공지

 

CVE-2015-5119는 윈도우용 플래시 플레이어 ActiveX 버전을 기준으로 18.0.0.203 이후 버전부터 패치가 적용되었다. 

 


[그림 20] ByteArrayObject::setUintProperty

 

패치된 버전에서의 setUintProperty 메소드는 AvmCore::integer를 먼저 호출하여 처리한 뒤, ByteArray의 배열주소를 얻어오는 방식으로 문제점을 해결하였다.

 

 

5. 참고 자료

 

https://github.com/hackedteam
https://helpx.adobe.com/kr/security/products/flash-player/apsb15-16.html
https://tsyrklevich.net/2015/07/22/hacking-team-0day-market/
http://googleprojectzero.blogspot.kr/2015/08/attacking-ecma-engines-with.html
https://github.com/adobe-flash/avmplus/

첨부파일 첨부파일이 없습니다.
태그