diff --git a/src/proto/smb.rs b/src/proto/smb.rs index ecb3f5d..1d17a3b 100644 --- a/src/proto/smb.rs +++ b/src/proto/smb.rs @@ -322,10 +322,10 @@ impl SMB1Header { // Negotiate SMB1Payload::NegotiateRequest(SMB1NegotiateRequest::new()) } - // 0x73 => { - // // Setup - // SMB1Payload::SessionSetupRequest(SMB2SessionSetupRequest::new()) - // } + 0x73 => { + // Setup + SMB1Payload::SessionSetupRequest(SMB1SessionSetupRequest::new()) + } _ => None?, }); self.payload.as_mut() @@ -455,42 +455,186 @@ impl MPacket for SMB1NegotiateRequest { } } -// #[derive(Debug, Clone)] -// struct SMB1SetupRequest { -// -// } +#[derive(Debug, Clone, Copy)] +enum SMB1SessionSetupRequestState { + WordCount, + AndXCommand, + AndXReserved, + AndXOffset, + MaxBufferSize, + MaxMPXCount, + VcNumber, + SessionKey, + SecurityBlobLength, + Reserved, + ServerCapabilities, + ByteCount, + SecurityBlob, + End, +} -// impl SMB1SetupRequest { -// // TODO -// fn new(data: &[u8]) -> Option { -// if data.len() < 38 { -// return None; -// } -// None -// } +#[derive(Debug, Clone)] +struct SMB1SessionSetupRequest { + // DISSECTION + d: PacketDissector, + // STRUCT + word_count: u8, + and_x_command: u8, + and_x_offset: u16, + max_buffer_size: u16, + max_mpx_count: u16, + vc_number: u16, + session_key: u32, + security_len: u16, + server_capabilities: u32, + byte_count: u16, +} -// fn repl(&self) -> Option> { -// None -// } -// } +impl MPacket for SMB1SessionSetupRequest { + fn new() -> SMB1SessionSetupRequest { + Self { + d: PacketDissector::new(SMB1SessionSetupRequestState::WordCount), + word_count: 0, + and_x_command: 0, + and_x_offset: 0, + max_buffer_size: 0, + max_mpx_count: 0, + vc_number: 0, + session_key: 0, + security_len: 0, + server_capabilities: 0, + byte_count: 0, + } + } + fn parse(&mut self, byte: &u8) { + // We expect extended security because that's what we asked for in the NegotiateRequest + match self.d.state { + SMB1SessionSetupRequestState::WordCount => { + self.word_count = *byte; + self.d.next_state(SMB1SessionSetupRequestState::AndXCommand); + } + SMB1SessionSetupRequestState::AndXCommand => { + self.and_x_command = *byte; + self.d + .next_state(SMB1SessionSetupRequestState::AndXReserved); + } + SMB1SessionSetupRequestState::AndXReserved => { + self.d.next_state(SMB1SessionSetupRequestState::AndXOffset); + } + SMB1SessionSetupRequestState::AndXOffset => { + self.and_x_offset = self.d.read_ule16( + byte, + self.and_x_offset, + SMB1SessionSetupRequestState::MaxBufferSize, + ); + } + SMB1SessionSetupRequestState::MaxBufferSize => { + self.max_buffer_size = self.d.read_ule16( + byte, + self.max_buffer_size, + SMB1SessionSetupRequestState::MaxMPXCount, + ); + } + SMB1SessionSetupRequestState::MaxMPXCount => { + self.max_mpx_count = self.d.read_ule16( + byte, + self.max_mpx_count, + SMB1SessionSetupRequestState::VcNumber, + ); + } + SMB1SessionSetupRequestState::VcNumber => { + self.vc_number = self.d.read_ule16( + byte, + self.vc_number, + SMB1SessionSetupRequestState::SessionKey, + ); + } + SMB1SessionSetupRequestState::SessionKey => { + self.session_key = self.d.read_ule32( + byte, + self.session_key, + SMB1SessionSetupRequestState::SecurityBlobLength, + ); + } + SMB1SessionSetupRequestState::SecurityBlobLength => { + self.security_len = self.d.read_ule16( + byte, + self.security_len, + SMB1SessionSetupRequestState::Reserved, + ); + } + SMB1SessionSetupRequestState::Reserved => { + self.d.i += 1; + self.d + .next_state_when_i_reaches(SMB1SessionSetupRequestState::ServerCapabilities, 4); + } + SMB1SessionSetupRequestState::ServerCapabilities => { + self.server_capabilities = self.d.read_ule32( + byte, + self.server_capabilities, + SMB1SessionSetupRequestState::ByteCount, + ); + } + SMB1SessionSetupRequestState::ByteCount => { + self.byte_count = self.d.read_ule16( + byte, + self.byte_count, + SMB1SessionSetupRequestState::SecurityBlob, + ); + } + SMB1SessionSetupRequestState::SecurityBlob => { + self.d.i += 1; + self.d.next_state_when_i_reaches( + SMB1SessionSetupRequestState::End, + self.security_len as usize, + ); + } + SMB1SessionSetupRequestState::End => {} + } + } + fn repl(&self) -> Option> { + if !matches!(self.d.state, SMB1SessionSetupRequestState::End) { + return None; + } + // "Windows 4.0" in UTF-16 + two null bytes + let native_os = b"W\x00i\x00n\x00d\x00o\x00w\x00s\x00 \x004\x00.\x000\x00\x00\x00"; + let native_man_lan = native_os; + let mut resp: Vec = Vec::new(); + resp.push(0x4); // WordCount + resp.push(0xff); // AndXCommand + resp.push(0x0); // AndXReserved + resp.extend_from_slice(&0x44_u16.to_le_bytes()); // AndXOffset + resp.extend_from_slice(&0x0_u16.to_le_bytes()); // Action + resp.extend_from_slice(&(SECURITY_BLOB_CHALLENGE.len() as u16).to_le_bytes()); // SecurityLen + resp.extend_from_slice( + &((SECURITY_BLOB_CHALLENGE.len() + native_os.len() + native_man_lan.len()) as u16) + .to_le_bytes(), + ); // ByteCount + resp.extend_from_slice(SECURITY_BLOB_CHALLENGE); // SecurityBlob + resp.extend_from_slice(native_os); + resp.extend_from_slice(native_man_lan); + warn!("SMB1 SessionSetup-Reply"); + Some(resp) + } +} #[derive(Debug, Clone)] enum SMB1Payload { NegotiateRequest(SMB1NegotiateRequest), - // SetupRequest(SMB1SetupRequest), + SessionSetupRequest(SMB1SessionSetupRequest), } impl SMB1Payload { fn repl(&self) -> Option> { match self { SMB1Payload::NegotiateRequest(x) => x.repl(), - // SMB1Payload::SessionSetupRequest(x) => x.repl(), + SMB1Payload::SessionSetupRequest(x) => x.repl(), } } fn parse(&mut self, byte: &u8) { match self { SMB1Payload::NegotiateRequest(x) => x.parse(byte), - // SMB1Payload::SessionSetupRequest(x) => x.repl(), + SMB1Payload::SessionSetupRequest(x) => x.parse(byte), } } } @@ -825,7 +969,7 @@ impl MPacket for SMB2NegotiateRequest { resp.extend_from_slice(&0x80_u16.to_le_bytes()); // SecurityBloboffset resp.extend_from_slice(&(SECURITY_BLOB_NEG_PROTO.len() as u16).to_le_bytes()); // SecurityBlobLength resp.extend_from_slice(&0x0_u32.to_le_bytes()); // NegotiateContextOffset - resp.extend_from_slice(SECURITY_BLOB_NEG_PROTO); // SecurityBlobw + resp.extend_from_slice(SECURITY_BLOB_NEG_PROTO); // SecurityBlob warn!("SMB2 Negotiate-Protocol-Reply ({})", dialect_name); Some(resp) } @@ -923,8 +1067,11 @@ impl MPacket for SMB2SessionSetupRequest { ); } SMB2SetupRequestState::SecurityBlob => { - // TODO ? Not super useful TBH, also this is ASN.1 :/// - self.d.next_state(SMB2SetupRequestState::End); + self.d.i += 1; + self.d.next_state_when_i_reaches( + SMB2SetupRequestState::End, + self.security_len as usize, + ); } SMB2SetupRequestState::End => {} } @@ -940,7 +1087,7 @@ impl MPacket for SMB2SessionSetupRequest { resp.extend_from_slice(&0x48_u16.to_le_bytes()); // SecurityBufferOffset resp.extend_from_slice(&(SECURITY_BLOB_CHALLENGE.len() as u16).to_le_bytes()); // SecurityLen resp.extend_from_slice(SECURITY_BLOB_CHALLENGE); // SecurityBlob - warn!("SMB2 Setup-Reply"); + warn!("SMB2 SessionSetup-Reply"); Some(resp) } } @@ -1011,6 +1158,7 @@ mod tests { // Sent by `smbclient -U "" -N -L 10.1.1.1 -d10 --option='client min protocol=NT1'` const SMB1_REQ_NEGOTIATE: &[u8] = b"\x00\x00\x00T\xffSMBr\x00\x00\x00\x00\x18C\xc8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xff\x00\x00\x00\x00\x001\x00\x02NT LANMAN 1.0\x00\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00"; + const SMB1_REQ_SESSION_SETUP: &[u8] = b"\x00\x00\x00\x9c\xffSMBs\x00\x00\x00\x00\x18C\xc8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x89T\x00\x00\x01\x00\x0c\xff\x00\x00\x00\xff\xff\x02\x00\x01\x00\x00\x00\x00\x00J\x00\x00\x00\x00\x00T\xc0\x00\x80a\x00`H\x06\x06+\x06\x01\x05\x05\x02\xa0>0<\xa0\x0e0\x0c\x06\n+\x06\x01\x04\x01\x827\x02\x02\n\xa2*\x04(NTLMSSP\x00\x01\x00\x00\x00\x15\x82\x08b\x00\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x06\x01\x00\x00\x00\x00\x00\x0f\x00U\x00n\x00i\x00x\x00\x00\x00S\x00a\x00m\x00b\x00a\x00\x00\x00"; // Sent by `smbclient -U "" -N -L 10.1.1.1 -d10` const SMB2_REQ_NEGOTIATE: &[u8] = b"\x00\x00\x00\xd0\xfeSMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x08\x00\x01\x00\x00\x00\x7f\x00\x00\x00\rr3\x97\"c\x8fA\x9f\xe0\xbawQ\x87rbx\x00\x00\x00\x03\x00\x00\x00\x02\x02\x10\x02\"\x02$\x02\x00\x03\x02\x03\x10\x03\x11\x03\x00\x00\x00\x00\x01\x00&\x00\x00\x00\x00\x00\x01\x00 \x00\x01\x00\xd5Z\x89\x87>\x80\xcd\x02\xc2\xab\x08\xa3\xf4\x94\xb6A\x05\x11V\xeeE\x19p\x19\xed\x17v\xda\x9b\x08\x99V\x00\x00\x02\x00\x06\x00\x00\x00\x00\x00\x02\x00\x02\x00\x01\x00\x00\x00\x05\x00\x10\x00\x00\x00\x00\x001\x000\x00.\x001\x00.\x001\x00.\x001\x00"; const SMB2_REQ_SESSION_SETUP: &[u8] = b"\x00\x00\x00\xa2\xfeSMB@\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00X\x00J\x00\x00\x00\x00\x00\x00\x00\x00\x00`H\x06\x06+\x06\x01\x05\x05\x02\xa0>0<\xa0\x0e0\x0c\x06\n+\x06\x01\x04\x01\x827\x02\x02\n\xa2*\x04(NTLMSSP\x00\x01\x00\x00\x00\x15\x82\x08b\x00\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x06\x01\x00\x00\x00\x00\x00\x0f"; @@ -1036,6 +1184,7 @@ mod tests { assert_eq!(smb1.mid, 0); let neg_request = match smb1.payload.expect("Error while reading payload") { SMB1Payload::NegotiateRequest(x) => x, + _ => panic!("Bad payload"), }; assert_eq!(neg_request.word_count, 0); assert_eq!(neg_request.byte_count, 49); @@ -1098,6 +1247,65 @@ mod tests { assert_eq!(answer[0x3c + 8..], expected[0x3c + 8..]); } #[test] + fn test_smb1_session_setup_request_parse() { + let mut nbtsession: NBTSession = NBTSession::new(); + nbtsession.parse_all(SMB1_REQ_SESSION_SETUP); + assert_eq!(nbtsession.nb_type, 0); + assert_eq!(nbtsession.length, 0x9c); + let smb1 = nbtsession.payload.expect("Error while unpacking SMB"); + assert_eq!(&smb1.start, b"\xffSMB"); + assert_eq!(smb1.command, 0x73); + assert_eq!(smb1.status, 0); + assert_eq!(smb1.flags, 24); + assert_eq!(smb1.flags2, 0xc843); + assert_eq!(smb1.pid_high, 0); + assert_eq!(smb1.tid, 0); + assert_eq!(smb1.pid_low, 21641); + assert_eq!(smb1.uid, 0); + assert_eq!(smb1.mid, 1); + let sess_setup_req = match smb1.payload.expect("Error while reading payload") { + SMB1Payload::SessionSetupRequest(x) => x, + _ => panic!("Bad type"), + }; + assert_eq!(sess_setup_req.word_count, 12); + assert_eq!(sess_setup_req.and_x_command, 0xff); + assert_eq!(sess_setup_req.and_x_offset, 0); + assert_eq!(sess_setup_req.max_buffer_size, 0xffff); + assert_eq!(sess_setup_req.max_mpx_count, 2); + assert_eq!(sess_setup_req.vc_number, 1); + assert_eq!(sess_setup_req.session_key, 0); + assert_eq!(sess_setup_req.security_len, 74); + assert_eq!(sess_setup_req.server_capabilities, 0x8000c054); + assert_eq!(sess_setup_req.server_capabilities, 0x8000c054); + } + #[test] + fn test_smb1_session_setup_request_reply() { + let masscanned = Masscanned { + synack_key: [0, 0], + mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"), + iface: None, + ip_addresses: None, + log: MetaLogger::new(), + }; + let client_info = ClientInfo::new(); + let answer = repl_smb1(SMB1_REQ_SESSION_SETUP, &masscanned, &client_info, None) + .expect("Error: no answer"); + let expected = [ + 0, 0, 0, 250, 255, 83, 77, 66, 115, 0, 0, 0, 0, 152, 7, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 137, 84, 0, 0, 1, 0, 4, 255, 0, 68, 0, 0, 0, 159, 0, 207, 0, 161, 129, + 156, 48, 129, 153, 160, 3, 10, 1, 1, 161, 12, 6, 10, 43, 6, 1, 4, 1, 130, 55, 2, 2, 10, + 162, 129, 131, 4, 129, 128, 78, 84, 76, 77, 83, 83, 80, 0, 2, 0, 0, 0, 8, 0, 8, 0, 56, + 0, 0, 0, 21, 130, 138, 226, 36, 145, 168, 246, 243, 137, 45, 52, 0, 0, 0, 0, 0, 0, 0, + 0, 64, 0, 64, 0, 64, 0, 0, 0, 10, 0, 97, 74, 0, 0, 0, 15, 87, 0, 73, 0, 78, 0, 49, 0, + 2, 0, 8, 0, 87, 0, 73, 0, 78, 0, 49, 0, 1, 0, 8, 0, 87, 0, 73, 0, 78, 0, 49, 0, 4, 0, + 8, 0, 87, 0, 73, 0, 78, 0, 49, 0, 3, 0, 8, 0, 87, 0, 73, 0, 78, 0, 49, 0, 7, 0, 8, 0, + 255, 38, 57, 245, 66, 29, 216, 1, 0, 0, 0, 0, 87, 0, 105, 0, 110, 0, 100, 0, 111, 0, + 119, 0, 115, 0, 32, 0, 52, 0, 46, 0, 48, 0, 0, 0, 87, 0, 105, 0, 110, 0, 100, 0, 111, + 0, 119, 0, 115, 0, 32, 0, 52, 0, 46, 0, 48, 0, 0, 0, + ]; + assert_eq!(answer, expected); + } + #[test] fn test_smb2_protocol_nego_parsing() { let mut nbtsession: NBTSession = NBTSession::new(); nbtsession.parse_all(SMB2_REQ_NEGOTIATE);