Coverage Report

Created: 2025-02-15 06:25

/src/wireshark/epan/dissectors/packet-rdp_ear.c
Line
Count
Source (jump to first uncovered line)
1
/* Packet-rdp_ear.c
2
 * Routines for the redirected authentication RDP channel
3
 * Copyright 2023, David Fort <contact@hardening-consulting.com>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
/*
13
 * See: "[MS-RDPEAR] "
14
 */
15
16
#include "config.h"
17
18
#include <epan/packet.h>
19
#include <epan/prefs.h>
20
#include <epan/conversation.h>
21
#include <epan/expert.h>
22
#include <epan/value_string.h>
23
#include <epan/asn1.h>
24
25
#include "packet-rdpudp.h"
26
#include "packet-gssapi.h"
27
#include "packet-ber.h"
28
#include "packet-dcerpc.h"
29
#include "packet-dcerpc-rcg.h"
30
31
32
14
#define PNAME  "RDP authentication redirection virtual channel Protocol"
33
14
#define PSNAME "rdpear"
34
14
#define PFNAME "rdp_ear"
35
36
void proto_register_rdp_ear(void);
37
void proto_reg_handoff_rdp_ear(void);
38
39
40
static int proto_rdp_ear;
41
42
static int hf_rdpear_protocolMagic;
43
static int hf_rdpear_length;
44
static int hf_rdpear_version;
45
static int hf_rdpear_reserved;
46
static int hf_rdpear_tspkgcontext;
47
48
static int hf_rdpear_payload;
49
static int hf_rdpear_packet_version;
50
static int hf_rdpear_packet_packageName;
51
static int hf_rdpear_packet_buffer;
52
53
static int hf_rdpear_package_reservedHeader;
54
static int hf_rdpear_package_command;
55
56
static int ett_rdp_ear;
57
static int ett_rdp_ear_innerPacket;
58
59
static dissector_handle_t gssapi_wrap_handle;
60
61
typedef enum {
62
  RCG_PACKAGE_KERBEROS,
63
  RCG_PACKAGE_NTLM,
64
  RCG_PACKAGE_UNKNOWN = 1,
65
} RcgPackageType;
66
67
typedef struct {
68
  RcgPackageType lastPackage;
69
} RcgContext;
70
71
static int
72
0
dissect_rdpear_ber_VERSION(bool implicit_tag, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, proto_tree *tree, int hf_index) {
73
0
  offset = dissect_ber_integer(implicit_tag, actx, tree, tvb, offset, hf_index, NULL);
74
0
  return offset;
75
0
}
76
77
static int
78
0
dissect_rdpear_ber_packageName(bool implicit_tag, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, proto_tree *tree, int hf_index) {
79
0
  RcgContext *rcg = (RcgContext*)actx->private_data;
80
81
0
  tvbuff_t *packageName = NULL;
82
0
  offset = dissect_ber_octet_string_with_encoding(implicit_tag, actx, tree, tvb, offset, hf_index, &packageName, ENC_UTF_16|ENC_LITTLE_ENDIAN);
83
84
0
  rcg->lastPackage = RCG_PACKAGE_UNKNOWN;
85
0
  if (packageName) {
86
0
    char kerb[] = {'K', 0, 'e', 0, 'r', 0, 'b', 0, 'e', 0, 'r', 0, 'o', 0, 's', 0 };
87
0
    char ntlm[] = {'N', 0, 'T', 0, 'L', 0, 'M', 0 };
88
89
0
    if (tvb_memeql(packageName, 0, kerb, sizeof(kerb)) == 0)
90
0
      rcg->lastPackage = RCG_PACKAGE_KERBEROS;
91
0
    else if (tvb_memeql(packageName, 0, ntlm, sizeof(ntlm)) == 0)
92
0
      rcg->lastPackage = RCG_PACKAGE_NTLM;
93
0
  }
94
95
0
  return offset;
96
0
}
97
98
99
static int
100
dissect_rdpear_packagePayload(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, RcgPackageType package)
101
0
{
102
0
  int offset = 0;
103
0
  gboolean isServerTarget = rdp_isServerAddressTarget(pinfo);
104
105
0
  proto_tree_add_item(tree, hf_rdpear_package_reservedHeader, tvb, offset, 16, ENC_NA);
106
0
  offset += 16;
107
108
0
  dcerpc_info di = { 0 };
109
0
  guint8 drep[4] = { 0x10, 0x00, 0x00, 0x00};
110
111
0
  dcerpc_call_value call_data = { 0 };
112
0
  di.conformant_run = 0;
113
0
  di.call_data = &call_data;
114
0
  init_ndr_pointer_list(&di);
115
116
0
  switch (package) {
117
0
  case RCG_PACKAGE_KERBEROS:
118
    // NDR headers
119
0
    offset += 24;
120
0
    if (isServerTarget)
121
0
      offset = rcg_dissect_struct_KerbCredIsoRemoteOutput(tvb, offset, pinfo, tree, &di, drep, hf_rdpear_package_command, 0);
122
0
    else
123
0
      offset = rcg_dissect_struct_KerbCredIsoRemoteInput(tvb, offset, pinfo, tree, &di, drep, hf_rdpear_package_command, 0);
124
125
0
    break;
126
0
  case RCG_PACKAGE_NTLM:
127
    // NDR headers
128
0
    offset += 20;
129
130
0
    if (isServerTarget)
131
0
      offset = rcg_dissect_struct_NtlmCredIsoRemoteOutput(tvb, offset, pinfo, tree, &di, drep, hf_rdpear_package_command, 0);
132
0
    else
133
0
      offset = rcg_dissect_struct_NtlmCredIsoRemoteInput(tvb, offset, pinfo, tree, &di, drep, hf_rdpear_package_command, 0);
134
0
    break;
135
0
  default:
136
0
    break;
137
0
  }
138
139
0
  return offset;
140
0
}
141
142
143
static int
144
0
dissect_rdpear_ber_packetBuffer(bool implicit_tag, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, proto_tree *tree, int hf_index) {
145
0
  RcgContext *rcg = (RcgContext*)actx->private_data;
146
0
  tvbuff_t *packageData = NULL;
147
0
  offset = dissect_ber_octet_string(implicit_tag, actx, tree, tvb, offset, hf_index, &packageData);
148
0
  if (packageData)
149
0
    dissect_rdpear_packagePayload(tree, actx->pinfo, packageData, rcg->lastPackage);
150
0
  return offset;
151
0
}
152
153
154
static const ber_sequence_t TSRemoteGuardInnerPacket_sequence[] = {
155
  { &hf_rdpear_packet_version, BER_CLASS_CON, 0, BER_FLAGS_OPTIONAL, dissect_rdpear_ber_VERSION },
156
  { &hf_rdpear_packet_packageName, BER_CLASS_CON, 1, 0, dissect_rdpear_ber_packageName },
157
  { &hf_rdpear_packet_buffer, BER_CLASS_CON, 2, 0, dissect_rdpear_ber_packetBuffer },
158
  { NULL, 0, 0, 0, NULL }
159
};
160
161
162
static int dissect_rcg_payload(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset)
163
0
{
164
0
  RcgContext rcg = { RCG_PACKAGE_UNKNOWN };
165
0
  asn1_ctx_t asn1_ctx;
166
0
  asn1_ctx_init(&asn1_ctx, ASN1_ENC_BER, true, pinfo);
167
0
  asn1_ctx.private_data = &rcg;
168
169
0
  offset = dissect_ber_sequence(false, &asn1_ctx, tree, tvb, offset,
170
0
      TSRemoteGuardInnerPacket_sequence, hf_rdpear_payload, ett_rdp_ear_innerPacket);
171
172
0
  return offset;
173
0
}
174
175
static int
176
dissect_rdp_ear(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_)
177
0
{
178
0
  tvbuff_t *payload_tvb = NULL;
179
0
  tvbuff_t *decr_tvb = NULL;
180
0
  gssapi_encrypt_info_t gssapi_encrypt;
181
0
  proto_item *item;
182
0
  int nextOffset, offset = 0;
183
0
  uint32_t pduLength;
184
0
  proto_tree *tree;
185
186
0
  parent_tree = proto_tree_get_root(parent_tree);
187
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "RDPEAR");
188
0
  col_clear(pinfo->cinfo, COL_INFO);
189
190
0
  pduLength = tvb_get_uint32(tvb, offset + 4, ENC_LITTLE_ENDIAN) + 24;
191
0
  nextOffset = offset + pduLength;
192
193
0
  item = proto_tree_add_item(parent_tree, proto_rdp_ear, tvb, offset, pduLength, ENC_NA);
194
0
  tree = proto_item_add_subtree(item, ett_rdp_ear);
195
196
0
  proto_tree_add_item(tree, hf_rdpear_protocolMagic, tvb, offset, 4, ENC_LITTLE_ENDIAN);
197
0
  offset += 4;
198
199
0
  proto_tree_add_item(tree, hf_rdpear_length, tvb, offset, 4, ENC_LITTLE_ENDIAN);
200
0
  offset += 4;
201
202
0
  proto_tree_add_item(tree, hf_rdpear_version, tvb, offset, 4, ENC_LITTLE_ENDIAN);
203
0
  offset += 4;
204
205
0
  proto_tree_add_item(tree, hf_rdpear_reserved, tvb, offset, 4, ENC_LITTLE_ENDIAN);
206
0
  offset += 4;
207
208
0
  proto_tree_add_item(tree, hf_rdpear_tspkgcontext, tvb, offset, 8, ENC_LITTLE_ENDIAN);
209
0
  offset += 8;
210
211
  /* ================== */
212
0
  payload_tvb = tvb_new_subset_length(tvb, offset, pduLength - 24);
213
0
  memset(&gssapi_encrypt, 0, sizeof(gssapi_encrypt));
214
0
  gssapi_encrypt.decrypt_gssapi_tvb = DECRYPT_GSSAPI_NORMAL;
215
0
  call_dissector_with_data(gssapi_wrap_handle, payload_tvb, pinfo, tree, &gssapi_encrypt);
216
217
0
  decr_tvb = gssapi_encrypt.gssapi_decrypted_tvb;
218
219
0
  if (decr_tvb != NULL) {
220
0
    dissect_rcg_payload(pinfo, tree, decr_tvb, 0);
221
0
  }
222
223
0
  offset = nextOffset;
224
0
  return offset;
225
0
}
226
227
228
14
void proto_register_rdp_ear(void) {
229
14
  static hf_register_info hf[] = {
230
14
    { &hf_rdpear_protocolMagic,
231
14
      { "Protocol magic", "rdp_ear.magic",
232
14
        FT_UINT32, BASE_HEX, NULL, 0x0,
233
14
        NULL, HFILL }},
234
14
    { &hf_rdpear_length,
235
14
      { "Length", "rdp_ear.length",
236
14
        FT_UINT32, BASE_DEC, NULL, 0x0,
237
14
        NULL, HFILL }},
238
14
    { &hf_rdpear_version,
239
14
      { "Version", "rdp_ear.version",
240
14
        FT_UINT32, BASE_HEX, NULL, 0x0,
241
14
        NULL, HFILL }},
242
14
    { &hf_rdpear_reserved,
243
14
      { "Reserved", "rdp_ear.reserved",
244
14
        FT_UINT32, BASE_HEX, NULL, 0x0,
245
14
        NULL, HFILL }},
246
14
    { &hf_rdpear_tspkgcontext,
247
14
      { "TsPkgContext", "rdp_ear.tspkgcontext",
248
14
        FT_UINT64, BASE_HEX, NULL, 0x0,
249
14
        NULL, HFILL }},
250
14
    { &hf_rdpear_payload,
251
14
      { "Payload", "rdp_ear.payload",
252
14
        FT_NONE, BASE_NONE, NULL, 0,
253
14
        NULL, HFILL }},
254
14
    { &hf_rdpear_packet_version,
255
14
      { "Version", "rdp_ear.payload.version",
256
14
        FT_INT32, BASE_DEC, NULL, 0,
257
14
        NULL, HFILL }},
258
14
    { &hf_rdpear_packet_packageName,
259
14
      { "Package", "rdp_ear.payload.package",
260
14
        FT_STRING, BASE_NONE, NULL, 0,
261
14
        NULL, HFILL }},
262
14
    { &hf_rdpear_packet_buffer,
263
14
      { "Buffer", "rdp_ear.payload.buffer",
264
14
        FT_BYTES, BASE_NONE, NULL, 0,
265
14
        NULL, HFILL }},
266
14
    { &hf_rdpear_package_reservedHeader,
267
14
      { "Reserved", "rdp_ear.package.reservedheader",
268
14
        FT_BYTES, BASE_NONE, NULL, 0,
269
14
        NULL, HFILL }},
270
14
    { &hf_rdpear_package_command,
271
14
      { "Command", "rdp_ear.package.command",
272
14
        FT_BYTES, BASE_NONE, NULL, 0,
273
14
        NULL, HFILL }},
274
275
14
  };
276
277
14
  static int *ett[] = {
278
14
    &ett_rdp_ear,
279
14
    &ett_rdp_ear_innerPacket,
280
14
  };
281
282
14
  proto_rdp_ear = proto_register_protocol(PNAME, PSNAME, PFNAME);
283
284
  /* Register fields and subtrees */
285
14
  proto_register_field_array(proto_rdp_ear, hf, array_length(hf));
286
14
  proto_register_subtree_array(ett, array_length(ett));
287
288
14
  register_dissector("rdp_ear", dissect_rdp_ear, proto_rdp_ear);
289
14
}
290
291
14
void proto_reg_handoff_rdp_ear(void) {
292
14
  gssapi_wrap_handle = find_dissector_add_dependency("gssapi_verf", proto_rdp_ear);
293
14
}