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 

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 加密格式初始化

cryptsetup luksFormat /dev/sda2

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

cryptsetup luksFormat /dev/vgssd/user1

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

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)指令如下 :

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

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

#cryptsetup luksDump /dev/sda2 LUKS header information for /dev/sda2
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
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

</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

</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

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再大一點)

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 ,並且還有加密區大小..

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就可.

 

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

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

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 來解密

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
/* 2024/5/10 */