TRAITOR OUTLOOK --------------- Write your mails, send'em via proxies, trust m$, feel safe... Here comes the analysis of the OUTLOOK's algorithm of "Message-ID" and "boundary" fields generation. Are you scared? ;) So, these fields are generated by INETCOMM.DLL. After some disasm, the following stuff were found: boundary (generated before Message-ID) -------- GetLocalTime(<); // SYSTEMTIME lt SystemTimeToFileTime(<, &ft); // FILETIME ft wsprintfA( boundary, "----=_NextPart_%03d_%04X_%08.8lX.%08.8lX", part_n, // part # of the multipart msg, 0-based N, // (*) ft.dwHighDateTime, ft.dwLowDateTime ); Message-ID ---------- // DWORD ip := current ip || 0x0100007F if error GetSystemTime(<); SystemTimeToFileTime(<, &ft); wsprintfA( MessageID, "%04x%08.8lx$%08.8lx$%08x@%s", // ^^^^ can be absent, depending on outlook version N+3, // (*) ft.dwHighDateTime, ft.dwLowDateTime, ip, get_perverted_hostname() // (**) ); (*) N is some number, which is increased by 11..13 when new message is generated/sent, and probably in other cases. (**) 1. gethostname() 2. remove all characters except ['A'..'Z', 'a'..'z', '0'..'9', '.'] 3. while (last char == '.') remove last char 4. if empty string, return "LocalHost" EXAMPLE ------- Lets use google to find some outlook msg containing headers. http://www.rational.com/HyperMail/uml_feedback/mailfiles/uml_feedback.9904 From uml_feedback-owner@Rational.Com Wed Apr 14 09:01:42 1999 Received: (from majordom@localhost) by mailhub.rational.com (8.8.7/8.8.7/RATIONAL-mailhub) id JAA19138 for uml_feedback-outgoing; Wed, 14 Apr 1999 09:01:41 -0700 (PDT) From: "Dendelphi" To: Cc: Date: Wed, 14 Apr 1999 10:00:33 -0500 Message-ID: <01be8687$8b98b3a0$29e084a1@misti.abaco.edu.pe> MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_NextPart_000_0004_01BE865D.A2C2ABA0" X-Priority: 3 X-MSMail-Priority: Normal X-Mailer: Microsoft Outlook Express 4.71.1712.3 X-MimeOLE: Produced By Microsoft MimeOLE V4.71.1712.3 Sender: owner-uml_feedback@Rational.Com Precedence: first-class Reply-To: "Dendelphi" X-Majordomo-Taboo: uml_feedback ... Here is our fields: boundary ----=_NextPart_000_0004_01BE865D.A2C2ABA0 Message-ID 01be8687$8b98b3a0$29e084a1@misti.abaco.edu.pe Now, lets use the following program: #include #include #include #include #pragma hdrstop void main(int argc, char* argv[]) { char *boundary, *messageid, *hostname; DWORD part_n, ip; int tz, n1, n2; FILETIME ft1, ft2; SYSTEMTIME st, lt; if (argc != 3) { printf("syntax:\n"); printf(" outlookx \n"); exit(0); } boundary = argv[1]; messageid = argv[2]; assert(!strncmp(boundary, "----=_NextPart_", 15)); assert(sscanf(boundary+15, "%03d", &part_n)); assert(sscanf(boundary+19, "%04X", &n1)); assert(sscanf(boundary+24, "%08X", &ft1.dwHighDateTime)); assert(sscanf(boundary+33, "%08X", &ft1.dwLowDateTime)); FileTimeToSystemTime(&ft1, &st); if (messageid[8] == '$') { n2 = -1; assert(sscanf(messageid+ 0, "%08x", &ft2.dwHighDateTime)); assert(sscanf(messageid+ 9, "%08x", &ft2.dwLowDateTime)); assert(sscanf(messageid+18, "%08x", &ip)); hostname = messageid+27; } else { assert(sscanf(messageid+ 0, "%04x", &n2)); assert(sscanf(messageid+ 4, "%08x", &ft2.dwHighDateTime)); assert(sscanf(messageid+13, "%08x", &ft2.dwLowDateTime)); assert(sscanf(messageid+22, "%08x", &ip)); hostname = messageid+31; } FileTimeToSystemTime(&ft2, <); tz = (*(__int64*)&ft1 - *(__int64*)&ft2) / 10000000 / 3600; printf("boundary = %s\n", boundary); printf(" part_n = %d\n", part_n); printf(" n1 = %d\n", n1); printf(" time = %04d/%02d/%02d %02d:%02d:%02d.%d GMT%s%d\n", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, tz > 0 ? "+" : "", tz); printf(" hostname = %s\n", hostname); printf("messageid = %s\n", messageid); if (n2 != -1) printf(" n2 = %d # delta=%d\n", n2, n2-n1); printf(" time = %04d/%02d/%02d %02d:%02d:%02d.%d GMT\n", lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond, lt.wMilliseconds); printf(" IP = %s\n", inet_ntoa(*(struct in_addr*)&ip)); printf(" hostname = %s\n", hostname); } c:\>outlookx ----=_NextPart_000_0004_01BE865D.A2C2ABA0 01be8687$8b98b3a0$29e084a1@misti.abaco.edu.pe Program output: boundary = ----=_NextPart_000_0004_01BE865D.A2C2ABA0 part_n = 0 n1 = 4 time = 1999/04/14 10:00:33.370 GMT-5 hostname = misti.abaco.edu.pe messageid = 01be8687$8b98b3a0$29e084a1@misti.abaco.edu.pe time = 1999/04/14 15:00:33.370 GMT IP = 161.132.224.41 hostname = misti.abaco.edu.pe As you can see, original IP and timezone of message creation can be found. Except that, we can check if delta between N numbers is the same as should be produced by the corresponding outlook version; this can be used by some spam tests ("forged" headers). Also, using initial N number we estimate how many messages were sent before our one, in the current outlook session. Also, there is time delta between datetime stored in the boundary and messageid. This delta is a sum of local timezone plus some milliseconds, passed between boundary and messageid generation. In some outlook versions, there is only timezone delta, while in other versions we can estimate cpu speed; for example on my machine delta is equal to timezone + 40..1000 milliseconds. Imagine some hacker who contact's you (or some support group); they reply; and now he knows IP address(es) of the machines in the local network; even if NAT's and/or proxies are used to send messages. As such, using outlook is unsafe, since it leads to original IP and other local information disclosure. On other hand, knowing this all, original IP could be forged. ;-) P.S. Probably, eudora and other m$, ole and other shit -related mailers do the same. * * *