[Format] SWF

SWF

0x00. Intro

SWF 파일 포멧에 대해 알아보겠다.
SWF 파일은 Adobe사에서 제공하는 플래시 동영상 파일 포맷으로 광고등 다양한 곳에서 사용되고 있는 파일 포맷이다. 이 또한 PDF 파일 포맷과 같은 독립성을 갖고 있다. 현재 APT 공격과 함께 가장 많이 사용되는 악성코드 배포 방법이며 최근 광고를 이용한 악성코드 감염에 SWF 신규 취약점을 사용하고 있다. 따라서 SWF 파일 포맷 분석을 통해 정적 분석을 통한 악성 여부 판단 방법을 확인해 본다.
  • 개발 언어 : Python 2.7.x
  • 개발 환경 : ipython Notebook
  • SWF Specification : SWF Specification, AVM2
  • 사용 도구 : HxD, Notepad++
  • 사용 모듈 : PyLZMA, PySWF
  • 개발 목표
    • PySWF 모듈의 확장형태로 개발한다.
    • ActionScript3의 명령어 형태로 화면에 출력한다.
  • 개발 모듈 다운로드 : v0.1
  • Usage : PySWF 모듈이 설치된 곳에 다운로드한 모듈을 복사한 후 다음의 위치에 하위 그림과 같은 코드를 추가하면 된다. PySWF 모듈은 File I/O를 기반으로 분석하는 형태를 갖고 있어서 DoABC( )가 필요한 부분을 별도의 파일 (DoABC.dmp)로 생성한 후 파일을 버퍼 형식으로 접근했다. 또한 원본 PySWF 모듈에서 결과값으로 사용한 Binary를 AVM2 Code로 저장힉 위해 기존의 self.bytes 에 저장하는 부분을 주석처리했다.
    • tag.py -> DoABC 클래스 -> parse( ) 함수

In [14]:
# -*- coding:utf-8 -*-

fname = r"CVE-2015-7645.swf_"

0x10. 전체 구조


SWF 파일 포맷의 기본 구조는 "Header(헤더)" 와 "Body(바디)"로 구성되어 있다.
  • Header(헤더) : SWF 시그니처와 파일 버전정보, 프레임 정보등을 담고 있다.
  • Body(바디) : Tag로 구성되어 있으며 Tag를 통해 속성, 프레임등 개별 정보를 담고 있다.

0x20. Header (파일 헤더)

SWF 파일의 첫 3Bytes는 SWF 파일임을 알 수 있는 시그니처가 존재하고 다음의 특징을 갖는다.
1) 시그니처가 거꾸로 되어 있다.
PDF 파일등 일반적으로 파일 포맷 시그니처는 앞에서 부터 읽을 수 있는 형태의 문자열로 존재한다. SWF 파일의 경우 "SWF" 와 같은 문자열로 되어 있으나 거꾸로 되어 있어 실제 모습은 "FWS" 로 되어 있다.

2) 시그니처가 3가지다.
SWF 파일은 플래쉬 파일로 동영상 재생 목적의 파일 포맷이다. 이미지나 동영상의 경우 파일 용량이 매우 크기 때문에 저장/이동시 효율성을 위해 압축해 저장하는게 일반적이다. SWF 파일의 경우도 동일하며 압축 여부/종류에 따라 3가지 시그니처로 되어 있다.
  • FWS : 압축되지 않는 경우
  • CWS : zlib로 압축한 경우
  • ZWS : LZMA로 압축한 경우
따라서 압축을 해제한 상태로 포맷 분석을 진행해야 한다.
In [15]:
def Ispossible(fname):
    swf_sig = file(fname, "rb").read(3)
    if swf_sig in ["FWS", "CWS", "ZWS"]:
        return swf_sig.upper()[::-1]
    else:
        return None

print Ispossible(fname)
SWF
다음 4번째 Byte는 파일 버전정보이다. 파일 버전정보는 ASCII값이 아닌 Hex로 되어 있는 숫자로 되어 있다. 보통 파일 포맷 분석시 파일 버전 정보는 큰 의미를 갖지 않는다. 하지만 SWF 파일의 경우 파일 버전에 따라 사용할 수 있는 기능이 다르기 때문에 파일 버전 정보가 중요하게 사용된다. 따라서 분석 모듈 개발시 반드시 확인해야 한다.
* 포인트 : 파일 포맷 버전에 따라 사용 가능한 기능이 다르기 때문에 파일 버전은 매우 중요한 정보가 된다.

다음 5~8Bytes 까지 4Bytes는 압축 해제시 데이터의 크기가 된다. 압축된 상태인 경우 파일 크기 이후 (Offset : 9) 부터 압축 해제 대상이 된다.
In [17]:
def decompress(fname):
    sig = Ispossible(fname)
    
    start_offset = 9
    data = file(fname, "rb").read()[start_offset:]
    if sig == "SWC":
        import zlib
        decomp_data = zlib.decompress(data)
    elif sig == "SWZ":
        import pylzma
        decomp_data = pylzma.decompress(data)
    else:
        decomp_data = data
    
    return decomp_data

print len(decompress(fname))
4069

0x30. Body (바디)

압축이 정상적으로 해제되고 Header의 나머지 부분(프레임 정보)을 제거하면 Body의 시작위치가 된다. SWF 파일의 Body는 Tag 단위로 나눌 수 있다. Tag에 따라 SWF 파일에서 출력하려는 동영상/사운드 등이 결정된다. Tag의 구조는 다음과 같다.

SWF 파일 버전 8 이상부터는 위의 구조를 따르도록 되어 있다. 따라서 첫번째 Tag는 반드시 FileAttribute Tag이며 마지막 Tag는 End Tag로 구성되어 있어야 한다. Tag의 길이는 가변길이이므로 순차적으로 Tag를 읽어 분석해야 한다.

0x40. Tags (태그)

Tag 단위 분석을 위해선 Tag의 포맷을 알아야 한다. Tag Format은 표현하고자 하는 데이터의 크기에 따라 2종류로 나눈다.


Tag Format은 RECORDHEADER로 시작된다. RECORDHEADER는 Tag의 종류를 나타내는 TagCode와 TagLength로 구성되어 있다. 만약 저장된 데이터의 크기가 64Bytes (2 ^ 6) 이상인 경우 RECORDHEADER 다음 4Bytes를 TagLength로 사용한다. 따라서 위의 그림과 같이 2가지 형태의 RECORDHEADER로 정의할 수 있다. ( 참고 : TagLength는 RECORDHEADER 크기를 제외한 크기이다. )
* 포인트 : RECORDHEADER는 TagCode와 TagLength로 구성되어 있으며 Tag 데이터에 따라 2Bytes ~ 6Bytes의 크기를 갖는다.

TagCode는 2가지 종류(Definition Tag, Control Tag), 69가지 (FileAttributesTag ~ EndTag까지) 로 나눌 수 있다. 이렇게 분류된 Tag가 위의 그림과 같이 조합되어 우리가 볼 수 있는 형태로 출력된다. 하지만 우리는 포맷 분석을 할 뿐 플레이어 개발이 목적이 아니므로 Definition Tag, Control Tag와 같은 분류까지는 염두하지 않아도 된다.
반대로 우리가 눈여겨볼 Tag는 FileAttribute, DoAction과 DoABC 이다.
  • 참고 : 간혹 Adobe에서 공식으로 배포한 Specification 문서내에 정의되지 않은 Tag가 SWF 파일 내에 존재하기도 한다. 이는 문서로 표기만 되지 않았을 뿐 사용가능한 Tag로 보인다. 이러한 Tag가 발견되면 "0x90. 참조" 의 "Undocument Tag 정보"를 참고하기 바란다.

0x41. FileAttribute


SWF 파일 버전이 9이상인 경우 Tag의 시작은 반드시 FileAttribute Tag라야 된다. FileAttribute Tag는 하위 Tag의 동작에 영향을 미칠 수 있는 속성정보를 bit flag 형태로 갖고 있다.
  • HasMetaData : MetaData가 존재하는지 여부를 알려주는 플래그이다.
  • SWFFlagsAS3 : ActionScript3가 SWF 파일 내부에 포함되어 있는지 여부를 알려주는 플래그이다.
MetaData가 있는 경우 별도의 Tag ( DefineBinaryData의 Data 필드 ) 에 MetaData가 정의되어 있다. 이 데이터 또한 AcrionScript3와 관련된 데이터이므로 향후 디컴파일 제작시 참조해야 한다.
SWF 파일의 경우 과거 취약점은 파일 포맷 분석/파싱 과정에서 모듈상에서 발생하는 Access Violation을 이용하는 형태였으나 최근엔 다수의 프로그래밍 언어처럼 User-After-Free 취약점을 타겟으로 경우가 많다. 따라서 SWF 파일 내의 Action Script를 확인해야 한다. 그러므로 HasMetaData나 SWFFlagsAS3 와 같은 플래그가 중요하게 되었고 따라서 FileAttribute가 중요한 Tag가 되었다.

0x42. DoABC

FileAttribute Tag가 SWF 파일 버전 9부터 지원하는 것처럼 DoABC Tag 또한 버전 9 부터 지원하는 Tag이다. 기본 구조는 다음과 같다.

DoABC Tag는 DoABC의 ABCData를 추출해 ActionScript Virtual Machine 2 (이하 AVM2) ByteCode로 변환해 분석해야 한다.

AVM2의 내부 구조는 위의 그림과 같다. 위의 그림처럼 ABCData 또한 파일 버전이 존재하고 저장된 정보의 종류에 따라 Constant, Metadata 등으로 나누어져 저장되어 있다. 따라서 개별 데이터를 모두 분석/참조해 AVM2 Code를 마치 C, C++ 언어로 작성된 파일을 디스어셈블리 한 것과 유사한 과정을 거쳐야 한다.

0x50. AVM2 (Action Script Virtual Machine2) Code

0x60. 취약한 부분

SWF 파일을 이용한 악성코드가 존재할 경우 다음의 부분을 살펴봐야 한다.
  • 파일 포맷 분석 과정에서 발생하는 포맷 취약점
  • SWF 파일 내부에 존재하는 다른 파일 포맷에 의해 발생하는 취약점
  • DoAction, DoABC등 ActionScript3의 프로그래밍 언어상의 문제점
  • DoABC의 AVM2 Code의 프로그래밍 언어상의 문제점

0x70. 취약점 분석정보

< Format >
< Type Confusion >

0x90. Bug

Bug 1. SWFHeader 분석 버그

선택한 취약점 SWF 파일의 경우 PySWF 모듈을 통해 분석하면 위와 같이 DoABC Tag가 없는 것으로 출력된다.

Sothink는 위와 같이 알수없는 오류와 함께 프로그램이 종료된다.

하지만 JPEXS를 통해 확인해 보면 위와 같이 정상적으로 DoABC Tag내의 ABCData를 확인할 수 있다.

코드상으론 End Tag를 만나면 종료되는 형태로 작성되어 있으나 End Tag를 만나는 위치가 Offset 0x1E로 되어 있다. 하지만 해당 위치는 "MetaData"의 일부이다. 즉, SWF Header를 분석하는 과정상에 문제가 발생한 것으로 PySWF가 버퍼가 아닌 파일 IO를 이용해 데이터륻 얻어온 만큼 잘못된 파일 Offset 연산이 추가된 것으로 보인다.

확인 결과 분석 대상 SWF 파일이 압축되지 않은 형태인 경우 위의 같이 프레임 정보를 얻어오는 과정이 중복으로 들어가 있다. 따라서 Header 클래스에서 연산을 제거하거나 parse_tags() 내의 연산 부분의 Indent를 맞춰주어야 한다.
Bug 2. s32 자료형 버그
s32 자료형은 일반적으로 알고 있는 Signed Int형이 아니다. AVM2 Specification에서 정의하고 있는 s32 자료형은 다음과 같다.

따라서 단순히 unpack으로 "<i" 으로 읽어들이면 오류가 발생한다. 따라서 1Byte 단위로 읽어들여야 하고 1Byte의 최상위 Bit의 Set, Unset에 따라 값을 연산해야 한다.

0x99. 참고

댓글

가장 많이 본 글