레지스트리는 윈도우즈 운영체제와 응용프로그램에 관련된 방대한 설정과 운영 정보가 기록된 데이터베이스입니다. 윈도우즈 운영체제 초창기에는 INI 파일을 사용했으나, 레지스트리가 도입되면서 표준화된 계층적 데이터 구조, 다중 사용자 환경 지원, 접근 권한 제어, 바이너리 포맷 파일 기반의 효율적 I/O, 타입 시스템, 트랜잭션 등을 제공하게 되었습니다.
레지스트리를 분석하면 어떤 프로그램이나 서비스가 부팅 시 자동으로 실행되는지, 어떤 프로그램을 최근에 실행했는지, 어떤 프로그램을 얼마나 오래 사용했는지, 최근 어떤 파일을 검색했는지, 어떤 파일을 열어봤는지, 어느 서버에 접속했는지, 어떤 파일을 압축했는지 등 무수히 많은 정보를 추출할 수 있습니다.
따라서 레지스트리 분석은 사고 조사 초기에 수행해야 할 중요한 단계이며, 여기에서 확인된 정보에 따라 후속 조사의 진행이 결정될 수 있습니다.
레지스트리 하이브 파일
레지스트리 편집기(regedit)를 통해 하나로 보이는 레지스트리의 계층적 구조는 물리적으로 여러 개의 레지스트리 하이브 파일에 분산되어 있습니다.
%SystemRoot%\System32\config
디렉터리에는 아래와 같은 레지스트리 하이브 파일이 존재합니다.
- SAM: HKEY_LOCAL_MACHINE\SAM
- SECURITY: HKEY_LOCAL_MACHINE\Security
- SOFTWARE: HKEY_LOCAL_MACHINE\Software
- SYSTEM: HKEY_LOCAL_MACHINE\System
또한 각 사용자 계정의 디렉터리에는 NTUSER.DAT 레지스트리 하이브 파일이 존재합니다.
HIVE 파일 구조
레지스트리 하이브 파일은 아래와 같이 구성되어 있습니다.
BASE 블록 구조
HIVE BIN 헤더 구조
로그프레소 포렌식의 hive-file 커맨드는 아래와 같은 필드를 출력합니다.
- key: 키
- type: 타입 (문자열, 이진값, DWORD, QWORD 등)
- name: 값
- value: 데이터
- last_written: 마지막 기록 시각
코드게이트 포렌식 문제 연습
로그프레소 포렌식 솔루션은 쿼리를 기반으로 다양한 포렌식 아티팩트를 연관 분석하는 강력한 기능을 지원합니다. 아래에서는 이전 코드게이트 2011 컨퍼런스에서 레지스트리와 관련하여 출제된 문제를 어떻게 분석하는지 설명합니다.
we are investigating the military secret's leaking. we found traffic with leaking secrets while monitoring the network. Security team was sent to investigate, immediately. But, there was no one present.
It was found by forensics team that all the leaked secrets were completely deleted by wiping tool. And the team has found a leaked trace using potable device. Before long, the suspect was detained. But he denies allegations.
Now, the investigation is focused on potable device. The given files are acquired registry files from system. The estimated time of the incident is Mon, 21 February 2011 15:24:28(KST).
Find a trace of portable device used for the incident.
The Key : "Vendor name" + "volume name" + "serial number" (please write in capitals)
- Codegate 2011 Forensic 300 문제 파일
제시된 파일의 압축을 풀면 6개의 레지스트리 하이브 파일을 확인할 수 있습니다.
먼저 시스템에 마운트된 장치 정보를 추출하기 위해 SYSTEM 하이브 파일에서 MountedDevices 키를 검색하면 아래와 같이 이진값으로 된 레지스트리 데이터를 확인할 수 있습니다.
이 데이터를 UTF-16으로 디코드하면 아래와 같은 문자열을 확인할 수 있습니다.
hive-file codegate2011\system.bak
| search key == "*MountedDevices" and name == "\\DosDevices*"
| eval value = substr(decode(value, "UTF-16LE"), 4)
USB 값만 필터링해서 정규식으로 파싱하면 제조사, 모델명, 버전, 시리얼을 추출할 수 있습니다.
hive-file codegate2011\system.bak
| search key == "*MountedDevices" and name == "\\DosDevices*"
| eval value = substr(decode(value, "UTF-16LE"), 4)
| search value == "*USB*"
| rex field=value "Ven_(?<vendor>[^&]+)&Prod_(?<product>[^&]+)&Rev_(?<version>[^#]+)#(?<serial>[^&]+)"
| eval serial = lower(serial)
| fields vendor, product, version, serial, value
그러나 아직 볼륨 이름과 장치를 연결한 시간을 확인하지 못한 상태입니다. 장치를 연결한 시간은 HKLM\SYSTEM\ControlSet00X\Enum\USB\VID_####&PID_#### 키의 마지막 수정 시간을 확인하면 됩니다. 아래와 같이 쿼리하면 66개의 키를 확인할 수 있습니다.
hive-file codegate2011\system.bak
| search key == "*USB\\VID_*"
| eval serial = lower(valueof(split(key, "\\"), 5))
| stats max(last_written) as last_connect by serial
볼륨 이름은 HKLM\SOFTWARE\Microsoft\Windows Portable Devices\Devices 키에서 확인할 수 있습니다. 아래와 같이 쿼리하면 40개의 키를 확인할 수 있습니다.
hive-file codegate2011\software.bak
| search key == "*Windows Portable Devices*" and name == "FriendlyName"
| rex field=key "&REV_[^#]+#(?<serial>[^&]+)"
| eval serial = lower(serial)
| stats first(value) as volume_name by serial
이 3종의 쿼리 결과를 시리얼 번호로 조인하면 원하는 결과를 한 번에 추출할 수 있습니다.
hive-file codegate2011\system.bak
| search key == "*MountedDevices*"
| eval value = substr(decode(value, "UTF-16LE"), 4)
| search value == "*USB*"
| rex field=value "Ven_(?<vendor>[^&]+)&Prod_(?<product>[^&]+)&Rev_(?<version>[^#]+)#(?<serial>[^&]+)"
| eval serial = lower(serial)
| stats count by vendor, product, version, serial, value
| join serial [
hive-file codegate2011\system.bak
| search key == "*USB\\VID_*" | eval serial = lower(valueof(split(key, "\\"), 5))
| stats max(last_written) as last_connect by serial
]
| join serial [
hive-file codegate2011\software.bak
| search key == "*Windows Portable Devices*" and name == "FriendlyName"
| rex field=key "&REV_[^#]+#(?<serial>[^&]+)"
| eval serial = lower(serial)
| stats first(value) as volume_name by serial
]
| search last_connect >= date("2011-02-21", "yyyy-MM-dd") and last_connect <= date("2011-02-22", "yyyy-MM-dd")
| order volume_name, vendor, product, version, serial, last_connect
이처럼 로그프레소 쿼리를 이용하여 레지스트리 포렌식 데이터를 손쉽게 분석하고 가공할 수 있으며, 재사용 가능한 라이브러리로 구축할 수 있습니다.