IBM Lotus Domino is an email client rather common in companies like Microsoft Outlook. This article proposes to observe a small part of the application engine, namely one method used to store the user's password. In a second step, we will conduct a brief analysis of all used algorithms then we will see how to implement a plugin for John The Ripper to bruteforce discovered digests.

Introduction

This article deals with Lotus Domino reverse engineering, especially the user authentication process when a mailbox is accessed. How password digest are stored? Which cryptographic algorithms are used? How? We will end up with a bruteforce implementation using John The Ripper.

We have used the 8.5 version during our analysis and it's probable that following analysis is valid for further or previous versions. We will also consider the Domino server has a classical configuration.

Password's hash storage

Password hashes are stored inside the well known user.id file, generated by the Lotus Domino server and stored on client file system, usually in %USERPROFILE%\AppData\Local\Lotus\Notes\Data. The interesting part is located at offset 0xD6 where the ciphered user password digest is stored:

User ciphered blob

Note that the blob size could vary and we still have to figure out why. It is stored as a unsigned short (16 bits) at offset 0xD6 and starting from offset 0xD8 ciphered data can be found.

To summarize, this file contains all information about user's profile:

  • Username and organization info (CN, O,...) ;

  • Language ;

  • Lotus Domino server address ;

  • User's certificate and password digest ;

  • And many other things that should be reversed...

The file format is not documented but with a bit of reverse engineering, we can put in the spot that nnotes.dll is in charge of parsing this file and executing different kind of cryptographic algorithms.

The mail client has to check user's credentials before he can access its mailbox. Credentials are not directly stored as a raw digest but they are ciphered using an unique key per user. Used algorithms are well known (like RC2 or SHA-1) however the global process is custom and a bit scrambled.

user.id deciphering process

The biggest surprise comes from the final digest bytes count, 5 bytes only or 40 bits. Quite surprising especially as we will see later that the SHA-1 algorithm is used for the key derivation process.

The last 5 bytes are generated with this algorithm:

void compute_msg_mac(uint8_t *msg,size_t len,uint8_t *msg_mac)
{
  size_t i,j;
  uint8_t c;

  for(i=j=0;i<len;i++)
  {
    if(j!=4)
    {
      msg_mac[j] = msg[i] ^ ebits_to_num[msg_mac[j] ^ msg_mac[j+1]];
      j++;
    }
    else
    {
      msg_mac[j] = msg[i] ^ ebits_to_num[msg_mac[j] ^ msg_mac[0]];
      j = 0;
    }
  }

  c = msg_mac[0];
  for(i=0;i<4;i++)
  {
    msg_mac[i] = msg_mac[i+1];
  }

  msg_mac[i] = c;
}

Why not directly using the classical SHA-1 + Salt? NSA again? Maybe too easy. However, many years ago, we know that the agency inserted a backdoor which stored some key (the one used when sending email) bits using asymmetric cryptography, so they could decipher any messages later with a little bruteforce.

http://www.cypherspace.org/adam/hacks/lotus-nsa-key.html

Lotus Domino key derivation

One thing is still missing, how the RC2 key is derived from user's password? Here is the global scheme, a custom 128 bits hash function is used and also a new kind of hash/checksum algorithm based on few trivial XOR operations:

User password derivation

The new checksum algorithm can be implemented as below:

void compute_key_mac(uint8_t *data,size_t len,uint8_t *mac,size_t mac_len)
{
  size_t i,j,mlen=mac_len-1;
  uint8_t k;

  for(i=0;i<16;i++)
  {
    k = ebits_to_num[mac[0] ^ mac[1]];

    for(j=0;j<mlen;j++)
      mac[j] = mac[j+1];

    mac[mlen] = data[i] ^ k;
  }
}

The ebits_to_num array can be found in openssl source code inside tab.c of RC2 package:

unsigned char ebits_to_num[256]=
{
  0xbd,0x56,0xea,0xf2,0xa2,0xf1,0xac,0x2a,0xb0,0x93,0xd1,0x9c,0x1b,0x33,0xfd,0xd0,
  0x30,0x04,0xb6,0xdc,0x7d,0xdf,0x32,0x4b,0xf7,0xcb,0x45,0x9b,0x31,0xbb,0x21,0x5a,
  0x41,0x9f,0xe1,0xd9,0x4a,0x4d,0x9e,0xda,0xa0,0x68,0x2c,0xc3,0x27,0x5f,0x80,0x36,
  0x3e,0xee,0xfb,0x95,0x1a,0xfe,0xce,0xa8,0x34,0xa9,0x13,0xf0,0xa6,0x3f,0xd8,0x0c,
  0x78,0x24,0xaf,0x23,0x52,0xc1,0x67,0x17,0xf5,0x66,0x90,0xe7,0xe8,0x07,0xb8,0x60,
  0x48,0xe6,0x1e,0x53,0xf3,0x92,0xa4,0x72,0x8c,0x08,0x15,0x6e,0x86,0x00,0x84,0xfa,
  0xf4,0x7f,0x8a,0x42,0x19,0xf6,0xdb,0xcd,0x14,0x8d,0x50,0x12,0xba,0x3c,0x06,0x4e,
  0xec,0xb3,0x35,0x11,0xa1,0x88,0x8e,0x2b,0x94,0x99,0xb7,0x71,0x74,0xd3,0xe4,0xbf,
  0x3a,0xde,0x96,0x0e,0xbc,0x0a,0xed,0x77,0xfc,0x37,0x6b,0x03,0x79,0x89,0x62,0xc6,
  0xd7,0xc0,0xd2,0x7c,0x6a,0x8b,0x22,0xa3,0x5b,0x05,0x5d,0x02,0x75,0xd5,0x61,0xe3,
  0x18,0x8f,0x55,0x51,0xad,0x1f,0x0b,0x5e,0x85,0xe5,0xc2,0x57,0x63,0xca,0x3d,0x6c,
  0xb4,0xc5,0xcc,0x70,0xb2,0x91,0x59,0x0d,0x47,0x20,0xc8,0x4f,0x58,0xe0,0x01,0xe2,
  0x16,0x38,0xc4,0x6f,0x3b,0x0f,0x65,0x46,0xbe,0x7e,0x2d,0x7b,0x82,0xf9,0x40,0xb5,
  0x1d,0x73,0xf8,0xeb,0x26,0xc7,0x87,0x97,0x25,0x54,0xb1,0x28,0xaa,0x98,0x9d,0xa5,
  0x64,0x6d,0x7a,0xd4,0x10,0x81,0x44,0xef,0x49,0xd6,0xae,0x2e,0xdd,0x76,0x5c,0x2f,
  0xa7,0x1c,0xc9,0x09,0x69,0x9a,0x83,0xcf,0x29,0x39,0xb9,0xe9,0x4c,0xff,0x43,0xab,
};

This array has been used for years in custom algorithms / cryptographic schemes implemented by IBM inside Lotus Notes, especially for user authentication.

At the end, a 64 bits key is produced and it can be directly used to decipher the user's blob stored in user.id file.

Credential check

To summarize, the scheme seems to be a bit confused and uncommon compared to many applications. In fact, the password is only used to derive a key when it is stored and the check is made against data stored in the user blob deciphered using this secret key.

Last 5 bytes of the deciphered blob could be considered as final password hash and all previous bytes checksum / hash should be equal to it. Thus, it reduces the overall security to almost 40 bits. Another solution to break the system could be to directly attack the RC2 64 bits key using bruteforce techniques, which is not so unrealistic considering modern equipment or cloud computing.

However, we will try to bruteforce the whole algorithm to retrieve the plaintext password. That's why we decided to implement a JTR plugin.

John The Ripper plugin

Now we know how to decipher user id blob and how to produce the same kind of digest from plaintext password, it is easy to write a John The Ripper plugin and benefit of its efficient bruteforce engine.

Writing a plugin for JTR is an easy process and we just have to write a C file, with a new fmt_main structure declaration (also defined in formats.h).

This structure is automatically registered once (and the associated code is generated by the Makefile) in the JTR engine and describe how the plugin should be used:

  • digest size, salt size, plaintext size ;

  • digest comparison ;

  • many callbacks to prepare, compute a digest from given plaintext passwords ;

  • hash table prototype to improve performances.

You can download the plugin lotus85_fmt_plug.c here , just store it in src folder and compile. The code is really not optimized but as the algorithm is overall easy and consists in few XOR operations, performances are not so bad and you can expect more than 100000 tries / second (e.g with a standard Core i7 2.8Ghz). It can be really improved by using parallel programming techniques with OpenMP for example.

The first thing to do is extracting the ciphered blob from the user.id file, you can use this tool LotusIdHashExtractor available here It just extracts the ciphered blob as a hexadecimal string that can be used with the JTR plugin.

Here is sample execution trace:

./john --wordlist=big_dict.txt  --format=lotus85 domino.dump
Loaded 1 password hash (Lotus Notes/Domino 8.5 [8/64])

guesses: 0  time: 0:00:00:06 0.06%  c/s: 75779  trying: DEWMUGUO4L - W7BTKCT9Y8
guesses: 0  time: 0:00:19:20 10.22% c/s: 66970  trying: TjAPKs7EGT - fXd0AackHq
guesses: 0  time: 0:00:34:52 18.70% c/s: 72320  trying: fzFp4whj - xZHGk8lc
lanetrotro         (?)
guesses: 1  time: 0:00:54:40 DONE (Tue Dec 17 23:27:10 2013)  c/s: 74491  trying: lanetrot11 - lanetrotze

This bruteforcer could be used in forensic investigations or while pentesting. Note that all tools are experimental and could not work in some situation, maybe depending on server configuration, used versions used or other parameters.

Conclusion

Here we just have made a minimalist analysis of the algorithm and there are still other things to check like the source of the user blob, are those bytes generated from user profile data or using a PRNG? Is the PRNG secure? Some new reverse engineering sessions should be started...

Finally, this analysis is just a little part of all things that are possible to do with this quite complex application. It could be interesting now to check how emails are ciphered, maybe still with this famous 64 bits key? And what about email parsing?

There are still many things to dig into.

Acknowledgments

  • Kevin Szkudlapski

  • Solar Designer for its excellent John The Ripper

  • All pony lovers :)

Update

Add licence for lotus85_fmt_plug.c


If you would like to learn more about our security audits and explore how we can help you, get in touch with us!