本文记录一次使用 Wireshark 分析 RDP 证书的过程,这次遇到的证书比较特殊,是服务器专有证书(Server Proprietary Certificate),不能直接使用 X.509 格式的方式查看,当时为了分析这个证书花了不少时间。

下面记录一下分析过程,不想看过程可以直接跳到第三章节看快速判断方法

一、定位数据位置

1. 筛选数据包

首先筛选 RDP 协议并找到 ServerData Encryption

2. 定位数据

定位到 serverCertificate 字段,就是证书数据

Remote Desktop Protocol -> serverData -> serverSecurityData -> serverCertificate

3. 查看数据

右键 serverCertificate 那一行,选择 「显示分组字节」,以 「原始数据」 方式显示,看到的就是字段数据了。

二、提取关键信息

上一步得到的数据为:

01000000010000000100000006001c01525341310801000000080000ff00000001000100ed35ff44bcce04b388cce4d949a30712111fd4a82fdde4a26245e5758a713f8b1d3cfe8cfd4393730f7b296c18df1a25b0983fb6f864b956cf6559379635b47bbf69caa76947db43e5bc4ed39299d821b7283f29c7e85555088e17616faef0fcef7902b711acbe01fabf1dd945c5a8a4f24c019a348ad2ecaf67a0ffeacf65a142ee5fa1e11e48d5d8969df007c48daa82ad4eed59276a8673e0faf5932f636d0b1e45e03d7ea2697a438fda3126cdded07944e6cc97119d0931d01726f059fa42b1597c802112d5e753571bac85359d9cd0441d5d4ba3599057eefea251c2708d3375bdb25bea8d19166f007e7f061d86b43366f15de48b679d2e59f594cac00000000000000000080048006001ac441fd87ad6978c35a093a794cb94fd32f8bce3a4ebbf2b24b0f7323eb3ecb633e57c187c4609c3a7811a4731e326e7fafd64c386badcbe5bf28e9e45010000000000000000

参考微软官方文档:[MS-RDPBCGR]: Server Certificate (SERVER_CERTIFICATE) | Microsoft Learn

文档中给出了每组字节的含义:

内容如下表:

0~31
dwVersion
certData (variable)

文档解释:

dwVersion (4 bytes): A 32-bit, unsigned integer. The format of this field is described by the following bitmask diagram.

最开头的 4 个字节是 dwVersion 字段,之后的数据就是证书数据 certData

接着看,dwVersion 又分为两部分:

内容如下表:

0~3031
certChainVersiont

文档解释:

certChainVersion (31 bits): A 31-bit, unsigned integer that contains the certificate version.

t (1 bit): A 1-bit field that indicates whether the certificate contained in the certData field has been permanently or temporarily issued to the server.

前 31 个 bit 是证书链版本(certChainVersion),第 32 个 bit 代表有效期类型。

1. 判断证书链版本

再看证书链版本的说明:

Value (31 bits)Meaning
CERT_CHAIN_VERSION_1
0x00000001
The certificate contained in the certData field is a Server Proprietary Certificate (section 2.2.1.4.3.1.1).
CERT_CHAIN_VERSION_2
0x00000002
The certificate contained in the certData field is an X.509 Certificate (section 5.3.3.2).

certChainVersion 的值为 0x00000001 时,证书属于 「服务器专有证书」,格式内容可以参考文档:[MS-RDPBCGR]: Server Proprietary Certificate (PROPRIETARYSERVERCERTIFICATE) | Microsoft Learn

certChainVersion 的值为 0x00000002 时,证书就属于常见的 X.509 格式证书。

这里补充说明一下这个字节怎么读,涉及到一个叫 「字节序」 的知识点,可以自行百度了解一下,现在例子中就属于 「小端序」 。

取证书中前 4 个字节为例,可以看到证书数据前 8 个字符是:

[ 01000000 ]

一个 「字符」 代表一位十六进制数,也就是 4 位二进制。一个字节是 8 位二进制,那就是 2 个 「字符」 表示一个字节,所以每两个字符分为一组,就是 4 个字节:

[ 01 ; 00 ; 00 ; 00 ]

然后以字节位单位从右往左读,得到:

[ 00000001 ]

也就是上面所说的 0x00000001,那么表示证书属于 「服务器专有证书」 。

上面例子中零太多了,不直观,再举个例子,假设 Wireshark 中看到的数据是:

[ 12 ; 34 ; 56 ; 78 ]

那么实际的数据不是 87654321,而是:

[ 78563412 ]

2. 分析证书内容

确定了证书版本,下一步就是按照专有证书的格式分析内容。继续参考文档:[MS-RDPBCGR]: Server Proprietary Certificate (PROPRIETARYSERVERCERTIFICATE) | Microsoft Learn

字段的官方说明:

dwVersion (4 bytes): A 32-bit, unsigned integer. The certificate version number. This field MUST be set to CERT_CHAIN_VERSION_1 (0x00000001).

dwSigAlgId (4 bytes): A 32-bit, unsigned integer. The signature algorithm identifier. This field MUST be set to SIGNATURE_ALG_RSA (0x00000001).

dwKeyAlgId (4 bytes): A 32-bit, unsigned integer. The key algorithm identifier. This field MUST be set to KEY_EXCHANGE_ALG_RSA (0x00000001).

wPublicKeyBlobType (2 bytes): A 16-bit, unsigned integer. The type of data in the PublicKeyBlob field. This field MUST be set to BB_RSA_KEY_BLOB (0x0006).

wPublicKeyBlobLen (2 bytes): A 16-bit, unsigned integer. The size in bytes of the PublicKeyBlob field.

PublicKeyBlob (variable): Variable-length server public key bytes, formatted using the Rivest-Shamir-Adleman (RSA) Public Key structure (section 2.2.1.4.3.1.1.1). The length in bytes is given by the wPublicKeyBlobLen field.

wSignatureBlobType (2 bytes): A 16-bit, unsigned integer. The type of data in the SignatureBlob field. This field is set to BB_RSA_SIGNATURE_BLOB (0x0008).

wSignatureBlobLen (2 bytes): A 16-bit, unsigned integer. The size in bytes of the SignatureBlob field.

SignatureBlob (variable): Variable-length signature of the certificate created with the Terminal Services Signing Key (sections 5.3.3.1.1 and 5.3.3.1.2). The length in bytes is given by the wSignatureBlobLen field.

版本(4 字节):固定值 0x00000001

签名算法 ID(4 字节):固定值 0x00000001,代表 RSA 算法。

密钥交换算法(4 字节):固定值 0x00000001,代表 RSA 算法。

公钥类型(2 字节):固定值 0x0006,代表 RSA 类型的密钥。

公钥长度(2 字节):表示下边 「PublicKeyBlob」公钥值的长度有多长,单位是字节

公钥对象:根据上面的长度判断占用几个字节。

签名类型(2 字节):固定值 0x0008,代表 RSA 签名。

签名长度(2 字节):表示下边 「SignatureBlob签名值的长度有多长,单位是字节

签名对象:根据上面的长度判断占用几个字节。

算法已经定死了,所以直接看公钥长度,第 13 、 14 字节代表公钥长度,既第 25~28 个字符,或者直接找 0600 后面的 4 个字符:

[ 1c ; 01 ]

按小端序读法这个十六进制数就是:

[ 011c ]

转换成十进制就是 284,就是说公钥对象长度是 284 个字节,即之后 ( 284 times 2 = 568 ) 个字符是公钥对象,注意公钥对象不等于公钥,后面还要继续分析:

525341310801000000080000ff00000001000100ed35ff44bcce04b388cce4d949a30712111fd4a82fdde4a26245e5758a713f8b1d3cfe8cfd4393730f7b296c18df1a25b0983fb6f864b956cf6559379635b47bbf69caa76947db43e5bc4ed39299d821b7283f29c7e85555088e17616faef0fcef7902b711acbe01fabf1dd945c5a8a4f24c019a348ad2ecaf67a0ffeacf65a142ee5fa1e11e48d5d8969df007c48daa82ad4eed59276a8673e0faf5932f636d0b1e45e03d7ea2697a438fda3126cdded07944e6cc97119d0931d01726f059fa42b1597c802112d5e753571bac85359d9cd0441d5d4ba3599057eefea251c2708d3375bdb25bea8d19166f007e7f061d86b43366f15de48b679d2e59f594cac00000000000000000

小技巧,复制到记事本里就很方便选取一定长度的字符。

公钥对象后面必定是 0800,也可以用这个帮助判断有没有选对。

公钥对象分析参考这篇:[MS-RDPBCGR]: RSA Public Key (RSA_PUBLIC_KEY) | Microsoft Learn

官方字段解释:

magic (4 bytes): A 32-bit, unsigned integer. The sentinel value. This field MUST be set to 0x31415352.

keylen (4 bytes): A 32-bit, unsigned integer. The size in bytes of the modulus field. This value is directly related to the bitlen field and MUST be ((bitlen / 8) + 8) bytes.

bitlen (4 bytes): A 32-bit, unsigned integer. The number of bits in the public key modulus.

datalen (4 bytes): A 32-bit, unsigned integer. The maximum number of bytes that can be encoded using the public key. This value is directly related to the bitlen field and MUST be ((bitlen / 8) – 1) bytes.

pubExp (4 bytes): A 32-bit, unsigned integer. The public exponent of the public key.

modulus (variable): A variable-length array of bytes containing the public key modulus. The length in bytes of this field is given by the keylen field. The modulus field contains all (bitlen / 8) bytes of the public key modulus and 8 bytes of zero padding (which MUST follow after the modulus bytes).

一个固定值(4 字节):0x31415352

modulus 字段的长度(4 字节):等于 ((bitlen / 8) + 8) 个字节。

bitlen(4 字节):公钥的模的比特数。

可用公钥编码的数据的最大长度(4 字节):等于 ((bitlen / 8) – 1) 个字节。

公钥的 E 值(4 字节):E 值,通常是 0x00010001,也就是 65537 。

modulus 字段:包含公钥的模值,即 N 值,最后用 8 个字节的零填充。

所以进行如下操作,modulus 字段去掉最后 16 个零,得到小端序顺序的 N 值:

ed35ff44bcce04b388cce4d949a30712111fd4a82fdde4a26245e5758a713f8b1d3cfe8cfd4393730f7b296c18df1a25b0983fb6f864b956cf6559379635b47bbf69caa76947db43e5bc4ed39299d821b7283f29c7e85555088e17616faef0fcef7902b711acbe01fabf1dd945c5a8a4f24c019a348ad2ecaf67a0ffeacf65a142ee5fa1e11e48d5d8969df007c48daa82ad4eed59276a8673e0faf5932f636d0b1e45e03d7ea2697a438fda3126cdded07944e6cc97119d0931d01726f059fa42b1597c802112d5e753571bac85359d9cd0441d5d4ba3599057eefea251c2708d3375bdb25bea8d19166f007e7f061d86b43366f15de48b679d2e59f594cac0

写了小脚本将小端序转成大端序(正常阅读顺序):

def read_pairs(input_string):
  if len(input_string) % 2 != 0:
    raise ValueError(" 输入字符串的长度必须是偶数")
  
  pairs = [input_string[i:i+2] for i in range(0, len(input_string), 2)]
  return pairs

little_endian_data = input(" 请输入字符串:")
pairs = read_pairs(little_endian_data)

integer_in_big_endian = ''
for pair in pairs:
  integer_in_big_endian = pair + integer_in_big_endian

print(integer_in_big_endian)

最后提取签名对象的方法也是一样的。

三、总结:快速判断的方法

直接看 serverCertificate 的开头,如果是 02 那就是 X.509 的证书,去除前 8 个字符剩下的就是证书的十六进制数据,转成 base64 编码保存到文本文件(注意是十六进制转 base64,不是字符转 base64),重命名为 cer 后缀即可双击打开。

X.509 格式证书第一个字节是 0x30,可以利用这点帮助判断数据正确性。

如果 serverCertificate 是以 01 开头,那属于专有证书。密评中只需要知道用什么算法即可,那签名算法、密钥交换算法都固定是 RSA 了,直接看第 25~28 个字符(或 0600 后的 4 个字符)得到公钥长度(注意是小端序读法),以此判断是 RSA2048 还是 RSA1024 算法,完成!

订阅评论
提醒
guest

0 评论
内联反馈
查看所有评论