VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

Traitor Outlook


[Back to index] [Comments]

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(&lt); // SYSTEMTIME lt
    SystemTimeToFileTime(&lt, &ft); // FILETIME ft
    wsprintfA( boundary,
               part_n,                  // part # of the multipart msg, 0-based
               N,                       // (*)
               ft.dwLowDateTime );


    // DWORD ip := current ip || 0x0100007F if error
    SystemTimeToFileTime(&lt, &ft);
    wsprintfA( MessageID,
             // ^^^^ can be absent, depending on outlook version
               N+3,                      // (*)
               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"


Lets use google to find some outlook msg containing headers.

      From [email protected]  Wed Apr 14 09:01:42 1999
      Received: (from [email protected])
              by (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" <[email protected]>
      To: <[email protected]>
      Cc: <[email protected]>
      Date: Wed, 14 Apr 1999 10:00:33 -0500
      Message-ID: <[email protected]>
      MIME-Version: 1.0
      Content-Type: multipart/alternative;
      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: [email protected]
      Precedence: first-class
      Reply-To: "Dendelphi" <[email protected]>
      X-Majordomo-Taboo: uml_feedback

Here is our fields:

    boundary    ----=_NextPart_000_0004_01BE865D.A2C2ABA0
    Message-ID  [email protected]

Now, lets use the following program:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#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("  outlookx <boundary> <messageid>\n");

  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;
    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, &lt);

  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,
    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,
  printf("  IP       = %s\n", inet_ntoa(*(struct in_addr*)&ip));
  printf("  hostname = %s\n", hostname);

c:\>outlookx ----=_NextPart_000_0004_01BE865D.A2C2ABA0 [email protected]

Program output:

    boundary = ----=_NextPart_000_0004_01BE865D.A2C2ABA0
    part_n   = 0
    n1       = 4
    time     = 1999/04/14 10:00:33.370 GMT-5
    hostname =
  messageid  = [email protected]
    time     = 1999/04/14 15:00:33.370 GMT
    IP       =
    hostname =

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.

By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! aka