/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 | } |