4ensiX

4ensiX

Forensics専門でなければ、CTFはDFIRの勉強にほとんど役立たないことをを知ったこの頃

The Sleuth Kitの使い方に触れながら Determine Window Version!

一個前の記事で、
Windowsのバージョン情報どこじゃい - 4ensiX
こんな話をした。 ていうことは、レジストリの任意のkeyを参照できればoffline imagefileのWindows Versionを判定できると思ったのでチャレンジする。 コンシュマー系のWindowsのみやってみる。

今回の流れ

  1. イメージファイルを用意する。これは、構成された完全なrawイメージを使う。
  2. The Sleuth Kitで目的のレジストリハイヴファイルを取り出す。
  3. keyをhivexであったり、何らかの方法で持ってくる。

これでWindows Versionの判定を行う。

レジストリを取り出すWindowsイメージの用意

始めに、バージョン判定するためのwindowsイメージを用意する。ここで使うイメージは、isoではなく、インストールされて構成されたイメージである必要がある。
方針としては、検証したいWindows Versionのisoイメージを拾ってきて、ここから仮想マシンを立てる。この仮想マシンのイメージをrawイメージにして解析する。そんなメンドクサイことなんでやるのって、それは構成されたrawイメージが欲しいからなのだけど、良い方法があればご教授願いたい。
今回はこちらのサイトからイメージをお借りする
GetMyOS

f:id:Zarat:20200130223452p:plain
GetMyOSのホーム
ここにはMac以外なら沢山あるので、色んな環境で検証したいときはおススメ。ただ、WindowsはEnterpriseじゃないとプロダクトキーが必要で使えないので注意。
ダウンロードして見たところ、WinXPくらいまでは、何事もなくVMが動かせた。

f:id:Zarat:20200130224825p:plain
Virtualboxでやってみた
しかし、Windows2000は、Enterpriseが見つからないし、WinMe以前は上手く動かない。しかしisoイメージがなくとも、そもそも仮想イメージを欲しているので仮想イメージがあればいいじゃない。 Windowsのバージョン情報どこじゃい - 4ensiX
Win95,Meは前回の終わりの方で示したVDIを利用する。


Windows Me

Windows Me VDI File for VirtualBox : Microsoft : Free Download, Borrow, and Streaming : Internet Archive

Windows98

X-MAS-CTF/A Trip To Grandmas House.md at master · RealAwesomeness/X-MAS-CTF · GitHub

Windows95

Windows 95 VDI File for VirtualBox : Microsoft : Free Download, Borrow, and Streaming : Internet Archive


ここらで、先ほど揃えたVMの仮想イメージと拾ってきたvdiをrawに変えてから中身みる。イメージの変換はこちらを参照ドぞ。
仮想イメージからファイルを取り出す エラーを吐いたVMからファイルを救え - 4ensiX

自分はこれぐらいの容量で用意した

f:id:Zarat:20200131224502p:plain
windows仮想マシンをできるだけ最小構成にしたあとrawイメージに変換した(ハズ)

The Sleuth Kitのお話

The Sleuth Kitはディスクイメージ解析といえば、AutopsyとFTK Imagerを聞くと思うのですが、そのうちのAutopsyのCLI版という感じで、Cライブラリで動くイメージファイルを扱うツールセット。コマンドは、

blkcalc - Converts between unallocated disk unit numbers and regular disk unit numbers.
blkcat - Display the contents of file system data unit in a disk image.
blkls - List or output file system data units.
blkstat - Display details of a file system data unit (i.e. block or sector).
fcat - Output the contents of a file based on its name.
ffind - Finds the name of the file or directory using a given inode.
fiwalk - print the filesystem statistics and exit.
fls - List file and directory names in a disk image.
fsstat - Display general details of a file system.
hfind - Lookup a hash value in a hash database.
icat - Output the contents of a file based on its inode number.
ifind - Find the meta-data structure that has allocated a given disk unit or file name.
ils - List inode information.
img_cat - Output contents of an image file.
img_stat - Display details of an image file.
istat - Display details of a meta-data structure (i.e. inode).
jcat - Show the contents of a block in the file system journal.
jls - List the contents of a file system journal.
jpeg_extract - jpeg extractor.
mactime - Create an ASCII time line of file activity.
mmcat - Output the contents of a partition to stdout.
mmls - Display the partition layout of a volume system (partition tables).
(以下略)

とまあこんな感じで、ココに色々あるのだが、今回はimg_stat,mmls,fsstat,fls,icatの5つでお送りいたす。

レジストリハイヴを取り出す前準備

基本方針
  1. ファイル形式を判定
    img_stat -t [image file]
  2. パーティションレイアウトを確認
    mmls [image file]
  3. ファイルシステムを確認
    fsstat -i [file type] -o [partition start] -t [image file]
  4. 上記の情報で展開できるか確認
    fsstat -i [file type] -f [file system] -o [partition start] [image file]
  5. レジストリハイヴのinode番号を捜索
    fls -i [file type] -f [file system] -o [partition start] [image file] [inode number]#繰り返す
  6. レジストリハイヴを取り出す
    icat -i [file type] -f [file system] -o [partition start] [image file] [inode number] > [registry]

という手順で進める。しかし、そう簡単にはいかない。1995から2019年のwindowsの変遷を一気に辿ろうものなら、それなりに各々に違いはあるのである。

バージョンごとイメージの違いを確認

用意したイメージはWindows 95からWindows 10から1バージョンずつである。これらはいくつかの点でバージョンによって違いがある。

バージョンごとのファイルシステム

まず、これらはファイルシステムを大まかに3種類に分けられる。

バージョンごとのパーティション情報の違い

次に、バージョンごとのパーティション情報の違いがある。 細かい説明がメンドクサイので、以下を見てもらう。

# mmls win95.raw
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000000062   0000000063   Unallocated
002:  000:000   0000000063   0004193279   0004193217   DOS FAT16 (0x06)
003:  -------   0004193280   0004194303   0000001024   Unallocated
# mmls win98.raw
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000000062   0000000063   Unallocated
002:  000:000   0000000063   0010474379   0010474317   Win95 FAT32 (0x0b)
003:  -------   0010474380   0010485759   0000011380   Unallocated
# mmls winMe.raw
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000000062   0000000063   Unallocated
002:  000:000   0000000063   0008385929   0008385867   Win95 FAT32 (0x0b)
003:  -------   0008385930   0008388607   0000002678   Unallocated

Windows 95, 98, Meまでは、FAT16FAT32の違いはあるが、どれもパーティションが63から始まっている。

# mmls winXPsimple.raw
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000000062   0000000063   Unallocated
002:  000:000   0000000063   0005233535   0005233473   NTFS / exFAT (0x07)
003:  -------   0005233536   0005242879   0000009344   Unallocated
# mmls winVista.raw
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  000:000   0000002048   0031455231   0031453184   NTFS / exFAT (0x07)
003:  -------   0031455232   0031457279   0000002048   Unallocated

Windows XPWindows Vistaを比べると、どちらもNTFSであるがパーティションの始まりが63と2048と異なる。

# mmls win7simple.raw
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  000:000   0000002048   0000206847   0000204800   NTFS / exFAT (0x07)
003:  000:001   0000206848   0025163775   0024956928   NTFS / exFAT (0x07)
004:  -------   0025163776   0025165823   0000002048   Unallocated
# mmls win8simple.raw
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  000:000   0000002048   0000718847   0000716800   NTFS / exFAT (0x07)
003:  000:001   0000718848   0025163775   0024444928   NTFS / exFAT (0x07)
004:  -------   0025163776   0025165823   0000002048   Unallocated
# mmls win10simple.raw
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  000:000   0000002048   0001187839   0001185792   NTFS / exFAT (0x07)
003:  000:001   0001187840   0025163775   0023975936   NTFS / exFAT (0x07)
004:  -------   0025163776   0025165823   0000002048   Unallocated

Windows 7以降では、全てNTFSであるがパーティション領域が2つある。これは、Windows 10 Drive Partitionsによると、1つ目はリカバリパーティションらしい。OSの問題をトラブルシューティングするための回復環境を置いておく領域のようだ。セーフモードとか選ぶ青い画面はここに別パーティションで保持されているということではないだろうか。我々が解析するパーティションは2つ目のパーティションである。

目的レジストリハイブパスの違い

ファイルシステムの変遷に伴い、レジストリハイヴにも変更があったようで、バージョンを参照できる目的レジストリハイブパスは3種類ある。

レジストリハイヴをdump! バージョン判定までいっちゃうゼ

ここでは、Windows 98Windows 10の2つのバージョンからレジストリハイヴを取り出し、バージョン判定までを紹介する。レジストリハイブからkeyの読み込みはhivexを使うがWindowsMe以前のレジストリハイブには対応していない?ようなのでstrings | grepする。

Windows 98からレジストリハイブを取り出してバージョン判定

基本方針に沿っていく。

1.ファイル形式を判定

# img_stat -t win98.raw
raw

2.パーティションレイアウトを確認

# mmls win98.raw
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000000062   0000000063   Unallocated
002:  000:000   0000000063   0010474379   0010474317   Win95 FAT32 (0x0b)
003:  -------   0010474380   0010485759   0000011380   Unallocated

3.ファイルシステムを確認

# fsstat -i raw -o 63 -t win98.raw
fat32

4.上記の情報で展開できるか確認

# fsstat -i raw -f fat32 -o 63 win98.raw
FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: FAT32

OEM Name: MSWIN4.1
Volume ID: 0x187b08f7
Volume Label (Boot Sector): NO NAME 
Volume Label (Root Directory):
File System Type Label: FAT32 
Next Free Sector (FS Info): 20470
Free Sector Count (FS Info): 9003760

Sectors before file system: 63

File System Layout (in sectors)
Total Range: 0 - 10474316
* Reserved: 0 - 31
** Boot Sector: 0
** FS Info Sector: 1
** Backup Boot Sector: 6
* FAT 0: 32 - 10250
* FAT 1: 10251 - 20469
* Data Area: 20470 - 10474316
** Cluster Area: 20470 - 10474309
*** Root Directory: 20470 - 20477
** Non-clustered: 10474310 - 10474316

METADATA INFORMATION
--------------------------------------------
Range: 2 - 167261558
Root Directory: 2

CONTENT INFORMATION
--------------------------------------------
Sector Size: 512
Cluster Size: 4096
Total Cluster Range: 2 - 1306731

FAT CONTENTS (in sectors)
--------------------------------------------
20470-20477 (8) -> EOF
20478-20485 (8) -> EOF
20486-20493 (8) -> EOF
20494-20541 (48) -> EOF
20542-20581 (40) -> EOF
20582-20629 (48) -> 37646
20630-21261 (632) -> 21638
(以下略)

5.レジストリハイヴのinode番号を捜索

# fls -i raw -f fat32 -o 63 win98.raw
r/r 3:    BOOTLOG.TXT
r/r 4:    COMMAND.COM
r/r 5:    SUHDLOG.DAT
r/r 6:    FRUNLOG.TXT
r/r 7:    MSDOS.---
r/r 8:    SETUPLOG.TXT
d/d 9:    WINDOWS
r/r 10:    NETLOG.TXT
r/r 11:    MSDOS.SYS
r/r 12:    VIDEOROM.BIN
r/r 13:    DETLOG.TXT
r/r 14:    CONFIG.SYS
r/r 15:    BOOTLOG.PRV
r/r 16:    AUTOEXEC.BAT
r/r 17:    SYSTEM.1ST
r/r 18:    IO.SYS
d/d 20:    My Documents
d/d 22:    Program Files
r/r 24:    io32.sys
d/d 25:    RECYCLED
r/r 26:    SCANDISK.LOG
r/r * 27:    _UTMP0.19
r/r 29:    autoexec.sdd
v/v 167261555:    $MBR
v/v 167261556:    $FAT1
v/v 167261557:    $FAT2
V/V 167261558:    $OrphanFiles
# fls -i raw -f fat32 -o 63 win98.raw | grep WINDOWS 
d/d 9:    WINDOWS
# fls -i raw -f fat32 -o 63 win98.raw 9 | grep SYSTEM.DAT
r/r 494448:    SYSTEM.DAT

6.レジストリハイヴを取り出す

# icat -i raw -f fat32 -o 63 win98.raw 494448 > SYSTEM.DAT

7.レジストリハイブからkeyを持ってくる。

strings SYSTEM.DAT | egrep 'ProductName' | uniq | sed -e 's/ProductName//g' | grep "Microsoft Windows"
Microsoft Windows 98

Windows Version Determine!

Windows 10からレジストリハイブを取り出してバージョン判定(簡略化)

必要な部分だけ説明を加える。

# img_stat -t win10simple.raw
raw
# mmls win10simple.raw
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  000:000   0000002048   0001187839   0001185792   NTFS / exFAT (0x07)
003:  000:001   0001187840   0025163775   0023975936   NTFS / exFAT (0x07)
004:  -------   0025163776   0025165823   0000002048   Unallocated

先ほど示した通り、参照するパーティションは2つ目の方。

# fsstat -i raw -o 1187840 -t win10simple.raw
ntfs
# fsstat -i raw -f ntfs -o 1187840 win10simple.raw
FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: NTFS
Volume Serial Number: DAE616D3E616B02D
OEM Name: NTFS 
Version: Windows XP

METADATA INFORMATION
--------------------------------------------
First Cluster of MFT: 786432
First Cluster of MFT Mirror: 2
Size of MFT Entries: 1024 bytes
Size of Index Records: 4096 bytes
Range: 0 - 93440
Root Directory: 5

CONTENT INFORMATION
--------------------------------------------
Sector Size: 512
Cluster Size: 4096
Total Cluster Range: 0 - 2996990
Total Sector Range: 0 - 23975934

$AttrDef Attribute Values:
$STANDARD_INFORMATION (16)   Size: 48-72   Flags: Resident
$ATTRIBUTE_LIST (32)   Size: No Limit   Flags: Non-resident
$FILE_NAME (48)   Size: 68-578   Flags: Resident,Index
$OBJECT_ID (64)   Size: 0-256   Flags: Resident
$SECURITY_DESCRIPTOR (80)   Size: No Limit   Flags: Non-resident
$VOLUME_NAME (96)   Size: 2-256   Flags: Resident
$VOLUME_INFORMATION (112)   Size: 12-12   Flags: Resident
$DATA (128)   Size: No Limit   Flags:
$INDEX_ROOT (144)   Size: No Limit   Flags: Resident
$INDEX_ALLOCATION (160)   Size: No Limit   Flags: Non-resident
$BITMAP (176)   Size: No Limit   Flags: Non-resident
$REPARSE_POINT (192)   Size: 0-16384   Flags: Non-resident
$EA_INFORMATION (208)   Size: 8-8   Flags: Resident
$EA (224)   Size: 0-65536   Flags:
$LOGGED_UTILITY_STREAM (256)   Size: 0-65536   Flags: Non-resident

何故かWindows XP判定。

# fls -i raw -f ntfs -o 1187840 win10simple.raw | grep Windows
d/d 1731-144-5:    Windows
# fls -i raw -f ntfs -o 1187840 win10simple.raw 1731 | grep System32
d/d 3757-144-7:    System32
# fls -i raw -f ntfs -o 1187840 win10simple.raw 3757 | grep config
d/d 3786-144-7:    config
r/r 40736-128-5:    configmanager2.dll
r/r 41741-128-4:    ipconfig.exe
r/r 42325-128-4:    mmc.exe.config
r/r 42403-128-4:    msconfig.exe
r/r 43649-128-5:    tetheringconfigsp.dll
r/r 43750-128-1:    UevAppMonitor.exe.config
r/r 44447-128-5:    wpr.config.xml
r/r 44488-128-5:    wsmanconfig_schema.xml
# fls -i raw -f ntfs -o 1187840 win10simple.raw 3786 | grep SOFTWARE
r/r 44905-128-5:    SOFTWARE.LOG1
r/r 44863-128-5:    SOFTWARE.LOG2
r/r 49608-128-4:    SOFTWARE
# icat -i raw -f ntfs -o 1187840 win10simple.raw 49608 > SOFTWARE

hivexの登場。

# hivexget SOFTWARE 'Microsoft\Windows NT\CurrentVersion' | egrep 'ProductName|ReleaseId|"ProductId'
"ProductName"="Windows 10 Enterprise Evaluation"
"ReleaseId"="1909"
"ProductId"="*****-*****-*****-*****"
#一応伏せとく

Windows Version Determine!

終わりに

また少しWindowsと仲良くなれました。
ついでに今回の一連の流れをシェルスクリプトにしてみました。初めて書いたシェルスクリプトであり、プログラミングも得意な方ではないので汚いかもしれませんがご査証ください。

# !/bin/sh
# oschecker.sh

# argument check
if [ $# -ne 1 ]; then
    echo "Invalid argument"
    exit 1
fi

if [ "$1" = "-h" ]; then
    echo "Usage: oschecker.sh [-h] [imagefile]"
    echo ""
    echo "-h     print description"
    echo ""
    echo "This script can determine Windows Version (above windows 95) from offline disk image!"
    echo "However, only the raw format is supported."
    echo "In addition, tools must be pre-instaled."
    echo ""
    echo 'Tools:'
    echo "  The Sleuth Kit"
    echo "       (https://www.sleuthkit.org/sleuthkit/)"
    echo ""
    echo "  hivex "
    echo "       ex) sudo apt install libhivex-bin"
    echo ""
    echo ""
    echo "Have a nice DFIR!"
    exit 0 
fi

if [ -f "$1" -a -r "$1" ]; then
    echo "$1"
else
    echo "File Error"
    exit 1
fi

# image open setting
ver="ntfs"

img_type=`img_stat -t "$1"`

partition_s=`mmls "$1" | grep -i FAT`

tmp=`echo "$partition_s" | grep NTFS`
if [ -z "$tmp" ]; then
    ver="fat"
fi

if [ `echo "$partition_s" | grep -c ''` = 2 ]; then
    partition_s=`echo "$partition_s" | grep "003:"`
fi

# image open
str="$partition_s"
ary=(`echo $str`)   # store array

partition_addr=`echo ${ary[2]}`
analyze_s=`fsstat -i "$img_type" -f "$ver" -o "$partition_addr" "$1"`

if [ -z "$analyze_s" ]; then
    echo "file open Error\n please restart"
    exit 1
fi

# search inode number of registry hive
if [ "$ver" = "fat"  ]; then
    tmp=`fls -i "$img_type" -f "$ver" -o "$partition_addr" "$1" | grep -i WINDOWS`
    str="$tmp"
    ary=(`echo $str`)
    target_inode=`echo ${ary[1]} | sed -e 's/://g' | sed -e 's/-.*//'`

    tmp=`fls -i "$img_type" -f "$ver" -o "$partition_addr" "$1" "$target_inode" | grep -i SYSTEM.DAT`
    str="$tmp"
    ary=(`echo $str`)
    target_inode=`echo ${ary[1]} | sed -e 's/://g' | sed -e 's/-.*//'`

    icat -i "$img_type" -f "$ver" -o "$partition_addr" "$1" "$target_inode" > SYSTEM.DAT
    tmp=`strings SYSTEM.DAT | egrep 'ProductName' | uniq | sed -e 's/ProductName//g' | grep "Microsoft Windows"`
    echo '"ProductName"="'"${tmp}"'"'
    rm SYSTEM.DAT

elif [ "$ver" = "ntfs"  ]; then
    tmp=`fls -i "$img_type" -f "$ver" -o "$partition_addr" "$1" | grep -i WINDOWS`
    str="$tmp"
    ary=(`echo $str`)
    target_inode=`echo ${ary[1]} | sed -e 's/://g' | sed -e 's/-.*//'`

    tmp=`fls -i "$img_type" -f "$ver" -o "$partition_addr" "$1" "$target_inode" | grep -i system32`
    str="$tmp"
    ary=(`echo $str`)
    target_inode=`echo ${ary[1]} | sed -e 's/://g' | sed -e 's/-.*//'`

    tmp=`fls -i "$img_type" -f "$ver" -o "$partition_addr" "$1" "$target_inode" | grep d/d | grep config`
    str="$tmp"
    ary=(`echo $str`)
    target_inode=`echo ${ary[1]} | sed -e 's/://g' | sed -e 's/-.*//'`

    tmp=`fls -i "$img_type" -f "$ver" -o "$partition_addr" "$1" "$target_inode" | grep -i software | grep -v LOG`
    str="$tmp"
    ary=(`echo $str`)
    target_inode=`echo ${ary[1]} | sed -e 's/://g' | sed -e 's/-.*//'`

    icat -i "$img_type" -f "$ver" -o "$partition_addr" "$1" "$target_inode" > software
    hivexget software 'Microsoft\Windows NT\CurrentVersion' | egrep 'ProductName|CSDVersion|ReleaseId|"ProductId'
    rm software

else
    echo "now making"
fi

こんな感じで使えます。

# ./oschecker.sh win95.raw
win95.raw
"ProductName"="Microsoft Windows 95"
# ./oschecker.sh win98.raw
win98.raw
"ProductName"="Microsoft Windows 98"
# ./oschecker.sh winMe.raw
winMe.raw
"ProductName"="Microsoft Windows ME"
# ./oschecker.sh winXPsimple.raw
winXPsimple.raw
"ProductName"="Microsoft Windows XP"
"CSDVersion"="Service Pack 3"
"ProductId"="*****-***-*******-*****"
# ./oschecker.sh winVista.raw
winVista.raw
"ProductName"="Windows Vista (TM) Enterprise"
"ProductId"="*****-***-*******-*****"
"CSDVersion"="Service Pack 2"
# ./oschecker.sh win7simple.raw
win7simple.raw
"ProductName"="Windows 7 Enterprise"
"ProductId"="*****-***-*******-*****"
"CSDVersion"="Service Pack 1"
# ./oschecker.sh win8simple.raw
win8simple.raw
"ProductName"="Windows 8 Enterprise N"
"ProductId"="*****-*****-*****-*****"
# ./oschecker.sh win10simple.raw
win10simple.raw
"ProductName"="Windows 10 Enterprise Evaluation"
"ReleaseId"="1909"
"ProductId"="*****-*****-*****-*****"

変数名とかにツッコミどころ満載だし、なんで関数にしないのとかあると思いますがモチベが来たら直します。