]> git.sesse.net Git - ntlmssp-extract/commitdiff
Initial commit. master
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 5 Oct 2013 21:47:12 +0000 (23:47 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 5 Oct 2013 21:47:12 +0000 (23:47 +0200)
ntlmssp.cpp [new file with mode: 0644]

diff --git a/ntlmssp.cpp b/ntlmssp.cpp
new file mode 100644 (file)
index 0000000..84381b2
--- /dev/null
@@ -0,0 +1,211 @@
+// Extracts NTMLSSP challenge/authentication pairs from a pcap file,
+// into a format crackable by the “jumbo” edition of John the Ripper.
+// Heavily inspired by https://github.com/psychomario/ntlmsspparse/blob/master/ntlmssp.py,
+// but infinitely faster.
+//
+// The struct definitions were lifted from ChromeOS, who probably
+// lifted them from some specification. They have been adapted a bit.
+
+#include <string.h>
+#include <pcap.h>
+#include <stdlib.h>
+#include <netinet/ip.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <map>
+#include <string>
+#include <algorithm>
+
+using namespace std;
+
+enum ntlmssp_message_type
+{
+       NTLMSSP_NEGOTIATE = 1,
+       NTLMSSP_CHALLENGE = 2,
+       NTLMSSP_AUTH      = 3,
+       NTLMSSP_UNKNOWN   = 4,
+};
+
+#define NTLMSSP_SIGNATURE "NTLMSSP"
+#define CIFS_CRYPTO_KEY_SIZE (8)
+
+typedef struct _SECURITY_BUFFER {
+        uint16_t Length;
+        uint16_t MaximumLength;
+        uint32_t BufferOffset;    /* offset to buffer */
+} __attribute__((packed)) SECURITY_BUFFER;
+
+struct _NTLM_MESSAGE {
+        uint8_t Signature[sizeof(NTLMSSP_SIGNATURE)];
+        uint32_t MessageType;
+};
+
+typedef struct _NEGOTIATE_MESSAGE {
+        uint8_t Signature[sizeof(NTLMSSP_SIGNATURE)];
+        uint32_t MessageType;     /* NtLmNegotiate = 1 */
+        uint32_t NegotiateFlags;
+        SECURITY_BUFFER DomainName;     /* RFC 1001 style and ASCII */
+        SECURITY_BUFFER WorkstationName;        /* RFC 1001 and ASCII */
+        /* SECURITY_BUFFER for version info not present since we
+           do not set the version is present flag */
+        char DomainString[0];
+        /* followed by WorkstationString */
+} __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
+
+typedef struct _CHALLENGE_MESSAGE {
+        uint8_t Signature[sizeof(NTLMSSP_SIGNATURE)];
+        uint32_t MessageType;   /* NtLmChallenge = 2 */
+        SECURITY_BUFFER TargetName;
+        uint32_t NegotiateFlags;
+        uint8_t Challenge[CIFS_CRYPTO_KEY_SIZE];
+        uint8_t Reserved[8];
+        SECURITY_BUFFER TargetInfoArray;
+        /* SECURITY_BUFFER for version info not present since we
+           do not set the version is present flag */
+} __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE;
+
+typedef struct _AUTHENTICATE_MESSAGE {
+        uint8_t Signature[sizeof(NTLMSSP_SIGNATURE)];
+        uint32_t MessageType;  /* NtLmsAuthenticate = 3 */
+        SECURITY_BUFFER LmChallengeResponse;
+        SECURITY_BUFFER NtChallengeResponse;
+        SECURITY_BUFFER DomainName;
+        SECURITY_BUFFER UserName;
+        SECURITY_BUFFER WorkstationName;
+        SECURITY_BUFFER SessionKey;
+        uint32_t NegotiateFlags;
+        /* SECURITY_BUFFER for version info not present since we
+           do not set the version is present flag */
+        char UserString[0];
+} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
+
+#define NTLMSSP_FEATURE_SESSION_KEY        0x00000001
+#define NTLMSSP_FEATURE_SIGN               0x00000002
+#define NTLMSSP_FEATURE_SEAL               0x00000004
+#define NTLMSSP_FEATURE_CCACHE   0x00000008
+
+struct quintuple {
+       uint32_t saddr, daddr;
+       uint16_t sport, dport;
+
+       bool operator< (const quintuple &o) const {
+               if (saddr != o.saddr)
+                       return saddr < o.saddr;
+               if (daddr != o.daddr)
+                       return daddr < o.daddr;
+               if (sport != o.sport)
+                       return sport < o.sport;
+               return dport < o.dport;
+       }
+};
+
+map<quintuple, string> prev_packets;
+
+string to_ascii(const SECURITY_BUFFER &buf, const void *base) {
+       const char *str = (char *)base + buf.BufferOffset;
+
+       string ret;
+       for (int i = 0; i < buf.Length; ++i) {
+               if (str[i] == 0) {
+                       // poor man's UTF-16 conversion :-)
+                       continue;
+               }
+               ret.push_back(str[i]);
+       }
+       return ret;
+}
+
+string to_hex(const SECURITY_BUFFER &buf, const void *base) {
+       const uint8_t *str = (uint8_t *)base + buf.BufferOffset;
+
+       string ret;
+       char tmp[256];
+       for (int i = 0; i < buf.Length; ++i) {
+               sprintf(tmp, "%02x", str[i]);
+               ret += tmp;
+       }
+       return ret;
+}
+
+string to_hex(const uint8_t *start, const uint8_t *end) {
+       string ret;
+       char tmp[256];
+       for (const uint8_t *ptr = start; ptr != end; ++ptr) {
+               sprintf(tmp, "%02x", *ptr);
+               ret += tmp;
+       }
+       return ret;
+}
+
+void my_callback(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
+{
+       int len = h->caplen;
+       if (len < 40) {
+               //printf("skipped short packet\n");
+               return;
+       }
+       if (bytes[12] != 0x08 || bytes[13] != 0x00) {
+               //printf("skipped non-IPv4 packet\n");
+               return;
+       }
+       const struct iphdr *ip = (const struct iphdr *)(bytes + 14);
+       const struct tcphdr *tcp = (const struct tcphdr *)(bytes + 14 + sizeof(*ip));
+       const u_char *data = bytes + 14 + sizeof(*ip) + sizeof(*tcp);
+       if (data > bytes + len) {
+               return;
+       }
+       const u_char *ntlmssp = (const u_char *)memmem(data, (bytes + len) - data, "NTLMSSP", 7);
+       if (ntlmssp == NULL) {
+               return;
+       }
+
+       quintuple invq;
+       invq.saddr = ip->daddr;
+       invq.daddr = ip->saddr;
+       invq.sport = tcp->dest;
+       invq.dport = tcp->source;
+
+       auto it = prev_packets.find(invq);
+       if (it == prev_packets.end()) {
+               const _CHALLENGE_MESSAGE *challenge = reinterpret_cast<const _CHALLENGE_MESSAGE *>(ntlmssp);
+               if (challenge->MessageType != NTLMSSP_CHALLENGE || ntlmssp + sizeof(_CHALLENGE_MESSAGE) > bytes + len) {
+                       return;
+               }       
+               quintuple q;
+               q.saddr = ip->saddr;
+               q.daddr = ip->daddr;
+               q.sport = tcp->source;
+               q.dport = tcp->dest;
+               prev_packets.insert(make_pair(q, string(ntlmssp, bytes + len)));
+               return;
+       }
+
+       const _CHALLENGE_MESSAGE *challenge = reinterpret_cast<const _CHALLENGE_MESSAGE *>(it->second.data());
+       const _AUTHENTICATE_MESSAGE *auth = reinterpret_cast<const _AUTHENTICATE_MESSAGE *>(ntlmssp);
+
+       if (auth->MessageType != NTLMSSP_AUTH || ntlmssp + sizeof(_AUTHENTICATE_MESSAGE) > bytes + len) {
+               return;
+       }
+
+       printf("%s::%s:%s:%s:%s\n",
+               to_ascii(auth->UserName, auth).c_str(),
+               to_ascii(auth->DomainName, auth).c_str(),
+               to_hex(auth->LmChallengeResponse, auth).c_str(),
+               to_hex(auth->NtChallengeResponse, auth).c_str(),
+               to_hex(challenge->Challenge, challenge->Challenge + CIFS_CRYPTO_KEY_SIZE).c_str());
+
+       prev_packets.erase(it);
+}
+
+int main(int argc, char **argv)
+{
+       pcap_t *pcap = pcap_open_offline(argv[1], NULL);
+       pcap_activate(pcap);
+       pcap_loop(pcap, -1, my_callback, NULL);
+       return 0;
+}
+