hkt1998

hkt1998

Wireshark分析RDP服务器专有证书

2024-03-10

本文记录一次使用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~30

31

certChainVersion

t

文档解释:

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算法,完成!