Keychain Forensics : Part III

물리 메모리 이미지와 키체인 파일 수집을 완료하면, 키체인 분석 준비가 끝난 것이다. 이번 포스팅에서는 키체인 구조를 이해함으로 물리 메모리에서 추출할 데이터가 어떤 것인지를 알아보고, 실제로 키체인을 분석하는 과정을 확인해보겠다. 키체인 파일은 애플 데이터베이스 파일 포맷(Apple Database File Format)으로 다음과 같은 구조를 가진다.

애플 데이터베이스 파일 포맷은 크게 애플 데이터베이스 헤더, 애플 데이터베이스 스키마 테이블, 하나 이상의 테이블로 이루어져 있으며, 헤더->스키마->테이블->데이터 순으로 분석이 가능한 구조로 되어 있다. 테이블은 테이블 헤더와 다수의 레코드로 이루어져 있으며, 레코드는 레코드 헤더와 레코드 데이터로 이루어져 있다. 실제로 사용자 데이터 정보는 레코드 데이터로 저장된다.

키체인 데이터베이스에는 약속된 테이블만 존재할 수 있다. 주요 테이블 정보는 다음과 같다.

일반적인 데이터베이스라면, 위의 순서를 따라 레코드 데이터를 사용자에게 보여주면 되지만, 키체인 패스워드 관리 시스템은 주요 정보를 암호화하여 저장하므로, 이 데이터를 복호화하는 방법을 알아야 한다. 사용자 데이터 복호화 순서는 그림 한 장으로 간략하게 표현할 수 있다.

키체인은 보안과 성능 두 마리 토끼를 잡기 위해, 키체인 테이블에 있는 하나의 데이터베이스 키로 각 레코드 키를 암/복호화 할 수있는 구조(성능 향상)를 가지고 있으며, 데이터베이스 키를 암호화하지 않을 경우에 키체인 파일 만으로 사용자 기밀 데이터가 유출되는 것을 막기 위해 사용자 패스워드 기반의 마스터 키를 이용하여 데이터베이스 키를 암호화하여 보관(보안성 향상)한다.

마스터 키는 사용자 패스워드를 기반으로 메모리에 키를 유지하기 때문에, 사용자 암호를 알거나, 루트 권한으로 메모리 이미지를 획득하지 않는 이상 키를 획득할 수 없다. 즉, 마스터 키 획득을 위해서 메모리 이미지를 수집한 것이다.

Mac OS X의 메모리 이미지를 분석하는 도구는 여러가지가 있지만, 키체인의 마스터 키를 추출할 수 있는 도구는 현재 volafox밖에 없다.

# python vol.py -i [fusion_builder_container hundred_percent="yes" overflow="visible"][fusion_builder_row][fusion_builder_column type="1_1" background_position="left top" background_color="" border_size="" border_color="" border_style="solid" spacing="yes" background_image="" background_repeat="no-repeat" padding="" margin_top="0px" margin_bottom="0px" class="" id="" animation_type="" animation_speed="0.3" animation_direction="left" hide_on_mobile="no" center_content="no" min_height="none"][MEMORY] -o keychaindump

$ python vol.py -i ~/Desktop/show/macosxml.mem -o keychaindump

[+] Virtual Memory Map Information
[-] Virtual Address Start Point: 0x108240000
[-] Virtual Address End Point: 0x7fffffe00000
[-] Number of Entries: 85

[+] Generating Process Virtual Memory Maps
[-] Region from 0x108240000 to 0x108349000 (r-x, max rwx;)
[-] Region from 0x108349000 to 0x108356000 (rw-, max rwx;)
[-] Region from 0x108356000 to 0x108371000 (r--, max rwx;)
[-] Region from 0x108371000 to 0x108372000 (r--, max rwx;)
[-] Region from 0x108372000 to 0x108373000 (r--, max rwx;)
[-] Region from 0x108373000 to 0x108374000 (rw-, max rwx;)
[-] Region from 0x108374000 to 0x108375000 (r--, max rwx;)
[-] Region from 0x108375000 to 0x108384000 (r-x, max rwx;)
[-] Region from 0x108384000 to 0x108385000 (rw-, max rwx;)
... ...
[-] Region from 0x108821000 to 0x108822000 (---, max rwx;)
[-] Region from 0x108822000 to 0x108837000 (rw-, max rwx;)
[-] Region from 0x108837000 to 0x108838000 (---, max rwx;)
[-] Region from 0x108838000 to 0x108839000 (---, max rwx;)
[-] Region from 0x108839000 to 0x10884e000 (rw-, max rwx;)
[-] Region from 0x10884e000 to 0x10884f000 (---, max rwx;)
[-] Region from 0x10884f000 to 0x1088aa000 (rw-, max rwx;)
[-] Region from 0x1088aa000 to 0x109acf000 (r--, max r-x;)
[-] Region from 0x7fef03400000 to 0x7fef03500000 (rw-, max rwx;)
[-] Region from 0x7fef03500000 to 0x7fef03600000 (rw-, max rwx;)
[-] Region from 0x7fef03600000 to 0x7fef03700000 (rw-, max rwx;)
[-] Region from 0x7fef03800000 to 0x7fef04000000 (rw-, max rwx;)
[-] Region from 0x7fef04000000 to 0x7fef04800000 (rw-, max rwx;)
[-] Region from 0x7fef04800000 to 0x7fef04900000 (rw-, max rwx;)
[-] Region from 0x7fef04900000 to 0x7fef04a00000 (rw-, max rwx;)
... ...
[-] Region from 0x7fff80000000 to 0x7fffc0000000 (r--, max rwx;)
[-] Region from 0x7fffc0000000 to 0x7fffffe00000 (r--, max rwx;)
[-] Region from 0x7fffffe00000 to 0x7fffffe01000 (r--, max r--;)
[-] Region from 0x7fffffe6e000 to 0x7fffffe6f000 (r-x, max r-x;)

[+] Find MALLOC_TINY heap range (guess)
[-] range 0x7fef03400000-0x7fef03500000
[-] range 0x7fef03500000-0x7fef03600000
[-] range 0x7fef03600000-0x7fef03700000
[-] range 0x7fef04800000-0x7fef04900000
[-] range 0x7fef04900000-0x7fef04a00000

[*] Search for keys in range 0x7fef03400000-0x7fef03500000 complete. master key candidates : 0
[*] Search for keys in range 0x7fef03500000-0x7fef03600000 complete. master key candidates : 0
[*] Search for keys in range 0x7fef03600000-0x7fef03700000 complete. master key candidates : 0
[*] Search for keys in range 0x7fef04800000-0x7fef04900000 complete. master key candidates : 0
[*] Search for keys in range 0x7fef04900000-0x7fef04a00000 complete. master key candidates : 6

[*] master key candidate: 78006A6CC504140E077D62D39F30DBBAFC5BDF5995039974
[*] master key candidate: 26C80BE3346E720DAA10620F2C9C8AD726CFCE2B818942F9
[*] master key candidate: 2DD97A4ED361F492C01FFF84962307D7B82343B94595726E
[*] master key candidate: 21BB87A2EB24FD663A0AC95E16BEEBF7728036994C0EEC19
[*] master key candidate: 05556393141766259F62053793F62098D21176BAAA540927
[*] master key candidate: 903C49F0FE0700C0133749F0FE0700404158544D00000000

물리 메모리에서 마스터 키를 추출하였으면, 이걸로 데이터베이스 키를 복호화하여 실제 사용자 기밀정보를 추출해야 한다. 사용자 기밀 정보 추출은 본 저자가 개발한 도구인 Chain Breaker로 수행할 수 있다.

# python chainbreaker.py -i [USER_KEYCHAIN] -k [MASTERKEY]

$ python chainbreaker.py -i ~/Desktop/show/login.keychain -k 26C80BE3346E720DAA10620F2C9C8AD726CFCE2B818942F9
[-] DB Key
00000000: 05 55 63 93 14 17 66 25 9F 62 05 37 93 F6 20 98 .Uc...f%.b.7.. .
00000010: D2 11 76 BA AA 54 09 27 ..v..T.'
[+] Symmetric Key Table: 0x00006488
[+] Generic Password: 0x0000dea4
[ +] Generic Password Record
[-] RecordSize : 0x000000fc
[-] Record Number : 0x00000000
[-] SECURE_STORAGE_GROUP(SSGP) Area : 0x0000004c
[-] Create DateTi me: 20130318062355Z
[-] Last Modified DateTime: 20130318062355Z
[-] Description :
[-] Creator :
[-] Type :
[-] PrintName : ***********@gmail.com
[-] Alias :
[-] Account : 1688945386
[-] Service : iCloud
[-] Password
00000000: ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ****************
00000010: 7A ** 69 ** 50 ** 51 36 ** ** ** 48 32 61 31 66 ****************
00000020: ** 49 ** 73 ** 62 ** 79 79 41 6F 3D **********=



[+] Internet Record
[-] RecordSize : 0x0000014c
[-] Record Number : 0x00000005
[-] SECURE_STORAGE_GROUP(SSGP) Area : 0x0000002c
[-] Create DateTime: 20130318065146Z
[-] Last Modified DateTime: 20130318065146Z
[-] Description : Web form password
[-] Comment : default
[-] Creator :
[-] Type :
[-] PrintName : www.facebook.com (***********@gmail.com)
[-] Alias :
[-] Protected :
[-] Account : ***********@gmail.com
[-] SecurityDomain :
[-] Server : www.facebook.com
[-] Protocol Type : kSecProtocolTypeHTTPS
[-] Auth Type : kSecAuthenticationTypeHTMLForm
[-] Port : 0
[-] Path :
[-] Password
00000000: ** ** ** ** ** ** ** ** ** ** ** ** ************

도구를 통해 사용자 패스워드를 손쉽게 획득함으로 포렌식 분석관은 로컬 시스템의 데이터에 제한되지 않고 클라우드 및 메일로 보관된 데이터에도 접근할 수 있는 가능성을 열어둘 수 있다. 물론 이러한 데이터를 획득하려면 수사영장을 다시 받아야하는 등 추가적인 소요가 발생할 순 있으나, 사용자 계정 인지한 상태에서 수사를 진행하는 것과 모르는 상태에서 진행하는 것은 수사관 입장에선 다를 수 있을 것이라 생각한다.

특히, 최근에는 데이터 암호화 솔루션을 이용하여 기밀 데이터를 보호하는 경우가 많기 때문에, 키체인에서 획득한 사용자의 패스워드로 암호화된 파일의 복호화를 시도해보거나, 암호화 솔루션이 키체인에 패스워드를 저장하는 경우에는 쉽게 데이터를 복호화하여 획득할 수 있을 것이다.

이러한 키체인 데이터 분석 기술이 여러 포렌식 분석관에게 도움이 되었으면 좋겠다. ;-)