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.
6 // The struct definitions were lifted from ChromeOS, who probably
7 // lifted them from some specification. They have been adapted a bit.
12 #include <netinet/ip.h>
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <netinet/tcp.h>
18 #include <arpa/inet.h>
25 enum ntlmssp_message_type
27 NTLMSSP_NEGOTIATE = 1,
28 NTLMSSP_CHALLENGE = 2,
33 #define NTLMSSP_SIGNATURE "NTLMSSP"
34 #define CIFS_CRYPTO_KEY_SIZE (8)
36 typedef struct _SECURITY_BUFFER {
38 uint16_t MaximumLength;
39 uint32_t BufferOffset; /* offset to buffer */
40 } __attribute__((packed)) SECURITY_BUFFER;
42 struct _NTLM_MESSAGE {
43 uint8_t Signature[sizeof(NTLMSSP_SIGNATURE)];
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 */
56 /* followed by WorkstationString */
57 } __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
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];
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;
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 */
84 } __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
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
92 uint32_t saddr, daddr;
93 uint16_t sport, dport;
95 bool operator< (const quintuple &o) const {
97 return saddr < o.saddr;
99 return daddr < o.daddr;
100 if (sport != o.sport)
101 return sport < o.sport;
102 return dport < o.dport;
106 map<quintuple, string> prev_packets;
108 string to_ascii(const SECURITY_BUFFER &buf, const void *base) {
109 const char *str = (char *)base + buf.BufferOffset;
112 for (int i = 0; i < buf.Length; ++i) {
114 // poor man's UTF-16 conversion :-)
117 ret.push_back(str[i]);
122 string to_hex(const SECURITY_BUFFER &buf, const void *base) {
123 const uint8_t *str = (uint8_t *)base + buf.BufferOffset;
127 for (int i = 0; i < buf.Length; ++i) {
128 sprintf(tmp, "%02x", str[i]);
134 string to_hex(const uint8_t *start, const uint8_t *end) {
137 for (const uint8_t *ptr = start; ptr != end; ++ptr) {
138 sprintf(tmp, "%02x", *ptr);
144 void my_callback(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
148 //printf("skipped short packet\n");
151 if (bytes[12] != 0x08 || bytes[13] != 0x00) {
152 //printf("skipped non-IPv4 packet\n");
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) {
161 const u_char *ntlmssp = (const u_char *)memmem(data, (bytes + len) - data, "NTLMSSP", 7);
162 if (ntlmssp == NULL) {
167 invq.saddr = ip->daddr;
168 invq.daddr = ip->saddr;
169 invq.sport = tcp->dest;
170 invq.dport = tcp->source;
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) {
181 q.sport = tcp->source;
183 prev_packets.insert(make_pair(q, string(ntlmssp, bytes + len)));
187 const _CHALLENGE_MESSAGE *challenge = reinterpret_cast<const _CHALLENGE_MESSAGE *>(it->second.data());
188 const _AUTHENTICATE_MESSAGE *auth = reinterpret_cast<const _AUTHENTICATE_MESSAGE *>(ntlmssp);
190 if (auth->MessageType != NTLMSSP_AUTH || ntlmssp + sizeof(_AUTHENTICATE_MESSAGE) > bytes + len) {
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());
201 prev_packets.erase(it);
204 int main(int argc, char **argv)
206 pcap_t *pcap = pcap_open_offline(argv[1], NULL);
208 pcap_loop(pcap, -1, my_callback, NULL);