1 /* Copyright (C) 2017 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18 use nom;
19 use nom::IResult;
20 use nom::combinator::rest;
21 use nom::number::streaming::{le_u8, le_u16, le_u32, le_u64};
22 use crate::smb::smb::*;
23 use crate::smb::nbss_records::NBSS_MSGTYPE_SESSION_MESSAGE;
24
25 #[derive(Debug,PartialEq)]
26 pub struct Smb2SecBlobRecord<'a> {
27 pub data: &'a[u8],
28 }
29
30 named!(pub parse_smb2_sec_blob<Smb2SecBlobRecord>,
31 do_parse!(
32 data: rest
33 >> ( Smb2SecBlobRecord {
34 data: data,
35 })
36 ));
37
38 #[derive(Debug,PartialEq)]
39 pub struct Smb2RecordDir<> {
40 pub request: bool,
41 }
42
43 named!(pub parse_smb2_record_direction<Smb2RecordDir>,
44 do_parse!(
45 _server_component: tag!(b"\xfeSMB")
46 >> _skip: take!(12)
47 >> flags: le_u8
48 >> (Smb2RecordDir {
49 request: flags & 0x01 == 0,
50 })
51 ));
52
53 #[derive(Debug,PartialEq)]
54 pub struct Smb2Record<'a> {
55 pub direction: u8, // 0 req, 1 res
56 pub nt_status: u32,
57 pub command: u16,
58 pub message_id: u64,
59 pub tree_id: u32,
60 pub async_id: u64,
61 pub session_id: u64,
62 pub data: &'a[u8],
63 }
64
65 impl<'a> Smb2Record<'a> {
is_async(&self) -> bool66 pub fn is_async(&self) -> bool {
67 self.async_id != 0
68 }
69 }
70
parse_smb2_request_flags(i:&[u8]) -> IResult<&[u8],(u8,u8,u8,u32,u8,u8,u8,u8)>71 fn parse_smb2_request_flags(i:&[u8]) -> IResult<&[u8],(u8,u8,u8,u32,u8,u8,u8,u8)> {
72 bits!(i,
73 tuple!(
74 take_bits!(2u8), // reserved / unused
75 take_bits!(1u8), // replay op
76 take_bits!(1u8), // dfs op
77 take_bits!(24u32), // reserved / unused
78 take_bits!(1u8), // signing
79 take_bits!(1u8), // chained
80 take_bits!(1u8), // async
81 take_bits!(1u8) // response
82 ))
83 }
84
85 named!(pub parse_smb2_request_record<Smb2Record>,
86 do_parse!(
87 _server_component: tag!(b"\xfeSMB")
88 >> hlen: le_u16
89 >> _credit_charge: le_u16
90 >> _channel_seq: le_u16
91 >> _reserved: take!(2)
92 >> command: le_u16
93 >> _credits_requested: le_u16
94 >> flags: parse_smb2_request_flags
95 >> chain_offset: le_u32
96 >> message_id: le_u64
97 >> _process_id: le_u32
98 >> tree_id: le_u32
99 >> session_id: le_u64
100 >> _signature: take!(16)
101 // there is probably a cleaner way to do this
102 >> data_c: cond!(chain_offset > hlen as u32, take!(chain_offset - hlen as u32))
103 >> data_r: cond!(chain_offset <= hlen as u32, rest)
104 >> (Smb2Record {
105 direction: flags.7,
106 nt_status: 0,
107 command:command,
108 message_id: message_id,
109 tree_id: tree_id,
110 async_id: 0,
111 session_id: session_id,
112 data: if data_c != None { data_c.unwrap() } else { data_r.unwrap() }
113 })
114 ));
115
116 #[derive(Debug,PartialEq)]
117 pub struct Smb2NegotiateProtocolRequestRecord<'a> {
118 pub dialects_vec: Vec<u16>,
119 pub client_guid: &'a[u8],
120 }
121
122 named!(pub parse_smb2_request_negotiate_protocol<Smb2NegotiateProtocolRequestRecord>,
123 do_parse!(
124 _struct_size: take!(2)
125 >> dialects_count: le_u16
126 >> _sec_mode: le_u16
127 >> _reserved1: le_u16
128 >> _capabilities: le_u32
129 >> client_guid: take!(16)
130 >> _ctx_offset: le_u32
131 >> _ctx_cnt: le_u16
132 >> _reserved2: le_u16
133 >> dia_vec: count!(le_u16, dialects_count as usize)
134 >> (Smb2NegotiateProtocolRequestRecord {
135 dialects_vec: dia_vec,
136 client_guid: client_guid,
137 })
138 ));
139
140 #[derive(Debug,PartialEq)]
141 pub struct Smb2NegotiateProtocolResponseRecord<'a> {
142 pub dialect: u16,
143 pub server_guid: &'a[u8],
144 }
145
146 named!(pub parse_smb2_response_negotiate_protocol<Smb2NegotiateProtocolResponseRecord>,
147 do_parse!(
148 _struct_size: take!(2)
149 >> _skip1: take!(2)
150 >> dialect: le_u16
151 >> _ctx_cnt: le_u16
152 >> server_guid: take!(16)
153 >> (Smb2NegotiateProtocolResponseRecord {
154 dialect,
155 server_guid
156 })
157 ));
158
159 named!(pub parse_smb2_response_negotiate_protocol_error<Smb2NegotiateProtocolResponseRecord>,
160 do_parse!(
161 _struct_size: take!(2)
162 >> _skip1: take!(2)
163 >> (Smb2NegotiateProtocolResponseRecord {
164 dialect: 0,
165 server_guid: &[],
166 })
167 ));
168
169
170 #[derive(Debug,PartialEq)]
171 pub struct Smb2SessionSetupRequestRecord<'a> {
172 pub data: &'a[u8],
173 }
174
175 named!(pub parse_smb2_request_session_setup<Smb2SessionSetupRequestRecord>,
176 do_parse!(
177 _struct_size: take!(2)
178 >> _flags: le_u8
179 >> _security_mode: le_u8
180 >> _capabilities: le_u32
181 >> _channel: le_u32
182 >> _sec_offset: le_u16
183 >> _sec_len: le_u16
184 >> _prev_ssn_id: take!(8)
185 >> data: rest
186 >> (Smb2SessionSetupRequestRecord {
187 data:data,
188 })
189 ));
190
191
192 #[derive(Debug,PartialEq)]
193 pub struct Smb2TreeConnectRequestRecord<'a> {
194 pub share_name: &'a[u8],
195 }
196
197 named!(pub parse_smb2_request_tree_connect<Smb2TreeConnectRequestRecord>,
198 do_parse!(
199 _struct_size: take!(2)
200 >> _offset_length: take!(4)
201 >> data: rest
202 >> (Smb2TreeConnectRequestRecord {
203 share_name:data,
204 })
205 ));
206
207 #[derive(Debug,PartialEq)]
208 pub struct Smb2TreeConnectResponseRecord<> {
209 pub share_type: u8,
210 }
211
212 named!(pub parse_smb2_response_tree_connect<Smb2TreeConnectResponseRecord>,
213 do_parse!(
214 _struct_size: take!(2)
215 >> share_type: le_u8
216 >> _share_flags: le_u32
217 >> _share_caps: le_u32
218 >> _access_mask: le_u32
219 >> (Smb2TreeConnectResponseRecord {
220 share_type
221 })
222 ));
223
224
225 #[derive(Debug,PartialEq)]
226 pub struct Smb2CreateRequestRecord<'a> {
227 pub disposition: u32,
228 pub create_options: u32,
229 pub data: &'a[u8],
230 }
231
232 named!(pub parse_smb2_request_create<Smb2CreateRequestRecord>,
233 do_parse!(
234 _skip1: take!(36)
235 >> disposition: le_u32
236 >> create_options: le_u32
237 >> _file_name_offset: le_u16
238 >> file_name_length: le_u16
239 >> _skip2: take!(8)
240 >> data: take!(file_name_length)
241 >> _skip3: rest
242 >> (Smb2CreateRequestRecord {
243 disposition,
244 create_options,
245 data
246 })
247 ));
248
249 #[derive(Debug,PartialEq)]
250 pub struct Smb2IOCtlRequestRecord<'a> {
251 pub is_pipe: bool,
252 pub function: u32,
253 pub guid: &'a[u8],
254 pub data: &'a[u8],
255 }
256
257 named!(pub parse_smb2_request_ioctl<Smb2IOCtlRequestRecord>,
258 do_parse!(
259 _skip: take!(2) // structure size
260 >> take!(2) // reserved
261 >> func: le_u32
262 >> guid: take!(16)
263 >> _indata_offset: le_u32
264 >> indata_len: le_u32
265 >> take!(4)
266 >> _outdata_offset: le_u32
267 >> _outdata_len: le_u32
268 >> take!(12)
269 >> data: take!(indata_len)
270 >> (Smb2IOCtlRequestRecord {
271 is_pipe: (func == 0x0011c017),
272 function: func,
273 guid:guid,
274 data:data,
275 })
276 ));
277
278 #[derive(Debug,PartialEq)]
279 pub struct Smb2IOCtlResponseRecord<'a> {
280 pub is_pipe: bool,
281 pub guid: &'a[u8],
282 pub data: &'a[u8],
283 pub indata_len: u32,
284 pub outdata_len: u32,
285 pub indata_offset: u32,
286 pub outdata_offset: u32,
287 }
288
289 named!(pub parse_smb2_response_ioctl<Smb2IOCtlResponseRecord>,
290 do_parse!(
291 _skip: take!(2) // structure size
292 >> take!(2) // reserved
293 >> func: le_u32
294 >> guid: take!(16)
295 >> indata_offset: le_u32
296 >> indata_len: le_u32
297 >> outdata_offset: le_u32
298 >> outdata_len: le_u32
299 >> take!(8)
300 >> take!(indata_len)
301 >> data: take!(outdata_len)
302 >> (Smb2IOCtlResponseRecord {
303 is_pipe: (func == 0x0011c017),
304 guid:guid,
305 data:data,
306 indata_len:indata_len,
307 outdata_len:outdata_len,
308 indata_offset:indata_offset,
309 outdata_offset:outdata_offset,
310 })
311 ));
312
313 #[derive(Debug,PartialEq)]
314 pub struct Smb2CloseRequestRecord<'a> {
315 pub guid: &'a[u8],
316 }
317
318 named!(pub parse_smb2_request_close<Smb2CloseRequestRecord>,
319 do_parse!(
320 _skip: take!(8)
321 >> guid: take!(16)
322 >> (Smb2CloseRequestRecord {
323 guid
324 })
325 ));
326
327 #[derive(Debug)]
328 pub struct Smb2SetInfoRequestRenameRecord<'a> {
329 pub name: &'a[u8],
330 }
331
332 named!(pub parse_smb2_request_setinfo_rename<Smb2SetInfoRequestRenameRecord>,
333 do_parse!(
334 _replace: le_u8
335 >> _reserved: take!(7)
336 >> _root_handle: take!(8)
337 >> name_len: le_u32
338 >> name: take!(name_len)
339 >> (Smb2SetInfoRequestRenameRecord {
340 name
341 })
342 ));
343
344 #[derive(Debug)]
345 pub struct Smb2SetInfoRequestRecord<'a> {
346 pub guid: &'a[u8],
347 pub class: u8,
348 pub infolvl: u8,
349 pub rename: Option<Smb2SetInfoRequestRenameRecord<'a>>,
350 }
351
352 named!(pub parse_smb2_request_setinfo<Smb2SetInfoRequestRecord>,
353 do_parse!(
354 _struct_size: le_u16
355 >> class: le_u8
356 >> infolvl: le_u8
357 >> setinfo_size: le_u32
358 >> _setinfo_offset: le_u16
359 >> _reserved: take!(2)
360 >> _additional_info: le_u32
361 >> guid: take!(16)
362 >> rename: cond!(class == 1 && infolvl == 10, flat_map!(take!(setinfo_size),parse_smb2_request_setinfo_rename))
363 >> (Smb2SetInfoRequestRecord {
364 guid: guid,
365 class: class,
366 infolvl: infolvl,
367 rename: rename,
368 })
369 ));
370
371 #[derive(Debug,PartialEq)]
372 pub struct Smb2WriteRequestRecord<'a> {
373 pub wr_len: u32,
374 pub wr_offset: u64,
375 pub guid: &'a[u8],
376 pub data: &'a[u8],
377 }
378
379 // can be called on incomplete records
380 named!(pub parse_smb2_request_write<Smb2WriteRequestRecord>,
381 do_parse!(
382 _skip1: take!(4)
383 >> wr_len: le_u32
384 >> wr_offset: le_u64
385 >> guid: take!(16)
386 >> _channel: le_u32
387 >> _remaining_bytes: le_u32
388 >> _write_flags: le_u32
389 >> _skip2: take!(4)
390 >> data: call!(parse_smb2_data, wr_len)
391 >> (Smb2WriteRequestRecord {
392 wr_len:wr_len,
393 wr_offset:wr_offset,
394 guid:guid,
395 data:data,
396 })
397 ));
398
399 #[derive(Debug,PartialEq)]
400 pub struct Smb2ReadRequestRecord<'a> {
401 pub rd_len: u32,
402 pub rd_offset: u64,
403 pub guid: &'a[u8],
404 }
405
406 named!(pub parse_smb2_request_read<Smb2ReadRequestRecord>,
407 do_parse!(
408 _skip1: take!(4)
409 >> rd_len: le_u32
410 >> rd_offset: le_u64
411 >> guid: take!(16)
412 >> _min_count: le_u32
413 >> _channel: le_u32
414 >> _remaining_bytes: le_u32
415 >> _skip2: take!(4)
416 >> (Smb2ReadRequestRecord {
417 rd_len:rd_len,
418 rd_offset:rd_offset,
419 guid:guid,
420 })
421 ));
422
423 #[derive(Debug,PartialEq)]
424 pub struct Smb2ReadResponseRecord<'a> {
425 pub len: u32,
426 pub data: &'a[u8],
427 }
428
429 // parse read/write data. If all is available, 'take' it.
430 // otherwise just return what we have. So this may return
431 // partial data.
parse_smb2_data<'a>(i: &'a[u8], len: u32) -> IResult<&'a[u8], &'a[u8]>432 fn parse_smb2_data<'a>(i: &'a[u8], len: u32)
433 -> IResult<&'a[u8], &'a[u8]>
434 {
435 if len as usize > i.len() {
436 rest(i)
437 } else {
438 take!(i, len)
439 }
440 }
441
442 // can be called on incomplete records
443 named!(pub parse_smb2_response_read<Smb2ReadResponseRecord>,
444 do_parse!(
445 _struct_size: le_u16
446 >> _data_offset: le_u16
447 >> rd_len: le_u32
448 >> _rd_rem: le_u32
449 >> _padding: take!(4)
450 >> data: call!(parse_smb2_data, rd_len)
451 >> (Smb2ReadResponseRecord {
452 len : rd_len,
453 data : data,
454 })
455 ));
456
457 #[derive(Debug,PartialEq)]
458 pub struct Smb2CreateResponseRecord<'a> {
459 pub guid: &'a[u8],
460 pub create_ts: SMBFiletime,
461 pub last_access_ts: SMBFiletime,
462 pub last_write_ts: SMBFiletime,
463 pub last_change_ts: SMBFiletime,
464 pub size: u64,
465 }
466
467 named!(pub parse_smb2_response_create<Smb2CreateResponseRecord>,
468 do_parse!(
469 _ssize: le_u16
470 >> _oplock: le_u8
471 >> _resp_flags: le_u8
472 >> _create_action: le_u32
473 >> create_ts: le_u64
474 >> last_access_ts: le_u64
475 >> last_write_ts: le_u64
476 >> last_change_ts: le_u64
477 >> _alloc_size: le_u64
478 >> eof: le_u64
479 >> _attrs: le_u32
480 >> _padding: take!(4)
481 >> guid: take!(16)
482 >> _skip2: take!(8)
483 >> (Smb2CreateResponseRecord {
484 guid : guid,
485 create_ts: SMBFiletime::new(create_ts),
486 last_access_ts: SMBFiletime::new(last_access_ts),
487 last_write_ts: SMBFiletime::new(last_write_ts),
488 last_change_ts: SMBFiletime::new(last_change_ts),
489 size: eof,
490 })
491 ));
492
493 #[derive(Debug,PartialEq)]
494 pub struct Smb2WriteResponseRecord<> {
495 pub wr_cnt: u32,
496 }
497
498 named!(pub parse_smb2_response_write<Smb2WriteResponseRecord>,
499 do_parse!(
500 _skip1: take!(4)
501 >> wr_cnt: le_u32
502 >> _skip2: take!(6)
503 >> (Smb2WriteResponseRecord {
504 wr_cnt : wr_cnt,
505 })
506 ));
507
508 named!(pub parse_smb2_response_record<Smb2Record>,
509 do_parse!(
510 tag!(b"\xfeSMB")
511 >> hlen: le_u16
512 >> _credit_charge: le_u16
513 >> nt_status: le_u32
514 >> command: le_u16
515 >> _credit_granted: le_u16
516 >> flags: parse_smb2_request_flags
517 >> chain_offset: le_u32
518 >> message_id: le_u64
519 >> _process_id: cond!(flags.6==0, le_u32)
520 >> tree_id: cond!(flags.6==0, le_u32)
521 >> async_id: cond!(flags.6==1, le_u64)
522 >> session_id: le_u64
523 >> _signature: take!(16)
524 // there is probably a cleaner way to do this
525 >> data_c: cond!(chain_offset > hlen as u32, take!(chain_offset - hlen as u32))
526 >> data_r: cond!(chain_offset <= hlen as u32, rest)
527 >> (Smb2Record {
528 direction: flags.7,
529 nt_status: nt_status,
530 message_id: message_id,
531 tree_id: tree_id.unwrap_or(0),
532 async_id: async_id.unwrap_or(0),
533 session_id: session_id,
534 command:command,
535 data: data_c.or(data_r).unwrap()
536 })
537 ));
538
smb_basic_search(d: &[u8]) -> usize539 fn smb_basic_search(d: &[u8]) -> usize {
540 let needle = b"SMB";
541 let mut r = 0 as usize;
542 // this could be replaced by aho-corasick
543 let iter = d.windows(needle.len());
544 for window in iter {
545 if window == needle {
546 return r;
547 }
548 r = r + 1;
549 }
550 return 0;
551 }
552
search_smb_record<'a>(i: &'a [u8]) -> nom::IResult<&'a [u8], &'a [u8]>553 pub fn search_smb_record<'a>(i: &'a [u8]) -> nom::IResult<&'a [u8], &'a [u8]> {
554 let mut d = i;
555 while d.len() >= 4 {
556 let index = smb_basic_search(d);
557 if index == 0 {
558 return Err(nom::Err::Error((d, nom::error::ErrorKind::Eof)));
559 }
560 if d[index - 1] == 0xfe || d[index - 1] == 0xff || d[index - 1] == 0xfd {
561 // if we have enough data, check nbss
562 if index < 5 || d[index-5] == NBSS_MSGTYPE_SESSION_MESSAGE {
563 return Ok((&d[index + 3..], &d[index - 1..]));
564 }
565 }
566 d = &d[index + 3..];
567 }
568 Err(nom::Err::Incomplete(nom::Needed::Size(4 as usize - d.len())))
569 }
570