Browser Forensic
디지털 포렌식에서 웹 브라우저 아티팩트 분석은 사용자가 특정 웹 브라우저에서 어떤 인터넷 행위를 했는지 확인할 수 있기 때문에 중요한 분석 과정으로 여겨진다.
본글은 Google의 Chrome 브라우저를 대상으로 History 삭제 정책을 확인하고, 이를 토대로 NTFS 파일 시스템에서의 History 복원 정책을 정의한다. 이후에 History 복원 정책을 토대로 '인터넷 사용 기록 삭제'가 적용된 환경에서 실제 아티팩트 분석을 진행해 정보 추출 가능 여부를 정의한다.
Scenario
본 실습은 Windows 11의 VM에서 진행하며, Chrome Browser의 실습 환경은 '검색어', '다운로드', '방문 URL' 기록이 존재할 수 있게 재현하고 이후 '인터넷 사용 기록 삭제'를 적용한다.
실습 환경에서의 시나리오 내용은 다음과 같고, 아래의 사진은 실습 환경 재현의 모습이다.
1. 'Best of the best 13기' 검색 후 BoB 사이트에 방문하여 pdf 파일을 다운로드한다.
2. 'Team H4C' 검색 후 H4C 사이트에 방문한다.
3. Chrome의 '인터넷 사용 기록 삭제' 기능을 이용해 인터넷 검색 및 방문 기록을 삭제한다.
History Deletion Policy
Windows의 NTFS 파일 시스템에서 Chrome 브라우저의 History 복원 과정을 정의하기 위해 먼저 History 삭제 정책을 정의해야 한다.
Chrome 브라우저의 History 데이터는 Journaling 모드는 'journal' 모드의 'TRUNCATE' 방식을 사용하는 SQLite에 저장되고, 삭제 방식은 'secure-delete' 방식을 사용한다.
History Recovery Flow Chart
본 문단의 삭제된 History 복원 과정은 '인터넷 사용 기록 삭제' 이벤트 이후의 이미지 파일을 기점으로 한다. Chrome Browser의 History 삭제 정책을 기반으로 NTFS 파일 시스템에서 효율적인 History 복원 흐름도를 정의한다.
Chrome Browser는 SQLite 파일에 History 데이터를 저장하고, SQLite의 'secure-delete' 모드로 History 데이터를 삭제한다. 이때, SQLite의 Journaling은 'jounal'의 'TRUNCATE' 방식을 이용하기 때문에 NTFS 파일 시스템 단위에서 'History-journal' 파일에 할당되었던 모든 RunList를 복원하여 삭제된 History 데이터를 복원할 수 있다. 흐름도의 각 순서 번호와 자세한 설명은 다음과 같다.
1. History 파일 추출 및 History-journal 파일 MFT number 확보.
- 이미지 파일을 FTK Imager에 업로드하여 History 파일 추출하고 SQLite 내부의 Table 데이터 존재 여부를 확인한다.
- 이미지 파일을 FTK Imager에 업로드하여 History-journal 파일의 MFT Number를 확보한다.
2. History-journal 파일의 MFT Entry 접근 및 History-journal 파일의 $DATA 속성 Log Record Format계산.
- 이전 단계에서 확보한 History-journal 파일의 MFT Number로 History-journal 파일의 MFT Entry에 접근한다.
- History-journal 파일의 $DATA 속성 영역 수정 시 $LogFile의 내부 Log Record Format에 기록되는 VCN, LCN, Attribute Offset, Attribute Length, Cluster Number, Page Size 값을 계산한다.
3. $LogFile에서 History-journal 파일에 할당되었던 모든 데이터 RunList 확보.
- 이전 단계에서 확보한 History-journal 파일의 Attribute Offset, Attribute Length, Cluster Number, Page Size, VCN, LCN 값을 해당 순서로 연접해 $LogFile에서 해당 Hex 값을 검색한다. 이후 검색된 Hex 값의 Log Record를 해석해 History-journal 파일의 $DATA 속성에 할당되었던 모든 Redo 및 Undo 데이터 즉, 모든 RunList를 확보한다.
4. History-journal 파일에 할당되었던 모든 Data Cluster 영역 추출.
- 이전 단계에서 확보한 History-journal 파일에 할당되었던 모든 RunList를 해석해, 디스크 이미지 파일에서 History-journal 파일에 할당되었던 모든 Data Cluster 영역을 추출한다.
5. '인터넷 사용 기록 삭제' 이벤트 이전의 데이터를 포함하는 SQLite 트랜잭션 백업 Page 확보 및 History 파일 복원.
- 이전 단계에서 추출한 History-journal 파일에 할당되었던 모든 Data Cluster 영역에서 '인터넷 사용 기록 삭제' 이벤트 이전의 유효한 데이터를 포함하는 SQLite 트랜잭션 백업 Page를 확보하고 History 파일에 복원한다.
History Recovery 1 (Normal Mode)
본 문단은 이전 문단에서 제시한 NTFS 파일 시스템에서의 효율적인 History 복원 흐름도를 기반으로, '인터넷 사용 기록 삭제' 이벤트가 발생한 History 파일의 복원을 진행한다.
1. History 파일 추출 및 History-journal 파일 MFT number 확보.
- 이미지 파일을 FTK Imager에 업로드하여 History 파일 추출하고 SQLite 내부의 Table 데이터 존재 여부를 확인한다.
아래는 FTK Imager로 History 파일을 추출한 모습이다.
추가적으로 History 파일의 중요한 데이터를 포함하는 일부 Table인 'downloads', 'keyword_search_terms', 'urls' Table을 확인한 모습으로, '인터넷 사용 기록 삭제' 이벤트가 발생해 해당 3개의 Table에서 유효한 데이터를 확인할 수 없는 모습이다.
- 이미지 파일을 FTK Imager에 업로드하여 History-journal 파일의 MFT Number를 확보한다.
아래는 FTK Imager로 History-journal 파일을 확인한 모습으로, MFT Number가 '124794'임을 알 수 있다.
2. History-journal 파일의 MFT Number로 MFT Entry 접근 및 History-journal 파일의 $DATA 속성 Log Record Format계산.
- 이전 단계에서 확보한 History-journal 파일의 MFT Number로 History-journal 파일의 MFT Entry에 접근한다.
History-journal 파일의 MFT Number가 '124794'이었으므로, $MFT의 '249588'(124794 x 2) 번 섹터로 이동하면 History-journal 파일의 MFT Entry에 접근할 수 있다. 아래는 History-journal 파일의 MFT Entry 모습이다.
- History-journal 파일의 $DATA 속성 영역 수정 시 $LogFile의 내부 Log Record Format에 기록되는 VCN, LCN, Attribute Offset, Attribute Length, Cluster Number, Page Size 값을 계산한다.
History-journal 파일의 MFT Number를 분석해 VCN, LCN, Cluster Number를 계산한다. VCN, LCN, Cluster Number는 각각 '$MFT에서의 해당 파일 MFT Entry Cluster 번호', '파티션에서의 해당 파일 MFT Entry Cluster 번호', 'Cluster 내부의 해당 파일의 MFT Entry 섹터 번호' 값을 의미한다. 때문에 VCN 값은 '0x79DE'(249588/8), LCN 값은 '0x0C79DE'(VCN + Cluster Number of $MFT), Cluster Number 값은 '0x04'(249588%8)가 된다.
History-journal 파일의 MFT Entry를 분석해 Attribute Offset, Attribute Length를 계산한다. Attribute Offset, Attribute Length는 각각 '데이터 수정이 일어나는 MFT Entry 내부의 Attribute Offset'과 '데이터 수정이 일어나는 Attribute 내부의 Offset' 값을 의미한다. 때문에 Attribute Offset 값은 '0x0180', Attribute Length 값은 '0x40'이 된다.
3. $LogFile에서 History-journal 파일에 할당되었던 모든 데이터 RunList 확보.
- 이전 단계에서 확보한 History-journal 파일의 Attribute Offset, Attribute Length, Cluster Number, Page Size, VCN, LCN 값을 해당 순서로 연접해 $LogFile에서 해당 Hex 값을 검색한다. 이후 검색된 Hex 값의 Log Record를 해석해 History-journal 파일의 $DATA 속성에 할당되었던 모든 Redo 및 Undo 데이터 즉, 모든 RunList를 확보한다.
이전 단계에서 확보한 VCN 값은 '0x79DE', LCN 값은 '0x0C79DE', Cluster Number 값은 '0x04', Attribute Offset 값은 '0x0180', Attribute Length 값은 '0x40'이 되고, Page Size 값은 항상 '0x02'로 고정된 값을 가지므로, 이를 Attribute Offset, Attribute Length, Cluster Number, Page Size, VCN, LCN 순으로 연접하면 '0x80 01 40 00 04 00 02 00 DE 79 00 00 00 00 00 00 DE 79 0C 00 00 00 00 00'이 된다. 해당 Hex 값을 NTFS의 $LogFile에 검색하고, 검색된 모든 영역의 Log Record의 Redo, Undo 값을 해석하여 History-journal 파일의 $DATA 속성에 할당되었던 모든 RunList를 추출한다.
아래는 NTFS의 $LogFile에서 10개의 Hex 값이 검색된 모습이다.
그중 하나의 Log Record 만을 수동으로 분석해 보았다. Redo 값(하늘색)은 '0x00 00 00 00 00 00 00 00'이고, Undo 값(연두색)은 '0x31 30 83 D5 23 00 00 00'이다.
4. History-journal 파일에 할당되었던 모든 Data Cluster 영역 추출.
- 이전 단계에서 확보한 History-journal 파일에 할당되었던 모든 RunList를 해석해, 디스크 이미지 파일에서 History-journal 파일에 할당되었던 모든 Data Cluster 영역을 추출한다.
필자는 3, 4 단계를 자동화해 주는 Python 코드를 작성해 분석을 진행하였다. 아래는 코드를 실행하는 모습으로, 이전 단계에서 수동으로 분석한 Log Record는 터미널에서 출력된 8번째 Log Record임을 확인할 수 있다.
추가로 해당 코드는 실행 시 인자로 입력한 디렉터리에 각 Log Record의 Data Cluster 영역을 추출해 Log Record의 번호 순을 이름으로 저장해 준다.
코드 실행 결과를 자세히 확인하면, Log Reord 2는 Log Reord 3의 부분 집합 영역, Log Reord 5는 Log Reord 6의 부분 집합 영역, Log Reord 7는 Log Reord 8의 부분 집합 영역, Log Reord 9와 Log Reord 10은 동일하다. 때문에 2, 5, 7, 10번째 Log Reord는 제외하고 다음 단계 분석을 진행하였다.
5. '인터넷 사용 기록 삭제' 이벤트 이전의 데이터를 포함하는 SQLite 트랜잭션 백업 Page 확보 및 History 파일 복원.
- 이전 단계에서 추출한 History-journal 파일에 할당되었던 모든 Data Cluster 영역에서 '인터넷 사용 기록 삭제' 이벤트 이전의 유효한 데이터를 포함하는 SQLite 트랜잭션 백업 Page를 확보하고 History 파일에 복원한다.
해당 분석 단계는 Python 코드가 수행할 수 없는 단계로, 추출된 파일의 Hex값을 일일이 분석해 '인터넷 사용 기록 삭제' 이벤트 이전의 유효한 백업 Page를 선별해야 한다. 본 실습에서는 'LogRecord_1', 'LogRecord_3' 파일에는 이미 다른 데이터가 덮여있었기 때문에, 'LogRecord_4', 'LogRecord_6', 'LogRecord_8', 'LogRecord_9' 파일만을 이용해 History 파일의 Page 복원을 진행한다.
본 실습은 History 내부의 여러 Table 중 중요한 데이터를 포함하는 일부 Table인 'downloads', 'keyword_search_terms', 'urls' Table 복원을 진행한다. 해당 3개의 Table은 첫 번째 단계에서 '인터넷 사용 기록 삭제' 이벤트 여부를 판별하기 위해 확인했던 Table로 이미 확실하게 삭제가 진행된 상태이다. 아래는 History 파일의 Hex 값을 확인해 'downloads', 'keyword_search_terms', 'urls' Table의 각 Root Page 번호를 확인하는 모습으로, 순서대로 Root page 번호는 '0x04', '0x0F', '0x0C' 값을 갖는다.
'LogRecord_4', 'LogRecord_6', 'LogRecord_8', 'LogRecord_9' 파일의 Hex 값을 일일이 분석해 'downloads', 'keyword_search_terms', 'urls' Table의 Leaf Page로 추측되는 Page를 선별한다. 이후 해당 Page를 복원하려는 Table의 Root Page에 덮어 History 복원을 진행한다.
아래는 'LogRecord_9' 파일에서 'downloads' Table의 Internal 혹은 Leaf Page로 추정되는 Page를 선별하고, History 파일의 Root Page 번호인 '0x04'번 ('0x03'번 섹터)에 덮어쓴 모습이다.
아래는 'LogRecord_6' 파일에서 'keyword_search_terms' Table의 Leaf Page로 추정되는 Page를 선별하고, History 파일의 Root Page 번호인 '0x0F'번 ('0x15'번 섹터)에 덮어쓴 모습이다.
아래는 'LogRecord_6' 파일에서 'urls' Table의 Leaf Page로 추정되는 Page를 선별하고, History 파일의 Root Page 번호인 '0x0C'번 ('0x12'번 섹터)에 덮어쓴 모습이다.
결과적으로 History 파일을 SQLite DB Browser로 열어 'downloads', 'keyword_search_terms', 'urls' Table을 확인한 모습으로, '인터넷 사용 기록 삭제' 이벤트가 발생하기 이전의 유효한 데이터를 복원한 모습이다.
Conclusion
결과적으로 NTFS 파일 시스템에서 Chrome 브라우저의 '인터넷 사용 기록 삭제'의 경우 'secure-delete'가 적용되어 History(SQLite) 파일 자체에서 삭제된 History 정보를 복원할 수 없고, History-journal 파일을 이용해 복원할 수 있다. 이 경우 $LogFile을 이용해 History-journal 파일에 할당되었던, RunList를 복원할 수 있으나, $LogFile의 사이클이 한 번 돌게 되면 RunList를 복원할 수 없게 된다. 이후에 복원한 RunList를 이용해 History-journal 파일에 할당되었던 Data Cluster Area를 추출해 '인터넷 사용 기록 삭제' 이벤트 이전의 유효한 데이터를 포함하는 Page를 복원할 수 있으나, SQLite의 journal 파일 특성상 트랜잭션이 정상적으로 완료되면 History-journal 파일에 할당되었던 디스크 영역이 비할당 영역으로 전환되기 때문에 해당 영역이 다른 데이터로 덮인다면 복원이 불가능하다.