Skip to main content

當你對NAS或Linux OS(怕被查水表)做了全扇區加密(FDE),其實這是用了Linux Unified Key Setup 簡稱LUKS
但做了這加密後,萬一系統出了狀況,硬碟損壞,需要資料救援,或忘記密碼等…又會是怎樣的狀況?
這篇將會做全面分析。

對於全扇區FDE架構建議先參考 OSSLab這篇文章

NAS陣列與分區架構

首先我們要瞭解現代NAS 陣列與分區與架構如圖:

(圖引用自 http://www.hivestream.de/tag/luks.html)

在一般NAS下,多顆HDD mdadm 組好的md ,再分區,前面通常會分割給NAS作業系統,會割最大塊的數據分區會使用LVM來分割
回顧一下LVM 
Physical Volume, PV, 實體捲軸
Volume Group, VG, 捲軸群組:將許多的 PV 整合成為一個捲軸群組 (VG)
Logical Volume, LV, 邏輯捲軸:最終將 VG 再切割出類似 partition 的 LV 即是可使用的裝置了
最大數據分區直接切PV ,PV 組VG 

1
pvcreate /dev/md1 Physical volume "/dev/md1" successfully created vgcreate vgssd /dev/md1 Volume group "vghdd" successfully created lvcreate -l 100%VG -n user1 vgsdd

對sda2 分區做LUKS 加密格式初始化

1
cryptsetup luksFormat /dev/sda2

對VG做LUKS 加密格式初始化,通常NAS加密Volume就是這樣格式化的
如果對整個MD加密,那當Raid參數丟失,因為沒有其他明文文件系統.推測Raid參數需要額外處理

1
cryptsetup luksFormat /dev/vgssd/user1

掛載LUKS 跟分割成ext4 檔案系統,當然要割成其他檔案系統如BTRFS也沒問題.

1
cryptsetup open /dev/vgssd/user1 vgssd-user1 mkfs.ext4 /dev/mapper/vgssd-user1

在全扇區加密下的檔案分區在已經格式化完畢完工。

NAS 全扇區加密對於資料救援會有何影響?

我們都知道全扇區加密,一定要有地方放metadata內有被加密的密鑰
LUKS 是將明文數據切割成若干 同樣大小Blocks。
使用同一個對稱密鑰塊和確定的算法對每個數據塊進行加解密
所以在LUKS metadata完整之下,資料恢復會是這樣的:
1.物理硬碟壞軌:如果有做Raid 1或5 或6,從其他顆正常扇區mirror 或XOR補齊.但如果小量整體,不會影響數據太大.
2.物理硬碟整顆損壞:如果有做Raid 1或5 或6,從其他顆正常扇區mirror 或XOR補.如果沒有則需要將單顆硬碟整顆修理好.
3.檔案刪除ext4:從日誌系統撈inode,或使用檔案碎片搜索法一樣可以正常運作.
4.分區掉失: 一樣可以資料恢復

如果有先組Raid,那Raid MD需要下面參數,整個MD資料才能正確無誤
1.走向方法
2.Stripe size
3.硬碟順序

所以假設Raid參數也丟失時候,就要利用文件系統來對Raid參數的逆向推測。
如果MD內留有未加密分區,則可以文件系統順利推導回去Raid參數。

如果做了全扇區加密,那該怎推敲Raid參數?
此時利用LUKS metadata ,來推算出Master Key。再對每個單顆硬碟做扇區解密轉換,才可求得明文文件系統
再利用文件系統特性參數來拼湊出Raid參數。(本文最後有寫推導方法與轉換指令)

假設是LUKS有問題狀況時候,該如何來做資料救援取得資料?

我們先來看整顆硬碟上LUKS架構

  • volume (or partition) header (208 byte)
  • 8 x key slots (48 byte*8)
  • key material(加密的split master key)

以上都為LUKS metadata(Header)

  • encrypted (volume) data:被加密的密文數據

第一定要先備份LUKS metdata(Header)指令如下 :

1
cryptsetup luksHeaderBackup <device> --header-backup-file <file>

查看LUKS  volume (or partition)  Header與Key slot

1
#cryptsetup luksDump /dev/sda2 LUKS header information for /dev/sda2
1
2
3
4
5
6
7
8
9
10
11
Version: 1
Cipher name: aes
Cipher mode: xts-plain64
Hash spec: sha256
Payload offset: 4096
MK bits: 256
MK digest: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
MK salt: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
MK iterations: 371000
UUID: 28c39f66-dcc3-4488-bd54-11ba239f7e68
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Key Slot 0: ENABLED
Iterations: 2968115
Salt: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
Key material offset: 8
AF stripes: 4000
Key Slot 1: ENABLED
Iterations: 2968115
Salt: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
Key material offset: 264
AF stripes: 4000
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED
<div>

LUKS volume (or partition) header +key slot 為 592 bytes 結構如下,建議上下核對 以下為big eddian

Offset Size(byte) Value Description
0 6 “LUKS\xba\xbe” 簽名  ASCII 顯示為 “LUKS..”
6 2 1 版本
8 32   加密方法 (Cipher name) etc:AES
Contains an ASCII string with an end-of-string character
40 32   加密模式 (Cipher mode) etc:xts-plain64
Contains an ASCII string with an end-of-string character
72 32   哈希模式 example:sha256
用於用戶密鑰計算和抗碎片分析(AF)擴散的散列方法
Contains an ASCII string with an end-of-string character
104 4   加密卷開始扇區 (Payload offset)
108 4   Master Key大小 (注意MK bits顯示為bit 通常為128,256,512) 
Contains the size of the key in bytes
112 20   Master Key digiest
132 32   Master key salt
164 4   Master key 迭代次數(MK iteration)
168 40   卷標識符(UUID)
包含帶有字符串結尾字符的ASCII字符串,該字符串由小寫的UUID組成
208 8 x 48   Key slots 48 bytes總共有8個

 

Key slot

Key slot is 48 bytes 內容如下

Offset Size(byte) Value Description
0 4   狀態 (of key slot)
0x0000dead ⇒ inactive (dead)
0x00ac71f3 ⇒ active
4 4   Key material 迭代次數 (Iterations)
8 32   Key material salt
40 4   Key material 開始扇區 (encrypted SMK開始位置)
44 4   每個 (anti-forensic) stripes有多少個Key material
(AF stripes)

主密鑰Master Key:(MK)這是最關鍵的密鑰,如果記憶體中撈的出來你也不用需要User 密碼跟LUKS Header),直接就可以解開加密的數據
                                        MK大小值取決於卷標頭中MK bits 通常是128或256或512 bit。

要特別注意的!這跟WD全扇區硬體加密硬碟一樣,當變更用戶密碼時候,MK主密鑰不會改變,變動的是Key material(encrypted SMK)

LUKS 加密架構

下面為 LUKS 初始加密時寫入Header  Pseudo code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
</span>masterKey = must be available, either because it is still in
 
memory from initialisation or because it has been
 
recovered by a correct password
 
masterKeyLength = phdr.key-bytes
 
emptyKeySlotindex = find inactive key slot indexin phdr by
 
scanning the keyslot.active field for
 
LUKS_KEY_DISABLED.
 
keyslot ks = phdr.keyslots[emptyKeySlotindex]
 
PBKDF2-IterationsPerSecond = benchmark system
 
ks.iteration-count = PBKDF2-IterationsPerSecond
 
intendedPassword Checking Time (in seconds)
 
ks. salt = generate random vector, length:LUKS_SSALTSIZE
 
splitKey = AFsplit(masterKey,
 
source
 
masterKey Length,
 
source length
 
ks.stripes)
 
// number of stripes
 
splitKeyLength = masterKeyLength * ks.stripes
 
pwd = read password from user input
 
pwd-PBKDF2ed = PBKDF2(password,
 
ks.salt,
 
ks.iteration-count
 
masterKeyLength)//
 
key size is the same
 
as for the bulk data
 
encryptedKey = encrypt(phdr.cipher-name,
 
phdr.cipher-mode,
 
pwd-PBKDF2ed,
 
splitKey,
 
cipher name
 
cipher mode
 
key
 
content
 
splitKey Length)
 
content length
 
//
 
write to partition (encrypted Key,
 
ks.key-material-offset //
 
splitKey Length)
 
source
 
sector
 
number
 
// length in bytes
 
ks.active = LUKS_KEY_ACTIVE
 
// mark key as active in phdr
 
update keyslot ks in phdr
 
<span class="text-only" data-eleid="3">

下面為LUKS metadata(Header+Key material)+用戶密碼的 ,完成mk驗證的Pseudo code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
</span>read phdr from disk
 
check for correct LUKS_MAGIC and compatible version number
 
masterKeyLength = phdr.key-bytes
 
pwd = read password from user input
 
foreachactive keyslot in phdr do as ks {
 
pwd-PBKDF2ed = PBKDF2(pwd, ks.salt, ks.iteration-count
 
read from partition (encrypted Key,
 
masterKey Length)
 
destination
 
// sector number
 
ks.key-material-offset,
 
masterKeyLength * ks.stripes) // number of bytes
 
splitKey = decrypt(phdr.cipherSpec, // cipher spec.
 
pwd-PBKDF2ed,
 
encrypted Key,
 
encrypted)
 
// key
 
content
 
content length
 
masterKeyCandidate = AFmerge(splitKey, masterkeyLength
 
ks.stripes)
 
MKCandidate-PBKDF2ed = PBKDF2( master Key Candidate,
 
phdr.mk-digest-salt,
 
phdr.mk-digest-iter,
 
LUKS_DIGEST_SIZE)
 
ifequal(MKCandidate-PBKDF2ed, phdr.mk-digest){
 
break loop and return masterKeyCandidate as
 
correct master key
 
}
 
error, password does not match any keyslot
 
<span class="text-only" data-eleid="3">

忘了用戶密碼怎辦?

一、由於LUKS沒有限制輸入密碼次數因此可以寫script嘗試爆破
下面參考自 https://nmattia.com/posts/2017-03-05-crack-luks-stutter-gnu-parallel.html

1
2
3
4
5
6
7
8
9
crack_maybe=$(cat &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;'EOF'
    echo PASS | cryptsetup open --test-passphrase ./encrypted-file
    rc=$?
    if [ "$rc" -ne "2" ]; then
        echo "return code $rc on input PASS"
        exit 255
    fi
EOF
)

我們將過程存儲在一些shell變量中,以便我們可以將它傳遞給xargs,例如:
stutter將潛在的密碼短語提供給xargs,crack_maybe在PASS用潛在密碼短語替換所有出現的密碼後調用。
如果cryptsetup返回任何其他內容2,我們退出exit 255,這基本上是告訴xargs停止的唯一方法(否則程式會繼續跑)  

二、 bruteforce-luks (GitHub)是用調用cryptsetup API爆破, 速度有好一點

不過上面二個方法當然不夠有效率
來看看hastcat爆破LUKS的思路…..
來重看一下LUKS 解密架構

 

第一次PBKDF2 跟AF轉換後就有有主密鑰,就可以開始嘗試做解密攻擊.
而不需要如正統流程再跑一次PBKDF2,這非常耗時間..
hashcat發現LUKS加密的過的數據會像是隨機數據
因此對數據區做熵檢查,如果熵低於某個閾值,可以假設密碼是正確的
要鏡像超過2MB超過LUKS metadata,就是因為需要包含數據區。

這思路很酷!以資料救援設備MRT來講,爆破FDE密鑰是利用驗證第0扇區 末端magic number為55AA
Hastcat開發者ATOM非常自豪他的LUKS破解思路,他認為比起市面上商業軟體如Passware都快了20倍以上!

Hashcat破解LUKS指令

首先要取得LUKS Header 2.1MB (是的 要比LUKS metadata再大一點)

1
2
dd if = / dev / XXXX of = header.luks bs = 512 count = 4097
./hashcat-3.5.0/hashcat64.bin -m 14600 -a 0 -w 3 header.luks Dictionary.txt -o luks_password.txt

-m = hash method – 14600 for LUK encryption
-a = Crack method – 0 for standard dictionary (3 for bruteforce)
-w = resource allocation – 3 for high
header.luks = 加密分區檔頭
Dictionary.txt = dictionary
-o = output luks_password.txt 

破解成功後,就可從luks_password.txt 的到用戶密碼。
爆破LUKS其實很困難,配上1070顯卡也才3000 Hash/s 上下
因此一般狀況下不必擔心LUKS 用戶密碼被破解

如果電腦沒有關機下, LUKS 還有其他破解方式:

1.假設是加密方法是AES,那可做記憶體鑑識 ,將記憶體DUMP ,使用findaes 找出在記憶體中的MK ,由於取得是MK就直接可以用了.

你要知道Ciper mode ,並且還有加密區大小..

1
echo "0 &lt;size&gt; crypt aes-xts-plain64 &lt;key&gt; 0 &lt;/dev/drive&gt; 4096" | sudo dmsetup create luks-volume

比較有趣的是,這個方法在Mount時候就可以忽略到LUKS header..

那當LUKS metadata(Header+ encrypted SMK)如果遭遇硬碟扇區損壞跟惡意擦除
(通常是上門查水表,駭客立刻消滅密鑰,讓警方無法做數位鑑識) 這會是怎樣狀況?

先來看看破壞方法,比如sdX1為LUKS分區 下DD擦除 前面2MB就可.

 

1
dd if=/dev/urandom of=/dev/sdX1 bs=512 count=20480

這樣還有機會做資料救援嗎?看一下主密鑰MK 是怎樣產生的

1
2
3
4
5
6
7
8
9
10
11
pwd−PBKDF2ed = PBKDF2(pwd,ks.salt,ks.iteration−count</pre>
<pre>                       masterKeyLength )</pre>
<pre>read from partition (encryptedKey, //destination</pre>
<pre>                     ks.key−material−offset, //sector number</pre>
<pre>                     masterKeyLength*ks.stripes) //number of bytes</pre>
<pre>splitKey =decrypt(phdr.cipherSpec, //cipher spec .</pre>
<pre>                  pwd−PBKDF2ed,//key</pre>
<pre>                  encryptedKey,//content</pre>
<pre>                  encrypted) //content length</pre>
<pre>masterKeyCandidate = AFmerge (splitKey,masterkeyLength ,</pre>
<pre>                              ks.stripes)

這會跟Hashcat不一樣 ,這時反而有User Key  (pwd) 
只求破解用masterKeyCandidate就可以做MK (沒做第二次PBKDF2驗證)所以只需要下面
以下這二個 是最必備的
1.encryptedKey:Key material ,encrypted SMK 以最上面為例,大小4000 x 256bit=125KBytes
2.ks.salt:Key slot 中的Key material salt大小32 byte以下為好推測的 同一款NAS 版本下應該是一樣
1.masterKeyLength 這好推測128 ,256,512bit
2.cipherSpec:cbc,ecb,xts
3.ks.iteration−count :Key material 迭代次數
4.ks.key−material−offset:Key slot中的Key material 開始扇區 (encrypted SMK開始位置)
5.ks.stripes:key slot Stripe 數.
6.encrypted :Material Key 大小=masterKeyLength *ks.stripes算出 MK 一樣檢查對數據區檢查熵,一定低於某個閾值.就應該為正確MK
求得MK後,一樣直接用crypt 來解密

1
echo "0 "size" crypt&nbsp;ciphername+cipherSpec key 0 "/dev/drive" 4096" | sudo dmsetup create luks-volum

前述中若NAS Raid參數丟失,而Raid MD中只有單一加密分區,LUKS metadata也完整時,也是用上面方法生成MK先
用上面指令做單顆硬碟解密,再根據解密文件系統分析推導出Raid參數。
看到這,確實當Key material (encrypted SMK),ks.salt(Key slot 中的Key material salt) 被擦除掉或是這部位硬碟扇區損壞。
也沒有從記憶體取中取得MK時,是無法解密資料的。

結論:

會寫這篇文其實是因為客戶遇到這樣狀況詢問我們:
一台6 Bay NAS做了加密,無法掛載資料,當初拿給原廠Support Team,但原廠也沒辦法作處理….
之後也不確定Raid組態跟LUKS Header是否還在
(這方面來講,原廠其實不該負責客戶資料,或是客戶應該要做全硬碟備份先,再給原廠處理。)
OSSLab之前會使用LUKS攻擊工具但不知道其原理
這次研究整合必須一次做到:
1.一般NAS架構上套件安裝流程才會知道架構.
2.加密程式原始碼與演算法架構
3.多種破解手法與破解程式經驗與思路最終編寫自己程式,才有機會做如此高難度的資安與資料恢復處理.


上面圖引用自與參考
https://github.com/libyal/libluksde
https://hashcat.net/forum/thread-6225.html
http://www.hivestream.de/tag/luks.html
http://netinfo-security.org/CN/abstract/abstract5917.shtml#
https://www.freebuf.com/articles/database/181010.html
https://blog.pnb.io/2018/02/bruteforcing-linux-full-disk-encryption.html
https://blog.appsecco.com/breaking-full-disk-encryption-from-a-memory-dump-5a868c4fc81e

 

Thx Chang

Author Thx Chang

More posts by Thx Chang
Menu
/* 2024/5/10 */