在这篇博文中,我们详细展示了*针对 XOR 编码的*已知明文攻击是如何进行的,并使用自定义工具自动执行该攻击,以解密和提取 Cobalt Strike 信标的配置。如果您对理论不感兴趣,只对工具感兴趣,请直接跳到结论!
已知明文攻击 (KPA) 是一种密码分析方法,分析人员拥有消息的明文和密文版本。攻击的目的是揭露加密算法和密钥。
XOR 编码简介
让我们首先就一个符号约定达成一致: 十进制整数的表示方式如下:
65
十六进制整数表示如下:
0x41
我们以以下明文消息为例:
IT security company NVISO was founded in 2013!
然后我们用密钥 ABC 对其进行异或编码。如下所示:
图 1:使用密钥 ABC 进行异或编码
它的工作原理如下。对每个字符,我们执行 8 位 XOR 运算。
-
我们取明文消息的第一个字符(I)和密钥的第一个字符(A),查找它们的数值(根据 ASCII 表):I 为 0x49,A 为 0x41。将 0x49 和 0x41 进行异或运算可得出 0x08。在ASCII 表中,0x08 是控制字符,因此不可打印(因此图 1 中的细矩形:这表示不可打印的字符)。 -
我们取明文消息的第二个字符(T)和密钥的第二个字符(B),查找它们的数值(根据 ASCII 表):T 为 0x54,B 为 0x42。将 0x54 和 0x42 进行异或运算可得出 0x16。在 ASCII 表中,0x16 是控制字符,因此不可打印。 -
我们取明文消息的第三个字符(空格字符)和密钥的第三个字符(C),查找它们的数值(根据 ASCII 表):是 0x20,而 C 是 0x43。将 0x20 和 0x43 进行异或运算得到 0x63。在 ASCII 表中,0x63 是小写字母 c(小写字母和大写字母的值不同:它们的第 6 位被切换)。 -
我们取明文消息的第四个字符(s)和密钥的第一个字符(A),查找它们的数值(根据 ASCII 表):s 为 0x73,A 为 0x41。将 0x73 和 0x41 进行异或运算可得出 0x32。在 ASCII 表中,0x32 是数字 2。由于我们的异或密钥(ABC)只有 3 个字符长,而明文长度超过 3 个字符,因此我们开始重复密钥。这就是为什么我们在使用密钥的最后一个字符(C)作为前一个字符后,再次从密钥的第一个字符(A)开始的原因。我们滚动密钥。
如此反复,直到处理完第 46 个字符:
-
字符 1:我们执行 I xor A 操作,得到不可打印的字符 -
字符 2:我们执行 T xor B 操作,得到不可打印的字符 -
字符 3:我们执行异或 C 运算,得到字符 c -
字符 4:我们执行异或 A 运算,得到字符 2 -
等等 …。 -
字符 46:我们执行操作 ! xor A,得到字符 `
此示例解释了如何使用比明文消息短的密钥对明文消息进行异或:明文异或密钥 -> 密文。
XOR密码分析
XOR 编码有一个有趣的特性,特别是如果你对密码分析感兴趣的话。当你将明文消息与密文进行 XOR 时,你将获得密钥:明文 XOR 密文 -> 密钥。
图 2:对明文和密文进行异或运算得到密钥流
这就是 XOR 编码的工作原理。现在让我们看看如何解码,无需知道密钥,也不需要知道完整的明文,只需知道明文的一部分即可。
在我们这里处理的情况下,我们有完整的密文和部分明文。在某些情况下(稍后解释),在这种情况下也可以恢复密钥。 假设我们拥有的部分明文是 NVISO:我们知道字符串 NVISO 出现在明文中(我们不知道),但我们不知道在哪里。 所以我们在这里要做的是重复字符串 NVISO 尽可能多的次数,使其与密文一样长(因此与明文一样长)。像这样:
NVISONVISONVISONVISONVISONVISONVISONVISONVISON
当我们对密文和重复的 NVISO 字符串执行异或运算时,我们得到以下结果:
图 3:对部分已知明文的密文进行异或运算
我们在这里看到的是一个由 3 个字符 (CAB) 组成的字符串,它开始重复自身 (CA),并且长度与部分明文完全相同:NVISO 为 5 个字符,CABCA 为 5 个字符。 这就是识别密钥的方法:寻找重复,即总长度与部分明文一样长。在我们的示例中,子字符串 CABCA 是 XOR 结果中唯一满足此条件的字符串。yy 和 bib 也是重复字符串,但它们比部分明文 (5 个字符) 短。 这也说明了部分 KPA 攻击 XOR 编码成功的最重要条件:部分明文必须比 XOR 密钥长。如果部分明文与 XOR 密钥一样长或更短,我们将不会观察到重复,因此我们将无法识别密钥。 包含我们的 XOR 密钥的字符串是 CABCA。由于我们假设我们正在处理滚动 XOR 密钥,因此密钥可以是 ABC、BCA 或 CAB。 让我们将包含 XOR 密钥的字符串向左和向右扩展,使其与密文一样长:
图 4:向左和向右扩展密钥流
最后,使用它作为 XOR 操作的密钥流:
图 5:将密文与扩展的密钥流进行异或运算
现在,我们已经恢复了完整的明文,因为我们知道了明文中比用于编码消息的 XOR 密钥更长的部分。 我们设计了示例,使密文和部分明文对齐,如下所示:
图 6:出于本示例的目的,明文和部分已知明文对齐
如果部分明文 NVISO 的密文前面的字符数不是明文 NVISO 长度的整数倍(20 / 4 = 5),则密文和部分明文将无法正确对齐,我们将无法计算出正确的密钥。 例如,像这样(我删除了开头的单词 IT):
图 7:更有可能的是,部分已知明文不会对齐
但这可以通过“滚动”部分明文来解决。如果我们的部分明文长度为 5 个字符,我们必须生成 5 个需要检查的部分明文流:
NVISO -> NVISONVISONVISONVISONVISONVISONVISONVISONVISON
VISON -> VISONVISONVISONVISONVISONVISONVISONVISONVISONV
ISONV -> ISONVISONVISONVISONVISONVISONVISONVISONVISONVI
SONVI -> SONVISONVISONVISONVISONVISONVISONVISONVISONVIS
ONVIS -> ONVISONVISONVISONVISONVISONVISONVISONVISONVISO
图 8:尝试第四个潜在密钥流
在这个例子中,我们生成的第四个流将会对齐,从而揭示密钥。
使用滚动 XOR 密钥对 XOR 编码密文进行部分 KPA 攻击,需要比 XOR 密钥更长的部分已知明文。长度差异越大,识别 XOR 密钥就越容易。
手动完成所有这些解码是一个非常繁琐的过程。这就是为什么我们有一个工具来自动化这个过程:xor-kpa.py。
让我们用例子来说明。 这是编码文件(密文):
图9:密文
这是包含已知明文的文件:
图10:部分已知明文
使用这两个文件作为输入运行工具 xor-kpa.py 得到以下结果:
图 11:xor-kpa.py 列出潜在密钥
我们看到该工具像我们一样恢复了密钥流 CABCA,并从中推断出正确的密钥:ABC。Extra 告诉我们有多少个字符重复(因此部分明文与 XOR 密钥相比有多少个额外的字符)。这个数字越大,我们越有信心恢复了正确的密钥。Divide 告诉我们完整的 XOR 密钥在密钥流中出现了多少次。 而 counts 告诉我们这个密文被找到了多少次(在我们的例子中,这意味着单词 NVISO 在明文中出现了不止一次)。xor -kpa.py 也可以为我们解码(使用选项 -d):
图 12:使用 xor-kpa.py 解码
下面是一个部分已知明文较长的例子(成立于 2013 年):
图 13:较长的部分已知明文
图 14:更长的密钥流
由于恢复的密钥流更长,提取正确密钥的概率更高。
结论
NVISO 经常遇到使用 XOR 编码且密钥长度超过一个字节的恶意软件或伪造品。工具 xor-kpa.py 可帮助我们解码此类文件。
该工具附带一些预定义的明文,这些明文经常出现在样本中(例如“此程序无法在 DOS 模式下运行”)。我们还包含了 Cobalt Strike 信标的明文。
当你使用帮助选项运行工具 xor-kpa.py 时,你会得到一个预定义的明文列表:
图 15:xor-kpa 的选项,包括预定义的已知明文
所有以 cs- 开头的预定义明文均适用于 Cobalt Strike。
作为最后一个例子,我们尝试解码一个疑似编码的 Cobalt Strike 信标 (beacon.vir) 的文件。我们将使用 cs-key-dot:这是存储在 Cobalt Strike 版本 4 的信标配置中的公钥的不变部分:
图 16:xor-kpa 显示潜在密钥
恢复了几个潜在密钥,最可能的密钥显示在输出的末尾。潜在密钥输出按密钥流中的重复次数排序:重复次数越多,密钥正确的可能性就越大,因此列在重复次数较少的密钥流的下方。
我们现在可以使用选项 -d 来解码具有最可能密钥(列表中的最后一个)的样本,并将输出通过管道传输到1768.py(一个用于提取信标配置的工具):
图 17:xor-kpa 解码信标并提取信标配置
由于使用 xor-kpa 解码,1768.py 能够提取正确的配置。
原文始发于微信公众号(红云谈安全):XOR 已知明文攻击
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论