]> git.sesse.net Git - ntlmssp-extract/blob - ntlmssp.cpp
Initial commit.
[ntlmssp-extract] / ntlmssp.cpp
1 // Extracts NTMLSSP challenge/authentication pairs from a pcap file,
2 // into a format crackable by the “jumbo” edition of John the Ripper.
3 // Heavily inspired by https://github.com/psychomario/ntlmsspparse/blob/master/ntlmssp.py,
4 // but infinitely faster.
5 //
6 // The struct definitions were lifted from ChromeOS, who probably
7 // lifted them from some specification. They have been adapted a bit.
8
9 #include <string.h>
10 #include <pcap.h>
11 #include <stdlib.h>
12 #include <netinet/ip.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <netinet/tcp.h>
18 #include <arpa/inet.h>
19 #include <map>
20 #include <string>
21 #include <algorithm>
22
23 using namespace std;
24
25 enum ntlmssp_message_type
26 {
27         NTLMSSP_NEGOTIATE = 1,
28         NTLMSSP_CHALLENGE = 2,
29         NTLMSSP_AUTH      = 3,
30         NTLMSSP_UNKNOWN   = 4,
31 };
32
33 #define NTLMSSP_SIGNATURE "NTLMSSP"
34 #define CIFS_CRYPTO_KEY_SIZE (8)
35
36 typedef struct _SECURITY_BUFFER {
37         uint16_t Length;
38         uint16_t MaximumLength;
39         uint32_t BufferOffset;    /* offset to buffer */
40 } __attribute__((packed)) SECURITY_BUFFER;
41
42 struct _NTLM_MESSAGE {
43         uint8_t Signature[sizeof(NTLMSSP_SIGNATURE)];
44         uint32_t MessageType;
45 };
46
47 typedef struct _NEGOTIATE_MESSAGE {
48         uint8_t Signature[sizeof(NTLMSSP_SIGNATURE)];
49         uint32_t MessageType;     /* NtLmNegotiate = 1 */
50         uint32_t NegotiateFlags;
51         SECURITY_BUFFER DomainName;     /* RFC 1001 style and ASCII */
52         SECURITY_BUFFER WorkstationName;        /* RFC 1001 and ASCII */
53         /* SECURITY_BUFFER for version info not present since we
54            do not set the version is present flag */
55         char DomainString[0];
56         /* followed by WorkstationString */
57 } __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
58
59 typedef struct _CHALLENGE_MESSAGE {
60         uint8_t Signature[sizeof(NTLMSSP_SIGNATURE)];
61         uint32_t MessageType;   /* NtLmChallenge = 2 */
62         SECURITY_BUFFER TargetName;
63         uint32_t NegotiateFlags;
64         uint8_t Challenge[CIFS_CRYPTO_KEY_SIZE];
65         uint8_t Reserved[8];
66         SECURITY_BUFFER TargetInfoArray;
67         /* SECURITY_BUFFER for version info not present since we
68            do not set the version is present flag */
69 } __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE;
70
71 typedef struct _AUTHENTICATE_MESSAGE {
72         uint8_t Signature[sizeof(NTLMSSP_SIGNATURE)];
73         uint32_t MessageType;  /* NtLmsAuthenticate = 3 */
74         SECURITY_BUFFER LmChallengeResponse;
75         SECURITY_BUFFER NtChallengeResponse;
76         SECURITY_BUFFER DomainName;
77         SECURITY_BUFFER UserName;
78         SECURITY_BUFFER WorkstationName;
79         SECURITY_BUFFER SessionKey;
80         uint32_t NegotiateFlags;
81         /* SECURITY_BUFFER for version info not present since we
82            do not set the version is present flag */
83         char UserString[0];
84 } __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
85
86 #define NTLMSSP_FEATURE_SESSION_KEY        0x00000001
87 #define NTLMSSP_FEATURE_SIGN               0x00000002
88 #define NTLMSSP_FEATURE_SEAL               0x00000004
89 #define NTLMSSP_FEATURE_CCACHE   0x00000008
90
91 struct quintuple {
92         uint32_t saddr, daddr;
93         uint16_t sport, dport;
94
95         bool operator< (const quintuple &o) const {
96                 if (saddr != o.saddr)
97                         return saddr < o.saddr;
98                 if (daddr != o.daddr)
99                         return daddr < o.daddr;
100                 if (sport != o.sport)
101                         return sport < o.sport;
102                 return dport < o.dport;
103         }
104 };
105
106 map<quintuple, string> prev_packets;
107
108 string to_ascii(const SECURITY_BUFFER &buf, const void *base) {
109         const char *str = (char *)base + buf.BufferOffset;
110
111         string ret;
112         for (int i = 0; i < buf.Length; ++i) {
113                 if (str[i] == 0) {
114                         // poor man's UTF-16 conversion :-)
115                         continue;
116                 }
117                 ret.push_back(str[i]);
118         }
119         return ret;
120 }
121
122 string to_hex(const SECURITY_BUFFER &buf, const void *base) {
123         const uint8_t *str = (uint8_t *)base + buf.BufferOffset;
124
125         string ret;
126         char tmp[256];
127         for (int i = 0; i < buf.Length; ++i) {
128                 sprintf(tmp, "%02x", str[i]);
129                 ret += tmp;
130         }
131         return ret;
132 }
133
134 string to_hex(const uint8_t *start, const uint8_t *end) {
135         string ret;
136         char tmp[256];
137         for (const uint8_t *ptr = start; ptr != end; ++ptr) {
138                 sprintf(tmp, "%02x", *ptr);
139                 ret += tmp;
140         }
141         return ret;
142 }
143
144 void my_callback(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
145 {
146         int len = h->caplen;
147         if (len < 40) {
148                 //printf("skipped short packet\n");
149                 return;
150         }
151         if (bytes[12] != 0x08 || bytes[13] != 0x00) {
152                 //printf("skipped non-IPv4 packet\n");
153                 return;
154         }
155         const struct iphdr *ip = (const struct iphdr *)(bytes + 14);
156         const struct tcphdr *tcp = (const struct tcphdr *)(bytes + 14 + sizeof(*ip));
157         const u_char *data = bytes + 14 + sizeof(*ip) + sizeof(*tcp);
158         if (data > bytes + len) {
159                 return;
160         }
161         const u_char *ntlmssp = (const u_char *)memmem(data, (bytes + len) - data, "NTLMSSP", 7);
162         if (ntlmssp == NULL) {
163                 return;
164         }
165
166         quintuple invq;
167         invq.saddr = ip->daddr;
168         invq.daddr = ip->saddr;
169         invq.sport = tcp->dest;
170         invq.dport = tcp->source;
171
172         auto it = prev_packets.find(invq);
173         if (it == prev_packets.end()) {
174                 const _CHALLENGE_MESSAGE *challenge = reinterpret_cast<const _CHALLENGE_MESSAGE *>(ntlmssp);
175                 if (challenge->MessageType != NTLMSSP_CHALLENGE || ntlmssp + sizeof(_CHALLENGE_MESSAGE) > bytes + len) {
176                         return;
177                 }       
178                 quintuple q;
179                 q.saddr = ip->saddr;
180                 q.daddr = ip->daddr;
181                 q.sport = tcp->source;
182                 q.dport = tcp->dest;
183                 prev_packets.insert(make_pair(q, string(ntlmssp, bytes + len)));
184                 return;
185         }
186
187         const _CHALLENGE_MESSAGE *challenge = reinterpret_cast<const _CHALLENGE_MESSAGE *>(it->second.data());
188         const _AUTHENTICATE_MESSAGE *auth = reinterpret_cast<const _AUTHENTICATE_MESSAGE *>(ntlmssp);
189
190         if (auth->MessageType != NTLMSSP_AUTH || ntlmssp + sizeof(_AUTHENTICATE_MESSAGE) > bytes + len) {
191                 return;
192         }
193
194         printf("%s::%s:%s:%s:%s\n",
195                 to_ascii(auth->UserName, auth).c_str(),
196                 to_ascii(auth->DomainName, auth).c_str(),
197                 to_hex(auth->LmChallengeResponse, auth).c_str(),
198                 to_hex(auth->NtChallengeResponse, auth).c_str(),
199                 to_hex(challenge->Challenge, challenge->Challenge + CIFS_CRYPTO_KEY_SIZE).c_str());
200
201         prev_packets.erase(it);
202 }
203
204 int main(int argc, char **argv)
205 {
206         pcap_t *pcap = pcap_open_offline(argv[1], NULL);
207         pcap_activate(pcap);
208         pcap_loop(pcap, -1, my_callback, NULL);
209         return 0;
210 }
211